File indexing completed on 2024-05-19 16:06:25

0001 /* This file is part of the KDE project
0002 
0003    Copyright 2008 Johannes Simon <johannes.simon@gmail.com>
0004    Copyright 2008 Inge Wallin     <inge@lysator.liu.se>
0005 
0006    This library is free software; you can redistribute it and/or
0007    modify it under the terms of the GNU Library General Public
0008    License as published by the Free Software Foundation; either
0009    version 2 of the License, or (at your option) any later version.
0010 
0011    This library is distributed in the hope that it will be useful,
0012    but WITHOUT ANY WARRANTY; without even the implied warranty of
0013    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
0014    Library General Public License for more details.
0015 
0016    You should have received a copy of the GNU Library General Public License
0017    along with this library; see the file COPYING.LIB.  If not, write to
0018    the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
0019  * Boston, MA 02110-1301, USA.
0020  */
0021 
0022 
0023 // Own
0024 #include "CellRegion.h"
0025 
0026 // C
0027 #include <cmath>
0028 
0029 // Qt
0030 #include <QPoint>
0031 #include <QRect>
0032 #include <QVector>
0033 #include <QStringList>
0034 
0035 // KoChart
0036 #include "TableSource.h"
0037 #include "ChartDebug.h"
0038 
0039 
0040 using std::pow;
0041 using namespace KoChart;
0042 
0043 /************************RegionParser*******************************/
0044 
0045 class Parser
0046 {
0047 public:
0048     Parser(const QString & input)
0049         : m_pos(m_input.constEnd())
0050     {
0051         m_input = input;
0052         if (m_input.contains(":.")) {
0053             // FIXME
0054             warnChartParse<<"Handle 'DotDoubleDot' in input string";
0055             m_input.replace(QStringLiteral(":."), QChar(':'));
0056         }
0057         m_delimiter.append(QChar::fromLatin1('.'));
0058         m_delimiter.append(QChar::fromLatin1(':'));
0059         m_delimiter.append(QChar::fromLatin1(';'));
0060         m_delimiter.append(QChar::fromLatin1(' '));
0061     }
0062     bool parse();
0063     QList< QRect > getResult() const { return m_result; }
0064     QString tableName() const { return m_tableName; }
0065 
0066 private:
0067     struct Token
0068     {
0069         enum TokenType{ Dot = 0, DoubleDot = 1, Space = 2, Spacer = 3, Identifier = 4, End };
0070         Token(TokenType type, const QString & identifier): m_type(type), m_identifier(identifier){}
0071         Token(): m_type(End) {}
0072         TokenType m_type;
0073         QString m_identifier;
0074     };
0075 
0076     inline Token parseToken();
0077     inline void eatWhitespaces();
0078     inline bool parseRegionList();
0079     inline bool parseRegion();
0080     inline bool parseName();
0081     inline bool parseName2();
0082     inline bool parsePoint();
0083     inline bool parseRegion2();
0084     inline void setTableName(const QString &name);
0085 
0086 public:
0087     QString toString(Token &token)
0088     {
0089         QString types = "Dot,DoubleDot,Space,Spacer,Identifier,End";
0090         QString s = QString("Token[%1").arg(types.split(',').value(token.m_type));
0091         if (token.m_type == Token::Identifier) {
0092             s += ", " + token.m_identifier;
0093         }
0094         s += ']';
0095         return s;
0096     }
0097 
0098 private:
0099     QString m_input;
0100     QString::ConstIterator m_pos;
0101     QList< QRect > m_result;
0102     Token m_currentToken;
0103     QRect m_currentRect;
0104     QPoint m_currentPoint;
0105     QString m_tableName;
0106     int m_index;
0107     QVector< QChar > m_delimiter;
0108 };
0109 
0110 void Parser::setTableName(const QString &name)
0111 {
0112     QString strippedName = name;
0113     if (name.startsWith(QChar::fromLatin1('$')))
0114         strippedName.remove(0, 1);
0115     if (m_tableName.isEmpty())        
0116         m_tableName = strippedName;
0117     else
0118         if (strippedName != m_tableName)
0119             debugChartParse << "More than one sheet referenced, this is currently not supported";
0120 }
0121 
0122 bool Parser::parse()
0123 {
0124     debugChartParse << "Input " << m_input;
0125     m_pos = m_input.constBegin();
0126     m_index = 0;
0127     m_currentToken = parseToken();
0128     return parseRegionList();
0129 }
0130 
0131 Parser::Token Parser::parseToken()
0132 {
0133     Token::TokenType type = Token::End;
0134     if (m_pos != m_input.constEnd()) {
0135         switch(m_delimiter.indexOf(*m_pos)) {
0136         case(0):
0137             type = Token::Dot;
0138             break;
0139         case(1):
0140             type = Token::DoubleDot;
0141             break;
0142         case(2):
0143         case(3):
0144             type = Token::Space;
0145             break;
0146         default:
0147             type = Token::Identifier;
0148         }
0149     }
0150     if (m_index >= m_input.size())
0151         type = Token::End;
0152     else if (*m_pos == QChar::fromLatin1('$')) {
0153         ++m_pos;
0154         ++m_index;
0155     }
0156     QString identifier;
0157     if (m_pos != m_input.constEnd() && *m_pos == QChar::fromLatin1('\'')) {
0158         ++m_pos;
0159         ++m_index;
0160         int startPos = m_index;
0161         for (; m_pos != m_input.constEnd() && *m_pos != QChar::fromLatin1('\''); ++m_pos, ++m_index)
0162             ;
0163 
0164         if (type == Token::Identifier)
0165             identifier = m_input.mid(startPos, m_index - startPos);
0166         if (m_pos != m_input.constEnd()) {
0167             ++m_pos;
0168             ++m_index;
0169         }
0170     }
0171     else {
0172         int startPos = m_index;
0173         for (; m_pos != m_input.constEnd() && !m_delimiter.contains(*m_pos); ++m_pos, ++m_index)
0174             ;
0175         if (m_pos != m_input.constEnd() && startPos == m_index) {
0176             ++m_index;
0177             ++m_pos;
0178         }
0179         if (type == Token::Identifier)
0180             identifier = m_input.mid(startPos, m_index - startPos);
0181     }
0182 
0183     Token t(type, identifier);
0184     debugChartParse<<toString(t);
0185     return t;
0186 }
0187 
0188 void Parser::eatWhitespaces()
0189 {
0190     for (; m_pos != m_input.constEnd() && *m_pos == QChar::fromLatin1(' '); ++m_pos, ++m_index)
0191         ;
0192 }
0193 
0194 bool Parser::parseRegionList()
0195 {
0196     bool res = true;
0197     for (; m_currentToken.m_type != Token::End; m_currentToken = parseToken()) {
0198         if (m_currentToken.m_type != Token::Space) {
0199             if (m_currentToken.m_type == Token::Identifier)
0200                 res = parseRegion();
0201             else
0202                 res = false;
0203         }
0204     }
0205     return res;
0206 }
0207 
0208 bool Parser::parseRegion()
0209 {
0210     debugChartParse << "parseRegion";
0211     bool res = true;
0212     res &= parseRegion2();
0213     m_currentToken = parseToken();
0214     debugChartParse << "CurrentToken " << m_currentToken.m_identifier << m_currentToken.m_type;
0215     if (m_currentToken.m_type == Token::DoubleDot) {
0216         const QPoint topLeft = m_currentPoint;
0217         m_currentToken = parseToken();
0218         res &= parseRegion2();
0219         //m_currentToken = parseToken();
0220         m_result.append(QRect(topLeft, m_currentPoint));
0221         debugChartParse << "DoubleDot"<<"result:"<<m_result;
0222     }
0223     else {
0224         m_result.append(QRect(m_currentPoint, m_currentPoint));
0225         debugChartParse << "NoDoubleDot"<<"result:"<<m_result;;
0226     }
0227     
0228     if (m_currentToken.m_type == Token::Space) {
0229         res &= parseRegionList();
0230     } else if (m_currentToken.m_type == Token::End) {
0231         debugChartParse<<"ParseRegion:"<<res<<toString(m_currentToken)<<m_result;;
0232         return res;
0233     } else {
0234         res = false;
0235     }
0236     debugChartParse<<"ParseRegion:"<<res<<toString(m_currentToken)<<m_result;;
0237     return res;
0238 }
0239 
0240 bool Parser::parseRegion2()
0241 {
0242     //debugChart << "ParseRegion2";
0243     bool res = true;
0244 
0245     if (m_currentToken.m_type != Token::Identifier && m_currentToken.m_type != Token::Dot)
0246         res = false;
0247 
0248     const QString firstIdentifier = m_currentToken.m_type != Token::Dot ? m_currentToken.m_identifier : tableName();
0249     if (m_currentToken.m_type != Token::Dot)
0250         m_currentToken = parseToken();
0251     if (m_currentToken.m_type == Token::Dot)
0252     {
0253         m_currentToken = parseToken();
0254         if (m_currentToken.m_type == Token::Identifier)
0255         {            
0256             QRegExp regEx(QString::fromLatin1("([$]*)([A-Z]+)([$]*)([0-9]+)"));
0257             regEx.exactMatch(m_currentToken.m_identifier);
0258             m_currentPoint = QPoint(CellRegion::rangeStringToInt(regEx.cap(2)), regEx.cap(4).toInt());
0259             //debugChart << "FUN" << regEx.cap(2) << " " << regEx.cap(4);
0260             setTableName(firstIdentifier);
0261         }
0262         else
0263             res = false;
0264     }
0265     else
0266     {
0267         QRegExp regEx(QString::fromLatin1("([$]*)([A-Z]+)([$]*)([0-9]+)"));
0268         regEx.exactMatch(firstIdentifier);
0269         //debugChart << "FUN" << regEx.cap(2) << " " << regEx.cap(4);
0270         m_currentPoint = QPoint(CellRegion::rangeStringToInt(regEx.cap(2)), regEx.cap(4).toInt());
0271     }
0272     //debugChart << "TableName "<< m_tableName;
0273     //debugChart << firstIdentifier;
0274     //debugChart << "Point" << m_currentPoint;
0275     //debugChart << m_currentToken.m_identifier;
0276     //debugChart << m_currentToken.m_type;
0277 
0278     return res;
0279 
0280 }
0281 
0282 // bool Parser::parsePoint()
0283 // {
0284 //     bool res = true;
0285 //     int startIndex = m_index;
0286 //     while(m_pos != m_input.end() && *m_pos != QChar::fromLatin1(':'))
0287 //     {
0288 //         ++m_pos;
0289 //         ++m_index;
0290 //     }
0291 //     const QString currentString = m_input.mid(startIndex, m_index - startIndex);
0292 //     debugChart << "PointString" << currentString;
0293 //     QRegExp regEx(QString::fromLatin1("[A-Z]+[0-9]+"));
0294 //     regEx.indexIn(currentString);
0295 //     m_currentPoint = QPoint(CellRegion::rangeStringToInt(regEx.cap(0)), regEx.cap(1).toInt());
0296 //     return res;
0297 // }
0298 /************************ENDRegionParser*******************************/
0299 
0300 static QString columnName(uint column);
0301 //static int rangeCharToInt(char c);
0302 
0303 /**
0304  * Makes sure that quotes are added if name contains spaces or special
0305  * characters. May also be used to escape certain characters if needed.
0306  */
0307 static QString formatTableName(const QString &name)
0308 {
0309     static const QList<QChar> specialChars =
0310         QList<QChar>() << ' ' << '\t' << '-' << '\'';
0311 
0312     bool containsSpecialChars = false;
0313     foreach(QChar c, specialChars)
0314         containsSpecialChars = containsSpecialChars || name.contains(c);
0315 
0316     if(containsSpecialChars)
0317         return QLatin1Char('\'') + name + QLatin1Char('\'');
0318 
0319     return name;
0320 }
0321 
0322 /**
0323  * Reverts any operation done by formatTableName(), so that ideally
0324  * unformatTableName(formatTableName(name)) == name
0325  */
0326 /* currently not used in CellRegion(TableSource *source, const QString& regions)
0327 static QString unformatTableName(QString name)
0328 {
0329     if (name.startsWith(QLatin1Char('\'')) && name.endsWith(QLatin1Char('\''))) {
0330         name.remove(0, 1).chop(1);
0331     }
0332 
0333     return name;
0334 }
0335 */
0336 class CellRegion::Private
0337 {
0338 public:
0339     Private();
0340     ~Private();
0341 
0342     QString pointToString(const QPoint &point) const;
0343 
0344     // These are actually one-dimensional, but can have different
0345     // orientations (hor / vert).
0346     QVector<QRect> rects;
0347 
0348     QRect          boundingRect;
0349     // NOTE: Don't forget to extend operator=() if you add new members
0350 
0351     /// Table this region is in (name/model pair provided by TableSource)
0352     Table *table;
0353 };
0354 
0355 
0356 CellRegion::Private::Private()
0357 {
0358     table = 0;
0359 }
0360 
0361 CellRegion::Private::~Private()
0362 {
0363 }
0364 
0365 
0366 // ================================================================
0367 //                         Class CellRegion
0368 
0369 
0370 CellRegion::CellRegion()
0371     : d(new Private())
0372 {
0373 }
0374 
0375 CellRegion::CellRegion(const CellRegion &region)
0376     : d(new Private())
0377 {
0378     // Use operator=();
0379     *this = region;
0380 }
0381 
0382 CellRegion::CellRegion(TableSource *source, const QString& regions)
0383     : d(new Private())
0384 {
0385     // A dollar sign before a part of the address means that this part
0386     // is absolute. This is irrelevant for us, however, thus we can remove
0387     // all occurrences of '$', and handle relative and absolute addresses in
0388     // the same way.
0389     // See ODF specs $8.3.1 "Referencing Table Cells"
0390     Parser parser(regions);
0391     const bool success = parser.parse();
0392     if (!success)
0393         warnChart << "Parsing cell region failed:"<<regions;
0394 
0395     QVector<QRect> rects = parser.getResult().toVector();
0396     for (int i = 0; i < rects.count(); ++i) {
0397         add(rects.at(i));
0398     }
0399     d->table = source->get(parser.tableName());
0400 //     QStringList regionsList = regions.split(' ', QString::SkipEmptyParts);
0401 //     Q_FOREACH(const QString& region, regionsList) {
0402 //       QString searchStr = QString(region).remove('$');
0403 //       QRegExp regEx;
0404 // 
0405 //       QStringList regionList = searchStr.split(';');
0406 //       Q_FOREACH(const QString &region, regionList) {
0407 //           const bool isPoint = !region.contains(':');
0408 //           if (isPoint)
0409 //               regEx = QRegExp("(|.*\\.)([A-Z]+)([0-9]+)");
0410 //           else // support range-notations like Sheet1.D2:Sheet1.F2 Sheet1.D2:F2 D2:F2
0411 //               regEx = QRegExp ("(|.*\\.)([A-Z]+)([0-9]+)\\:(|.*\\.)([A-Z]+)([0-9]+)");
0412 // 
0413 //           // Check if region string is valid (e.g. not empty)
0414 //           if (regEx.indexIn(region) >= 0) {
0415 //               // It is possible for a cell-range-address as defined in ODF to contain
0416 //               // refernces to cells of more than one sheet. This, however, we ignore
0417 //               // here. We do not support more than one table in a cell region.
0418 //               // Also we do not support regions spanned over different sheets. For us
0419 //               // everything is either on no sheet or on the same sheet.
0420 //               QString sheetName = regEx.cap(1);
0421 //               if (sheetName.endsWith("."))
0422 //                   sheetName = sheetName.left(sheetName.length() - 1);
0423 //               // TODO: Support for multiple tables in one region
0424 //               d->table = source->get(unformatTableName(sheetName));
0425 // 
0426 //               QPoint topLeft(rangeStringToInt(regEx.cap(2)), regEx.cap(3).toInt());
0427 //               if (isPoint) {
0428 //                   d->rects.append(QRect(topLeft, QSize(1, 1)));
0429 //               } else {
0430 //                   QPoint bottomRight(rangeStringToInt(regEx.cap(5)), regEx.cap(6).toInt());
0431 //                   d->rects.append(QRect(topLeft, bottomRight));
0432 //               }
0433 //           }
0434 //       }
0435 //     }
0436 }
0437 
0438 CellRegion::CellRegion(Table *table, const QPoint &point)
0439     : d(new Private())
0440 {
0441     d->table = table;
0442     add(point);
0443 }
0444 
0445 CellRegion::CellRegion(Table *table, const QRect &rect)
0446     : d(new Private())
0447 {
0448     d->table = table;
0449     add(rect);
0450 }
0451 
0452 CellRegion::CellRegion(Table *table, const QVector<QRect> &rects)
0453     : d(new Private())
0454 {
0455     d->table = table;
0456     foreach(const QRect& rect, rects)
0457         add(rect);
0458 }
0459 
0460 CellRegion::CellRegion(Table *table)
0461     : d(new Private())
0462 {
0463     d->table = table;
0464 }
0465 
0466 CellRegion::~CellRegion()
0467 {
0468     delete d;
0469 }
0470 
0471 
0472 CellRegion& CellRegion::operator = (const CellRegion& region)
0473 {
0474     d->rects        = region.d->rects;
0475     d->boundingRect = region.d->boundingRect;
0476     d->table        = region.d->table;
0477 
0478     return *this;
0479 }
0480 
0481 bool CellRegion::operator == (const CellRegion &other) const
0482 {
0483     return d->rects == other.d->rects;
0484 }
0485 
0486 
0487 Table *CellRegion::table() const
0488 {
0489     return d->table;
0490 }
0491 
0492 QVector<QRect> CellRegion::rects() const
0493 {
0494     return d->rects;
0495 }
0496 
0497 int CellRegion::rectCount() const
0498 {
0499     return d->rects.size();
0500 }
0501 
0502 QString CellRegion::sheetName() const
0503 {
0504     return d->table->name();
0505 }
0506 
0507 bool CellRegion::isValid() const
0508 {
0509     return d->rects.size() > 0 && d->table ;
0510 }
0511 
0512 QString CellRegion::Private::pointToString(const QPoint &point) const
0513 {
0514     QString result;
0515 
0516     result.append('$' + columnName(point.x()));
0517     result.append('$' + QString::number(point.y()));
0518 
0519     return result;
0520 }
0521 
0522 QString CellRegion::toString() const
0523 {
0524     if (!isValid())
0525         return QString();
0526 
0527     QString result;
0528     for (int i = 0; i < d->rects.count(); ++i) {
0529         const QRect range = d->rects[i];
0530         // Top-left corner
0531         if (table())
0532             result.append('$' + formatTableName(table()->name()) + '.');
0533         result.append(d->pointToString(range.topLeft()));
0534 
0535         // If it is not a point, append rect's bottom-right corner
0536         if (range.topLeft() != range.bottomRight()) {
0537             result.append(':');
0538             result.append(d->pointToString(range.bottomRight()));
0539         }
0540 
0541         // Separate ranges by a space
0542         // See odf 18.3.6cellRangeAddressList
0543         if (i < d->rects.count() - 1)
0544             result.append(' ');
0545     }
0546     return result;
0547 }
0548 
0549 
0550 bool CellRegion::contains(const QPoint &point, bool proper) const
0551 {
0552     foreach (const QRect &rect, d->rects) {
0553         if (rect.contains(point, proper))
0554             return true;
0555     }
0556 
0557     return false;
0558 }
0559 
0560 bool CellRegion::contains(const QRect &rect, bool proper) const
0561 {
0562     foreach (const QRect &r, d->rects) {
0563         if (r.contains(rect, proper))
0564             return true;
0565     }
0566 
0567     return false;
0568 }
0569 
0570 bool CellRegion::intersects(const CellRegion &other) const
0571 {
0572     // If both regions lie within only one table and these tables
0573     // are different, they trivially do not intersect.
0574     if (table() && other.table() &&
0575          table() != other.table())
0576         return false;
0577 
0578     foreach (const QRect &r, d->rects) {
0579         foreach(const QRect &_r, other.d->rects) {
0580             if (r.intersects(_r))
0581                 return true;
0582         }
0583     }
0584 
0585     return false;
0586 }
0587 
0588 CellRegion CellRegion::intersected(const QRect &rect) const
0589 {
0590     CellRegion intersections;
0591 
0592     foreach (const QRect &r, d->rects) {
0593         if (r.intersects(rect))
0594             intersections.add(r.intersected(rect));
0595     }
0596 
0597     return intersections;
0598 }
0599 
0600 Qt::Orientation CellRegion::orientation() const
0601 {
0602     foreach (const QRect &rect, d->rects) {
0603         if (rect.width() > 1)
0604                 return Qt::Horizontal;
0605         if (rect.height() > 1)
0606                 return Qt::Vertical;
0607     }
0608 
0609     // Default if region is only one cell
0610     return Qt::Vertical;
0611 }
0612 
0613 int CellRegion::cellCount() const
0614 {
0615     int count = 0;
0616 
0617     /*FIXME the following would be more correct cause it
0618      * would also cover multi-dimensional ranges (means
0619      * where rect.width()>1 *and* rect.height()>1). But
0620      * for that kchart needs lot of fixing (e.g. in
0621      * the CellRegion to proper handle multi-dimension
0622      * ranges too).
0623      *
0624     foreach (const QRect &rect, d->rects)
0625         count += (rect.width() * rect.height());
0626     */
0627 
0628     if (orientation() == Qt::Horizontal) {
0629         foreach (const QRect &rect, d->rects)
0630             count += rect.width();
0631     }
0632     else {
0633         foreach(const QRect &rect, d->rects)
0634             count += rect.height();
0635     }
0636     return count;
0637 }
0638 
0639 void CellRegion::add(const CellRegion &other)
0640 {
0641     add(other.rects());
0642 }
0643 
0644 void CellRegion::add(const QPoint &point)
0645 {
0646     add(QRect(point, QSize(1, 1)));
0647 }
0648 
0649 void CellRegion::add(const QRect &rect)
0650 {
0651 // These checks are obsolete, a CellRegion can be used otherwise as well
0652 #if 0
0653     if (!rect.isValid()) {
0654         warnChart << "CellRegion::add() Attempt to add invalid rectangle";
0655         warnChart << "CellRegion::add():" << rect;
0656         return;
0657     }
0658 
0659     if (rect.width() > 1 && rect.height() > 1) {
0660         warnChart << "CellRegion::add() Attempt to add rectangle with height AND width > 1";
0661         warnChart << "CellRegion::add():" << rect;
0662         return;
0663     }
0664 #endif
0665 
0666     d->rects.append(rect);
0667     d->boundingRect |= rect;
0668 }
0669 
0670 void CellRegion::add(const QVector<QRect> &rects)
0671 {
0672     foreach (const QRect &rect, rects)
0673         add(rect);
0674 }
0675 
0676 QRect CellRegion::boundingRect() const
0677 {
0678     return d->boundingRect;
0679 }
0680 
0681 bool CellRegion::hasPointAtIndex(int index) const
0682 {
0683     return pointAtIndex(index) != QPoint(-1, -1);
0684 }
0685 
0686 QPoint CellRegion::pointAtIndex(int index) const
0687 {
0688     // sum of all previous rectangle indices
0689     int i = 0;
0690 
0691     foreach (const QRect &rect, d->rects) {
0692         // Rectangle is horizontal
0693         if (rect.width() > 1) {
0694             // Found it!
0695             // Index refers to point in current rectangle
0696             if (i + rect.width() > index) {
0697                 // Local index of point in this rectangle
0698                 int j = index - i;
0699                 return QPoint(rect.x() + j, rect.y());
0700             }
0701 
0702             // add number of indices in current rectangle to total index count
0703             i += rect.width();
0704         }
0705         else {
0706             // Found it!
0707             // Index refers to point in current rectangle
0708             if (i + rect.height() > index) {
0709                 // Local index of point in this rectangle
0710                 int j = index - i;
0711                 return QPoint(rect.x(), rect.y() + j);
0712             }
0713 
0714             // add number of indices in current rectangle to total index count
0715             i += rect.height();
0716         }
0717     }
0718 
0719     // Invalid index!
0720     return QPoint(-1, -1);
0721 }
0722 
0723 int CellRegion::indexAtPoint(const QPoint &point) const
0724 {
0725     int indicesLeftToPoint = 0;
0726     bool found = false;
0727 
0728     foreach (const QRect &rect, d->rects) {
0729         if (!rect.contains(point)) {
0730             indicesLeftToPoint += rect.width() > 1 ? rect.width() : rect.height();
0731             continue;
0732         }
0733 
0734         found = true;
0735         if (rect.width() > 1)
0736             indicesLeftToPoint += point.x() - rect.topLeft().x();
0737         else
0738             indicesLeftToPoint += point.y() - rect.topLeft().y();
0739     }
0740 
0741     return found ? indicesLeftToPoint : -1;
0742 }
0743 
0744 #if 0 // Unused?
0745 static int rangeCharToInt(char c)
0746 {
0747     return (c >= 'A' && c <= 'Z') ? (c - 'A' + 1) : -1;
0748 }
0749 
0750 static int rangeStringToInt(const QString &string)
0751 {
0752     int result = 0;
0753     const int size = string.size();
0754     for (int i = 0; i < size; i++) {
0755         //debugChart << "---" << float(rangeCharToInt(string[i].toLatin1()) * pow(10.0, (size - i - 1)));
0756         result += rangeCharToInt(string[i].toLatin1()) * pow(10.0, (size - i - 1));
0757     }
0758     //debugChart << "+++++ result=" << result;
0759     return result;
0760 }
0761 
0762 static QString rangeIntToString(int i)
0763 {
0764     QString tmp = QString::number(i);
0765     for (int j = 0; j < tmp.size(); j++) {
0766         tmp[j] = 'A' + tmp[j].toLatin1() - '1';
0767     }
0768 
0769     //debugChart << "tmp=" << tmp;
0770     return tmp;
0771 }
0772 #endif
0773 
0774 int CellRegion::rangeCharToInt(char c)
0775 {
0776     return (c >= 'A' && c <= 'Z') ? (c - 'A' + 1) : -1;
0777 }
0778 
0779 int CellRegion::rangeStringToInt(const QString &string)
0780 {
0781     int result = 0;
0782     const int size = string.size();
0783     for (int i = 0; i < size; i++) {
0784         result += rangeCharToInt(string[i].toLatin1()) * pow(10.0, (size - i - 1));
0785     }
0786 
0787     return result;
0788 }
0789 
0790 QString CellRegion::rangeIntToString(int i)
0791 {
0792     QString tmp = QString::number(i);
0793     for (int j = 0; j < tmp.size(); j++) {
0794         tmp[j] = 'A' + tmp[j].toLatin1() - '1';
0795     }
0796 
0797     return tmp;
0798 }
0799 
0800 // Return the symbolic name of any column.
0801 QString CellRegion::columnName(uint column)
0802 {
0803     if (column < 1 || column > 32767)
0804         return QString("@@@");
0805 
0806     QString   str;
0807     unsigned  digits = 1;
0808     unsigned  offset = 0;
0809 
0810     column--;
0811 
0812     for (unsigned limit = 26; column >= limit + offset; limit *= 26, ++digits)
0813         offset += limit;
0814 
0815     for (unsigned col = column - offset; digits; --digits, col /= 26)
0816         str.prepend(QChar('A' + (col % 26)));
0817 
0818     return str;
0819 }
0820 
0821 QDebug operator<<(QDebug dbg, const KoChart::CellRegion &r)
0822 {
0823     dbg << "CellRegion[" << r.toString() << ']';
0824     return dbg;
0825 }