From de5a0e52d2447ebd42bdfe3492fca41e25b7c1be Mon Sep 17 00:00:00 2001 From: Debao Zhang Date: Wed, 22 Oct 2014 15:30:06 +0800 Subject: [PATCH] Fix Issue #46: Add internal support for shared formulas --- examples/xlsx/formulas/main.cpp | 23 ++++++++++++++---- src/xlsx/xlsxcellformula.cpp | 22 ++++++++++++----- src/xlsx/xlsxcellformula.h | 6 ++++- src/xlsx/xlsxcellformula_p.h | 3 ++- src/xlsx/xlsxworksheet.cpp | 42 ++++++++++++++++++++++++++++++--- src/xlsx/xlsxworksheet_p.h | 2 ++ 6 files changed, 82 insertions(+), 16 deletions(-) diff --git a/examples/xlsx/formulas/main.cpp b/examples/xlsx/formulas/main.cpp index 515339c..1338841 100644 --- a/examples/xlsx/formulas/main.cpp +++ b/examples/xlsx/formulas/main.cpp @@ -55,17 +55,30 @@ int main() Worksheet *sheet = xlsx.currentWorksheet(); for (int row=2; row<20; ++row) { - sheet->write(row, 2, QString(row%5+1, 'X')); //B2:B19 - sheet->write(row, 3, QString(row%5+1, 'X')); //C2:C19 - sheet->write(row, 5, 100.0 - row); //E2:E19 + sheet->write(row, 2, row*2); //B2:B19 + sheet->write(row, 3, row*3); //C2:C19 } - sheet->writeFormula("C20", CellFormula("SUM(IF((C2:C19=\"X\")*(B2:B19=\"X\"),1,0))", "C20", CellFormula::ArrayType)); - sheet->writeFormula("F2", CellFormula("E2:E19*10", "F2:F19", CellFormula::ArrayType)); + sheet->writeFormula("D2", CellFormula("B2:B19+C2:C19", "D2:D19", CellFormula::ArrayType)); //![2] + //![21] + xlsx.addSheet("SharedFormula"); + sheet = xlsx.currentWorksheet(); + + for (int row=2; row<20; ++row) { + sheet->write(row, 2, row*2); //B2:B19 + sheet->write(row, 3, row*3); //C2:C19 + } + sheet->writeFormula("D2", CellFormula("=B2+C2", "D2:D19", CellFormula::SharedType)); + //![21] + //![3] xlsx.save(); //![3] + //Make sure that read/write works well. + Document xlsx2("Book1.xlsx"); + xlsx2.saveAs("Book2.xlsx"); + return 0; } diff --git a/src/xlsx/xlsxcellformula.cpp b/src/xlsx/xlsxcellformula.cpp index 78942f2..858d347 100644 --- a/src/xlsx/xlsxcellformula.cpp +++ b/src/xlsx/xlsxcellformula.cpp @@ -24,6 +24,7 @@ ****************************************************************************/ #include "xlsxcellformula.h" #include "xlsxcellformula_p.h" +#include "xlsxutility_p.h" #include #include @@ -31,7 +32,7 @@ QT_BEGIN_NAMESPACE_XLSX CellFormulaPrivate::CellFormulaPrivate(const QString &formula_, const CellRange &ref_, CellFormula::FormulaType type_) - :formula(formula_), type(type_), reference(ref_), si(0) + :formula(formula_), type(type_), reference(ref_), ca(false), si(0) { //Remove the formula '=' sign if exists if (formula.startsWith(QLatin1String("="))) @@ -42,7 +43,8 @@ CellFormulaPrivate::CellFormulaPrivate(const QString &formula_, const CellRange CellFormulaPrivate::CellFormulaPrivate(const CellFormulaPrivate &other) : QSharedData(other) - , formula(other.formula), type(other.type), reference(other.reference), si(other.si) + , formula(other.formula), type(other.type), reference(other.reference) + , ca(other.ca), si(other.si) { } @@ -139,7 +141,7 @@ CellFormula::FormulaType CellFormula::formulaType() const /*! * Returns the contents of the formula. */ -QString CellFormula::formulaContent() const +QString CellFormula::formulaText() const { return d ? d->formula : QString(); } @@ -190,10 +192,14 @@ bool CellFormula::saveToXml(QXmlStreamWriter &writer) const writer.writeAttribute(QStringLiteral("t"), t); if (d->reference.isValid()) writer.writeAttribute(QStringLiteral("ref"), d->reference.toString()); + if (d->ca) + writer.writeAttribute(QStringLiteral("ca"), QStringLiteral("1")); if (d->type == CellFormula::SharedType) writer.writeAttribute(QStringLiteral("si"), QString::number(d->si)); - writer.writeCharacters(d->formula); + if (!d->formula.isEmpty()) + writer.writeCharacters(d->formula); + writer.writeEndElement(); //f return true; @@ -220,10 +226,14 @@ bool CellFormula::loadFromXml(QXmlStreamReader &reader) if (attributes.hasAttribute(QLatin1String("ref"))) { QString refString = attributes.value(QLatin1String("ref")).toString(); d->reference = CellRange(refString); - } else if (attributes.hasAttribute(QLatin1String("si"))) { - d->si = attributes.value(QLatin1String("si")).toString().toInt(); } + QString ca = attributes.value(QLatin1String("si")).toString(); + d->ca = parseXsdBoolean(ca, false); + + if (attributes.hasAttribute(QLatin1String("si"))) + d->si = attributes.value(QLatin1String("si")).toString().toInt(); + d->formula = reader.readElementText(); return true; } diff --git a/src/xlsx/xlsxcellformula.h b/src/xlsx/xlsxcellformula.h index f7e1c64..0d4679b 100644 --- a/src/xlsx/xlsxcellformula.h +++ b/src/xlsx/xlsxcellformula.h @@ -35,6 +35,8 @@ QT_BEGIN_NAMESPACE_XLSX class CellFormulaPrivate; class CellRange; +class Worksheet; +class WorksheetPrivate; class Q_XLSX_EXPORT CellFormula { @@ -56,7 +58,7 @@ public: bool isValid() const; FormulaType formulaType() const; - QString formulaContent() const; + QString formulaText() const; CellRange reference() const; int sharedIndex() const; @@ -66,6 +68,8 @@ public: bool saveToXml(QXmlStreamWriter &writer) const; bool loadFromXml(QXmlStreamReader &reader); private: + friend class Worksheet; + friend class WorksheetPrivate; QExplicitlySharedDataPointer d; }; diff --git a/src/xlsx/xlsxcellformula_p.h b/src/xlsx/xlsxcellformula_p.h index 8919cdc..6aeddc9 100644 --- a/src/xlsx/xlsxcellformula_p.h +++ b/src/xlsx/xlsxcellformula_p.h @@ -55,7 +55,8 @@ public: QString formula; //formula contents CellFormula::FormulaType type; CellRange reference; - int si; + bool ca; //Calculate Cell + int si; //Shared group index }; QT_END_NAMESPACE_XLSX diff --git a/src/xlsx/xlsxworksheet.cpp b/src/xlsx/xlsxworksheet.cpp index f9872b5..7028094 100755 --- a/src/xlsx/xlsxworksheet.cpp +++ b/src/xlsx/xlsxworksheet.cpp @@ -513,7 +513,7 @@ QVariant Worksheet::read(int row, int column) const if (!cell) return QVariant(); if (cell->hasFormula() && cell->formula().formulaType() == CellFormula::NormalType) - return QVariant(QLatin1String("=")+cell->formula().formulaContent()); + return QVariant(QLatin1String("=")+cell->formula().formulaText()); if (cell->isDateTime()) { double val = cell->value().toDouble(); QDateTime dt = cell->dateTime(); @@ -709,7 +709,7 @@ bool Worksheet::writeFormula(const CellReference &row_column, const CellFormula /*! Write \a formula to the cell (\a row, \a column) with the \a format and \a result. */ -bool Worksheet::writeFormula(int row, int column, const CellFormula &formula, const Format &format, double result) +bool Worksheet::writeFormula(int row, int column, const CellFormula &formula_, const Format &format, double result) { Q_D(Worksheet); if (d->checkDimensions(row, column)) @@ -718,10 +718,42 @@ bool Worksheet::writeFormula(int row, int column, const CellFormula &formula, co Format fmt = format.isValid() ? format : d->cellFormat(row, column); d->workbook->styles()->addXfFormat(fmt); + CellFormula formula = formula_; + formula.d->ca = true; + if (formula.formulaType() == CellFormula::SharedType) { + //Assign proper shared index for shared formula + int si=0; + while(d->sharedFormulaMap.contains(si)) + ++si; + formula.d->si = si; + d->sharedFormulaMap[si] = formula; + } + QSharedPointer data = QSharedPointer(new Cell(result, Cell::NumberType, fmt, this)); data->d_ptr->formula = formula; d->cellTable[row][column] = data; + CellRange range = formula.reference(); + if (formula.formulaType() == CellFormula::SharedType) { + CellFormula sf(QString(), CellFormula::SharedType); + sf.d->si = formula.sharedIndex(); + for (int r=range.firstRow(); r<=range.lastRow(); ++r) { + for (int c=range.firstColumn(); c<=range.lastColumn(); ++c) { + if (!(r==row && c==column)) { + if(Cell *cell = cellAt(r, c)) { + cell->d_ptr->formula = sf; + } else { + QSharedPointer newCell = QSharedPointer(new Cell(result, Cell::NumberType, fmt, this)); + newCell->d_ptr->formula = sf; + d->cellTable[r][c] = newCell; + } + } + } + } + } else if (formula.formulaType() == CellFormula::SharedType) { + + } + return true; } @@ -1896,7 +1928,11 @@ void WorksheetPrivate::loadXmlSheetData(QXmlStreamReader &reader) while (!reader.atEnd() && !(reader.name() == QLatin1String("c") && reader.tokenType() == QXmlStreamReader::EndElement)) { if (reader.readNextStartElement()) { if (reader.name() == QLatin1String("f")) { - cell->d_func()->formula.loadFromXml(reader); + CellFormula &formula = cell->d_func()->formula; + formula.loadFromXml(reader); + if (formula.formulaType() == CellFormula::SharedType && !formula.formulaText().isEmpty()) { + sharedFormulaMap[formula.sharedIndex()] = formula; + } } else if (reader.name() == QLatin1String("v")) { QString value = reader.readElementText(); if (cellType == Cell::SharedStringType) { diff --git a/src/xlsx/xlsxworksheet_p.h b/src/xlsx/xlsxworksheet_p.h index cd67a32..2930969 100644 --- a/src/xlsx/xlsxworksheet_p.h +++ b/src/xlsx/xlsxworksheet_p.h @@ -41,6 +41,7 @@ #include "xlsxcell.h" #include "xlsxdatavalidation.h" #include "xlsxconditionalformatting.h" +#include "xlsxcellformula.h" #include #include @@ -194,6 +195,7 @@ public: QList dataValidationsList; QList conditionalFormattingList; + QMap sharedFormulaMap; CellRange dimension; int previous_row;