diff --git a/examples/xlsx/formulas/doc/src/formulas.qdoc b/examples/xlsx/formulas/doc/src/formulas.qdoc new file mode 100644 index 0000000..a91850c --- /dev/null +++ b/examples/xlsx/formulas/doc/src/formulas.qdoc @@ -0,0 +1,24 @@ +/*! + \example formulas + \title Formulas Example + \brief This is a simplest Qt Xlsx example. + \ingroup qtxlsx-examples + + This example demonstrates how to create a new + .xlsx file containing formulas + with Qt Xlsx Library. So lets see how this is achieved. + + This creates a new instance of the all important Document + class which gives you access to the Excel workbook and worksheets. + \snippet formulas/main.cpp 0 + + A default worksheet have been created by Document. Let's start + by adding some basic formulas. + \snippet formulas/main.cpp 1 + + Then add one array formula. + \snippet formulas/main.cpp 2 + + Now save the file and all its components. + \snippet formulas/main.cpp 3 +*/ diff --git a/examples/xlsx/formulas/formulas.pro b/examples/xlsx/formulas/formulas.pro new file mode 100644 index 0000000..10d2978 --- /dev/null +++ b/examples/xlsx/formulas/formulas.pro @@ -0,0 +1,9 @@ +TARGET = hello + +#include(../../../src/xlsx/qtxlsx.pri) +QT+=xlsx + +CONFIG += console +CONFIG -= app_bundle + +SOURCES += main.cpp diff --git a/examples/xlsx/formulas/main.cpp b/examples/xlsx/formulas/main.cpp new file mode 100644 index 0000000..7d0bbda --- /dev/null +++ b/examples/xlsx/formulas/main.cpp @@ -0,0 +1,63 @@ +#include +#include "xlsxdocument.h" +#include "xlsxformat.h" +#include "xlsxworksheet.h" + +QTXLSX_USE_NAMESPACE + +int main() +{ + //![0] + Document xlsx; + //![0] + + //![1] + xlsx.setColumn("A", "B", 40); + Format *rAlign = xlsx.createFormat(); + rAlign->setHorizontalAlignment(Format::AlignRight); + Format *lAlign = xlsx.createFormat(); + lAlign->setHorizontalAlignment(Format::AlignLeft); + xlsx.write("B3", 40, lAlign); + xlsx.write("B4", 30, lAlign); + xlsx.write("B5", 50, lAlign); + xlsx.write("A7", "SUM(B3:B5)=", rAlign); + xlsx.write("B7", "=SUM(B3:B5)", lAlign); + xlsx.write("A8", "AVERAGE(B3:B5)=", rAlign); + xlsx.write("B8", "=AVERAGE(B3:B5)", lAlign); + xlsx.write("A9", "MAX(B3:B5)=", rAlign); + xlsx.write("B9", "=MAX(B3:B5)", lAlign); + xlsx.write("A10", "MIN(B3:B5)=", rAlign); + xlsx.write("B10", "=MIN(B3:B5)", lAlign); + xlsx.write("A11", "COUNT(B3:B5)=", rAlign); + xlsx.write("B11", "=COUNT(B3:B5)", lAlign); + + xlsx.write("A13", "IF(B7>100,\"large\",\"small\")=", rAlign); + xlsx.write("B13", "=IF(B7>100,\"large\",\"small\")", lAlign); + + xlsx.write("A15", "SQRT(25)=", rAlign); + xlsx.write("B15", "=SQRT(25)", lAlign); + xlsx.write("A16", "RAND()=", rAlign); + xlsx.write("B16", "=RAND()", lAlign); + xlsx.write("A17", "2*PI()=", rAlign); + xlsx.write("B17", "=2*PI()", lAlign); + + xlsx.write("A19", "UPPER(\"qtxlsx\")=", rAlign); + xlsx.write("B19", "=UPPER(\"qtxlsx\")", lAlign); + xlsx.write("A20", "LEFT(\"ubuntu\",3)=", rAlign); + xlsx.write("B20", "=LEFT(\"ubuntu\",3)", lAlign); + xlsx.write("A21", "LEN(\"Hello Qt!\")=", rAlign); + xlsx.write("B21", "=LEN(\"Hello Qt!\")", lAlign); + //![1] + + //![2] + for (int row=22; row<=30; ++row) + xlsx.write(row, 1, 100.0 - row); + xlsx.currentWorksheet()->writeArrayFormula("B22:B30", "{=A22:A30*10}"); + //![2] + + //![3] + xlsx.save(); + //![3] + + return 0; +} diff --git a/examples/xlsx/xlsx.pro b/examples/xlsx/xlsx.pro index c95a7be..302109c 100644 --- a/examples/xlsx/xlsx.pro +++ b/examples/xlsx/xlsx.pro @@ -9,5 +9,6 @@ SUBDIRS = hello \ numberformat \ datavalidation \ definename \ + formulas \ demo diff --git a/src/xlsx/xlsxcell.h b/src/xlsx/xlsxcell.h index 2db413d..6546db9 100644 --- a/src/xlsx/xlsxcell.h +++ b/src/xlsx/xlsxcell.h @@ -46,7 +46,8 @@ public: Formula, Boolean, Error, - InlineString + InlineString, + ArrayFormula }; DataType dataType() const; diff --git a/src/xlsx/xlsxcell_p.h b/src/xlsx/xlsxcell_p.h index 54ff6c9..b656283 100644 --- a/src/xlsx/xlsxcell_p.h +++ b/src/xlsx/xlsxcell_p.h @@ -27,6 +27,7 @@ #include "xlsxglobal.h" #include "xlsxcell.h" +#include "xlsxcellrange.h" QT_BEGIN_NAMESPACE_XLSX @@ -40,6 +41,7 @@ public: QString formula; Cell::DataType dataType; Format *format; + CellRange range; //used for arrayFormula Worksheet *parent; Cell *q_ptr; diff --git a/src/xlsx/xlsxworksheet.cpp b/src/xlsx/xlsxworksheet.cpp index 202d310..1393536 100755 --- a/src/xlsx/xlsxworksheet.cpp +++ b/src/xlsx/xlsxworksheet.cpp @@ -125,8 +125,6 @@ void WorksheetPrivate::calculateSpans() if ((row_num)%16 == 0 || row_num == dimension.lastRow()) { int span_index = row_num / 16; if (span_max != INT32_MIN) { - span_min += 1; - span_max += 1; row_spans[span_index] = QStringLiteral("%1:%2").arg(span_min).arg(span_max); span_max = INT32_MIN; } @@ -459,6 +457,8 @@ int Worksheet::write(int row, int column, const QVariant &value, Format *format) QRegularExpression urlPattern(QStringLiteral("^([fh]tt?ps?://)|(mailto:)|(file://)")); if (token.startsWith(QLatin1String("="))) { ret = writeFormula(row, column, token, format); + } else if (token.startsWith(QLatin1String("{=")) && token.endsWith(QLatin1Char('}'))) { + ret = writeArrayFormula(CellRange(row, column, row, column), token, format); } else if (token.contains(urlPattern)) { ret = writeHyperlink(row, column, QUrl(token)); } else { @@ -676,6 +676,51 @@ int Worksheet::writeFormula(int row, int column, const QString &formula, Format return error; } +/*! + Write \a formula to the \a range with the \a format +*/ +int Worksheet::writeArrayFormula(const CellRange &range, const QString &formula, Format *format) +{ + Q_D(Worksheet); + int error = 0; + + if (d->checkDimensions(range.firstRow(), range.firstColumn())) + return -1; + if (d->checkDimensions(range.lastRow(), range.lastColumn())) + return -1; + QString _formula = formula; + //Remove the formula "{=" and "}" sign if exists + if (_formula.startsWith(QLatin1String("{="))) + _formula.remove(0,2); + if (_formula.endsWith(QLatin1Char('}'))) + _formula.chop(1); + + for (int row=range.firstRow(); row<=range.lastRow(); ++row) { + for (int column=range.firstColumn(); column<=range.lastColumn(); ++column) { + Format *_format = format ? format : d->cellFormat(row, column); + if (row == range.firstRow() && column == range.firstColumn()) { + QSharedPointer data(new Cell(0, Cell::ArrayFormula, _format, this)); + data->d_ptr->formula = _formula; + data->d_ptr->range = range; + d->cellTable[row][column] = data; + } else { + d->cellTable[row][column] = QSharedPointer(new Cell(0, Cell::Numeric, _format, this)); + } + d->workbook->styles()->addFormat(_format); + } + } + + return error; +} + +/*! + \overload + */ +int Worksheet::writeArrayFormula(const QString &range, const QString &formula, Format *format) +{ + return writeArrayFormula(CellRange(range), formula, format); +} + /*! \overload */ @@ -1130,6 +1175,13 @@ void WorksheetPrivate::writeCellData(XmlStreamWriter &writer, int row, int col, writer.writeAttribute(QStringLiteral("t"), QStringLiteral("str")); writer.writeTextElement(QStringLiteral("f"), cell->formula()); writer.writeTextElement(QStringLiteral("v"), cell->value().toString()); + } else if (cell->dataType() == Cell::ArrayFormula) { + writer.writeStartElement(QStringLiteral("f")); + writer.writeAttribute(QStringLiteral("t"), QStringLiteral("array")); + writer.writeAttribute(QStringLiteral("ref"), cell->d_ptr->range.toString()); + writer.writeCharacters(cell->formula()); + writer.writeEndElement(); //f + writer.writeTextElement(QStringLiteral("v"), cell->value().toString()); } else if (cell->dataType() == Cell::Boolean) { writer.writeAttribute(QStringLiteral("t"), QStringLiteral("b")); writer.writeTextElement(QStringLiteral("v"), cell->value().toBool() ? QStringLiteral("1") : QStringLiteral("0")); @@ -1725,13 +1777,22 @@ QSharedPointer WorksheetPrivate::readNumericCellData(XmlStreamReader &read QString v_str; QString f_str; + QSharedPointer cell; while (!(reader.name() == QLatin1String("c") && reader.tokenType() == QXmlStreamReader::EndElement)) { reader.readNextStartElement(); if (reader.tokenType() == QXmlStreamReader::StartElement) { - if (reader.name() == QLatin1String("v")) + if (reader.name() == QLatin1String("v")) { v_str = reader.readElementText(); - else if (reader.name() == QLatin1String("f")) + } else if (reader.name() == QLatin1String("f")) { + QXmlStreamAttributes fAttrs = reader.attributes(); + if (fAttrs.hasAttribute(QLatin1String("array"))) { + cell = QSharedPointer(new Cell(0, Cell::ArrayFormula)); + cell->d_ptr->range = CellRange(fAttrs.value(QLatin1String("ref")).toString()); + } else { + cell = QSharedPointer(new Cell(0, Cell::Formula)); + } f_str = reader.readElementText(); + } } } @@ -1743,10 +1804,11 @@ QSharedPointer WorksheetPrivate::readNumericCellData(XmlStreamReader &read return QSharedPointer(new Cell(v_str.toDouble(), Cell::Numeric)); } else { //formula type - QSharedPointer cell(new Cell(v_str.toDouble(), Cell::Formula)); + cell->d_ptr->value = v_str.toDouble(); cell->d_ptr->formula = f_str; - return cell; } + + return cell; } void WorksheetPrivate::readSheetData(XmlStreamReader &reader) diff --git a/src/xlsx/xlsxworksheet.h b/src/xlsx/xlsxworksheet.h index 905beda..b27524b 100755 --- a/src/xlsx/xlsxworksheet.h +++ b/src/xlsx/xlsxworksheet.h @@ -65,6 +65,8 @@ public: int writeNumeric(int row, int column, double value, Format *format=0); int writeFormula(const QString &row_column, const QString &formula, Format *format=0, double result=0); int writeFormula(int row, int column, const QString &formula, Format *format=0, double result=0); + int writeArrayFormula(const QString &range, const QString &formula, Format *format=0); + int writeArrayFormula(const CellRange &range, const QString &formula, Format *format=0); int writeBlank(const QString &row_column, Format *format=0); int writeBlank(int row, int column, Format *format=0); int writeBool(const QString &row_column, bool value, Format *format=0);