File indexing completed on 2024-04-28 16:21:18

0001 /* This file is part of the KDE project
0002    Copyright 2010 Marijn Kruisselbrink <mkruisselbrink@kde.org>
0003    Copyright 2006-2007 Stefan Nikolaus <stefan.nikolaus@kdemail.net>
0004    Copyright 2005 Raphael Langerhorst <raphael.langerhorst@kdemail.net>
0005    Copyright 2004-2005 Tomas Mecir <mecirt@gmail.com>
0006    Copyright 2004-2006 Inge Wallin <inge@lysator.liu.se>
0007    Copyright 1999-2002,2004,2005 Laurent Montel <montel@kde.org>
0008    Copyright 2002-2005 Ariya Hidayat <ariya@kde.org>
0009    Copyright 2001-2003 Philipp Mueller <philipp.mueller@gmx.de>
0010    Copyright 2002-2003 Norbert Andres <nandres@web.de>
0011    Copyright 2003 Reinhart Geiser <geiseri@kde.org>
0012    Copyright 2003-2005 Meni Livne <livne@kde.org>
0013    Copyright 2003 Peter Simonsson <psn@linux.se>
0014    Copyright 1999-2002 David Faure <faure@kde.org>
0015    Copyright 2000-2002 Werner Trobin <trobin@kde.org>
0016    Copyright 1999,2002 Harri Porten <porten@kde.org>
0017    Copyright 2002 John Dailey <dailey@vt.edu>
0018    Copyright 1998-2000 Torben Weis <weis@kde.org>
0019    Copyright 2000 Bernd Wuebben <wuebben@kde.org>
0020    Copyright 2000 Simon Hausmann <hausmann@kde.org
0021    Copyright 1999 Stephan Kulow <coolo@kde.org>
0022    Copyright 1999 Michael Reiher <michael.reiher@gmx.de>
0023    Copyright 1999 Boris Wedl <boris.wedl@kfunigraz.ac.at>
0024    Copyright 1998-1999 Reginald Stadlbauer <reggie@kde.org>
0025 
0026    This library is free software; you can redistribute it and/or
0027    modify it under the terms of the GNU Library General Public
0028    License as published by the Free Software Foundation; either
0029    version 2 of the License, or (at your option) any later version.
0030 
0031    This library is distributed in the hope that it will be useful,
0032    but WITHOUT ANY WARRANTY; without even the implied warranty of
0033    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
0034    Library General Public License for more details.
0035 
0036    You should have received a copy of the GNU Library General Public License
0037    along with this library; see the file COPYING.LIB.  If not, write to
0038    the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
0039    Boston, MA 02110-1301, USA.
0040 */
0041 
0042 // Local
0043 #include "Cell.h"
0044 
0045 #include <stdlib.h>
0046 #include <ctype.h>
0047 #include <float.h>
0048 #include <math.h>
0049 
0050 #include "SheetsDebug.h"
0051 #include "CalculationSettings.h"
0052 #include "CellStorage.h"
0053 #include "Condition.h"
0054 #include "Formula.h"
0055 #include "Global.h"
0056 #include "Localization.h"
0057 #include "LoadingInfo.h"
0058 #include "Map.h"
0059 #include "NamedAreaManager.h"
0060 #include "RowColumnFormat.h"
0061 #include "RowFormatStorage.h"
0062 #include "Sheet.h"
0063 #include "Style.h"
0064 #include "StyleManager.h"
0065 #include "Util.h"
0066 #include "Value.h"
0067 #include "Validity.h"
0068 #include "ValueFormatter.h"
0069 #include "ValueParser.h"
0070 #include "StyleStorage.h"
0071 
0072 #include <KoXmlReader.h>
0073 
0074 #include <QTimer>
0075 #include <QTextDocument>
0076 #include <QTextCursor>
0077 
0078 using namespace Calligra::Sheets;
0079 
0080 class Q_DECL_HIDDEN Cell::Private : public QSharedData
0081 {
0082 public:
0083     Private() : sheet(0), column(0), row(0) {}
0084 
0085     Sheet*  sheet;
0086     uint    column  : 17; // KS_colMax
0087     uint    row     : 21; // KS_rowMax
0088 };
0089 
0090 
0091 Cell::Cell()
0092         : d(0)
0093 {
0094 }
0095 
0096 Cell::Cell(const Sheet* sheet, int col, int row)
0097         : d(new Private)
0098 {
0099     Q_ASSERT(sheet != 0);
0100     Q_ASSERT_X(1 <= col && col <= KS_colMax, __FUNCTION__, QString("%1 out of bounds").arg(col).toLocal8Bit());
0101     Q_ASSERT_X(1 <= row && row <= KS_rowMax, __FUNCTION__, QString("%1 out of bounds").arg(row).toLocal8Bit());
0102     d->sheet = const_cast<Sheet*>(sheet);
0103     d->column = col;
0104     d->row = row;
0105 }
0106 
0107 Cell::Cell(const Sheet* sheet, const QPoint& pos)
0108         : d(new Private)
0109 {
0110     Q_ASSERT(sheet != 0);
0111     Q_ASSERT_X(1 <= pos.x() && pos.x() <= KS_colMax, __FUNCTION__, QString("%1 out of bounds").arg(pos.x()).toLocal8Bit());
0112     Q_ASSERT_X(1 <= pos.y() && pos.y() <= KS_rowMax, __FUNCTION__, QString("%1 out of bounds").arg(pos.y()).toLocal8Bit());
0113     d->sheet = const_cast<Sheet*>(sheet);
0114     d->column = pos.x();
0115     d->row = pos.y();
0116 }
0117 
0118 Cell::Cell(const Cell& other)
0119         : d(other.d)
0120 {
0121 }
0122 
0123 Cell::~Cell()
0124 {
0125 }
0126 
0127 // Return the sheet that this cell belongs to.
0128 Sheet* Cell::sheet() const
0129 {
0130     Q_ASSERT(!isNull());
0131     return d->sheet;
0132 }
0133 
0134 KLocale* Cell::locale() const
0135 {
0136     return sheet()->map()->calculationSettings()->locale();
0137 }
0138 
0139 // Return true if this is the default cell.
0140 bool Cell::isDefault() const
0141 {
0142     // check each stored attribute
0143     if (!value().isEmpty())
0144         return false;
0145     if (formula() != Formula::empty())
0146         return false;
0147     if (!link().isEmpty())
0148         return false;
0149     if (doesMergeCells() == true)
0150         return false;
0151     if (!style().isDefault())
0152         return false;
0153     if (!comment().isEmpty())
0154         return false;
0155     if (!conditions().isEmpty())
0156         return false;
0157     if (!validity().isEmpty())
0158         return false;
0159     return true;
0160 }
0161 
0162 // Return true if this is the default cell (apart from maybe a custom style).
0163 bool Cell::hasDefaultContent() const
0164 {
0165     // check each stored attribute
0166     if (value() != Value())
0167         return false;
0168     if (formula() != Formula::empty())
0169         return false;
0170     if (!link().isEmpty())
0171         return false;
0172     if (doesMergeCells() == true)
0173         return false;
0174     if (!comment().isEmpty())
0175         return false;
0176     if (!conditions().isEmpty())
0177         return false;
0178     if (!validity().isEmpty())
0179         return false;
0180     return true;
0181 }
0182 
0183 bool Cell::isEmpty() const
0184 {
0185     // empty = no value or formula
0186     if (value() != Value())
0187         return false;
0188     if (formula() != Formula())
0189         return false;
0190     return true;
0191 }
0192 
0193 bool Cell::isNull() const
0194 {
0195     return (!d);
0196 }
0197 
0198 // Return true if this cell is a formula.
0199 //
0200 bool Cell::isFormula() const
0201 {
0202     return !formula().expression().isEmpty();
0203 }
0204 
0205 // Return the column number of this cell.
0206 //
0207 int Cell::column() const
0208 {
0209     // Make sure this isn't called for the null cell.  This assert
0210     // can save you (could have saved me!) the hassle of some very
0211     // obscure bugs.
0212     Q_ASSERT(!isNull());
0213     Q_ASSERT(1 <= d->column);   //&& d->column <= KS_colMax );
0214     return d->column;
0215 }
0216 
0217 // Return the row number of this cell.
0218 int Cell::row() const
0219 {
0220     // Make sure this isn't called for the null cell.  This assert
0221     // can save you (could have saved me!) the hassle of some very
0222     // obscure bugs.
0223     Q_ASSERT(!isNull());
0224     Q_ASSERT(1 <= d->row);   //&& d->row <= KS_rowMax );
0225     return d->row;
0226 }
0227 
0228 // Return the name of this cell, i.e. the string that the user would
0229 // use to reference it.  Example: A1, BZ16
0230 //
0231 QString Cell::name() const
0232 {
0233     return name(column(), row());
0234 }
0235 
0236 // Return the name of any cell given by (col, row).
0237 //
0238 // static
0239 QString Cell::name(int col, int row)
0240 {
0241     return columnName(col) + QString::number(row);
0242 }
0243 
0244 // Return the name of this cell, including the sheet name.
0245 // Example: sheet1!A5
0246 //
0247 QString Cell::fullName() const
0248 {
0249     return fullName(sheet(), column(), row());
0250 }
0251 
0252 // Return the full name of any cell given a sheet and (col, row).
0253 //
0254 // static
0255 QString Cell::fullName(const Sheet* s, int col, int row)
0256 {
0257     return s->sheetName() + '!' + name(col, row);
0258 }
0259 
0260 // Return the symbolic name of the column of this cell.  Examples: A, BB.
0261 //
0262 QString Cell::columnName() const
0263 {
0264     return columnName(column());
0265 }
0266 
0267 // Return the symbolic name of any column.
0268 //
0269 // static
0270 QString Cell::columnName(uint column)
0271 {
0272     if (column < 1)     //|| column > KS_colMax)
0273         return QString("@@@");
0274 
0275     QString   str;
0276     unsigned  digits = 1;
0277     unsigned  offset = 0;
0278 
0279     --column;
0280 
0281     for (unsigned limit = 26; column >= limit + offset; limit *= 26, ++digits)
0282         offset += limit;
0283 
0284     for (unsigned col = column - offset; digits; --digits, col /= 26)
0285         str.prepend(QChar('A' + (col % 26)));
0286 
0287     return str;
0288 }
0289 
0290 QString Cell::comment() const
0291 {
0292     return sheet()->cellStorage()->comment(d->column, d->row);
0293 }
0294 
0295 void Cell::setComment(const QString& comment)
0296 {
0297     sheet()->cellStorage()->setComment(Region(cellPosition()), comment);
0298 }
0299 
0300 Conditions Cell::conditions() const
0301 {
0302     return sheet()->cellStorage()->conditions(d->column, d->row);
0303 }
0304 
0305 void Cell::setConditions(const Conditions& conditions)
0306 {
0307     sheet()->cellStorage()->setConditions(Region(cellPosition()), conditions);
0308 }
0309 
0310 Database Cell::database() const
0311 {
0312     return sheet()->cellStorage()->database(d->column, d->row);
0313 }
0314 
0315 Formula Cell::formula() const
0316 {
0317     return sheet()->cellStorage()->formula(d->column, d->row);
0318 }
0319 
0320 void Cell::setFormula(const Formula& formula)
0321 {
0322     sheet()->cellStorage()->setFormula(column(), row(), formula);
0323 }
0324 
0325 Style Cell::style() const
0326 {
0327     return sheet()->cellStorage()->style(d->column, d->row);
0328 }
0329 
0330 Style Cell::effectiveStyle() const
0331 {
0332     Style style = sheet()->cellStorage()->style(d->column, d->row);
0333     // use conditional formatting attributes
0334     const Style conditionalStyle = conditions().testConditions(*this);
0335     if (!conditionalStyle.isEmpty()) {
0336         style.merge(conditionalStyle);
0337     }
0338     return style;
0339 }
0340 
0341 void Cell::setStyle(const Style& style)
0342 {
0343     sheet()->cellStorage()->setStyle(Region(cellPosition()), style);
0344     sheet()->cellStorage()->styleStorage()->contains(cellPosition());
0345 }
0346 
0347 Validity Cell::validity() const
0348 {
0349     return sheet()->cellStorage()->validity(d->column, d->row);
0350 }
0351 
0352 void Cell::setValidity(Validity validity)
0353 {
0354     sheet()->cellStorage()->setValidity(Region(cellPosition()), validity);
0355 }
0356 
0357 
0358 
0359 
0360 
0361 // Return the user input of this cell.  This could, for instance, be a
0362 // formula.
0363 //
0364 QString Cell::userInput() const
0365 {
0366     const Formula formula = this->formula();
0367     if (!formula.expression().isEmpty())
0368         return formula.expression();
0369     return sheet()->cellStorage()->userInput(d->column, d->row);
0370 }
0371 
0372 void Cell::setUserInput(const QString& string)
0373 {
0374     QString old = userInput();
0375 
0376     if (!string.isEmpty() && string[0] == '=') {
0377         // set the formula
0378         Formula formula(sheet(), *this);
0379         formula.setExpression(string);
0380         setFormula(formula);
0381         // remove an existing user input (the non-formula one)
0382         sheet()->cellStorage()->setUserInput(d->column, d->row, QString());
0383     } else {
0384         // remove an existing formula
0385         setFormula(Formula::empty());
0386         // set the value
0387         sheet()->cellStorage()->setUserInput(d->column, d->row, string);
0388     }
0389 
0390     if (old != string) {
0391         // remove any existing richtext
0392         setRichText(QSharedPointer<QTextDocument>());
0393     }
0394 }
0395 
0396 void Cell::setRawUserInput(const QString& string)
0397 {
0398     if (!string.isEmpty() && string[0] == '=') {
0399         // set the formula
0400         Formula formula(sheet(), *this);
0401         formula.setExpression(string);
0402         setFormula(formula);
0403     } else {
0404         // set the value
0405         sheet()->cellStorage()->setUserInput(d->column, d->row, string);
0406     }
0407 }
0408 
0409 
0410 // Return the out text, i.e. the text that is visible in the cells
0411 // square when shown.  This could, for instance, be the calculated
0412 // result of a formula.
0413 //
0414 QString Cell::displayText(const Style& s, Value *v, bool *showFormula) const
0415 {
0416     if (isNull())
0417         return QString();
0418 
0419     QString string;
0420     const Style style = s.isEmpty() ? effectiveStyle() : s;
0421     // Display a formula if warranted.  If not, display the value instead;
0422     // this is the most common case.
0423     if ( isFormula() && !(sheet()->isProtected() && style.hideFormula()) &&
0424          ( (showFormula && *showFormula) || (!showFormula && sheet()->getShowFormula()) ) )
0425     {
0426         string = userInput();
0427         if (showFormula)
0428             *showFormula = true;
0429     } else if (!isEmpty()) {
0430         Value theValue = sheet()->map()->formatter()->formatText(value(), style.formatType(), style.precision(),
0431                  style.floatFormat(), style.prefix(),
0432                  style.postfix(), style.currency().symbol(),
0433                  style.customFormat(), style.thousandsSep());
0434         if (v) *v = theValue;
0435         string = theValue.asString();
0436         if (showFormula)
0437             *showFormula = false;
0438     }
0439     return string;
0440 }
0441 
0442 
0443 // Return the value of this cell.
0444 //
0445 const Value Cell::value() const
0446 {
0447     return sheet()->cellStorage()->value(d->column, d->row);
0448 }
0449 
0450 
0451 // Set the value of this cell.
0452 //
0453 void Cell::setValue(const Value& value)
0454 {
0455     sheet()->cellStorage()->setValue(d->column, d->row, value);
0456 }
0457 
0458 
0459 QSharedPointer<QTextDocument> Cell::richText() const
0460 {
0461     return sheet()->cellStorage()->richText(d->column, d->row);
0462 }
0463 
0464 void Cell::setRichText(QSharedPointer<QTextDocument> text)
0465 {
0466     sheet()->cellStorage()->setRichText(d->column, d->row, text);
0467 }
0468 
0469 // FIXME: Continue commenting and cleaning here (ingwa)
0470 
0471 
0472 void Cell::copyFormat(const Cell& cell)
0473 {
0474     Q_ASSERT(!isNull());   // trouble ahead...
0475     Q_ASSERT(!cell.isNull());
0476     Value value = this->value();
0477     value.setFormat(cell.value().format());
0478     sheet()->cellStorage()->setValue(d->column, d->row, value);
0479     if (!style().isDefault() || !cell.style().isDefault())
0480         setStyle(cell.style());
0481     if (!conditions().isEmpty() || !cell.conditions().isEmpty())
0482         setConditions(cell.conditions());
0483 }
0484 
0485 void Cell::copyAll(const Cell& cell)
0486 {
0487     Q_ASSERT(!isNull());   // trouble ahead...
0488     Q_ASSERT(!cell.isNull());
0489     copyFormat(cell);
0490     copyContent(cell);
0491     if (!comment().isEmpty() || !cell.comment().isEmpty())
0492         setComment(cell.comment());
0493     if (!validity().isEmpty() || !cell.validity().isEmpty())
0494         setValidity(cell.validity());
0495 }
0496 
0497 void Cell::copyContent(const Cell& cell)
0498 {
0499     Q_ASSERT(!isNull());   // trouble ahead...
0500     Q_ASSERT(!cell.isNull());
0501     if (cell.isFormula()) {
0502         // change all the references, e.g. from A1 to A3 if copying
0503         // from e.g. B2 to B4
0504         Formula formula(sheet(), *this);
0505         formula.setExpression(decodeFormula(cell.encodeFormula()));
0506         setFormula(formula);
0507     } else {
0508         // copy the user input
0509         sheet()->cellStorage()->setUserInput(d->column, d->row, cell.userInput());
0510     }
0511     // copy the value in both cases
0512     sheet()->cellStorage()->setValue(d->column, d->row, cell.value());
0513 }
0514 
0515 bool Cell::needsPrinting() const
0516 {
0517     if (!userInput().trimmed().isEmpty())
0518         return true;
0519     if (!comment().trimmed().isEmpty())
0520         return true;
0521 
0522     const Style style = effectiveStyle();
0523 
0524     // Cell borders?
0525     if (style.hasAttribute(Style::TopPen) ||
0526             style.hasAttribute(Style::LeftPen) ||
0527             style.hasAttribute(Style::RightPen) ||
0528             style.hasAttribute(Style::BottomPen) ||
0529             style.hasAttribute(Style::FallDiagonalPen) ||
0530             style.hasAttribute(Style::GoUpDiagonalPen))
0531         return true;
0532 
0533     // Background color or brush?
0534     if (style.hasAttribute(Style::BackgroundBrush)) {
0535         QBrush brush = style.backgroundBrush();
0536 
0537         // Only brushes that are visible (ie. they have a brush style
0538         // and are not white) need to be drawn
0539         if ((brush.style() != Qt::NoBrush) &&
0540                 (brush.color() != Qt::white || !brush.texture().isNull()))
0541             return true;
0542     }
0543 
0544     if (style.hasAttribute(Style::BackgroundColor)) {
0545         debugSheetsRender << "needsPrinting: Has background color";
0546         QColor backgroundColor = style.backgroundColor();
0547 
0548         // We don't need to print anything, if the background is white opaque or fully transparent.
0549         if (!(backgroundColor == Qt::white || backgroundColor.alpha() == 0))
0550             return true;
0551     }
0552 
0553     return false;
0554 }
0555 
0556 
0557 QString Cell::encodeFormula(bool fixedReferences) const
0558 {
0559     if (!isFormula())
0560         return QString();
0561 
0562     QString result('=');
0563     const Tokens tokens = formula().tokens();
0564     for (int i = 0; i < tokens.count(); ++i) {
0565         const Token token = tokens[i];
0566         switch (token.type()) {
0567         case Token::Cell:
0568         case Token::Range: {
0569             if (sheet()->map()->namedAreaManager()->contains(token.text())) {
0570                 result.append(token.text()); // simply keep the area name
0571                 break;
0572             }
0573             const Region region(token.text(), sheet()->map());
0574             // Actually, a contiguous region, but the fixation is needed
0575             Region::ConstIterator end = region.constEnd();
0576             for (Region::ConstIterator it = region.constBegin(); it != end; ++it) {
0577                 if (!(*it)->isValid())
0578                     continue;
0579                 if ((*it)->type() == Region::Element::Point) {
0580                     if ((*it)->sheet())
0581                         result.append((*it)->sheet()->sheetName() + '!');
0582                     const QPoint pos = (*it)->rect().topLeft();
0583                     if ((*it)->isColumnFixed())
0584                         result.append(QString("$%1").arg(pos.x()));
0585                     else if (fixedReferences)
0586                         result.append(QChar(0xA7) + QString("%1").arg(pos.x()));
0587                     else
0588                         result.append(QString("#%1").arg(pos.x() - (int)d->column));
0589                     if ((*it)->isRowFixed())
0590                         result.append(QString("$%1#").arg(pos.y()));
0591                     else if (fixedReferences)
0592                         result.append(QChar(0xA7) + QString("%1#").arg(pos.y()));
0593                     else
0594                         result.append(QString("#%1#").arg(pos.y() - (int)d->row));
0595                 } else { // ((*it)->type() == Region::Range)
0596                     if ((*it)->sheet())
0597                         result.append((*it)->sheet()->sheetName() + '!');
0598                     QPoint pos = (*it)->rect().topLeft();
0599                     if ((*it)->isLeftFixed())
0600                         result.append(QString("$%1").arg(pos.x()));
0601                     else if (fixedReferences)
0602                         result.append(QChar(0xA7) + QString("%1").arg(pos.x()));
0603                     else
0604                         result.append(QString("#%1").arg(pos.x() - (int)d->column));
0605                     if ((*it)->isTopFixed())
0606                         result.append(QString("$%1#").arg(pos.y()));
0607                     else if (fixedReferences)
0608                         result.append(QChar(0xA7) + QString("%1#").arg(pos.y()));
0609                     else
0610                         result.append(QString("#%1#").arg(pos.y() - (int)d->row));
0611                     result.append(':');
0612                     pos = (*it)->rect().bottomRight();
0613                     if ((*it)->isRightFixed())
0614                         result.append(QString("$%1").arg(pos.x()));
0615                     else if (fixedReferences)
0616                         result.append(QChar(0xA7) + QString("%1").arg(pos.x()));
0617                     else
0618                         result.append(QString("#%1").arg(pos.x() - (int)d->column));
0619                     if ((*it)->isBottomFixed())
0620                         result.append(QString("$%1#").arg(pos.y()));
0621                     else if (fixedReferences)
0622                         result.append(QChar(0xA7) + QString("%1#").arg(pos.y()));
0623                     else
0624                         result.append(QString("#%1#").arg(pos.y() - (int)d->row));
0625                 }
0626             }
0627             break;
0628         }
0629         default: {
0630             result.append(token.text());
0631             break;
0632         }
0633         }
0634     }
0635     //debugSheets << result;
0636     return result;
0637 }
0638 
0639 QString Cell::decodeFormula(const QString &_text) const
0640 {
0641     QString erg;
0642     unsigned int pos = 0;
0643     const unsigned int length = _text.length();
0644 
0645     if (_text.isEmpty())
0646         return QString();
0647 
0648     while (pos < length) {
0649         if (_text[pos] == '"') {
0650             erg += _text[pos++];
0651             while (pos < length && _text[pos] != '"') {
0652                 erg += _text[pos++];
0653                 // Allow escaped double quotes (\")
0654                 if (pos < length && _text[pos] == '\\' && _text[pos+1] == '"') {
0655                     erg += _text[pos++];
0656                     erg += _text[pos++];
0657                 }
0658             }
0659             if (pos < length)
0660                 erg += _text[pos++];
0661         } else if (_text[pos] == '#' || _text[pos] == '$' || _text[pos] == QChar(0xA7)) {
0662             bool abs1 = false;
0663             bool abs2 = false;
0664             bool era1 = false; // if 1st is relative but encoded absolutely
0665             bool era2 = false;
0666 
0667             QChar _t = _text[pos++];
0668             if (_t == '$')
0669                 abs1 = true;
0670             else if (_t == QChar(0xA7))
0671                 era1 = true;
0672 
0673             int col = 0;
0674             unsigned int oldPos = pos;
0675             while (pos < length && (_text[pos].isDigit() || _text[pos] == '-')) ++pos;
0676             if (pos != oldPos)
0677                 col = _text.mid(oldPos, pos - oldPos).toInt();
0678             if (!abs1 && !era1)
0679                 col += d->column;
0680             // Skip '#' or '$'
0681 
0682             _t = _text[pos++];
0683             if (_t == '$')
0684                 abs2 = true;
0685             else if (_t == QChar(0xA7))
0686                 era2 = true;
0687 
0688             int row = 0;
0689             oldPos = pos;
0690             while (pos < length && (_text[pos].isDigit() || _text[pos] == '-')) ++pos;
0691             if (pos != oldPos)
0692                 row = _text.mid(oldPos, pos - oldPos).toInt();
0693             if (!abs2 && !era2)
0694                 row += d->row;
0695             // Skip '#' or '$'
0696             ++pos;
0697             if (row < 1 || col < 1 || row > KS_rowMax || col > KS_colMax) {
0698                 debugSheetsODF << "Cell::decodeFormula: row or column out of range (col:" << col << " | row:" << row << ')';
0699                 erg += Value::errorREF().errorMessage();
0700             } else {
0701                 if (abs1)
0702                     erg += '$';
0703                 erg += Cell::columnName(col); //Get column text
0704 
0705                 if (abs2)
0706                     erg += '$';
0707                 erg += QString::number(row);
0708             }
0709         } else
0710             erg += _text[pos++];
0711     }
0712 
0713     return erg;
0714 }
0715 
0716 
0717 // ----------------------------------------------------------------
0718 //                          Formula handling
0719 
0720 
0721 bool Cell::makeFormula()
0722 {
0723 //   debugSheetsFormula ;
0724 
0725     // sanity check
0726     if (!isFormula())
0727         return false;
0728 
0729     // parse the formula and check for errors
0730     if (!formula().isValid()) {
0731         sheet()->showStatusMessage(i18n("Parsing of formula in cell %1 failed.", fullName()));
0732         setValue(Value::errorPARSE());
0733         return false;
0734     }
0735     return true;
0736 }
0737 
0738 int Cell::effectiveAlignX() const
0739 {
0740     const Style style = effectiveStyle();
0741     int align = style.halign();
0742     if (align == Style::HAlignUndefined) {
0743         //numbers should be right-aligned by default, as well as BiDi text
0744         if ((style.formatType() == Format::Text) || value().isString())
0745             align = (displayText().isRightToLeft()) ? Style::Right : Style::Left;
0746         else {
0747             Value val = value();
0748             while (val.isArray()) val = val.element(0, 0);
0749             if (val.isBoolean() || val.isNumber())
0750                 align = Style::Right;
0751             else
0752                 align = Style::Left;
0753         }
0754     }
0755     return align;
0756 }
0757 
0758 double Cell::width() const
0759 {
0760     const int rightCol = d->column + mergedXCells();
0761     double width = 0.0;
0762     for (int col = d->column; col <= rightCol; ++col)
0763         width += sheet()->columnFormat(col)->width();
0764     return width;
0765 }
0766 
0767 double Cell::height() const
0768 {
0769     const int bottomRow = d->row + mergedYCells();
0770     return sheet()->rowFormats()->totalRowHeight(d->row, bottomRow);
0771 }
0772 
0773 // parses the text
0774 void Cell::parseUserInput(const QString& text)
0775 {
0776 //   debugSheets ;
0777 
0778     // empty string?
0779     if (text.isEmpty()) {
0780         setValue(Value::empty());
0781         setUserInput(text);
0782         setFormula(Formula::empty());
0783         return;
0784     }
0785 
0786     // a formula?
0787     if (text[0] == '=') {
0788         Formula formula(sheet(), *this);
0789         formula.setExpression(text);
0790         setFormula(formula);
0791 
0792         // parse the formula and check for errors
0793         if (!formula.isValid()) {
0794             sheet()->showStatusMessage(i18n("Parsing of formula in cell %1 failed.", fullName()));
0795             setValue(Value::errorPARSE());
0796             return;
0797         }
0798         return;
0799     }
0800 
0801     // keep the old formula and value for the case, that validation fails
0802     const Formula oldFormula = formula();
0803     const QString oldUserInput = userInput();
0804     const Value oldValue = value();
0805 
0806     // here, the new value is not a formula anymore; clear an existing one
0807     setFormula(Formula());
0808 
0809     Value value;
0810     if (style().formatType() == Format::Text)
0811         value = Value(QString(text));
0812     else {
0813         // Parses the text and return the appropriate value.
0814         value = sheet()->map()->parser()->parse(text);
0815 
0816 #if 0
0817         // Parsing as time acts like an autoformat: we even change the input text
0818         // [h]:mm:ss -> might get set by ValueParser
0819         if (isTime() && (formatType() != Format::Time7))
0820             setUserInput(locale()->formatTime(value().asDateTime(sheet()->map()->calculationSettings()).time(), true));
0821 #endif
0822 
0823         // convert first letter to uppercase ?
0824         if (sheet()->getFirstLetterUpper() && value.isString() && !text.isEmpty()) {
0825             QString str = value.asString();
0826             value = Value(str[0].toUpper() + str.right(str.length() - 1));
0827         }
0828     }
0829     // set the new value
0830     setUserInput(text);
0831     setValue(value);
0832 
0833     // validation
0834     if (!sheet()->isLoading()) {
0835         Validity validity = this->validity();
0836         if (!validity.testValidity(this)) {
0837             debugSheetsODF << "Validation failed";
0838             //reapply old value if action == stop
0839             setFormula(oldFormula);
0840             setUserInput(oldUserInput);
0841             setValue(oldValue);
0842         }
0843     }
0844 }
0845 
0846 QString Cell::link() const
0847 {
0848     return sheet()->cellStorage()->link(d->column, d->row);
0849 }
0850 
0851 void Cell::setLink(const QString& link)
0852 {
0853     sheet()->cellStorage()->setLink(d->column, d->row, link);
0854 
0855     if (!link.isEmpty() && userInput().isEmpty())
0856         parseUserInput(link);
0857 }
0858 
0859 bool Cell::isDate() const
0860 {
0861     const Format::Type t = style().formatType();
0862     return (Format::isDate(t) || ((t == Format::Generic) && (value().format() == Value::fmt_Date)));
0863 }
0864 
0865 bool Cell::isTime() const
0866 {
0867     const Format::Type t = style().formatType();
0868     return (Format::isTime(t) || ((t == Format::Generic) && (value().format() == Value::fmt_Time)));
0869 }
0870 
0871 bool Cell::isText() const
0872 {
0873     const Format::Type t = style().formatType();
0874     return t == Format::Text;
0875 }
0876 
0877 // Return true if this cell is part of a merged cell, but not the
0878 // master cell.
0879 
0880 bool Cell::isPartOfMerged() const
0881 {
0882     return sheet()->cellStorage()->isPartOfMerged(d->column, d->row);
0883 }
0884 
0885 Cell Cell::masterCell() const
0886 {
0887     return sheet()->cellStorage()->masterCell(d->column, d->row);
0888 }
0889 
0890 // Merge a number of cells, i.e. make this cell obscure a number of
0891 // other cells.  If _x and _y == 0, then the merging is removed.
0892 void Cell::mergeCells(int _col, int _row, int _x, int _y)
0893 {
0894     sheet()->cellStorage()->mergeCells(_col, _row, _x, _y);
0895 }
0896 
0897 bool Cell::doesMergeCells() const
0898 {
0899     return sheet()->cellStorage()->doesMergeCells(d->column, d->row);
0900 }
0901 
0902 int Cell::mergedXCells() const
0903 {
0904     return sheet()->cellStorage()->mergedXCells(d->column, d->row);
0905 }
0906 
0907 int Cell::mergedYCells() const
0908 {
0909     return sheet()->cellStorage()->mergedYCells(d->column, d->row);
0910 }
0911 
0912 bool Cell::isLocked() const
0913 {
0914     return sheet()->cellStorage()->isLocked(d->column, d->row);
0915 }
0916 
0917 QRect Cell::lockedCells() const
0918 {
0919     return sheet()->cellStorage()->lockedCells(d->column, d->row);
0920 }
0921 
0922 
0923 // ================================================================
0924 //                       Saving and loading
0925 
0926 
0927 QDomElement Cell::save(QDomDocument& doc, int xOffset, int yOffset, bool era)
0928 {
0929     // Save the position of this cell
0930     QDomElement cell = doc.createElement("cell");
0931     cell.setAttribute("row", QString::number(row() - yOffset));
0932     cell.setAttribute("column", QString::number(column() - xOffset));
0933 
0934     //
0935     // Save the formatting information
0936     //
0937     QDomElement formatElement(doc.createElement("format"));
0938     style().saveXML(doc, formatElement, sheet()->map()->styleManager());
0939     if (formatElement.hasChildNodes() || formatElement.attributes().length())   // don't save empty tags
0940         cell.appendChild(formatElement);
0941 
0942     if (doesMergeCells()) {
0943         if (mergedXCells())
0944             formatElement.setAttribute("colspan", QString::number(mergedXCells()));
0945         if (mergedYCells())
0946             formatElement.setAttribute("rowspan", QString::number(mergedYCells()));
0947     }
0948 
0949     Conditions conditions = this->conditions();
0950     if (!conditions.isEmpty()) {
0951         QDomElement conditionElement = conditions.saveConditions(doc, sheet()->map()->converter());
0952         if (!conditionElement.isNull())
0953             cell.appendChild(conditionElement);
0954     }
0955 
0956     Validity validity = this->validity();
0957     if (!validity.isEmpty()) {
0958         QDomElement validityElement = validity.saveXML(doc, sheet()->map()->converter());
0959         if (!validityElement.isNull())
0960             cell.appendChild(validityElement);
0961     }
0962 
0963     const QString comment = this->comment();
0964     if (!comment.isEmpty()) {
0965         QDomElement commentElement = doc.createElement("comment");
0966         commentElement.appendChild(doc.createCDATASection(comment));
0967         cell.appendChild(commentElement);
0968     }
0969 
0970     //
0971     // Save the text
0972     //
0973     if (!userInput().isEmpty()) {
0974         // Formulas need to be encoded to ensure that they
0975         // are position independent.
0976         if (isFormula()) {
0977             QDomElement txt = doc.createElement("text");
0978             // if we are cutting to the clipboard, relative references need to be encoded absolutely
0979             txt.appendChild(doc.createTextNode(encodeFormula(era)));
0980             cell.appendChild(txt);
0981 
0982             /* we still want to save the results of the formula */
0983             QDomElement formulaResult = doc.createElement("result");
0984             saveCellResult(doc, formulaResult, displayText());
0985             cell.appendChild(formulaResult);
0986 
0987         } else if (!link().isEmpty()) {
0988             // KSpread pre 1.4 saves link as rich text, marked with first char '
0989             // Have to be saved in some CDATA section because of too many special characters.
0990             QDomElement txt = doc.createElement("text");
0991             QString qml = "!<a href=\"" + link() + "\">" + userInput() + "</a>";
0992             txt.appendChild(doc.createCDATASection(qml));
0993             cell.appendChild(txt);
0994         } else {
0995             // Save the cell contents (in a locale-independent way)
0996             QDomElement txt = doc.createElement("text");
0997             saveCellResult(doc, txt, userInput());
0998             cell.appendChild(txt);
0999         }
1000     }
1001     if (cell.hasChildNodes() || cell.attributes().length() > 2)   // don't save empty tags
1002         // (the >2 is due to "row" and "column" attributes)
1003         return cell;
1004     else
1005         return QDomElement();
1006 }
1007 
1008 bool Cell::saveCellResult(QDomDocument& doc, QDomElement& result,
1009                           QString str)
1010 {
1011     QString dataType = "Other"; // fallback
1012 
1013     if (value().isNumber()) {
1014         if (isDate()) {
1015             // serial number of date
1016             QDate dd = value().asDateTime(sheet()->map()->calculationSettings()).date();
1017             dataType = "Date";
1018             str = "%1/%2/%3";
1019             str = str.arg(dd.year()).arg(dd.month()).arg(dd.day());
1020         } else if (isTime()) {
1021             // serial number of time
1022             dataType = "Time";
1023             str = value().asDateTime(sheet()->map()->calculationSettings()).time().toString();
1024         } else {
1025             // real number
1026             dataType = "Num";
1027             if (value().isInteger())
1028                 str = QString::number(value().asInteger());
1029             else
1030                 str = QString::number(numToDouble(value().asFloat()), 'g', DBL_DIG);
1031         }
1032     }
1033 
1034     if (value().isBoolean()) {
1035         dataType = "Bool";
1036         str = value().asBoolean() ? "true" : "false";
1037     }
1038 
1039     if (value().isString()) {
1040         dataType = "Str";
1041         str = value().asString();
1042     }
1043 
1044     result.setAttribute("dataType", dataType);
1045 
1046     const QString displayText = this->displayText();
1047     if (!displayText.isEmpty())
1048         result.setAttribute("outStr", displayText);
1049     result.appendChild(doc.createTextNode(str));
1050 
1051     return true; /* really isn't much of a way for this function to fail */
1052 }
1053 
1054 bool Cell::load(const KoXmlElement & cell, int _xshift, int _yshift,
1055                 Paste::Mode mode, Paste::Operation op, bool paste)
1056 {
1057     bool ok;
1058 
1059     //
1060     // First of all determine in which row and column this
1061     // cell belongs.
1062     //
1063     d->row = cell.attribute("row").toInt(&ok) + _yshift;
1064     if (!ok) return false;
1065     d->column = cell.attribute("column").toInt(&ok) + _xshift;
1066     if (!ok) return false;
1067 
1068     // Validation
1069     if (d->row < 1 || d->row > KS_rowMax) {
1070         debugSheets << "Cell::load: Value out of range Cell:row=" << d->row;
1071         return false;
1072     }
1073     if (d->column < 1 || d->column > KS_colMax) {
1074         debugSheets << "Cell::load: Value out of range Cell:column=" << d->column;
1075         return false;
1076     }
1077 
1078     //
1079     // Load formatting information.
1080     //
1081     KoXmlElement formatElement = cell.namedItem("format").toElement();
1082     if (!formatElement.isNull() &&
1083             ((mode == Paste::Normal) || (mode == Paste::Format) || (mode == Paste::NoBorder))) {
1084         int mergedXCells = 0;
1085         int mergedYCells = 0;
1086         if (formatElement.hasAttribute("colspan")) {
1087             int i = formatElement.attribute("colspan").toInt(&ok);
1088             if (!ok) return false;
1089             // Validation
1090             if (i < 0 || i > KS_spanMax) {
1091                 debugSheets << "Value out of range Cell::colspan=" << i;
1092                 return false;
1093             }
1094             if (i)
1095                 mergedXCells = i;
1096         }
1097 
1098         if (formatElement.hasAttribute("rowspan")) {
1099             int i = formatElement.attribute("rowspan").toInt(&ok);
1100             if (!ok) return false;
1101             // Validation
1102             if (i < 0 || i > KS_spanMax) {
1103                 debugSheets << "Value out of range Cell::rowspan=" << i;
1104                 return false;
1105             }
1106             if (i)
1107                 mergedYCells = i;
1108         }
1109 
1110         if (mergedXCells != 0 || mergedYCells != 0)
1111             mergeCells(d->column, d->row, mergedXCells, mergedYCells);
1112 
1113         Style style;
1114         if (!style.loadXML(formatElement, mode))
1115             return false;
1116         setStyle(style);
1117     }
1118 
1119     //
1120     // Load the condition section of a cell.
1121     //
1122     KoXmlElement conditionsElement = cell.namedItem("condition").toElement();
1123     if (!conditionsElement.isNull()) {
1124         Conditions conditions;
1125         Map *const map = sheet()->map();
1126         ValueParser *const valueParser = map->parser();
1127         conditions.loadConditions(conditionsElement, valueParser);
1128         if (!conditions.isEmpty())
1129             setConditions(conditions);
1130     } else if (paste && (mode == Paste::Normal || mode == Paste::NoBorder)) {
1131         //clear the conditional formatting
1132         setConditions(Conditions());
1133     }
1134 
1135     KoXmlElement validityElement = cell.namedItem("validity").toElement();
1136     if (!validityElement.isNull()) {
1137         Validity validity;
1138         if (validity.loadXML(this, validityElement))
1139             setValidity(validity);
1140     } else if (paste && (mode == Paste::Normal || mode == Paste::NoBorder)) {
1141         // clear the validity
1142         setValidity(Validity());
1143     }
1144 
1145     //
1146     // Load the comment
1147     //
1148     KoXmlElement comment = cell.namedItem("comment").toElement();
1149     if (!comment.isNull() &&
1150             (mode == Paste::Normal || mode == Paste::Comment || mode == Paste::NoBorder)) {
1151         QString t = comment.text();
1152         //t = t.trimmed();
1153         setComment(t);
1154     }
1155 
1156     //
1157     // The real content of the cell is loaded here. It is stored in
1158     // the "text" tag, which contains either a text or a CDATA section.
1159     //
1160     // TODO: make this suck less. We set data twice, in loadCellData, and
1161     // also here. Not good.
1162     KoXmlElement text = cell.namedItem("text").toElement();
1163 
1164     if (!text.isNull() &&
1165             (mode == Paste::Normal || mode == Paste::Text || mode == Paste::NoBorder || mode == Paste::Result)) {
1166 
1167         /* older versions mistakenly put the datatype attribute on the cell instead
1168            of the text. Just move it over in case we're parsing an old document */
1169         QString dataType;
1170         if (cell.hasAttribute("dataType"))     // new docs
1171             dataType = cell.attribute("dataType");
1172 
1173         KoXmlElement result = cell.namedItem("result").toElement();
1174         QString txt = text.text();
1175         if ((mode == Paste::Result) && (txt[0] == '='))
1176             // paste text of the element, if we want to paste result
1177             // and the source cell contains a formula
1178             setUserInput(result.text());
1179         else
1180             //otherwise copy everything
1181             loadCellData(text, op, dataType);
1182 
1183         if (!result.isNull()) {
1184             QString dataType;
1185             QString t = result.text();
1186 
1187             if (result.hasAttribute("dataType"))
1188                 dataType = result.attribute("dataType");
1189 
1190             // boolean ?
1191             if (dataType == "Bool") {
1192                 if (t == "false")
1193                     setValue(Value(false));
1194                 else if (t == "true")
1195                     setValue(Value(true));
1196             } else if (dataType == "Num") {
1197                 bool ok = false;
1198                 double dd = t.toDouble(&ok);
1199                 if (ok)
1200                     setValue(Value(dd));
1201             } else if (dataType == "Date") {
1202                 bool ok = false;
1203                 double dd = t.toDouble(&ok);
1204                 if (ok) {
1205                     Value value(dd);
1206                     value.setFormat(Value::fmt_Date);
1207                     setValue(value);
1208                 } else {
1209                     int pos   = t.indexOf('/');
1210                     int year  = t.mid(0, pos).toInt();
1211                     int pos1  = t.indexOf('/', pos + 1);
1212                     int month = t.mid(pos + 1, ((pos1 - 1) - pos)).toInt();
1213                     int day   = t.right(t.length() - pos1 - 1).toInt();
1214                     QDate date(year, month, day);
1215                     if (date.isValid())
1216                         setValue(Value(date, sheet()->map()->calculationSettings()));
1217                 }
1218             } else if (dataType == "Time") {
1219                 bool ok = false;
1220                 double dd = t.toDouble(&ok);
1221                 if (ok) {
1222                     Value value(dd);
1223                     value.setFormat(Value::fmt_Time);
1224                     setValue(value);
1225                 } else {
1226                     int hours   = -1;
1227                     int minutes = -1;
1228                     int second  = -1;
1229                     int pos, pos1;
1230                     pos   = t.indexOf(':');
1231                     hours = t.mid(0, pos).toInt();
1232                     pos1  = t.indexOf(':', pos + 1);
1233                     minutes = t.mid(pos + 1, ((pos1 - 1) - pos)).toInt();
1234                     second  = t.right(t.length() - pos1 - 1).toInt();
1235                     QTime time(hours, minutes, second);
1236                     if (time.isValid())
1237                         setValue(Value(time));
1238                 }
1239             } else {
1240                 setValue(Value(t));
1241             }
1242         }
1243     }
1244 
1245     return true;
1246 }
1247 
1248 bool Cell::loadCellData(const KoXmlElement & text, Paste::Operation op, const QString &_dataType)
1249 {
1250     //TODO: use converter()->asString() to generate userInput()
1251 
1252     QString t = text.text();
1253     t = t.trimmed();
1254 
1255     // A formula like =A1+A2 ?
1256     if ((!t.isEmpty()) && (t[0] == '=')) {
1257         t = decodeFormula(t);
1258         parseUserInput(pasteOperation(t, userInput(), op));
1259 
1260         makeFormula();
1261     }
1262     // rich text ?
1263     else if ((!t.isEmpty()) && (t[0] == '!')) {
1264         // KSpread pre 1.4 stores hyperlink as rich text (first char is '!')
1265         // extract the link and the corresponding text
1266         // This is a rather dirty hack, but enough for Calligra Sheets generated XML
1267         bool inside_tag = false;
1268         QString qml_text;
1269         QString tag;
1270         QString qml_link;
1271 
1272         for (int i = 1; i < t.length(); ++i) {
1273             QChar ch = t[i];
1274             if (ch == '<') {
1275                 if (!inside_tag) {
1276                     inside_tag = true;
1277                     tag.clear();
1278                 }
1279             } else if (ch == '>') {
1280                 if (inside_tag) {
1281                     inside_tag = false;
1282                     if (tag.startsWith(QLatin1String("a href=\""), Qt::CaseSensitive) &&
1283                         tag.endsWith(QLatin1Char('"'))) {
1284                         qml_link.remove(0, 8).chop(1);
1285                     }
1286                     tag.clear();
1287                 }
1288             } else {
1289                 if (!inside_tag)
1290                     qml_text += ch;
1291                 else
1292                     tag += ch;
1293             }
1294         }
1295 
1296         if (!qml_link.isEmpty())
1297             setLink(qml_link);
1298         setUserInput(qml_text);
1299         setValue(Value(qml_text));
1300     } else {
1301         bool newStyleLoading = true;
1302         QString dataType = _dataType;
1303 
1304         if (dataType.isNull()) {
1305             if (text.hasAttribute("dataType")) {   // new docs
1306                 dataType = text.attribute("dataType");
1307             } else { // old docs: do the ugly solution of parsing the text
1308                 // ...except for date/time
1309                 if (isDate() && (t.count('/') == 2))
1310                     dataType = "Date";
1311                 else if (isTime() && (t.count(':') == 2))
1312                     dataType = "Time";
1313                 else {
1314                     parseUserInput(pasteOperation(t, userInput(), op));
1315                     newStyleLoading = false;
1316                 }
1317             }
1318         }
1319 
1320         if (newStyleLoading) {
1321             // boolean ?
1322             if (dataType == "Bool")
1323                 setValue(Value(t.toLower() == "true"));
1324 
1325             // number ?
1326             else if (dataType == "Num") {
1327                 bool ok = false;
1328                 if (t.contains('.'))
1329                     setValue(Value(t.toDouble(&ok)));      // We save in non-localized format
1330                 else
1331                     setValue(Value(t.toLongLong(&ok)));
1332                 if (!ok) {
1333                     warnSheets << "Couldn't parse '" << t << "' as number.";
1334                 }
1335                 /* We will need to localize the text version of the number */
1336                 KLocale* locale = sheet()->map()->calculationSettings()->locale();
1337 
1338                 /* KLocale::formatNumber requires the precision we want to return.
1339                 */
1340                 int precision = t.length() - t.indexOf('.') - 1;
1341 
1342                 if (style().formatType() == Format::Percentage) {
1343                     if (value().isInteger())
1344                         t = locale->formatNumber(value().asInteger() * 100);
1345                     else
1346                         t = locale->formatNumber(numToDouble(value().asFloat() * 100.0), precision);
1347                     setUserInput(pasteOperation(t, userInput(), op));
1348                     setUserInput(userInput() + '%');
1349                 } else {
1350                     if (value().isInteger())
1351                         t = locale->formatLong(value().asInteger());
1352                     else
1353                         t = locale->formatNumber(numToDouble(value().asFloat()), precision);
1354                     setUserInput(pasteOperation(t, userInput(), op));
1355                 }
1356             }
1357 
1358             // date ?
1359             else if (dataType == "Date") {
1360                 int pos = t.indexOf('/');
1361                 int year = t.mid(0, pos).toInt();
1362                 int pos1 = t.indexOf('/', pos + 1);
1363                 int month = t.mid(pos + 1, ((pos1 - 1) - pos)).toInt();
1364                 int day = t.right(t.length() - pos1 - 1).toInt();
1365                 setValue(Value(QDate(year, month, day), sheet()->map()->calculationSettings()));
1366                 if (value().asDate(sheet()->map()->calculationSettings()).isValid())   // Should always be the case for new docs
1367                     setUserInput(locale()->formatDate(value().asDate(sheet()->map()->calculationSettings()), KLocale::ShortDate));
1368                 else { // This happens with old docs, when format is set wrongly to date
1369                     parseUserInput(pasteOperation(t, userInput(), op));
1370                 }
1371             }
1372 
1373             // time ?
1374             else if (dataType == "Time") {
1375                 int hours = -1;
1376                 int minutes = -1;
1377                 int second = -1;
1378                 int pos, pos1;
1379                 pos = t.indexOf(':');
1380                 hours = t.mid(0, pos).toInt();
1381                 pos1 = t.indexOf(':', pos + 1);
1382                 minutes = t.mid(pos + 1, ((pos1 - 1) - pos)).toInt();
1383                 second = t.right(t.length() - pos1 - 1).toInt();
1384                 setValue(Value(QTime(hours, minutes, second)));
1385                 if (value().asTime().isValid())    // Should always be the case for new docs
1386                     setUserInput(locale()->formatTime(value().asTime(), true));
1387                 else { // This happens with old docs, when format is set wrongly to time
1388                     parseUserInput(pasteOperation(t, userInput(), op));
1389                 }
1390             }
1391 
1392             else {
1393                 // Set the cell's text
1394                 setUserInput(pasteOperation(t, userInput(), op));
1395                 setValue(Value(userInput()));
1396             }
1397         }
1398     }
1399 
1400     if (!sheet()->isLoading())
1401         parseUserInput(userInput());
1402 
1403     return true;
1404 }
1405 
1406 QTime Cell::toTime(const KoXmlElement &element)
1407 {
1408     //TODO: can't we use tryParseTime (after modification) instead?
1409     QString t = element.text();
1410     t = t.trimmed();
1411     int hours = -1;
1412     int minutes = -1;
1413     int second = -1;
1414     int pos, pos1;
1415     pos = t.indexOf(':');
1416     hours = t.mid(0, pos).toInt();
1417     pos1 = t.indexOf(':', pos + 1);
1418     minutes = t.mid(pos + 1, ((pos1 - 1) - pos)).toInt();
1419     second = t.right(t.length() - pos1 - 1).toInt();
1420     setValue(Value(QTime(hours, minutes, second)));
1421     return value().asTime();
1422 }
1423 
1424 QDate Cell::toDate(const KoXmlElement &element)
1425 {
1426     QString t = element.text();
1427     int pos;
1428     int pos1;
1429     int year = -1;
1430     int month = -1;
1431     int day = -1;
1432     pos = t.indexOf('/');
1433     year = t.mid(0, pos).toInt();
1434     pos1 = t.indexOf('/', pos + 1);
1435     month = t.mid(pos + 1, ((pos1 - 1) - pos)).toInt();
1436     day = t.right(t.length() - pos1 - 1).toInt();
1437     setValue(Value(QDate(year, month, day), sheet()->map()->calculationSettings()));
1438     return value().asDate(sheet()->map()->calculationSettings());
1439 }
1440 
1441 QString Cell::pasteOperation(const QString &new_text, const QString &old_text, Paste::Operation op)
1442 {
1443     if (op == Paste::OverWrite)
1444         return new_text;
1445 
1446     QString tmp_op;
1447     QString tmp;
1448     QString old;
1449 
1450     if (!new_text.isEmpty() && new_text[0] == '=') {
1451         tmp = new_text.right(new_text.length() - 1);
1452     } else {
1453         tmp = new_text;
1454     }
1455 
1456     if (old_text.isEmpty() &&
1457             (op == Paste::Add || op == Paste::Mul || op == Paste::Sub || op == Paste::Div)) {
1458         old = "=0";
1459     }
1460 
1461     if (!old_text.isEmpty() && old_text[0] == '=') {
1462         old = old_text.right(old_text.length() - 1);
1463     } else {
1464         old = old_text;
1465     }
1466 
1467     bool b1, b2;
1468     tmp.toDouble(&b1);
1469     old.toDouble(&b2);
1470     if (b1 && !b2 && old.length() == 0) {
1471         old = '0';
1472         b2 = true;
1473     }
1474 
1475     if (b1 && b2) {
1476         switch (op) {
1477         case  Paste::Add:
1478             tmp_op = QString::number(old.toDouble() + tmp.toDouble());
1479             break;
1480         case Paste::Mul :
1481             tmp_op = QString::number(old.toDouble() * tmp.toDouble());
1482             break;
1483         case Paste::Sub:
1484             tmp_op = QString::number(old.toDouble() - tmp.toDouble());
1485             break;
1486         case Paste::Div:
1487             tmp_op = QString::number(old.toDouble() / tmp.toDouble());
1488             break;
1489         default:
1490             Q_ASSERT(0);
1491         }
1492 
1493         return tmp_op;
1494     } else if ((new_text[0] == '=' && old_text[0] == '=') ||
1495                (b1 && old_text[0] == '=') || (new_text[0] == '=' && b2)) {
1496         switch (op) {
1497         case Paste::Add :
1498             tmp_op = "=(" + old + ")+" + '(' + tmp + ')';
1499             break;
1500         case Paste::Mul :
1501             tmp_op = "=(" + old + ")*" + '(' + tmp + ')';
1502             break;
1503         case Paste::Sub:
1504             tmp_op = "=(" + old + ")-" + '(' + tmp + ')';
1505             break;
1506         case Paste::Div:
1507             tmp_op = "=(" + old + ")/" + '(' + tmp + ')';
1508             break;
1509         default :
1510             Q_ASSERT(0);
1511         }
1512 
1513         tmp_op = decodeFormula(tmp_op);
1514         return tmp_op;
1515     }
1516 
1517     tmp = decodeFormula(new_text);
1518     return tmp;
1519 }
1520 
1521 Cell& Cell::operator=(const Cell & other)
1522 {
1523     d = other.d;
1524     return *this;
1525 }
1526 
1527 bool Cell::operator<(const Cell& other) const
1528 {
1529     if (sheet() != other.sheet())
1530         return sheet() < other.sheet(); // pointers!
1531     if (row() < other.row())
1532         return true;
1533     return ((row() == other.row()) && (column() < other.column()));
1534 }
1535 
1536 bool Cell::operator==(const Cell& other) const
1537 {
1538     return (row() == other.row() && column() == other.column() && sheet() == other.sheet());
1539 }
1540 
1541 bool Cell::operator!() const
1542 {
1543     return (!d);   // isNull()
1544 }
1545 
1546 bool Cell::compareData(const Cell& other) const
1547 {
1548     if (value() != other.value())
1549         return false;
1550     if (formula() != other.formula())
1551         return false;
1552     if (link() != other.link())
1553         return false;
1554     if (mergedXCells() != other.mergedXCells())
1555         return false;
1556     if (mergedYCells() != other.mergedYCells())
1557         return false;
1558     if (style() != other.style())
1559         return false;
1560     if (comment() != other.comment())
1561         return false;
1562     if (conditions() != other.conditions())
1563         return false;
1564     if (validity() != other.validity())
1565         return false;
1566     return true;
1567 }
1568 
1569 QPoint Cell::cellPosition() const
1570 {
1571     Q_ASSERT(!isNull());
1572     return QPoint(column(), row());
1573 }