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 }