diff --git a/src/xlsx/qtxlsx.pri b/src/xlsx/qtxlsx.pri index 32adbad..34efa42 100755 --- a/src/xlsx/qtxlsx.pri +++ b/src/xlsx/qtxlsx.pri @@ -32,6 +32,7 @@ HEADERS += $$PWD/xlsxdocpropscore_p.h \ $$PWD/xlsxcell_p.h \ $$PWD/xlsxdatavalidation.h \ $$PWD/xlsxdatavalidation_p.h \ + $$PWD/xlsxcellreference.h \ $$PWD/xlsxcellrange.h \ $$PWD/xlsxrichstring_p.h \ $$PWD/xlsxrichstring.h \ @@ -66,6 +67,7 @@ SOURCES += $$PWD/xlsxdocpropscore.cpp \ $$PWD/xlsxdocument.cpp \ $$PWD/xlsxcell.cpp \ $$PWD/xlsxdatavalidation.cpp \ + $$PWD/xlsxcellreference.cpp \ $$PWD/xlsxcellrange.cpp \ $$PWD/xlsxrichstring.cpp \ $$PWD/xlsxconditionalformatting.cpp \ diff --git a/src/xlsx/xlsxcellreference.cpp b/src/xlsx/xlsxcellreference.cpp new file mode 100644 index 0000000..fcfe375 --- /dev/null +++ b/src/xlsx/xlsxcellreference.cpp @@ -0,0 +1,182 @@ +/**************************************************************************** +** Copyright (c) 2013-2014 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 "xlsxcellreference.h" +#include +#include +#include + +QT_BEGIN_NAMESPACE_XLSX + +namespace { + +int intPow(int x, int p) +{ + if (p == 0) return 1; + if (p == 1) return x; + + int tmp = intPow(x, p/2); + if (p%2 == 0) return tmp * tmp; + else return x * tmp * tmp; +} + +QString col_to_name(int col_num) +{ + static QMap col_cache; + + if (!col_cache.contains(col_num)) { + QString col_str; + int remainder; + while (col_num) { + remainder = col_num % 26; + if (remainder == 0) + remainder = 26; + col_str.prepend(QChar('A'+remainder-1)); + col_num = (col_num - 1) / 26; + } + col_cache.insert(col_num, col_str); + } + + return col_cache[col_num]; +} + +int col_from_name(const QString &col_str) +{ + int col = 0; + int expn = 0; + for (int i=col_str.size()-1; i>-1; --i) { + col += (col_str[i].unicode() - 'A' + 1) * intPow(26, expn); + expn++; + } + + return col; +} +} //namespace + +/*! + \class CellReference + \brief For one single cell such as "A1" + \inmodule QtXlsx + + The CellReference class stores the cell location in a worksheet. +*/ + +/*! + Constructs an invalid Cell Reference +*/ +CellReference::CellReference() + : _row(-1), _column(-1) +{ +} + +/*! + Constructs the Reference from the given \a row, and \a column. +*/ +CellReference::CellReference(int row, int column) + : _row(row), _column(column) +{ +} + +/*! + \overload + Constructs the Reference form the given \a cell string. +*/ +CellReference::CellReference(const QString &cell) +{ + init(cell); +} + +/*! + \overload + Constructs the Reference form the given \a cell string. +*/ +CellReference::CellReference(const char *cell) +{ + init(QString::fromLatin1(cell)); +} + +void CellReference::init(const QString &cell_str) +{ + static QRegularExpression re(QStringLiteral("^\\$?([A-Z]{1,3})\\$?(\\d+)$")); + QRegularExpressionMatch match = re.match(cell_str); + if (match.hasMatch()) { + const QString col_str = match.captured(1); + const QString row_str = match.captured(2); + _row = row_str.toInt(); + _column = col_from_name(col_str); + } +} + +/*! + Constructs a Reference by copying the given \a + other Reference. +*/ +CellReference::CellReference(const CellReference &other) + : _row(other._row), _column(other._column) +{ +} + +/*! + Destroys the Reference. +*/ +CellReference::~CellReference() +{ +} + +/*! + Convert the Reference to string notation, such as "A1" or "$A$1". + If current object is invalid, an empty string will be returned. +*/ +QString CellReference::toString(bool row_abs, bool col_abs) const +{ + if (!isValid()) + return QString(); + + QString cell_str; + if (col_abs) + cell_str.append(QLatin1Char('$')); + cell_str.append(col_to_name(_column)); + if (row_abs) + cell_str.append(QLatin1Char('$')); + cell_str.append(QString::number(_row)); + return cell_str; +} + +/*! + Returns a CellReference equivalent of the string \a cell +*/ +CellReference CellReference::fromString(const QString &cell) +{ + return CellReference(cell); +} + +/*! + * Returns true if the Reference is valid. + */ +bool CellReference::isValid() const +{ + return _row > 0 && _column > 0; +} + +QT_END_NAMESPACE_XLSX diff --git a/src/xlsx/xlsxcellreference.h b/src/xlsx/xlsxcellreference.h new file mode 100644 index 0000000..6a917c2 --- /dev/null +++ b/src/xlsx/xlsxcellreference.h @@ -0,0 +1,66 @@ +/**************************************************************************** +** Copyright (c) 2013-2014 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_XLSXCELLREFERENCE_H +#define QXLSX_XLSXCELLREFERENCE_H +#include "xlsxglobal.h" + +QT_BEGIN_NAMESPACE_XLSX + +class Q_XLSX_EXPORT CellReference +{ +public: + CellReference(); + CellReference(int row, int column); + CellReference(const QString &cell); + CellReference(const char *cell); + CellReference(const CellReference &other); + ~CellReference(); + + QString toString(bool row_abs=false, bool col_abs=false) const; + static CellReference fromString(const QString &cell); + bool isValid() const; + inline void setRow(int row) { _row = row; } + inline void setColumn(int col) { _column = col; } + inline int row() const { return _row; } + inline int column() const { return _column; } + + inline bool operator ==(const CellReference &other) const + { + return _row==other._row && _column==other._column; + } + inline bool operator !=(const CellReference &other) const + { + return _row!=other._row || _column!=other._column; + } +private: + void init(const QString &cell); + int _row, _column; +}; + +QT_END_NAMESPACE_XLSX + +Q_DECLARE_TYPEINFO(QXlsx::CellReference, Q_MOVABLE_TYPE); + +#endif // QXLSX_XLSXCELLREFERENCE_H diff --git a/tests/auto/auto.pro b/tests/auto/auto.pro index a9b56ec..73c3547 100644 --- a/tests/auto/auto.pro +++ b/tests/auto/auto.pro @@ -11,4 +11,5 @@ SUBDIRS=\ styles \ format \ richstring \ - xlsxconditionalformatting + xlsxconditionalformatting \ + cellreference diff --git a/tests/auto/cellreference/cellreference.pro b/tests/auto/cellreference/cellreference.pro new file mode 100644 index 0000000..1e4382c --- /dev/null +++ b/tests/auto/cellreference/cellreference.pro @@ -0,0 +1,19 @@ +#------------------------------------------------- +# +# Project created by QtCreator 2013-08-30T11:16:26 +# +#------------------------------------------------- + +QT += testlib xlsx #xlsx-private +CONFIG += testcase +DEFINES += XLSX_TEST + +TARGET = tst_cellreferencetest +CONFIG += console +CONFIG -= app_bundle + +TEMPLATE = app + + +SOURCES += tst_cellreferencetest.cpp +DEFINES += SRCDIR=\\\"$$PWD/\\\" diff --git a/tests/auto/cellreference/tst_cellreferencetest.cpp b/tests/auto/cellreference/tst_cellreferencetest.cpp new file mode 100644 index 0000000..7a979bb --- /dev/null +++ b/tests/auto/cellreference/tst_cellreferencetest.cpp @@ -0,0 +1,83 @@ +#include "xlsxcellreference.h" +#include +#include + +using namespace QXlsx; + +class CellReferenceTest : public QObject +{ + Q_OBJECT + +public: + CellReferenceTest(); + +private Q_SLOTS: + void test_toString_data(); + void test_toString(); + void test_fromString_data(); + void test_fromString(); +}; + +CellReferenceTest::CellReferenceTest() +{ +} + +void CellReferenceTest::test_fromString() +{ + QFETCH(QString, cell); + QFETCH(int, row); + QFETCH(int, col); + CellReference pos = CellReference::fromString(cell); + QCOMPARE(pos.row(), row); + QCOMPARE(pos.column(), col); +} + +void CellReferenceTest::test_fromString_data() +{ + QTest::addColumn("cell"); + QTest::addColumn("row"); + QTest::addColumn("col"); + + QTest::newRow("A1") << "A1" << 1 << 1; + QTest::newRow("B1") << "B1" << 1 << 2; + QTest::newRow("C1") << "C1" << 1 << 3; + QTest::newRow("J1") << "J1" << 1 << 10; + QTest::newRow("A2") << "A2" << 2 << 1; + QTest::newRow("A3") << "A3" << 3 << 1; + QTest::newRow("A10") << "$A10" << 10 << 1; + QTest::newRow("Z8") << "Z$8" << 8 << 26; + QTest::newRow("AA10") << "$AA$10" << 10 << 27; + QTest::newRow("IU2") << "IU2" << 2 << 255; + QTest::newRow("XFD1") << "XFD1" << 1 << 16384; + QTest::newRow("XFE1048577") << "XFE1048577" << 1048577 << 16385; +} + +void CellReferenceTest::test_toString() +{ + QFETCH(int, row); + QFETCH(int, col); + QFETCH(bool, row_abs); + QFETCH(bool, col_abs); + QFETCH(QString, cell); + + QCOMPARE(CellReference(row,col).toString(row_abs, col_abs), cell); +} + +void CellReferenceTest::test_toString_data() +{ + QTest::addColumn("row"); + QTest::addColumn("col"); + QTest::addColumn("row_abs"); + QTest::addColumn("col_abs"); + QTest::addColumn("cell"); + + QTest::newRow("simple") << 1 << 1 << false << false << "A1"; + QTest::newRow("rowabs") << 1 << 1 << true << false << "A$1"; + QTest::newRow("colabs") << 1 << 1 << false << true << "$A1"; + QTest::newRow("bothabs") << 1 << 1 << true << true << "$A$1"; + QTest::newRow("...") << 1048577 << 16385 << false << false << "XFE1048577"; +} + +QTEST_APPLESS_MAIN(CellReferenceTest) + +#include "tst_cellreferencetest.moc"