00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00029
00030
00031
00032
00033
00034
00035
00036
00037
00038
00039
00040
00041
00042 #include "Cell.h"
00043
00044 #include <stdlib.h>
00045 #include <ctype.h>
00046 #include <float.h>
00047 #include <math.h>
00048
00049 #include "CalculationSettings.h"
00050 #include "CellStorage.h"
00051 #include "Condition.h"
00052 #include "Formula.h"
00053 #include "GenValidationStyle.h"
00054 #include "Global.h"
00055 #include "Localization.h"
00056 #include "LoadingInfo.h"
00057 #include "Map.h"
00058 #include "NamedAreaManager.h"
00059 #include "OdfLoadingContext.h"
00060 #include "OdfSavingContext.h"
00061 #include "RowColumnFormat.h"
00062 #include "Selection.h"
00063 #include "ShapeApplicationData.h"
00064 #include "Sheet.h"
00065 #include "Style.h"
00066 #include "StyleManager.h"
00067 #include "Util.h"
00068 #include "Value.h"
00069 #include "Validity.h"
00070 #include "ValueConverter.h"
00071 #include "ValueFormatter.h"
00072 #include "ValueParser.h"
00073
00074 #include <KoShape.h>
00075 #include <KoShapeLoadingContext.h>
00076 #include <KoShapeRegistry.h>
00077 #include <KoStyleStack.h>
00078 #include <KoXmlNS.h>
00079 #include <KoXmlReader.h>
00080 #include <KoOdfStylesReader.h>
00081 #include <KoXmlWriter.h>
00082
00083 #include <kdebug.h>
00084 #include <KNotification>
00085
00086 #include <QTimer>
00087
00088 using namespace KSpread;
00089
00090 class Cell::Private : public QSharedData
00091 {
00092 public:
00093 Private() : sheet( 0 ), column( 0 ), row( 0 ) {}
00094
00095 Sheet* sheet;
00096 uint column : 16;
00097 uint row : 16;
00098 };
00099
00100
00101 Cell::Cell()
00102 : d( 0 )
00103 {
00104 }
00105
00106 Cell::Cell( const Sheet* sheet, int col, int row )
00107 : d( new Private )
00108 {
00109 Q_ASSERT( sheet != 0 );
00110 Q_ASSERT( 1 <= col && col <= KS_colMax );
00111 Q_ASSERT( 1 <= row && row <= KS_rowMax );
00112 d->sheet = const_cast<Sheet*>( sheet );
00113 d->column = col;
00114 d->row = row;
00115 }
00116
00117 Cell::Cell( const Sheet* sheet, const QPoint& pos )
00118 : d( new Private )
00119 {
00120 Q_ASSERT( sheet != 0 );
00121 Q_ASSERT( 1 <= pos.x() && pos.x() <= KS_colMax );
00122 Q_ASSERT( 1 <= pos.y() && pos.y() <= KS_rowMax );
00123 d->sheet = const_cast<Sheet*>( sheet );
00124 d->column = pos.x();
00125 d->row = pos.y();
00126 }
00127
00128 Cell::Cell( const Cell& other )
00129 : d( other.d )
00130 {
00131 }
00132
00133 Cell::~Cell()
00134 {
00135 }
00136
00137
00138 Sheet* Cell::sheet() const
00139 {
00140 Q_ASSERT( !isNull() );
00141 return d->sheet;
00142 }
00143
00144 KLocale* Cell::locale() const
00145 {
00146 return sheet()->map()->calculationSettings()->locale();
00147 }
00148
00149
00150 bool Cell::isDefault() const
00151 {
00152
00153 if ( value() != Value() )
00154 return false;
00155 if ( formula() != Formula() )
00156 return false;
00157 if (!link().isEmpty())
00158 return false;
00159 if ( doesMergeCells() == true )
00160 return false;
00161 if (!style().isDefault())
00162 return false;
00163 if (!comment().isEmpty())
00164 return false;
00165 if (!conditions().isEmpty())
00166 return false;
00167 if (!validity().isEmpty())
00168 return false;
00169 return true;
00170 }
00171
00172 bool Cell::isEmpty() const
00173 {
00174
00175 if ( value() != Value() )
00176 return false;
00177 if ( formula() != Formula() )
00178 return false;
00179 return true;
00180 }
00181
00182 bool Cell::isNull() const
00183 {
00184 return ( !d );
00185 }
00186
00187
00188
00189 bool Cell::isFormula() const
00190 {
00191 return !formula().expression().isEmpty();
00192 }
00193
00194
00195
00196 int Cell::column() const
00197 {
00198
00199
00200
00201 Q_ASSERT( !isNull() );
00202 Q_ASSERT( 1 <= d->column );
00203 return d->column;
00204 }
00205
00206
00207 int Cell::row() const
00208 {
00209
00210
00211
00212 Q_ASSERT( !isNull() );
00213 Q_ASSERT( 1 <= d->row );
00214 return d->row;
00215 }
00216
00217
00218
00219
00220 QString Cell::name() const
00221 {
00222 return name( column(), row() );
00223 }
00224
00225
00226
00227
00228 QString Cell::name( int col, int row )
00229 {
00230 return columnName( col ) + QString::number( row );
00231 }
00232
00233
00234
00235
00236 QString Cell::fullName() const
00237 {
00238 return fullName( sheet(), column(), row() );
00239 }
00240
00241
00242
00243
00244 QString Cell::fullName( const Sheet* s, int col, int row )
00245 {
00246 return s->sheetName() + '!' + name( col, row );
00247 }
00248
00249
00250
00251 QString Cell::columnName() const
00252 {
00253 return columnName( column() );
00254 }
00255
00256
00257
00258
00259 QString Cell::columnName( uint column )
00260 {
00261 if ( column < 1 || column > KS_colMax )
00262 return QString("@@@");
00263
00264 QString str;
00265 unsigned digits = 1;
00266 unsigned offset = 0;
00267
00268 column--;
00269
00270 for( unsigned limit = 26; column >= limit+offset; limit *= 26, digits++ )
00271 offset += limit;
00272
00273 for( unsigned col = column - offset; digits; --digits, col/=26 )
00274 str.prepend( QChar( 'A' + (col%26) ) );
00275
00276 return str;
00277 }
00278
00279 QString Cell::comment() const
00280 {
00281 return sheet()->cellStorage()->comment( d->column, d->row );
00282 }
00283
00284 void Cell::setComment( const QString& comment )
00285 {
00286 sheet()->cellStorage()->setComment( Region(cellPosition()), comment );
00287 }
00288
00289 Conditions Cell::conditions() const
00290 {
00291 return sheet()->cellStorage()->conditions( d->column, d->row );
00292 }
00293
00294 void Cell::setConditions( const Conditions& conditions )
00295 {
00296 sheet()->cellStorage()->setConditions( Region(cellPosition()), conditions );
00297 }
00298
00299 Database Cell::database() const
00300 {
00301 return sheet()->cellStorage()->database( d->column, d->row );
00302 }
00303
00304 Formula Cell::formula() const
00305 {
00306 return sheet()->cellStorage()->formula( d->column, d->row );
00307 }
00308
00309 void Cell::setFormula( const Formula& formula )
00310 {
00311 sheet()->cellStorage()->setFormula( column(), row(), formula );
00312 }
00313
00314 Style Cell::style() const
00315 {
00316 return sheet()->cellStorage()->style( d->column, d->row );
00317 }
00318
00319 Style Cell::effectiveStyle() const
00320 {
00321 Style style = sheet()->cellStorage()->style( d->column, d->row );
00322
00323 if ( Style* conditialStyle = conditions().testConditions( *this ) )
00324 style.merge( *conditialStyle );
00325 return style;
00326 }
00327
00328 void Cell::setStyle( const Style& style )
00329 {
00330 sheet()->cellStorage()->setStyle( Region(cellPosition()), style );
00331 }
00332
00333 Validity Cell::validity() const
00334 {
00335 return sheet()->cellStorage()->validity( d->column, d->row );
00336 }
00337
00338 void Cell::setValidity( Validity validity )
00339 {
00340 sheet()->cellStorage()->setValidity( Region(cellPosition()), validity );
00341 }
00342
00343
00344
00345
00346
00347
00348
00349
00350 QString Cell::userInput() const
00351 {
00352 const Formula formula = this->formula();
00353 if ( !formula.expression().isEmpty() )
00354 return formula.expression();
00355 return sheet()->cellStorage()->userInput( d->column, d->row );
00356 }
00357
00358 void Cell::setUserInput( const QString& string )
00359 {
00360 if ( !string.isEmpty() && string[0] == '=' )
00361 {
00362
00363 Formula formula( sheet(), *this );
00364 formula.setExpression( string );
00365 setFormula( formula );
00366
00367 sheet()->cellStorage()->setUserInput( d->column, d->row, QString() );
00368 }
00369 else
00370 {
00371
00372 setFormula( Formula() );
00373
00374 sheet()->cellStorage()->setUserInput( d->column, d->row, string );
00375 }
00376 }
00377
00378
00379
00380
00381
00382
00383 QString Cell::displayText() const
00384 {
00385 if ( isNull() )
00386 return QString();
00387
00388 QString string;
00389 const Style style = effectiveStyle();
00390
00391
00392 if (isFormula() && sheet()->getShowFormula() && !( sheet()->isProtected() && style.hideFormula())) {
00393 string = userInput();
00394 } else if (!isEmpty()) {
00395 string = sheet()->map()->formatter()->formatText(value(), style.formatType(), style.precision(),
00396 style.floatFormat(), style.prefix(),
00397 style.postfix(), style.currency().symbol()).asString();
00398 }
00399 return string;
00400 }
00401
00402
00403
00404
00405 const Value Cell::value() const
00406 {
00407 return sheet()->cellStorage()->value( d->column, d->row );
00408 }
00409
00410
00411
00412
00413 void Cell::setValue( const Value& value )
00414 {
00415 sheet()->cellStorage()->setValue( d->column, d->row, value );
00416 }
00417
00418
00419
00420
00421 void Cell::copyFormat( const Cell& cell )
00422 {
00423 Q_ASSERT( !isNull() );
00424 Q_ASSERT( !cell.isNull() );
00425 Value value = this->value();
00426 value.setFormat( cell.value().format() );
00427 sheet()->cellStorage()->setValue( d->column, d->row, value );
00428 if (!style().isDefault() || !cell.style().isDefault())
00429 setStyle(cell.style());
00430 if (!conditions().isEmpty() || !cell.conditions().isEmpty())
00431 setConditions(cell.conditions());
00432 }
00433
00434 void Cell::copyAll( const Cell& cell )
00435 {
00436 Q_ASSERT( !isNull() );
00437 Q_ASSERT( !cell.isNull() );
00438 copyFormat( cell );
00439 copyContent( cell );
00440 if (!comment().isEmpty() || !cell.comment().isEmpty())
00441 setComment(cell.comment());
00442 if (!validity().isEmpty() || !cell.validity().isEmpty())
00443 setValidity(cell.validity());
00444 }
00445
00446 void Cell::copyContent( const Cell& cell )
00447 {
00448 Q_ASSERT( !isNull() );
00449 Q_ASSERT( !cell.isNull() );
00450 if (cell.isFormula())
00451 {
00452
00453
00454 Formula formula( sheet(), *this );
00455 formula.setExpression( decodeFormula( cell.encodeFormula() ) );
00456 setFormula( formula );
00457 }
00458 else
00459 {
00460
00461 sheet()->cellStorage()->setUserInput( d->column, d->row, cell.userInput() );
00462 }
00463
00464 sheet()->cellStorage()->setValue( d->column, d->row, cell.value() );
00465 }
00466
00467 bool Cell::needsPrinting() const
00468 {
00469 if ( !userInput().trimmed().isEmpty() )
00470 return true;
00471 if ( !comment().trimmed().isEmpty() )
00472 return true;
00473
00474 const Style style = effectiveStyle();
00475
00476
00477 if ( style.hasAttribute( Style::TopPen ) ||
00478 style.hasAttribute( Style::LeftPen ) ||
00479 style.hasAttribute( Style::RightPen ) ||
00480 style.hasAttribute( Style::BottomPen ) ||
00481 style.hasAttribute( Style::FallDiagonalPen ) ||
00482 style.hasAttribute( Style::GoUpDiagonalPen ) )
00483 return true;
00484
00485
00486 if ( style.hasAttribute( Style::BackgroundBrush ) ) {
00487 QBrush brush = style.backgroundBrush();
00488
00489
00490
00491 if ( (brush.style() != Qt::NoBrush) &&
00492 (brush.color() != Qt::white || !brush.texture().isNull()) )
00493 return true;
00494 }
00495
00496 if ( style.hasAttribute( Style::BackgroundColor ) ) {
00497 kDebug(36004) <<"needsPrinting: Has background color";
00498 QColor backgroundColor = style.backgroundColor();
00499
00500
00501 if ( !( backgroundColor == Qt::white || backgroundColor.alpha() == 0 ) )
00502 return true;
00503 }
00504
00505 return false;
00506 }
00507
00508
00509 QString Cell::encodeFormula(bool fixedReferences) const
00510 {
00511 if (!isFormula())
00512 return QString();
00513
00514 QString result('=');
00515 const Tokens tokens = formula().tokens();
00516 for (int i = 0; i < tokens.count(); ++i)
00517 {
00518 const Token token = tokens[i];
00519 switch (token.type())
00520 {
00521 case Token::Cell:
00522 case Token::Range:
00523 {
00524 if (sheet()->map()->namedAreaManager()->contains(token.text()))
00525 {
00526 result.append(token.text());
00527 break;
00528 }
00529 const Region region(token.text(), sheet()->map());
00530
00531 Region::ConstIterator end = region.constEnd();
00532 for (Region::ConstIterator it = region.constBegin(); it != end; ++it)
00533 {
00534 if (!(*it)->isValid())
00535 continue;
00536 if ((*it)->type() == Region::Element::Point)
00537 {
00538 if ((*it)->sheet())
00539 result.append((*it)->sheet()->sheetName() + '!');
00540 const QPoint pos = (*it)->rect().topLeft();
00541 if ((*it)->isColumnFixed())
00542 result.append(QString("$%1").arg(pos.x()));
00543 else if (fixedReferences)
00544 result.append(QChar(0xA7) + QString("%1").arg(pos.x()));
00545 else
00546 result.append(QString("#%1").arg(pos.x() - (int)d->column));
00547 if ((*it)->isRowFixed())
00548 result.append(QString("$%1#").arg(pos.y()));
00549 else if (fixedReferences)
00550 result.append(QChar(0xA7) + QString("%1#").arg(pos.y()));
00551 else
00552 result.append(QString("#%1#").arg(pos.y() - (int)d->row));
00553 }
00554 else
00555 {
00556 if ((*it)->sheet())
00557 result.append((*it)->sheet()->sheetName() + '!');
00558 QPoint pos = (*it)->rect().topLeft();
00559 if ((*it)->isLeftFixed())
00560 result.append(QString("$%1").arg(pos.x()));
00561 else if (fixedReferences)
00562 result.append(QChar(0xA7) + QString("%1").arg(pos.x()));
00563 else
00564 result.append(QString("#%1").arg(pos.x() - (int)d->column));
00565 if ((*it)->isTopFixed())
00566 result.append(QString("$%1#").arg(pos.y()));
00567 else if (fixedReferences)
00568 result.append(QChar(0xA7) + QString("%1#").arg(pos.y()));
00569 else
00570 result.append(QString("#%1#").arg(pos.y() - (int)d->row));
00571 result.append(':');
00572 pos = (*it)->rect().bottomRight();
00573 if ((*it)->isRightFixed())
00574 result.append(QString("$%1").arg(pos.x()));
00575 else if (fixedReferences)
00576 result.append(QChar(0xA7) + QString("%1").arg(pos.x()));
00577 else
00578 result.append(QString("#%1").arg(pos.x() - (int)d->column));
00579 if ((*it)->isBottomFixed())
00580 result.append(QString("$%1#").arg(pos.y()));
00581 else if (fixedReferences)
00582 result.append(QChar(0xA7) + QString("%1#").arg(pos.y()));
00583 else
00584 result.append(QString("#%1#").arg(pos.y() - (int)d->row));
00585 }
00586 }
00587 break;
00588 }
00589 default:
00590 {
00591 result.append(token.text());
00592 break;
00593 }
00594 }
00595 }
00596 kDebug() << result;
00597 return result;
00598 }
00599
00600 QString Cell::decodeFormula(const QString &_text) const
00601 {
00602 QString erg = "";
00603 unsigned int pos = 0;
00604 const unsigned int length = _text.length();
00605
00606 if ( _text.isEmpty() )
00607 return QString();
00608
00609 while ( pos < length )
00610 {
00611 if ( _text[pos] == '"' )
00612 {
00613 erg += _text[pos++];
00614 while ( pos < length && _text[pos] != '"' )
00615 {
00616 erg += _text[pos++];
00617
00618 if ( pos < length && _text[pos] == '\\' && _text[pos+1] == '"' )
00619 {
00620 erg += _text[pos++];
00621 erg += _text[pos++];
00622 }
00623 }
00624 if ( pos < length )
00625 erg += _text[pos++];
00626 }
00627 else if ( _text[pos] == '#' || _text[pos] == '$' || _text[pos] == QChar(0xA7))
00628 {
00629 bool abs1 = false;
00630 bool abs2 = false;
00631 bool era1 = false;
00632 bool era2 = false;
00633
00634 QChar _t = _text[pos++];
00635 if ( _t == '$' )
00636 abs1 = true;
00637 else if ( _t == QChar(0xA7) )
00638 era1 = true;
00639
00640 int col = 0;
00641 unsigned int oldPos = pos;
00642 while ( pos < length && ( _text[pos].isDigit() || _text[pos] == '-' ) ) ++pos;
00643 if ( pos != oldPos )
00644 col = _text.mid(oldPos, pos-oldPos).toInt();
00645 if ( !abs1 && !era1 )
00646 col += d->column;
00647
00648
00649 _t = _text[pos++];
00650 if ( _t == '$' )
00651 abs2 = true;
00652 else if ( _t == QChar(0xA7) )
00653 era2 = true;
00654
00655 int row = 0;
00656 oldPos = pos;
00657 while ( pos < length && ( _text[pos].isDigit() || _text[pos] == '-' ) ) ++pos;
00658 if ( pos != oldPos )
00659 row = _text.mid(oldPos, pos-oldPos).toInt();
00660 if ( !abs2 && !era2)
00661 row += d->row;
00662
00663 ++pos;
00664 if ( row < 1 || col < 1 || row > KS_rowMax || col > KS_colMax )
00665 {
00666 kDebug(36003) <<"Cell::decodeFormula: row or column out of range (col:" << col <<" | row:" << row << ')';
00667 erg += Value::errorREF().errorMessage();
00668 }
00669 else
00670 {
00671 if ( abs1 )
00672 erg += '$';
00673 erg += Cell::columnName(col);
00674
00675 if ( abs2 )
00676 erg += '$';
00677 erg += QString::number( row );
00678 }
00679 }
00680 else
00681 erg += _text[pos++];
00682 }
00683
00684 return erg;
00685 }
00686
00687
00688
00689
00690
00691
00692 bool Cell::makeFormula()
00693 {
00694
00695
00696
00697 if ( !isFormula() )
00698 return false;
00699
00700
00701 if ( !formula().isValid() )
00702 {
00703 KNotification *notify = new KNotification("ParsingError");
00704 notify->setText(i18n("Parsing of formula in cell <i>%1</i> failed.", fullName()));
00705 notify->addContext("cell", fullName());
00706 notify->addContext("expression", userInput());
00707 QTimer::singleShot(0, notify, SLOT(sendEvent()));
00708 setValue( Value::errorPARSE() );
00709 return false;
00710 }
00711 return true;
00712 }
00713
00714 int Cell::effectiveAlignX() const
00715 {
00716 const Style style = effectiveStyle();
00717 int align = style.halign();
00718 if ( align == Style::HAlignUndefined )
00719 {
00720
00721 if ((style.formatType() == Format::Text) || value().isString())
00722 align = (displayText().isRightToLeft()) ? Style::Right : Style::Left;
00723 else
00724 {
00725 Value val = value();
00726 while (val.isArray()) val = val.element (0, 0);
00727 if (val.isBoolean() || val.isNumber())
00728 align = Style::Right;
00729 else
00730 align = Style::Left;
00731 }
00732 }
00733 return align;
00734 }
00735
00736 double Cell::width() const
00737 {
00738 const int rightCol = d->column + mergedXCells();
00739 double width = 0.0;
00740 for ( int col = d->column; col <= rightCol; ++col )
00741 width += sheet()->columnFormat( col )->width();
00742 return width;
00743 }
00744
00745 double Cell::height() const
00746 {
00747 const int bottomRow = d->row + mergedYCells();
00748 double height = 0.0;
00749 for ( int row = d->row; row <= bottomRow; ++row )
00750 height += sheet()->rowFormat( row )->height();
00751 return height;
00752 }
00753
00754
00755 void Cell::parseUserInput( const QString& text )
00756 {
00757
00758
00759
00760 if ( text.isEmpty() )
00761 {
00762 setValue( Value::empty() );
00763 setUserInput( text );
00764 setFormula( Formula() );
00765 return;
00766 }
00767
00768
00769 if ( text[0] == '=' )
00770 {
00771 Formula formula( sheet(), *this );
00772 formula.setExpression( text );
00773 setFormula( formula );
00774
00775
00776 if ( !formula.isValid() )
00777 {
00778 KNotification *notify = new KNotification("ParsingError");
00779 notify->setText(i18n("Parsing of formula in cell <i>%1</i> failed.", fullName()));
00780 notify->addContext("cell", fullName());
00781 notify->addContext("expression", userInput());
00782 QTimer::singleShot(0, notify, SLOT(sendEvent()));
00783 setValue( Value::errorPARSE() );
00784 return;
00785 }
00786 return;
00787 }
00788
00789
00790 const Formula oldFormula = formula();
00791 const QString oldUserInput = userInput();
00792 const Value oldValue = value();
00793
00794
00795 setFormula( Formula() );
00796
00797 Value value;
00798 if ( style().formatType() == Format::Text )
00799 value = Value( QString( text ) );
00800 else
00801 {
00802
00803 value = sheet()->map()->parser()->parse( text );
00804
00805 #if 0
00806
00807
00808 if ( isTime() && ( formatType() != Format::Time7 ) )
00809 setUserInput( locale()->formatTime( value().asDateTime( sheet()->map()->calculationSettings()).time(), true ) );
00810 #endif
00811
00812
00813 if ( sheet()->getFirstLetterUpper() && value.isString() && !text.isEmpty() )
00814 {
00815 QString str = value.asString();
00816 value = Value( str[0].toUpper() + str.right( str.length()-1 ) );
00817 }
00818 }
00819
00820 setUserInput( text );
00821 setValue( value );
00822
00823
00824 if ( !sheet()->isLoading() )
00825 {
00826 Validity validity = this->validity();
00827 if ( !validity.testValidity( this ) )
00828 {
00829 kDebug() <<"Validation failed";
00830
00831 setFormula( oldFormula );
00832 setUserInput( oldUserInput );
00833 setValue( oldValue );
00834 }
00835 }
00836 }
00837
00838 QString Cell::link() const
00839 {
00840 return sheet()->cellStorage()->link( d->column, d->row );
00841 }
00842
00843 void Cell::setLink( const QString& link )
00844 {
00845 sheet()->cellStorage()->setLink( d->column, d->row, link );
00846
00847 if ( !link.isEmpty() && userInput().isEmpty() )
00848 parseUserInput( link );
00849 }
00850
00851 bool Cell::isDate() const
00852 {
00853 const Format::Type t = style().formatType();
00854 return (Format::isDate(t) || ((t == Format::Generic) && (value().format() == Value::fmt_Date)));
00855 }
00856
00857 bool Cell::isTime() const
00858 {
00859 const Format::Type t = style().formatType();
00860 return (Format::isTime(t) || ((t == Format::Generic) && (value().format() == Value::fmt_Time)));
00861 }
00862
00863 bool Cell::isText() const
00864 {
00865 const Format::Type t = style().formatType();
00866 return t == Format::Text;
00867 }
00868
00869
00870
00871
00872 bool Cell::isPartOfMerged() const
00873 {
00874 return sheet()->cellStorage()->isPartOfMerged( d->column, d->row );
00875 }
00876
00877 Cell Cell::masterCell() const
00878 {
00879 return sheet()->cellStorage()->masterCell( d->column, d->row );
00880 }
00881
00882
00883
00884 void Cell::mergeCells( int _col, int _row, int _x, int _y )
00885 {
00886 sheet()->cellStorage()->mergeCells( _col, _row, _x, _y );
00887 }
00888
00889 bool Cell::doesMergeCells() const
00890 {
00891 return sheet()->cellStorage()->doesMergeCells( d->column, d->row );
00892 }
00893
00894 int Cell::mergedXCells() const
00895 {
00896 return sheet()->cellStorage()->mergedXCells( d->column, d->row );
00897 }
00898
00899 int Cell::mergedYCells() const
00900 {
00901 return sheet()->cellStorage()->mergedYCells( d->column, d->row );
00902 }
00903
00904 bool Cell::isLocked() const
00905 {
00906 return sheet()->cellStorage()->isLocked( d->column, d->row );
00907 }
00908
00909 QRect Cell::lockedCells() const
00910 {
00911 return sheet()->cellStorage()->lockedCells( d->column, d->row );
00912 }
00913
00914
00915
00916
00917
00918
00919 QDomElement Cell::save(QDomDocument& doc, int xOffset, int yOffset, bool era)
00920 {
00921
00922 QDomElement cell = doc.createElement( "cell" );
00923 cell.setAttribute( "row", row() - yOffset );
00924 cell.setAttribute( "column", column() - xOffset );
00925
00926
00927
00928
00929 QDomElement formatElement( doc.createElement( "format" ) );
00930 style().saveXML(doc, formatElement, sheet()->map()->styleManager());
00931 if ( formatElement.hasChildNodes() || formatElement.attributes().length() )
00932 cell.appendChild( formatElement );
00933
00934 if ( doesMergeCells() )
00935 {
00936 if ( mergedXCells() )
00937 formatElement.setAttribute( "colspan", mergedXCells() );
00938 if ( mergedYCells() )
00939 formatElement.setAttribute( "rowspan", mergedYCells() );
00940 }
00941
00942 Conditions conditions = this->conditions();
00943 if ( !conditions.isEmpty() )
00944 {
00945 QDomElement conditionElement = conditions.saveConditions( doc );
00946 if ( !conditionElement.isNull() )
00947 cell.appendChild( conditionElement );
00948 }
00949
00950 Validity validity = this->validity();
00951 if ( !validity.isEmpty() )
00952 {
00953 QDomElement validityElement = validity.saveXML( doc );
00954 if ( !validityElement.isNull() )
00955 cell.appendChild( validityElement );
00956 }
00957
00958 const QString comment = this->comment();
00959 if ( !comment.isEmpty() )
00960 {
00961 QDomElement commentElement = doc.createElement( "comment" );
00962 commentElement.appendChild( doc.createCDATASection( comment ) );
00963 cell.appendChild( commentElement );
00964 }
00965
00966
00967
00968
00969 if ( !userInput().isEmpty() )
00970 {
00971
00972
00973 if ( isFormula() )
00974 {
00975 QDomElement txt = doc.createElement( "text" );
00976
00977 txt.appendChild( doc.createTextNode( encodeFormula( era ) ) );
00978 cell.appendChild( txt );
00979
00980
00981 QDomElement formulaResult = doc.createElement( "result" );
00982 saveCellResult( doc, formulaResult, displayText() );
00983 cell.appendChild( formulaResult );
00984
00985 }
00986 else if ( !link().isEmpty() )
00987 {
00988
00989
00990 QDomElement txt = doc.createElement( "text" );
00991 QString qml = "!<a href=\"" + link() + "\">" + userInput() + "</a>";
00992 txt.appendChild( doc.createCDATASection( qml ) );
00993 cell.appendChild( txt );
00994 }
00995 else
00996 {
00997
00998 QDomElement txt = doc.createElement( "text" );
00999 saveCellResult( doc, txt, userInput() );
01000 cell.appendChild( txt );
01001 }
01002 }
01003 if ( cell.hasChildNodes() || cell.attributes().length() > 2 )
01004
01005 return cell;
01006 else
01007 return QDomElement();
01008 }
01009
01010 bool Cell::saveCellResult( QDomDocument& doc, QDomElement& result,
01011 QString str )
01012 {
01013 QString dataType = "Other";
01014
01015 if ( value().isNumber() )
01016 {
01017 if ( isDate() )
01018 {
01019
01020 QDate dd = value().asDateTime(sheet()->map()->calculationSettings()).date();
01021 dataType = "Date";
01022 str = "%1/%2/%3";
01023 str = str.arg(dd.year()).arg(dd.month()).arg(dd.day());
01024 }
01025 else if( isTime() )
01026 {
01027
01028 dataType = "Time";
01029 str = value().asDateTime(sheet()->map()->calculationSettings()).time().toString();
01030 }
01031 else
01032 {
01033
01034 dataType = "Num";
01035 if (value().isInteger())
01036 str = QString::number(value().asInteger());
01037 else
01038 str = QString::number(numToDouble (value().asFloat()), 'g', DBL_DIG);
01039 }
01040 }
01041
01042 if ( value().isBoolean() )
01043 {
01044 dataType = "Bool";
01045 str = value().asBoolean() ? "true" : "false";
01046 }
01047
01048 if ( value().isString() )
01049 {
01050 dataType = "Str";
01051 str = value().asString();
01052 }
01053
01054 result.setAttribute( "dataType", dataType );
01055
01056 const QString displayText = this->displayText();
01057 if ( !displayText.isEmpty() )
01058 result.setAttribute( "outStr", displayText );
01059 result.appendChild( doc.createTextNode( str ) );
01060
01061 return true;
01062 }
01063
01064 void Cell::saveOdfAnnotation( KoXmlWriter &xmlwriter )
01065 {
01066 const QString comment = this->comment();
01067 if ( !comment.isEmpty() )
01068 {
01069
01070 xmlwriter.startElement( "office:annotation" );
01071 const QStringList text = comment.split( '\n', QString::SkipEmptyParts );
01072 for ( QStringList::ConstIterator it = text.begin(); it != text.end(); ++it ) {
01073 xmlwriter.startElement( "text:p" );
01074 xmlwriter.addTextNode( *it );
01075 xmlwriter.endElement();
01076 }
01077 xmlwriter.endElement();
01078 }
01079 }
01080
01081 QString Cell::saveOdfCellStyle( KoGenStyle ¤tCellStyle, KoGenStyles &mainStyles )
01082 {
01083 const Conditions conditions = this->conditions();
01084 if ( !conditions.isEmpty() )
01085 {
01086
01087 currentCellStyle = KoGenStyle( KoGenStyle::StyleAutoTableCell, "table-cell" );
01088 conditions.saveOdfConditions( currentCellStyle );
01089 }
01090 return style().saveOdf( currentCellStyle, mainStyles, d->sheet->map()->styleManager() );
01091 }
01092
01093
01094 bool Cell::saveOdf( KoXmlWriter& xmlwriter, KoGenStyles &mainStyles,
01095 int row, int column, int &repeated,
01096 OdfSavingContext& tableContext)
01097 {
01098
01099 if ( !isPartOfMerged() )
01100 xmlwriter.startElement( "table:table-cell" );
01101 else
01102 xmlwriter.startElement( "table:covered-table-cell" );
01103 #if 0
01104
01105 QFont font;
01106 Value const value( cell.value() );
01107 if ( !cell.isDefault() )
01108 {
01109 font = cell.format()->textFont( i, row );
01110 m_styles.addFont( font );
01111
01112 if ( cell.format()->hasProperty( Style::SComment ) )
01113 hasComment = true;
01114 }
01115 #endif
01116
01117 if ( link().isEmpty() )
01118 saveOdfValue (xmlwriter);
01119
01120
01121
01122 if ((!tableContext.rowDefaultStyles.contains(row) &&
01123 !tableContext.columnDefaultStyles.contains(column) &&
01124 !(style().isDefault() && conditions().isEmpty())) ||
01125 (tableContext.rowDefaultStyles.contains(row) && tableContext.rowDefaultStyles[row] != style()) ||
01126 (tableContext.columnDefaultStyles.contains(column) && tableContext.columnDefaultStyles[column] != style()))
01127 {
01128 KoGenStyle currentCellStyle;
01129 saveOdfCellStyle( currentCellStyle, mainStyles );
01130
01131 if ( !currentCellStyle.isDefaultStyle() )
01132 xmlwriter.addAttribute( "table:style-name", mainStyles.styles().find(currentCellStyle).value() );
01133 }
01134
01135
01136 const QString comment = this->comment();
01137 if (isEmpty() && comment.isEmpty() && !isPartOfMerged() && !doesMergeCells() &&
01138 !tableContext.cellHasAnchoredShapes(sheet(), row, column)) {
01139 bool refCellIsDefault = isDefault();
01140 int j = column + 1;
01141 Cell nextCell = sheet()->cellStorage()->nextInRow( column, row );
01142 while ( !nextCell.isNull() )
01143 {
01144
01145
01146
01147
01148 if (nextCell.column() != j || (!nextCell.isEmpty() || tableContext.cellHasAnchoredShapes(sheet(), row, column))) {
01149 if ( refCellIsDefault )
01150 {
01151
01152
01153 repeated = nextCell.column() - j + 1;
01154 }
01155
01156
01157
01158 break;
01159 }
01160
01161 if ( nextCell.isPartOfMerged() || nextCell.doesMergeCells() ||
01162 !nextCell.comment().isEmpty() || tableContext.cellHasAnchoredShapes(sheet(), row, column) ||
01163 !(nextCell.style() == style() && nextCell.conditions() == conditions()) )
01164 {
01165 break;
01166 }
01167 ++repeated;
01168
01169 nextCell = sheet()->cellStorage()->nextInRow( j++, row );
01170 }
01171 kDebug(36003) << "Cell::saveOdf: empty cell in column" << column
01172 << "repeated" << repeated << "time(s)" << endl;
01173
01174 if ( repeated > 1 )
01175 xmlwriter.addAttribute( "table:number-columns-repeated", QString::number( repeated ) );
01176 }
01177
01178 Validity validity = Cell( sheet(), column, row ).validity();
01179 if ( !validity.isEmpty() )
01180 {
01181 GenValidationStyle styleVal(&validity);
01182 xmlwriter.addAttribute( "table:validation-name", tableContext.valStyle.lookup( styleVal ) );
01183 }
01184 if ( isFormula() )
01185 {
01186
01187 QString formula = Odf::encodeFormula( userInput(), locale() );
01188 xmlwriter.addAttribute( "table:formula", formula );
01189 }
01190 else if ( !link().isEmpty() )
01191 {
01192
01193 xmlwriter.startElement( "text:p" );
01194 xmlwriter.startElement( "text:a" );
01195 const QString url = link();
01196
01197 if ( Util::localReferenceAnchor( url ) )
01198 xmlwriter.addAttribute( "xlink:href", ( '#'+url ) );
01199 else
01200 xmlwriter.addAttribute( "xlink:href", url );
01201 xmlwriter.addTextNode( userInput() );
01202 xmlwriter.endElement();
01203 xmlwriter.endElement();
01204 }
01205
01206 if ( doesMergeCells() )
01207 {
01208 int colSpan = mergedXCells() + 1;
01209 int rowSpan = mergedYCells() + 1;
01210
01211 if ( colSpan > 1 )
01212 xmlwriter.addAttribute( "table:number-columns-spanned", QString::number( colSpan ) );
01213
01214 if ( rowSpan > 1 )
01215 xmlwriter.addAttribute( "table:number-rows-spanned", QString::number( rowSpan ) );
01216 }
01217
01218 if ( !isEmpty() && link().isEmpty() )
01219 {
01220 xmlwriter.startElement( "text:p" );
01221 xmlwriter.addTextNode( displayText().toUtf8() );
01222 xmlwriter.endElement();
01223 }
01224
01225
01226
01227
01228
01229 if (tableContext.cellHasAnchoredShapes(sheet(), row, column)) {
01230 const QList<KoShape*> shapes = tableContext.cellAnchoredShapes(sheet(), row, column);
01231 for (int i = 0; i < shapes.count(); ++i) {
01232 KoShape* const shape = shapes[i];
01233 const QPointF bottomRight = shape->boundingRect().bottomRight();
01234 double endX = 0.0;
01235 double endY = 0.0;
01236 const int col = sheet()->leftColumn(bottomRight.x(), endX);
01237 const int row = sheet()->topRow(bottomRight.y(), endY);
01238 shape->setAdditionalAttribute("table:end-cell-address", Cell(sheet(), col, row).name());
01239 shape->setAdditionalAttribute("table:end-x", QString::number(bottomRight.x() - endX));
01240 shape->setAdditionalAttribute("table:end-y", QString::number(bottomRight.y() - endY));
01241 shapes[i]->saveOdf(tableContext.shapeContext);
01242 shape->removeAdditionalAttribute("table:end-cell-address");
01243 shape->removeAdditionalAttribute("table:end-x");
01244 shape->removeAdditionalAttribute("table:end-y");
01245 }
01246 }
01247
01248 saveOdfAnnotation( xmlwriter );
01249
01250 xmlwriter.endElement();
01251 return true;
01252 }
01253
01254 void Cell::saveOdfValue (KoXmlWriter &xmlWriter)
01255 {
01256 switch (value().format())
01257 {
01258 case Value::fmt_None: break;
01259 case Value::fmt_Boolean:
01260 {
01261 xmlWriter.addAttribute( "office:value-type", "boolean" );
01262 xmlWriter.addAttribute( "office:boolean-value", ( value().asBoolean() ?
01263 "true" : "false" ) );
01264 break;
01265 }
01266 case Value::fmt_Number:
01267 {
01268 if (isDate()) {
01269 xmlWriter.addAttribute( "office:value-type", "date" );
01270 xmlWriter.addAttribute( "office:date-value",
01271 value().asDate(sheet()->map()->calculationSettings()).toString( Qt::ISODate ) );
01272 } else if (isText()) {
01273 xmlWriter.addAttribute( "office:value-type", "string" );
01274 if (value().isInteger())
01275 xmlWriter.addAttribute( "office:string-value", QString::number( value().asInteger() ) );
01276 else
01277 xmlWriter.addAttribute( "office:string-value", QString::number( numToDouble (value().asFloat()), 'g', DBL_DIG ) );
01278 } else {
01279 xmlWriter.addAttribute( "office:value-type", "float" );
01280 if (value().isInteger())
01281 xmlWriter.addAttribute( "office:value", QString::number( value().asInteger() ) );
01282 else
01283 xmlWriter.addAttribute( "office:value", QString::number( numToDouble (value().asFloat()), 'g', DBL_DIG ) );
01284 }
01285 break;
01286 }
01287 case Value::fmt_Percent:
01288 {
01289 xmlWriter.addAttribute( "office:value-type", "percentage" );
01290 xmlWriter.addAttribute( "office:value",
01291 QString::number( (double) numToDouble (value().asFloat() )) );
01292 break;
01293 }
01294 case Value::fmt_Money:
01295 {
01296 xmlWriter.addAttribute( "office:value-type", "currency" );
01297 const Style style = this->style();
01298 if ( style.hasAttribute( Style::CurrencyFormat ) )
01299 {
01300 Currency currency = style.currency();
01301 xmlWriter.addAttribute( "office:currency", currency.code() );
01302 }
01303 xmlWriter.addAttribute( "office:value", QString::number( (double) numToDouble (value().asFloat()) ) );
01304 break;
01305 }
01306 case Value::fmt_DateTime: break;
01307 case Value::fmt_Date:
01308 {
01309 if (isTime()) {
01310 xmlWriter.addAttribute( "office:value-type", "time" );
01311 xmlWriter.addAttribute( "office:time-value",
01312 value().asTime(sheet()->map()->calculationSettings()).toString( "PThhHmmMssS" ) );
01313 } else {
01314 xmlWriter.addAttribute( "office:value-type", "date" );
01315 xmlWriter.addAttribute( "office:date-value",
01316 value().asDate(sheet()->map()->calculationSettings()).toString( Qt::ISODate ) );
01317 }
01318 break;
01319 }
01320 case Value::fmt_Time:
01321 {
01322 xmlWriter.addAttribute( "office:value-type", "time" );
01323 xmlWriter.addAttribute( "office:time-value",
01324 value().asTime(sheet()->map()->calculationSettings()).toString( "PThhHmmMssS" ) );
01325 break;
01326 }
01327 case Value::fmt_String:
01328 {
01329 xmlWriter.addAttribute( "office:value-type", "string" );
01330 xmlWriter.addAttribute( "office:string-value", value().asString() );
01331 break;
01332 }
01333 };
01334 }
01335
01336 bool Cell::loadOdf(const KoXmlElement& element, OdfLoadingContext& tableContext)
01337 {
01338 kDebug(36003) <<"*** Loading cell properties ***** at" << name();
01339
01340
01341 loadOdfCellText( element );
01342
01343
01344
01345
01346 bool isFormula = false;
01347 if ( element.hasAttributeNS( KoXmlNS::table, "formula" ) )
01348 {
01349 kDebug(36003)<<" formula :"<<element.attributeNS( KoXmlNS::table,"formula", QString() );
01350 isFormula = true;
01351 QString oasisFormula( element.attributeNS( KoXmlNS::table, "formula", QString() ) );
01352
01353
01354 QStringList prefixes = QStringList() << "oooc:" << "kspr:" << "of:" << "msoxl:";
01355 QString namespacePrefix;
01356 foreach (const QString &prefix, prefixes) {
01357 if (oasisFormula.startsWith( prefix )) {
01358 oasisFormula = oasisFormula.mid( prefix.length() );
01359 namespacePrefix = prefix;
01360 break;
01361 }
01362 }
01363 oasisFormula = Odf::decodeFormula( oasisFormula, locale(), namespacePrefix );
01364 setUserInput( oasisFormula );
01365 }
01366 else if ( !userInput().isEmpty() && userInput().at(0) == '=' )
01367 setUserInput( userInput().prepend('\'') );
01368
01369
01370
01371
01372 if ( element.hasAttributeNS( KoXmlNS::table, "validation-name" ) )
01373 {
01374 const QString validationName = element.attributeNS(KoXmlNS::table,"validation-name", QString());
01375 kDebug(36003) << " validation-name:" << validationName;
01376 Validity validity;
01377 validity.loadOdfValidation(this, validationName, tableContext);
01378 if ( !validity.isEmpty() )
01379 setValidity( validity );
01380 }
01381
01382
01383
01384
01385 if( element.hasAttributeNS( KoXmlNS::office, "value-type" ) )
01386 {
01387 const QString valuetype = element.attributeNS( KoXmlNS::office, "value-type", QString() );
01388 kDebug(36003)<<" value-type:" << valuetype;
01389 if( valuetype == "boolean" )
01390 {
01391 const QString val = element.attributeNS( KoXmlNS::office, "boolean-value", QString() ).toLower();
01392 if( ( val == "true" ) || ( val == "false" ) )
01393 setValue( Value( val == "true" ) );
01394 }
01395
01396
01397 else if( valuetype == "float" )
01398 {
01399 bool ok = false;
01400 const Value value(element.attributeNS(KoXmlNS::office, "value", QString()).toDouble(&ok));
01401 if (ok)
01402 setValue(value);
01403 if (!isFormula && userInput().isEmpty())
01404 setUserInput(sheet()->map()->converter()->asString(value).asString());
01405 }
01406
01407
01408 else if( valuetype == "currency" )
01409 {
01410 bool ok = false;
01411 Value value(element.attributeNS(KoXmlNS::office, "value", QString()).toDouble(&ok));
01412 if (ok)
01413 {
01414 value.setFormat( Value::fmt_Money );
01415 setValue( value );
01416
01417 if (element.hasAttributeNS( KoXmlNS::office, "currency" ) )
01418 {
01419 Currency currency(element.attributeNS( KoXmlNS::office, "currency", QString() ) );
01420
01421 Style style;
01422 style.setCurrency( currency );
01423 setStyle( style );
01424 }
01425 }
01426 }
01427 else if( valuetype == "percentage" )
01428 {
01429 bool ok = false;
01430 Value value(element.attributeNS(KoXmlNS::office, "value", QString()).toDouble(&ok));
01431 if (ok)
01432 {
01433 value.setFormat(Value::fmt_Percent);
01434 setValue(value);
01435 if (!isFormula && userInput().isEmpty())
01436 setUserInput(sheet()->map()->converter()->asString(value).asString());
01437
01438 #if 0
01439 Style style;
01440 style.setFormatType( Format::Percentage );
01441 setStyle( style );
01442 #endif
01443 }
01444 }
01445 else if ( valuetype == "date" )
01446 {
01447 QString value = element.attributeNS( KoXmlNS::office, "date-value", QString() );
01448 kDebug(36003) <<"Type: date, value:" << value;
01449
01450
01451 int year = 0, month = 0, day = 0;
01452 bool ok = false;
01453
01454 int p1 = value.indexOf( '-' );
01455 if ( p1 > 0 )
01456 year = value.left( p1 ).toInt( &ok );
01457
01458 kDebug(36003) <<"year:" << value.left( p1 );
01459
01460 int p2 = value.indexOf( '-', ++p1 );
01461
01462 if ( ok )
01463 month = value.mid( p1, p2 - p1 ).toInt( &ok );
01464
01465 kDebug(36003) <<"month:" << value.mid( p1, p2 - p1 );
01466
01467 if ( ok )
01468 day = value.right( value.length() - p2 - 1 ).toInt( &ok );
01469
01470 kDebug(36003) <<"day:" << value.right( value.length() - p2 );
01471
01472 if ( ok )
01473 {
01474 setValue( Value( QDate( year, month, day ), sheet()->map()->calculationSettings()) );
01475
01476 #if 0
01477 Style style;
01478 style.setFormatType( Format::ShortDate );
01479 setStyle( style );
01480 #endif
01481 kDebug(36003) <<"Set QDate:" << year <<" -" << month <<" -" << day;
01482 }
01483
01484 }
01485 else if ( valuetype == "time" )
01486 {
01487 QString value = element.attributeNS( KoXmlNS::office, "time-value", QString() );
01488 kDebug(36003) <<"Type: time:" << value;
01489
01490 int hours = 0, minutes = 0, seconds = 0;
01491 int l = value.length();
01492 QString num;
01493 bool ok = false;
01494 for ( int i = 0; i < l; ++i )
01495 {
01496 if ( value[i].isNumber() )
01497 {
01498 num += value[i];
01499 continue;
01500 }
01501 else if ( value[i] == 'H' )
01502 hours = num.toInt( &ok );
01503 else if ( value[i] == 'M' )
01504 minutes = num.toInt( &ok );
01505 else if ( value[i] == 'S' )
01506 seconds = num.toInt( &ok );
01507 else
01508 continue;
01509
01510 kDebug(36003) <<"Num:" << num;
01511
01512 num = "";
01513 if ( !ok )
01514 break;
01515 }
01516 kDebug(36003) <<"Hours:" << hours <<"," << minutes <<"," << seconds;
01517
01518 if ( ok )
01519 {
01520
01521
01522 setValue( Value( QTime( hours % 24, minutes, seconds ), sheet()->map()->calculationSettings()) );
01523
01524 #if 0
01525 Style style;
01526 style.setFormatType( Format::Time );
01527 setStyle( style );
01528 #endif
01529 }
01530 }
01531 else if( valuetype == "string" )
01532 {
01533 if ( element.hasAttributeNS( KoXmlNS::office, "string-value" ))
01534 {
01535 QString value = element.attributeNS( KoXmlNS::office, "string-value", QString() );
01536 setValue( Value(value) );
01537 }
01538 else
01539 {
01540
01541 setValue(Value(userInput()));
01542 }
01543
01544 #if 0
01545 Style style;
01546 style.setFormatType( Format::Text );
01547 setStyle( style );
01548 #endif
01549 }
01550 else
01551 {
01552 kDebug(36003) <<" Unknown type. Parsing user input.";
01553
01554 parseUserInput(userInput());
01555 }
01556 }
01557 else
01558 {
01559 kDebug(36003) <<" No value type specified. Parsing user input.";
01560
01561 parseUserInput(userInput());
01562 }
01563
01564
01565
01566
01567 int colSpan = 1;
01568 int rowSpan = 1;
01569 if ( element.hasAttributeNS( KoXmlNS::table, "number-columns-spanned" ) )
01570 {
01571 bool ok = false;
01572 int span = element.attributeNS( KoXmlNS::table, "number-columns-spanned", QString() ).toInt( &ok );
01573 if( ok ) colSpan = span;
01574 }
01575 if ( element.hasAttributeNS( KoXmlNS::table, "number-rows-spanned" ) )
01576 {
01577 bool ok = false;
01578 int span = element.attributeNS( KoXmlNS::table, "number-rows-spanned", QString() ).toInt( &ok );
01579 if( ok ) rowSpan = span;
01580 }
01581 if ( colSpan > 1 || rowSpan > 1 )
01582 mergeCells( d->column, d->row, colSpan - 1, rowSpan - 1 );
01583
01584
01585
01586
01587 KoXmlElement annotationElement = KoXml::namedItemNS( element, KoXmlNS::office, "annotation" );
01588 if ( !annotationElement.isNull() )
01589 {
01590 QString comment;
01591 KoXmlNode node = annotationElement.firstChild();
01592 while( !node.isNull() )
01593 {
01594 KoXmlElement commentElement = node.toElement();
01595 if( !commentElement.isNull() )
01596 if( commentElement.localName() == "p" && commentElement.namespaceURI() == KoXmlNS::text )
01597 {
01598 if( !comment.isEmpty() ) comment.append( '\n' );
01599 comment.append( commentElement.text() );
01600 }
01601
01602 node = node.nextSibling();
01603 }
01604 if( !comment.isEmpty() )
01605 setComment( comment );
01606 }
01607
01608 loadOdfObjects(element, tableContext.odfContext);
01609
01610 return true;
01611 }
01612
01613 void Cell::loadOdfCellText( const KoXmlElement& parent )
01614 {
01615
01616 KoXmlElement textParagraphElement;
01617 QString cellText;
01618
01619 bool multipleTextParagraphsFound=false;
01620
01621 forEachElement( textParagraphElement , parent )
01622 {
01623 if ( textParagraphElement.localName() == "p" &&
01624 textParagraphElement.namespaceURI()== KoXmlNS::text )
01625 {
01626
01627 if (cellText.isEmpty())
01628 cellText = textParagraphElement.text();
01629 else
01630 {
01631 cellText += '\n' + textParagraphElement.text();
01632 multipleTextParagraphsFound=true;
01633 }
01634
01635 KoXmlElement textA = KoXml::namedItemNS( textParagraphElement, KoXmlNS::text, "a" );
01636 if( !textA.isNull() )
01637 {
01638 if ( textA.hasAttributeNS( KoXmlNS::xlink, "href" ) )
01639 {
01640 QString link = textA.attributeNS( KoXmlNS::xlink, "href", QString() );
01641 cellText = textA.text();
01642 setUserInput( cellText );
01643
01644 if ( (!link.isEmpty()) && (link[0]=='#') )
01645 link=link.remove( 0, 1 );
01646 setLink( link );
01647 }
01648 }
01649 }
01650 }
01651
01652 if (!cellText.isNull())
01653 {
01654 setUserInput( cellText );
01655
01656 }
01657
01658
01659 if ( multipleTextParagraphsFound )
01660 {
01661 Style style;
01662 style.setWrapText( true );
01663 setStyle( style );
01664 }
01665 }
01666
01667 void Cell::loadOdfObjects( const KoXmlElement &parent, KoOdfLoadingContext& odfContext )
01668 {
01669
01670
01671
01672 KoShapeLoadingContext::addAdditionalAttributeData(KoShapeLoadingContext::AdditionalAttributeData(
01673 KoXmlNS::table, "end-cell-address",
01674 "table:end-cell-address"));
01675 KoShapeLoadingContext::addAdditionalAttributeData(KoShapeLoadingContext::AdditionalAttributeData(
01676 KoXmlNS::table, "end-x",
01677 "table:end-x"));
01678 KoShapeLoadingContext::addAdditionalAttributeData(KoShapeLoadingContext::AdditionalAttributeData(
01679 KoXmlNS::table, "end-y",
01680 "table:end-y"));
01681
01682 KoShapeLoadingContext shapeContext(odfContext, d->sheet->dataCenterMap());
01683 KoXmlElement element;
01684 forEachElement(element, parent)
01685 {
01686 if (element.namespaceURI() != KoXmlNS::draw)
01687 continue;
01688
01689 KoShape* shape = KoShapeRegistry::instance()->createShapeFromOdf(element, shapeContext);
01690 if (!shape)
01691 {
01692 kDebug(36003) << "Unable to load shape.";
01693 continue;
01694 }
01695
01696 d->sheet->addShape(shape);
01697
01698
01699
01700 if (!shape->hasAdditionalAttribute("table:end-cell-address") ||
01701 !shape->hasAdditionalAttribute("table:end-x") ||
01702 !shape->hasAdditionalAttribute("table:end-y"))
01703 {
01704 kDebug(36003) << "Not all attributes found, that are necessary for cell anchoring.";
01705 continue;
01706 }
01707
01708 Region endCell(Region::loadOdf(shape->additionalAttribute("table:end-cell-address")),
01709 d->sheet->map(), d->sheet);
01710 if (!endCell.isValid() || !endCell.isSingular())
01711 continue;
01712
01713 QString string = shape->additionalAttribute("table:end-x");
01714 if (string.isNull())
01715 continue;
01716 double endX = KoUnit::parseValue(string);
01717
01718 string = shape->additionalAttribute("table:end-y");
01719 if (string.isNull())
01720 continue;
01721 double endY = KoUnit::parseValue(string);
01722
01723
01724 QPointF position = shape->position();
01725 for (int col = 1; col < column(); ++col)
01726 position += QPointF(d->sheet->columnFormat(col)->width(), 0.0);
01727 for (int row = 1; row < this->row(); ++row)
01728 position += QPointF(0.0, d->sheet->rowFormat(row)->height());
01729 shape->setPosition(position);
01730
01731
01732
01733
01734 QSizeF size = QSizeF(endX, endY);
01735 for (int col = column(); col < endCell.firstRange().left(); ++col)
01736 size += QSizeF(d->sheet->columnFormat(col)->width(), 0.0);
01737 for (int row = this->row(); row < endCell.firstRange().top(); ++row)
01738 size += QSizeF(0.0, d->sheet->rowFormat(row)->height());
01739 shape->setSize(size);
01740
01741 dynamic_cast<ShapeApplicationData*>(shape->applicationData())->setAnchoredToCell(true);
01742 }
01743 }
01744
01745
01746 bool Cell::load( const KoXmlElement & cell, int _xshift, int _yshift,
01747 Paste::Mode mode, Paste::Operation op, bool paste )
01748 {
01749 bool ok;
01750
01751
01752
01753
01754
01755 d->row = cell.attribute( "row" ).toInt( &ok ) + _yshift;
01756 if ( !ok ) return false;
01757 d->column = cell.attribute( "column" ).toInt( &ok ) + _xshift;
01758 if ( !ok ) return false;
01759
01760
01761 if ( d->row < 1 || d->row > KS_rowMax )
01762 {
01763 kDebug(36001) <<"Cell::load: Value out of range Cell:row=" << d->row;
01764 return false;
01765 }
01766 if ( d->column < 1 || d->column > KS_colMax )
01767 {
01768 kDebug(36001) <<"Cell::load: Value out of range Cell:column=" << d->column;
01769 return false;
01770 }
01771
01772
01773
01774
01775 KoXmlElement formatElement = cell.namedItem( "format" ).toElement();
01776 if (!formatElement.isNull() &&
01777 ((mode == Paste::Normal) || (mode == Paste::Format) || (mode == Paste::NoBorder)))
01778 {
01779 int mergedXCells = 0;
01780 int mergedYCells = 0;
01781 if ( formatElement.hasAttribute( "colspan" ) )
01782 {
01783 int i = formatElement.attribute("colspan").toInt( &ok );
01784 if ( !ok ) return false;
01785
01786 if ( i < 0 || i > KS_spanMax )
01787 {
01788 kDebug(36001) <<"Value out of range Cell::colspan=" << i;
01789 return false;
01790 }
01791 if ( i )
01792 mergedXCells = i;
01793 }
01794
01795 if ( formatElement.hasAttribute( "rowspan" ) )
01796 {
01797 int i = formatElement.attribute("rowspan").toInt( &ok );
01798 if ( !ok ) return false;
01799
01800 if ( i < 0 || i > KS_spanMax )
01801 {
01802 kDebug(36001) <<"Value out of range Cell::rowspan=" << i;
01803 return false;
01804 }
01805 if ( i )
01806 mergedYCells = i;
01807 }
01808
01809 if ( mergedXCells != 0 || mergedYCells != 0 )
01810 mergeCells( d->column, d->row, mergedXCells, mergedYCells );
01811
01812 Style style;
01813 if (!style.loadXML( formatElement, mode))
01814 return false;
01815 setStyle( style );
01816 }
01817
01818
01819
01820
01821 KoXmlElement conditionsElement = cell.namedItem( "condition" ).toElement();
01822 if ( !conditionsElement.isNull())
01823 {
01824 Conditions conditions;
01825 conditions.loadConditions(sheet()->map()->styleManager(), conditionsElement);
01826 if ( !conditions.isEmpty() )
01827 setConditions( conditions );
01828 } else if (paste && (mode == Paste::Normal || mode == Paste::NoBorder)) {
01829
01830 setConditions( Conditions() );
01831 }
01832
01833 KoXmlElement validityElement = cell.namedItem( "validity" ).toElement();
01834 if ( !validityElement.isNull())
01835 {
01836 Validity validity;
01837 if ( validity.loadXML( this, validityElement ) )
01838 setValidity( validity );
01839 } else if (paste && (mode == Paste::Normal || mode == Paste::NoBorder)) {
01840
01841 setValidity( Validity() );
01842 }
01843
01844
01845
01846
01847 KoXmlElement comment = cell.namedItem( "comment" ).toElement();
01848 if (!comment.isNull() &&
01849 (mode == Paste::Normal || mode == Paste::Comment || mode == Paste::NoBorder))
01850 {
01851 QString t = comment.text();
01852
01853 setComment( t );
01854 }
01855
01856
01857
01858
01859
01860
01861
01862 KoXmlElement text = cell.namedItem( "text" ).toElement();
01863
01864 if (!text.isNull() &&
01865 (mode == Paste::Normal || mode == Paste::Text || mode == Paste::NoBorder || mode == Paste::Result))
01866 {
01867
01868
01869
01870 #ifdef KOXML_USE_QDOM
01871 if ( cell.hasAttribute( "dataType" ) )
01872 text.setAttribute( "dataType", cell.attribute( "dataType" ) );
01873 #else
01874 #ifdef __GNUC__
01875 #warning Problem with KoXmlReader conversion!
01876 #endif
01877 kWarning() << "Problem with KoXmlReader conversion!";
01878 #endif
01879
01880 KoXmlElement result = cell.namedItem( "result" ).toElement();
01881 QString txt = text.text();
01882 if ((mode == Paste::Result) && (txt[0] == '='))
01883
01884
01885 setUserInput( result.text() );
01886 else
01887
01888 loadCellData(text, op);
01889
01890 if ( !result.isNull() )
01891 {
01892 QString dataType;
01893 QString t = result.text();
01894
01895 if ( result.hasAttribute( "dataType" ) )
01896 dataType = result.attribute( "dataType" );
01897
01898 bool clear = true;
01899
01900 if( dataType == "Bool" )
01901 {
01902 if ( t == "false" )
01903 setValue( Value(false) );
01904 else if ( t == "true" )
01905 setValue( Value(true) );
01906 else
01907 clear = false;
01908 }
01909 else if( dataType == "Num" )
01910 {
01911 bool ok = false;
01912 double dd = t.toDouble( &ok );
01913 if ( ok )
01914 setValue ( Value(dd) );
01915 else
01916 clear = false;
01917 }
01918 else if( dataType == "Date" )
01919 {
01920 bool ok = false;
01921 double dd = t.toDouble( &ok );
01922 if (ok)
01923 {
01924 Value value(dd);
01925 value.setFormat(Value::fmt_Date);
01926 setValue(value);
01927 }
01928 else
01929 {
01930 int pos = t.indexOf( '/' );
01931 int year = t.mid( 0, pos ).toInt();
01932 int pos1 = t.indexOf( '/', pos + 1 );
01933 int month = t.mid( pos + 1, ( ( pos1 - 1 ) - pos ) ).toInt();
01934 int day = t.right( t.length() - pos1 - 1 ).toInt();
01935 QDate date( year, month, day );
01936 if ( date.isValid() )
01937 setValue( Value( date, sheet()->map()->calculationSettings()) );
01938 else
01939 clear = false;
01940 }
01941 }
01942 else if( dataType == "Time" )
01943 {
01944 bool ok = false;
01945 double dd = t.toDouble( &ok );
01946 if (ok)
01947 {
01948 Value value(dd);
01949 value.setFormat(Value::fmt_Time);
01950 setValue(value);
01951 }
01952 else
01953 {
01954 int hours = -1;
01955 int minutes = -1;
01956 int second = -1;
01957 int pos, pos1;
01958 pos = t.indexOf( ':' );
01959 hours = t.mid( 0, pos ).toInt();
01960 pos1 = t.indexOf( ':', pos + 1 );
01961 minutes = t.mid( pos + 1, ( ( pos1 - 1 ) - pos ) ).toInt();
01962 second = t.right( t.length() - pos1 - 1 ).toInt();
01963 QTime time( hours, minutes, second );
01964 if ( time.isValid() )
01965 setValue( Value( time, sheet()->map()->calculationSettings()) );
01966 else
01967 clear = false;
01968 }
01969 }
01970 else
01971 {
01972 setValue( Value(t) );
01973 }
01974 }
01975 }
01976
01977 return true;
01978 }
01979
01980 bool Cell::loadCellData(const KoXmlElement & text, Paste::Operation op )
01981 {
01982
01983
01984 QString t = text.text();
01985 t = t.trimmed();
01986
01987
01988 if( (!t.isEmpty()) && (t[0] == '=') )
01989 {
01990 t = decodeFormula(t);
01991 parseUserInput (pasteOperation( t, userInput(), op ));
01992
01993 makeFormula();
01994 }
01995
01996 else if ((!t.isEmpty()) && (t[0] == '!') )
01997 {
01998
01999
02000
02001 bool inside_tag = false;
02002 QString qml_text;
02003 QString tag;
02004 QString qml_link;
02005
02006 for( int i = 1; i < t.length(); i++ )
02007 {
02008 QChar ch = t[i];
02009 if( ch == '<' )
02010 {
02011 if( !inside_tag )
02012 {
02013 inside_tag = true;
02014 tag.clear();
02015 }
02016 }
02017 else if( ch == '>' )
02018 {
02019 if( inside_tag )
02020 {
02021 inside_tag = false;
02022 if( tag.startsWith( "a href=\"", Qt::CaseSensitive ) )
02023 if( tag.endsWith( '"' ) )
02024 qml_link = tag.mid( 8, tag.length()-9 );
02025 tag.clear();
02026 }
02027 }
02028 else
02029 {
02030 if( !inside_tag )
02031 qml_text += ch;
02032 else
02033 tag += ch;
02034 }
02035 }
02036
02037 if( !qml_link.isEmpty() )
02038 setLink( qml_link );
02039 setUserInput( qml_text );
02040 setValue( Value( qml_text ) );
02041 }
02042 else
02043 {
02044 bool newStyleLoading = true;
02045 QString dataType;
02046
02047 if ( text.hasAttribute( "dataType" ) )
02048 {
02049 dataType = text.attribute( "dataType" );
02050 }
02051 else
02052 {
02053
02054 if (isDate() && ( t.count('/') == 2 ))
02055 dataType = "Date";
02056 else if (isTime() && ( t.count(':') == 2 ) )
02057 dataType = "Time";
02058 else
02059 {
02060 parseUserInput( pasteOperation( t, userInput(), op ) );
02061 newStyleLoading = false;
02062 }
02063 }
02064
02065 if ( newStyleLoading )
02066 {
02067
02068 if( dataType == "Bool" )
02069 setValue(Value(t.toLower() == "true"));
02070
02071
02072 else if( dataType == "Num" )
02073 {
02074 bool ok = false;
02075 if (t.contains('.'))
02076 setValue ( Value( t.toDouble(&ok) ) );
02077 else
02078 setValue ( Value( t.toLongLong(&ok) ) );
02079 if ( !ok )
02080 {
02081 kWarning(36001) << "Couldn't parse '" << t << "' as number.";
02082 }
02083
02084 KLocale* locale = sheet()->map()->calculationSettings()->locale();
02085
02086
02087
02088 int precision = t.length() - t.indexOf('.') - 1;
02089
02090 if ( style().formatType() == Format::Percentage )
02091 {
02092 if (value().isInteger())
02093 t = locale->formatNumber( value().asInteger() * 100 );
02094 else
02095 t = locale->formatNumber( numToDouble (value().asFloat() * 100.0), precision );
02096 setUserInput( pasteOperation( t, userInput(), op ) );
02097 setUserInput( userInput() + '%' );
02098 }
02099 else
02100 {
02101 if (value().isInteger())
02102 t = locale->formatLong(value().asInteger());
02103 else
02104 t = locale->formatNumber(numToDouble (value().asFloat()), precision);
02105 setUserInput( pasteOperation( t, userInput(), op ) );
02106 }
02107 }
02108
02109
02110 else if( dataType == "Date" )
02111 {
02112 int pos = t.indexOf('/');
02113 int year = t.mid(0,pos).toInt();
02114 int pos1 = t.indexOf('/',pos+1);
02115 int month = t.mid(pos+1,((pos1-1)-pos)).toInt();
02116 int day = t.right(t.length()-pos1-1).toInt();
02117 setValue( Value( QDate(year,month,day), sheet()->map()->calculationSettings()) );
02118 if ( value().asDate(sheet()->map()->calculationSettings()).isValid() )
02119 setUserInput( locale()->formatDate( value().asDate(sheet()->map()->calculationSettings()), KLocale::ShortDate ) );
02120 else
02121 {
02122 parseUserInput( pasteOperation( t, userInput(), op ) );
02123 }
02124 }
02125
02126
02127 else if( dataType == "Time" )
02128 {
02129 int hours = -1;
02130 int minutes = -1;
02131 int second = -1;
02132 int pos, pos1;
02133 pos = t.indexOf(':');
02134 hours = t.mid(0,pos).toInt();
02135 pos1 = t.indexOf(':',pos+1);
02136 minutes = t.mid(pos+1,((pos1-1)-pos)).toInt();
02137 second = t.right(t.length()-pos1-1).toInt();
02138 setValue( Value( QTime(hours,minutes,second), sheet()->map()->calculationSettings()) );
02139 if ( value().asTime( sheet()->map()->calculationSettings()).isValid() )
02140 setUserInput( locale()->formatTime( value().asTime( sheet()->map()->calculationSettings()), true ) );
02141 else
02142 {
02143 parseUserInput( pasteOperation( t, userInput(), op ) );
02144 }
02145 }
02146
02147 else
02148 {
02149
02150 setUserInput( pasteOperation( t, userInput(), op ) );
02151 setValue( Value(userInput()) );
02152 }
02153 }
02154 }
02155
02156 if ( !sheet()->isLoading() )
02157 parseUserInput( userInput() );
02158
02159 return true;
02160 }
02161
02162 QTime Cell::toTime(const KoXmlElement &element)
02163 {
02164
02165 QString t = element.text();
02166 t = t.trimmed();
02167 int hours = -1;
02168 int minutes = -1;
02169 int second = -1;
02170 int pos, pos1;
02171 pos = t.indexOf(':');
02172 hours = t.mid(0,pos).toInt();
02173 pos1 = t.indexOf(':',pos+1);
02174 minutes = t.mid(pos+1,((pos1-1)-pos)).toInt();
02175 second = t.right(t.length()-pos1-1).toInt();
02176 setValue( Value( QTime(hours,minutes,second), sheet()->map()->calculationSettings()) );
02177 return value().asTime( sheet()->map()->calculationSettings());
02178 }
02179
02180 QDate Cell::toDate(const KoXmlElement &element)
02181 {
02182 QString t = element.text();
02183 int pos;
02184 int pos1;
02185 int year = -1;
02186 int month = -1;
02187 int day = -1;
02188 pos = t.indexOf('/');
02189 year = t.mid(0,pos).toInt();
02190 pos1 = t.indexOf('/',pos+1);
02191 month = t.mid(pos+1,((pos1-1)-pos)).toInt();
02192 day = t.right(t.length()-pos1-1).toInt();
02193 setValue( Value( QDate(year,month,day), sheet()->map()->calculationSettings()) );
02194 return value().asDate( sheet()->map()->calculationSettings());
02195 }
02196
02197 QString Cell::pasteOperation( const QString &new_text, const QString &old_text, Paste::Operation op )
02198 {
02199 if ( op == Paste::OverWrite )
02200 return new_text;
02201
02202 QString tmp_op;
02203 QString tmp;
02204 QString old;
02205
02206 if( !new_text.isEmpty() && new_text[0] == '=' )
02207 {
02208 tmp = new_text.right( new_text.length() - 1 );
02209 }
02210 else
02211 {
02212 tmp = new_text;
02213 }
02214
02215 if ( old_text.isEmpty() &&
02216 ( op == Paste::Add || op == Paste::Mul || op == Paste::Sub || op == Paste::Div ) )
02217 {
02218 old = "=0";
02219 }
02220
02221 if( !old_text.isEmpty() && old_text[0] == '=' )
02222 {
02223 old = old_text.right( old_text.length() - 1 );
02224 }
02225 else
02226 {
02227 old = old_text;
02228 }
02229
02230 bool b1, b2;
02231 tmp.toDouble( &b1 );
02232 old.toDouble( &b2 );
02233 if (b1 && !b2 && old.length() == 0)
02234 {
02235 old = '0';
02236 b2 = true;
02237 }
02238
02239 if( b1 && b2 )
02240 {
02241 switch( op )
02242 {
02243 case Paste::Add:
02244 tmp_op = QString::number(old.toDouble()+tmp.toDouble());
02245 break;
02246 case Paste::Mul :
02247 tmp_op = QString::number(old.toDouble()*tmp.toDouble());
02248 break;
02249 case Paste::Sub:
02250 tmp_op = QString::number(old.toDouble()-tmp.toDouble());
02251 break;
02252 case Paste::Div:
02253 tmp_op = QString::number(old.toDouble()/tmp.toDouble());
02254 break;
02255 default:
02256 Q_ASSERT( 0 );
02257 }
02258
02259 return tmp_op;
02260 }
02261 else if ( ( new_text[0] == '=' && old_text[0] == '=' ) ||
02262 ( b1 && old_text[0] == '=' ) || ( new_text[0] == '=' && b2 ) )
02263 {
02264 switch( op )
02265 {
02266 case Paste::Add :
02267 tmp_op="=("+old+")+"+'('+tmp+')';
02268 break;
02269 case Paste::Mul :
02270 tmp_op="=("+old+")*"+'('+tmp+')';
02271 break;
02272 case Paste::Sub:
02273 tmp_op="=("+old+")-"+'('+tmp+')';
02274 break;
02275 case Paste::Div:
02276 tmp_op="=("+old+")/"+'('+tmp+')';
02277 break;
02278 default :
02279 Q_ASSERT( 0 );
02280 }
02281
02282 tmp_op = decodeFormula(tmp_op);
02283 return tmp_op;
02284 }
02285
02286 tmp = decodeFormula(new_text);
02287 return tmp;
02288 }
02289
02290 Cell& Cell::operator=( const Cell& other )
02291 {
02292 d = other.d;
02293 return *this;
02294 }
02295
02296 bool Cell::operator<( const Cell& other ) const
02297 {
02298 if ( sheet() != other.sheet() )
02299 return sheet() < other.sheet();
02300 if ( row() < other.row() )
02301 return true;
02302 return ( ( row() == other.row() ) && ( column() < other.column() ) );
02303 }
02304
02305 bool Cell::operator==( const Cell& other ) const
02306 {
02307 return ( row() == other.row() && column() == other.column() && sheet() == other.sheet() );
02308 }
02309
02310 bool Cell::operator!() const
02311 {
02312 return ( !d );
02313 }
02314
02315 bool Cell::compareData( const Cell& other ) const
02316 {
02317 if ( value() != other.value() )
02318 return false;
02319 if ( formula() != other.formula() )
02320 return false;
02321 if ( link() != other.link() )
02322 return false;
02323 if ( mergedXCells() != other.mergedXCells() )
02324 return false;
02325 if ( mergedYCells() != other.mergedYCells() )
02326 return false;
02327 if ( style() != other.style() )
02328 return false;
02329 if ( comment() != other.comment() )
02330 return false;
02331 if ( conditions() != other.conditions() )
02332 return false;
02333 if ( validity() != other.validity() )
02334 return false;
02335 return true;
02336 }
02337
02338 QPoint Cell::cellPosition() const
02339 {
02340 Q_ASSERT( !isNull() );
02341 return QPoint( column(), row() );
02342 }