kspread

Cell.cpp

Go to the documentation of this file.
00001 /* This file is part of the KDE project
00002    Copyright 2006-2007 Stefan Nikolaus <stefan.nikolaus@kdemail.net>
00003    Copyright 2005 Raphael Langerhorst <raphael.langerhorst@kdemail.net>
00004    Copyright 2004-2005 Tomas Mecir <mecirt@gmail.com>
00005    Copyright 2004-2006 Inge Wallin <inge@lysator.liu.se>
00006    Copyright 1999-2002,2004,2005 Laurent Montel <montel@kde.org>
00007    Copyright 2002-2005 Ariya Hidayat <ariya@kde.org>
00008    Copyright 2001-2003 Philipp Mueller <philipp.mueller@gmx.de>
00009    Copyright 2002-2003 Norbert Andres <nandres@web.de>
00010    Copyright 2003 Reinhart Geiser <geiseri@kde.org>
00011    Copyright 2003-2005 Meni Livne <livne@kde.org>
00012    Copyright 2003 Peter Simonsson <psn@linux.se>
00013    Copyright 1999-2002 David Faure <faure@kde.org>
00014    Copyright 2000-2002 Werner Trobin <trobin@kde.org>
00015    Copyright 1999,2002 Harri Porten <porten@kde.org>
00016    Copyright 2002 John Dailey <dailey@vt.edu>
00017    Copyright 1998-2000 Torben Weis <weis@kde.org>
00018    Copyright 2000 Bernd Wuebben <wuebben@kde.org>
00019    Copyright 2000 Simon Hausmann <hausmann@kde.org
00020    Copyright 1999 Stephan Kulow <coolo@kde.org>
00021    Copyright 1999 Michael Reiher <michael.reiher@gmx.de>
00022    Copyright 1999 Boris Wedl <boris.wedl@kfunigraz.ac.at>
00023    Copyright 1998-1999 Reginald Stadlbauer <reggie@kde.org>
00024 
00025    This library is free software; you can redistribute it and/or
00026    modify it under the terms of the GNU Library General Public
00027    License as published by the Free Software Foundation; either
00028    version 2 of the License, or (at your option) any later version.
00029 
00030    This library is distributed in the hope that it will be useful,
00031    but WITHOUT ANY WARRANTY; without even the implied warranty of
00032    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00033    Library General Public License for more details.
00034 
00035    You should have received a copy of the GNU Library General Public License
00036    along with this library; see the file COPYING.LIB.  If not, write to
00037    the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
00038    Boston, MA 02110-1301, USA.
00039 */
00040 
00041 // Local
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; // KS_colMax
00097     uint    row     : 16; // KS_rowMax
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 // Return the sheet that this cell belongs to.
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 // Return true if this is the default cell.
00150 bool Cell::isDefault() const
00151 {
00152     // check each stored attribute
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     // empty = no value or formula
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 // Return true if this cell is a formula.
00188 //
00189 bool Cell::isFormula() const
00190 {
00191     return !formula().expression().isEmpty();
00192 }
00193 
00194 // Return the column number of this cell.
00195 //
00196 int Cell::column() const
00197 {
00198     // Make sure this isn't called for the null cell.  This assert
00199     // can save you (could have saved me!) the hassle of some very
00200     // obscure bugs.
00201     Q_ASSERT( !isNull() );
00202     Q_ASSERT( 1 <= d->column ); //&& d->column <= KS_colMax );
00203     return d->column;
00204 }
00205 
00206 // Return the row number of this cell.
00207 int Cell::row() const
00208 {
00209     // Make sure this isn't called for the null cell.  This assert
00210     // can save you (could have saved me!) the hassle of some very
00211     // obscure bugs.
00212     Q_ASSERT( !isNull() );
00213     Q_ASSERT( 1 <= d->row ); //&& d->row <= KS_rowMax );
00214     return d->row;
00215 }
00216 
00217 // Return the name of this cell, i.e. the string that the user would
00218 // use to reference it.  Example: A1, BZ16
00219 //
00220 QString Cell::name() const
00221 {
00222     return name( column(), row() );
00223 }
00224 
00225 // Return the name of any cell given by (col, row).
00226 //
00227 // static
00228 QString Cell::name( int col, int row )
00229 {
00230     return columnName( col ) + QString::number( row );
00231 }
00232 
00233 // Return the name of this cell, including the sheet name.
00234 // Example: sheet1!A5
00235 //
00236 QString Cell::fullName() const
00237 {
00238     return fullName( sheet(), column(), row() );
00239 }
00240 
00241 // Return the full name of any cell given a sheet and (col, row).
00242 //
00243 // static
00244 QString Cell::fullName( const Sheet* s, int col, int row )
00245 {
00246     return s->sheetName() + '!' + name( col, row );
00247 }
00248 
00249 // Return the symbolic name of the column of this cell.  Examples: A, BB.
00250 //
00251 QString Cell::columnName() const
00252 {
00253     return columnName( column() );
00254 }
00255 
00256 // Return the symbolic name of any column.
00257 //
00258 // static
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     // use conditional formatting attributes
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 // Return the user input of this cell.  This could, for instance, be a
00348 // formula.
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         // set the formula
00363         Formula formula( sheet(), *this );
00364         formula.setExpression( string );
00365         setFormula( formula );
00366         // remove an existing user input (the non-formula one)
00367         sheet()->cellStorage()->setUserInput( d->column, d->row, QString() );
00368     }
00369     else
00370     {
00371         // remove an existing formula
00372         setFormula( Formula() );
00373         // set the value
00374         sheet()->cellStorage()->setUserInput( d->column, d->row, string );
00375     }
00376 }
00377 
00378 
00379 // Return the out text, i.e. the text that is visible in the cells
00380 // square when shown.  This could, for instance, be the calculated
00381 // result of a formula.
00382 //
00383 QString Cell::displayText() const
00384 {
00385     if ( isNull() )
00386         return QString();
00387 
00388     QString string;
00389     const Style style = effectiveStyle();
00390     // Display a formula if warranted.  If not, display the value instead;
00391     // this is the most common case.
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 // Return the value of this cell.
00404 //
00405 const Value Cell::value() const
00406 {
00407     return sheet()->cellStorage()->value( d->column, d->row );
00408 }
00409 
00410 
00411 // Set the value of this cell.
00412 //
00413 void Cell::setValue( const Value& value )
00414 {
00415     sheet()->cellStorage()->setValue( d->column, d->row, value );
00416 }
00417 
00418 // FIXME: Continue commenting and cleaning here (ingwa)
00419 
00420 
00421 void Cell::copyFormat( const Cell& cell )
00422 {
00423     Q_ASSERT( !isNull() ); // trouble ahead...
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() ); // trouble ahead...
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() ); // trouble ahead...
00449     Q_ASSERT( !cell.isNull() );
00450     if (cell.isFormula())
00451     {
00452         // change all the references, e.g. from A1 to A3 if copying
00453         // from e.g. B2 to B4
00454         Formula formula( sheet(), *this );
00455         formula.setExpression( decodeFormula( cell.encodeFormula() ) );
00456         setFormula( formula );
00457     }
00458     else
00459     {
00460         // copy the user input
00461         sheet()->cellStorage()->setUserInput( d->column, d->row, cell.userInput() );
00462     }
00463     // copy the value in both cases
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     // Cell borders?
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     // Background color or brush?
00486     if ( style.hasAttribute( Style::BackgroundBrush ) ) {
00487         QBrush brush = style.backgroundBrush();
00488 
00489         // Only brushes that are visible (ie. they have a brush style
00490         // and are not white) need to be drawn
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         // We don't need to print anything, if the background is white opaque or fully transparent.
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()); // simply keep the area name
00527                     break;
00528                 }
00529                 const Region region(token.text(), sheet()->map());
00530                 // Actually, a contiguous region, but the fixation is needed
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 // ((*it)->type() == Region::Range)
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                 // Allow escaped double quotes (\")
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; // if 1st is relative but encoded absolutely
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             // Skip '#' or '$'
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             // Skip '#' or '$'
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); //Get column text
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 //                          Formula handling
00690 
00691 
00692 bool Cell::makeFormula()
00693 {
00694 //   kDebug(36002) ;
00695 
00696     // sanity check
00697     if ( !isFormula() )
00698         return false;
00699 
00700     // parse the formula and check for errors
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         //numbers should be right-aligned by default, as well as BiDi text
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 // parses the text
00755 void Cell::parseUserInput( const QString& text )
00756 {
00757 //   kDebug() ;
00758 
00759     // empty string?
00760     if ( text.isEmpty() )
00761     {
00762         setValue( Value::empty() );
00763         setUserInput( text );
00764         setFormula( Formula() );
00765         return;
00766     }
00767 
00768     // a formula?
00769     if ( text[0] == '=' )
00770     {
00771         Formula formula( sheet(), *this );
00772         formula.setExpression( text );
00773         setFormula( formula );
00774 
00775         // parse the formula and check for errors
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     // keep the old formula and value for the case, that validation fails
00790     const Formula oldFormula = formula();
00791     const QString oldUserInput = userInput();
00792     const Value oldValue = value();
00793 
00794     // here, the new value is not a formula anymore; clear an existing one
00795     setFormula( Formula() );
00796 
00797     Value value;
00798     if ( style().formatType() == Format::Text )
00799         value = Value( QString( text ) );
00800     else
00801     {
00802         // Parses the text and return the appropriate value.
00803         value = sheet()->map()->parser()->parse( text );
00804 
00805 #if 0
00806         // Parsing as time acts like an autoformat: we even change the input text
00807         // [h]:mm:ss -> might get set by ValueParser
00808         if ( isTime() && ( formatType() != Format::Time7 ) )
00809             setUserInput( locale()->formatTime( value().asDateTime( sheet()->map()->calculationSettings()).time(), true ) );
00810 #endif
00811 
00812         // convert first letter to uppercase ?
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     // set the new value
00820     setUserInput( text );
00821     setValue( value );
00822 
00823     // validation
00824     if ( !sheet()->isLoading() )
00825     {
00826         Validity validity = this->validity();
00827         if ( !validity.testValidity( this ) )
00828         {
00829             kDebug() <<"Validation failed";
00830             //reapply old value if action == stop
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 // Return true if this cell is part of a merged cell, but not the
00870 // master cell.
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 // Merge a number of cells, i.e. make this cell obscure a number of
00883 // other cells.  If _x and _y == 0, then the merging is removed.
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 //                       Saving and loading
00917 
00918 
00919 QDomElement Cell::save(QDomDocument& doc, int xOffset, int yOffset, bool era)
00920 {
00921     // Save the position of this cell
00922     QDomElement cell = doc.createElement( "cell" );
00923     cell.setAttribute( "row", row() - yOffset );
00924     cell.setAttribute( "column", column() - xOffset );
00925 
00926     //
00927     // Save the formatting information
00928     //
00929     QDomElement formatElement( doc.createElement( "format" ) );
00930     style().saveXML(doc, formatElement, sheet()->map()->styleManager());
00931     if ( formatElement.hasChildNodes() || formatElement.attributes().length() ) // don't save empty tags
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     // Save the text
00968     //
00969     if ( !userInput().isEmpty() )
00970     {
00971         // Formulas need to be encoded to ensure that they
00972         // are position independent.
00973         if ( isFormula() )
00974         {
00975             QDomElement txt = doc.createElement( "text" );
00976             // if we are cutting to the clipboard, relative references need to be encoded absolutely
00977             txt.appendChild( doc.createTextNode( encodeFormula( era ) ) );
00978             cell.appendChild( txt );
00979 
00980             /* we still want to save the results of the formula */
00981             QDomElement formulaResult = doc.createElement( "result" );
00982             saveCellResult( doc, formulaResult, displayText() );
00983             cell.appendChild( formulaResult );
00984 
00985         }
00986         else if ( !link().isEmpty() )
00987         {
00988             // KSpread pre 1.4 saves link as rich text, marked with first char '
00989             // Have to be saved in some CDATA section because of too many special charatcers.
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             // Save the cell contents (in a locale-independent way)
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 ) // don't save empty tags
01004         // (the >2 is due to "row" and "column" attributes)
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"; // fallback
01014 
01015   if ( value().isNumber() )
01016   {
01017       if ( isDate() )
01018       {
01019           // serial number of date
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           // serial number of time
01028           dataType = "Time";
01029           str = value().asDateTime(sheet()->map()->calculationSettings()).time().toString();
01030       }
01031       else
01032       {
01033           // real number
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; /* really isn't much of a way for this function to fail */
01062 }
01063 
01064 void Cell::saveOdfAnnotation( KoXmlWriter &xmlwriter )
01065 {
01066     const QString comment = this->comment();
01067     if ( !comment.isEmpty() )
01068     {
01069         //<office:annotation draw:style-name="gr1" draw:text-style-name="P1" svg:width="2.899cm" svg:height="2.691cm" svg:x="2.858cm" svg:y="0.001cm" draw:caption-point-x="-2.858cm" draw:caption-point-y="-0.001cm">
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 &currentCellStyle, KoGenStyles &mainStyles )
01082 {
01083     const Conditions conditions = this->conditions();
01084     if ( !conditions.isEmpty() )
01085     {
01086         // this has to be an automatic style
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     // see: OpenDocument, 8.1.3 Table Cell
01099     if ( !isPartOfMerged() )
01100         xmlwriter.startElement( "table:table-cell" );
01101     else
01102         xmlwriter.startElement( "table:covered-table-cell" );
01103 #if 0
01104     //add font style
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     // NOTE save the value before the style as long as the Formatter does not work correctly
01117     if ( link().isEmpty() )
01118       saveOdfValue (xmlwriter);
01119 
01120     // Either there's no column and row default and the style's not the default style,
01121     // or the style is different to one of them. The row default takes precedence.
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; // the type determined in saveOdfCellStyle
01129         saveOdfCellStyle( currentCellStyle, mainStyles );
01130         // skip 'table:style-name' attribute for the default style
01131         if ( !currentCellStyle.isDefaultStyle() )
01132             xmlwriter.addAttribute( "table:style-name", mainStyles.styles().find(currentCellStyle).value() );
01133     }
01134 
01135     // group empty cells with the same style
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         // if
01145         //   the next cell is not the adjacent one
01146         // or
01147         //   the next cell is not empty
01148         if (nextCell.column() != j || (!nextCell.isEmpty() || tableContext.cellHasAnchoredShapes(sheet(), row, column))) {
01149           if ( refCellIsDefault )
01150           {
01151             // if the origin cell was a default cell,
01152             // we count the default cells
01153             repeated = nextCell.column() - j + 1;
01154           }
01155           // otherwise we just stop here to process the adjacent
01156           // cell in the next iteration of the outer loop
01157           // (in Sheet::saveOdfCells)
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         // get the next cell and set the index to the adjacent cell
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       //kDebug(36003) <<"Formula found";
01187       QString formula = Odf::encodeFormula( userInput(), locale() );
01188       xmlwriter.addAttribute( "table:formula", formula );
01189     }
01190     else if ( !link().isEmpty() )
01191     {
01192         //kDebug(36003)<<"Link found";
01193         xmlwriter.startElement( "text:p" );
01194         xmlwriter.startElement( "text:a" );
01195         const QString url = link();
01196         //Reference cell is started by '#'
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     // flake
01226     // Save shapes that are anchored to this cell.
01227     // see: OpenDocument, 2.3.1 Text Documents
01228     // see: OpenDocument, 9.2 Drawing Shapes
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;  //NOTHING HERE
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;  //NOTHING HERE
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     //Search and load each paragraph of text. Each paragraph is separated by a line break.
01341     loadOdfCellText( element );
01342 
01343     //
01344     // formula
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         // each spreadsheet application likes to safe formulas with a different namespace
01353         // prefix, so remove all of them
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) == '=' ) //prepend ' to the text to avoid = to be painted
01367         setUserInput( userInput().prepend('\'') );
01368 
01369     //
01370     // validation
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     // value type
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         // integer and floating-point value
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         // currency value
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                   // FIXME Stefan: Only set it, if it differs from the default currency.
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 // FIXME Stefan: Should be handled by Value::Format. Verify and remove!
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             // "1980-10-15"
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 // FIXME Stefan: Should be handled by Value::Format. Verify and remove!
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             // "PT15H10M12S"
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                 // Value kval( timeToNum( hours, minutes, seconds ) );
01521                 // cell.setValue( kval );
01522                 setValue( Value( QTime( hours % 24, minutes, seconds ), sheet()->map()->calculationSettings()) );
01523 // FIXME Stefan: Should be handled by Value::Format. Verify and remove!
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                 // use the paragraph(s) read in before
01541                 setValue(Value(userInput()));
01542             }
01543 // FIXME Stefan: Should be handled by Value::Format. Verify and remove!
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             // Set the value by parsing the user input.
01554             parseUserInput(userInput());
01555         }
01556     }
01557     else // no value-type attribute
01558     {
01559         kDebug(36003) <<"  No value type specified. Parsing user input.";
01560         // Set the value by parsing the user input.
01561         parseUserInput(userInput());
01562     }
01563 
01564     //
01565     // merged cells ?
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     // cell comment/annotation
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     //Search and load each paragraph of text. Each paragraph is separated by a line break
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             // our text, could contain formating for value or result of formul
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                     // The value will be set later in loadOdf().
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         // The value will be set later in loadOdf().
01656     }
01657 
01658     //Enable word wrapping if multiple lines of text have been found.
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     // Register additional attributes, that identify shapes anchored in cells.
01670     // Their dimensions need adjustment after all rows are loaded,
01671     // because the position of the end cell is not always known yet.
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         // All three attributes are necessary for cell anchored shapes.
01699         // Otherwise, they are anchored in the sheet.
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         // The position is relative to the upper left sheet corner until now. Move it.
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         // The column dimensions are already the final ones, but not the row dimensions.
01732         // The default height is used for the not yet loaded rows.
01733         // TODO Stefan: Honor non-default row heights later!
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     // First of all determine in which row and column this
01753     // cell belongs.
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     // Validation
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     // Load formatting information.
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             // Validation
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             // Validation
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     // Load the condition section of a cell.
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       //clear the conditional formatting
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       // clear the validity
01841       setValidity( Validity() );
01842     }
01843 
01844     //
01845     // Load the comment
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         //t = t.trimmed();
01853         setComment( t );
01854     }
01855 
01856     //
01857     // The real content of the cell is loaded here. It is stored in
01858     // the "text" tag, which contains either a text or a CDATA section.
01859     //
01860     // TODO: make this suck less. We set data twice, in loadCellData, and
01861     // also here. Not good.
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       /* older versions mistakenly put the datatype attribute on the cell
01868          instead of the text.  Just move it over in case we're parsing
01869          an old document */
01870 #ifdef KOXML_USE_QDOM
01871       if ( cell.hasAttribute( "dataType" ) ) // new docs
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         // paste text of the element, if we want to paste result
01884         // and the source cell contains a formula
01885           setUserInput( result.text() );
01886       else
01887           //otherwise copy everything
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         // boolean ?
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   //TODO: use converter()->asString() to generate userInput()
01983 
01984   QString t = text.text();
01985   t = t.trimmed();
01986 
01987   // A formula like =A1+A2 ?
01988   if( (!t.isEmpty()) && (t[0] == '=') )
01989   {
01990     t = decodeFormula(t);
01991     parseUserInput (pasteOperation( t, userInput(), op ));
01992 
01993     makeFormula();
01994   }
01995   // rich text ?
01996   else if ((!t.isEmpty()) && (t[0] == '!') )
01997   {
01998       // KSpread pre 1.4 stores hyperlink as rich text (first char is '!')
01999       // extract the link and the correspoding text
02000       // This is a rather dirty hack, but enough for KSpread generated XML
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" ) ) // new docs
02048     {
02049         dataType = text.attribute( "dataType" );
02050     }
02051     else // old docs: do the ugly solution of parsing the text
02052     {
02053       // ...except for date/time
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       // boolean ?
02068       if( dataType == "Bool" )
02069         setValue(Value(t.toLower() == "true"));
02070 
02071       // number ?
02072       else if( dataType == "Num" )
02073       {
02074         bool ok = false;
02075         if (t.contains('.'))
02076           setValue ( Value( t.toDouble(&ok) ) ); // We save in non-localized format
02077         else
02078           setValue ( Value( t.toLongLong(&ok) ) );
02079         if ( !ok )
02080         {
02081           kWarning(36001) << "Couldn't parse '" << t << "' as number.";
02082         }
02083         /* We will need to localize the text version of the number */
02084         KLocale* locale = sheet()->map()->calculationSettings()->locale();
02085 
02086         /* KLocale::formatNumber requires the precision we want to return.
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       // date ?
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() ) // Should always be the case for new docs
02119           setUserInput( locale()->formatDate( value().asDate(sheet()->map()->calculationSettings()), KLocale::ShortDate ) );
02120         else // This happens with old docs, when format is set wrongly to date
02121         {
02122           parseUserInput( pasteOperation( t, userInput(), op ) );
02123         }
02124       }
02125 
02126       // time ?
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() ) // Should always be the case for new docs
02140           setUserInput( locale()->formatTime( value().asTime( sheet()->map()->calculationSettings()), true ) );
02141         else  // This happens with old docs, when format is set wrongly to time
02142         {
02143           parseUserInput( pasteOperation( t, userInput(), op ) );
02144         }
02145       }
02146 
02147       else
02148       {
02149         // Set the cell's text
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     //TODO: can't we use tryParseTime (after modification) instead?
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(); // pointers!
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 ); // isNull()
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 }