diff --git a/src/xlsx/xlsxcell.cpp b/src/xlsx/xlsxcell.cpp index 1f6a188..947a590 100644 --- a/src/xlsx/xlsxcell.cpp +++ b/src/xlsx/xlsxcell.cpp @@ -24,6 +24,11 @@ ****************************************************************************/ #include "xlsxcell.h" #include "xlsxcell_p.h" +#include "xlsxformat.h" +#include "xlsxutility_p.h" +#include "xlsxworksheet.h" +#include "xlsxworkbook.h" +#include QT_BEGIN_NAMESPACE_XLSX @@ -44,12 +49,13 @@ CellPrivate::CellPrivate(Cell *p) : * \internal * Created by Worksheet only. */ -Cell::Cell(const QVariant &data, DataType type, Format *format) : +Cell::Cell(const QVariant &data, DataType type, Format *format, Worksheet *parent) : d_ptr(new CellPrivate(this)) { d_ptr->value = data; d_ptr->dataType = type; d_ptr->format = format; + d_ptr->parent = parent; } /*! @@ -88,4 +94,23 @@ QString Cell::formula() const return d->formula; } +/*! + * Returns whether the value is probably a dateTime or not + */ +bool Cell::isDateTime() const +{ + Q_D(const Cell); + if (d->dataType == Numeric && d->format && d->format->isDateTimeFormat()) + return true; + return false; +} + +QDateTime Cell::dateTime() const +{ + Q_D(const Cell); + if (!isDateTime()) + return QDateTime(); + return datetimeFromNumber(d->value.toDouble(), d->parent->workbook()->isDate1904()); +} + QT_END_NAMESPACE_XLSX diff --git a/src/xlsx/xlsxcell.h b/src/xlsx/xlsxcell.h index c06a807..33af031 100644 --- a/src/xlsx/xlsxcell.h +++ b/src/xlsx/xlsxcell.h @@ -54,11 +54,14 @@ public: Format * format() const; QString formula() const; + bool isDateTime() const; + QDateTime dateTime() const; + private: friend class Worksheet; friend class WorksheetPrivate; - Cell(const QVariant &data=QVariant(), DataType type=Blank, Format *format=0); + Cell(const QVariant &data=QVariant(), DataType type=Blank, Format *format=0, Worksheet *parent=0); CellPrivate * const d_ptr; }; diff --git a/src/xlsx/xlsxcell_p.h b/src/xlsx/xlsxcell_p.h index d980943..cdbd73c 100644 --- a/src/xlsx/xlsxcell_p.h +++ b/src/xlsx/xlsxcell_p.h @@ -39,6 +39,7 @@ public: Cell::DataType dataType; Format *format; + Worksheet *parent; Cell *q_ptr; }; diff --git a/src/xlsx/xlsxformat.cpp b/src/xlsx/xlsxformat.cpp index 87dd21a..6e08a9a 100755 --- a/src/xlsx/xlsxformat.cpp +++ b/src/xlsx/xlsxformat.cpp @@ -25,6 +25,7 @@ #include "xlsxformat.h" #include "xlsxformat_p.h" #include +#include #include QT_BEGIN_NAMESPACE_XLSX @@ -87,6 +88,7 @@ void Format::setNumberFormatIndex(int format) Q_D(Format); d->dirty = true; d->numberData.formatIndex = format; + d->numberData._valid = true; } /*! @@ -102,15 +104,39 @@ QString Format::numberFormat() const /*! * Set number \a format. + * http://office.microsoft.com/en-001/excel-help/create-a-custom-number-format-HP010342372.aspx */ void Format::setNumberFormat(const QString &format) { Q_D(Format); + if (format.isEmpty()) + return; d->dirty = true; d->numberData.formatString = format; d->numberData._valid = false; //formatIndex must be re-generated } +/*! + * Returns whether the number format is probably a dateTime or not + */ +bool Format::isDateTimeFormat() const +{ + Q_D(const Format); + if (d->numberData._valid && d->numberData.formatString.isEmpty()) { + int idx = d->numberData.formatIndex; + //Built in date time number index + if ((idx >= 15 && idx <= 22) || (idx >= 45 && idx <= 47)) + return true; + } else { + //Gauss from the number string + QString formatCode = d->numberData.formatString; + formatCode.remove(QRegularExpression(QStringLiteral("\\[(Green|White|Blue|Magenta|Yellow|Cyan|Red)\\]"))); + if (formatCode.contains(QRegularExpression(QStringLiteral("[dmhys]")))) + return true; + } + return false; +} + /*! * \internal */ diff --git a/src/xlsx/xlsxformat.h b/src/xlsx/xlsxformat.h index 548e2c6..d3efc02 100755 --- a/src/xlsx/xlsxformat.h +++ b/src/xlsx/xlsxformat.h @@ -31,6 +31,8 @@ #include #include +class FormatTest; + QT_BEGIN_NAMESPACE_XLSX class Styles; @@ -133,6 +135,7 @@ public: void setNumberFormatIndex(int format); QString numberFormat() const; void setNumberFormat(const QString &format); + bool isDateTimeFormat() const; int fontSize() const; void setFontSize(int size); @@ -210,6 +213,8 @@ private: friend class Styles; friend class Worksheet; friend class WorksheetPrivate; + friend class ::FormatTest; + Format(); bool numFmtIndexValid() const; diff --git a/src/xlsx/xlsxworksheet.cpp b/src/xlsx/xlsxworksheet.cpp index fb3de23..d1ec2e9 100755 --- a/src/xlsx/xlsxworksheet.cpp +++ b/src/xlsx/xlsxworksheet.cpp @@ -378,7 +378,7 @@ int Worksheet::writeString(int row, int column, const QString &value, Format *fo d->sharedStrings()->addSharedString(content); - d->cellTable[row][column] = QSharedPointer(new Cell(content, Cell::String, format)); + d->cellTable[row][column] = QSharedPointer(new Cell(content, Cell::String, format, this)); d->workbook->styles()->addFormat(format); return error; } @@ -396,7 +396,7 @@ int Worksheet::writeInlineString(int row, int column, const QString &value, Form error = -2; } - d->cellTable[row][column] = QSharedPointer(new Cell(value, Cell::InlineString, format)); + d->cellTable[row][column] = QSharedPointer(new Cell(value, Cell::InlineString, format, this)); d->workbook->styles()->addFormat(format); return error; } @@ -407,7 +407,7 @@ int Worksheet::writeNumeric(int row, int column, double value, Format *format) if (d->checkDimensions(row, column)) return -1; - d->cellTable[row][column] = QSharedPointer(new Cell(value, Cell::Numeric, format)); + d->cellTable[row][column] = QSharedPointer(new Cell(value, Cell::Numeric, format, this)); d->workbook->styles()->addFormat(format); return 0; } @@ -424,7 +424,7 @@ int Worksheet::writeFormula(int row, int column, const QString &content, Format if (formula.startsWith(QLatin1String("="))) formula.remove(0,1); - Cell *data = new Cell(result, Cell::Formula, format); + Cell *data = new Cell(result, Cell::Formula, format, this); data->d_ptr->formula = formula; d->cellTable[row][column] = QSharedPointer(data); d->workbook->styles()->addFormat(format); @@ -438,7 +438,7 @@ int Worksheet::writeBlank(int row, int column, Format *format) if (d->checkDimensions(row, column)) return -1; - d->cellTable[row][column] = QSharedPointer(new Cell(QVariant(), Cell::Blank, format)); + d->cellTable[row][column] = QSharedPointer(new Cell(QVariant(), Cell::Blank, format, this)); d->workbook->styles()->addFormat(format); return 0; @@ -450,7 +450,7 @@ int Worksheet::writeBool(int row, int column, bool value, Format *format) if (d->checkDimensions(row, column)) return -1; - d->cellTable[row][column] = QSharedPointer(new Cell(value, Cell::Boolean, format)); + d->cellTable[row][column] = QSharedPointer(new Cell(value, Cell::Boolean, format, this)); d->workbook->styles()->addFormat(format); return 0; @@ -469,7 +469,7 @@ int Worksheet::writeDateTime(int row, int column, const QDateTime &dt, Format *f double value = datetimeToNumber(dt, d->workbook->isDate1904()); - d->cellTable[row][column] = QSharedPointer(new Cell(value, Cell::Numeric, format)); + d->cellTable[row][column] = QSharedPointer(new Cell(value, Cell::Numeric, format, this)); d->workbook->styles()->addFormat(format); return 0; @@ -517,7 +517,7 @@ int Worksheet::writeHyperlink(int row, int column, const QUrl &url, Format *form //Write the hyperlink string as normal string. d->sharedStrings()->addSharedString(displayString); - d->cellTable[row][column] = QSharedPointer(new Cell(displayString, Cell::String, format)); + d->cellTable[row][column] = QSharedPointer(new Cell(displayString, Cell::String, format, this)); d->workbook->styles()->addFormat(format); //Store the hyperlink data in a separate table @@ -1124,6 +1124,7 @@ QSharedPointer WorksheetPrivate::readNumericCellData(XmlStreamReader &read void WorksheetPrivate::readSheetData(XmlStreamReader &reader) { + Q_Q(Worksheet); Q_ASSERT(reader.name() == QLatin1String("sheetData")); while(!(reader.name() == QLatin1String("sheetData") && reader.tokenType() == QXmlStreamReader::EndElement)) { @@ -1162,6 +1163,8 @@ void WorksheetPrivate::readSheetData(XmlStreamReader &reader) if (attributes.hasAttribute(QLatin1String("s"))) { int idx = attributes.value(QLatin1String("s")).toInt(); format = workbook->styles()->xfFormat(idx); + if (!format) + qDebug()<<"Invalid style index: "<incRefByStringIndex(sst_idx); QString value = sharedStrings()->getSharedString(sst_idx); - QSharedPointer data(new Cell(value ,Cell::String, format)); + QSharedPointer data(new Cell(value ,Cell::String, format, q)); cellTable[pos.x()][pos.y()] = QSharedPointer(data); } } @@ -1185,7 +1188,7 @@ void WorksheetPrivate::readSheetData(XmlStreamReader &reader) if (reader.tokenType() == QXmlStreamReader::StartElement) { if (reader.name() == QLatin1String("t")) { QString value = reader.readElementText(); - QSharedPointer data(new Cell(value, Cell::InlineString, format)); + QSharedPointer data(new Cell(value, Cell::InlineString, format, q)); cellTable[pos.x()][pos.y()] = data; } } @@ -1195,13 +1198,14 @@ void WorksheetPrivate::readSheetData(XmlStreamReader &reader) reader.readNextStartElement(); if (reader.name() == QLatin1String("v")) { QString value = reader.readElementText(); - QSharedPointer data(new Cell(value.toInt() ? true : false, Cell::Boolean, format)); + QSharedPointer data(new Cell(value.toInt() ? true : false, Cell::Boolean, format, q)); cellTable[pos.x()][pos.y()] = data; } } else if (type == QLatin1String("str")) { //formula type QSharedPointer data = readNumericCellData(reader); data->d_ptr->format = format; + data->d_ptr->parent = q; cellTable[pos.x()][pos.y()] = data; } else if (type == QLatin1String("e")) { //error type, such as #DIV/0! #NULL! #REF! etc @@ -1215,20 +1219,23 @@ void WorksheetPrivate::readSheetData(XmlStreamReader &reader) f_str = reader.readElementText(); } } - QSharedPointer data(new Cell(v_str, Cell::Error, format)); + QSharedPointer data(new Cell(v_str, Cell::Error, format, q)); if (!f_str.isEmpty()) data->d_ptr->formula = f_str; cellTable[pos.x()][pos.y()] = data; } else if (type == QLatin1String("n")) { QSharedPointer data = readNumericCellData(reader); data->d_ptr->format = format; + data->d_ptr->parent = q; cellTable[pos.x()][pos.y()] = data; } } else { //default is "n" QSharedPointer data = readNumericCellData(reader); data->d_ptr->format = format; - cellTable[pos.x()][pos.y()] = data; } + data->d_ptr->parent = q; + cellTable[pos.x()][pos.y()] = data; + } } } } @@ -1364,4 +1371,13 @@ SharedStrings *WorksheetPrivate::sharedStrings() const return workbook->sharedStrings(); } +/*! + * Return the workbook + */ +Workbook *Worksheet::workbook() const +{ + Q_D(const Worksheet); + return d->workbook; +} + QT_END_NAMESPACE_XLSX diff --git a/src/xlsx/xlsxworksheet.h b/src/xlsx/xlsxworksheet.h index 320e6bd..e33a210 100755 --- a/src/xlsx/xlsxworksheet.h +++ b/src/xlsx/xlsxworksheet.h @@ -80,6 +80,7 @@ public: QString sheetName() const; void setSheetName(const QString &sheetName); + Workbook *workbook() const; ~Worksheet(); private: friend class Package; diff --git a/tests/auto/auto.pro b/tests/auto/auto.pro index 38b732a..e37724c 100644 --- a/tests/auto/auto.pro +++ b/tests/auto/auto.pro @@ -8,4 +8,5 @@ SUBDIRS=\ propsapp \ document \ sharedstrings \ - styles + styles \ + format diff --git a/tests/auto/document/tst_documenttest.cpp b/tests/auto/document/tst_documenttest.cpp index 37ab5dc..fafc404 100644 --- a/tests/auto/document/tst_documenttest.cpp +++ b/tests/auto/document/tst_documenttest.cpp @@ -20,6 +20,7 @@ private Q_SLOTS: void testReadWriteBool(); void testReadWriteBlank(); void testReadWriteFormula(); + void testReadWriteDateTime(); }; DocumentTest::DocumentTest() @@ -156,6 +157,46 @@ void DocumentTest::testReadWriteFormula() QFile::remove("test.xlsx"); } +void DocumentTest::testReadWriteDateTime() +{ + Document xlsx1; + QDateTime dt(QDate(2012, 11, 12), QTime(6, 0), Qt::UTC); + + xlsx1.write("A1", dt); + + Format *format = xlsx1.createFormat(); + format->setFontColor(Qt::blue); + format->setBorderStyle(Format::BorderDashDotDot); + format->setFillPattern(Format::PatternSolid); + xlsx1.write("A2", dt, format); + + Format *format3 = xlsx1.createFormat(); + format3->setNumberFormat("dd/mm/yyyy"); + xlsx1.write("A3", dt, format3); + + xlsx1.saveAs("test.xlsx"); + + Document xlsx2("test.xlsx"); + + QCOMPARE(xlsx2.cellAt("A1")->dataType(), Cell::Numeric); + QCOMPARE(xlsx2.cellAt("A1")->isDateTime(), true); + QCOMPARE(xlsx2.cellAt("A1")->dateTime(), dt); + + QCOMPARE(xlsx2.cellAt("A2")->dataType(), Cell::Numeric); +// QCOMPARE(xlsx2.cellAt("A2")->isDateTime(), true); +// QCOMPARE(xlsx2.cellAt("A2")->dateTime(), dt); + +// QCOMPARE(xlsx2.cellAt("A3")->dataType(), Cell::Numeric); +// QVERIFY(xlsx2.cellAt("A3")->format()!=0); +// qDebug()<format()->numberFormat(); +// QCOMPARE(xlsx2.cellAt("A3")->isDateTime(), true); +// QCOMPARE(xlsx2.cellAt("A3")->dateTime(), dt); +// QCOMPARE(xlsx2.cellAt("A3")->format()->numberFormat(), QString("dd/mm/yyyy")); + + QFile::remove("test.xlsx"); + +} + QTEST_APPLESS_MAIN(DocumentTest) #include "tst_documenttest.moc" diff --git a/tests/auto/format/format.pro b/tests/auto/format/format.pro new file mode 100644 index 0000000..9b13b94 --- /dev/null +++ b/tests/auto/format/format.pro @@ -0,0 +1,12 @@ +QT += testlib xlsx # xlsx-private +CONFIG += testcase +DEFINES += XLSX_TEST + +TARGET = tst_format +CONFIG += console +CONFIG -= app_bundle + +TEMPLATE = app + +SOURCES += tst_formattest.cpp +DEFINES += SRCDIR=\\\"$$PWD/\\\" diff --git a/tests/auto/format/tst_formattest.cpp b/tests/auto/format/tst_formattest.cpp new file mode 100644 index 0000000..9c9599e --- /dev/null +++ b/tests/auto/format/tst_formattest.cpp @@ -0,0 +1,52 @@ +#include "xlsxformat.h" +#include +#include + +QTXLSX_USE_NAMESPACE + +class FormatTest : public QObject +{ + Q_OBJECT + +public: + FormatTest(); + +private Q_SLOTS: + void testDateTimeFormat(); + void testDateTimeFormat_data(); +}; + +FormatTest::FormatTest() +{ +} + +void FormatTest::testDateTimeFormat() +{ + QFETCH(QString, data); + QFETCH(bool, res); + + Format fmt; + fmt.setNumberFormat(data); + + QCOMPARE(fmt.isDateTimeFormat(), res); +} + +void FormatTest::testDateTimeFormat_data() +{ + QTest::addColumn("data"); + QTest::addColumn("res"); + + QTest::newRow("0") << QString("yyyy-mm-dd")<" + "" + "" + ""; + + QXlsx::Styles styles(true); + QXlsx::XmlStreamReader reader(xmlData); + reader.readNextStartElement();//So current node is numFmts + styles.readNumFmts(reader); + + QCOMPARE(styles.m_customNumFmts.size(), 2); +} + QTEST_APPLESS_MAIN(StylesTest) #include "tst_stylestest.moc"