andre@1: /****************************************************************************
andre@1: ** Copyright (c) 2013-2014 Debao Zhang <hello@debao.me>
andre@1: ** All right reserved.
andre@1: **
andre@1: ** Permission is hereby granted, free of charge, to any person obtaining
andre@1: ** a copy of this software and associated documentation files (the
andre@1: ** "Software"), to deal in the Software without restriction, including
andre@1: ** without limitation the rights to use, copy, modify, merge, publish,
andre@1: ** distribute, sublicense, and/or sell copies of the Software, and to
andre@1: ** permit persons to whom the Software is furnished to do so, subject to
andre@1: ** the following conditions:
andre@1: **
andre@1: ** The above copyright notice and this permission notice shall be
andre@1: ** included in all copies or substantial portions of the Software.
andre@1: **
andre@1: ** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
andre@1: ** EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
andre@1: ** MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
andre@1: ** NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
andre@1: ** LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
andre@1: ** OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
andre@1: ** WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
andre@1: **
andre@1: ****************************************************************************/
andre@1: #include "xlsxcellreference.h"
andre@1: #include <QStringList>
andre@1: #include <QMap>
andre@1: #include <QRegularExpression>
andre@1: 
andre@1: QT_BEGIN_NAMESPACE_XLSX
andre@1: 
andre@1: namespace {
andre@1: 
andre@1: int intPow(int x, int p)
andre@1: {
andre@1:   if (p == 0) return 1;
andre@1:   if (p == 1) return x;
andre@1: 
andre@1:   int tmp = intPow(x, p/2);
andre@1:   if (p%2 == 0) return tmp * tmp;
andre@1:   else return x * tmp * tmp;
andre@1: }
andre@1: 
andre@1: QString col_to_name(int col_num)
andre@1: {
andre@1:     static QMap<int, QString> col_cache;
andre@1: 
andre@1:     if (!col_cache.contains(col_num)) {
andre@1:         QString col_str;
andre@1:         int remainder;
andre@1:         while (col_num) {
andre@1:             remainder = col_num % 26;
andre@1:             if (remainder == 0)
andre@1:                 remainder = 26;
andre@1:             col_str.prepend(QChar('A'+remainder-1));
andre@1:             col_num = (col_num - 1) / 26;
andre@1:         }
andre@1:         col_cache.insert(col_num, col_str);
andre@1:     }
andre@1: 
andre@1:     return col_cache[col_num];
andre@1: }
andre@1: 
andre@1: int col_from_name(const QString &col_str)
andre@1: {
andre@1:     int col = 0;
andre@1:     int expn = 0;
andre@1:     for (int i=col_str.size()-1; i>-1; --i) {
andre@1:         col += (col_str[i].unicode() - 'A' + 1) * intPow(26, expn);
andre@1:         expn++;
andre@1:     }
andre@1: 
andre@1:     return col;
andre@1: }
andre@1: } //namespace
andre@1: 
andre@1: /*!
andre@1:     \class CellReference
andre@1:     \brief For one single cell such as "A1"
andre@1:     \inmodule QtXlsx
andre@1: 
andre@1:     The CellReference class stores the cell location in a worksheet.
andre@1: */
andre@1: 
andre@1: /*!
andre@1:     Constructs an invalid Cell Reference
andre@1: */
andre@1: CellReference::CellReference()
andre@1:     : _row(-1), _column(-1)
andre@1: {
andre@1: }
andre@1: 
andre@1: /*!
andre@1:     Constructs the Reference from the given \a row, and \a column.
andre@1: */
andre@1: CellReference::CellReference(int row, int column)
andre@1:     : _row(row), _column(column)
andre@1: {
andre@1: }
andre@1: 
andre@1: /*!
andre@1:     \overload
andre@1:     Constructs the Reference form the given \a cell string.
andre@1: */
andre@1: CellReference::CellReference(const QString &cell)
andre@1: {
andre@1:     init(cell);
andre@1: }
andre@1: 
andre@1: /*!
andre@1:     \overload
andre@1:     Constructs the Reference form the given \a cell string.
andre@1: */
andre@1: CellReference::CellReference(const char *cell)
andre@1: {
andre@1:     init(QString::fromLatin1(cell));
andre@1: }
andre@1: 
andre@1: void CellReference::init(const QString &cell_str)
andre@1: {
andre@1:     static QRegularExpression re(QStringLiteral("^\\$?([A-Z]{1,3})\\$?(\\d+)$"));
andre@1:     QRegularExpressionMatch match = re.match(cell_str);
andre@1:     if (match.hasMatch()) {
andre@1:         const QString col_str = match.captured(1);
andre@1:         const QString row_str = match.captured(2);
andre@1:         _row = row_str.toInt();
andre@1:         _column = col_from_name(col_str);
andre@1:     }
andre@1: }
andre@1: 
andre@1: /*!
andre@1:     Constructs a Reference by copying the given \a
andre@1:     other Reference.
andre@1: */
andre@1: CellReference::CellReference(const CellReference &other)
andre@1:     : _row(other._row), _column(other._column)
andre@1: {
andre@1: }
andre@1: 
andre@1: /*!
andre@1:     Destroys the Reference.
andre@1: */
andre@1: CellReference::~CellReference()
andre@1: {
andre@1: }
andre@1: 
andre@1: /*!
andre@1:      Convert the Reference to string notation, such as "A1" or "$A$1".
andre@1:      If current object is invalid, an empty string will be returned.
andre@1: */
andre@1: QString CellReference::toString(bool row_abs, bool col_abs) const
andre@1: {
andre@1:     if (!isValid())
andre@1:         return QString();
andre@1: 
andre@1:     QString cell_str;
andre@1:     if (col_abs)
andre@1:         cell_str.append(QLatin1Char('$'));
andre@1:     cell_str.append(col_to_name(_column));
andre@1:     if (row_abs)
andre@1:         cell_str.append(QLatin1Char('$'));
andre@1:     cell_str.append(QString::number(_row));
andre@1:     return cell_str;
andre@1: }
andre@1: 
andre@1: /*!
andre@1:  * Returns true if the Reference is valid.
andre@1:  */
andre@1: bool CellReference::isValid() const
andre@1: {
andre@1:     return _row > 0 && _column > 0;
andre@1: }
andre@1: 
andre@1: QT_END_NAMESPACE_XLSX