diff --git a/examples/xlsx/demo/main.cpp b/examples/xlsx/demo/main.cpp index 6e82a04..91c7da2 100644 --- a/examples/xlsx/demo/main.cpp +++ b/examples/xlsx/demo/main.cpp @@ -270,6 +270,21 @@ int main() xlsx.write("E8", 2); xlsx.mergeCells("E8:F21", centerAlign); + //--------------------------------------------------------------- + //Create the fifth sheet. + xlsx.addWorksheet("Grouping"); + qsrand(QDateTime::currentMSecsSinceEpoch()); + for (int row=1; row<31; ++row) { + for (int col=0; col<10; ++col) + xlsx.write(row, col, qrand() % 100); + } + xlsx.groupRows(3, 6); + xlsx.groupRows(10, 25, false); + xlsx.groupRows(14, 16, false); + xlsx.groupRows(19, 21, false); + xlsx.groupColumns(0, 1); + xlsx.groupColumns(4, 7, false); + xlsx.saveAs("Book1.xlsx"); //Make sure that read/write works well. diff --git a/src/xlsx/xlsxdocument.cpp b/src/xlsx/xlsxdocument.cpp index a2e4ee8..cae48bf 100644 --- a/src/xlsx/xlsxdocument.cpp +++ b/src/xlsx/xlsxdocument.cpp @@ -224,6 +224,22 @@ bool Document::setColumn(const QString &colFirst, const QString &colLast, double return currentWorksheet()->setColumn(colFirst, colLast, width, format, hidden); } +/*! + Groups rows from rowFirst to rowLast. Returns false if error occurs. + */ +bool Document::groupRows(int rowFirst, int rowLast, bool collapsed) +{ + return currentWorksheet()->groupRows(rowFirst, rowLast, collapsed); +} + +/*! + Groups columns from colFirst to colLast. Returns false if error occurs. + */ +bool Document::groupColumns(int colFirst, int colLast, bool collapsed) +{ + return currentWorksheet()->groupColumns(colFirst, colLast, collapsed); +} + /*! * \brief Add a data validation rule for current worksheet * \param validation diff --git a/src/xlsx/xlsxdocument.h b/src/xlsx/xlsxdocument.h index 4d61c5c..097dc7f 100644 --- a/src/xlsx/xlsxdocument.h +++ b/src/xlsx/xlsxdocument.h @@ -66,6 +66,8 @@ public: bool setRow(const QString &row, double height, Format* format=0, bool hidden=false); bool setColumn(int colFirst, int colLast, double width, Format* format=0, bool hidden=false); bool setColumn(const QString &colFirst, const QString &colLast, double width, Format* format=0, bool hidden=false); + bool groupRows(int rowFirst, int rowLast, bool collapsed = true); + bool groupColumns(int colFirst, int colLast, bool collapsed = true); bool addDataValidation(const DataValidation &validation); Cell *cellAt(const QString &cell) const; diff --git a/src/xlsx/xlsxworksheet.cpp b/src/xlsx/xlsxworksheet.cpp index 0af1b1d..f8747bf 100755 --- a/src/xlsx/xlsxworksheet.cpp +++ b/src/xlsx/xlsxworksheet.cpp @@ -845,8 +845,10 @@ void Worksheet::saveToXmlFile(QIODevice *device) if (!d->colsInfo.isEmpty()) { writer.writeStartElement(QStringLiteral("cols")); - for (int i=0; icolsInfo.size(); ++i) { - QSharedPointer col_info = d->colsInfo[i]; + QMapIterator > it(d->colsInfo); + while (it.hasNext()) { + it.next(); + QSharedPointer col_info = it.value(); writer.writeStartElement(QStringLiteral("col")); writer.writeAttribute(QStringLiteral("min"), QString::number(col_info->firstColumn + 1)); writer.writeAttribute(QStringLiteral("max"), QString::number(col_info->lastColumn + 1)); @@ -1156,6 +1158,48 @@ bool Worksheet::setRow(const QString &row, double height, Format *format, bool h return false; } +void WorksheetPrivate::splitColsInfo(int colFirst, int colLast) +{ + // Split current columnInfo, for example, if "A:H" has been set, + // we are trying to set "B:D", there should be "A", "B:D", "E:H". + // This will be more complex if we try to set "C:F" after "B:D". + { + QMapIterator > it(colsInfo); + while(it.hasNext()) { + it.next(); + QSharedPointer info = it.value(); + if (colFirst > info->firstColumn && colFirst <= info->lastColumn) { + //split the range, + QSharedPointer info2(new XlsxColumnInfo(*info)); + info->lastColumn = colFirst - 1; + info2->firstColumn = colFirst; + colsInfo.insert(colFirst, info2); + for (int c = info2->firstColumn; c <= info2->lastColumn; ++c) + colsInfoHelper[c] = info2; + + break; + } + } + } + { + QMapIterator > it(colsInfo); + while(it.hasNext()) { + it.next(); + QSharedPointer info = it.value(); + if (colLast >= info->firstColumn && colLast < info->lastColumn) { + QSharedPointer info2(new XlsxColumnInfo(*info)); + info->lastColumn = colLast; + info2->firstColumn = colLast + 1; + colsInfo.insert(colLast + 1, info2); + for (int c = info2->firstColumn; c <= info2->lastColumn; ++c) + colsInfoHelper[c] = info2; + + break; + } + } + } +} + /*! Sets column width and format for all columns from colFirst to colLast. Column width measured as the number of characters of the maximum digit width of the @@ -1176,12 +1220,35 @@ bool Worksheet::setColumn(int colFirst, int colLast, double width, Format *forma if (d->checkDimensions(0, colFirst, ignore_row, ignore_col)) return false; - QSharedPointer info(new XlsxColumnInfo(colFirst, colLast, width, format, hidden)); - d->colsInfo.append(info); - - for (int col=colFirst; col<=colLast; ++col) - d->colsInfoHelper[col] = info; + d->splitColsInfo(colFirst, colLast); + + QList nodes; + nodes.append(colFirst); + for (int col = colFirst; col <= colLast; ++col) { + if (d->colsInfo.contains(col)) { + if (nodes.last() != col) + nodes.append(col); + int nextCol = d->colsInfo[col]->lastColumn + 1; + if (nextCol <= colLast) + nodes.append(nextCol); + } + } + for (int idx = 0; idx < nodes.size(); ++idx) { + int colStart = nodes[idx]; + if (d->colsInfo.contains(colStart)) { + QSharedPointer info = d->colsInfo[colStart]; + info->width = width; + info->format = format; + info->hidden = hidden; + } else { + int colEnd = (idx == nodes.size() - 1) ? colLast : nodes[idx+1] - 1; + QSharedPointer info(new XlsxColumnInfo(colStart, colEnd, width, format, hidden)); + d->colsInfo.insert(colFirst, info); + for (int c = colStart; c <= colEnd; ++c) + d->colsInfoHelper[c] = info; + } + } d->workbook->styles()->addFormat(format); return true; @@ -1237,6 +1304,52 @@ bool Worksheet::groupColumns(int colFirst, int colLast, bool collapsed) { Q_D(Worksheet); + d->splitColsInfo(colFirst, colLast); + + QList nodes; + nodes.append(colFirst); + for (int col = colFirst; col <= colLast; ++col) { + if (d->colsInfo.contains(col)) { + if (nodes.last() != col) + nodes.append(col); + int nextCol = d->colsInfo[col]->lastColumn + 1; + if (nextCol <= colLast) + nodes.append(nextCol); + } + } + + for (int idx = 0; idx < nodes.size(); ++idx) { + int colStart = nodes[idx]; + if (d->colsInfo.contains(colStart)) { + QSharedPointer info = d->colsInfo[colStart]; + info->outlineLevel += 1; + if (collapsed) + info->hidden = true; + } else { + int colEnd = (idx == nodes.size() - 1) ? colLast : nodes[idx+1] - 1; + QSharedPointer info(new XlsxColumnInfo(colStart, colEnd)); + info->outlineLevel += 1; + d->colsInfo.insert(colFirst, info); + if (collapsed) + info->hidden = true; + for (int c = colStart; c <= colEnd; ++c) + d->colsInfoHelper[c] = info; + } + } + + if (collapsed) { + int col = colLast+1; + d->splitColsInfo(col, col); + if (d->colsInfo.contains(col)) + d->colsInfo[col]->collapsed = true; + else { + QSharedPointer info(new XlsxColumnInfo(col, col)); + info->collapsed = true; + d->colsInfo.insert(col, info); + d->colsInfoHelper[col] = info; + } + } + return false; } @@ -1638,7 +1751,7 @@ void WorksheetPrivate::readColumnsInfo(XmlStreamReader &reader) if (colAttrs.hasAttribute(QLatin1String("outlineLevel"))) info->outlineLevel = colAttrs.value(QLatin1String("outlineLevel")).toInt(); - colsInfo.append(info); + colsInfo.insert(min, info); for (int col=min; col<=max; ++col) colsInfoHelper[col] = info; } diff --git a/src/xlsx/xlsxworksheet_p.h b/src/xlsx/xlsxworksheet_p.h index f7e1c1e..47fdb69 100644 --- a/src/xlsx/xlsxworksheet_p.h +++ b/src/xlsx/xlsxworksheet_p.h @@ -163,6 +163,8 @@ public: Format *cellFormat(int row, int col) const; QString generateDimensionString(); void calculateSpans(); + void splitColsInfo(int colFirst, int colLast); + void writeSheetData(XmlStreamWriter &writer); void writeCellData(XmlStreamWriter &writer, int row, int col, QSharedPointer cell); void writeMergeCells(XmlStreamWriter &writer); @@ -195,7 +197,7 @@ public: QStringList externDrawingList; QList imageList; QMap > rowsInfo; - QList > colsInfo; + QMap > colsInfo; QMap > colsInfoHelper; QList > drawingLinks; diff --git a/tests/auto/worksheet/tst_worksheet.cpp b/tests/auto/worksheet/tst_worksheet.cpp index d1fd0a9..9abc93c 100644 --- a/tests/auto/worksheet/tst_worksheet.cpp +++ b/tests/auto/worksheet/tst_worksheet.cpp @@ -221,7 +221,7 @@ void WorksheetTest::testReadColsInfo() sheet.d_ptr->readColumnsInfo(reader); QCOMPARE(sheet.d_ptr->colsInfo.size(), 1); - QCOMPARE(sheet.d_ptr->colsInfo[0]->width, 5.0); + QCOMPARE(sheet.d_ptr->colsInfo[9]->width, 5.0); } void WorksheetTest::testReadRowsInfo() @@ -241,7 +241,7 @@ void WorksheetTest::testReadRowsInfo() sheet.d_ptr->readSheetData(reader); QCOMPARE(sheet.d_ptr->rowsInfo.size(), 1); - QCOMPARE(sheet.d_ptr->rowsInfo[3]->height, 40.0); + QCOMPARE(sheet.d_ptr->rowsInfo[2]->height, 40.0); } void WorksheetTest::testReadMergeCells()