diff --git a/examples/xlsx/datavalidation/datavalidation.pro b/examples/xlsx/datavalidation/datavalidation.pro new file mode 100644 index 0000000..441bc66 --- /dev/null +++ b/examples/xlsx/datavalidation/datavalidation.pro @@ -0,0 +1,9 @@ +TARGET = datavalidation + +#include(../../../src/xlsx/qtxlsx.pri) +QT+=xlsx + +CONFIG += console +CONFIG -= app_bundle + +SOURCES += main.cpp diff --git a/examples/xlsx/datavalidation/main.cpp b/examples/xlsx/datavalidation/main.cpp new file mode 100644 index 0000000..a2ab4f2 --- /dev/null +++ b/examples/xlsx/datavalidation/main.cpp @@ -0,0 +1,22 @@ +#include +#include "xlsxdocument.h" +#include "xlsxdatavalidation.h" + +QTXLSX_USE_NAMESPACE + +int main() +{ + Document xlsx; + xlsx.write("A1", "A2 and A3:E5 only accept the number between 33 and 99"); + + //![1] + DataValidation validation(DataValidation::Whole, DataValidation::Between, "33", "99"); + validation.addRange("A2"); + validation.addRange("A3:E5"); + validation.setPromptMessage("Please Input Integer between 33 and 99"); + xlsx.addDataValidation(validation); + //![1] + + xlsx.save(); + return 0; +} diff --git a/examples/xlsx/xlsx.pro b/examples/xlsx/xlsx.pro index 0ac7146..37ab891 100644 --- a/examples/xlsx/xlsx.pro +++ b/examples/xlsx/xlsx.pro @@ -5,5 +5,6 @@ SUBDIRS = hello style \ mergecells \ rowcolumn \ numberformat \ - readwrite + readwrite \ + datavalidation diff --git a/src/xlsx/qtxlsx.pri b/src/xlsx/qtxlsx.pri index df2a548..1260a5c 100755 --- a/src/xlsx/qtxlsx.pri +++ b/src/xlsx/qtxlsx.pri @@ -28,7 +28,10 @@ HEADERS += $$PWD/xlsxdocpropscore_p.h \ $$PWD/xlsxdocument.h \ $$PWD/xlsxdocument_p.h \ $$PWD/xlsxcell.h \ - $$PWD/xlsxcell_p.h + $$PWD/xlsxcell_p.h \ + $$PWD/xlsxdatavalidation.h \ + $$PWD/xlsxdatavalidation_p.h \ + $$PWD/xlsxcellrange.h SOURCES += $$PWD/xlsxdocpropscore.cpp \ $$PWD/xlsxdocpropsapp.cpp \ @@ -48,4 +51,6 @@ SOURCES += $$PWD/xlsxdocpropscore.cpp \ $$PWD/xlsxxmlreader.cpp \ $$PWD/xlsxzipreader.cpp \ $$PWD/xlsxdocument.cpp \ - $$PWD/xlsxcell.cpp + $$PWD/xlsxcell.cpp \ + $$PWD/xlsxdatavalidation.cpp \ + $$PWD/xlsxcellrange.cpp diff --git a/src/xlsx/xlsxcell_p.h b/src/xlsx/xlsxcell_p.h index cdbd73c..54ff6c9 100644 --- a/src/xlsx/xlsxcell_p.h +++ b/src/xlsx/xlsxcell_p.h @@ -23,6 +23,8 @@ ** ****************************************************************************/ #ifndef XLSXCELL_P_H +#define XLSXCELL_P_H + #include "xlsxglobal.h" #include "xlsxcell.h" @@ -44,6 +46,5 @@ public: }; QT_END_NAMESPACE_XLSX -#define XLSXCELL_P_H #endif // XLSXCELL_P_H diff --git a/src/xlsx/xlsxcellrange.cpp b/src/xlsx/xlsxcellrange.cpp new file mode 100644 index 0000000..010bb6c --- /dev/null +++ b/src/xlsx/xlsxcellrange.cpp @@ -0,0 +1,119 @@ +/**************************************************************************** +** Copyright (c) 2013 Debao Zhang +** All right reserved. +** +** Permission is hereby granted, free of charge, to any person obtaining +** a copy of this software and associated documentation files (the +** "Software"), to deal in the Software without restriction, including +** without limitation the rights to use, copy, modify, merge, publish, +** distribute, sublicense, and/or sell copies of the Software, and to +** permit persons to whom the Software is furnished to do so, subject to +** the following conditions: +** +** The above copyright notice and this permission notice shall be +** included in all copies or substantial portions of the Software. +** +** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +** EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +** MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +** NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +** LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +** OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +** WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +** +****************************************************************************/ +#include "xlsxcellrange.h" +#include "xlsxutility_p.h" +#include +#include +#include +QT_BEGIN_NAMESPACE_XLSX +/*! + \class CellRange + + \brief The CellRange class provides a way to + + \inmodule QtXlsx + + The CellRange class stores the top left and bottom + right rows and columns of a range in a worksheet. +*/ + +/*! + Constructs an range, i.e. a range + whose rowCount() and columnCount() are 0. +*/ +CellRange::CellRange() + : top(-1), left(-1), bottom(-2), right(-2) +{ +} + +/*! + Constructs the range from the given \a top, \a + left, \a bottom and \a right rows and columns. + + \sa topRow(), leftColumn(), bottomRow(), rightColumn() +*/ +CellRange::CellRange(int top, int left, int bottom, int right) + : top(top), left(left), bottom(bottom), right(right) +{ +} + +/*! + \overload + Constructs the range form the given \a range string. +*/ +CellRange::CellRange(const QString &range) +{ + QStringList rs = range.split(QLatin1Char(':')); + if (rs.size() == 2) { + QPoint start = xl_cell_to_rowcol(rs[0]); + QPoint end = xl_cell_to_rowcol(rs[1]); + top = start.x(); + left = start.y(); + bottom = end.x(); + right = end.y(); + } else { + QPoint p = xl_cell_to_rowcol(rs[0]); + top = p.x(); + left = p.y(); + bottom = p.x(); + right = p.y(); + } +} + +/*! + Constructs a the range by copying the given \a + other range. +*/ +CellRange::CellRange(const CellRange &other) + : top(other.top), left(other.left), bottom(other.bottom), right(other.right) +{ +} + +/*! + Destroys the range. +*/ +CellRange::~CellRange() +{ +} + +/*! + Convert the range to string notation, such as "A1:B5". +*/ +QString CellRange::toString() const +{ + if (left == -1 || top == -1) + return QString(); + + if (left == right && top == bottom) { + //Single cell + return xl_rowcol_to_cell(top, left); + } + + QString cell_1 = xl_rowcol_to_cell(top, left); + QString cell_2 = xl_rowcol_to_cell(bottom, right); + return cell_1 + QLatin1String(":") + cell_2; +} + +QT_END_NAMESPACE_XLSX diff --git a/src/xlsx/xlsxcellrange.h b/src/xlsx/xlsxcellrange.h new file mode 100644 index 0000000..c3e70dd --- /dev/null +++ b/src/xlsx/xlsxcellrange.h @@ -0,0 +1,54 @@ +/**************************************************************************** +** Copyright (c) 2013 Debao Zhang +** All right reserved. +** +** Permission is hereby granted, free of charge, to any person obtaining +** a copy of this software and associated documentation files (the +** "Software"), to deal in the Software without restriction, including +** without limitation the rights to use, copy, modify, merge, publish, +** distribute, sublicense, and/or sell copies of the Software, and to +** permit persons to whom the Software is furnished to do so, subject to +** the following conditions: +** +** The above copyright notice and this permission notice shall be +** included in all copies or substantial portions of the Software. +** +** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +** EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +** MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +** NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +** LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +** OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +** WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +** +****************************************************************************/ +#ifndef QXLSX_XLSXCELLRANGE_H +#define QXLSX_XLSXCELLRANGE_H +#include "xlsxglobal.h" + +QT_BEGIN_NAMESPACE_XLSX + +class Q_XLSX_EXPORT CellRange +{ +public: + CellRange(); + CellRange(int firstRow, int firstColumn, int lastRow, int lastColumn); + CellRange(const QString &range); + CellRange(const CellRange &other); + ~CellRange(); + + QString toString() const; + inline int firstRow() const { return top; } + inline int lastRow() const { return bottom; } + inline int firstColumn() const { return left; } + inline int lastColumn() const { return right; } + inline int rowCount() const { return bottom - top + 1; } + inline int columnCount() const { return right - left + 1; } + +private: + int top, left, bottom, right; +}; + +QT_END_NAMESPACE_XLSX + +#endif // QXLSX_XLSXCELLRANGE_H diff --git a/src/xlsx/xlsxdatavalidation.cpp b/src/xlsx/xlsxdatavalidation.cpp new file mode 100644 index 0000000..bc89079 --- /dev/null +++ b/src/xlsx/xlsxdatavalidation.cpp @@ -0,0 +1,302 @@ +/**************************************************************************** +** Copyright (c) 2013 Debao Zhang +** All right reserved. +** +** Permission is hereby granted, free of charge, to any person obtaining +** a copy of this software and associated documentation files (the +** "Software"), to deal in the Software without restriction, including +** without limitation the rights to use, copy, modify, merge, publish, +** distribute, sublicense, and/or sell copies of the Software, and to +** permit persons to whom the Software is furnished to do so, subject to +** the following conditions: +** +** The above copyright notice and this permission notice shall be +** included in all copies or substantial portions of the Software. +** +** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +** EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +** MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +** NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +** LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +** OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +** WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +** +****************************************************************************/ + +#include "xlsxdatavalidation.h" +#include "xlsxdatavalidation_p.h" +#include "xlsxworksheet.h" +#include "xlsxcellrange.h" + +QT_BEGIN_NAMESPACE_XLSX + +DataValidationPrivate::DataValidationPrivate() + :validationType(DataValidation::None), validationOperator(DataValidation::Between) + , errorStyle(DataValidation::Stop), allowBlank(false), isPromptMessageVisible(true) + , isErrorMessageVisible(true) +{ + +} + +DataValidationPrivate::DataValidationPrivate(DataValidation::ValidationType type, DataValidation::ValidationOperator op, const QString &formula1, const QString &formula2, bool allowBlank) + :validationType(type), validationOperator(op) + , errorStyle(DataValidation::Stop), allowBlank(allowBlank), isPromptMessageVisible(true) + , isErrorMessageVisible(true), formula1(formula1), formula2(formula2) +{ + +} + +DataValidationPrivate::DataValidationPrivate(const DataValidationPrivate &other) + :QSharedData(other) + , validationType(DataValidation::None), validationOperator(DataValidation::Between) + , errorStyle(DataValidation::Stop), allowBlank(false), isPromptMessageVisible(true) + , isErrorMessageVisible(true) +{ + +} + +DataValidationPrivate::~DataValidationPrivate() +{ + +} + +/*! + * \class DataValidation + * + * The data validation can be applied to a single cell or a range of cells. + */ + +/*! + * \enum DataValidation::ValidationType + * + * The enum type defines the type of data that you wish to validate. + * + * \value None the type of data is unrestricted. This is the same as not applying a data validation. + * \value Whole restricts the cell to integer values. Means "Whole number"? + * \value Decimal restricts the cell to decimal values. + * \value List restricts the cell to a set of user specified values. + * \value Date restricts the cell to date values. + * \value Time restricts the cell to time values. + * \value TextLength restricts the cell data based on an integer string length. + * \value Custom restricts the cell based on an external Excel formula that returns a true/false value. + */ + +/*! + * \enum DataValidation::ValidationOperator + * + * The enum type defines the criteria by which the data in the + * cell is validated + * + * \value Between + * \value NotBetween + * \value Equal + * \value NotEqual + * \value LessThan + * \value LessThanOrEqual + * \value GreaterThan + * \value GreaterThanOrEqual + */ + +/*! + * \enum DataValidation::ErrorStyle + * + * The enum type defines the type of error dialog that + * is displayed. + * + * \value Error + * \value Warning + * \value Information + */ + +/*! + * Construct a data validation object + */ +DataValidation::DataValidation(ValidationType type, ValidationOperator op, const QString &formula1, const QString &formula2, bool allowBlank) + :d(new DataValidationPrivate(type, op, formula1, formula2, allowBlank)) +{ + +} + +/*! + Construct a data validation object +*/ +DataValidation::DataValidation() + :d(new DataValidationPrivate()) +{ + +} + +/*! + \internal +*/ +DataValidation::DataValidation(const DataValidation &other) + :d(other.d) +{ + +} + +/*! + * Destroy the object. + */ +DataValidation::~DataValidation() +{ +} + +DataValidation::ValidationType DataValidation::validationType() const +{ + return d->validationType; +} + +DataValidation::ValidationOperator DataValidation::validationOperator() const +{ + return d->validationOperator; +} + +DataValidation::ErrorStyle DataValidation::errorStyle() const +{ + return d->errorStyle; +} + +QString DataValidation::formula1() const +{ + return d->formula1; +} + +QString DataValidation::formula2() const +{ + return d->formula2; +} + +bool DataValidation::allowBlank() const +{ + return d->allowBlank; +} + +QString DataValidation::errorMessage() const +{ + return d->errorMessage; +} + +QString DataValidation::errorMessageTitle() const +{ + return d->errorMessageTitle; +} + +QString DataValidation::promptMessage() const +{ + return d->promptMessage; +} + +QString DataValidation::promptMessageTitle() const +{ + return d->promptMessageTitle; +} + +bool DataValidation::isPromptMessageVisible() const +{ + return d->isPromptMessageVisible; +} + +bool DataValidation::isErrorMessageVisible() const +{ + return d->isErrorMessageVisible; +} + +QList DataValidation::ranges() const +{ + return d->ranges; +} + +void DataValidation::setValidationType(DataValidation::ValidationType type) +{ + d->validationType = type; +} + +void DataValidation::setValidationOperator(DataValidation::ValidationOperator op) +{ + d->validationOperator = op; +} + +void DataValidation::setErrorStyle(DataValidation::ErrorStyle es) +{ + d->errorStyle = es; +} + +void DataValidation::setFormula1(const QString &formula) +{ + d->formula1 = formula; +} + +void DataValidation::setFormula2(const QString &formula) +{ + d->formula2 = formula; +} + +void DataValidation::setErrorMessage(const QString &error, const QString &title) +{ + d->errorMessage = error; + d->errorMessageTitle = title; +} + +void DataValidation::setPromptMessage(const QString &prompt, const QString &title) +{ + d->promptMessage = prompt; + d->promptMessageTitle = title; +} + +void DataValidation::setAllowBlank(bool enable) +{ + d->allowBlank = enable; +} + +void DataValidation::setPromptMessageVisible(bool visible) +{ + d->isPromptMessageVisible = visible; +} + +void DataValidation::setErrorMessageVisible(bool visible) +{ + d->isErrorMessageVisible = visible; +} + +/*! + Add the \a cell which the DataValidation will apply to. + */ +void DataValidation::addCell(const QString &cell) +{ + d->ranges.append(CellRange(cell)); +} + +/*! + \overload + */ +void DataValidation::addCell(int row, int col) +{ + d->ranges.append(CellRange(row, col, row, col)); +} + +/*! + Add the \a range which the DataValidation will apply to. + */ +void DataValidation::addRange(const QString &range) +{ + d->ranges.append(CellRange(range)); +} + +/*! + \overload + */ +void DataValidation::addRange(int firstRow, int firstCol, int lastRow, int lastCol) +{ + d->ranges.append(CellRange(firstRow, firstCol, lastRow, lastCol)); +} + +/*! + \overload + */ +void DataValidation::addRange(const CellRange &range) +{ + d->ranges.append(range); +} + +QT_END_NAMESPACE_XLSX diff --git a/src/xlsx/xlsxdatavalidation.h b/src/xlsx/xlsxdatavalidation.h new file mode 100644 index 0000000..bd7ab3f --- /dev/null +++ b/src/xlsx/xlsxdatavalidation.h @@ -0,0 +1,118 @@ +/**************************************************************************** +** Copyright (c) 2013 Debao Zhang +** All right reserved. +** +** Permission is hereby granted, free of charge, to any person obtaining +** a copy of this software and associated documentation files (the +** "Software"), to deal in the Software without restriction, including +** without limitation the rights to use, copy, modify, merge, publish, +** distribute, sublicense, and/or sell copies of the Software, and to +** permit persons to whom the Software is furnished to do so, subject to +** the following conditions: +** +** The above copyright notice and this permission notice shall be +** included in all copies or substantial portions of the Software. +** +** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +** EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +** MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +** NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +** LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +** OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +** WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +** +****************************************************************************/ +#ifndef QXLSX_XLSXDATAVALIDATION_H +#define QXLSX_XLSXDATAVALIDATION_H + +#include "xlsxglobal.h" +#include +#include +#include + +QT_BEGIN_NAMESPACE_XLSX + +class Worksheet; +class CellRange; + +class DataValidationPrivate; +class Q_XLSX_EXPORT DataValidation +{ +public: + enum ValidationType + { + None, + Whole, + Decimal, + List, + Date, + Time, + TextLength, + Custom + }; + + enum ValidationOperator + { + Between, + NotBetween, + Equal, + NotEqual, + LessThan, + LessThanOrEqual, + GreaterThan, + GreaterThanOrEqual + }; + + enum ErrorStyle + { + Stop, + Warning, + Information + }; + + DataValidation(); + DataValidation(ValidationType type, ValidationOperator op=Between, const QString &formula1=QString() + , const QString &formula2=QString(), bool allowBlank=false); + DataValidation(const DataValidation &other); + ~DataValidation(); + + ValidationType validationType() const; + ValidationOperator validationOperator() const; + ErrorStyle errorStyle() const; + QString formula1() const; + QString formula2() const; + bool allowBlank() const; + QString errorMessage() const; + QString errorMessageTitle() const; + QString promptMessage() const; + QString promptMessageTitle() const; + bool isPromptMessageVisible() const; + bool isErrorMessageVisible() const; + QList ranges() const; + + void setValidationType(ValidationType type); + void setValidationOperator(ValidationOperator op); + void setErrorStyle(ErrorStyle es); + void setFormula1(const QString &formula); + void setFormula2(const QString &formula); + void setErrorMessage(const QString &error, const QString &title=QString()); + void setPromptMessage(const QString &prompt, const QString &title=QString()); + void setAllowBlank(bool enable); + void setPromptMessageVisible(bool visible); + void setErrorMessageVisible(bool visible); + + void addCell(const QString &cell); + void addCell(int row, int col); + void addRange(const QString &range); + void addRange(int firstRow, int firstCol, int lastRow, int lastCol); + void addRange(const CellRange &range); + + //needed by QSharedDataPointer!! + DataValidation &operator=(const DataValidation &other); +private: + QSharedDataPointer d; +}; + +QT_END_NAMESPACE_XLSX + +#endif // QXLSX_XLSXDATAVALIDATION_H diff --git a/src/xlsx/xlsxdatavalidation_p.h b/src/xlsx/xlsxdatavalidation_p.h new file mode 100644 index 0000000..967cb8a --- /dev/null +++ b/src/xlsx/xlsxdatavalidation_p.h @@ -0,0 +1,57 @@ +/**************************************************************************** +** Copyright (c) 2013 Debao Zhang +** All right reserved. +** +** Permission is hereby granted, free of charge, to any person obtaining +** a copy of this software and associated documentation files (the +** "Software"), to deal in the Software without restriction, including +** without limitation the rights to use, copy, modify, merge, publish, +** distribute, sublicense, and/or sell copies of the Software, and to +** permit persons to whom the Software is furnished to do so, subject to +** the following conditions: +** +** The above copyright notice and this permission notice shall be +** included in all copies or substantial portions of the Software. +** +** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +** EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +** MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +** NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +** LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +** OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +** WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +** +****************************************************************************/ + +#ifndef XLSXDATAVALIDATION_P_H +#define XLSXDATAVALIDATION_P_H +#include "xlsxdatavalidation.h" +#include + +QT_BEGIN_NAMESPACE_XLSX + +class Q_XLSX_EXPORT DataValidationPrivate : public QSharedData +{ +public: + DataValidationPrivate(); + DataValidationPrivate(DataValidation::ValidationType type, DataValidation::ValidationOperator op, const QString &formula1, const QString &formula2, bool allowBlank); + DataValidationPrivate(const DataValidationPrivate &other); + ~DataValidationPrivate(); + + DataValidation::ValidationType validationType; + DataValidation::ValidationOperator validationOperator; + DataValidation::ErrorStyle errorStyle; + bool allowBlank; + bool isPromptMessageVisible; + bool isErrorMessageVisible; + QString formula1; + QString formula2; + QString errorMessage; + QString errorMessageTitle; + QString promptMessage; + QString promptMessageTitle; + QList ranges; +}; + +QT_END_NAMESPACE_XLSX +#endif // XLSXDATAVALIDATION_P_H diff --git a/src/xlsx/xlsxdocument.cpp b/src/xlsx/xlsxdocument.cpp index a59688b..e9bf5cd 100644 --- a/src/xlsx/xlsxdocument.cpp +++ b/src/xlsx/xlsxdocument.cpp @@ -182,6 +182,16 @@ bool Document::setColumn(int colFirst, int colLast, double width, Format *format return currentWorksheet()->setColumn(colFirst, colLast, width, format, hidden); } +/*! + * \brief Add a data validation rule for current worksheet + * \param validation + * \return + */ +bool Document::addDataValidation(const DataValidation &validation) +{ + return currentWorksheet()->addDataValidation(validation); +} + /*! * Returns a Cell object based on the given \a pos. */ diff --git a/src/xlsx/xlsxdocument.h b/src/xlsx/xlsxdocument.h index c651eab..796aaf6 100644 --- a/src/xlsx/xlsxdocument.h +++ b/src/xlsx/xlsxdocument.h @@ -39,6 +39,7 @@ class Worksheet; class Package; class Format; class Cell; +class DataValidation; class DocumentPrivate; class Q_XLSX_EXPORT Document : public QObject @@ -60,6 +61,7 @@ public: int unmergeCells(const QString &range); bool setRow(int row, double height, Format* format=0, bool hidden=false); bool setColumn(int colFirst, int colLast, double width, Format* format=0, bool hidden=false); + bool addDataValidation(const DataValidation &validation); Cell *cellAt(const QString &cell) const; Cell *cellAt(int row, int col) const; diff --git a/src/xlsx/xlsxworksheet.cpp b/src/xlsx/xlsxworksheet.cpp index a11c5f3..779d53f 100755 --- a/src/xlsx/xlsxworksheet.cpp +++ b/src/xlsx/xlsxworksheet.cpp @@ -34,6 +34,7 @@ #include "xlsxstyles_p.h" #include "xlsxcell.h" #include "xlsxcell_p.h" +#include "xlsxcellrange.h" #include #include @@ -526,6 +527,16 @@ int Worksheet::writeHyperlink(int row, int column, const QUrl &url, Format *form return error; } +bool Worksheet::addDataValidation(const DataValidation &validation) +{ + Q_D(Worksheet); + if (validation.ranges().isEmpty() || validation.validationType()==DataValidation::None) + return false; + + d->dataValidationsList.append(validation); + return true; +} + int Worksheet::insertImage(int row, int column, const QImage &image, const QPointF &offset, double xScale, double yScale) { Q_D(Worksheet); @@ -673,6 +684,7 @@ void Worksheet::saveToXmlFile(QIODevice *device) writer.writeEndElement();//sheetData d->writeMergeCells(writer); + d->writeDataValidation(writer); d->writeHyperlinks(writer); d->writeDrawings(writer); @@ -791,6 +803,82 @@ void WorksheetPrivate::writeMergeCells(XmlStreamWriter &writer) writer.writeEndElement(); //mergeCells } +void WorksheetPrivate::writeDataValidation(XmlStreamWriter &writer) +{ + if (dataValidationsList.isEmpty()) + return; + + static QMap typeMap; + static QMap opMap; + static QMap esMap; + if (typeMap.isEmpty()) { + typeMap.insert(DataValidation::None, QStringLiteral("none")); + typeMap.insert(DataValidation::Whole, QStringLiteral("whole")); + typeMap.insert(DataValidation::Decimal, QStringLiteral("decimal")); + typeMap.insert(DataValidation::List, QStringLiteral("list")); + typeMap.insert(DataValidation::Date, QStringLiteral("date")); + typeMap.insert(DataValidation::Time, QStringLiteral("time")); + typeMap.insert(DataValidation::TextLength, QStringLiteral("textLength")); + typeMap.insert(DataValidation::Custom, QStringLiteral("custom")); + + opMap.insert(DataValidation::Between, QStringLiteral("between")); + opMap.insert(DataValidation::NotBetween, QStringLiteral("notBetween")); + opMap.insert(DataValidation::Equal, QStringLiteral("equal")); + opMap.insert(DataValidation::NotEqual, QStringLiteral("notEqual")); + opMap.insert(DataValidation::LessThan, QStringLiteral("lessThan")); + opMap.insert(DataValidation::LessThanOrEqual, QStringLiteral("lessThanOrEqual")); + opMap.insert(DataValidation::GreaterThan, QStringLiteral("greaterThan")); + opMap.insert(DataValidation::GreaterThanOrEqual, QStringLiteral("greaterThanOrEqual")); + + esMap.insert(DataValidation::Stop, QStringLiteral("stop")); + esMap.insert(DataValidation::Warning, QStringLiteral("warning")); + esMap.insert(DataValidation::Information, QStringLiteral("information")); + } + + writer.writeStartElement(QStringLiteral("dataValidations")); + writer.writeAttribute(QStringLiteral("count"), QString::number(dataValidationsList.size())); + + foreach (DataValidation validation, dataValidationsList) { + writer.writeStartElement(QStringLiteral("dataValidation")); + if (validation.validationType() != DataValidation::None) + writer.writeAttribute(QStringLiteral("type"), typeMap[validation.validationType()]); + if (validation.errorStyle() != DataValidation::Stop) + writer.writeAttribute(QStringLiteral("errorStyle"), esMap[validation.errorStyle()]); + if (validation.validationOperator() != DataValidation::Between) + writer.writeAttribute(QStringLiteral("operator"), opMap[validation.validationOperator()]); + if (validation.allowBlank()) + writer.writeAttribute(QStringLiteral("allowBlank"), QStringLiteral("1")); +// if (validation.dropDownVisible()) +// writer.writeAttribute(QStringLiteral("showDropDown"), QStringLiteral("1")); + if (validation.isPromptMessageVisible()) + writer.writeAttribute(QStringLiteral("showInputMessage"), QStringLiteral("1")); + if (validation.isErrorMessageVisible()) + writer.writeAttribute(QStringLiteral("showErrorMessage"), QStringLiteral("1")); + if (!validation.errorMessageTitle().isEmpty()) + writer.writeAttribute(QStringLiteral("errorTitle"), validation.errorMessageTitle()); + if (!validation.errorMessage().isEmpty()) + writer.writeAttribute(QStringLiteral("error"), validation.errorMessage()); + if (!validation.promptMessageTitle().isEmpty()) + writer.writeAttribute(QStringLiteral("promptTitle"), validation.promptMessageTitle()); + if (!validation.promptMessage().isEmpty()) + writer.writeAttribute(QStringLiteral("prompt"), validation.promptMessage()); + + QStringList sqref; + foreach (CellRange range, validation.ranges()) + sqref.append(range.toString()); + writer.writeAttribute(QStringLiteral("sqref"), sqref.join(QLatin1Char(' '))); + + if (!validation.formula1().isEmpty()) + writer.writeTextElement(QStringLiteral("formula1"), validation.formula1()); + if (!validation.formula2().isEmpty()) + writer.writeTextElement(QStringLiteral("formula2"), validation.formula2()); + + writer.writeEndElement(); //dataValidation + } + + writer.writeEndElement(); //dataValidations +} + void WorksheetPrivate::writeHyperlinks(XmlStreamWriter &writer) { if (urlTable.isEmpty()) @@ -1374,6 +1462,116 @@ void WorksheetPrivate::readMergeCells(XmlStreamReader &reader) qDebug("read merge cells error"); } +void WorksheetPrivate::readDataValidations(XmlStreamReader &reader) +{ + Q_ASSERT(reader.name() == QLatin1String("dataValidations")); + QXmlStreamAttributes attributes = reader.attributes(); + int count = attributes.value(QLatin1String("count")).toInt(); + + while(!(reader.name() == QLatin1String("dataValidations") + && reader.tokenType() == QXmlStreamReader::EndElement)) { + reader.readNextStartElement(); + if (reader.tokenType() == QXmlStreamReader::StartElement + && reader.name() == QLatin1String("dataValidation")) { + readDataValidation(reader); + } + } + + if (dataValidationsList.size() != count) + qDebug("read data validation error"); +} + +void WorksheetPrivate::readDataValidation(XmlStreamReader &reader) +{ + Q_ASSERT(reader.name() == QLatin1String("dataValidation")); + + static QMap typeMap; + static QMap opMap; + static QMap esMap; + if (typeMap.isEmpty()) { + typeMap.insert(QStringLiteral("none"), DataValidation::None); + typeMap.insert(QStringLiteral("whole"), DataValidation::Whole); + typeMap.insert(QStringLiteral("decimal"), DataValidation::Decimal); + typeMap.insert(QStringLiteral("list"), DataValidation::List); + typeMap.insert(QStringLiteral("date"), DataValidation::Date); + typeMap.insert(QStringLiteral("time"), DataValidation::Time); + typeMap.insert(QStringLiteral("textLength"), DataValidation::TextLength); + typeMap.insert(QStringLiteral("custom"), DataValidation::Custom); + + opMap.insert(QStringLiteral("between"), DataValidation::Between); + opMap.insert(QStringLiteral("notBetween"), DataValidation::NotBetween); + opMap.insert(QStringLiteral("equal"), DataValidation::Equal); + opMap.insert(QStringLiteral("notEqual"), DataValidation::NotEqual); + opMap.insert(QStringLiteral("lessThan"), DataValidation::LessThan); + opMap.insert(QStringLiteral("lessThanOrEqual"), DataValidation::LessThanOrEqual); + opMap.insert(QStringLiteral("greaterThan"), DataValidation::GreaterThan); + opMap.insert(QStringLiteral("greaterThanOrEqual"), DataValidation::GreaterThanOrEqual); + + esMap.insert(QStringLiteral("stop"), DataValidation::Stop); + esMap.insert(QStringLiteral("warning"), DataValidation::Warning); + esMap.insert(QStringLiteral("information"), DataValidation::Information); + } + + DataValidation validation; + QXmlStreamAttributes attrs = reader.attributes(); + + QString sqref = attrs.value(QLatin1String("sqref")).toString(); + foreach (QString range, sqref.split(QLatin1Char(' '))) + validation.addRange(range); + + if (attrs.hasAttribute(QLatin1String("type"))) { + QString t = attrs.value(QLatin1String("type")).toString(); + validation.setValidationType(typeMap.contains(t) ? typeMap[t] : DataValidation::None); + } + if (attrs.hasAttribute(QLatin1String("errorStyle"))) { + QString es = attrs.value(QLatin1String("errorStyle")).toString(); + validation.setErrorStyle(esMap.contains(es) ? esMap[es] : DataValidation::Stop); + } + if (attrs.hasAttribute(QLatin1String("operator"))) { + QString op = attrs.value(QLatin1String("operator")).toString(); + validation.setValidationOperator(opMap.contains(op) ? opMap[op] : DataValidation::Between); + } + if (attrs.hasAttribute(QLatin1String("allowBlank"))) { + validation.setAllowBlank(true); + } else { + validation.setAllowBlank(false); + } + if (attrs.hasAttribute(QLatin1String("showInputMessage"))) { + validation.setPromptMessageVisible(true); + } else { + validation.setPromptMessageVisible(false); + } + if (attrs.hasAttribute(QLatin1String("showErrorMessage"))) { + validation.setErrorMessageVisible(true); + } else { + validation.setErrorMessageVisible(false); + } + + QString et = attrs.value(QLatin1String("errorTitle")).toString(); + QString e = attrs.value(QLatin1String("error")).toString(); + if (!e.isEmpty() || !et.isEmpty()) + validation.setErrorMessage(e, et); + + QString pt = attrs.value(QLatin1String("promptTitle")).toString(); + QString p = attrs.value(QLatin1String("prompt")).toString(); + if (!p.isEmpty() || !pt.isEmpty()) + validation.setPromptMessage(p, pt); + + //find the end + while(!(reader.name() == QLatin1String("dataValidation") && reader.tokenType() == QXmlStreamReader::EndElement)) { + reader.readNextStartElement(); + if (reader.tokenType() == QXmlStreamReader::StartElement) { + if (reader.name() == QLatin1String("formula1")) { + validation.setFormula1(reader.readElementText()); + } else if (reader.name() == QLatin1String("formula2")) { + validation.setFormula2(reader.readElementText()); + } + } + } + + dataValidationsList.append(validation); +} + bool Worksheet::loadFromXmlFile(QIODevice *device) { Q_D(Worksheet); @@ -1409,6 +1607,8 @@ bool Worksheet::loadFromXmlFile(QIODevice *device) d->readSheetData(reader); } else if (reader.name() == QLatin1String("mergeCells")) { d->readMergeCells(reader); + } else if (reader.name() == QLatin1String("dataValidations")) { + d->readDataValidations(reader); } } } diff --git a/src/xlsx/xlsxworksheet.h b/src/xlsx/xlsxworksheet.h index 8be1a19..38003f4 100755 --- a/src/xlsx/xlsxworksheet.h +++ b/src/xlsx/xlsxworksheet.h @@ -43,6 +43,7 @@ class Package; class Workbook; class Format; class Drawing; +class DataValidation; struct XlsxImageData; class WorksheetPrivate; @@ -61,6 +62,8 @@ public: int writeDateTime(int row, int column, const QDateTime& dt, Format *format=0); int writeHyperlink(int row, int column, const QUrl &url, Format *format=0, const QString &display=QString(), const QString &tip=QString()); + bool addDataValidation(const DataValidation &validation); + Cell *cellAt(const QString &row_column) const; Cell *cellAt(int row, int column) const; diff --git a/src/xlsx/xlsxworksheet_p.h b/src/xlsx/xlsxworksheet_p.h index 1b1f395..817aa46 100644 --- a/src/xlsx/xlsxworksheet_p.h +++ b/src/xlsx/xlsxworksheet_p.h @@ -27,6 +27,7 @@ #include "xlsxglobal.h" #include "xlsxworksheet.h" #include "xlsxcell.h" +#include "xlsxdatavalidation.h" #include #include @@ -177,6 +178,7 @@ public: void writeMergeCells(XmlStreamWriter &writer); void writeHyperlinks(XmlStreamWriter &writer); void writeDrawings(XmlStreamWriter &writer); + void writeDataValidation(XmlStreamWriter &writer); int rowPixelsSize(int row); int colPixelsSize(int col); XlsxObjectPositionData objectPixelsPosition(int col_start, int row_start, double x1, double y1, double width, double height); @@ -186,6 +188,8 @@ public: void readSheetData(XmlStreamReader &reader); void readColumnsInfo(XmlStreamReader &reader); void readMergeCells(XmlStreamReader &reader); + void readDataValidations(XmlStreamReader &reader); + void readDataValidation(XmlStreamReader &reader); SharedStrings *sharedStrings() const; @@ -203,6 +207,8 @@ public: QMap > colsInfoHelper; QList > drawingLinks; + QList dataValidationsList; + int xls_rowmax; int xls_colmax; int xls_strmax; diff --git a/tests/auto/worksheet/tst_worksheet.cpp b/tests/auto/worksheet/tst_worksheet.cpp index 58b78c1..2a6ed21 100644 --- a/tests/auto/worksheet/tst_worksheet.cpp +++ b/tests/auto/worksheet/tst_worksheet.cpp @@ -3,6 +3,7 @@ #include "xlsxworksheet.h" #include "xlsxcell.h" +#include "xlsxdatavalidation.h" #include "private/xlsxworksheet_p.h" #include "private/xlsxxmlreader_p.h" #include "private/xlsxsharedstrings_p.h" @@ -24,6 +25,7 @@ private Q_SLOTS: void testWriteCells(); void testWriteHyperlinks(); + void testWriteDataValidations(); void testMerge(); void testUnMerge(); @@ -31,6 +33,7 @@ private Q_SLOTS: void testReadColsInfo(); void testReadRowsInfo(); void testReadMergeCells(); + void testReadDataValidations(); }; WorksheetTest::WorksheetTest() @@ -164,6 +167,20 @@ void WorksheetTest::testWriteHyperlinks() QCOMPARE(sheet.d_ptr->sharedStrings()->getSharedString(4), QStringLiteral("xyz@debao.me?subject=Test")); } +void WorksheetTest::testWriteDataValidations() +{ + QXlsx::Worksheet sheet("", 1, 0); + QXlsx::DataValidation validation(QXlsx::DataValidation::Whole); + validation.setFormula1("10"); + validation.setFormula2("100"); + validation.addCell("A1"); + validation.addCell("C2:C4"); + sheet.addDataValidation(validation); + + QByteArray xmldata = sheet.saveToXmlData(); + QVERIFY(xmldata.contains("10100")); + } + void WorksheetTest::testMerge() { QXlsx::Worksheet sheet("", 1, 0); @@ -285,6 +302,23 @@ void WorksheetTest::testReadMergeCells() QCOMPARE(sheet.d_ptr->merges[0].row_end, 4); } +void WorksheetTest::testReadDataValidations() +{ + const QByteArray xmlData = "" + "10100" + "10100" + ""; + + QXlsx::XmlStreamReader reader(xmlData); + reader.readNextStartElement();//current node is dataValidations + + QXlsx::Worksheet sheet("", 1, 0); + sheet.d_ptr->readDataValidations(reader); + + QCOMPARE(sheet.d_ptr->dataValidationsList.size(), 2); + QCOMPARE(sheet.d_ptr->dataValidationsList[0].validationType(), QXlsx::DataValidation::Whole); +} + QTEST_APPLESS_MAIN(WorksheetTest) #include "tst_worksheet.moc"