kspread

kspread_cell.cc

00001 /* This file is part of the KDE project
00002 
00003    Copyright 2006 Stefan Nikolaus <stefan.nikolaus@kdemail.net>
00004    Copyright 2005 Raphael Langerhorst <raphael.langerhorst@kdemail.net>
00005    Copyright 2004-2005 Tomas Mecir <mecirt@gmail.com>
00006    Copyright 2004-2006 Inge Wallin <inge@lysator.liu.se>
00007    Copyright 1999-2002,2004,2005 Laurent Montel <montel@kde.org>
00008    Copyright 2002-2005 Ariya Hidayat <ariya@kde.org>
00009    Copyright 2001-2003 Philipp Mueller <philipp.mueller@gmx.de>
00010    Copyright 2002-2003 Norbert Andres <nandres@web.de>
00011    Copyright 2003 Reinhart Geiser <geiseri@kde.org>
00012    Copyright 2003-2005 Meni Livne <livne@kde.org>
00013    Copyright 2003 Peter Simonsson <psn@linux.se>
00014    Copyright 1999-2002 David Faure <faure@kde.org>
00015    Copyright 2000-2002 Werner Trobin <trobin@kde.org>
00016    Copyright 1999,2002 Harri Porten <porten@kde.org>
00017    Copyright 2002 John Dailey <dailey@vt.edu>
00018    Copyright 1998-2000 Torben Weis <weis@kde.org>
00019    Copyright 2000 Bernd Wuebben <wuebben@kde.org>
00020    Copyright 2000 Simon Hausmann <hausmann@kde.org
00021    Copyright 1999 Stephan Kulow <coolo@kde.org>
00022    Copyright 1999 Michael Reiher <michael.reiher.gmx.de>
00023    Copyright 1999 Boris Wedl <boris.wedl@kfunigraz.ac.at>
00024    Copyright 1998-1999 Reginald Stadlbauer <reggie@kde.org>
00025 
00026 
00027    This library is free software; you can redistribute it and/or
00028    modify it under the terms of the GNU Library General Public
00029    License as published by the Free Software Foundation; either
00030    version 2 of the License, or (at your option) any later version.
00031 
00032    This library is distributed in the hope that it will be useful,
00033    but WITHOUT ANY WARRANTY; without even the implied warranty of
00034    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00035    Library General Public License for more details.
00036 
00037    You should have received a copy of the GNU Library General Public License
00038    along with this library; see the file COPYING.LIB.  If not, write to
00039    the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
00040  * Boston, MA 02110-1301, USA.
00041 */
00042 
00043 #include <stdlib.h>
00044 #include <ctype.h>
00045 #include <float.h>
00046 #include <math.h>
00047 
00048 #include <qapplication.h>
00049 #include <qpopupmenu.h>
00050 #include <qregexp.h>
00051 
00052 #include "kspread_canvas.h"
00053 #include "kspread_condition.h"
00054 #include "kspread_doc.h"
00055 #include "kspread_format.h"
00056 #include "kspread_global.h"
00057 #include "kspread_map.h"
00058 #include "kspread_sheet.h"
00059 #include "kspread_sheetprint.h"
00060 #include "kspread_style.h"
00061 #include "kspread_style_manager.h"
00062 #include "kspread_util.h"
00063 #include "ksploadinginfo.h"
00064 #include "kspread_genvalidationstyle.h"
00065 #include "kspread_locale.h"
00066 #include "kspread_value.h"
00067 #include "kspread_view.h"
00068 #include "kspread_value.h"
00069 #include "formula.h"
00070 #include "selection.h"
00071 #include "valueconverter.h"
00072 #include "valueformatter.h"
00073 #include "valueparser.h"
00074 
00075 #include <KoStyleStack.h>
00076 #include <KoRect.h>
00077 #include <KoXmlNS.h>
00078 #include <KoDom.h>
00079 #include <KoXmlWriter.h>
00080 #include <KoOasisStyles.h>
00081 
00082 #include <kmessagebox.h>
00083 
00084 #include <kdebug.h>
00085 
00086 using namespace KSpread;
00087 
00088 #define BORDER_SPACE 1
00089 
00090 
00095 namespace Cell_LNS
00096 {
00097   QChar decimal_point = '\0';
00098 }
00099 
00100 using namespace Cell_LNS;
00101 
00102 
00103 // Some variables are placed in Cell::Extra because normally they're
00104 // not required in simple case of cell(s). For example, most plain
00105 // text cells don't need to store information about spanned columns
00106 // and rows, as this is only the case with merged cells.
00107 //
00108 // When the cell is getting complex (e.g. merged with other cells,
00109 // contains rich text, has validation criteria, etc), this Cell::Extra
00110 // is allocated by Cell::Private and starts to be
00111 // available. Otherwise, it won't exist at all.
00112 
00113 class Cell::Extra
00114 {
00115 public:
00116   Extra() {}
00117 
00118   // Not empty when the cell holds a link
00119   QString link;
00120 
00121   // Number of cells explicitly merged by the user in X and Y directions.
00122   int mergedXCells;
00123   int mergedYCells;
00124 
00125   // Number of additional cells.
00126   int extraXCells;
00127   int extraYCells;
00128 
00129   // If this cell overlaps other cells, then we have the cells width and
00130   // height stored here.  These values do not mean anything unless
00131   // extraXCells and/or extraYCells are different from 0.
00132   double extraWidth;
00133   double extraHeight;
00134 
00135   // A list of cells that obscure this one.
00136   // If this list is not empty, then this cell is obscured by another
00137   // enlarged object. This means that we have to call this object in order
00138   // of painting it for example instead of painting 'this'.
00139   //
00140   // FIXME (comment): If the list consists of more than one obscuring
00141   //                  element, then is there an order between them that
00142   //                  is important?
00143   QValueList<Cell*> obscuringCells;
00144 
00145   // If non-NULL, contains a pointer to a condition or a validity test.
00146   Conditions  *conditions;
00147   Validity    *validity;
00148 
00149   // Store the number of line when multirow is used (default is 0)
00150   int nbLines;
00151 
00152 private:
00153   // Don't allow implicit copy.
00154   Extra& operator=( const Extra& );
00155 };
00156 
00157 
00158 class Cell::Private
00159 {
00160 public:
00161 
00162   Private();
00163   ~Private();
00164 
00165 public:
00166 
00167   // This cell's row and column. If either of them is 0, this is the
00168   // default cell and its row/column can not be determined.  Note that
00169   // in the isDefault() method, only column is tested.
00170   int  row;
00171   int  column;
00172 
00173   // Value of the cell, either typed by user or as result of formula
00174   Value value;
00175 
00176   // Holds the user's input.
00177   //
00178   // FIXME:
00179   // Eventually, we'll want to get rid of strText and generate
00180   // user's input on-the-fly. Then, for normal cells, we'll generate
00181   // this string using converter()->asString
00182   // (value()).
00183   //
00184   // Here the problem is, that strText also holds the formula -
00185   // we'll need to provide some method to generate it from the
00186   // parsed version, created in KSpread::Formula. Hence, we won't be
00187   // able to get rid of strText until we switch to the new formula
00188   // parser and until we write some method that re-generates the
00189   // input formula...
00190   //
00191   // Alternately, we can keep using strText for formulas and
00192   // generate it dynamically for static cells...
00193   //
00194   //  /Tomas
00195   //
00196   QString  strText;
00197 
00198   // This is the text we want to display. Not necessarily the same
00199   // as strText, e.g. strText="1" and strOutText="1.00" Also holds
00200   // value that we got from calculation, formerly known as
00201   // strFormulaOut
00202   QString  strOutText;
00203 
00204   // the Formula object for the cell
00205   KSpread::Formula *formula;
00206 
00207   // Position and dimension of displayed text.
00208   // FIXME (comment): Which coordinate system?  pixels?  mm/cm?  zoom?
00209   double  textX;
00210   double  textY;
00211   double  textWidth;
00212   double  textHeight;
00213 
00214   // result of "fm.ascent()" in makeLayout. used in offsetAlign.
00215   int  fmAscent;
00216 
00217   // Pointers to neighboring cells.
00218   // FIXME (comment): Which order?
00219   Cell  *nextCell;
00220   Cell  *previousCell;
00221 
00222   bool        hasExtra() const { return (cellExtra != 0); };
00223   Extra      *extra();
00224 
00225   Format     *format;
00226   Q_UINT32   flags;
00227 
00228 private:
00229   // "Extra stuff", see explanation for Cell::Extra.
00230   Extra  *cellExtra;
00231 };
00232 
00233 
00234 Cell::Private::Private()
00235 {
00236   // Some basic data.
00237   row     = 0;
00238   column  = 0;
00239   value   = Value::empty();
00240   formula = 0;
00241 
00242   // Formatting
00243   textX      = 0.0;
00244   textY      = 0.0;
00245   textWidth  = 0.0;
00246   textHeight = 0.0;
00247   fmAscent   = 0;
00248 
00249   nextCell     = 0;
00250   previousCell = 0;
00251 
00252   // Default is to not have the "extra" stuff in a cell.
00253   cellExtra = 0;
00254   format = 0;
00255   flags = 0;
00256 }
00257 
00258 
00259 Cell::Private::~Private()
00260 {
00261   delete cellExtra;
00262   delete formula;
00263 }
00264 
00265 
00266 Cell::Extra* Cell::Private::extra()
00267 {
00268     if ( !cellExtra ) {
00269       cellExtra = new Extra;
00270       cellExtra->conditions   = 0;
00271       cellExtra->validity     = 0;
00272 
00273       cellExtra->mergedXCells = 0;
00274       cellExtra->mergedYCells = 0;
00275       cellExtra->extraXCells  = 0;
00276       cellExtra->extraYCells  = 0;
00277       cellExtra->extraWidth   = 0.0;
00278       cellExtra->extraHeight  = 0.0;
00279       cellExtra->nbLines      = 0;
00280 //      cellExtra->highlight    = QColor(0,0,0);
00281     }
00282 
00283     return cellExtra;
00284 }
00285 
00286 
00287 /*****************************************************************************
00288  *
00289  *                                 Cell
00290  *
00291  *****************************************************************************/
00292 
00293 
00294 Cell::Cell( Sheet * _sheet, int _column, int _row )
00295 {
00296   d = new Private;
00297   d->row = _row;
00298   d->column = _column;
00299   d->format = new Format(_sheet, _sheet->doc()->styleManager()->defaultStyle());
00300   d->format->setCell(this);
00301   clearAllErrors();
00302 }
00303 
00304 
00305 Cell::Cell( Sheet * _sheet, Style * _style,  int _column, int _row )
00306 {
00307   d = new Private;
00308   d->row = _row;
00309   d->column = _column;
00310   d->format = new Format( _sheet, _style );
00311   d->format->setCell(this);
00312 
00313   clearAllErrors();
00314 }
00315 
00316 Format* Cell::format() const
00317 {
00318   return d->format;
00319 }
00320 
00321 // Return the sheet that this cell belongs to.
00322 Sheet * Cell::sheet() const
00323 {
00324   return d->format->sheet();
00325 }
00326 
00327 // Return true if this is the default cell.
00328 bool Cell::isDefault() const
00329 {
00330   return ( d->column == 0 );
00331 }
00332 
00333 // Return the row number of this cell.
00334 int Cell::row() const
00335 {
00336   // Make sure this isn't called for the default cell.  This assert
00337   // can save you (could have saved me!) the hassle of some very
00338   // obscure bugs.
00339 
00340   if ( isDefault() )
00341   {
00342     kdWarning(36001) << "Error: Calling Cell::row() for default cell" << endl;
00343     return 0;
00344   }
00345 
00346   return d->row;
00347 }
00348 
00349 
00350 // Return the column number of this cell.
00351 //
00352 int Cell::column() const
00353 {
00354   // Make sure this isn't called for the default cell.  This assert
00355   // can save you (could have saved me!) the hassle of some very
00356   // obscure bugs.
00357   if ( isDefault() )
00358   {
00359     kdWarning(36001) << "Error: Calling Cell::column() for default cell" << endl;
00360     return 0;
00361   }
00362   return d->column;
00363 }
00364 
00365 
00366 // Return the name of this cell, i.e. the string that the user would
00367 // use to reference it.  Example: A1, BZ16
00368 //
00369 QString Cell::name() const
00370 {
00371     return name( d->column, d->row );
00372 }
00373 
00374 
00375 // Return the name of any cell given by (col, row).
00376 //
00377 QString Cell::name( int col, int row )
00378 {
00379     return columnName( col ) + QString::number( row );
00380 }
00381 
00382 
00383 // Return the name of this cell, including the sheet name.
00384 // Example: sheet1!A5
00385 //
00386 QString Cell::fullName() const
00387 {
00388     return fullName( sheet(), d->column, d->row );
00389 }
00390 
00391 
00392 // Return the full name of any cell given a sheet and (col, row).
00393 //
00394 QString Cell::fullName( const Sheet* s, int col, int row )
00395 {
00396   return s->sheetName() + "!" + name( col, row );
00397 }
00398 
00399 
00400 // Return the symbolic name of the column of this cell.  Examples: A, BB.
00401 //
00402 QString Cell::columnName() const
00403 {
00404   return columnName( d->column );
00405 }
00406 
00407 KLocale* Cell::locale() const
00408 {
00409   return d->format->sheet()->doc()->locale();
00410 }
00411 
00412 // Return the symbolic name of any column.
00413 //
00414 QString Cell::columnName( uint column )
00415 {
00416     QString   str;
00417     unsigned  digits = 1;
00418     unsigned  offset = 0;
00419 
00420     column--;
00421 
00422     if( column > 4058115285U ) return  QString("@@@");
00423 
00424     for( unsigned limit = 26; column >= limit+offset; limit *= 26, digits++ )
00425         offset += limit;
00426 
00427     for( unsigned c = column - offset; digits; --digits, c/=26 )
00428         str.prepend( QChar( 'A' + (c%26) ) );
00429 
00430     return str;
00431 }
00432 
00433 
00434 // Return true if this cell is a formula.
00435 //
00436 bool Cell::isFormula() const
00437 {
00438     return d->strText[0] == '=';
00439 }
00440 
00441 
00442 // Return the input text of this cell.  This could, for instance, be a
00443 // formula.
00444 //
00445 // FIXME: These two functions are inconsistently named.  It should be
00446 //        either text() and outText() or strText() and strOutText().
00447 //
00448 QString Cell::text() const
00449 {
00450     return d->strText;
00451 }
00452 
00453 
00454 // Return the out text, i.e. the text that is visible in the cells
00455 // square when shown.  This could, for instance, be the calculated
00456 // result of a formula.
00457 //
00458 QString Cell::strOutText() const
00459 {
00460     return d->strOutText;
00461 }
00462 
00463 Formula *Cell::formula () const
00464 {
00465   return d->formula;
00466 }
00467 
00468 
00469 // Return the value of this cell.
00470 //
00471 const Value Cell::value() const
00472 {
00473   return d->value;
00474 }
00475 
00476 
00477 // Set the value of this cell.  It also clears all errors if the value
00478 // itself is not an error.
00479 //
00480 // In addition to this, it calculates the outstring and sets the dirty
00481 // flags so that a redraw is forced.
00482 //
00483 void Cell::setValue( const Value& v )
00484 {
00485   if (v.type() != Value::Error)
00486     clearAllErrors();
00487 
00488   //If the value has not changed then we don't need to do anything
00489   //(ie. no need to relayout, update dependant cells etc.),
00490   //unless this cell contains a formula, in which case its dependancies might have changed
00491   //even though the value has not.  For example, if this cell was previously empty (and its value is
00492   //therefore empty) and a new dependency upon an empty cell has been added.  The new value would still
00493   //be empty, but the dependencies need to be updated (via the call to valueChanged() below).
00494   if ( ( d->value == v ) && ( !isFormula() ) )
00495     return;
00496 
00497   d->value = v;
00498 
00499   setFlag(Flag_LayoutDirty);
00500   setFlag(Flag_TextFormatDirty);
00501 
00502   // Format and set the outText.
00503   setOutputText();
00504 
00505   // Set the displayed text, if we hold an error value.
00506   if (d->value.type() == Value::Error)
00507     d->strOutText = d->value.errorMessage ();
00508 
00509   // Value of the cell has changed - trigger necessary actions
00510   valueChanged ();
00511 
00512     if ( !format()->sheet()->isLoading() )
00513         format()->sheet()->setRegionPaintDirty(cellRect());
00514 }
00515 
00516 void Cell::setCellValue (const Value &v, FormatType fmtType, const QString &txt)
00517 {
00518     if ( !txt.isNull() )
00519     {
00520         d->strText = txt;
00521         if ( isFormula() )
00522             makeFormula();
00523     }
00524     else if ( !isFormula() )
00525         d->strText = sheet()->doc()->converter()->asString (v).asString();
00526     if (fmtType != No_format)
00527         format()->setFormatType (fmtType);
00528     setValue (v);
00529 }
00530 
00531 // FIXME: Continue commenting and cleaning here (ingwa)
00532 
00533 
00534 Cell* Cell::previousCell() const
00535 {
00536     return d->previousCell;
00537 }
00538 
00539 Cell* Cell::nextCell() const
00540 {
00541     return d->nextCell;
00542 }
00543 
00544 void Cell::setPreviousCell( Cell* c )
00545 {
00546     d->previousCell = c;
00547 }
00548 
00549 void Cell::setNextCell( Cell* c )
00550 {
00551     d->nextCell = c;
00552 }
00553 
00554 Validity* Cell::getValidity( int newStruct  )
00555 {
00556     if ( (!newStruct) && (!d->hasExtra()))
00557       //we don't have validity struct and we don't want one
00558       return 0;
00559 
00560     if( ( d->extra()->validity == 0 ) && ( newStruct == -1 ) )
00561         d->extra()->validity = new Validity;
00562     return  d->extra()->validity;
00563 }
00564 
00565 void Cell::removeValidity()
00566 {
00567     if (!d->hasExtra())
00568       return;
00569 
00570     delete d->extra()->validity;
00571     d->extra()->validity = 0;
00572 }
00573 
00574 
00575 void Cell::copyFormat( const int column , const int row )
00576 {
00577     const Cell * cell = format()->sheet()->cellAt( column , row );
00578 
00579     copyFormat( cell );
00580 }
00581 
00582 void Cell::copyFormat( const Cell* cell )
00583 {
00584 
00585     Q_ASSERT(cell);
00586 
00587     d->value.setFormat(cell->d->value.format());
00588     format()->copy(*(cell->format()));
00589 
00590     /*format()->setAlign( cell->format()->align( _column, _row ) );
00591     format()->setAlignY( cell->format()->alignY( _column, _row ) );
00592     format()->setTextFont( cell->format()->textFont( _column, _row ) );
00593     format()->setTextColor( cell->format()->textColor( _column, _row ) );
00594     format()->setBgColor( cell->bgColor( _column, _row) );
00595     setLeftBorderPen( cell->leftBorderPen( _column, _row ) );
00596     setTopBorderPen( cell->topBorderPen( _column, _row ) );
00597     setBottomBorderPen( cell->bottomBorderPen( _column, _row ) );
00598     setRightBorderPen( cell->rightBorderPen( _column, _row ) );
00599     format()->setFallDiagonalPen( cell->format()->fallDiagonalPen( _column, _row ) );
00600     format()->setGoUpDiagonalPen( cell->format()->goUpDiagonalPen( _column, _row ) );
00601     format()->setBackGroundBrush( cell->backGroundBrush( _column, _row) );
00602     format()->setPrecision( cell->format()->precision( _column, _row ) );
00603     format()->setPrefix( cell->format()->prefix( _column, _row ) );
00604     format()->setPostfix( cell->format()->postfix( _column, _row ) );
00605     format()->setFloatFormat( cell->format()->floatFormat( _column, _row ) );
00606     format()->setFloatColor( cell->format()->floatColor( _column, _row ) );
00607     format()->setMultiRow( cell->format()->multiRow( _column, _row ) );
00608     format()->setVerticalText( cell->format()->verticalText( _column, _row ) );
00609     format()->setDontPrintText( cell->format()->getDontprintText(_column, _row ) );
00610     format()->setNotProtected( cell->format()->notProtected(_column, _row ) );
00611     format()->setHideAll(cell->format()->isHideAll(_column, _row ) );
00612     format()->setHideFormula(cell->format()->isHideFormula(_column, _row ) );
00613     format()->setIndent( cell->format()->getIndent(_column, _row ) );
00614     format()->setAngle( cell->format()->getAngle(_column, _row) );
00615     format()->setFormatType( cell->format()->getFormatType(_column, _row) );
00616     Format::Currency c;
00617     if ( cell->format()->currencyInfo( c ) )
00618       format()->setCurrency( c );*/
00619 
00620     QValueList<Conditional> conditionList = cell->conditionList();
00621     if (d->hasExtra())
00622       delete d->extra()->conditions;
00623     if ( cell->d->hasExtra() && cell->d->extra()->conditions )
00624       setConditionList( conditionList );
00625     else
00626       if (d->hasExtra())
00627         d->extra()->conditions = 0;
00628 
00629     /*format()->setComment( cell->format()->comment( _column, _row ) );*/
00630 }
00631 
00632 void Cell::copyAll( Cell *cell )
00633 {
00634     Q_ASSERT( !isDefault() ); // trouble ahead...
00635     copyFormat( cell );
00636     copyContent( cell );
00637 }
00638 
00639 void Cell::copyContent( const Cell* cell )
00640 {
00641     Q_ASSERT( !isDefault() ); // trouble ahead...
00642 
00643     if (cell->isFormula() && cell->column() > 0 && cell->row() > 0)
00644     {
00645       // change all the references, e.g. from A1 to A3 if copying
00646       // from e.g. B2 to B4
00647       QString d = cell->encodeFormula();
00648       setCellText( cell->decodeFormula( d ) );
00649     }
00650     else
00651       setCellText( cell->text() );
00652 
00653 }
00654 
00655 void Cell::defaultStyle()
00656 {
00657   format()->defaultStyleFormat();
00658 
00659   if (!d->hasExtra())
00660     return;
00661 
00662   if ( d->extra()->conditions )
00663   {
00664     delete d->extra()->conditions;
00665     d->extra()->conditions = 0;
00666   }
00667 
00668   delete d->extra()->validity;
00669   d->extra()->validity = 0L;
00670 }
00671 
00672 
00673 // Merge a number of cells, i.e. make this cell obscure a number of
00674 // other cells.  If _x and _y == 0, then the merging is removed.
00675 
00676 void Cell::mergeCells( int _col, int _row, int _x, int _y )
00677 {
00678   // Start by unobscuring the cells that we obscure right now
00679   int  extraXCells = d->hasExtra() ? d->extra()->extraXCells : 0;
00680   int  extraYCells = d->hasExtra() ? d->extra()->extraYCells : 0;
00681   for ( int x = _col; x <= _col + extraXCells; ++x ) {
00682     for ( int y = _row; y <= _row + extraYCells; ++y ) {
00683       if ( x != _col || y != _row )
00684         format()->sheet()->nonDefaultCell( x, y )->unobscure(this);
00685     }
00686   }
00687 
00688   // If no merging, then remove all traces, and return.
00689   if ( _x == 0 && _y == 0 ) {
00690     clearFlag( Flag_Merged );
00691     if (d->hasExtra()) {
00692       d->extra()->extraXCells  = 0;
00693       d->extra()->extraYCells  = 0;
00694       d->extra()->extraWidth   = 0.0;
00695       d->extra()->extraHeight  = 0.0;
00696       d->extra()->mergedXCells = 0;
00697       d->extra()->mergedYCells = 0;
00698     }
00699 
00700     // Refresh the layout
00701     setFlag( Flag_LayoutDirty );
00702     return;
00703   }
00704 
00705   // At this point, we know that we will merge some cells.
00706   setFlag(Flag_Merged);
00707   d->extra()->extraXCells  = _x;
00708   d->extra()->extraYCells  = _y;
00709   d->extra()->mergedXCells = _x;
00710   d->extra()->mergedYCells = _y;
00711 
00712   // Obscure the cells
00713   for ( int x = _col; x <= _col + _x; ++x ) {
00714     for ( int y = _row; y <= _row + _y; ++y ) {
00715       if ( x != _col || y != _row )
00716   format()->sheet()->nonDefaultCell( x, y )->obscure( this, true );
00717     }
00718   }
00719 
00720   // Refresh the layout
00721   setFlag( Flag_LayoutDirty );
00722 }
00723 
00724 void Cell::move( int col, int row )
00725 {
00726     setLayoutDirtyFlag();
00727     setCalcDirtyFlag();
00728     setDisplayDirtyFlag();
00729 
00730     //int ex = extraXCells();
00731     //int ey = d->extra()->extraYCells();
00732 
00733     if (d->hasExtra())
00734       d->extra()->obscuringCells.clear();
00735 
00736     // Unobscure the objects we obscure right now
00737     int extraXCells = d->hasExtra() ? d->extra()->extraXCells : 0;
00738     int extraYCells = d->hasExtra() ? d->extra()->extraYCells : 0;
00739     for( int x = d->column; x <= d->column + extraXCells; ++x )
00740         for( int y = d->row; y <= d->row + extraYCells; ++y )
00741             if ( x != d->column || y != d->row )
00742             {
00743                 Cell *cell = format()->sheet()->nonDefaultCell( x, y );
00744                 cell->unobscure(this);
00745             }
00746 
00747     d->column = col;
00748     d->row    = row;
00749 
00750     if (d->hasExtra())
00751     {
00752       //    d->extra()->extraXCells = 0;
00753       //    d->extra()->extraYCells = 0;
00754       d->extra()->mergedXCells = 0;
00755       d->extra()->mergedYCells = 0;
00756     }
00757 
00758     // Cell value has been changed (because we're another cell now).
00759     valueChanged ();
00760 }
00761 
00762 void Cell::setLayoutDirtyFlag( bool format )
00763 {
00764     setFlag( Flag_LayoutDirty );
00765     if ( format )
00766         setFlag( Flag_TextFormatDirty );
00767 
00768     if (!d->hasExtra())
00769       return;
00770 
00771     QValueList<Cell*>::iterator it  = d->extra()->obscuringCells.begin();
00772     QValueList<Cell*>::iterator end = d->extra()->obscuringCells.end();
00773     for ( ; it != end; ++it ) {
00774       (*it)->setLayoutDirtyFlag( format );
00775     }
00776 }
00777 
00778 bool Cell::needsPrinting() const
00779 {
00780   if ( isDefault() )
00781     return false;
00782 
00783   if ( !d->strText.stripWhiteSpace().isEmpty() ) {
00784     return true;
00785   }
00786 
00787   // Cell borders?
00788   if ( format()->hasProperty( Format::PTopBorder )
00789        || format()->hasProperty( Format::PLeftBorder )
00790        || format()->hasProperty( Format::PRightBorder )
00791        || format()->hasProperty( Format::PBottomBorder )
00792        || format()->hasProperty( Format::PFallDiagonal )
00793        || format()->hasProperty( Format::PGoUpDiagonal ) ) {
00794     return true;
00795   }
00796 
00797   // Background color or brush?
00798   if ( format()->hasProperty( Format::PBackgroundBrush ) ) {
00799 
00800     const QBrush& brush=backGroundBrush(column(),row());
00801 
00802     //Only brushes that are visible (ie. they have a brush style and are not white)
00803     //need to be drawn
00804     if ( (brush.style() != Qt::NoBrush) &&
00805          (brush.color() != Qt::white || brush.pixmap()) )
00806         return true;
00807 
00808   }
00809 
00810   if ( format()->hasProperty( Format::PBackgroundColor ) ) {
00811     kdDebug() << "needsPrinting: Has background colour" << endl;
00812     QColor backgroundColor=bgColor(column(),row());
00813 
00814     //We don't need to print anything if the background is white
00815     if (backgroundColor != Qt::white)
00816         return true;
00817   }
00818 
00819   return false;
00820 }
00821 
00822 bool Cell::isEmpty() const
00823 {
00824     return isDefault() || d->strText.isEmpty();
00825 }
00826 
00827 
00828 // Return true if this cell is obscured by some other cell.
00829 
00830 bool Cell::isObscured() const
00831 {
00832   if (!d->hasExtra())
00833     return false;
00834 
00835   return !( d->extra()->obscuringCells.isEmpty() );
00836 }
00837 
00838 
00839 // Return true if this cell is part of a merged cell, but not the
00840 // master cell.
00841 
00842 bool Cell::isPartOfMerged() const
00843 {
00844   if (!d->hasExtra())
00845     return false;
00846 
00847   QValueList<Cell*>::const_iterator it = d->extra()->obscuringCells.begin();
00848   QValueList<Cell*>::const_iterator end = d->extra()->obscuringCells.end();
00849   for ( ; it != end; ++it ) {
00850     Cell *cell = *it;
00851 
00852     if (cell->doesMergeCells()) {
00853       // The cell might merge extra cells, and then overlap even
00854       // beyond that so just knowing that the obscuring cell merges
00855       // extra isn't enough.  We have to know that this cell is one of
00856       // the ones it is forcing over.
00857       if (column() <= cell->column() + cell->d->extra()->mergedXCells
00858     && row() <= cell->row() + cell->mergedYCells() )
00859   return true;
00860     }
00861   }
00862 
00863   return false;
00864 }
00865 
00866 
00867 // Return the cell that obscures this one.  If no cell is obscuring,
00868 // then return this.  This method is slightly complicated because we
00869 // can have several layers of obscuring.
00870 //
00871 // Update: it seems that if we do an actual merge, then the obscuring
00872 // cell is prepended and if just expanding, then it is appended.  This
00873 // means that we should be able to just look at the first one.
00874 
00875 Cell *Cell::ultimateObscuringCell() const
00876 {
00877   if (!d->hasExtra())
00878     return (Cell *) this;
00879 
00880   else if (d->extra()->obscuringCells.isEmpty())
00881     return (Cell *) this;
00882 
00883   else
00884     return d->extra()->obscuringCells.first();
00885 
00886 #if 0
00887   QValueList<Cell*>::const_iterator it = d->extra()->obscuringCells.begin();
00888   QValueList<Cell*>::const_iterator end = d->extra()->obscuringCells.end();
00889   for ( ; it != end; ++it ) {
00890     Cell *cell = *it;
00891 
00892     if (cell->doesMergeCells()) {
00893       // The cell might merge extra cells, and then overlap even
00894       // beyond that so just knowing that the obscuring cell merges
00895       // extra isn't enough.  We have to know that this cell is one of
00896       // the ones it is forcing over.
00897       if (column() <= cell->column() + cell->d->extra()->mergedXCells
00898     && row() <= cell->row() + cell->mergedYCells() )
00899   return true;
00900     }
00901   }
00902 
00903   return false;
00904 #endif
00905 }
00906 
00907 
00908 QValueList<Cell*> Cell::obscuringCells() const
00909 {
00910   if (!d->hasExtra())
00911   {
00912     QValueList<Cell*> empty;
00913     return empty;
00914   }
00915   return d->extra()->obscuringCells;
00916 }
00917 
00918 void Cell::clearObscuringCells()
00919 {
00920   if (!d->hasExtra())
00921     return;
00922   d->extra()->obscuringCells.clear();
00923 }
00924 
00925 void Cell::obscure( Cell *cell, bool isForcing )
00926 {
00927   if (d->hasExtra())
00928   {
00929     d->extra()->obscuringCells.remove( cell ); // removes *all* occurrences
00930     cell->clearObscuringCells();
00931   }
00932   if ( isForcing )
00933   {
00934     d->extra()->obscuringCells.prepend( cell );
00935   }
00936   else
00937   {
00938     d->extra()->obscuringCells.append( cell );
00939   }
00940   setFlag(Flag_LayoutDirty);
00941   format()->sheet()->setRegionPaintDirty( cellRect() );
00942 }
00943 
00944 void Cell::unobscure( Cell * cell )
00945 {
00946   if (d->hasExtra())
00947     d->extra()->obscuringCells.remove( cell );
00948   setFlag( Flag_LayoutDirty );
00949   format()->sheet()->setRegionPaintDirty( cellRect() );
00950 }
00951 
00952 QString Cell::encodeFormula( bool _era, int _col, int _row ) const
00953 {
00954     if ( _col == -1 )
00955         _col = d->column;
00956     if ( _row == -1 )
00957         _row = d->row;
00958 
00959     QString erg = "";
00960 
00961     if(d->strText.isEmpty())
00962         return d->strText;
00963 
00964     bool fix1 = false;
00965     bool fix2 = false;
00966     bool onNumber = false;
00967     unsigned int pos = 0;
00968     const unsigned int length = d->strText.length();
00969 
00970     // All this can surely be made 10 times faster, but I just "ported" it to QString
00971     // without any attempt to optimize things -- this is really brittle (Werner)
00972     while ( pos < length )
00973     {
00974         if ( d->strText[pos] == '"' )
00975         {
00976             erg += d->strText[pos++];
00977             while ( pos < length && d->strText[pos] != '"' )  // till the end of the world^H^H^H "string"
00978             {
00979                 erg += d->strText[pos++];
00980                 // Allow escaped double quotes (\")
00981                 if ( pos < length && d->strText[pos] == '\\' && d->strText[pos+1] == '"' )
00982                 {
00983                     erg += d->strText[pos++];
00984                     erg += d->strText[pos++];
00985                 }
00986             }
00987             if ( pos < length )  // also copy the trailing double quote
00988                 erg += d->strText[pos++];
00989 
00990             onNumber = false;
00991         }
00992         else if ( d->strText[pos].isDigit() )
00993         {
00994           erg += d->strText[pos++];
00995           fix1 = fix2 = false;
00996           onNumber = true;
00997         }
00998         else if ( d->strText[pos] != '$' && !d->strText[pos].isLetter() )
00999         {
01000             erg += d->strText[pos++];
01001             fix1 = fix2 = false;
01002             onNumber = false;
01003         }
01004         else
01005         {
01006             QString tmp = "";
01007             if ( d->strText[pos] == '$' )
01008             {
01009                 tmp = "$";
01010                 pos++;
01011                 fix1 = true;
01012             }
01013             if ( d->strText[pos].isLetter() )
01014             {
01015                 QString buffer;
01016                 unsigned int pos2 = 0;
01017                 while ( pos < length && d->strText[pos].isLetter() )
01018                 {
01019                     tmp += d->strText[pos];
01020                     buffer[pos2++] = d->strText[pos++];
01021                 }
01022                 if ( d->strText[pos] == '$' )
01023                 {
01024                     tmp += "$";
01025                     pos++;
01026                     fix2 = true;
01027                 }
01028                 if ( d->strText[pos].isDigit() )
01029                 {
01030                     const unsigned int oldPos = pos;
01031                     while ( pos < length && d->strText[pos].isDigit() ) ++pos;
01032                     int row = 0;
01033                     if ( pos != oldPos )
01034                         row = d->strText.mid(oldPos, pos-oldPos).toInt();
01035                     // Is it a sheet name || is it a function name like DEC2HEX
01036                     /* or if we're parsing a number, this could just be the
01037                        exponential part of it  (1.23E4) */
01038                     if ( ( d->strText[pos] == '!' ) ||
01039                          d->strText[pos].isLetter() ||
01040                          onNumber )
01041                     {
01042                         erg += tmp;
01043                         fix1 = fix2 = false;
01044                         pos = oldPos;
01045                     }
01046                     else // It must be a cell identifier
01047                     {
01048                         //now calculate the row as integer value
01049                         int col = 0;
01050                         col = util_decodeColumnLabelText( buffer );
01051                         if ( fix1 )
01052                             erg += QString( "$%1" ).arg( col );
01053                         else
01054                             if (_era)
01055                                 erg += QChar(0xA7) + QString( "%1" ).arg( col );
01056                             else
01057                                 erg += QString( "#%1" ).arg( col - _col );
01058 
01059                         if ( fix2 )
01060                             erg += QString( "$%1#").arg( row );
01061                         else
01062                             if (_era)
01063                                 erg += QChar(0xA7) + QString( "%1#" ).arg( row );
01064                             else
01065                                 erg += QString( "#%1#" ).arg( row - _row );
01066                     }
01067                 }
01068                 else
01069                 {
01070                     erg += tmp;
01071                     fix1 = fix2 = false;
01072                 }
01073             }
01074             else
01075             {
01076                 erg += tmp;
01077                 fix1 = false;
01078             }
01079             onNumber = false;
01080         }
01081     }
01082 
01083     return erg;
01084 }
01085 
01086 QString Cell::decodeFormula( const QString &_text, int _col, int _row) const
01087 {
01088     if ( _col == -1 )
01089         _col = d->column;
01090     if ( _row == -1 )
01091         _row = d->row;
01092 
01093     QString erg = "";
01094     unsigned int pos = 0;
01095     const unsigned int length = _text.length();
01096 
01097     if ( _text.isEmpty() )
01098         return QString();
01099 
01100     while ( pos < length )
01101     {
01102         if ( _text[pos] == '"' )
01103         {
01104             erg += _text[pos++];
01105             while ( pos < length && _text[pos] != '"' )
01106             {
01107                 erg += _text[pos++];
01108                 // Allow escaped double quotes (\")
01109                 if ( pos < length && _text[pos] == '\\' && _text[pos+1] == '"' )
01110                 {
01111                     erg += _text[pos++];
01112                     erg += _text[pos++];
01113                 }
01114             }
01115             if ( pos < length )
01116                 erg += _text[pos++];
01117         }
01118         else if ( _text[pos] == '#' || _text[pos] == '$' || _text[pos] == QChar(0xA7))
01119         {
01120             bool abs1 = false;
01121             bool abs2 = false;
01122             bool era1 = false; // if 1st is relative but encoded absolutely
01123             bool era2 = false;
01124 
01125             QChar _t = _text[pos++];
01126             if ( _t == '$' )
01127                 abs1 = true;
01128             else if ( _t == QChar(0xA7) )
01129                 era1 = true;
01130 
01131             int col = 0;
01132             unsigned int oldPos = pos;
01133             while ( pos < length && ( _text[pos].isDigit() || _text[pos] == '-' ) ) ++pos;
01134             if ( pos != oldPos )
01135                 col = _text.mid(oldPos, pos-oldPos).toInt();
01136             if ( !abs1 && !era1 )
01137                 col += _col;
01138             // Skip '#' or '$'
01139 
01140             _t = _text[pos++];
01141             if ( _t == '$' )
01142                  abs2 = true;
01143             else if ( _t == QChar(0xA7) )
01144                  era2 = true;
01145 
01146             int row = 0;
01147             oldPos = pos;
01148             while ( pos < length && ( _text[pos].isDigit() || _text[pos] == '-' ) ) ++pos;
01149             if ( pos != oldPos )
01150                 row = _text.mid(oldPos, pos-oldPos).toInt();
01151             if ( !abs2 && !era2)
01152                 row += _row;
01153             // Skip '#' or '$'
01154             ++pos;
01155             if ( row < 1 || col < 1 || row > KS_rowMax || col > KS_colMax )
01156             {
01157                 kdDebug(36001) << "Cell::decodeFormula: row or column out of range (col: " << col << " | row: " << row << ")" << endl;
01158                 erg = "=\"#### " + i18n("REFERENCE TO COLUMN OR ROW IS OUT OF RANGE") + "\"";
01159                 return erg;
01160             }
01161             if ( abs1 )
01162                 erg += "$";
01163             erg += Cell::columnName(col); //Get column text
01164 
01165             if ( abs2 )
01166                 erg += "$";
01167             erg += QString::number( row );
01168         }
01169         else
01170             erg += _text[pos++];
01171     }
01172 
01173     return erg;
01174 }
01175 
01176 
01177 void Cell::freeAllObscuredCells()
01178 {
01179     //
01180     // Free all obscured cells.
01181     //
01182 
01183   if (!d->hasExtra())
01184     return;
01185 
01186   for ( int x = d->column + d->extra()->mergedXCells;
01187   x <= d->column + d->extra()->extraXCells; ++x ) {
01188     for ( int y = d->row + d->extra()->mergedYCells;
01189     y <= d->row + d->extra()->extraYCells; ++y ) {
01190       if ( x != d->column || y != d->row ) {
01191         Cell *cell = format()->sheet()->cellAt( x, y );
01192         cell->unobscure(this);
01193       }
01194     }
01195   }
01196 
01197   d->extra()->extraXCells = d->extra()->mergedXCells;
01198   d->extra()->extraYCells = d->extra()->mergedYCells;
01199 
01200 }
01201 
01202 
01203 // ----------------------------------------------------------------
01204 //                              Layout
01205 
01206 
01207 // Recalculate the entire layout.  This includes the following members:
01208 //
01209 //   d->textX,     d->textY
01210 //   d->textWidth, d->textHeight
01211 //   d->fmAscent
01212 //   d->extra()->extraXCells, d->extra()->extraYCells
01213 //   d->extra()->extraWidth,  d->extra()->extraHeight
01214 //   d->extra()->nbLines (if multirow)
01215 //
01216 // and, of course,
01217 //
01218 //   d->strOutText
01219 //
01220 
01221 void Cell::makeLayout( QPainter &_painter, int _col, int _row )
01222 {
01223   // Are _col and _row really needed ?
01224   //
01225   // Yes they are: they are useful if this is the default layout, in
01226   // which case d->row and d->column are 0 and 0, but _col and _row
01227   // are the real coordinates of the cell.
01228 
01229   // There is no need to remake the layout if it hasn't changed.
01230   if ( !testFlag( Flag_LayoutDirty ) )
01231     return;
01232 
01233   // Some initializations.
01234   if (d->hasExtra())
01235     d->extra()->nbLines = 0;
01236   clearFlag( Flag_CellTooShortX );
01237   clearFlag( Flag_CellTooShortY );
01238 
01239   // Initiate the cells that this one is obscuring to the ones that
01240   // are actually merged.
01241   freeAllObscuredCells();
01242   if (d->hasExtra())
01243     mergeCells( d->column, d->row,
01244          d->extra()->mergedXCells, d->extra()->mergedYCells );
01245 
01246   // If the column for this cell is hidden or the row is too low,
01247   // there is no use in remaking the layout.
01248   ColumnFormat  *cl1 = format()->sheet()->columnFormat( _col );
01249   RowFormat     *rl1 = format()->sheet()->rowFormat( _row );
01250   if ( cl1->isHide()
01251        || ( rl1->dblHeight() <= format()->sheet()->doc()->unzoomItY( 2 ) ) ) {
01252       clearFlag( Flag_LayoutDirty );
01253       return;
01254   }
01255 
01256   // Recalculate the output text, d->strOutText.
01257   setOutputText();
01258 
01259   // Empty text?  Reset the outstring and, if this is the default
01260   // cell, return.
01261   if ( d->strOutText.isEmpty() ) {
01262     d->strOutText = QString::null;
01263 
01264     if ( isDefault() ) {
01265       clearFlag( Flag_LayoutDirty );
01266       return;
01267     }
01268   }
01269 
01270   // Up to here, we have just cared about the contents, not the
01271   // painting of it.  Now it is time to see if the contents fits into
01272   // the cell and, if not, maybe rearrange the outtext a bit.
01273   //
01274   // First, Determine the correct font with zoom taken into account,
01275   // and apply it to _painter.  Then calculate text dimensions, i.e.
01276   // d->textWidth and d->textHeight.
01277   applyZoomedFont( _painter, _col, _row );
01278   textSize( _painter );
01279 
01280   //
01281   // Calculate the size of the cell
01282   //
01283   RowFormat     *rl = format()->sheet()->rowFormat( d->row );
01284   ColumnFormat  *cl = format()->sheet()->columnFormat( d->column );
01285 
01286   double         width  = cl->dblWidth();
01287   double         height = rl->dblHeight();
01288 
01289   // Calculate extraWidth and extraHeight if we have a merged cell.
01290   if ( testFlag( Flag_Merged ) ) {
01291     int  extraXCells = d->hasExtra() ? d->extra()->extraXCells : 0;
01292     int  extraYCells = d->hasExtra() ? d->extra()->extraYCells : 0;
01293 
01294     // FIXME: Introduce double extraWidth/Height here and use them
01295     //        instead (see FIXME about this in paintCell()).
01296 
01297     for ( int x = _col + 1; x <= _col + extraXCells; x++ )
01298       width += format()->sheet()->columnFormat( x )->dblWidth();
01299 
01300     for ( int y = _row + 1; y <= _row + extraYCells; y++ )
01301       height += format()->sheet()->rowFormat( y )->dblHeight();
01302   }
01303 
01304   // Cache the newly calculated extraWidth and extraHeight if we have
01305   // already allocated a struct for it.  Otherwise it will be zero, so
01306   // don't bother.
01307   if (d->hasExtra()) {
01308     d->extra()->extraWidth  = width;
01309     d->extra()->extraHeight = height;
01310   }
01311 
01312   QFontMetrics  fm = _painter.fontMetrics();
01313   d->fmAscent = fm.ascent();
01314 
01315   // Check if we need to break the line into multiple lines and are
01316   // allowed to do so.  If so, set `lines' to the number of lines that
01317   // are needed to fit into the total width of the combined cell.
01318   //
01319   // Also recalculate d->textHeight, d->textWidth, d->extra->nbLines
01320   // and d->strOutText.
01321   //
01322   int  lines = 1;
01323   if ( d->textWidth > (width - 2 * BORDER_SPACE
01324        - format()->leftBorderWidth( _col, _row )
01325        - format()->rightBorderWidth( _col, _row ) )
01326        && format()->multiRow( _col, _row ) )
01327   {
01328     // Copy of d->strOutText but without the newlines.
01329 //     QString  o = d->strOutText.replace( QChar('\n'), " " );
01330 
01331     // don't remove the existing LF, these are intended line wraps (whishlist #9881)
01332     QString  o = d->strOutText;
01333 
01334     // Break the line at appropriate places, i.e. spaces, if
01335     // necessary.  This means to change the spaces where breaks occur
01336     // into newlines.
01337     if ( o.find(' ') != -1 )
01338     {
01339       d->strOutText = "";
01340 
01341       // Make sure that we have a space at the end.
01342       o += ' ';
01343 
01344       int start = 0;    // Start of the line we are handling now
01345       int breakpos = 0;   // The next candidate pos to break the string
01346       int pos1 = 0;
01347       int availableWidth = (int) ( width - 2 * BORDER_SPACE
01348           - format()->leftBorderWidth( _col, _row )
01349           - format()->rightBorderWidth( _col, _row ) );
01350 
01351       do {
01352 
01353         breakpos = o.find( ' ', breakpos );
01354         int linefeed = o.find( '\n', pos1 );
01355 
01356 //         kdDebug() << "start: " << start << "; breakpos: " << breakpos << "; pos1: " << pos1 << "; linefeed: " << linefeed << endl;
01357 
01358         //don't miss LF as a position to calculate current lineWidth
01359         int work_breakpos = breakpos;
01360         if (pos1 < linefeed && linefeed < breakpos)
01361           work_breakpos = linefeed;
01362 
01363         double lineWidth = format()->sheet()->doc()
01364             ->unzoomItX( fm.width( d->strOutText.mid( start, (pos1 - start) )
01365                 + o.mid( pos1, work_breakpos - pos1 ) ) );
01366 
01367         //linefeed could be -1 when no linefeed is found!
01368         if (breakpos > linefeed && linefeed > 0)
01369         {
01370 //           kdDebug() << "applying linefeed to start;" << endl;
01371           start = linefeed;
01372           lines++;
01373         }
01374 
01375         if ( lineWidth <= availableWidth ) {
01376             // We have room for the rest of the line.  End it here.
01377             d->strOutText += o.mid( pos1, breakpos - pos1 );
01378             pos1 = breakpos;
01379         }
01380         else {
01381             // Still not enough room.  Try to split further.
01382             if ( o.at( pos1 ) == ' ' )
01383             pos1++;
01384 
01385             if ( pos1 != 0 && breakpos != -1 ) {
01386             d->strOutText += "\n" + o.mid( pos1, breakpos - pos1 );
01387             lines++;
01388             }
01389             else
01390             d->strOutText += o.mid( pos1, breakpos - pos1 );
01391 
01392             start = pos1;
01393             pos1 = breakpos;
01394         }
01395 
01396         breakpos++;
01397       } while( o.find( ' ', breakpos ) != -1 );
01398     }
01399     else
01400     {
01401       lines = o.contains('\n');
01402     }
01403 
01404     d->textHeight *= lines;
01405     if (lines > 1)
01406       d->extra()->nbLines = lines;
01407 
01408     d->textX = 0.0;
01409 
01410     // Calculate the maximum width, taking into account linebreaks,
01411     // and put it in d->textWidth.
01412     QString  t;
01413     int      i;
01414     int      pos = 0;
01415     d->textWidth = 0.0;
01416     do {
01417       i = d->strOutText.find( "\n", pos );
01418 
01419       if ( i == -1 )
01420   t = d->strOutText.mid( pos, d->strOutText.length() - pos );
01421       else {
01422   t = d->strOutText.mid( pos, i - pos );
01423   pos = i + 1;
01424       }
01425 
01426       double  tw = format()->sheet()->doc()->unzoomItX( fm.width( t ) );
01427       if ( tw > d->textWidth )
01428   d->textWidth = tw;
01429     } while ( i != -1 );
01430   }
01431 
01432   // Calculate d->textX and d->textY
01433   offsetAlign( _col, _row );
01434 
01435   int a = effAlignX();
01436 
01437   // Get indentation.  This is only used for left aligned text.
01438   double indent = 0.0;
01439   if ( a == Format::Left && !isEmpty() )
01440     indent = format()->getIndent( _col, _row );
01441 
01442   // Set Flag_CellTooShortX if the text is vertical or angled, and too
01443   // high for the cell.
01444   if ( format()->verticalText( _col, _row ) || format()->getAngle( _col, _row ) != 0 ) {
01445     //RowFormat  *rl = format()->sheet()->rowFormat( _row );
01446 
01447     if ( d->textHeight >= height/*rl->dblHeight()*/ )
01448       setFlag( Flag_CellTooShortX );
01449   }
01450 
01451   // Do we have to occupy additional cells to the right?  This is only
01452   // done for cells that have no merged cells in the Y direction.
01453   //
01454   // FIXME: Check if all cells along the merged edge to the right are
01455   //        empty and use the extra space?  No, probably not.
01456   //
01457   if ( d->textWidth + indent > ( width - 2 * BORDER_SPACE
01458          - format()->leftBorderWidth( _col, _row )
01459          - format()->rightBorderWidth( _col, _row ) )
01460        && ( !d->hasExtra() || d->extra()->mergedYCells == 0 ) )
01461   {
01462     int c = d->column;
01463 
01464     // Find free cells to the right of this one.
01465     int end = 0;
01466     while ( !end ) {
01467       ColumnFormat  *cl2  = format()->sheet()->columnFormat( c + 1 );
01468       Cell   *cell = format()->sheet()->visibleCellAt( c + 1, d->row );
01469 
01470       if ( cell->isEmpty() ) {
01471   width += cl2->dblWidth() - 1;
01472   c++;
01473 
01474   // Enough space?
01475   if ( d->textWidth + indent <= ( width - 2 * BORDER_SPACE
01476           - format()->leftBorderWidth( _col, _row )
01477           - format()->rightBorderWidth( _col, _row ) ) )
01478     end = 1;
01479       }
01480       else
01481   // Not enough space, but the next cell is not empty
01482   end = -1;
01483     }
01484 
01485     // Try to use additional space from the neighboring cells that
01486     // were calculated in the last step.  This is the place that we
01487     // set d->extra()->extraXCells and d->extra()->extraWidth.
01488     //
01489     // Currently this is only done for left aligned cells. We have to
01490     // check to make sure we haven't already force-merged enough cells
01491     //
01492     // FIXME: Why not right/center aligned text?
01493     //
01494     // FIXME: Shouldn't we check to see if end == -1 here before
01495     //        setting Flag_CellTooShortX?
01496     //
01497     if ( format()->align( _col, _row ) == Format::Left
01498          || ( format()->align( _col, _row ) == Format::Undefined
01499         && !value().isNumber() ) )
01500     {
01501       if ( c - d->column > d->extra()->mergedXCells ) {
01502   d->extra()->extraXCells = c - d->column;
01503   d->extra()->extraWidth  = width;
01504   for ( int i = d->column + 1; i <= c; ++i ) {
01505     Cell *cell = format()->sheet()->nonDefaultCell( i, d->row );
01506     cell->obscure( this );
01507   }
01508 
01509   // Not enough space
01510   if ( end == -1 )
01511     setFlag( Flag_CellTooShortX );
01512       }
01513       else
01514   setFlag( Flag_CellTooShortX );
01515     }
01516     else
01517       setFlag( Flag_CellTooShortX );
01518   }
01519 
01520   // Do we have to occupy additional cells at the bottom ?
01521   //
01522   // FIXME: Setting to make the current cell grow.
01523   //
01524   if ( format()->multiRow( _col, _row )
01525        && d->textHeight > ( height - 2 * BORDER_SPACE
01526           - format()->topBorderWidth( _col, _row )
01527           - format()->bottomBorderWidth( _col, _row ) ) )
01528   {
01529     int  r   = d->row;
01530     int  end = 0;
01531 
01532     // Find free cells bottom to this one
01533     while ( !end ) {
01534       RowFormat    *rl2  = format()->sheet()->rowFormat( r + 1 );
01535       Cell  *cell = format()->sheet()->visibleCellAt( d->column, r + 1 );
01536 
01537       if ( cell->isEmpty() ) {
01538   height += rl2->dblHeight() - 1.0;
01539   r++;
01540 
01541   // Enough space ?
01542   if ( d->textHeight <= ( height - 2 * BORDER_SPACE
01543         - format()->topBorderWidth( _col, _row )
01544         - format()->bottomBorderWidth( _col, _row ) ) )
01545     end = 1;
01546       }
01547       else
01548   // Not enough space, but the next cell is not empty.
01549   end = -1;
01550     }
01551 
01552     // Check to make sure we haven't already force-merged enough cells.
01553     if ( r - d->row > d->extra()->mergedYCells )
01554     {
01555       d->extra()->extraYCells = r - d->row;
01556       d->extra()->extraHeight = height;
01557 
01558       for ( int i = d->row + 1; i <= r; ++i )
01559       {
01560     Cell  *cell = format()->sheet()->nonDefaultCell( d->column, i );
01561     cell->obscure( this );
01562       }
01563 
01564       // Not enough space?
01565       if ( end == -1 )
01566     setFlag( Flag_CellTooShortY );
01567     }
01568     else
01569       setFlag( Flag_CellTooShortY );
01570   }
01571 
01572   clearFlag( Flag_LayoutDirty );
01573 
01574   return;
01575 }
01576 
01577 
01578 void Cell::valueChanged ()
01579 {
01580   update();
01581 
01582   format()->sheet()->valueChanged (this);
01583 }
01584 
01585 
01586 // Recalculate d->strOutText.
01587 //
01588 
01589 void Cell::setOutputText()
01590 {
01591   if ( isDefault() ) {
01592     d->strOutText = QString::null;
01593 
01594     if ( d->hasExtra() && d->extra()->conditions )
01595       d->extra()->conditions->checkMatches();
01596 
01597     return;
01598   }
01599 
01600   // If nothing has changed, we don't need to remake the text layout.
01601   if ( !testFlag(Flag_TextFormatDirty) )
01602     return;
01603 
01604   // We don't want to remake the layout unnecessarily.
01605   clearFlag( Flag_TextFormatDirty );
01606 
01607   // Display a formula if warranted.  If not, display the value instead;
01608   // this is the most common case.
01609   if ( (!hasError()) && isFormula() && format()->sheet()->getShowFormula()
01610        && !( format()->sheet()->isProtected() && format()->isHideFormula( d->column, d->row ) )
01611        || isEmpty() )
01612     d->strOutText = d->strText;
01613   else {
01614     d->strOutText = sheet()->doc()->formatter()->formatText (this,
01615                    formatType());
01616   }
01617 
01618   // Check conditions if needed.
01619   if ( d->hasExtra() && d->extra()->conditions )
01620     d->extra()->conditions->checkMatches();
01621 }
01622 
01623 
01624 // Recalculate d->textX and d->textY.
01625 //
01626 // Used in makeLayout() and calculateTextParameters().
01627 //
01628 
01629 void Cell::offsetAlign( int _col, int _row )
01630 {
01631   int       a;
01632   Format::AlignY  ay;
01633   int       tmpAngle;
01634   bool      tmpVerticalText;
01635   bool      tmpMultiRow;
01636 
01637   if ( d->hasExtra()
01638        && d->extra()->conditions
01639        && d->extra()->conditions->matchedStyle() )
01640   {
01641     Style  *style = d->extra()->conditions->matchedStyle();
01642 
01643     if ( style->hasFeature( Style::SAlignX, true ) )
01644       a = style->alignX();
01645     else
01646       a = format()->align( _col, _row );
01647 
01648     if ( style->hasFeature( Style::SVerticalText, true ) )
01649       tmpVerticalText = style->hasProperty( Style::PVerticalText );
01650     else
01651       tmpVerticalText = format()->verticalText( _col, _row );
01652 
01653     if ( style->hasFeature( Style::SMultiRow, true ) )
01654       tmpMultiRow = style->hasProperty( Style::PMultiRow );
01655     else
01656       tmpMultiRow = format()->multiRow( _col, _row );
01657 
01658     if ( style->hasFeature( Style::SAlignY, true ) )
01659       ay = style->alignY();
01660     else
01661       ay = format()->alignY( _col, _row );
01662 
01663     if ( style->hasFeature( Style::SAngle, true ) )
01664       tmpAngle = style->rotateAngle();
01665     else
01666       tmpAngle = format()->getAngle( _col, _row );
01667   }
01668   else {
01669     a               = format()->align( _col, _row );
01670     ay              = format()->alignY( _col, _row );
01671     tmpAngle        = format()->getAngle( _col, _row );
01672     tmpVerticalText = format()->verticalText( _col, _row );
01673     tmpMultiRow     = format()->multiRow( _col, _row );
01674   }
01675 
01676   RowFormat     *rl = format()->sheet()->rowFormat( _row );
01677   ColumnFormat  *cl = format()->sheet()->columnFormat( _col );
01678 
01679   double  w = cl->dblWidth();
01680   double  h = rl->dblHeight();
01681 
01682   if ( d->hasExtra() ) {
01683     if ( d->extra()->extraXCells )  w = d->extra()->extraWidth;
01684     if ( d->extra()->extraYCells )  h = d->extra()->extraHeight;
01685   }
01686 
01687   const double effTop = BORDER_SPACE + 0.5 * effTopBorderPen( _col, _row ).width();
01688   const double effBottom = h - BORDER_SPACE - 0.5 * effBottomBorderPen( _col, _row ).width();
01689 
01690   // Calculate d->textY based on the vertical alignment and a few
01691   // other inputs.
01692   switch( ay )
01693   {
01694   case Format::Top:
01695   {
01696     if ( tmpAngle == 0 )
01697     {
01698       d->textY = effTop + (double) d->fmAscent / format()->sheet()->doc()->zoomedResolutionY();
01699     }
01700     else if ( tmpAngle < 0 )
01701     {
01702       d->textY = effTop;
01703     }
01704     else
01705     {
01706       d->textY = effTop
01707                + (double) d->fmAscent * cos( tmpAngle * M_PI / 180 ) / format()->sheet()->doc()->zoomedResolutionY();
01708     }
01709     break;
01710   }
01711   case Format::Bottom:
01712   {
01713     if ( !tmpVerticalText && !tmpMultiRow && !tmpAngle )
01714     {
01715       d->textY = effBottom;
01716     }
01717     else if ( tmpAngle != 0 )
01718     {
01719       // Is enough place available?
01720       if ( effBottom - effTop - d->textHeight > 0 )
01721       {
01722         if ( tmpAngle < 0 )
01723         {
01724           d->textY = effBottom - d->textHeight;
01725         }
01726         else
01727         {
01728           d->textY = effBottom - d->textHeight
01729                    + ( (double) d->fmAscent * cos( tmpAngle * M_PI / 180 ) / format()->sheet()->doc()->zoomedResolutionY() );
01730         }
01731       }
01732       else
01733       {
01734         if ( tmpAngle < 0 )
01735         {
01736           d->textY = effTop;
01737         }
01738         else
01739         {
01740           d->textY = effTop
01741                   + ( (double) d->fmAscent * cos( tmpAngle * M_PI / 180 )
01742                   / format()->sheet()->doc()->zoomedResolutionY() );
01743         }
01744       }
01745     }
01746     else if ( tmpMultiRow && !tmpVerticalText )
01747     {
01748       // Is enough place available?
01749       if ( effBottom - effTop - d->textHeight > 0 )
01750       {
01751         d->textY = effBottom - d->textHeight
01752                  + (double)d->fmAscent / format()->sheet()->doc()->zoomedResolutionY();
01753       }
01754       else
01755       {
01756         d->textY = effTop
01757                  + (double) d->fmAscent / format()->sheet()->doc()->zoomedResolutionY();
01758       }
01759     }
01760     else
01761     {
01762       // Is enough place available?
01763       if ( effBottom - effTop - d->textHeight > 0 )
01764       {
01765         d->textY = effBottom - d->textHeight
01766                  + (double)d->fmAscent / format()->sheet()->doc()->zoomedResolutionY();
01767       }
01768       else
01769       {
01770         d->textY = effTop
01771                  + (double) d->fmAscent / format()->sheet()->doc()->zoomedResolutionY();
01772       }
01773     }
01774     break;
01775   }
01776   case Format::Middle:
01777   case Format::UndefinedY:
01778   {
01779     if ( !tmpVerticalText && !tmpMultiRow && !tmpAngle )
01780     {
01781       d->textY = ( h - d->textHeight ) / 2
01782                + (double) d->fmAscent / format()->sheet()->doc()->zoomedResolutionY();
01783     }
01784     else if ( tmpAngle != 0 )
01785     {
01786       // Is enough place available?
01787       if ( effBottom - effTop - d->textHeight > 0 )
01788       {
01789         if ( tmpAngle < 0 )
01790         {
01791           d->textY = ( h - d->textHeight ) / 2;
01792         }
01793         else
01794         {
01795           d->textY = ( h - d->textHeight ) / 2
01796                    + (double) d->fmAscent * cos( tmpAngle * M_PI / 180 ) / format()->sheet()->doc()->zoomedResolutionY();
01797         }
01798       }
01799       else
01800       {
01801         if ( tmpAngle < 0 )
01802         {
01803           d->textY = effTop;
01804         }
01805         else
01806         {
01807           d->textY = effTop
01808                    + ( (double)d->fmAscent * cos( tmpAngle * M_PI / 180 ) / format()->sheet()->doc()->zoomedResolutionY() );
01809         }
01810       }
01811     }
01812     else if ( tmpMultiRow && !tmpVerticalText )
01813     {
01814       // Is enough place available?
01815       if ( effBottom - effTop - d->textHeight > 0 )
01816       {
01817         d->textY = ( h - d->textHeight ) / 2
01818                  + (double) d->fmAscent / format()->sheet()->doc()->zoomedResolutionY();
01819       }
01820       else
01821       {
01822         d->textY = effTop
01823                  + (double) d->fmAscent / format()->sheet()->doc()->zoomedResolutionY();
01824       }
01825     }
01826     else
01827     {
01828       // Is enough place available?
01829       if ( effBottom - effTop - d->textHeight > 0 )
01830       {
01831         d->textY = ( h - d->textHeight ) / 2
01832                  + (double) d->fmAscent / format()->sheet()->doc()->zoomedResolutionY();
01833       }
01834       else
01835         d->textY = effTop
01836                  + (double)d->fmAscent / format()->sheet()->doc()->zoomedResolutionY();
01837     }
01838     break;
01839   }
01840   }
01841 
01842   a = effAlignX();
01843   if ( format()->sheet()->getShowFormula() &&
01844        !( format()->sheet()->isProtected() && format()->isHideFormula( _col, _row ) ) )
01845   {
01846     a = Format::Left;
01847   }
01848 
01849   // Calculate d->textX based on alignment and textwidth.
01850   switch ( a ) {
01851   case Format::Left:
01852     d->textX = 0.5 * effLeftBorderPen( _col, _row ).width() + BORDER_SPACE;
01853     break;
01854   case Format::Right:
01855     d->textX = w - BORDER_SPACE - d->textWidth
01856              - 0.5 * effRightBorderPen( _col, _row ).width();
01857     break;
01858   case Format::Center:
01859     d->textX = 0.5 * ( w - BORDER_SPACE - d->textWidth -
01860                        0.5 * effRightBorderPen( _col, _row ).width() );
01861     break;
01862   }
01863 }
01864 
01865 
01866 // Recalculate the current text dimensions, i.e. d->textWidth and
01867 // d->textHeight.
01868 //
01869 // Used in makeLayout() and calculateTextParameters().
01870 //
01871 void Cell::textSize( QPainter &_paint )
01872 {
01873   QFontMetrics  fm = _paint.fontMetrics();
01874   // Horizontal text ?
01875 
01876   int    tmpAngle;
01877   int    _row = row();
01878   int    _col = column();
01879   bool   tmpVerticalText;
01880   bool   fontUnderlined;
01881   Format::AlignY ay;
01882 
01883   // Set tmpAngle, tmpeVerticalText, ay and fontUnderlined according
01884   // to if there is a matching condition or not.
01885   if ( d->hasExtra()
01886        && d->extra()->conditions
01887        && d->extra()->conditions->matchedStyle() )
01888   {
01889     Style  *style = d->extra()->conditions->matchedStyle();
01890 
01891     if ( style->hasFeature( Style::SAngle, true ) )
01892       tmpAngle = style->rotateAngle();
01893     else
01894       tmpAngle = format()->getAngle( _col, _row );
01895 
01896     if ( style->hasFeature( Style::SVerticalText, true ) )
01897       tmpVerticalText = style->hasProperty( Style::PVerticalText );
01898     else
01899       tmpVerticalText = format()->verticalText( _col, _row );
01900 
01901     if ( style->hasFeature( Style::SAlignY, true ) )
01902       ay = style->alignY();
01903     else
01904       ay = format()->alignY( _col, _row );
01905 
01906     if ( style->hasFeature( Style::SFontFlag, true ) )
01907       fontUnderlined = ( style->fontFlags() & (uint) Style::FUnderline );
01908     else
01909       fontUnderlined = format()->textFontUnderline( _col, _row );
01910   }
01911   else {
01912     // The cell has no condition with a maxed style.
01913     tmpAngle        = format()->getAngle( _col, _row );
01914     tmpVerticalText = format()->verticalText( _col, _row );
01915     ay              = format()->alignY( _col, _row );
01916     fontUnderlined  = format()->textFontUnderline( _col, _row );
01917   }
01918 
01919   // Set d->textWidth and d->textHeight to correct values according to
01920   // if the text is horizontal, vertical or rotated.
01921   if ( !tmpVerticalText && !tmpAngle ) {
01922     // Horizontal text.
01923 
01924     d->textWidth = format()->sheet()->doc()->unzoomItX( fm.width( d->strOutText ) );
01925     int offsetFont = 0;
01926     if ( ( ay == Format::Bottom ) && fontUnderlined ) {
01927       offsetFont = fm.underlinePos() + 1;
01928     }
01929 
01930     d->textHeight = format()->sheet()->doc()->unzoomItY( fm.ascent() + fm.descent()
01931             + offsetFont );
01932   }
01933   else if ( tmpAngle!= 0 ) {
01934     // Rotated text.
01935 
01936     d->textHeight = format()->sheet()->doc()
01937       ->unzoomItY( int( cos( tmpAngle * M_PI / 180 )
01938       * ( fm.ascent() + fm.descent() )
01939       + abs( int( ( fm.width( d->strOutText )
01940               * sin( tmpAngle * M_PI / 180 ) ) ) ) ) );
01941 
01942     d->textWidth = format()->sheet()->doc()
01943       ->unzoomItX( int( abs( int( ( sin( tmpAngle * M_PI / 180 )
01944             * ( fm.ascent() + fm.descent() ) ) ) )
01945       + fm.width( d->strOutText )
01946         * cos ( tmpAngle * M_PI / 180 ) ) );
01947   }
01948   else {
01949     // Vertical text.
01950     int width = 0;
01951     for ( unsigned int i = 0; i < d->strOutText.length(); i++ )
01952       width = QMAX( width, fm.width( d->strOutText.at( i ) ) );
01953 
01954     d->textWidth  = format()->sheet()->doc()->unzoomItX( width );
01955     d->textHeight = format()->sheet()->doc()->unzoomItY( ( fm.ascent() + fm.descent() )
01956             * d->strOutText.length() );
01957   }
01958 }
01959 
01960 
01961 // Get the effective font to use after the zooming and apply it to `painter'.
01962 //
01963 // Used in makeLayout() and calculateTextParameters().
01964 //
01965 
01966 void Cell::applyZoomedFont( QPainter &painter, int _col, int _row )
01967 {
01968   QFont  tmpFont( format()->textFont( _col, _row ) );
01969 
01970   // If there is a matching condition on this cell then set the
01971   // according style parameters.
01972   if ( d->hasExtra()
01973        && d->extra()->conditions
01974        && d->extra()->conditions->matchedStyle() ) {
01975 
01976     Style * s = d->extra()->conditions->matchedStyle();
01977 
01978     // Other size?
01979     if ( s->hasFeature( Style::SFontSize, true ) )
01980       tmpFont.setPointSizeFloat( s->fontSize() );
01981 
01982     // Other attributes?
01983     if ( s->hasFeature( Style::SFontFlag, true ) ) {
01984       uint flags = s->fontFlags();
01985 
01986       tmpFont.setBold(      flags & (uint) Style::FBold );
01987       tmpFont.setUnderline( flags & (uint) Style::FUnderline );
01988       tmpFont.setItalic(    flags & (uint) Style::FItalic );
01989       tmpFont.setStrikeOut( flags & (uint) Style::FStrike );
01990     }
01991 
01992     // Other family?
01993     if ( s->hasFeature( Style::SFontFamily, true ) )
01994       tmpFont.setFamily( s->fontFamily() );
01995   }
01996 #if 0
01997   else
01998   /*
01999    * could somebody please explaint why we check for isProtected or isHideFormula here
02000    */
02001    if ( d->extra()->conditions
02002   && d->extra()->conditions->currentCondition( condition )
02003   && !(format()->sheet()->getShowFormula()
02004        && !( format()->sheet()->isProtected()
02005        && format()->isHideFormula( d->column, d->row ) ) ) )
02006    {
02007      if ( condition.fontcond )
02008        tmpFont = *(condition.fontcond);
02009      else
02010        tmpFont = condition.style->font();
02011    }
02012 #endif
02013 
02014   // Scale the font size according to the current zoom.
02015   tmpFont.setPointSizeFloat( 0.01 * format()->sheet()->doc()->zoom()
02016            * tmpFont.pointSizeFloat() );
02017 
02018   painter.setFont( tmpFont );
02019 }
02020 
02021 
02022 //used in Sheet::adjustColumnHelper and Sheet::adjustRow
02023 void Cell::calculateTextParameters( QPainter &_painter,
02024              int _col, int _row )
02025 {
02026   // Apply the correct font to _painter.
02027   applyZoomedFont( _painter, _col, _row );
02028 
02029   // Recalculate d->textWidth and d->textHeight
02030   textSize( _painter );
02031 
02032   // Recalculate d->textX and d->textY.
02033   offsetAlign( _col, _row );
02034 }
02035 
02036 
02037 // ----------------------------------------------------------------
02038 //                          Formula handling
02039 
02040 
02041 bool Cell::makeFormula()
02042 {
02043   clearFormula ();
02044 
02045   d->formula = new KSpread::Formula (sheet(), this);
02046   d->formula->setExpression (d->strText);
02047 
02048   if (!d->formula->isValid ()) {
02049   // Did a syntax error occur ?
02050     clearFormula();
02051 
02052     if (format()->sheet()->doc()->getShowMessageError())
02053     {
02054       QString tmp(i18n("Error in cell %1\n\n"));
02055       tmp = tmp.arg( fullName() );
02056       KMessageBox::error( (QWidget*)0L, tmp);
02057     }
02058     setFlag(Flag_ParseError);
02059     Value v;
02060     v.setError ( "####" );
02061     setValue (v);
02062     return false;
02063   }
02064 
02065   // we must recalc
02066   setCalcDirtyFlag ();
02067 
02068   return true;
02069 }
02070 
02071 void Cell::clearFormula()
02072 {
02073   delete d->formula;
02074   d->formula = 0L;
02075 }
02076 
02077 bool Cell::calc(bool delay)
02078 {
02079   if ( !isFormula() )
02080     return true;
02081 
02082   if (d->formula == 0)
02083   {
02084     if ( testFlag( Flag_ParseError ) )  // there was a parse error
02085       return false;
02086     else
02087     {
02088       /* we were probably at a "isLoading() = true" state when we originally
02089        * parsed
02090        */
02091       makeFormula ();
02092 
02093       if ( d->formula == 0 ) // there was a parse error
02094         return false;
02095     }
02096   }
02097 
02098   if ( !testFlag( Flag_CalcDirty ) )
02099     return true;
02100 
02101   if ( delay )
02102   {
02103     if ( format()->sheet()->doc()->delayCalculation() )
02104       return true;
02105   }
02106 
02107   setFlag(Flag_LayoutDirty);
02108   setFlag(Flag_TextFormatDirty);
02109   clearFlag(Flag_CalcDirty);
02110 
02111   Value result = d->formula->eval ();
02112   setValue (result);
02113   if (result.isNumber())
02114     checkNumberFormat(); // auto-chooses number or scientific
02115 
02116   clearFlag(Flag_CalcDirty);
02117   setFlag(Flag_LayoutDirty);
02118 
02119   return true;
02120 }
02121 
02122 
02123 // ================================================================
02124 //                            Painting
02125 
02126 
02127 // Paint the cell.  This is the main function that calls a lot of
02128 //                  helper functions.
02129 //
02130 // `rect'       is the rectangle that we should paint on.  If the cell
02131 //              does not overlap this rectangle, we can return immediately.
02132 // `coordinate' is the origin (the upper left) of the cell in document
02133 //              coordinates.
02134 //
02135 
02136 void Cell::paintCell( const KoRect   &rect, QPainter & painter,
02137           View    *view,
02138           const KoPoint  &coordinate,
02139           const QPoint   &cellRef,
02140           int paintBorder,
02141           QPen & rightPen, QPen & bottomPen,
02142           QPen & leftPen,  QPen & topPen,
02143           QValueList<QPoint> &mergedCellsPainted,
02144           bool drawCursor )
02145 {
02146   bool paintBorderRight  = paintBorder & Border_Right;
02147   bool paintBorderBottom = paintBorder & Border_Bottom;
02148   bool paintBorderLeft   = paintBorder & Border_Left;
02149   bool paintBorderTop    = paintBorder & Border_Top;
02150 
02151   // If we are already painting this cell, then return immediately.
02152   // This avoids infinite recursion.
02153   if ( testFlag( Flag_PaintingCell ) )
02154     return;
02155 
02156   // Indicate that we are painting this cell now.
02157   setFlag( Flag_PaintingCell );
02158 
02159   // This flag indicates that we are working on drawing the cells that
02160   // another cell is obscuring.  The value is the number of levels down we
02161   // are currently working -- i.e. a cell obscured by a cell which is
02162   // obscured by a cell.
02163   static int  paintingObscured = 0;
02164 
02165 #if 0
02166   if (paintingObscured == 0)
02167     kdDebug(36001) << "painting cell " << name() << endl;
02168   else
02169     kdDebug(36001) << "  painting obscured cell " << name() << endl;
02170 #endif
02171 
02172   // Sanity check: If we're working on drawing an obscured cell, that
02173   // means this cell should have a cell that obscures it.
02174   Q_ASSERT(!(paintingObscured > 0 && d->extra()->obscuringCells.isEmpty()));
02175 
02176   // The parameter cellref should be *this, unless this is the default cell.
02177   Q_ASSERT(isDefault()
02178      || (((cellRef.x() == d->column) && (cellRef.y() == d->row))));
02179 
02180   Sheet::LayoutDirection sheetDir =  format()->sheet()->layoutDirection();
02181 
02182   double left = coordinate.x();
02183 
02184   ColumnFormat * colFormat = format()->sheet()->columnFormat( cellRef.x() );
02185   RowFormat    * rowFormat = format()->sheet()->rowFormat( cellRef.y() );
02186 
02187   // Set width, height to the total width and height that this cell
02188   // covers, including obscured cells, and width0, height0 to the
02189   // width and height of this cell, maybe merged but never implicitly
02190   // extended.
02191   double  width0  = colFormat->dblWidth();
02192   double  height0 = rowFormat->dblHeight();
02193   double  width   = width0;
02194   double  height  = height0;
02195 
02196   // Handle right-to-left layout.
02197   // In an RTL sheet the cells have to be painted at their opposite horizontal
02198   // location on the canvas, meaning that column A will be the rightmost column
02199   // on screen, column B will be to the left of it and so on. Here we change
02200   // the horizontal coordinate at which we start painting the cell in case the
02201   // sheet's direction is RTL. We do this only if paintingObscured is 0,
02202   // otherwise the cell's painting location will flip back and forth in
02203   // consecutive calls to paintCell when painting obscured cells.
02204   if ( sheetDir == Sheet::RightToLeft && paintingObscured == 0
02205        && view && view->canvasWidget() )
02206   {
02207     double  dwidth = view->doc()->unzoomItX(view->canvasWidget()->width());
02208     left = dwidth - coordinate.x() - width;
02209   }
02210 
02211   // See if this cell is merged or has overflown into neighbor cells.
02212   // In that case, the width/height is greater than just the cell
02213   // itself.
02214   if (d->hasExtra()) {
02215     if (d->extra()->mergedXCells > 0 || d->extra()->mergedYCells > 0) {
02216       // merged cell extends to the left if sheet is RTL
02217       if ( sheetDir == Sheet::RightToLeft ) {
02218         left -= d->extra()->extraWidth - width;
02219       }
02220       width0  = d->extra()->extraWidth;
02221       height0 = d->extra()->extraHeight;
02222       width   = d->extra()->extraWidth;
02223       height  = d->extra()->extraHeight;
02224     }
02225     else {
02226 #if 0
02227       width  += d->extra()->extraXCells ? d->extra()->extraWidth  : 0;
02228       height += d->extra()->extraYCells ? d->extra()->extraHeight : 0;
02229 #else
02230       // FIXME: Make extraWidth/Height really contain the *extra* width/height.
02231       if ( d->extra()->extraXCells )
02232   width  = d->extra()->extraWidth;
02233       if ( d->extra()->extraYCells )
02234   height = d->extra()->extraHeight;
02235 #endif
02236     }
02237   }
02238 
02239   // Check if the cell is "selected", i.e. it should be drawn with the
02240   // color that indicates selection (dark blue).  If more than one
02241   // square is selected, the last one uses the ordinary colors.  In
02242   // that case, "selected" will be set to false even though the cell
02243   // itself really is selected.
02244   bool  selected = false;
02245   if ( view != NULL ) {
02246     selected = view->selectionInfo()->contains( cellRef );
02247 
02248     // But the cell doesn't look selected if this is the marker cell.
02249     Cell  *cell = format()->sheet()->cellAt( view->selectionInfo()->marker() );
02250     QPoint bottomRight( view->selectionInfo()->marker().x() + cell->extraXCells(),
02251        view->selectionInfo()->marker().y() + cell->extraYCells() );
02252     QRect markerArea( view->selectionInfo()->marker(), bottomRight );
02253     selected = selected && !( markerArea.contains( cellRef ) );
02254 
02255     // Don't draw any selection at all when printing.
02256     if ( painter.device()->isExtDev() || !drawCursor )
02257       selected = false;
02258   }
02259 
02260   // Need to make a new layout ?
02261   //
02262   // FIXME: We have already used (at least) extraWidth/Height above,
02263   //        and now we are recalculating the layout.  This has to be
02264   //        moved up above all uses.
02265   //
02266   // FIXME: This needs to be taken out eventually - it is done in
02267   //        canvas::paintUpdates().
02268   if ( testFlag( Flag_LayoutDirty ) )
02269     makeLayout( painter, cellRef.x(), cellRef.y() );
02270 
02271   // ----------------  Start the actual painting.  ----------------
02272 
02273   // If the rect of this cell doesn't intersect the rect that should
02274   // be painted, we can skip the rest and return. (Note that we need
02275   // to calculate `left' first before we can do this.)
02276   const KoRect  cellRect( left, coordinate.y(), width, height );
02277   const KoRect  cellRect0( left, coordinate.y(), width0, height0 );
02278   if ( !cellRect.intersects( rect ) ) {
02279     clearFlag( Flag_PaintingCell );
02280     return;
02281   }
02282 
02283   // Get the background color.
02284   //
02285   // If there is a condition giving the background color for this cell
02286   // (and it matches), use that one, otherwise get the standard
02287   // background.
02288   QColor backgroundColor;
02289   if ( d->hasExtra() && d->extra()->conditions
02290        && d->extra()->conditions->matchedStyle()
02291        && d->extra()->conditions->matchedStyle()->hasFeature( Style::SBackgroundColor, true ) )
02292     backgroundColor = d->extra()->conditions->matchedStyle()->bgColor();
02293   else
02294     backgroundColor = bgColor( cellRef.x(), cellRef.y() );
02295 
02296   // 1. Paint the background.
02297   if ( !isPartOfMerged() )
02298     paintBackground( painter, cellRect0, cellRef, selected, backgroundColor );
02299 
02300   // 2. Paint the default borders if we are on screen or if we are printing
02301   //    and the checkbox to do this is checked.
02302   if ( painter.device()->devType() != QInternal::Printer
02303        || format()->sheet()->print()->printGrid())
02304     paintDefaultBorders( painter, rect, cellRect, cellRef,
02305        paintBorderRight, paintBorderBottom,
02306                          paintBorderLeft,  paintBorderTop,
02307        rightPen, bottomPen, leftPen, topPen );
02308 
02309   // 3. Paint all the cells that this one obscures.  They may only be
02310   //    partially obscured.
02311   //
02312   // The `paintingObscured' variable is used to avoid infinite
02313   // recursion since cells sometimes paint their obscuring cell as
02314   // well.
02315   paintingObscured++;
02316 
02317   if (d->hasExtra() && (d->extra()->extraXCells > 0
02318       || d->extra()->extraYCells > 0)) {
02319     //kdDebug(36001) << "painting obscured cells for " << name() << endl;
02320 
02321     paintObscuredCells( rect, painter, view, cellRect, cellRef,
02322       paintBorderRight, paintBorderBottom,
02323       paintBorderLeft,  paintBorderTop,
02324       rightPen, bottomPen, leftPen, topPen,
02325       mergedCellsPainted);
02326 
02327     // FIXME: Is this the right place for this?
02328     if ( d->extra()->mergedXCells > 0 || d->extra()->mergedYCells > 0 )
02329       mergedCellsPainted.prepend( cellRef );
02330   }
02331   paintingObscured--;
02332 
02333   // 4. Paint the borders of the cell if no other cell is forcing this
02334   // one, i.e. this cell is not part of a merged cell.
02335   //
02336 
02337   // If we print pages, then we disable clipping, otherwise borders are
02338   // cut in the middle at the page borders.
02339   if ( painter.device()->isExtDev() )
02340     painter.setClipping( false );
02341 
02342   // Paint the borders if this cell is not part of another merged cell.
02343   if ( !isPartOfMerged() ) {
02344     // if (!testFlag(Flag_Highlight))
02345     paintCellBorders( painter, rect, cellRect0,
02346           cellRef,
02347           paintBorderRight, paintBorderBottom,
02348                       paintBorderLeft,  paintBorderTop,
02349           rightPen, bottomPen, leftPen, topPen );
02350   }
02351 
02352   // Turn clipping back on.
02353   if ( painter.device()->isExtDev() )
02354     painter.setClipping( true );
02355 
02356   // 5. Paint diagonal lines and page borders.
02357   paintCellDiagonalLines( painter, cellRect0, cellRef );
02358 
02359   paintPageBorders( painter, cellRect0, cellRef,
02360         paintBorderRight, paintBorderBottom );
02361 
02362 
02363   // 6. Now paint the content, if this cell isn't obscured.
02364   if ( !isObscured() ) {
02365 
02366     // 6a. Paint possible comment indicator.
02367     if ( !painter.device()->isExtDev()
02368    || format()->sheet()->print()->printCommentIndicator() )
02369       paintCommentIndicator( painter, cellRect, cellRef, backgroundColor );
02370 
02371     // 6b. Paint possible formula indicator.
02372     if ( !painter.device()->isExtDev()
02373    || format()->sheet()->print()->printFormulaIndicator() )
02374       paintFormulaIndicator( painter, cellRect, backgroundColor );
02375 
02376     // 6c. Paint possible indicator for clipped text.
02377     paintMoreTextIndicator( painter, cellRect, backgroundColor );
02378 
02379      //6c. Paint cell highlight
02380 #if 0
02381     if (highlightBorder != Border_None)
02382       paintCellHighlight ( painter, cellRect, cellRef, highlightBorder,
02383          rightHighlightPen, bottomHighlightPen,
02384          leftHighlightPen,  topHighlightPen );
02385 #endif
02386 
02387     // 6d. Paint the text in the cell unless:
02388     //  a) it is empty
02389     //  b) something indicates that the text should not be painted
02390     //  c) the sheet is protected and the cell is hidden.
02391     if ( !d->strOutText.isEmpty()
02392    && ( !painter.device()->isExtDev()
02393         || !format()->getDontprintText( cellRef.x(), cellRef.y() ) )
02394    && !( format()->sheet()->isProtected()
02395          && format()->isHideAll( cellRef.x(), cellRef.y() ) ) )
02396     {
02397       paintText( painter, cellRect, cellRef );
02398     }
02399   }
02400 
02401   // 7. If this cell is obscured and we are not already painting obscured
02402   //    cells, then paint the obscuring cell(s).  Otherwise don't do
02403   //    anything so that we don't cause an infinite loop.
02404   if ( isObscured() && paintingObscured == 0 &&
02405        !( sheetDir == Sheet::RightToLeft && painter.device()->isExtDev() ) )
02406   {
02407 
02408     //kdDebug(36001) << "painting cells that obscure " << name() << endl;
02409 
02410     // Store the obscuringCells list in a list of QPoint(column, row)
02411     // This avoids crashes during the iteration through
02412     // obscuringCells, when the cells may get non valid or the list
02413     // itself gets changed during a call of obscuringCell->paintCell
02414     // (this happens e.g. when there is an updateDepend)
02415     if (d->hasExtra()) {
02416       QValueList<QPoint>           listPoints;
02417       QValueList<Cell*>::iterator  it = d->extra()->obscuringCells.begin();
02418       QValueList<Cell*>::iterator  end = d->extra()->obscuringCells.end();
02419       for ( ; it != end; ++it ) {
02420         Cell *obscuringCell = *it;
02421 
02422         listPoints.append( QPoint( obscuringCell->column(), obscuringCell->row() ) );
02423       }
02424 
02425       QValueList<QPoint>::iterator  it1  = listPoints.begin();
02426       QValueList<QPoint>::iterator  end1 = listPoints.end();
02427       for ( ; it1 != end1; ++it1 ) {
02428         QPoint obscuringCellRef = *it1;
02429 
02430   // Only paint those obscuring cells that haven't been already
02431   // painted yet.
02432   //
02433   // This optimization removes an O(n^4) behaviour where n is
02434   // the number of cells on one edge in a merged cell.
02435   if ( mergedCellsPainted.contains( obscuringCellRef ) )
02436     continue;
02437 
02438         Cell *obscuringCell = format()->sheet()->cellAt( obscuringCellRef.x(),
02439                obscuringCellRef.y() );
02440 
02441         if ( obscuringCell != 0 ) {
02442           double x = format()->sheet()->dblColumnPos( obscuringCellRef.x() );
02443           double y = format()->sheet()->dblRowPos( obscuringCellRef.y() );
02444           if ( view != 0 ) {
02445             x -= view->canvasWidget()->xOffset();
02446             y -= view->canvasWidget()->yOffset();
02447           }
02448 
02449           KoPoint corner( x, y );
02450           painter.save();
02451 
02452     // Get the effective pens for the borders.  These are
02453     // determined by possible conditions on the cell with
02454     // associated styles.
02455           QPen rp( obscuringCell->effRightBorderPen( obscuringCellRef.x(),
02456                  obscuringCellRef.y() ) );
02457           QPen bp( obscuringCell->effBottomBorderPen( obscuringCellRef.x(),
02458                   obscuringCellRef.y() ) );
02459           QPen lp( obscuringCell->effLeftBorderPen( obscuringCellRef.x(),
02460                 obscuringCellRef.y() ) );
02461           QPen tp( obscuringCell->effTopBorderPen( obscuringCellRef.x(),
02462                obscuringCellRef.y() ) );
02463 
02464 
02465     //kdDebug(36001) << "  painting obscuring cell "
02466     //     << obscuringCell->name() << endl;
02467     // QPen highlightPen;
02468 
02469     //Note: Painting of highlight isn't quite right.  If several
02470     //      cells are merged, then the whole merged cell will be
02471     //      painted with the colour of the last cell referenced
02472     //      which is inside the merged range.
02473           obscuringCell->paintCell( rect, painter, view,
02474                                     corner, obscuringCellRef,
02475             Border_Left|Border_Top|Border_Right|Border_Bottom,
02476             rp, bp, lp, tp,
02477             mergedCellsPainted); // new pens
02478           painter.restore();
02479         }
02480       }
02481     }
02482   }
02483 
02484   // We are done with the painting, so remove the flag on the cell.
02485   clearFlag( Flag_PaintingCell );
02486 }
02487 
02488 
02489 
02490 // The following code was commented out in the above function.  I'll
02491 // leave it here in case this functionality is ever re-implemented and
02492 // someone wants some code to start from.
02493 //
02494 #if 0
02495 
02499   if ( d->style == Cell::ST_Button ) {
02500     QBrush fill( Qt::lightGray );
02501     QApplication::style().drawControl( QStyle::CE_PushButton, &_painter, this,
02502                QRect( _tx + 1, _ty + 1, w2 - 1, h2 - 1 ),
02503                defaultColorGroup ); //, selected, &fill );
02504   }
02505 
02509   else if ( d->style == Cell::ST_Select ) {
02510     QApplication::style().drawComboButton(  &_painter, _tx + 1, _ty + 1,
02511               w2 - 1, h2 - 1,
02512               defaultColorGroup, selected );
02513   }
02514 #endif
02515 
02516 
02517 #if 0
02518  void Cell::paintCellHighlight(QPainter& painter,
02519              const KoRect& cellRect,
02520              const QPoint& cellRef,
02521              const int highlightBorder,
02522              const QPen& rightPen,
02523              const QPen& bottomPen,
02524              const QPen& leftPen,
02525              const QPen& topPen
02526              )
02527 {
02528   //painter.drawLine(cellRect.left(),cellRect.top(),cellRect.right(),cellRect.bottom());
02529   //QPen pen(d->extra()->highlight);
02530   //painter.setPen(highlightPen);
02531 
02532   QBrush nullBrush;
02533   painter.setBrush(nullBrush);
02534 
02535   QRect zoomedCellRect = sheet()->doc()->zoomRect( cellRect );
02536 
02537   //The highlight rect is just inside the main cell rect
02538   //This saves the hassle of repainting nearby cells when the highlight is changed as the highlight areas
02539   //do not overlap
02540   zoomedCellRect.setLeft(zoomedCellRect.left()+1);
02541   //zoomedCellRect.setRight(zoomedCellRect.right()-1);
02542   zoomedCellRect.setTop(zoomedCellRect.top()+1);
02543   //zoomedCellRect.setBottom(zoomedCellRect.bottom()-1);
02544 
02545   if ( cellRef.x() != KS_colMax )
02546     zoomedCellRect.setWidth( zoomedCellRect.width() - 1 );
02547   if ( cellRef.y() != KS_rowMax )
02548   zoomedCellRect.setHeight( zoomedCellRect.height() - 1 );
02549 
02550   if (highlightBorder & Border_Top)
02551   {
02552     painter.setPen(topPen);
02553     painter.drawLine(zoomedCellRect.left(),zoomedCellRect.top(),zoomedCellRect.right(),zoomedCellRect.top());
02554   }
02555   if (highlightBorder & Border_Left)
02556   {
02557     painter.setPen(leftPen);
02558     painter.drawLine(zoomedCellRect.left(),zoomedCellRect.top(),zoomedCellRect.left(),zoomedCellRect.bottom());
02559   }
02560   if (highlightBorder & Border_Right)
02561   {
02562     painter.setPen(rightPen);
02563     painter.drawLine(zoomedCellRect.right(),zoomedCellRect.top(),zoomedCellRect.right(),zoomedCellRect.bottom());
02564   }
02565   if (highlightBorder & Border_Bottom)
02566   {
02567     painter.setPen(bottomPen);
02568     painter.drawLine(zoomedCellRect.left(),zoomedCellRect.bottom(),zoomedCellRect.right(),zoomedCellRect.bottom());
02569   }
02570 
02571   if (highlightBorder & Border_SizeGrip)
02572   {
02573     QBrush brush(rightPen.color());
02574     painter.setBrush(brush);
02575     painter.setPen(rightPen);
02576     painter.drawRect(zoomedCellRect.right()-3,zoomedCellRect.bottom()-3,4,4);
02577   }
02578 
02579   //painter.drawRect(zoomedCellRect.left(),zoomedCellRect.top(),zoomedCellRect.width(),zoomedCellRect.height());
02580 }
02581 #endif
02582 
02583 
02584 // Paint all the cells that this cell obscures (helper function to paintCell).
02585 //
02586 void Cell::paintObscuredCells(const KoRect& rect, QPainter& painter,
02587             View* view,
02588             const KoRect &cellRect,
02589             const QPoint &cellRef,
02590             bool paintBorderRight,
02591             bool _paintBorderBottom,
02592             bool paintBorderLeft,
02593             bool _paintBorderTop,
02594             QPen & rightPen, QPen & _bottomPen,
02595             QPen & leftPen,  QPen & _topPen,
02596             QValueList<QPoint> &mergedCellsPainted)
02597 {
02598   // If there are no obscured cells, return.
02599   if ( !extraXCells() && !extraYCells() )
02600     return;
02601 
02602   double  ypos = cellRect.y();
02603   int     maxY = extraYCells();
02604   int     maxX = extraXCells();
02605 
02606   // Loop through the rectangle of squares that we obscure and paint them.
02607   for ( int y = 0; y <= maxY; ++y ) {
02608     double xpos = cellRect.x();
02609     RowFormat* rl = format()->sheet()->rowFormat( cellRef.y() + y );
02610 
02611     for( int x = 0; x <= maxX; ++ x ) {
02612       ColumnFormat * cl = format()->sheet()->columnFormat( cellRef.x() + x );
02613       if ( y != 0 || x != 0 ) {
02614   uint  column = cellRef.x() + x;
02615   uint  row    = cellRef.y() + y;
02616 
02617   QPen  topPen;
02618   QPen  bottomPen;
02619   bool  paintBorderTop;
02620   bool  paintBorderBottom;
02621 
02622   Cell  *cell = format()->sheet()->cellAt( column, row );
02623   KoPoint       corner( xpos, ypos );
02624 
02625   // Check if the upper and lower borders should be painted, and
02626   // if so which pens we should use.  There used to be a nasty
02627   // bug here (#61452).
02628   // Check top pen.  Only check if this is not on the top row.
02629   topPen         = _topPen;
02630   paintBorderTop = _paintBorderTop;
02631   if ( row > 1 && !cell->isPartOfMerged() ) {
02632     Cell  *cellUp = format()->sheet()->cellAt( column, row - 1 );
02633 
02634     if ( cellUp->isDefault() )
02635       paintBorderTop = false;
02636     else {
02637       // If the cell towards the top is part of a merged cell, get
02638       // the pointer to the master cell.
02639       cellUp = cellUp->ultimateObscuringCell();
02640 
02641       topPen = cellUp->effBottomBorderPen( cellUp->column(),
02642              cellUp->row() );
02643 
02644 #if 0
02645       int  penWidth = QMAX(1, sheet()->doc()->zoomItY( topPen.width() ));
02646       topPen.setWidth( penWidth );
02647 #endif
02648     }
02649   }
02650 
02651   // FIXME: I thought we had to check bottom pen as well.
02652   //        However, it looks as if we don't need to.  It works anyway.
02653   bottomPen         = _bottomPen;
02654   paintBorderBottom = _paintBorderBottom;
02655 
02656   int  paintBorder = Border_None;
02657   if (paintBorderLeft)   paintBorder |= Cell::Border_Left;
02658   if (paintBorderRight)  paintBorder |= Cell::Border_Right;
02659   if (paintBorderTop)    paintBorder |= Cell::Border_Top;
02660   if (paintBorderBottom) paintBorder |= Cell::Border_Bottom;
02661 
02662   /*Cell::BorderSides highlightBorder = Border_None;
02663     QPen highlightPen;*/
02664 
02665 
02666   //kdDebug(36001) << "calling paintcell for obscured cell "
02667   //       << cell->name() << endl;
02668   cell->paintCell( rect, painter, view,
02669        corner,
02670        QPoint( cellRef.x() + x, cellRef.y() + y ),
02671        paintBorder,
02672        rightPen, bottomPen, leftPen, topPen,
02673        mergedCellsPainted);
02674       }
02675       xpos += cl->dblWidth();
02676     }
02677 
02678     ypos += rl->dblHeight();
02679   }
02680 }
02681 
02682 
02683 // Paint the background of this cell.
02684 //
02685 void Cell::paintBackground( QPainter& painter, const KoRect &cellRect,
02686           const QPoint &cellRef, bool selected,
02687           QColor &backgroundColor )
02688 {
02689   QColorGroup  defaultColorGroup = QApplication::palette().active();
02690   QRect        zoomedCellRect    = sheet()->doc()->zoomRect( cellRect );
02691 
02692   // If this is not the KS_rowMax and/or KS_colMax, then we reduce
02693   // width and/or height by one.  This is due to the fact that the
02694   // right/bottom most pixel is shared with the left/top most pixel of
02695   // the following cell.  Only in the case of KS_colMax/KS_rowMax we
02696   // need to draw even this pixel, as there isn't a following cell to
02697   // draw the background pixel.
02698    if ( cellRef.x() != KS_colMax )
02699      zoomedCellRect.setWidth( zoomedCellRect.width() - 1 );
02700    if ( cellRef.y() != KS_rowMax )
02701      zoomedCellRect.setHeight( zoomedCellRect.height() - 1 );
02702 
02703   // Determine the correct background color
02704   if ( selected )
02705   {
02706     //If the cell's background color is too bright, use the default highlight color
02707     //Otherwise use a lighter version of the cell's background color.
02708     QColor c;
02709 
02710     int averageColor = (backgroundColor.red() + backgroundColor.green() + backgroundColor.blue()) / 3;
02711 
02712     if (averageColor > 180)
02713     if (averageColor > 225)
02714             c = View::highlightColor();
02715         else
02716         c = backgroundColor.light( 115 ); //15% lighter
02717     else
02718        c = backgroundColor.light( 125 ); //25% lighter
02719 
02720     painter.setBackgroundColor( c );
02721   }
02722   else {
02723     QColor bg( backgroundColor );
02724 
02725     // Handle printers separately.
02726     if ( !painter.device()->isExtDev() ) {
02727       if ( bg.isValid() )
02728         painter.setBackgroundColor( bg );
02729       else
02730         painter.setBackgroundColor( defaultColorGroup.base() );
02731     }
02732     else {
02733       //bad hack but there is a qt bug
02734       //so I can print backgroundcolor
02735       QBrush bb( bg );
02736       if ( !bg.isValid() )
02737         bb.setColor( Qt::white );
02738 
02739       painter.fillRect( zoomedCellRect, bb );
02740       return;
02741     }
02742   }
02743 
02744   // Erase the background of the cell.
02745   if ( !painter.device()->isExtDev() )
02746     painter.eraseRect( zoomedCellRect );
02747 
02748   // Get a background brush
02749   QBrush bb;
02750   if ( d->hasExtra()
02751        && d->extra()->conditions
02752        && d->extra()->conditions->matchedStyle()
02753        && d->extra()->conditions->matchedStyle()->hasFeature( Style::SBackgroundBrush, true ) )
02754     bb = d->extra()->conditions->matchedStyle()->backGroundBrush();
02755   else
02756     bb = backGroundBrush( cellRef.x(), cellRef.y() );
02757 
02758   // Draw background pattern if necessary.
02759   if ( bb.style() != Qt::NoBrush )
02760     painter.fillRect( zoomedCellRect, bb );
02761 
02762   backgroundColor = painter.backgroundColor();
02763 }
02764 
02765 
02766 // Paint the standard light grey borders that are always visible.
02767 //
02768 void Cell::paintDefaultBorders( QPainter& painter, const KoRect &rect,
02769         const KoRect &cellRect,
02770         const QPoint &cellRef,
02771         bool paintBorderRight, bool /*paintBorderBottom*/,
02772         bool paintBorderLeft,  bool paintBorderTop,
02773         QPen const & rightPen, QPen const & /*bottomPen*/,
02774         QPen const & leftPen, QPen const & topPen )
02775 {
02776     /*
02777         *** Notes about optimisation ***
02778 
02779         This function was painting the top , left , right & bottom lines in almost all cells previously, contrary to what the comment
02780         below says should happen.  There doesn't appear to be a UI option to enable or disable showing of the grid when printing at the moment,
02781         so I have disabled drawing of right and bottom borders for all cells.
02782 
02783         I also couldn't work out under what conditions the variables dt / db would come out as anything other than 0 in the code
02784         for painting the various borders.  The effTopBorderPen / effBottomBorderPen calls were taking up a lot of time
02785         according some profiling I did.  If that code really is necessary, we need to find a more efficient way of getting the widths
02786         than grabbing the whole QPen object and asking it.
02787 
02788 
02789         --Robert Knight (robertknight@gmail.com)
02790     */
02791   Doc* doc = sheet()->doc();
02792 
02793   Sheet::LayoutDirection sheetDir =  format()->sheet()->layoutDirection();
02794   bool paintingToExternalDevice = painter.device()->isExtDev();
02795 
02796   // Each cell is responsible for drawing it's top and left portions
02797   // of the "default" grid. --Or not drawing it if it shouldn't be
02798   // there.  It's also responsible to paint the right and bottom, if
02799   // it is the last cell on a print out.
02800 
02801   bool paintTop;
02802   bool paintLeft;
02803   bool paintBottom=false;
02804   bool paintRight=false;
02805 
02806   paintLeft   = ( paintBorderLeft && leftPen.style() == Qt::NoPen
02807       && sheet()->getShowGrid() && sheetDir==Sheet::LeftToRight );
02808   paintRight  = ( paintBorderRight && rightPen.style() == Qt::NoPen
02809       && sheet()->getShowGrid() && sheetDir==Sheet::RightToLeft );
02810   paintTop    = ( paintBorderTop && topPen.style() == Qt::NoPen
02811       && sheet()->getShowGrid() );
02812 //  paintBottom = ( paintBorderBottom && sheet()->getShowGrid()
02813 //                  && bottomPen.style() == Qt::NoPen );
02814 
02815 
02816    //Set the single-pixel with pen for drawing the borders with.
02817    painter.setPen( QPen( sheet()->doc()->gridColor(), 1, Qt::SolidLine ) );
02818 
02819   // If there are extra cells, there might be more conditions.
02820   if (d->hasExtra()) {
02821     QValueList<Cell*>::const_iterator it  = d->extra()->obscuringCells.begin();
02822     QValueList<Cell*>::const_iterator end = d->extra()->obscuringCells.end();
02823     for ( ; it != end; ++it ) {
02824       Cell *cell = *it;
02825 
02826       paintTop  = paintTop && ( cell->row() == cellRef.y() );
02827       paintBottom = false;
02828 
02829       if ( sheetDir == Sheet::RightToLeft ) {
02830         paintRight = paintRight && ( cell->column() == cellRef.x() );
02831         paintLeft = false;
02832       }
02833       else {
02834         paintLeft = paintLeft && ( cell->column() == cellRef.x() );
02835         paintRight = false;
02836       }
02837     }
02838   }
02839 
02840   // The left border.
02841   if ( paintLeft ) {
02842     int dt = 0;
02843     int db = 0;
02844 
02845     #if 0
02846     if ( cellRef.x() > 1 ) {
02847       Cell  *cell_west = format()->sheet()->cellAt( cellRef.x() - 1,
02848                 cellRef.y() );
02849       QPen t = cell_west->effTopBorderPen( cellRef.x() - 1, cellRef.y() );
02850       QPen b = cell_west->effBottomBorderPen( cellRef.x() - 1, cellRef.y() );
02851 
02852       if ( t.style() != Qt::NoPen )
02853         dt = ( t.width() + 1 )/2;
02854       if ( b.style() != Qt::NoPen )
02855         db = ( t.width() / 2);
02856     }
02857     #endif
02858 
02859     // If we are on paper printout, we limit the length of the lines.
02860     // On paper, we always have full cells, on screen not.
02861     if ( paintingToExternalDevice ) {
02862       if ( sheetDir == Sheet::RightToLeft )
02863         painter.drawLine( doc->zoomItX( QMAX( rect.left(),   cellRect.right() ) ),
02864                           doc->zoomItY( QMAX( rect.top(),    cellRect.y() + dt ) ),
02865                           doc->zoomItX( QMIN( rect.right(),  cellRect.right() ) ),
02866                           doc->zoomItY( QMIN( rect.bottom(), cellRect.bottom() - db ) ) );
02867       else
02868         painter.drawLine( doc->zoomItX( QMAX( rect.left(),   cellRect.x() ) ),
02869                           doc->zoomItY( QMAX( rect.top(),    cellRect.y() + dt ) ),
02870                           doc->zoomItX( QMIN( rect.right(),  cellRect.x() ) ),
02871                           doc->zoomItY( QMIN( rect.bottom(), cellRect.bottom() - db ) ) );
02872     }
02873     else {
02874       if ( sheetDir == Sheet::RightToLeft )
02875         painter.drawLine( doc->zoomItX( cellRect.right() ),
02876                           doc->zoomItY( cellRect.y() + dt ),
02877                           doc->zoomItX( cellRect.right() ),
02878                           doc->zoomItY( cellRect.bottom() - db ) );
02879       else
02880         painter.drawLine( doc->zoomItX( cellRect.x() ),
02881                           doc->zoomItY( cellRect.y() + dt ),
02882                           doc->zoomItX( cellRect.x() ),
02883                           doc->zoomItY( cellRect.bottom() - db ) );
02884     }
02885   }
02886 
02887 
02888   // The top border.
02889   if ( paintTop ) {
02890     int dl = 0;
02891     int dr = 0;
02892 
02893     #if 0
02894     if ( cellRef.y() > 1 ) {
02895       Cell  *cell_north = format()->sheet()->cellAt( cellRef.x(),
02896                  cellRef.y() - 1 );
02897 
02898       QPen l = cell_north->effLeftBorderPen(  cellRef.x(), cellRef.y() - 1 );
02899       QPen r = cell_north->effRightBorderPen( cellRef.x(), cellRef.y() - 1 );
02900 
02901       if ( l.style() != Qt::NoPen )
02902         dl = ( l.width() - 1 ) / 2 + 1;
02903       if ( r.style() != Qt::NoPen )
02904         dr = r.width() / 2;
02905     }
02906     #endif
02907 
02908 
02909 
02910     // If we are on paper printout, we limit the length of the lines.
02911     // On paper, we always have full cells, on screen not.
02912     if ( paintingToExternalDevice ) {
02913       painter.drawLine( doc->zoomItX( QMAX( rect.left(),   cellRect.x() + dl ) ),
02914                         doc->zoomItY( QMAX( rect.top(),    cellRect.y() ) ),
02915                         doc->zoomItX( QMIN( rect.right(),  cellRect.right() - dr ) ),
02916                         doc->zoomItY( QMIN( rect.bottom(), cellRect.y() ) ) );
02917     }
02918     else {
02919       painter.drawLine( doc->zoomItX( cellRect.x() + dl ),
02920                         doc->zoomItY( cellRect.y() ),
02921                         doc->zoomItX( cellRect.right() - dr ),
02922                         doc->zoomItY( cellRect.y() ) );
02923     }
02924   }
02925 
02926 
02927   // The right border.
02928   if ( paintRight ) {
02929     int dt = 0;
02930     int db = 0;
02931 
02932     #if 0
02933     if ( cellRef.x() < KS_colMax ) {
02934       Cell  *cell_east = format()->sheet()->cellAt( cellRef.x() + 1,
02935                 cellRef.y() );
02936 
02937       QPen t = cell_east->effTopBorderPen(    cellRef.x() + 1, cellRef.y() );
02938       QPen b = cell_east->effBottomBorderPen( cellRef.x() + 1, cellRef.y() );
02939 
02940       if ( t.style() != Qt::NoPen )
02941         dt = ( t.width() + 1 ) / 2;
02942       if ( b.style() != Qt::NoPen )
02943         db = ( t.width() / 2);
02944     }
02945     #endif
02946 
02947     //painter.setPen( QPen( sheet()->doc()->gridColor(), 1, Qt::SolidLine ) );
02948 
02949     // If we are on paper printout, we limit the length of the lines.
02950     // On paper, we always have full cells, on screen not.
02951     if ( painter.device()->isExtDev() )     {
02952       if ( sheetDir == Sheet::RightToLeft )
02953         painter.drawLine( doc->zoomItX( QMAX( rect.left(),   cellRect.x() ) ),
02954                           doc->zoomItY( QMAX( rect.top(),    cellRect.y() + dt ) ),
02955                           doc->zoomItX( QMIN( rect.right(),  cellRect.x() ) ),
02956                           doc->zoomItY( QMIN( rect.bottom(), cellRect.bottom() - db ) ) );
02957       else
02958         painter.drawLine( doc->zoomItX( QMAX( rect.left(),   cellRect.right() ) ),
02959                           doc->zoomItY( QMAX( rect.top(),    cellRect.y() + dt ) ),
02960                           doc->zoomItX( QMIN( rect.right(),  cellRect.right() ) ),
02961                           doc->zoomItY( QMIN( rect.bottom(), cellRect.bottom() - db ) ) );
02962     }
02963     else {
02964       if ( sheetDir == Sheet::RightToLeft )
02965         painter.drawLine( doc->zoomItX( cellRect.x() ),
02966                           doc->zoomItY( cellRect.y() + dt ),
02967                           doc->zoomItX( cellRect.x() ),
02968                           doc->zoomItY( cellRect.bottom() - db ) );
02969       else
02970         painter.drawLine( doc->zoomItX( cellRect.right() ),
02971                           doc->zoomItY( cellRect.y() + dt ),
02972                           doc->zoomItX( cellRect.right() ),
02973                           doc->zoomItY( cellRect.bottom() - db ) );
02974     }
02975   }
02976 
02977   // The bottom border.
02978   /*if ( paintBottom ) {
02979     int dl = 0;
02980     int dr = 0;
02981     if ( cellRef.y() < KS_rowMax ) {
02982       Cell  *cell_south = format()->sheet()->cellAt( cellRef.x(),
02983                  cellRef.y() + 1 );
02984 
02985       QPen l = cell_south->effLeftBorderPen(  cellRef.x(), cellRef.y() + 1 );
02986       QPen r = cell_south->effRightBorderPen( cellRef.x(), cellRef.y() + 1 );
02987 
02988       if ( l.style() != Qt::NoPen )
02989         dl = ( l.width() - 1 ) / 2 + 1;
02990       if ( r.style() != Qt::NoPen )
02991         dr = r.width() / 2;
02992     }
02993 
02994     painter.setPen( QPen( sheet()->doc()->gridColor(), 1, Qt::SolidLine ) );
02995 
02996     // If we are on paper printout, we limit the length of the lines.
02997     // On paper, we always have full cells, on screen not.
02998     if ( painter.device()->isExtDev() ) {
02999       painter.drawLine( doc->zoomItX( QMAX( rect.left(),   cellRect.x() + dl ) ),
03000                         doc->zoomItY( QMAX( rect.top(),    cellRect.bottom() ) ),
03001                         doc->zoomItX( QMIN( rect.right(),  cellRect.right() - dr ) ),
03002                         doc->zoomItY( QMIN( rect.bottom(), cellRect.bottom() ) ) );
03003     }
03004     else {
03005       painter.drawLine( doc->zoomItX( cellRect.x() + dl ),
03006                         doc->zoomItY( cellRect.bottom() ),
03007                         doc->zoomItX( cellRect.right() - dr ),
03008                         doc->zoomItY( cellRect.bottom() ) );
03009     }
03010   }*/
03011 }
03012 
03013 
03014 // Paint a comment indicator if the cell has a comment.
03015 //
03016 void Cell::paintCommentIndicator( QPainter& painter,
03017           const KoRect &cellRect,
03018           const QPoint &/*cellRef*/,
03019           QColor &backgroundColor )
03020 {
03021   Doc * doc = sheet()->doc();
03022 
03023   // Point the little corner if there is a comment attached
03024   // to this cell.
03025   if ( ( format()->propertiesMask() & (uint) Format::PComment )
03026        && cellRect.width() > 10.0
03027        && cellRect.height() > 10.0
03028        && ( sheet()->print()->printCommentIndicator()
03029            || ( !painter.device()->isExtDev() && sheet()->getShowCommentIndicator() ) ) ) {
03030     QColor penColor = Qt::red;
03031 
03032     // If background has high red part, switch to blue.
03033     if ( qRed( backgroundColor.rgb() ) > 127 &&
03034          qGreen( backgroundColor.rgb() ) < 80 &&
03035          qBlue( backgroundColor.rgb() ) < 80 )
03036     {
03037         penColor = Qt::blue;
03038     }
03039 
03040     // Get the triangle.
03041     QPointArray  point( 3 );
03042     if ( format()->sheet()->layoutDirection()==Sheet::RightToLeft ) {
03043       point.setPoint( 0, doc->zoomItX( cellRect.x() + 6.0 ),
03044                          doc->zoomItY( cellRect.y() ) );
03045       point.setPoint( 1, doc->zoomItX( cellRect.x() ),
03046                          doc->zoomItY( cellRect.y() ) );
03047       point.setPoint( 2, doc->zoomItX( cellRect.x() ),
03048                          doc->zoomItY( cellRect.y() + 6.0 ) );
03049     }
03050     else {
03051       point.setPoint( 0, doc->zoomItX( cellRect.right() - 5.0 ),
03052                          doc->zoomItY( cellRect.y() ) );
03053       point.setPoint( 1, doc->zoomItX( cellRect.right() ),
03054                          doc->zoomItY( cellRect.y() ) );
03055       point.setPoint( 2, doc->zoomItX( cellRect.right() ),
03056                          doc->zoomItY( cellRect.y() + 5.0 ) );
03057     }
03058 
03059     // And draw it.
03060     painter.setBrush( QBrush( penColor ) );
03061     painter.setPen( Qt::NoPen );
03062     painter.drawPolygon( point );
03063   }
03064 }
03065 
03066 
03067 
03068 // Paint a small rectangle if this cell holds a formula.
03069 //
03070 void Cell::paintFormulaIndicator( QPainter& painter,
03071           const KoRect &cellRect,
03072           QColor &backgroundColor )
03073 {
03074   if ( isFormula() &&
03075       format()->sheet()->getShowFormulaIndicator() &&
03076       cellRect.width()  > 10.0 &&
03077       cellRect.height() > 10.0 )
03078   {
03079     Doc* doc = sheet()->doc();
03080 
03081     QColor penColor = Qt::blue;
03082     // If background has high blue part, switch to red.
03083     if ( qRed( backgroundColor.rgb() ) < 80 &&
03084         qGreen( backgroundColor.rgb() ) < 80 &&
03085         qBlue( backgroundColor.rgb() ) > 127 )
03086     {
03087         penColor = Qt::red;
03088     }
03089 
03090     // Get the triangle...
03091     QPointArray point( 3 );
03092     if ( format()->sheet()->layoutDirection()==Sheet::RightToLeft ) {
03093       point.setPoint( 0, doc->zoomItX( cellRect.right() - 6.0 ),
03094                          doc->zoomItY( cellRect.bottom() ) );
03095       point.setPoint( 1, doc->zoomItX( cellRect.right() ),
03096                          doc->zoomItY( cellRect.bottom() ) );
03097       point.setPoint( 2, doc->zoomItX( cellRect.right() ),
03098                          doc->zoomItY( cellRect.bottom() - 6.0 ) );
03099     }
03100     else {
03101       point.setPoint( 0, doc->zoomItX( cellRect.x() ),
03102                          doc->zoomItY( cellRect.bottom() - 6.0 ) );
03103       point.setPoint( 1, doc->zoomItX( cellRect.x() ),
03104                          doc->zoomItY( cellRect.bottom() ) );
03105       point.setPoint( 2, doc->zoomItX( cellRect.x() + 6.0 ),
03106                          doc->zoomItY( cellRect.bottom() ) );
03107     }
03108 
03109     // ...and draw it.
03110     painter.setBrush( QBrush( penColor ) );
03111     painter.setPen( Qt::NoPen );
03112     painter.drawPolygon( point );
03113   }
03114 }
03115 
03116 
03117 // Paint an indicator that the text in the cell is cut.
03118 //
03119 void Cell::paintMoreTextIndicator( QPainter& painter,
03120            const KoRect &cellRect,
03121            QColor &backgroundColor )
03122 {
03123   // Show a red triangle when it's not possible to write all text in cell.
03124   // Don't print the red triangle if we're printing.
03125   if( testFlag( Flag_CellTooShortX ) &&
03126       !painter.device()->isExtDev() &&
03127       cellRect.height() > 4.0  &&
03128       cellRect.width()  > 4.0 )
03129   {
03130     Doc* doc = sheet()->doc();
03131 
03132     QColor penColor = Qt::red;
03133     // If background has high red part, switch to blue.
03134     if ( qRed( backgroundColor.rgb() ) > 127
03135    && qGreen( backgroundColor.rgb() ) < 80
03136    && qBlue( backgroundColor.rgb() ) < 80 )
03137     {
03138       penColor = Qt::blue;
03139     }
03140 
03141     // Get the triangle...
03142     QPointArray point( 3 );
03143     if ( d->strOutText.isRightToLeft() ) {
03144       point.setPoint( 0, doc->zoomItX( cellRect.left() + 4.0 ),
03145                          doc->zoomItY( cellRect.y() + cellRect.height() / 2.0 -4.0 ) );
03146       point.setPoint( 1, doc->zoomItX( cellRect.left() ),
03147                          doc->zoomItY( cellRect.y() + cellRect.height() / 2.0 ));
03148       point.setPoint( 2, doc->zoomItX( cellRect.left() + 4.0 ),
03149                          doc->zoomItY( cellRect.y() + cellRect.height() / 2.0 +4.0 ) );
03150     }
03151     else {
03152       point.setPoint( 0, doc->zoomItX( cellRect.right() - 4.0 ),
03153                          doc->zoomItY( cellRect.y() + cellRect.height() / 2.0 - 4.0 ) );
03154       point.setPoint( 1, doc->zoomItX( cellRect.right() ),
03155                          doc->zoomItY( cellRect.y() + cellRect.height() / 2.0 ) );
03156       point.setPoint( 2, doc->zoomItX( cellRect.right() - 4.0 ),
03157                          doc->zoomItY( cellRect.y() + cellRect.height() / 2.0 + 4.0 ) );
03158     }
03159 
03160     // ...and paint it.
03161     painter.setBrush( QBrush( penColor ) );
03162     painter.setPen( Qt::NoPen );
03163     painter.drawPolygon( point );
03164   }
03165 }
03166 
03167 
03168 // Paint the real contents of a cell - the text.
03169 //
03170 void Cell::paintText( QPainter& painter,
03171           const KoRect &cellRect,
03172           const QPoint &cellRef )
03173 {
03174   Doc    *doc = sheet()->doc();
03175 
03176   ColumnFormat  *colFormat         = format()->sheet()->columnFormat( cellRef.x() );
03177 
03178   QColorGroup    defaultColorGroup = QApplication::palette().active();
03179   QColor         textColorPrint    = effTextColor( cellRef.x(), cellRef.y() );
03180 
03181   // Resolve the text color if invalid (=default).
03182   if ( !textColorPrint.isValid() ) {
03183     if ( painter.device()->isExtDev() )
03184       textColorPrint = Qt::black;
03185     else
03186       textColorPrint = QApplication::palette().active().text();
03187   }
03188 
03189   QPen tmpPen( textColorPrint );
03190 
03191   // Set the font according to the current zoom.
03192   applyZoomedFont( painter, cellRef.x(), cellRef.y() );
03193 
03194   // Check for red font color for negative values.
03195   if ( !d->hasExtra()
03196        || !d->extra()->conditions
03197        || !d->extra()->conditions->matchedStyle() ) {
03198     if ( value().isNumber()
03199          && !( format()->sheet()->getShowFormula()
03200                && !( format()->sheet()->isProtected()
03201          && format()->isHideFormula( d->column, d->row ) ) ) )
03202     {
03203       double v = value().asFloat();
03204       if ( format()->floatColor( cellRef.x(), cellRef.y()) == Format::NegRed
03205      && v < 0.0 )
03206         tmpPen.setColor( Qt::red );
03207     }
03208   }
03209 
03210   // Check for blue color, for hyperlink.
03211   if ( !link().isEmpty() ) {
03212     tmpPen.setColor( QApplication::palette().active().link() );
03213     QFont f = painter.font();
03214     f.setUnderline( true );
03215     painter.setFont( f );
03216   }
03217 
03218 #if 0
03219 /****
03220 
03221  For now I am commenting this out -- with the default color display you
03222  can read normal text through a highlighted background.  Maybe this isn't
03223  always the case, though, and we can put the highlighted text color back in.
03224  In that case, we need to somewhere in here figure out if the text overlaps
03225  another cell outside of the selection, otherwise that portion of the text
03226  will be printed white on white.  So just that portion would need to be
03227  painted again in the normal color.
03228 
03229  This should probably be done eventually, anyway, because I like using the
03230  reverse text color for highlighted cells.  I just don't like extending the
03231  cell 'highlight' background outside of the selection rectangle because it
03232  looks REALLY ugly.
03233 */
03234 
03235   if ( selected && ( cellRef.x() != marker.x() || cellRef.y() != marker.y() ) )
03236   {
03237     QPen p( tmpPen );
03238     p.setColor( defaultColorGroup.highlightedText() );
03239     painter.setPen( p );
03240   }
03241   else {
03242     painter.setPen(tmpPen);
03243   }
03244 #endif
03245   painter.setPen( tmpPen );
03246 
03247   QString  tmpText   = d->strOutText;
03248   double   tmpHeight = d->textHeight;
03249   double   tmpWidth  = d->textWidth;
03250 
03251   // If the cell is to narrow to paint the whole contents, then pick
03252   // out a part of the content that we paint.  The result of this is
03253   // dependent on the data type of the content.
03254   //
03255   // FIXME: Make this dependent on the height as well.
03256   //
03257   if ( testFlag( Flag_CellTooShortX ) ) {
03258     d->strOutText = textDisplaying( painter );
03259 
03260     // Recalculate the text width and the offset.
03261     textSize( painter );
03262     offsetAlign( column(), row() );
03263   }
03264 
03265   // Hide zero.
03266   if ( format()->sheet()->getHideZero()
03267        && value().isNumber()
03268        && value().asFloat() == 0 ) {
03269     d->strOutText = QString::null;
03270   }
03271 
03272   // Clear extra cell if column or row is hidden
03273   //
03274   // FIXME: I think this should be done before the call to
03275   // textDisplaying() above.
03276   //
03277   if ( colFormat->isHide() || ( cellRect.height() <= 2 ) ) {
03278     freeAllObscuredCells();  /* TODO: This looks dangerous...must check when I
03279                                 have time */
03280     d->strOutText = "";
03281   }
03282 
03283   double indent = 0.0;
03284   double offsetCellTooShort = 0.0;
03285   int a = effAlignX();
03286 
03287   // Apply indent if text is align to left not when text is at right or middle.
03288   if (  a == Format::Left && !isEmpty() ) {
03289     // FIXME: The following condition should be remade into a call to
03290     //        a new convenience function:
03291     //   if ( hasConditionStyleFeature( Style::SIndent, true )...
03292     //        This should be done throughout the entire file.
03293     //
03294     if ( d->hasExtra()
03295    && d->extra()->conditions
03296          && d->extra()->conditions->matchedStyle()
03297          && d->extra()->conditions->matchedStyle()->hasFeature( Style::SIndent, true ) )
03298       indent = d->extra()->conditions->matchedStyle()->indent();
03299     else
03300       indent = format()->getIndent( column(), row() );
03301   }
03302 
03303   // Made an offset, otherwise ### is under red triangle.
03304   if ( a == Format::Right && !isEmpty() && testFlag( Flag_CellTooShortX ) )
03305     offsetCellTooShort = format()->sheet()->doc()->unzoomItX( 4 );
03306 
03307   QFontMetrics fm2 = painter.fontMetrics();
03308   double offsetFont = 0.0;
03309 
03310   if ( format()->alignY( column(), row() ) == Format::Bottom
03311        && format()->textFontUnderline( column(), row() ) )
03312     offsetFont = format()->sheet()->doc()->unzoomItX( fm2.underlinePos() + 1 );
03313 
03314   int  tmpAngle;
03315   bool tmpMultiRow;
03316   bool tmpVerticalText;
03317 
03318   // Check for angled or vertical text.
03319   if ( d->hasExtra()
03320        && d->extra()->conditions
03321        && d->extra()->conditions->matchedStyle() )
03322   {
03323     Style  *matchedStyle = d->extra()->conditions->matchedStyle();
03324 
03325     if ( matchedStyle->hasFeature( Style::SAngle, true ) )
03326       tmpAngle = d->extra()->conditions->matchedStyle()->rotateAngle();
03327     else
03328       tmpAngle = format()->getAngle( cellRef.x(), cellRef.y() );
03329 
03330     if ( matchedStyle->hasFeature( Style::SVerticalText, true ) )
03331       tmpVerticalText = matchedStyle->hasProperty( Style::PVerticalText );
03332     else
03333       tmpVerticalText = format()->verticalText( cellRef.x(), cellRef.y() );
03334 
03335     if ( matchedStyle->hasFeature( Style::SMultiRow, true ) )
03336       tmpMultiRow = matchedStyle->hasProperty( Style::PMultiRow );
03337     else
03338       tmpMultiRow = format()->multiRow( cellRef.x(), cellRef.y() );
03339   }
03340   else {
03341     tmpAngle        = format()->getAngle( cellRef.x(), cellRef.y() );
03342     tmpVerticalText = format()->verticalText( cellRef.x(), cellRef.y() );
03343     tmpMultiRow     = format()->multiRow( cellRef.x(), cellRef.y() );
03344   }
03345 
03346   // Actually paint the text.
03347   //    There are 4 possible cases:
03348   //        - One line of text , horizontal
03349   //        - Angled text
03350   //        - Multiple rows of text , horizontal
03351   //        - Vertical text
03352   if ( !tmpMultiRow && !tmpVerticalText && !tmpAngle ) {
03353     // Case 1: The simple case, one line, no angle.
03354 
03355     painter.drawText( doc->zoomItX( indent + cellRect.x() + d->textX - offsetCellTooShort ),
03356                       doc->zoomItY( cellRect.y() + d->textY - offsetFont ), d->strOutText );
03357   }
03358   else if ( tmpAngle != 0 ) {
03359     // Case 2: an angle.
03360 
03361     int angle = tmpAngle;
03362     QFontMetrics fm = painter.fontMetrics();
03363 
03364     painter.rotate( angle );
03365     double x;
03366     if ( angle > 0 )
03367       x = indent + cellRect.x() + d->textX;
03368     else
03369       x = indent + cellRect.x() + d->textX
03370         - doc->unzoomItX((int) (( fm.descent() + fm.ascent() ) * sin( angle * M_PI / 180 )));
03371     double y;
03372     if ( angle > 0 )
03373       y = cellRect.y() + d->textY;
03374     else
03375       y = cellRect.y() + d->textY + d->textHeight;
03376     painter.drawText( doc->zoomItX( x * cos( angle * M_PI / 180 ) +
03377                                     y * sin( angle * M_PI / 180 ) ),
03378                       doc->zoomItY( -x * sin( angle * M_PI / 180 ) +
03379                                      y * cos( angle * M_PI / 180 ) ),
03380                       d->strOutText );
03381     painter.rotate( -angle );
03382   }
03383   else if ( tmpMultiRow && !tmpVerticalText ) {
03384     // Case 3: Multiple rows, but horizontal.
03385 
03386     QString t;
03387     int i;
03388     int pos = 0;
03389     double dy = 0.0;
03390     QFontMetrics fm = painter.fontMetrics();
03391     do {
03392       i = d->strOutText.find( "\n", pos );
03393       if ( i == -1 )
03394         t = d->strOutText.mid( pos, d->strOutText.length() - pos );
03395       else {
03396         t = d->strOutText.mid( pos, i - pos );
03397         pos = i + 1;
03398       }
03399 
03400       int align = effAlignX();
03401       if ( format()->sheet()->getShowFormula()
03402      && !( format()->sheet()->isProtected()
03403      && format()->isHideFormula( d->column, d->row ) ) )
03404         align = Format::Left;
03405 
03406       // #### Torben: This looks duplicated for me
03407       switch ( align ) {
03408        case Format::Left:
03409         d->textX = effLeftBorderPen( cellRef.x(), cellRef.y() ).width() + BORDER_SPACE;
03410         break;
03411 
03412        case Format::Right:
03413         d->textX = cellRect.width() - BORDER_SPACE - doc->unzoomItX( fm.width( t ) )
03414           - effRightBorderPen( cellRef.x(), cellRef.y() ).width();
03415         break;
03416 
03417        case Format::Center:
03418         d->textX = ( cellRect.width() - doc->unzoomItX( fm.width( t ) ) ) / 2;
03419       }
03420 
03421       painter.drawText( doc->zoomItX( indent + cellRect.x() + d->textX ),
03422                         doc->zoomItY( cellRect.y() + d->textY + dy ), t );
03423       dy += doc->unzoomItY( fm.descent() + fm.ascent() );
03424     } while ( i != -1 );
03425   }
03426   else if ( tmpVerticalText && !d->strOutText.isEmpty() ) {
03427     // Case 4: Vertical text.
03428 
03429     QString t;
03430     int i = 0;
03431     int len = 0;
03432     double dy = 0.0;
03433     QFontMetrics fm = painter.fontMetrics();
03434     do {
03435       len = d->strOutText.length();
03436       t = d->strOutText.at( i );
03437       painter.drawText( doc->zoomItX( indent + cellRect.x() + d->textX ),
03438                         doc->zoomItY( cellRect.y() + d->textY + dy ), t );
03439       dy += doc->unzoomItY( fm.descent() + fm.ascent() );
03440       i++;
03441     } while ( i != len );
03442   }
03443 
03444   // Check for too short cell and set the outText for future reference.
03445   if ( testFlag( Flag_CellTooShortX ) ) {
03446     d->strOutText = tmpText;
03447     d->textHeight = tmpHeight;
03448     d->textWidth  = tmpWidth;
03449   }
03450 
03451   if ( format()->sheet()->getHideZero() && value().isNumber()
03452        && value().asFloat() == 0 )
03453     d->strOutText = tmpText;
03454 
03455   if ( colFormat->isHide() || ( cellRect.height() <= 2 ) )
03456     d->strOutText = tmpText;
03457 }
03458 
03459 
03460 // Paint page borders on the page.  Only do this on the screen.
03461 //
03462 void Cell::paintPageBorders( QPainter& painter,
03463            const KoRect &cellRect,
03464            const QPoint &cellRef,
03465            bool paintBorderRight,
03466            bool paintBorderBottom )
03467 {
03468   // Not screen?  Return immediately.
03469   if ( painter.device()->isExtDev() )
03470     return;
03471 
03472   if ( ! format()->sheet()->isShowPageBorders() )
03473     return;
03474 
03475   SheetPrint* print = format()->sheet()->print();
03476 
03477   Sheet::LayoutDirection sheetDir =  format()->sheet()->layoutDirection();
03478 
03479   Doc* doc = sheet()->doc();
03480   int zcellRect_left = doc->zoomItX (cellRect.left());
03481   int zcellRect_right = doc->zoomItX (cellRect.right());
03482   int zcellRect_top = doc->zoomItY (cellRect.top());
03483   int zcellRect_bottom = doc->zoomItY (cellRect.bottom());
03484 
03485   // Draw page borders
03486 
03487   if ( cellRef.x() >= print->printRange().left()
03488        && cellRef.x() <= print->printRange().right() + 1
03489        && cellRef.y() >= print->printRange().top()
03490        && cellRef.y() <= print->printRange().bottom() + 1 )
03491   {
03492     if ( print->isOnNewPageX( cellRef.x() )
03493    && cellRef.y() <= print->printRange().bottom() )
03494     {
03495       painter.setPen( sheet()->doc()->pageBorderColor() );
03496 
03497       if ( sheetDir == Sheet::RightToLeft )
03498         painter.drawLine( zcellRect_right, zcellRect_top,
03499                           zcellRect_right, zcellRect_bottom );
03500       else
03501         painter.drawLine( zcellRect_left, zcellRect_top,
03502                           zcellRect_left, zcellRect_bottom );
03503     }
03504 
03505     if ( print->isOnNewPageY( cellRef.y() ) &&
03506           ( cellRef.x() <= print->printRange().right() ) )
03507     {
03508       painter.setPen( sheet()->doc()->pageBorderColor() );
03509       painter.drawLine( zcellRect_left,  zcellRect_top,
03510                         zcellRect_right, zcellRect_top );
03511     }
03512 
03513     if ( paintBorderRight ) {
03514       if ( print->isOnNewPageX( cellRef.x() + 1 )
03515             && cellRef.y() <= print->printRange().bottom() ) {
03516         painter.setPen( sheet()->doc()->pageBorderColor() );
03517 
03518         if ( sheetDir == Sheet::RightToLeft )
03519           painter.drawLine( zcellRect_left, zcellRect_top,
03520                             zcellRect_left, zcellRect_bottom );
03521         else
03522           painter.drawLine( zcellRect_right, zcellRect_top,
03523                             zcellRect_right, zcellRect_bottom );
03524       }
03525     }
03526 
03527     if ( paintBorderBottom ) {
03528       if ( print->isOnNewPageY( cellRef.y() + 1 )
03529           && cellRef.x() <= print->printRange().right() ) {
03530         painter.setPen( sheet()->doc()->pageBorderColor() );
03531         painter.drawLine( zcellRect_left,  zcellRect_bottom,
03532                           zcellRect_right, zcellRect_bottom );
03533       }
03534     }
03535   }
03536 }
03537 
03538 
03539 // Paint the cell borders.
03540 //
03541 void Cell::paintCellBorders( QPainter& painter, const KoRect& rect,
03542            const KoRect &cellRect,
03543            const QPoint &cellRef,
03544            bool paintRight, bool paintBottom,
03545            bool paintLeft,  bool paintTop,
03546            QPen & _rightPen, QPen & _bottomPen,
03547            QPen & _leftPen,  QPen & _topPen )
03548 {
03549 
03550   //Sanity check: If we are not painting any of the borders then the function
03551   //really shouldn't be called at all.
03552   if ( (!paintLeft) && (!paintRight) && (!paintTop) && (!paintBottom) )
03553         return;
03554 
03555   Doc * doc = sheet()->doc();
03556 
03557   Sheet::LayoutDirection sheetDir =  format()->sheet()->layoutDirection();
03558 
03559   // compute zoomed rectangles
03560   // I don't use KoRect, because that ends up producing lots of warnings
03561   // about double->int conversions in calls to painter.drawLine
03562   int zrect_left (doc->zoomItX (rect.left()));
03563   int zrect_right (doc->zoomItX (rect.right()));
03564   int zrect_top (doc->zoomItY (rect.top()));
03565   int zrect_bottom (doc->zoomItY (rect.bottom()));
03566   int zcellRect_left (doc->zoomItX (cellRect.left()));
03567   int zcellRect_right (doc->zoomItX (cellRect.right()));
03568   int zcellRect_top (doc->zoomItY (cellRect.top()));
03569   int zcellRect_bottom (doc->zoomItY (cellRect.bottom()));
03570 
03571   /* we might not paint some borders if this cell is merged with another in
03572      that direction
03573   bool paintLeft   = paintBorderLeft;
03574   bool paintRight  = paintBorderRight;
03575   bool paintTop    = paintBorderTop;
03576   bool paintBottom = paintBorderBottom;
03577   */
03578 
03579   // paintRight  = paintRight  && ( extraXCells() == 0 );
03580   // paintBottom = paintBottom && ( d->extra()->extraYCells() == 0 );
03581 
03582   if (d->hasExtra()) {
03583     QValueList<Cell*>::const_iterator it  = d->extra()->obscuringCells.begin();
03584     QValueList<Cell*>::const_iterator end = d->extra()->obscuringCells.end();
03585     for ( ; it != end; ++it ) {
03586       Cell* cell = *it;
03587 
03588       int xDiff = cellRef.x() - cell->column();
03589       int yDiff = cellRef.y() - cell->row();
03590       paintLeft = paintLeft && xDiff == 0;
03591       paintTop  = paintTop  && yDiff == 0;
03592 
03593       // Paint the border(s) if either this one should or if we have a
03594       // merged cell with this cell as its border.
03595       paintRight  = paintRight  && cell->mergedXCells() == xDiff;
03596       paintBottom = paintBottom && cell->mergedYCells() == yDiff;
03597     }
03598   }
03599 
03600   // Must create copies of these since otherwise the zoomIt()
03601   // operation will be performed on them repeatedly.
03602   QPen  leftPen( _leftPen );
03603   QPen  rightPen( _rightPen );
03604   QPen  topPen( _topPen );
03605   QPen  bottomPen( _bottomPen );
03606 
03607   // Determine the pens that should be used for drawing
03608   // the borders.
03609   //
03610   int left_penWidth   = QMAX( 1, doc->zoomItX( leftPen.width() ) );
03611   int right_penWidth  = QMAX( 1, doc->zoomItX( rightPen.width() ) );
03612   int top_penWidth    = QMAX( 1, doc->zoomItY( topPen.width() ) );
03613   int bottom_penWidth = QMAX( 1, doc->zoomItY( bottomPen.width() ) );
03614 
03615   leftPen.setWidth( left_penWidth );
03616   rightPen.setWidth( right_penWidth );
03617   topPen.setWidth( top_penWidth );
03618   bottomPen.setWidth( bottom_penWidth );
03619 
03620   if ( paintLeft && leftPen.style() != Qt::NoPen ) {
03621     int top = ( QMAX( 0, -1 + top_penWidth ) ) / 2 +
03622               ( ( QMAX( 0, -1 + top_penWidth ) ) % 2 );
03623     int bottom = ( QMAX( 0, -1 + bottom_penWidth ) ) / 2 + 1;
03624 
03625     painter.setPen( leftPen );
03626 
03627     //kdDebug(36001) << "    painting left border of cell " << name() << endl;
03628 
03629     // If we are on paper printout, we limit the length of the lines.
03630     // On paper, we always have full cells, on screen not.
03631     if ( painter.device()->isExtDev() ) {
03632       // FIXME: There is probably Cut&Paste bugs here as well as below.
03633       //        The QMIN/QMAX and left/right pairs don't really make sense.
03634       //
03635       //    UPDATE: In fact, most of these QMIN/QMAX combinations
03636       //            are TOTALLY BOGUS.  For one thing, the idea
03637       //            that we always have full cells on paper is wrong
03638       //            since we can have embedded sheets in e.g. kword,
03639       //            and those can be arbitrarily clipped.  WE HAVE TO
03640       //            REVISE THIS WHOLE BORDER PAINTING SECTION!
03641       //
03642       if ( sheetDir == Sheet::RightToLeft )
03643         painter.drawLine( QMIN( zrect_right,  zcellRect_right ),
03644                           QMAX( zrect_top,    zcellRect_top - top ),
03645                           QMIN( zrect_right,  zcellRect_right ),
03646                           QMIN( zrect_bottom, zcellRect_bottom + bottom ) );
03647       else
03648         painter.drawLine( QMAX( zrect_left,   zcellRect_left ),
03649                           QMAX( zrect_top,    zcellRect_top - top ),
03650                           QMAX( zrect_left,   zcellRect_left ),
03651                           QMIN( zrect_bottom, zcellRect_bottom + bottom ) );
03652     }
03653     else {
03654       if ( sheetDir == Sheet::RightToLeft )
03655         painter.drawLine( zcellRect_right,
03656                           zcellRect_top - top,
03657                           zcellRect_right,
03658                           zcellRect_bottom + bottom );
03659       else
03660         painter.drawLine( zcellRect_left,
03661                           zcellRect_top - top,
03662                           zcellRect_left,
03663                           zcellRect_bottom + bottom );
03664     }
03665   }
03666 
03667   if ( paintRight && rightPen.style() != Qt::NoPen ) {
03668     int top = ( QMAX( 0, -1 + top_penWidth ) ) / 2 +
03669               ( ( QMAX( 0, -1 + top_penWidth ) ) % 2 );
03670     int bottom = ( QMAX( 0, -1 + bottom_penWidth ) ) / 2 + 1;
03671 
03672     painter.setPen( rightPen );
03673 
03674     //kdDebug(36001) << "    painting right border of cell " << name() << endl;
03675 
03676     // If we are on paper printout, we limit the length of the lines.
03677     // On paper, we always have full cells, on screen not.
03678     if ( painter.device()->isExtDev() ) {
03679       if ( sheetDir == Sheet::RightToLeft )
03680         painter.drawLine( QMAX( zrect_left, zcellRect_left ),
03681                           QMAX( zrect_top, zcellRect_top - top ),
03682                           QMAX( zrect_left, zcellRect_left ),
03683                           QMIN( zrect_bottom, zcellRect_bottom + bottom ) );
03684       else {
03685   // FIXME: This is the way all these things should look.
03686   //        Make it so.
03687   //
03688   // Only print the right border if it is visible.
03689   if ( zcellRect_right <= zrect_right + right_penWidth / 2)
03690     painter.drawLine( zcellRect_right,
03691           QMAX( zrect_top, zcellRect_top - top ),
03692           zcellRect_right,
03693           QMIN( zrect_bottom, zcellRect_bottom + bottom ) );
03694       }
03695     }
03696     else {
03697       if ( sheetDir == Sheet::RightToLeft )
03698         painter.drawLine( zcellRect_left,
03699                           zcellRect_top - top,
03700                           zcellRect_left,
03701                           zcellRect_bottom + bottom );
03702       else
03703         painter.drawLine( zcellRect_right,
03704                           zcellRect_top - top,
03705                           zcellRect_right,
03706                           zcellRect_bottom + bottom );
03707     }
03708   }
03709 
03710   if ( paintTop && topPen.style() != Qt::NoPen ) {
03711     painter.setPen( topPen );
03712 
03713     //kdDebug(36001) << "    painting top border of cell " << name()
03714     //       << " [" << zcellRect_left << "," << zcellRect_right
03715     //       << ": " << zcellRect_right - zcellRect_left << "]" << endl;
03716 
03717     // If we are on paper printout, we limit the length of the lines.
03718     // On paper, we always have full cells, on screen not.
03719     if ( painter.device()->isExtDev() ) {
03720       if ( zcellRect_top >= zrect_top + top_penWidth / 2)
03721   painter.drawLine( QMAX( zrect_left,   zcellRect_left ),
03722         zcellRect_top,
03723         QMIN( zrect_right,  zcellRect_right ),
03724         zcellRect_top );
03725     }
03726     else {
03727       painter.drawLine( zcellRect_left, zcellRect_top,
03728                         zcellRect_right, zcellRect_top );
03729     }
03730   }
03731 
03732   if ( paintBottom && bottomPen.style() != Qt::NoPen ) {
03733     painter.setPen( bottomPen );
03734 
03735     //kdDebug(36001) << "    painting bottom border of cell " << name()
03736     //       << " [" << zcellRect_left << "," << zcellRect_right
03737     //       << ": " << zcellRect_right - zcellRect_left << "]" << endl;
03738 
03739     // If we are on paper printout, we limit the length of the lines.
03740     // On paper, we always have full cells, on screen not.
03741     if ( painter.device()->isExtDev() ) {
03742       if ( zcellRect_bottom <= zrect_bottom + bottom_penWidth / 2)
03743   painter.drawLine( QMAX( zrect_left,   zcellRect_left ),
03744         zcellRect_bottom,
03745         QMIN( zrect_right,  zcellRect_right ),
03746         zcellRect_bottom );
03747     }
03748     else {
03749       painter.drawLine( zcellRect_left, zcellRect_bottom,
03750                         zcellRect_right, zcellRect_bottom );
03751     }
03752   }
03753 
03754   // FIXME: Look very closely at when the following code is really needed.
03755   //        I can't really see any case, but I might be wrong.
03756   //        Since the code below is buggy, and incredibly complex,
03757   //        I am currently disabling it.  If somebody wants to enable
03758   //        it again, then please also solve bug 68977: "Embedded KSpread
03759   //        document printing problem" at the same time.
03760   return;
03761 
03762 #if 0
03763   // Look at the cells on our corners. It may happen that we
03764   // just erased parts of their borders corner, so we might need
03765   // to repaint these corners.
03766   //
03767   QPen  vert_pen, horz_pen;
03768   int   vert_penWidth, horz_penWidth;
03769 
03770   // Some useful referenses.
03771   Cell  *cell_north     = format()->sheet()->cellAt( cellRef.x(),
03772                  cellRef.y() - 1 );
03773   Cell  *cell_northwest = format()->sheet()->cellAt( cellRef.x() - 1,
03774                  cellRef.y() - 1 );
03775   Cell  *cell_west      = format()->sheet()->cellAt( cellRef.x() - 1,
03776                  cellRef.y() );
03777   Cell  *cell_northeast = format()->sheet()->cellAt( cellRef.x() + 1,
03778                  cellRef.y() - 1 );
03779   Cell  *cell_east      = format()->sheet()->cellAt( cellRef.x() + 1,
03780                  cellRef.y() );
03781   Cell  *cell_south     = format()->sheet()->cellAt( cellRef.x(),
03782                  cellRef.y() + 1 );
03783   Cell  *cell_southwest = format()->sheet()->cellAt( cellRef.x() - 1,
03784                  cellRef.y() + 1 );
03785   Cell  *cell_southeast = format()->sheet()->cellAt( cellRef.x() + 1,
03786                  cellRef.y() + 1 );
03787 
03788   // Fix the borders which meet at the top left corner
03789   if ( cell_north->effLeftBorderValue( cellRef.x(), cellRef.y() - 1 )
03790        >= cell_northwest->effRightBorderValue( cellRef.x() - 1, cellRef.y() - 1 ) )
03791     vert_pen = cell_north->effLeftBorderPen( cellRef.x(), cellRef.y() - 1 );
03792   else
03793     vert_pen = cell_northwest->effRightBorderPen( cellRef.x() - 1,
03794               cellRef.y() - 1 );
03795 
03796   vert_penWidth = QMAX( 1, doc->zoomItX( vert_pen.width() ) );
03797   vert_pen.setWidth( vert_penWidth );
03798 
03799   if ( vert_pen.style() != Qt::NoPen ) {
03800     if ( cell_west->effTopBorderValue( cellRef.x() - 1, cellRef.y() )
03801          >= cell_northwest->effBottomBorderValue( cellRef.x() - 1, cellRef.y() - 1 ) )
03802       horz_pen = cell_west->effTopBorderPen( cellRef.x() - 1, cellRef.y() );
03803     else
03804       horz_pen = cell_northwest->effBottomBorderPen( cellRef.x() - 1,
03805                  cellRef.y() - 1 );
03806 
03807     horz_penWidth = QMAX( 1, doc->zoomItY( horz_pen.width() ) );
03808     int bottom = ( QMAX( 0, -1 + horz_penWidth ) ) / 2 + 1;
03809 
03810     painter.setPen( vert_pen );
03811     // If we are on paper printout, we limit the length of the lines.
03812     // On paper, we always have full cells, on screen not.
03813     if ( painter.device()->isExtDev() ) {
03814       if ( sheetDir == Sheet::RightToLeft )
03815         painter.drawLine( QMAX( zrect_left, zcellRect_right ),
03816                           QMAX( zrect_top, zcellRect_top ),
03817                           QMIN( zrect_right, zcellRect_right ),
03818                           QMIN( zrect_bottom, zcellRect_top + bottom ) );
03819       else
03820         painter.drawLine( QMAX( zrect_left, zcellRect_left ),
03821                           QMAX( zrect_top, zcellRect_top ),
03822                           QMIN( zrect_right, zcellRect_left ),
03823                           QMIN( zrect_bottom, zcellRect_top + bottom ) );
03824     }
03825     else {
03826       if ( sheetDir == Sheet::RightToLeft )
03827         painter.drawLine( zcellRect_right, zcellRect_top,
03828                           zcellRect_right, zcellRect_top + bottom );
03829       else
03830         painter.drawLine( zcellRect_left, zcellRect_top,
03831                           zcellRect_left, zcellRect_top + bottom );
03832     }
03833   }
03834 
03835   // Fix the borders which meet at the top right corner
03836   if ( cell_north->effRightBorderValue( cellRef.x(), cellRef.y() - 1 )
03837        >= cell_northeast->effLeftBorderValue( cellRef.x() + 1,
03838                 cellRef.y() - 1 ) )
03839     vert_pen = cell_north->effRightBorderPen( cellRef.x(), cellRef.y() - 1 );
03840   else
03841     vert_pen = cell_northeast->effLeftBorderPen( cellRef.x() + 1,
03842              cellRef.y() - 1 );
03843 
03844   // vert_pen = effRightBorderPen( cellRef.x(), cellRef.y() - 1 );
03845   vert_penWidth = QMAX( 1, doc->zoomItX( vert_pen.width() ) );
03846   vert_pen.setWidth( vert_penWidth );
03847   if ( ( vert_pen.style() != Qt::NoPen ) && ( cellRef.x() < KS_colMax ) ) {
03848     if ( cell_east->effTopBorderValue( cellRef.x() + 1, cellRef.y() )
03849          >= cell_northeast->effBottomBorderValue( cellRef.x() + 1,
03850               cellRef.y() - 1 ) )
03851       horz_pen = cell_east->effTopBorderPen( cellRef.x() + 1, cellRef.y() );
03852     else
03853       horz_pen = cell_northeast->effBottomBorderPen( cellRef.x() + 1,
03854                  cellRef.y() - 1 );
03855 
03856     // horz_pen = effTopBorderPen( cellRef.x() + 1, cellRef.y() );
03857     horz_penWidth = QMAX( 1, doc->zoomItY( horz_pen.width() ) );
03858     int bottom = ( QMAX( 0, -1 + horz_penWidth ) ) / 2 + 1;
03859 
03860     painter.setPen( vert_pen );
03861     //If we are on paper printout, we limit the length of the lines
03862     //On paper, we always have full cells, on screen not
03863     if ( painter.device()->isExtDev() ) {
03864       if ( sheetDir == Sheet::RightToLeft )
03865         painter.drawLine( QMAX( zrect_left, zcellRect_left ),
03866                           QMAX( zrect_top, zcellRect_top ),
03867                           QMIN( zrect_right, zcellRect_left ),
03868                           QMIN( zrect_bottom, zcellRect_top + bottom ) );
03869       else
03870         painter.drawLine( QMAX( zrect_left, zcellRect_right ),
03871                           QMAX( zrect_top, zcellRect_top ),
03872                           QMIN( zrect_right, zcellRect_right ),
03873                           QMIN( zrect_bottom, zcellRect_top + bottom ) );
03874     }
03875     else {
03876       if ( sheetDir == Sheet::RightToLeft )
03877         painter.drawLine( zcellRect_left, zcellRect_top,
03878                           zcellRect_left, zcellRect_top + bottom );
03879       else
03880         painter.drawLine( zcellRect_right, zcellRect_top,
03881                           zcellRect_right, zcellRect_top + bottom );
03882     }
03883   }
03884 
03885   // Bottom
03886   if ( cellRef.y() < KS_rowMax ) {
03887     // Fix the borders which meet at the bottom left corner
03888     if ( cell_south->effLeftBorderValue( cellRef.x(), cellRef.y() + 1 )
03889          >= cell_southwest->effRightBorderValue( cellRef.x() - 1,
03890              cellRef.y() + 1 ) )
03891       vert_pen = cell_south->effLeftBorderPen( cellRef.x(), cellRef.y() + 1 );
03892     else
03893       vert_pen = cell_southwest->effRightBorderPen( cellRef.x() - 1,
03894                 cellRef.y() + 1 );
03895 
03896     // vert_pen = effLeftBorderPen( cellRef.x(), cellRef.y() + 1 );
03897     vert_penWidth = QMAX( 1, doc->zoomItY( vert_pen.width() ) );
03898     vert_pen.setWidth( vert_penWidth );
03899     if ( vert_pen.style() != Qt::NoPen ) {
03900       if ( cell_west->effBottomBorderValue( cellRef.x() - 1, cellRef.y() )
03901            >= cell_southwest->effTopBorderValue( cellRef.x() - 1,
03902              cellRef.y() + 1 ) )
03903         horz_pen = cell_west->effBottomBorderPen( cellRef.x() - 1,
03904               cellRef.y() );
03905       else
03906         horz_pen = cell_southwest->effTopBorderPen( cellRef.x() - 1,
03907                 cellRef.y() + 1 );
03908 
03909       // horz_pen = effBottomBorderPen( cellRef.x() - 1, cellRef.y() );
03910       horz_penWidth = QMAX( 1, doc->zoomItX( horz_pen.width() ) );
03911       int bottom = ( QMAX( 0, -1 + horz_penWidth ) ) / 2;
03912 
03913       painter.setPen( vert_pen );
03914       // If we are on paper printout, we limit the length of the lines.
03915       // On paper, we always have full cells, on screen not.
03916       if ( painter.device()->isExtDev() ) {
03917         if ( sheetDir == Sheet::RightToLeft )
03918           painter.drawLine( QMAX( zrect_left, zcellRect_right ),
03919                             QMAX( zrect_top, zcellRect_bottom - bottom ),
03920                             QMIN( zrect_right, zcellRect_right ),
03921                             QMIN( zrect_bottom, zcellRect_bottom ) );
03922         else
03923           painter.drawLine( QMAX( zrect_left, zcellRect_left ),
03924                             QMAX( zrect_top, zcellRect_bottom - bottom ),
03925                             QMIN( zrect_right, zcellRect_left ),
03926                             QMIN( zrect_bottom, zcellRect_bottom ) );
03927       }
03928       else {
03929         if ( sheetDir == Sheet::RightToLeft )
03930           painter.drawLine( zcellRect_right, zcellRect_bottom - bottom,
03931                             zcellRect_right, zcellRect_bottom );
03932         else
03933           painter.drawLine( zcellRect_left, zcellRect_bottom - bottom,
03934                             zcellRect_left, zcellRect_bottom );
03935       }
03936     }
03937 
03938     // Fix the borders which meet at the bottom right corner
03939     if ( cell_south->effRightBorderValue( cellRef.x(), cellRef.y() + 1 )
03940          >= cell_southeast->effLeftBorderValue( cellRef.x() + 1,
03941             cellRef.y() + 1 ) )
03942       vert_pen = cell_south->effRightBorderPen( cellRef.x(), cellRef.y() + 1 );
03943     else
03944       vert_pen = cell_southeast->effLeftBorderPen( cellRef.x() + 1,
03945                cellRef.y() + 1 );
03946 
03947     // vert_pen = effRightBorderPen( cellRef.x(), cellRef.y() + 1 );
03948     vert_penWidth = QMAX( 1, doc->zoomItY( vert_pen.width() ) );
03949     vert_pen.setWidth( vert_penWidth );
03950     if ( ( vert_pen.style() != Qt::NoPen ) && ( cellRef.x() < KS_colMax ) ) {
03951       if ( cell_east ->effBottomBorderValue( cellRef.x() + 1, cellRef.y() )
03952            >= cell_southeast->effTopBorderValue( cellRef.x() + 1,
03953              cellRef.y() + 1 ) )
03954 
03955         horz_pen = format()->sheet()->cellAt( cellRef.x() + 1, cellRef.y() )
03956     ->effBottomBorderPen( cellRef.x() + 1, cellRef.y() );
03957       else
03958         horz_pen = format()->sheet()->cellAt( cellRef.x() + 1, cellRef.y() + 1 )
03959     ->effTopBorderPen( cellRef.x() + 1, cellRef.y() + 1 );
03960 
03961       // horz_pen = effBottomBorderPen( cellRef.x() + 1, cellRef.y() );
03962       horz_penWidth = QMAX( 1, doc->zoomItX( horz_pen.width() ) );
03963       int bottom = ( QMAX( 0, -1 + horz_penWidth ) ) / 2;
03964 
03965       painter.setPen( vert_pen );
03966       // If we are on paper printout, we limit the length of the lines.
03967       // On paper, we always have full cells, on screen not.
03968       if ( painter.device()->isExtDev() )      {
03969         if ( sheetDir == Sheet::RightToLeft )
03970           painter.drawLine( QMAX( zrect_left, zcellRect_left ),
03971                             QMAX( zrect_top, zcellRect_bottom - bottom ),
03972                             QMIN( zrect_right, zcellRect_left ),
03973                             QMIN( zrect_bottom, zcellRect_bottom ) );
03974         else
03975           painter.drawLine( QMAX( zrect_left, zcellRect_right ),
03976                             QMAX( zrect_top, zcellRect_bottom - bottom ),
03977                             QMIN( zrect_right, zcellRect_right ),
03978                             QMIN( zrect_bottom, zcellRect_bottom ) );
03979       }
03980       else {
03981         if ( sheetDir == Sheet::RightToLeft )
03982           painter.drawLine( zcellRect_left, zcellRect_bottom - bottom,
03983                             zcellRect_left, zcellRect_bottom );
03984         else
03985           painter.drawLine( zcellRect_right, zcellRect_bottom - bottom,
03986                             zcellRect_right, zcellRect_bottom );
03987       }
03988     }
03989   }
03990   #endif
03991 }
03992 
03993 
03994 // Paint diagonal lines through the cell.
03995 //
03996 void Cell::paintCellDiagonalLines( QPainter& painter,
03997            const KoRect &cellRect,
03998            const QPoint &cellRef )
03999 {
04000   if ( isPartOfMerged() )
04001     return;
04002 
04003   Doc* doc = sheet()->doc();
04004 
04005   if ( effFallDiagonalPen( cellRef.x(), cellRef.y() ).style() != Qt::NoPen ) {
04006     painter.setPen( effFallDiagonalPen( cellRef.x(), cellRef.y() ) );
04007     painter.drawLine( doc->zoomItX( cellRect.x() ),
04008           doc->zoomItY( cellRect.y() ),
04009           doc->zoomItX( cellRect.right() ),
04010           doc->zoomItY( cellRect.bottom() ) );
04011   }
04012 
04013   if ( effGoUpDiagonalPen( cellRef.x(), cellRef.y() ).style() != Qt::NoPen ) {
04014     painter.setPen( effGoUpDiagonalPen( cellRef.x(), cellRef.y() ) );
04015     painter.drawLine( doc->zoomItX( cellRect.x() ),
04016           doc->zoomItY( cellRect.bottom() ),
04017           doc->zoomItX( cellRect.right() ),
04018           doc->zoomItY( cellRect.y() ) );
04019   }
04020 }
04021 
04022 
04023 //                        End of Painting
04024 // ================================================================
04025 
04026 
04027 int Cell::defineAlignX()
04028 {
04029   int a = format()->align( column(), row() );
04030   if ( a == Format::Undefined )
04031   {
04032     //numbers should be right-aligned by default, as well as BiDi text
04033     if ((formatType() == Text_format) || value().isString())
04034       a = (d->strOutText.isRightToLeft()) ?
04035                                Format::Right : Format::Left;
04036     else {
04037       Value val = value();
04038       while (val.isArray()) val = val.element (0, 0);
04039       if (val.isBoolean() || val.isNumber())
04040         a = Format::Right;
04041       else
04042         a = Format::Left;
04043     }
04044   }
04045   return a;
04046 }
04047 
04048 int Cell::effAlignX()
04049 {
04050   if ( d->hasExtra() && d->extra()->conditions
04051        && d->extra()->conditions->matchedStyle()
04052        && d->extra()->conditions->matchedStyle()->hasFeature( Style::SAlignX, true ) )
04053     return d->extra()->conditions->matchedStyle()->alignX();
04054 
04055   return defineAlignX();
04056 }
04057 
04058 // Cut strOutText, so that it only holds the part that can be displayed.
04059 //
04060 // Used in paintText().
04061 //
04062 
04063 QString Cell::textDisplaying( QPainter &_painter )
04064 {
04065   QFontMetrics  fm = _painter.fontMetrics();
04066   int           a  = format()->align( column(), row() );
04067 
04068   bool isNumeric = value().isNumber();
04069 
04070   if ( !format()->verticalText( column(),row() ) ) {
04071     // Non-vertical text: the ordinary case.
04072 
04073     // Not enough space but align to left
04074     double  len = 0.0;
04075     int     extraXCells = d->hasExtra() ? d->extra()->extraXCells : 0;
04076 
04077     for ( int i = column(); i <= column() + extraXCells; i++ ) {
04078       ColumnFormat *cl2 = format()->sheet()->columnFormat( i );
04079       len += cl2->dblWidth() - 1.0; //-1.0 because the pixel in between 2 cells is shared between both cells
04080     }
04081 
04082     QString  tmp;
04083     double   tmpIndent = 0.0;
04084     if ( !isEmpty() )
04085       tmpIndent = format()->getIndent( column(), row() );
04086 
04087     // Start out with the whole text, cut one character at a time, and
04088     // when the text finally fits, return it.
04089     for ( int i = d->strOutText.length(); i != 0; i-- )
04090     {
04091       //Note that numbers are always treated as left-aligned since if we have to cut digits off, they should
04092       //always be the least significant ones at the end of the string
04093       if ( a == Format::Left || a == Format::Undefined || isNumeric)
04094         tmp = d->strOutText.left(i);
04095       else if ( a == Format::Right)
04096         tmp = d->strOutText.right(i);
04097       else
04098         tmp = d->strOutText.mid( ( d->strOutText.length() - i ) / 2, i);
04099 
04100       if (isNumeric)
04101       {
04102       //For numeric values, we can cut off digits after the decimal point to make it fit,
04103       //but not the integer part of the number.
04104       //If this number still contains a fraction part then we don't need to do anything, if we have run
04105       //out of space to fit even the integer part of the number then display #########
04106       //TODO Perhaps try to display integer part in standard form if there is not enough room for it?
04107 
04108       if (!tmp.contains('.'))
04109         d->strOutText=QString().fill('#',20);
04110       }
04111 
04112       // 4 equal length of red triangle +1 point.
04113       if ( format()->sheet()->doc()->unzoomItX( fm.width( tmp ) ) + tmpIndent
04114      < len - 4.0 - 1.0 )
04115       {
04116     if ( format()->getAngle( column(), row() ) != 0 )
04117     {
04118             QString tmp2;
04119             RowFormat *rl = format()->sheet()->rowFormat( row() );
04120             if ( d->textHeight > rl->dblHeight() )
04121         {
04122                 for ( int j = d->strOutText.length(); j != 0; j-- )
04123             {
04124                     tmp2 = d->strOutText.left( j );
04125                     if ( format()->sheet()->doc()->unzoomItY( fm.width( tmp2 ) ) < rl->dblHeight() - 1.0 )
04126                     {
04127                         return d->strOutText.left( QMIN( tmp.length(), tmp2.length() ) );
04128                     }
04129                 }
04130             }
04131             else
04132                 return tmp;
04133 
04134     }
04135     else
04136             return tmp;
04137       }
04138     }
04139     return QString( "" );
04140   }
04141   else if ( format()->verticalText( column(), row() ) ) {
04142     // Vertical text.
04143 
04144     RowFormat  *rl = format()->sheet()->rowFormat( row() );
04145     double      tmpIndent = 0.0;
04146 
04147     // Not enough space but align to left.
04148     double  len = 0.0;
04149     int     extraXCells = d->hasExtra() ? d->extra()->extraXCells : 0;
04150 
04151     for ( int i = column(); i <= column() + extraXCells; i++ ) {
04152       ColumnFormat  *cl2 = format()->sheet()->columnFormat( i );
04153 
04154       // -1.0 because the pixel in between 2 cells is shared between both cells
04155       len += cl2->dblWidth() - 1.0;
04156     }
04157 
04158     if ( !isEmpty() )
04159       tmpIndent = format()->getIndent( column(), row() );
04160 
04161     if ( ( d->textWidth + tmpIndent > len ) || d->textWidth == 0.0 )
04162       return QString( "" );
04163 
04164     for ( int i = d->strOutText.length(); i != 0; i-- ) {
04165       if ( format()->sheet()->doc()->unzoomItY( fm.ascent() + fm.descent() ) * i
04166      < rl->dblHeight() - 1.0 )
04167   return d->strOutText.left( i );
04168     }
04169 
04170     return QString( "" );
04171   }
04172 
04173   ColumnFormat  *cl = format()->sheet()->columnFormat( column() );
04174   double         w = cl->dblWidth();
04175 
04176   if ( d->hasExtra() && (d->extra()->extraWidth != 0.0) )
04177     w = d->extra()->extraWidth;
04178 
04179   QString tmp;
04180   for ( int i = d->strOutText.length(); i != 0; i-- ) {
04181     tmp = d->strOutText.left( i );
04182 
04183     // 4 equals lenght of red triangle +1 pixel
04184     if ( format()->sheet()->doc()->unzoomItX( fm.width( tmp ) ) < w - 4.0 - 1.0 )
04185       return tmp;
04186   }
04187 
04188   return  QString::null;
04189 }
04190 
04191 
04192 double Cell::dblWidth( int _col, const Canvas *_canvas ) const
04193 {
04194   if ( _col < 0 )
04195     _col = d->column;
04196 
04197   if ( _canvas )
04198   {
04199     if ( testFlag(Flag_Merged) )
04200       return d->extra()->extraWidth;
04201 
04202     const ColumnFormat *cl = format()->sheet()->columnFormat( _col );
04203     return cl->dblWidth( _canvas );
04204   }
04205 
04206   if ( testFlag(Flag_Merged) )
04207     return d->extra()->extraWidth;
04208 
04209   const ColumnFormat *cl = format()->sheet()->columnFormat( _col );
04210   return cl->dblWidth();
04211 }
04212 
04213 int Cell::width( int _col, const Canvas *_canvas ) const
04214 {
04215   return int( dblWidth( _col, _canvas ) );
04216 }
04217 
04218 double Cell::dblHeight( int _row, const Canvas *_canvas ) const
04219 {
04220   if ( _row < 0 )
04221     _row = d->row;
04222 
04223   if ( _canvas )
04224   {
04225     if ( testFlag(Flag_Merged) )
04226       return d->extra()->extraHeight;
04227 
04228     const RowFormat *rl = format()->sheet()->rowFormat( _row );
04229     return rl->dblHeight( _canvas );
04230   }
04231 
04232   if ( testFlag(Flag_Merged) )
04233     return d->extra()->extraHeight;
04234 
04235   const RowFormat *rl = format()->sheet()->rowFormat( _row );
04236   return rl->dblHeight();
04237 }
04238 
04239 int Cell::height( int _row, const Canvas *_canvas ) const
04240 {
04241   return int( dblHeight( _row, _canvas ) );
04242 }
04243 
04245 //
04246 // Misc Properties.
04247 // Reimplementation of Format methods.
04248 //
04250 
04251 const QBrush& Cell::backGroundBrush( int _col, int _row ) const
04252 {
04253   if ( d->hasExtra() && (!d->extra()->obscuringCells.isEmpty()) )
04254   {
04255     const Cell* cell = d->extra()->obscuringCells.first();
04256     return cell->backGroundBrush( cell->column(), cell->row() );
04257   }
04258 
04259   return format()->backGroundBrush( _col, _row );
04260 }
04261 
04262 const QColor& Cell::bgColor( int _col, int _row ) const
04263 {
04264   if ( d->hasExtra() && (!d->extra()->obscuringCells.isEmpty()) )
04265   {
04266     const Cell* cell = d->extra()->obscuringCells.first();
04267     return cell->bgColor( cell->column(), cell->row() );
04268   }
04269 
04270   return format()->bgColor( _col, _row );
04271 }
04272 
04274 //
04275 // Borders.
04276 // Reimplementation of Format methods.
04277 //
04279 
04280 void Cell::setLeftBorderPen( const QPen& p )
04281 {
04282   if ( column() == 1 )
04283   {
04284     Cell* cell = format()->sheet()->cellAt( column() - 1, row() );
04285     if ( cell && cell->format()->hasProperty( Format::PRightBorder )
04286          && format()->sheet()->cellAt( column(), row() ) == this )
04287         cell->format()->clearProperty( Format::PRightBorder );
04288   }
04289 
04290   format()->setLeftBorderPen( p );
04291 }
04292 
04293 void Cell::setTopBorderPen( const QPen& p )
04294 {
04295   if ( row() == 1 )
04296   {
04297     Cell* cell = format()->sheet()->cellAt( column(), row() - 1 );
04298     if ( cell && cell->format()->hasProperty( Format::PBottomBorder )
04299          && format()->sheet()->cellAt( column(), row() ) == this )
04300         cell->format()->clearProperty( Format::PBottomBorder );
04301   }
04302   format()->setTopBorderPen( p );
04303 }
04304 
04305 void Cell::setRightBorderPen( const QPen& p )
04306 {
04307     Cell* cell = 0L;
04308     if ( column() < KS_colMax )
04309         cell = format()->sheet()->cellAt( column() + 1, row() );
04310 
04311     if ( cell && cell->format()->hasProperty( Format::PLeftBorder )
04312          && format()->sheet()->cellAt( column(), row() ) == this )
04313         cell->format()->clearProperty( Format::PLeftBorder );
04314 
04315     format()->setRightBorderPen( p );
04316 }
04317 
04318 void Cell::setBottomBorderPen( const QPen& p )
04319 {
04320     Cell* cell = 0L;
04321     if ( row() < KS_rowMax )
04322         cell = format()->sheet()->cellAt( column(), row() + 1 );
04323 
04324     if ( cell && cell->format()->hasProperty( Format::PTopBorder )
04325          && format()->sheet()->cellAt( column(), row() ) == this )
04326         cell->format()->clearProperty( Format::PTopBorder );
04327 
04328     format()->setBottomBorderPen( p );
04329 }
04330 
04331 const QPen& Cell::rightBorderPen( int _col, int _row ) const
04332 {
04333     if ( !format()->hasProperty( Format::PRightBorder ) && ( _col < KS_colMax ) )
04334     {
04335         Cell * cell = format()->sheet()->cellAt( _col + 1, _row );
04336         if ( cell && cell->format()->hasProperty( Format::PLeftBorder ) )
04337             return cell->leftBorderPen( _col + 1, _row );
04338     }
04339 
04340     return format()->rightBorderPen( _col, _row );
04341 }
04342 
04343 const QPen& Cell::leftBorderPen( int _col, int _row ) const
04344 {
04345     if ( !format()->hasProperty( Format::PLeftBorder ) )
04346     {
04347         const Cell * cell = format()->sheet()->cellAt( _col - 1, _row );
04348         if ( cell && cell->format()->hasProperty( Format::PRightBorder ) )
04349             return cell->rightBorderPen( _col - 1, _row );
04350     }
04351 
04352     return format()->leftBorderPen( _col, _row );
04353 }
04354 
04355 const QPen& Cell::bottomBorderPen( int _col, int _row ) const
04356 {
04357     if ( !format()->hasProperty( Format::PBottomBorder ) && ( _row < KS_rowMax ) )
04358     {
04359         const Cell * cell = format()->sheet()->cellAt( _col, _row + 1 );
04360         if ( cell && cell->format()->hasProperty( Format::PTopBorder ) )
04361             return cell->topBorderPen( _col, _row + 1 );
04362     }
04363 
04364     return format()->bottomBorderPen( _col, _row );
04365 }
04366 
04367 const QPen& Cell::topBorderPen( int _col, int _row ) const
04368 {
04369     if ( !format()->hasProperty( Format::PTopBorder ) )
04370     {
04371         const Cell * cell = format()->sheet()->cellAt( _col, _row - 1 );
04372         if ( cell->format()->hasProperty( Format::PBottomBorder ) )
04373             return cell->bottomBorderPen( _col, _row - 1 );
04374     }
04375 
04376     return format()->topBorderPen( _col, _row );
04377 }
04378 
04379 const QColor & Cell::effTextColor( int col, int row ) const
04380 {
04381   if ( d->hasExtra() && d->extra()->conditions
04382        && d->extra()->conditions->matchedStyle()
04383        && d->extra()->conditions->matchedStyle()->hasFeature( Style::STextPen, true ) )
04384     return d->extra()->conditions->matchedStyle()->pen().color();
04385 
04386   return format()->textColor( col, row );
04387 }
04388 
04389 const QPen& Cell::effLeftBorderPen( int col, int row ) const
04390 {
04391   if ( isPartOfMerged() )
04392   {
04393     Cell * cell = d->extra()->obscuringCells.first();
04394     return cell->effLeftBorderPen( cell->column(), cell->row() );
04395   }
04396 
04397   if ( d->hasExtra() && d->extra()->conditions
04398        && d->extra()->conditions->matchedStyle()
04399        && d->extra()->conditions->matchedStyle()->hasFeature( Style::SLeftBorder, true ) )
04400     return d->extra()->conditions->matchedStyle()->leftBorderPen();
04401 
04402   return leftBorderPen( col, row );
04403 }
04404 
04405 const QPen& Cell::effTopBorderPen( int col, int row ) const
04406 {
04407   if ( isPartOfMerged() )
04408   {
04409     Cell * cell = d->extra()->obscuringCells.first();
04410     return cell->effTopBorderPen( cell->column(), cell->row() );
04411   }
04412 
04413   if ( d->hasExtra() && d->extra()->conditions
04414        && d->extra()->conditions->matchedStyle()
04415        && d->extra()->conditions->matchedStyle()->hasFeature( Style::STopBorder, true ) )
04416     return d->extra()->conditions->matchedStyle()->topBorderPen();
04417 
04418   return topBorderPen( col, row );
04419 }
04420 
04421 const QPen& Cell::effRightBorderPen( int col, int row ) const
04422 {
04423   if ( isPartOfMerged() )
04424   {
04425     Cell * cell = d->extra()->obscuringCells.first();
04426     return cell->effRightBorderPen( cell->column(), cell->row() );
04427   }
04428 
04429   if ( d->hasExtra() && d->extra()->conditions
04430        && d->extra()->conditions->matchedStyle()
04431        && d->extra()->conditions->matchedStyle()->hasFeature( Style::SRightBorder, true ) )
04432     return d->extra()->conditions->matchedStyle()->rightBorderPen();
04433 
04434   return rightBorderPen( col, row );
04435 }
04436 
04437 const QPen& Cell::effBottomBorderPen( int col, int row ) const
04438 {
04439   if ( isPartOfMerged() )
04440   {
04441     Cell * cell = d->extra()->obscuringCells.first();
04442     return cell->effBottomBorderPen( cell->column(), cell->row() );
04443   }
04444 
04445   if ( d->hasExtra() && d->extra()->conditions
04446        && d->extra()->conditions->matchedStyle()
04447        && d->extra()->conditions->matchedStyle()->hasFeature( Style::SBottomBorder, true ) )
04448     return d->extra()->conditions->matchedStyle()->bottomBorderPen();
04449 
04450   return bottomBorderPen( col, row );
04451 }
04452 
04453 const QPen & Cell::effGoUpDiagonalPen( int col, int row ) const
04454 {
04455   if ( d->hasExtra() && d->extra()->conditions
04456        && d->extra()->conditions->matchedStyle()
04457        && d->extra()->conditions->matchedStyle()->hasFeature( Style::SGoUpDiagonal, true ) )
04458     return d->extra()->conditions->matchedStyle()->goUpDiagonalPen();
04459 
04460   return format()->goUpDiagonalPen( col, row );
04461 }
04462 
04463 const QPen & Cell::effFallDiagonalPen( int col, int row ) const
04464 {
04465   if ( d->hasExtra() && d->extra()->conditions
04466        && d->extra()->conditions->matchedStyle()
04467        && d->extra()->conditions->matchedStyle()->hasFeature( Style::SFallDiagonal, true ) )
04468     return d->extra()->conditions->matchedStyle()->fallDiagonalPen();
04469 
04470   return format()->fallDiagonalPen( col, row );
04471 }
04472 
04473 uint Cell::effBottomBorderValue( int col, int row ) const
04474 {
04475   if ( isPartOfMerged() )
04476   {
04477     Cell * cell = d->extra()->obscuringCells.first();
04478     return cell->effBottomBorderValue( cell->column(), cell->row() );
04479   }
04480 
04481   if ( d->hasExtra() && d->extra()->conditions
04482       && d->extra()->conditions->matchedStyle() )
04483     return d->extra()->conditions->matchedStyle()->bottomPenValue();
04484 
04485   return format()->bottomBorderValue( col, row );
04486 }
04487 
04488 uint Cell::effRightBorderValue( int col, int row ) const
04489 {
04490   if ( isPartOfMerged() )
04491   {
04492     Cell * cell = d->extra()->obscuringCells.first();
04493     return cell->effRightBorderValue( cell->column(), cell->row() );
04494   }
04495 
04496   if ( d->hasExtra() && d->extra()->conditions
04497       && d->extra()->conditions->matchedStyle() )
04498     return d->extra()->conditions->matchedStyle()->rightPenValue();
04499 
04500   return format()->rightBorderValue( col, row );
04501 }
04502 
04503 uint Cell::effLeftBorderValue( int col, int row ) const
04504 {
04505   if ( isPartOfMerged() )
04506   {
04507     Cell * cell = d->extra()->obscuringCells.first();
04508     return cell->effLeftBorderValue( cell->column(), cell->row() );
04509   }
04510 
04511   if ( d->hasExtra() && d->extra()->conditions
04512       && d->extra()->conditions->matchedStyle() )
04513     return d->extra()->conditions->matchedStyle()->leftPenValue();
04514 
04515   return format()->leftBorderValue( col, row );
04516 }
04517 
04518 uint Cell::effTopBorderValue( int col, int row ) const
04519 {
04520   if ( isPartOfMerged() )
04521   {
04522     Cell * cell = d->extra()->obscuringCells.first();
04523     return cell->effTopBorderValue( cell->column(), cell->row() );
04524   }
04525 
04526   if ( d->hasExtra() && d->extra()->conditions
04527       && d->extra()->conditions->matchedStyle() )
04528     return d->extra()->conditions->matchedStyle()->topPenValue();
04529 
04530   return format()->topBorderValue( col, row );
04531 }
04532 
04534 //
04535 // Precision
04536 //
04538 
04539 void Cell::incPrecision()
04540 {
04541   //TODO: This is ugly. Why not simply regenerate the text to display? Tomas
04542 
04543   if ( !value().isNumber() )
04544     return;
04545   int tmpPreci = format()->precision( column(), row() );
04546 
04547   if ( tmpPreci == -1 )
04548   {
04549     int pos = d->strOutText.find(decimal_point);
04550     if ( pos == -1 )
04551         pos = d->strOutText.find('.');
04552     if ( pos == -1 )
04553       format()->setPrecision(1);
04554     else
04555     {
04556       int start = 0;
04557       if ( d->strOutText.find('%') != -1 )
04558         start = 2;
04559       else if ( d->strOutText.find(locale()->currencySymbol()) == ((int)(d->strOutText.length()-locale()->currencySymbol().length())) )
04560         start = locale()->currencySymbol().length() + 1;
04561       else if ( (start=d->strOutText.find('E')) != -1 )
04562         start = d->strOutText.length() - start;
04563 
04564       //kdDebug(36001) << "start=" << start << " pos=" << pos << " length=" << d->strOutText.length() << endl;
04565       format()->setPrecision( QMAX( 0, (int)d->strOutText.length() - start - pos ) );
04566     }
04567   }
04568   else if ( tmpPreci < 10 )
04569   {
04570     format()->setPrecision( ++tmpPreci );
04571   }
04572   setFlag(Flag_LayoutDirty);
04573 }
04574 
04575 void Cell::decPrecision()
04576 {
04577   //TODO: This is ugly. Why not simply regenerate the text to display? Tomas
04578 
04579   if ( !value().isNumber() )
04580     return;
04581   int preciTmp = format()->precision( column(), row() );
04582 //  kdDebug(36001) << "decPrecision: tmpPreci = " << tmpPreci << endl;
04583   if ( format()->precision(column(),row()) == -1 )
04584   {
04585     int pos = d->strOutText.find( decimal_point );
04586     int start = 0;
04587     if ( d->strOutText.find('%') != -1 )
04588         start = 2;
04589     else if ( d->strOutText.find(locale()->currencySymbol()) == ((int)(d->strOutText.length()-locale()->currencySymbol().length())) )
04590         start = locale()->currencySymbol().length() + 1;
04591     else if ( (start = d->strOutText.find('E')) != -1 )
04592         start = d->strOutText.length() - start;
04593     else
04594         start = 0;
04595 
04596     if ( pos == -1 )
04597       return;
04598 
04599     format()->setPrecision(d->strOutText.length() - pos - 2 - start);
04600     //   if ( preciTmp < 0 )
04601     //      format()->setPrecision( preciTmp );
04602   }
04603   else if ( preciTmp > 0 )
04604   {
04605     format()->setPrecision( --preciTmp );
04606   }
04607   setFlag( Flag_LayoutDirty );
04608 }
04609 
04610 //set numerical value
04611 //used in Sheet::setSeries (nowhere else yet)
04612 void Cell::setNumber( double number )
04613 {
04614   setValue( Value( number ) );
04615 
04616   d->strText.setNum( number );
04617   setDisplayText(d->strText);
04618   checkNumberFormat();
04619 }
04620 
04621 void Cell::setCellText( const QString& _text, bool asText )
04622 {
04623  // QString ctext = _text;
04624 
04625 // (Tomas) is this trim necessary for anything ?
04626 //  if( ctext.length() > 5000 )
04627 //    ctext = ctext.left( 5000 );
04628 
04629   // empty string ?
04630   if (_text.length() == 0) {
04631     d->strOutText = d->strText = "";
04632     setValue (Value::empty());
04633     return;
04634   }
04635 
04636   // as text ?
04637   if (asText) {
04638     d->strOutText = _text;
04639     d->strText    = _text;
04640     setValue (Value (_text));
04641 
04642     return;
04643   }
04644 
04645   QString oldText = d->strText;
04646   setDisplayText( _text );
04647   if(!format()->sheet()->isLoading() && !testValidity() )
04648   {
04649     //reapply old value if action == stop
04650     setDisplayText( oldText );
04651   }
04652 }
04653 
04654 void Cell::setDisplayText( const QString& _text )
04655 {
04656   bool isLoading = format()->sheet()->isLoading();
04657 
04658   if (!isLoading)
04659     format()->sheet()->doc()->emitBeginOperation( false );
04660 
04661   d->strText = _text;
04662 
04666   if ( !d->strText.isEmpty() && d->strText[0] == '=' )
04667   {
04668     setFlag(Flag_LayoutDirty);
04669     setFlag(Flag_TextFormatDirty);
04670 
04671     if ( !makeFormula() )
04672       kdError(36001) << "ERROR: Syntax ERROR" << endl;
04673     setCalcDirtyFlag ();
04674   }
04675 
04679   else
04680   {
04681     // Find out what data type it is
04682     checkTextInput();
04683 
04684     setFlag(Flag_LayoutDirty);
04685     setFlag(Flag_TextFormatDirty);
04686   }
04687 
04688   if ( !isLoading )
04689     format()->sheet()->doc()->emitEndOperation( QRect( d->column, d->row, 1, 1 ) );
04690 }
04691 
04692 void Cell::setLink( const QString& link )
04693 {
04694   d->extra()->link = link;
04695 
04696   if( !link.isEmpty() && d->strText.isEmpty() )
04697     setCellText( link );
04698 }
04699 
04700 QString Cell::link() const
04701 {
04702   return d->hasExtra() ? d->extra()->link : QString::null;
04703 }
04704 
04705 void Cell::update()
04706 {
04707   /* those obscuring us need to redo their layout cause they can't obscure us
04708      now that we've got text.
04709      This includes cells obscuring cells that we are obscuring
04710   */
04711   for (int x = d->column; x <= d->column + extraXCells(); x++)
04712   {
04713     for (int y = d->row; y <= d->row + extraYCells(); y++)
04714     {
04715       Cell* cell = format()->sheet()->cellAt(x,y);
04716       cell->setLayoutDirtyFlag();
04717     }
04718   }
04719 
04720   setCalcDirtyFlag();
04721 
04722   /* TODO - is this a good place for this? */
04723   updateChart(true);
04724 }
04725 
04726 bool Cell::testValidity() const
04727 {
04728     bool valid = false;
04729     if( d->hasExtra() && d->extra()->validity && d->extra()->validity->m_restriction != Restriction::None )
04730     {
04731         //fixme
04732         if ( d->extra()->validity->allowEmptyCell && d->strText.isEmpty() )
04733             return true;
04734 
04735         if( value().isNumber() &&
04736             (d->extra()->validity->m_restriction == Restriction::Number ||
04737              (d->extra()->validity->m_restriction == Restriction::Integer &&
04738               value().asFloat() == ceil(value().asFloat()))))
04739         {
04740             switch( d->extra()->validity->m_cond)
04741             {
04742               case Conditional::Equal:
04743                 valid = ( value().asFloat() - d->extra()->validity->valMin < DBL_EPSILON
04744                           && value().asFloat() - d->extra()->validity->valMin >
04745                           (0.0 - DBL_EPSILON));
04746                 break;
04747               case Conditional::DifferentTo:
04748                 valid = !(  ( value().asFloat() - d->extra()->validity->valMin < DBL_EPSILON
04749                               && value().asFloat() - d->extra()->validity->valMin >
04750                               (0.0 - DBL_EPSILON)) );
04751                 break;
04752               case Conditional::Superior:
04753                 valid = ( value().asFloat() > d->extra()->validity->valMin);
04754                 break;
04755               case Conditional::Inferior:
04756                 valid = ( value().asFloat()  <d->extra()->validity->valMin);
04757                 break;
04758               case Conditional::SuperiorEqual:
04759                 valid = ( value().asFloat() >= d->extra()->validity->valMin);
04760                 break;
04761               case Conditional::InferiorEqual:
04762                 valid = (value().asFloat() <= d->extra()->validity->valMin);
04763                 break;
04764               case Conditional::Between:
04765                 valid = ( value().asFloat() >= d->extra()->validity->valMin &&
04766                           value().asFloat() <= d->extra()->validity->valMax);
04767                 break;
04768               case Conditional::Different:
04769                 valid = (value().asFloat() < d->extra()->validity->valMin ||
04770                          value().asFloat() > d->extra()->validity->valMax);
04771                 break;
04772             default :
04773                 break;
04774             }
04775         }
04776         else if(d->extra()->validity->m_restriction==Restriction::Text)
04777         {
04778             valid = value().isString();
04779         }
04780         else if ( d->extra()->validity->m_restriction == Restriction::List )
04781         {
04782             //test int value
04783             if ( value().isString() && d->extra()->validity->listValidity.contains( value().asString() ) )
04784                 valid = true;
04785         }
04786         else if(d->extra()->validity->m_restriction==Restriction::TextLength)
04787         {
04788             if( value().isString() )
04789             {
04790                 int len = d->strOutText.length();
04791                 switch( d->extra()->validity->m_cond)
04792                 {
04793                   case Conditional::Equal:
04794                     if (len == d->extra()->validity->valMin)
04795                         valid = true;
04796                     break;
04797                   case Conditional::DifferentTo:
04798                     if (len != d->extra()->validity->valMin)
04799                         valid = true;
04800                     break;
04801                   case Conditional::Superior:
04802                     if(len > d->extra()->validity->valMin)
04803                         valid = true;
04804                     break;
04805                   case Conditional::Inferior:
04806                     if(len < d->extra()->validity->valMin)
04807                         valid = true;
04808                     break;
04809                   case Conditional::SuperiorEqual:
04810                     if(len >= d->extra()->validity->valMin)
04811                         valid = true;
04812                     break;
04813                   case Conditional::InferiorEqual:
04814                     if(len <= d->extra()->validity->valMin)
04815                         valid = true;
04816                     break;
04817                   case Conditional::Between:
04818                     if(len >= d->extra()->validity->valMin && len <= d->extra()->validity->valMax)
04819                         valid = true;
04820                     break;
04821                   case Conditional::Different:
04822                     if(len <d->extra()->validity->valMin || len >d->extra()->validity->valMax)
04823                         valid = true;
04824                     break;
04825                 default :
04826                     break;
04827                 }
04828             }
04829         }
04830         else if(d->extra()->validity->m_restriction == Restriction::Time && isTime())
04831         {
04832             switch( d->extra()->validity->m_cond)
04833             {
04834               case Conditional::Equal:
04835                 valid = (value().asTime() == d->extra()->validity->timeMin);
04836                 break;
04837               case Conditional::DifferentTo:
04838                 valid = (value().asTime() != d->extra()->validity->timeMin);
04839                 break;
04840               case Conditional::Superior:
04841                 valid = (value().asTime() > d->extra()->validity->timeMin);
04842                 break;
04843               case Conditional::Inferior:
04844                 valid = (value().asTime() < d->extra()->validity->timeMin);
04845                 break;
04846               case Conditional::SuperiorEqual:
04847                 valid = (value().asTime() >= d->extra()->validity->timeMin);
04848                 break;
04849               case Conditional::InferiorEqual:
04850                 valid = (value().asTime() <= d->extra()->validity->timeMin);
04851                 break;
04852               case Conditional::Between:
04853                 valid = (value().asTime() >= d->extra()->validity->timeMin &&
04854                          value().asTime() <= d->extra()->validity->timeMax);
04855                 break;
04856               case Conditional::Different:
04857                 valid = (value().asTime() < d->extra()->validity->timeMin ||
04858                          value().asTime() > d->extra()->validity->timeMax);
04859                 break;
04860             default :
04861                 break;
04862 
04863             }
04864         }
04865         else if(d->extra()->validity->m_restriction == Restriction::Date && isDate())
04866         {
04867             switch( d->extra()->validity->m_cond)
04868             {
04869               case Conditional::Equal:
04870                 valid = (value().asDate() == d->extra()->validity->dateMin);
04871                 break;
04872               case Conditional::DifferentTo:
04873                 valid = (value().asDate() != d->extra()->validity->dateMin);
04874                 break;
04875               case Conditional::Superior:
04876                 valid = (value().asDate() > d->extra()->validity->dateMin);
04877                 break;
04878               case Conditional::Inferior:
04879                 valid = (value().asDate() < d->extra()->validity->dateMin);
04880                 break;
04881               case Conditional::SuperiorEqual:
04882                 valid = (value().asDate() >= d->extra()->validity->dateMin);
04883                 break;
04884               case Conditional::InferiorEqual:
04885                 valid = (value().asDate() <= d->extra()->validity->dateMin);
04886                 break;
04887               case Conditional::Between:
04888                 valid = (value().asDate() >= d->extra()->validity->dateMin &&
04889                          value().asDate() <= d->extra()->validity->dateMax);
04890                 break;
04891               case Conditional::Different:
04892                 valid = (value().asDate() < d->extra()->validity->dateMin ||
04893                          value().asDate() > d->extra()->validity->dateMax);
04894                 break;
04895             default :
04896                 break;
04897 
04898             }
04899         }
04900     }
04901     else
04902     {
04903         valid= true;
04904     }
04905 
04906     if(!valid &&d->extra()->validity != NULL && d->extra()->validity->displayMessage)
04907     {
04908         switch (d->extra()->validity->m_action )
04909         {
04910           case Action::Stop:
04911             KMessageBox::error((QWidget*)0L, d->extra()->validity->message,
04912                                d->extra()->validity->title);
04913             break;
04914           case Action::Warning:
04915             KMessageBox::warningYesNo((QWidget*)0L, d->extra()->validity->message,
04916                                       d->extra()->validity->title);
04917             break;
04918           case Action::Information:
04919             KMessageBox::information((QWidget*)0L, d->extra()->validity->message,
04920                                      d->extra()->validity->title);
04921             break;
04922         }
04923     }
04924     if (!d->hasExtra())
04925         return true;  //okay if there's no validity
04926     return (valid || d->extra()->validity == NULL || d->extra()->validity->m_action != Action::Stop);
04927 }
04928 
04929 FormatType Cell::formatType() const
04930 {
04931     return format()->getFormatType( d->column, d->row );
04932 }
04933 
04934 double Cell::textWidth() const
04935 {
04936     return d->textWidth;
04937 }
04938 
04939 double Cell::textHeight() const
04940 {
04941     return d->textHeight;
04942 }
04943 
04944 int Cell::mergedXCells() const
04945 {
04946     return d->hasExtra() ? d->extra()->mergedXCells : 0;
04947 }
04948 
04949 int Cell::mergedYCells() const
04950 {
04951     return d->hasExtra() ? d->extra()->mergedYCells : 0;
04952 }
04953 
04954 int Cell::extraXCells() const
04955 {
04956     return d->hasExtra() ? d->extra()->extraXCells : 0;
04957 }
04958 
04959 int Cell::extraYCells() const
04960 {
04961     return d->hasExtra() ? d->extra()->extraYCells : 0;
04962 }
04963 
04964 double Cell::extraWidth() const
04965 {
04966     return d->hasExtra() ? d->extra()->extraWidth : 0;
04967 }
04968 
04969 double Cell::extraHeight() const
04970 {
04971     return d->hasExtra() ? d->extra()->extraHeight : 0;
04972 }
04973 
04974 
04975 bool Cell::isDate() const
04976 {
04977   FormatType ft = formatType();
04978 
04979   return (formatIsDate (ft) || ((ft == Generic_format) &&
04980       (value().format() == Value::fmt_Date)));
04981 }
04982 
04983 bool Cell::isTime() const
04984 {
04985   FormatType ft = formatType();
04986 
04987   return (formatIsTime (ft) || ((ft == Generic_format) &&
04988       (value().format() == Value::fmt_Time)));
04989 }
04990 
04991 void Cell::setCalcDirtyFlag()
04992 {
04993   if ( !isFormula() )
04994   {
04995     //don't set the flag if we don't hold a formula
04996     clearFlag(Flag_CalcDirty);
04997     return;
04998   }
04999   setFlag(Flag_CalcDirty);
05000   format()->sheet()->setRegionPaintDirty(cellRect());
05001 }
05002 
05003 
05004 bool Cell::updateChart(bool refresh)
05005 {
05006     // Update a chart for example if it depends on this cell.
05007     if ( d->row != 0 && d->column != 0 )
05008     {
05009         CellBinding *bind;
05010         for ( bind = format()->sheet()->firstCellBinding(); bind != 0L; bind = format()->sheet()->nextCellBinding() )
05011         {
05012             if ( bind->contains( d->column, d->row ) )
05013             {
05014                 if (!refresh)
05015                     return true;
05016 
05017                 bind->cellChanged( this );
05018             }
05019         }
05020         return true;
05021     }
05022     return false;
05023 
05024 }
05025 
05026 double Cell::getDouble ()
05027 {
05028   if (isDefault())
05029     return 0.0;
05030   //(Tomas) umm can't we simply call value().asFloat() ?
05031   if (isDate())
05032   {
05033     QDate date = value().asDate();
05034     QDate dummy (1900, 1, 1);
05035     return (dummy.daysTo (date) + 1);
05036   }
05037   if (isTime())
05038   {
05039     QTime time  = value().asTime();
05040     QTime dummy;
05041     return dummy.secsTo( time );
05042   }
05043   if (value().isNumber())
05044     return value().asFloat();
05045 
05046   return 0.0;
05047 }
05048 
05049 void Cell::convertToDouble ()
05050 {
05051   if (isDefault())
05052     return;
05053 
05054   setValue (getDouble ());
05055 }
05056 
05057 void Cell::convertToPercent ()
05058 {
05059   if (isDefault())
05060     return;
05061 
05062   setValue (getDouble ());
05063   d->value.setFormat (Value::fmt_Percent);
05064 }
05065 
05066 void Cell::convertToMoney ()
05067 {
05068   if (isDefault())
05069     return;
05070 
05071   setValue (getDouble ());
05072   d->value.setFormat (Value::fmt_Money);
05073   format()->setPrecision (locale()->fracDigits());
05074 }
05075 
05076 void Cell::convertToTime ()
05077 {
05078   //(Tomas) This is weird. And I mean *REALLY* weird. First, we
05079   //generate a time (QTime), then we convert it to text, then
05080   //we give the text to the cell and ask it to parse it. Weird...
05081 
05082   if (isDefault() || isEmpty())
05083     return;
05084 
05085   setValue (getDouble ());
05086   QTime time = value().asDateTime().time();
05087   int msec = (int) ( (value().asFloat() - (int) value().asFloat()) * 1000 );
05088   time = time.addMSecs( msec );
05089   setCellText( time.toString() );
05090 }
05091 
05092 void Cell::convertToDate ()
05093 {
05094   //(Tomas) This is weird. And I mean *REALLY* weird. First, we
05095   //generate a date (QDate), then we convert it to text, then
05096   //we give the text to the cell and ask it to parse it. Weird...
05097 
05098   if (isDefault() || isEmpty())
05099     return;
05100 
05101   setValue (getDouble ());
05102 
05103   //TODO: why did we call setValue(), when we override it here?
05104   QDate date(1900, 1, 1);
05105   date = date.addDays( (int) value().asFloat() - 1 );
05106   date = value().asDateTime().date();
05107   setCellText (locale()->formatDate (date, true));
05108 }
05109 
05110 void Cell::checkTextInput()
05111 {
05112   // Goal of this method: determine the value of the cell
05113   clearAllErrors();
05114 
05115   d->value = Value::empty();
05116 
05117   // Get the text from that cell
05118   QString str = d->strText;
05119 
05120   sheet()->doc()->parser()->parse (str, this);
05121 
05122   // Parsing as time acts like an autoformat: we even change d->strText
05123   // [h]:mm:ss -> might get set by ValueParser
05124   if (isTime() && (formatType() != Time_format7))
05125     d->strText = locale()->formatTime( value().asDateTime().time(), true);
05126 
05127   // convert first letter to uppercase ?
05128   if (format()->sheet()->getFirstLetterUpper() && value().isString() &&
05129       (!d->strText.isEmpty()))
05130   {
05131     QString str = value().asString();
05132     setValue( Value( str[0].upper() + str.right( str.length()-1 ) ) );
05133   }
05134 }
05135 
05136 //used in calc, setNumber, ValueParser
05137 void Cell::checkNumberFormat()
05138 {
05139     if ( formatType() == Number_format && value().isNumber() )
05140     {
05141         if ( value().asFloat() > 1e+10 )
05142             format()->setFormatType( Scientific_format );
05143     }
05144 }
05145 
05146 
05147 // ================================================================
05148 //                       Saving and loading
05149 
05150 
05151 QDomElement Cell::save( QDomDocument& doc,
05152              int _x_offset, int _y_offset,
05153              bool force, bool copy, bool era )
05154 {
05155     // Save the position of this cell
05156     QDomElement cell = doc.createElement( "cell" );
05157     cell.setAttribute( "row", d->row - _y_offset );
05158     cell.setAttribute( "column", d->column - _x_offset );
05159     //
05160     // Save the formatting information
05161     //
05162     QDomElement formatElement = format()->save( doc, d->column, d->row, force, copy );
05163     if ( formatElement.hasChildNodes() || formatElement.attributes().length() ) // don't save empty tags
05164         cell.appendChild( formatElement );
05165 
05166     if ( doesMergeCells() )
05167     {
05168         if ( extraXCells() )
05169             formatElement.setAttribute( "colspan", extraXCells() );
05170         if ( extraYCells() )
05171             formatElement.setAttribute( "rowspan", extraYCells() );
05172     }
05173 
05174     if ( d->hasExtra() && d->extra()->conditions )
05175     {
05176       QDomElement conditionElement = d->extra()->conditions->saveConditions( doc );
05177 
05178       if ( !conditionElement.isNull() )
05179         cell.appendChild( conditionElement );
05180     }
05181 
05182     if ( d->hasExtra() && (d->extra()->validity != 0) )
05183     {
05184         QDomElement validity = doc.createElement("validity");
05185 
05186         QDomElement param=doc.createElement("param");
05187         param.setAttribute("cond",(int)d->extra()->validity->m_cond);
05188         param.setAttribute("action",(int)d->extra()->validity->m_action);
05189         param.setAttribute("allow",(int)d->extra()->validity->m_restriction);
05190         param.setAttribute("valmin",d->extra()->validity->valMin);
05191         param.setAttribute("valmax",d->extra()->validity->valMax);
05192         param.setAttribute("displaymessage",d->extra()->validity->displayMessage);
05193         param.setAttribute("displayvalidationinformation",d->extra()->validity->displayValidationInformation);
05194         param.setAttribute("allowemptycell",d->extra()->validity->allowEmptyCell);
05195         if ( !d->extra()->validity->listValidity.isEmpty() )
05196             param.setAttribute( "listvalidity", d->extra()->validity->listValidity.join( ";" ) );
05197         validity.appendChild(param);
05198         QDomElement title = doc.createElement( "title" );
05199         title.appendChild( doc.createTextNode( d->extra()->validity->title ) );
05200         validity.appendChild( title );
05201         QDomElement message = doc.createElement( "message" );
05202         message.appendChild( doc.createCDATASection( d->extra()->validity->message ) );
05203         validity.appendChild( message );
05204 
05205         QDomElement inputTitle = doc.createElement( "inputtitle" );
05206         inputTitle.appendChild( doc.createTextNode( d->extra()->validity->titleInfo ) );
05207         validity.appendChild( inputTitle );
05208 
05209         QDomElement inputMessage = doc.createElement( "inputmessage" );
05210         inputMessage.appendChild( doc.createTextNode( d->extra()->validity->messageInfo ) );
05211         validity.appendChild( inputMessage );
05212 
05213 
05214 
05215         QString tmp;
05216         if ( d->extra()->validity->timeMin.isValid() )
05217         {
05218                 QDomElement timeMin = doc.createElement( "timemin" );
05219                 tmp=d->extra()->validity->timeMin.toString();
05220                 timeMin.appendChild( doc.createTextNode( tmp ) );
05221                 validity.appendChild( timeMin );
05222         }
05223         if ( d->extra()->validity->timeMax.isValid() )
05224         {
05225                 QDomElement timeMax = doc.createElement( "timemax" );
05226                 tmp=d->extra()->validity->timeMax.toString();
05227                 timeMax.appendChild( doc.createTextNode( tmp ) );
05228                 validity.appendChild( timeMax );
05229         }
05230 
05231         if ( d->extra()->validity->dateMin.isValid() )
05232         {
05233                 QDomElement dateMin = doc.createElement( "datemin" );
05234                 QString tmp("%1/%2/%3");
05235                 tmp = tmp.arg(d->extra()->validity->dateMin.year()).arg(d->extra()->validity->dateMin.month()).arg(d->extra()->validity->dateMin.day());
05236                 dateMin.appendChild( doc.createTextNode( tmp ) );
05237                 validity.appendChild( dateMin );
05238         }
05239         if ( d->extra()->validity->dateMax.isValid() )
05240         {
05241                 QDomElement dateMax = doc.createElement( "datemax" );
05242                 QString tmp("%1/%2/%3");
05243                 tmp = tmp.arg(d->extra()->validity->dateMax.year()).arg(d->extra()->validity->dateMax.month()).arg(d->extra()->validity->dateMax.day());
05244                 dateMax.appendChild( doc.createTextNode( tmp ) );
05245                 validity.appendChild( dateMax );
05246         }
05247 
05248         cell.appendChild( validity );
05249     }
05250 
05251     if ( format()->comment() )
05252     {
05253         QDomElement comment = doc.createElement( "comment" );
05254         comment.appendChild( doc.createCDATASection( *format()->comment() ) );
05255         cell.appendChild( comment );
05256     }
05257 
05258     //
05259     // Save the text
05260     //
05261     if ( !d->strText.isEmpty() )
05262     {
05263         // Formulas need to be encoded to ensure that they
05264         // are position independent.
05265         if ( isFormula() )
05266         {
05267             QDomElement text = doc.createElement( "text" );
05268             // if we are cutting to the clipboard, relative references need to be encoded absolutely
05269             text.appendChild( doc.createTextNode( encodeFormula( era ) ) );
05270             cell.appendChild( text );
05271 
05272             /* we still want to save the results of the formula */
05273             QDomElement formulaResult = doc.createElement( "result" );
05274             saveCellResult( doc, formulaResult, d->strOutText );
05275             cell.appendChild( formulaResult );
05276 
05277         }
05278         else if ( !link().isEmpty() )
05279         {
05280             // KSpread pre 1.4 saves link as rich text, marked with first char '
05281             // Have to be saved in some CDATA section because of too many special charatcers.
05282             QDomElement text = doc.createElement( "text" );
05283             QString qml = "!<a href=\"" + link() + "\">" + d->strText + "</a>";
05284             text.appendChild( doc.createCDATASection( qml ) );
05285             cell.appendChild( text );
05286         }
05287         else
05288         {
05289             // Save the cell contents (in a locale-independent way)
05290             QDomElement text = doc.createElement( "text" );
05291             saveCellResult( doc, text, d->strText );
05292             cell.appendChild( text );
05293         }
05294     }
05295     if ( cell.hasChildNodes() || cell.attributes().length() > 2 ) // don't save empty tags
05296         // (the >2 is due to "row" and "column" attributes)
05297         return cell;
05298     else
05299         return QDomElement();
05300 }
05301 
05302 bool Cell::saveCellResult( QDomDocument& doc, QDomElement& result,
05303                                   QString str )
05304 {
05305   QString dataType = "Other"; // fallback
05306 
05307   if ( value().isNumber() )
05308   {
05309       if ( isDate() )
05310       {
05311           // serial number of date
05312           QDate dd = value().asDateTime().date();
05313           dataType = "Date";
05314           str = "%1/%2/%3";
05315           str = str.arg(dd.year()).arg(dd.month()).arg(dd.day());
05316       }
05317       else if( isTime() )
05318       {
05319           // serial number of time
05320           dataType = "Time";
05321           str = value().asDateTime().time().toString();
05322       }
05323       else
05324       {
05325           // real number
05326           dataType = "Num";
05327           if (value().isInteger())
05328             str = QString::number(value().asInteger());
05329           else
05330             str = QString::number(value().asFloat(), 'g', DBL_DIG);
05331       }
05332   }
05333 
05334   if ( value().isBoolean() )
05335   {
05336       dataType = "Bool";
05337       str = value().asBoolean() ? "true" : "false";
05338   }
05339 
05340   if ( value().isString() )
05341   {
05342       dataType = "Str";
05343       str = value().asString();
05344   }
05345 
05346   result.setAttribute( "dataType", dataType );
05347   if ( !d->strOutText.isEmpty() )
05348     result.setAttribute( "outStr", d->strOutText );
05349   result.appendChild( doc.createTextNode( str ) );
05350 
05351   return true; /* really isn't much of a way for this function to fail */
05352 }
05353 
05354 void Cell::saveOasisAnnotation( KoXmlWriter &xmlwriter )
05355 {
05356     if ( format()->comment() )
05357     {
05358         //<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">
05359         xmlwriter.startElement( "office:annotation" );
05360         QStringList text = QStringList::split( "\n", *format()->comment() );
05361         for ( QStringList::Iterator it = text.begin(); it != text.end(); ++it ) {
05362             xmlwriter.startElement( "text:p" );
05363             xmlwriter.addTextNode( *it );
05364             xmlwriter.endElement();
05365         }
05366         xmlwriter.endElement();
05367     }
05368 }
05369 
05370 
05371 
05372 QString Cell::saveOasisCellStyle( KoGenStyle &currentCellStyle, KoGenStyles &mainStyles )
05373 {
05374     if ( d->hasExtra() && d->extra()->conditions )
05375     {
05376         // this has to be an automatic style
05377         currentCellStyle = KoGenStyle( Doc::STYLE_CELL_AUTO, "table-cell" );
05378         d->extra()->conditions->saveOasisConditions( currentCellStyle );
05379     }
05380     return format()->saveOasisCellStyle( currentCellStyle, mainStyles );
05381 }
05382 
05383 
05384 bool Cell::saveOasis( KoXmlWriter& xmlwriter, KoGenStyles &mainStyles,
05385                       int row, int column, int &repeated,
05386                       GenValidationStyles &valStyle )
05387 {
05388     if ( !isPartOfMerged() )
05389         xmlwriter.startElement( "table:table-cell" );
05390     else
05391         xmlwriter.startElement( "table:covered-table-cell" );
05392 #if 0
05393     //add font style
05394     QFont font;
05395     Value const value( cell->value() );
05396     if ( !cell->isDefault() )
05397     {
05398       font = cell->format()->textFont( i, row );
05399       m_styles.addFont( font );
05400 
05401       if ( cell->format()->hasProperty( Format::PComment ) )
05402         hasComment = true;
05403     }
05404 #endif
05405     // NOTE save the value before the style as long as the Formatter does not work correctly
05406     if ( link().isEmpty() )
05407       saveOasisValue (xmlwriter);
05408 
05409     KoGenStyle currentCellStyle; // the type determined in saveOasisCellStyle
05410     saveOasisCellStyle( currentCellStyle,mainStyles );
05411     // skip 'table:style-name' attribute for the default style
05412     if ( !currentCellStyle.isDefaultStyle() )
05413       xmlwriter.addAttribute( "table:style-name", mainStyles.styles()[currentCellStyle] );
05414 
05415     // group empty cells with the same style
05416     if ( isEmpty() && !format()->hasProperty( Format::PComment ) &&
05417          !isPartOfMerged() && !doesMergeCells() )
05418     {
05419       bool refCellIsDefault = isDefault();
05420       int j = column + 1;
05421       Cell *nextCell = format()->sheet()->getNextCellRight( column, row );
05422       while ( nextCell )
05423       {
05424         // if
05425         //   the next cell is not the adjacent one
05426         // or
05427         //   the next cell is not empty
05428         if ( nextCell->column() != j || !nextCell->isEmpty() )
05429         {
05430           if ( refCellIsDefault )
05431           {
05432             // if the origin cell was a default cell,
05433             // we count the default cells
05434             repeated = nextCell->column() - j + 1;
05435           }
05436           // otherwise we just stop here to process the adjacent
05437           // cell in the next iteration of the outer loop
05438           // (in Sheet::saveOasisCells)
05439           break;
05440         }
05441 
05442         KoGenStyle nextCellStyle; // the type is determined in saveOasisCellStyle
05443         nextCell->saveOasisCellStyle( nextCellStyle,mainStyles );
05444 
05445         if ( nextCell->isPartOfMerged() || nextCell->doesMergeCells() ||
05446              nextCell->format()->hasProperty( Format::PComment ) ||
05447              !(nextCellStyle == currentCellStyle) )
05448         {
05449           break;
05450         }
05451         ++repeated;
05452         // get the next cell and set the index to the adjacent cell
05453         nextCell = format()->sheet()->getNextCellRight( j++, row );
05454       }
05455       kdDebug() << "Cell::saveOasis: empty cell in column " << column << " "
05456                 << "repeated " << repeated << " time(s)" << endl;
05457 
05458       if ( repeated > 1 )
05459         xmlwriter.addAttribute( "table:number-columns-repeated", QString::number( repeated ) );
05460     }
05461 
05462 
05463     if (d->hasExtra() && d->extra()->validity)
05464     {
05465         GenValidationStyle styleVal(d->extra()->validity);
05466         xmlwriter.addAttribute( "table:validation-name", valStyle.lookup( styleVal ) );
05467     }
05468     if ( isFormula() )
05469     {
05470         //kdDebug() << "Formula found" << endl;
05471       QString formula( convertFormulaToOasisFormat( text() ) );
05472       xmlwriter.addAttribute( "table:formula", formula );
05473     }
05474     else if ( !link().isEmpty() )
05475     {
05476         //kdDebug()<<"Link found \n";
05477         xmlwriter.startElement( "text:p" );
05478         xmlwriter.startElement( "text:a" );
05479         //Reference cell is started by "#"
05480         if ( localReferenceAnchor( link() ) )
05481             xmlwriter.addAttribute( " xlink:href", ( "#"+link() ) );
05482         else
05483             xmlwriter.addAttribute( " xlink:href", link() );
05484         xmlwriter.addTextNode( text() );
05485         xmlwriter.endElement();
05486         xmlwriter.endElement();
05487     }
05488 
05489     if ( doesMergeCells() )
05490     {
05491       int colSpan = mergedXCells() + 1;
05492       int rowSpan = mergedYCells() + 1;
05493 
05494       if ( colSpan > 1 )
05495         xmlwriter.addAttribute( "table:number-columns-spanned", QString::number( colSpan ) );
05496 
05497       if ( rowSpan > 1 )
05498         xmlwriter.addAttribute( "table:number-rows-spanned", QString::number( rowSpan ) );
05499     }
05500 
05501     if ( !isEmpty() && link().isEmpty() )
05502     {
05503         xmlwriter.startElement( "text:p" );
05504         xmlwriter.addTextNode( strOutText().utf8() );
05505         xmlwriter.endElement();
05506     }
05507 
05508     saveOasisAnnotation( xmlwriter );
05509 
05510     xmlwriter.endElement();
05511     return true;
05512 }
05513 
05514 void Cell::saveOasisValue (KoXmlWriter &xmlWriter)
05515 {
05516   switch (value().format())
05517   {
05518     case Value::fmt_None: break;  //NOTHING HERE
05519     case Value::fmt_Boolean:
05520     {
05521       xmlWriter.addAttribute( "office:value-type", "boolean" );
05522       xmlWriter.addAttribute( "office:boolean-value", ( value().asBoolean() ?
05523           "true" : "false" ) );
05524       break;
05525     }
05526     case Value::fmt_Number:
05527     {
05528       xmlWriter.addAttribute( "office:value-type", "float" );
05529       if (value().isInteger())
05530         xmlWriter.addAttribute( "office:value", QString::number( value().asInteger() ) );
05531       else
05532         xmlWriter.addAttribute( "office:value", QString::number( value().asFloat(), 'g', DBL_DIG ) );
05533       break;
05534     }
05535     case Value::fmt_Percent:
05536     {
05537       xmlWriter.addAttribute( "office:value-type", "percentage" );
05538       xmlWriter.addAttribute( "office:value",
05539           QString::number( value().asFloat() ) );
05540       break;
05541     }
05542     case Value::fmt_Money:
05543     {
05544       xmlWriter.addAttribute( "office:value-type", "currency" );
05545       Format::Currency currency;
05546       if (format()->currencyInfo(currency))
05547         xmlWriter.addAttribute( "office:currency", Currency::getCurrencyCode(currency.type) );
05548       xmlWriter.addAttribute( "office:value",
05549           QString::number( value().asFloat() ) );
05550       break;
05551     }
05552     case Value::fmt_DateTime: break;  //NOTHING HERE
05553     case Value::fmt_Date:
05554     {
05555       xmlWriter.addAttribute( "office:value-type", "date" );
05556       xmlWriter.addAttribute( "office:date-value",
05557           value().asDate().toString( Qt::ISODate ) );
05558       break;
05559     }
05560     case Value::fmt_Time:
05561     {
05562       xmlWriter.addAttribute( "office:value-type", "time" );
05563       xmlWriter.addAttribute( "office:time-value",
05564           value().asTime().toString( "PThhHmmMssS" ) );
05565       break;
05566     }
05567     case Value::fmt_String:
05568     {
05569       xmlWriter.addAttribute( "office:value-type", "string" );
05570       xmlWriter.addAttribute( "office:string-value", value().asString() );
05571       break;
05572     }
05573   };
05574 }
05575 
05576 QString Cell::convertFormulaToOasisFormat( const QString & formula ) const
05577 {
05578     QString s;
05579     QRegExp exp("(\\$?)([a-zA-Z]+)(\\$?)([0-9]+)");
05580     int n = exp.search( formula, 0 );
05581     kdDebug() << "Exp: " << formula << ", n: " << n << ", Length: " << formula.length()
05582               << ", Matched length: " << exp.matchedLength() << endl;
05583 
05584     bool inQuote1 = false;
05585     bool inQuote2 = false;
05586     int i = 0;
05587     int l = (int) formula.length();
05588     if ( l <= 0 )
05589         return formula;
05590     while ( i < l )
05591     {
05592         if ( ( n != -1 ) && ( n < i ) )
05593         {
05594             n = exp.search( formula, i );
05595             kdDebug() << "Exp: " << formula.right( l - i ) << ", n: " << n << endl;
05596         }
05597         if ( formula[i] == '"' )
05598         {
05599             inQuote1 = !inQuote1;
05600             s += formula[i];
05601             ++i;
05602             continue;
05603         }
05604         if ( formula[i] == '\'' )
05605         {
05606             // named area
05607             inQuote2 = !inQuote2;
05608             ++i;
05609             continue;
05610         }
05611         if ( inQuote1 || inQuote2 )
05612         {
05613             s += formula[i];
05614             ++i;
05615             continue;
05616         }
05617         if ( ( formula[i] == '=' ) && ( formula[i + 1] == '=' ) )
05618         {
05619             s += '=';
05620             ++i;++i;
05621             continue;
05622         }
05623         if ( formula[i] == '!' )
05624         {
05625             insertBracket( s );
05626             s += '.';
05627             ++i;
05628             continue;
05629         }
05630         if ( formula[i] == ',' )
05631         {
05632             s += '.';
05633             ++i;
05634             continue;
05635         }
05636         if ( n == i )
05637         {
05638             int ml = exp.matchedLength();
05639             if ( formula[ i + ml ] == '!' )
05640             {
05641                 kdDebug() << "No cell ref but sheet name" << endl;
05642                 s += formula[i];
05643                 ++i;
05644                 continue;
05645             }
05646             if ( ( i > 0 ) && ( formula[i - 1] != '!' ) )
05647                 s += "[.";
05648             for ( int j = 0; j < ml; ++j )
05649             {
05650                 s += formula[i];
05651                 ++i;
05652             }
05653             s += ']';
05654             continue;
05655         }
05656 
05657         s += formula[i];
05658         ++i;
05659     }
05660 
05661     return s;
05662 }
05663 
05664 void Cell::loadOasisConditional( QDomElement * style )
05665 {
05666     if ( style )//safe
05667     {
05668         QDomElement e;
05669         forEachElement( e, style->toElement() )
05670         {
05671             if ( e.localName() == "map" && e.namespaceURI() == KoXmlNS::style )
05672             {
05673                 if (d->hasExtra())
05674                     delete d->extra()->conditions;
05675                 d->extra()->conditions = new Conditions( this );
05676                 d->extra()->conditions->loadOasisConditions( e );
05677                 d->extra()->conditions->checkMatches();
05678                 // break here
05679                 // Conditions::loadOasisConditions finishes the iteration
05680                 break;
05681             }
05682         }
05683     }
05684 }
05685 
05686 bool Cell::loadOasis( const QDomElement& element , KoOasisLoadingContext& oasisContext , Style* style )
05687 {
05688     kdDebug() << "*** Loading cell properties ***** at " << column() << "," << row () << endl;
05689 
05690     if ( element.hasAttributeNS( KoXmlNS::table, "style-name" ) )
05691     {
05692         kdDebug()<<" table:style-name: "<<element.attributeNS( KoXmlNS::table, "style-name", QString::null )<<endl;
05693         oasisContext.fillStyleStack( element, KoXmlNS::table, "styleName", "table-cell" );
05694 
05695         QString str = element.attributeNS( KoXmlNS::table, "style-name", QString::null );
05696         const QDomElement* cellStyle = oasisContext.oasisStyles().findStyle( str, "table-cell" );
05697 
05698         if ( cellStyle )
05699             loadOasisConditional( const_cast<QDomElement *>( cellStyle ) );
05700     }
05701 
05702     if (style)
05703     {
05704         format()->setStyle( style );
05705     }
05706 
05707     //Search and load each paragraph of text. Each paragraph is separated by a line break.
05708     loadOasisCellText( element );
05709 
05710     //
05711     // formula
05712     //
05713     bool isFormula = false;
05714     if ( element.hasAttributeNS( KoXmlNS::table, "formula" ) )
05715     {
05716         kdDebug()<<" formula :"<<element.attributeNS( KoXmlNS::table, "formula", QString::null )<<endl;
05717         isFormula = true;
05718         QString oasisFormula( element.attributeNS( KoXmlNS::table, "formula", QString::null ) );
05719         //necessary to remove it to load formula from oocalc2.0 (use namespace)
05720         if (oasisFormula.startsWith( "oooc:" ) )
05721             oasisFormula= oasisFormula.mid( 5 );
05722         else if (oasisFormula.startsWith( "kspr:" ) )
05723             oasisFormula= oasisFormula.mid( 5 );
05724         // TODO Stefan: merge this into Oasis::decodeFormula
05725         checkForNamedAreas( oasisFormula );
05726         oasisFormula = Oasis::decodeFormula( oasisFormula, locale() );
05727         setCellText( oasisFormula );
05728     }
05729     else if ( d->strText.at(0) == '=' ) //prepend ' to the text to avoid = to be painted
05730         d->strText.prepend('\'');
05731 
05732     //
05733     // validation
05734     //
05735     if ( element.hasAttributeNS( KoXmlNS::table, "validation-name" ) )
05736     {
05737         kdDebug()<<" validation-name: "<<element.attributeNS( KoXmlNS::table, "validation-name", QString::null )<<endl;
05738         loadOasisValidation( element.attributeNS( KoXmlNS::table, "validation-name", QString::null ) );
05739     }
05740 
05741     //
05742     // value type
05743     //
05744     if( element.hasAttributeNS( KoXmlNS::office, "value-type" ) )
05745     {
05746         QString valuetype = element.attributeNS( KoXmlNS::office, "value-type", QString::null );
05747         kdDebug()<<"  value-type: " << valuetype << endl;
05748         if( valuetype == "boolean" )
05749         {
05750           QString val = element.attributeNS( KoXmlNS::office, "boolean-value", QString::null ).lower();
05751             if( ( val == "true" ) || ( val == "false" ) )
05752             {
05753                 bool value = val == "true";
05754                 setCellValue( value );
05755             }
05756         }
05757 
05758         // integer and floating-point value
05759         else if( valuetype == "float" )
05760         {
05761             bool ok = false;
05762             double value = element.attributeNS( KoXmlNS::office, "value", QString::null ).toDouble( &ok );
05763             if( ok )
05764                 setCellValue( value );
05765 
05766             if ( !isFormula && d->strText.isEmpty())
05767             {
05768                 QString str = locale()->formatNumber( value, 15 );
05769                 setCellText( str );
05770             }
05771         }
05772 
05773         // currency value
05774         else if( valuetype == "currency" )
05775         {
05776             bool ok = false;
05777             double value = element.attributeNS( KoXmlNS::office, "value", QString::null ).toDouble( &ok );
05778             if( ok )
05779             {
05780                 setCellValue( value, Money_format );
05781 
05782                 if (element.hasAttributeNS( KoXmlNS::office, "currency" ) )
05783                 {
05784                   Currency currency(element.attributeNS( KoXmlNS::office, "currency", QString::null ) );
05785                   format()->setCurrency( currency.getIndex(), currency.getDisplayCode() );
05786                 }
05787             }
05788         }
05789         else if( valuetype == "percentage" )
05790         {
05791             bool ok = false;
05792             double v = element.attributeNS( KoXmlNS::office, "value", QString::null ).toDouble( &ok );
05793             if( ok )
05794             {
05795                 Value value;
05796                 value.setValue (v);
05797                 value.setFormat (Value::fmt_Percent);
05798                 setCellValue( value );
05799 
05800                 if ( !isFormula && d->strText.isEmpty())
05801                 {
05802                     QString str = locale()->formatNumber( v, 15 );
05803                     setCellText( str );
05804                 }
05805 
05806                 format()->setFormatType (Percentage_format);
05807             }
05808         }
05809         else if ( valuetype == "date" )
05810         {
05811             QString value = element.attributeNS( KoXmlNS::office, "value", QString::null );
05812             if ( value.isEmpty() )
05813                 value = element.attributeNS( KoXmlNS::office, "date-value", QString::null );
05814             kdDebug() << "Type: date, value: " << value << endl;
05815 
05816             // "1980-10-15"
05817             int year = 0, month = 0, day = 0;
05818             bool ok = false;
05819 
05820             int p1 = value.find( '-' );
05821             if ( p1 > 0 )
05822                 year  = value.left( p1 ).toInt( &ok );
05823 
05824             kdDebug() << "year: " << value.left( p1 ) << endl;
05825 
05826             int p2 = value.find( '-', ++p1 );
05827 
05828             if ( ok )
05829                 month = value.mid( p1, p2 - p1  ).toInt( &ok );
05830 
05831             kdDebug() << "month: " << value.mid( p1, p2 - p1 ) << endl;
05832 
05833             if ( ok )
05834                 day = value.right( value.length() - p2 - 1 ).toInt( &ok );
05835 
05836             kdDebug() << "day: " << value.right( value.length() - p2 ) << endl;
05837 
05838             if ( ok )
05839             {
05840                 setCellValue( QDate( year, month, day ) );
05841                 if ( style )
05842                     format()->setFormatType (style->formatType());
05843                 kdDebug() << "Set QDate: " << year << " - " << month << " - " << day << endl;
05844             }
05845 
05846         }
05847         else if ( valuetype == "time" )
05848         {
05849             QString value = element.attributeNS( KoXmlNS::office, "value", QString::null );
05850             if ( value.isEmpty() )
05851                 value = element.attributeNS( KoXmlNS::office, "time-value", QString::null );
05852             kdDebug() << "Type: time: " << value << endl;
05853             // "PT15H10M12S"
05854             int hours = 0, minutes = 0, seconds = 0;
05855             int l = value.length();
05856             QString num;
05857             bool ok = false;
05858             for ( int i = 0; i < l; ++i )
05859             {
05860                 if ( value[i].isNumber() )
05861                 {
05862                     num += value[i];
05863                     continue;
05864                 }
05865                 else if ( value[i] == 'H' )
05866                     hours   = num.toInt( &ok );
05867                 else if ( value[i] == 'M' )
05868                     minutes = num.toInt( &ok );
05869                 else if ( value[i] == 'S' )
05870                     seconds = num.toInt( &ok );
05871                 else
05872                     continue;
05873 
05874                 kdDebug() << "Num: " << num << endl;
05875 
05876                 num = "";
05877                 if ( !ok )
05878                     break;
05879             }
05880             kdDebug() << "Hours: " << hours << ", " << minutes << ", " << seconds << endl;
05881 
05882             if ( ok )
05883             {
05884                 // Value kval( timeToNum( hours, minutes, seconds ) );
05885                 // cell->setValue( kval );
05886                 setCellValue( QTime( hours % 24, minutes, seconds ) );
05887                 if ( style )
05888                     format()->setFormatType (style->formatType());
05889             }
05890         }
05891         else if( valuetype == "string" )
05892         {
05893             QString value = element.attributeNS( KoXmlNS::office, "value", QString::null );
05894             if ( value.isEmpty() && element.hasAttributeNS( KoXmlNS::office, "string-value" ))
05895             {
05896                 //if there is not string-value entry don't overwrite value stored into <text:p>
05897                 value = element.attributeNS( KoXmlNS::office, "string-value", QString::null );
05898                 setCellValue( value );
05899             }
05900             format()->setFormatType (Text_format);
05901         }
05902         else
05903             kdDebug()<<" type of value found : "<<valuetype<<endl;
05904     }
05905 
05906     //
05907     // merged cells ?
05908     //
05909     int colSpan = 1;
05910     int rowSpan = 1;
05911     if ( element.hasAttributeNS( KoXmlNS::table, "number-columns-spanned" ) )
05912     {
05913         bool ok = false;
05914         int span = element.attributeNS( KoXmlNS::table, "number-columns-spanned", QString::null ).toInt( &ok );
05915         if( ok ) colSpan = span;
05916     }
05917     if ( element.hasAttributeNS( KoXmlNS::table, "number-rows-spanned" ) )
05918     {
05919         bool ok = false;
05920         int span = element.attributeNS( KoXmlNS::table, "number-rows-spanned", QString::null ).toInt( &ok );
05921         if( ok ) rowSpan = span;
05922     }
05923     if ( colSpan > 1 || rowSpan > 1 )
05924         mergeCells( d->column, d->row, colSpan - 1, rowSpan - 1 );
05925 
05926     //
05927     // cell comment/annotation
05928     //
05929     QDomElement annotationElement = KoDom::namedItemNS( element, KoXmlNS::office, "annotation" );
05930     if ( !annotationElement.isNull() )
05931     {
05932         QString comment;
05933         QDomNode node = annotationElement.firstChild();
05934         while( !node.isNull() )
05935         {
05936             QDomElement commentElement = node.toElement();
05937             if( !commentElement.isNull() )
05938                 if( commentElement.localName() == "p" && commentElement.namespaceURI() == KoXmlNS::text )
05939                 {
05940                     if( !comment.isEmpty() ) comment.append( '\n' );
05941                     comment.append( commentElement.text() );
05942                 }
05943 
05944             node = node.nextSibling();
05945         }
05946 
05947         if( !comment.isEmpty() )
05948             format()->setComment( comment );
05949     }
05950 
05951     QDomElement frame = KoDom::namedItemNS( element, KoXmlNS::draw, "frame" );
05952     if ( !frame.isNull() )
05953       loadOasisObjects( frame, oasisContext );
05954 
05955     if (isFormula)
05956       setCalcDirtyFlag ();   // formulas must be recalculated
05957 
05958     return true;
05959 }
05960 
05961 void Cell::loadOasisCellText( const QDomElement& parent )
05962 {
05963     //Search and load each paragraph of text. Each paragraph is separated by a line break
05964     QDomElement textParagraphElement;
05965     QString cellText;
05966 
05967     bool multipleTextParagraphsFound=false;
05968 
05969     forEachElement( textParagraphElement , parent )
05970     {
05971         if ( textParagraphElement.localName()=="p" &&
05972              textParagraphElement.namespaceURI()== KoXmlNS::text )
05973         {
05974             // our text, could contain formating for value or result of formul
05975             if (cellText.isEmpty())
05976                 cellText = textParagraphElement.text();
05977             else
05978             {
05979                 cellText += "\n"+textParagraphElement.text();
05980                 multipleTextParagraphsFound=true;
05981             }
05982 
05983             QDomElement textA = KoDom::namedItemNS( textParagraphElement, KoXmlNS::text, "a" );
05984             if( !textA.isNull() )
05985             {
05986                 if ( textA.hasAttributeNS( KoXmlNS::xlink, "href" ) )
05987                 {
05988                     QString link = textA.attributeNS( KoXmlNS::xlink, "href", QString::null );
05989                     cellText = textA.text();
05990                     setCellText( cellText );
05991                     setValue( cellText );
05992                     if ( link[0]=='#' )
05993                         link=link.remove( 0, 1 );
05994                     setLink( link );
05995                 }
05996             }
05997         }
05998     }
05999 
06000     if (!cellText.isNull())
06001     {
06002         setCellText( cellText );
06003         setValue( cellText );
06004     }
06005 
06006     //Enable word wrapping if multiple lines of text have been found.
06007     if ( multipleTextParagraphsFound )
06008     {
06009         format()->setMultiRow(true);
06010     }
06011 }
06012 
06013 void Cell::loadOasisObjects( const QDomElement &parent, KoOasisLoadingContext& oasisContext )
06014 {
06015     for( QDomElement e = parent; !e.isNull(); e = e.nextSibling().toElement() )
06016     {
06017         if ( e.localName() == "frame" && e.namespaceURI() == KoXmlNS::draw )
06018         {
06019           EmbeddedObject *obj = 0;
06020           QDomNode object = KoDom::namedItemNS( e, KoXmlNS::draw, "object" );
06021           if ( !object.isNull() )
06022           {
06023             if ( !object.toElement().attributeNS( KoXmlNS::draw, "notify-on-update-of-ranges", QString::null).isNull() )
06024               obj = new EmbeddedChart( sheet()->doc(), sheet() );
06025             else
06026               obj = new EmbeddedKOfficeObject( sheet()->doc(), sheet() );
06027           }
06028           else
06029           {
06030             QDomNode image = KoDom::namedItemNS( e, KoXmlNS::draw, "image" );
06031             if ( !image.isNull() )
06032               obj = new EmbeddedPictureObject( sheet(), sheet()->doc()->pictureCollection() );
06033             else
06034               kdDebug() << "Object type wasn't loaded!" << endl;
06035           }
06036 
06037           if ( obj )
06038           {
06039             obj->loadOasis( e, oasisContext );
06040             sheet()->doc()->insertObject( obj );
06041 
06042             QString ref = e.attributeNS( KoXmlNS::table, "end-cell-address", QString::null );
06043             if ( ref.isNull() )
06044               continue;
06045 
06046             ref = Oasis::decodeFormula( ref );
06047             Point point( ref );
06048             if ( !point.isValid() )
06049               continue;
06050 
06051             KoRect geometry = obj->geometry();
06052             geometry.setLeft( geometry.left() + sheet()->columnPos( d->column, 0 ) );
06053             geometry.setTop( geometry.top() + sheet()->rowPos( d->row, 0 ) );
06054 
06055             QString str = e.attributeNS( KoXmlNS::table, "end-x", QString::null );
06056             if ( !str.isNull() )
06057             {
06058               uint end_x = (uint) KoUnit::parseValue( str );
06059               geometry.setRight( sheet()->columnPos( point.column(), 0) + end_x );
06060             }
06061 
06062             str = e.attributeNS( KoXmlNS::table, "end-y", QString::null );
06063             if ( !str.isNull() )
06064             {
06065               uint end_y = (uint) KoUnit::parseValue( str );
06066               geometry.setBottom( sheet()->rowPos( point.row(), 0) + end_y );
06067             }
06068 
06069             obj->setGeometry( geometry );
06070           }
06071         }
06072     }
06073 }
06074 
06075 void Cell::loadOasisValidation( const QString& validationName )
06076 {
06077     QDomElement element = sheet()->doc()->loadingInfo()->validation( validationName);
06078     if (d->hasExtra())
06079       delete d->extra()->validity;
06080     d->extra()->validity = new Validity;
06081     if ( element.hasAttributeNS( KoXmlNS::table, "condition" ) )
06082     {
06083         QString valExpression = element.attributeNS( KoXmlNS::table, "condition", QString::null );
06084         kdDebug()<<" element.attribute( table:condition ) "<<valExpression<<endl;
06085         //Condition ::= ExtendedTrueCondition | TrueFunction 'and' TrueCondition
06086         //TrueFunction ::= cell-content-is-whole-number() | cell-content-is-decimal-number() | cell-content-is-date() | cell-content-is-time()
06087         //ExtendedTrueCondition ::= ExtendedGetFunction | cell-content-text-length() Operator Value
06088         //TrueCondition ::= GetFunction | cell-content() Operator Value
06089         //GetFunction ::= cell-content-is-between(Value, Value) | cell-content-is-not-between(Value, Value)
06090         //ExtendedGetFunction ::= cell-content-text-length-is-between(Value, Value) | cell-content-text-length-is-not-between(Value, Value)
06091         //Operator ::= '<' | '>' | '<=' | '>=' | '=' | '!='
06092         //Value ::= NumberValue | String | Formula
06093         //A Formula is a formula without an equals (=) sign at the beginning. See section 8.1.3 for more information.
06094         //A String comprises one or more characters surrounded by quotation marks.
06095         //A NumberValue is a whole or decimal number. It must not contain comma separators for numbers of 1000 or greater.
06096 
06097         //ExtendedTrueCondition
06098         if ( valExpression.contains( "cell-content-text-length()" ) )
06099         {
06100             //"cell-content-text-length()>45"
06101             valExpression = valExpression.remove("oooc:cell-content-text-length()" );
06102             kdDebug()<<" valExpression = :"<<valExpression<<endl;
06103             d->extra()->validity->m_restriction = Restriction::TextLength;
06104 
06105             loadOasisValidationCondition( valExpression );
06106         }
06107         else if ( valExpression.contains( "cell-content-is-text()" ) )
06108         {
06109             d->extra()->validity->m_restriction = Restriction::Text;
06110         }
06111         //cell-content-text-length-is-between(Value, Value) | cell-content-text-length-is-not-between(Value, Value) | cell-content-is-in-list( StringList )
06112         else if ( valExpression.contains( "cell-content-text-length-is-between" ) )
06113         {
06114             d->extra()->validity->m_restriction = Restriction::TextLength;
06115             d->extra()->validity->m_cond = Conditional::Between;
06116             valExpression = valExpression.remove( "oooc:cell-content-text-length-is-between(" );
06117             kdDebug()<<" valExpression :"<<valExpression<<endl;
06118             valExpression = valExpression.remove( ")" );
06119             QStringList listVal = QStringList::split( ",", valExpression );
06120             loadOasisValidationValue( listVal );
06121         }
06122         else if ( valExpression.contains( "cell-content-text-length-is-not-between" ) )
06123         {
06124             d->extra()->validity->m_restriction = Restriction::TextLength;
06125             d->extra()->validity->m_cond = Conditional::Different;
06126             valExpression = valExpression.remove( "oooc:cell-content-text-length-is-not-between(" );
06127             kdDebug()<<" valExpression :"<<valExpression<<endl;
06128             valExpression = valExpression.remove( ")" );
06129             kdDebug()<<" valExpression :"<<valExpression<<endl;
06130             QStringList listVal = QStringList::split( ",", valExpression );
06131             loadOasisValidationValue( listVal );
06132         }
06133         else if ( valExpression.contains( "cell-content-is-in-list(" ) )
06134         {
06135             d->extra()->validity->m_restriction = Restriction::List;
06136             valExpression = valExpression.remove( "oooc:cell-content-is-in-list(" );
06137             kdDebug()<<" valExpression :"<<valExpression<<endl;
06138             valExpression = valExpression.remove( ")" );
06139             d->extra()->validity->listValidity = QStringList::split( ";", valExpression );
06140 
06141         }
06142         //TrueFunction ::= cell-content-is-whole-number() | cell-content-is-decimal-number() | cell-content-is-date() | cell-content-is-time()
06143         else
06144         {
06145             if (valExpression.contains( "cell-content-is-whole-number()" ) )
06146             {
06147                 d->extra()->validity->m_restriction =  Restriction::Number;
06148                 valExpression = valExpression.remove( "oooc:cell-content-is-whole-number() and " );
06149             }
06150             else if (valExpression.contains( "cell-content-is-decimal-number()" ) )
06151             {
06152                 d->extra()->validity->m_restriction = Restriction::Integer;
06153                 valExpression = valExpression.remove( "oooc:cell-content-is-decimal-number() and " );
06154             }
06155             else if (valExpression.contains( "cell-content-is-date()" ) )
06156             {
06157                 d->extra()->validity->m_restriction = Restriction::Date;
06158                 valExpression = valExpression.remove( "oooc:cell-content-is-date() and " );
06159             }
06160             else if (valExpression.contains( "cell-content-is-time()" ) )
06161             {
06162                 d->extra()->validity->m_restriction = Restriction::Time;
06163                 valExpression = valExpression.remove( "oooc:cell-content-is-time() and " );
06164             }
06165             kdDebug()<<"valExpression :"<<valExpression<<endl;
06166 
06167             if ( valExpression.contains( "cell-content()" ) )
06168             {
06169                 valExpression = valExpression.remove( "cell-content()" );
06170                 loadOasisValidationCondition( valExpression );
06171             }
06172             //GetFunction ::= cell-content-is-between(Value, Value) | cell-content-is-not-between(Value, Value)
06173             //for the moment we support just int/double value, not text/date/time :(
06174             if ( valExpression.contains( "cell-content-is-between(" ) )
06175             {
06176                 valExpression = valExpression.remove( "cell-content-is-between(" );
06177                 valExpression = valExpression.remove( ")" );
06178                 QStringList listVal = QStringList::split( "," , valExpression );
06179                 loadOasisValidationValue( listVal );
06180                 d->extra()->validity->m_cond = Conditional::Between;
06181             }
06182             if ( valExpression.contains( "cell-content-is-not-between(" ) )
06183             {
06184                 valExpression = valExpression.remove( "cell-content-is-not-between(" );
06185                 valExpression = valExpression.remove( ")" );
06186                 QStringList listVal = QStringList::split( ",", valExpression );
06187                 loadOasisValidationValue( listVal );
06188                 d->extra()->validity->m_cond = Conditional::Different;
06189             }
06190         }
06191     }
06192     if ( element.hasAttributeNS( KoXmlNS::table, "allow-empty-cell" ) )
06193     {
06194         kdDebug()<<" element.hasAttribute( table:allow-empty-cell ) :"<<element.hasAttributeNS( KoXmlNS::table, "allow-empty-cell" )<<endl;
06195         d->extra()->validity->allowEmptyCell = ( ( element.attributeNS( KoXmlNS::table, "allow-empty-cell", QString::null )=="true" ) ? true : false );
06196     }
06197     if ( element.hasAttributeNS( KoXmlNS::table, "base-cell-address" ) )
06198     {
06199         //todo what is it ?
06200     }
06201 
06202     QDomElement help = KoDom::namedItemNS( element, KoXmlNS::table, "help-message" );
06203     if ( !help.isNull() )
06204     {
06205         if ( help.hasAttributeNS( KoXmlNS::table, "title" ) )
06206         {
06207             kdDebug()<<"help.attribute( table:title ) :"<<help.attributeNS( KoXmlNS::table, "title", QString::null )<<endl;
06208             d->extra()->validity->titleInfo = help.attributeNS( KoXmlNS::table, "title", QString::null );
06209         }
06210         if ( help.hasAttributeNS( KoXmlNS::table, "display" ) )
06211         {
06212             kdDebug()<<"help.attribute( table:display ) :"<<help.attributeNS( KoXmlNS::table, "display", QString::null )<<endl;
06213             d->extra()->validity->displayValidationInformation = ( ( help.attributeNS( KoXmlNS::table, "display", QString::null )=="true" ) ? true : false );
06214         }
06215         QDomElement attrText = KoDom::namedItemNS( help, KoXmlNS::text, "p" );
06216         if ( !attrText.isNull() )
06217         {
06218             kdDebug()<<"help text :"<<attrText.text()<<endl;
06219             d->extra()->validity->messageInfo = attrText.text();
06220         }
06221     }
06222 
06223     QDomElement error = KoDom::namedItemNS( element, KoXmlNS::table, "error-message" );
06224     if ( !error.isNull() )
06225     {
06226         if ( error.hasAttributeNS( KoXmlNS::table, "title" ) )
06227             d->extra()->validity->title = error.attributeNS( KoXmlNS::table, "title", QString::null );
06228         if ( error.hasAttributeNS( KoXmlNS::table, "message-type" ) )
06229         {
06230             QString str = error.attributeNS( KoXmlNS::table, "message-type", QString::null );
06231             if ( str == "warning" )
06232               d->extra()->validity->m_action = Action::Warning;
06233             else if ( str == "information" )
06234               d->extra()->validity->m_action = Action::Information;
06235             else if ( str == "stop" )
06236               d->extra()->validity->m_action = Action::Stop;
06237             else
06238                 kdDebug()<<"validation : message type unknown  :"<<str<<endl;
06239         }
06240 
06241         if ( error.hasAttributeNS( KoXmlNS::table, "display" ) )
06242         {
06243             kdDebug()<<" display message :"<<error.attributeNS( KoXmlNS::table, "display", QString::null )<<endl;
06244             d->extra()->validity->displayMessage = (error.attributeNS( KoXmlNS::table, "display", QString::null )=="true");
06245         }
06246         QDomElement attrText = KoDom::namedItemNS( error, KoXmlNS::text, "p" );
06247         if ( !attrText.isNull() )
06248             d->extra()->validity->message = attrText.text();
06249     }
06250 }
06251 
06252 
06253 void Cell::loadOasisValidationValue( const QStringList &listVal )
06254 {
06255     bool ok = false;
06256     kdDebug()<<" listVal[0] :"<<listVal[0]<<" listVal[1] :"<<listVal[1]<<endl;
06257 
06258     if ( d->extra()->validity->m_restriction == Restriction::Date )
06259     {
06260         d->extra()->validity->dateMin = QDate::fromString( listVal[0] );
06261         d->extra()->validity->dateMax = QDate::fromString( listVal[1] );
06262     }
06263     else if ( d->extra()->validity->m_restriction == Restriction::Time )
06264     {
06265         d->extra()->validity->timeMin = QTime::fromString( listVal[0] );
06266         d->extra()->validity->timeMax = QTime::fromString( listVal[1] );
06267     }
06268     else
06269     {
06270         d->extra()->validity->valMin = listVal[0].toDouble(&ok);
06271         if ( !ok )
06272         {
06273             d->extra()->validity->valMin = listVal[0].toInt(&ok);
06274             if ( !ok )
06275                 kdDebug()<<" Try to parse this value :"<<listVal[0]<<endl;
06276 
06277 #if 0
06278             if ( !ok )
06279                 d->extra()->validity->valMin = listVal[0];
06280 #endif
06281         }
06282         ok=false;
06283         d->extra()->validity->valMax = listVal[1].toDouble(&ok);
06284         if ( !ok )
06285         {
06286             d->extra()->validity->valMax = listVal[1].toInt(&ok);
06287             if ( !ok )
06288                 kdDebug()<<" Try to parse this value :"<<listVal[1]<<endl;
06289 
06290 #if 0
06291             if ( !ok )
06292                 d->extra()->validity->valMax = listVal[1];
06293 #endif
06294         }
06295     }
06296 }
06297 
06298 void Cell::loadOasisValidationCondition( QString &valExpression )
06299 {
06300     QString value;
06301     if (valExpression.find( "<=" )==0 )
06302     {
06303         value = valExpression.remove( 0,2 );
06304         d->extra()->validity->m_cond = Conditional::InferiorEqual;
06305     }
06306     else if (valExpression.find( ">=" )==0 )
06307     {
06308         value = valExpression.remove( 0,2 );
06309         d->extra()->validity->m_cond = Conditional::SuperiorEqual;
06310     }
06311     else if (valExpression.find( "!=" )==0 )
06312     {
06313         //add Differentto attribute
06314         value = valExpression.remove( 0,2 );
06315         d->extra()->validity->m_cond = Conditional::DifferentTo;
06316     }
06317     else if ( valExpression.find( "<" )==0 )
06318     {
06319         value = valExpression.remove( 0,1 );
06320         d->extra()->validity->m_cond = Conditional::Inferior;
06321     }
06322     else if(valExpression.find( ">" )==0 )
06323     {
06324         value = valExpression.remove( 0,1 );
06325         d->extra()->validity->m_cond = Conditional::Superior;
06326     }
06327     else if (valExpression.find( "=" )==0 )
06328     {
06329         value = valExpression.remove( 0,1 );
06330         d->extra()->validity->m_cond = Conditional::Equal;
06331     }
06332     else
06333         kdDebug()<<" I don't know how to parse it :"<<valExpression<<endl;
06334     if ( d->extra()->validity->m_restriction == Restriction::Date )
06335     {
06336         d->extra()->validity->dateMin = QDate::fromString( value );
06337     }
06338     else if (d->extra()->validity->m_restriction == Restriction::Date )
06339     {
06340         d->extra()->validity->timeMin = QTime::fromString( value );
06341     }
06342     else
06343     {
06344         bool ok = false;
06345         d->extra()->validity->valMin = value.toDouble(&ok);
06346         if ( !ok )
06347         {
06348             d->extra()->validity->valMin = value.toInt(&ok);
06349             if ( !ok )
06350                 kdDebug()<<" Try to parse this value :"<<value<<endl;
06351 
06352 #if 0
06353             if ( !ok )
06354                 d->extra()->validity->valMin = value;
06355 #endif
06356         }
06357     }
06358 }
06359 
06360 
06361 bool Cell::load( const QDomElement & cell, int _xshift, int _yshift,
06362                  Paste::Mode pm, Paste::Operation op, bool paste )
06363 {
06364     bool ok;
06365 
06366     //
06367     // First of all determine in which row and column this
06368     // cell belongs.
06369     //
06370     d->row = cell.attribute( "row" ).toInt( &ok ) + _yshift;
06371     if ( !ok ) return false;
06372     d->column = cell.attribute( "column" ).toInt( &ok ) + _xshift;
06373     if ( !ok ) return false;
06374 
06375     // Validation
06376     if ( d->row < 1 || d->row > KS_rowMax )
06377     {
06378         kdDebug(36001) << "Cell::load: Value out of Range Cell:row=" << d->row << endl;
06379         return false;
06380     }
06381     if ( d->column < 1 || d->column > KS_colMax )
06382     {
06383         kdDebug(36001) << "Cell::load: Value out of Range Cell:column=" << d->column << endl;
06384         return false;
06385     }
06386 
06387     //
06388     // Load formatting information.
06389     //
06390     QDomElement f = cell.namedItem( "format" ).toElement();
06391     if ( !f.isNull()
06392           && ( (pm == Paste::Normal) || (pm == Paste::Format) || (pm == Paste::NoBorder) ) )
06393     {
06394         // send pm parameter. Didn't load Borders if pm==NoBorder
06395 
06396       if ( !format()->load( f, pm, paste ) )
06397             return false;
06398 
06399         if ( f.hasAttribute( "colspan" ) )
06400         {
06401             int i = f.attribute("colspan").toInt( &ok );
06402             if ( !ok ) return false;
06403             // Validation
06404             if ( i < 0 || i > KS_spanMax )
06405             {
06406                 kdDebug(36001) << "Value out of range Cell::colspan=" << i << endl;
06407                 return false;
06408             }
06409             if (i || d->hasExtra())
06410               d->extra()->extraXCells = i;
06411             if ( i > 0 )
06412             {
06413               setFlag(Flag_Merged);
06414             }
06415         }
06416 
06417         if ( f.hasAttribute( "rowspan" ) )
06418         {
06419             int i = f.attribute("rowspan").toInt( &ok );
06420             if ( !ok ) return false;
06421             // Validation
06422             if ( i < 0 || i > KS_spanMax )
06423             {
06424                 kdDebug(36001) << "Value out of range Cell::rowspan=" << i << endl;
06425                 return false;
06426             }
06427             if (i || d->hasExtra())
06428               d->extra()->extraYCells = i;
06429             if ( i > 0 )
06430             {
06431               setFlag(Flag_Merged);
06432             }
06433         }
06434 
06435         if ( testFlag( Flag_Merged ) )
06436         {
06437             if (d->hasExtra())
06438               mergeCells( d->column, d->row, d->extra()->extraXCells, d->extra()->extraYCells );
06439         }
06440 
06441     }
06442 
06443     //
06444     // Load the condition section of a cell.
06445     //
06446     QDomElement conditionsElement = cell.namedItem( "condition" ).toElement();
06447     if ( !conditionsElement.isNull())
06448     {
06449       if (d->hasExtra())
06450         delete d->extra()->conditions;
06451       d->extra()->conditions = new Conditions( this );
06452       d->extra()->conditions->loadConditions( conditionsElement );
06453       d->extra()->conditions->checkMatches();
06454     }
06455     else if ((pm == Paste::Normal) || (pm == Paste::NoBorder))
06456     {
06457       //clear the conditional formatting
06458       if (d->hasExtra())
06459       {
06460         delete d->extra()->conditions;
06461         d->extra()->conditions = 0;
06462       }
06463     }
06464 
06465     QDomElement validity = cell.namedItem( "validity" ).toElement();
06466     if ( !validity.isNull())
06467     {
06468         QDomElement param = validity.namedItem( "param" ).toElement();
06469         if(!param.isNull())
06470         {
06471           d->extra()->validity = new Validity;
06472           if ( param.hasAttribute( "cond" ) )
06473           {
06474             d->extra()->validity->m_cond = (Conditional::Type) param.attribute("cond").toInt( &ok );
06475             if ( !ok )
06476               return false;
06477           }
06478           if ( param.hasAttribute( "action" ) )
06479           {
06480             d->extra()->validity->m_action = (Action::Type) param.attribute("action").toInt( &ok );
06481             if ( !ok )
06482               return false;
06483           }
06484           if ( param.hasAttribute( "allow" ) )
06485           {
06486             d->extra()->validity->m_restriction = (Restriction::Type) param.attribute("allow").toInt( &ok );
06487             if ( !ok )
06488               return false;
06489           }
06490           if ( param.hasAttribute( "valmin" ) )
06491           {
06492             d->extra()->validity->valMin = param.attribute("valmin").toDouble( &ok );
06493             if ( !ok )
06494               return false;
06495           }
06496           if ( param.hasAttribute( "valmax" ) )
06497           {
06498             d->extra()->validity->valMax = param.attribute("valmax").toDouble( &ok );
06499             if ( !ok )
06500               return false;
06501           }
06502           if ( param.hasAttribute( "displaymessage" ) )
06503           {
06504               d->extra()->validity->displayMessage = ( bool )param.attribute("displaymessage").toInt();
06505           }
06506           if ( param.hasAttribute( "displayvalidationinformation" ) )
06507           {
06508               d->extra()->validity->displayValidationInformation = ( bool )param.attribute("displayvalidationinformation").toInt();
06509           }
06510           if ( param.hasAttribute( "allowemptycell" ) )
06511           {
06512               d->extra()->validity->allowEmptyCell = ( bool )param.attribute("allowemptycell").toInt();
06513           }
06514           if ( param.hasAttribute("listvalidity") )
06515           {
06516               d->extra()->validity->listValidity=QStringList::split(";", param.attribute("listvalidity") );
06517           }
06518         }
06519         QDomElement inputTitle = validity.namedItem( "inputtitle" ).toElement();
06520         if (!inputTitle.isNull())
06521         {
06522             d->extra()->validity->titleInfo = inputTitle.text();
06523         }
06524         QDomElement inputMessage = validity.namedItem( "inputmessage" ).toElement();
06525         if (!inputMessage.isNull())
06526         {
06527             d->extra()->validity->messageInfo = inputMessage.text();
06528         }
06529 
06530         QDomElement title = validity.namedItem( "title" ).toElement();
06531         if (!title.isNull())
06532         {
06533             d->extra()->validity->title = title.text();
06534         }
06535         QDomElement message = validity.namedItem( "message" ).toElement();
06536         if (!message.isNull())
06537         {
06538             d->extra()->validity->message = message.text();
06539         }
06540         QDomElement timeMin = validity.namedItem( "timemin" ).toElement();
06541         if ( !timeMin.isNull()  )
06542         {
06543             d->extra()->validity->timeMin = toTime(timeMin);
06544         }
06545         QDomElement timeMax = validity.namedItem( "timemax" ).toElement();
06546         if ( !timeMax.isNull()  )
06547         {
06548             d->extra()->validity->timeMax = toTime(timeMax);
06549          }
06550         QDomElement dateMin = validity.namedItem( "datemin" ).toElement();
06551         if ( !dateMin.isNull()  )
06552         {
06553             d->extra()->validity->dateMin = toDate(dateMin);
06554          }
06555         QDomElement dateMax = validity.namedItem( "datemax" ).toElement();
06556         if ( !dateMax.isNull()  )
06557         {
06558             d->extra()->validity->dateMax = toDate(dateMax);
06559          }
06560     }
06561     else if ((pm == Paste::Normal) || (pm == Paste::NoBorder))
06562     {
06563       // clear the validity
06564       removeValidity();
06565     }
06566 
06567     //
06568     // Load the comment
06569     //
06570     QDomElement comment = cell.namedItem( "comment" ).toElement();
06571     if ( !comment.isNull() && ( pm == Paste::Normal || pm == Paste::Comment || pm == Paste::NoBorder ))
06572     {
06573         QString t = comment.text();
06574         //t = t.stripWhiteSpace();
06575         format()->setComment( t );
06576     }
06577 
06578     //
06579     // The real content of the cell is loaded here. It is stored in
06580     // the "text" tag, which contains either a text or a CDATA section.
06581     //
06582     // TODO: make this suck less. We set data twice, in loadCellData, and
06583     // also here. Not good.
06584     QDomElement text = cell.namedItem( "text" ).toElement();
06585 
06586     if ( !text.isNull() &&
06587           ( pm == Paste::Normal || pm == Paste::Text || pm == Paste::NoBorder || pm == Paste::Result ) )
06588     {
06589       /* older versions mistakenly put the datatype attribute on the cell
06590          instead of the text.  Just move it over in case we're parsing
06591          an old document */
06592       if ( cell.hasAttribute( "dataType" ) ) // new docs
06593         text.setAttribute( "dataType", cell.attribute( "dataType" ) );
06594 
06595       QDomElement result = cell.namedItem( "result" ).toElement();
06596       QString txt = text.text();
06597       if ((pm == Paste::Result) && (txt[0] == '='))
06598         // paste text of the element, if we want to paste result
06599         // and the source cell contains a formula
06600         // note that we mustn't use setCellValue after this, or else we lose
06601         // all the formulas ...
06602           d->strText = result.text();
06603       else
06604           //otherwise copy everything
06605           loadCellData(text, op);
06606 
06607       if ( !result.isNull() )
06608       {
06609         QString dataType;
06610         QString t = result.text();
06611 
06612         if ( result.hasAttribute( "dataType" ) )
06613           dataType = result.attribute( "dataType" );
06614         if ( result.hasAttribute( "outStr" ) )
06615         {
06616           d->strOutText = result.attribute( "outStr" );
06617           if ( !d->strOutText.isEmpty() )
06618             clearFlag( Flag_TextFormatDirty );
06619         }
06620 
06621         bool clear = true;
06622         // boolean ?
06623         if( dataType == "Bool" )
06624         {
06625           if ( t == "false" )
06626             setValue( false );
06627           else if ( t == "true" )
06628             setValue( true );
06629           else
06630             clear = false;
06631         }
06632         else if( dataType == "Num" )
06633         {
06634           bool ok = false;
06635           double dd = t.toDouble( &ok );
06636           if ( ok )
06637             setValue ( dd );
06638           else
06639             clear = false;
06640         }
06641         else if( dataType == "Date" )
06642         {
06643           bool ok = false;
06644           double dd = t.toDouble( &ok );
06645           if ( ok )
06646             setValue ( dd );
06647           else
06648           {
06649             int pos   = t.find( '/' );
06650             int year  = t.mid( 0, pos ).toInt();
06651             int pos1  = t.find( '/', pos + 1 );
06652             int month = t.mid( pos + 1, ( ( pos1 - 1 ) - pos ) ).toInt();
06653             int day   = t.right( t.length() - pos1 - 1 ).toInt();
06654             QDate date( year, month, day );
06655             if ( date.isValid() )
06656               setValue( date );
06657             else
06658               clear = false;
06659           }
06660         }
06661         else if( dataType == "Time" )
06662         {
06663           bool ok = false;
06664           double dd = t.toDouble( &ok );
06665           if ( ok )
06666             setCellValue( dd );
06667           else
06668           {
06669             int hours   = -1;
06670             int minutes = -1;
06671             int second  = -1;
06672             int pos, pos1;
06673             pos   = t.find( ':' );
06674             hours = t.mid( 0, pos ).toInt();
06675             pos1  = t.find( ':', pos + 1 );
06676             minutes = t.mid( pos + 1, ( ( pos1 - 1 ) - pos ) ).toInt();
06677             second  = t.right( t.length() - pos1 - 1 ).toInt();
06678             QTime time( hours, minutes, second );
06679             if ( time.isValid() )
06680               setValue( time );
06681             else
06682               clear = false;
06683           }
06684         }
06685         else
06686         {
06687           setValue( t );
06688         }
06689 
06690         // if ( clear )
06691         //   clearFlag( Flag_CalcDirty );
06692       }
06693     }
06694 
06695     return true;
06696 }
06697 
06698 bool Cell::loadCellData(const QDomElement & text, Paste::Operation op )
06699 {
06700   //TODO: use converter()->asString() to generate strText
06701 
06702   QString t = text.text();
06703   t = t.stripWhiteSpace();
06704 
06705   setFlag(Flag_LayoutDirty);
06706   setFlag(Flag_TextFormatDirty);
06707 
06708   // A formula like =A1+A2 ?
06709   if( t[0] == '=' )
06710   {
06711     t = decodeFormula( t, d->column, d->row );
06712     setCellText (pasteOperation( t, d->strText, op ));
06713 
06714     setFlag(Flag_CalcDirty);
06715     clearAllErrors();
06716 
06717     if ( !makeFormula() )
06718       kdError(36001) << "ERROR: Syntax ERROR" << endl;
06719   }
06720   // rich text ?
06721   else if (t[0] == '!' )
06722   {
06723       // KSpread pre 1.4 stores hyperlink as rich text (first char is '!')
06724       // extract the link and the correspoding text
06725       // This is a rather dirty hack, but enough for KSpread generated XML
06726       bool inside_tag = false;
06727       QString qml_text;
06728       QString tag;
06729       QString qml_link;
06730 
06731       for( unsigned i = 1; i < t.length(); i++ )
06732       {
06733         QChar ch = t[i];
06734         if( ch == '<' )
06735         {
06736           if( !inside_tag )
06737           {
06738             inside_tag = true;
06739             tag = QString::null;
06740           }
06741         }
06742         else if( ch == '>' )
06743         {
06744           if( inside_tag )
06745           {
06746             inside_tag = false;
06747             if( tag.startsWith( "a href=\"", true ) )
06748             if( tag.endsWith( "\"" ) )
06749               qml_link = tag.mid( 8, tag.length()-9 );
06750             tag = QString::null;
06751           }
06752         }
06753         else
06754         {
06755           if( !inside_tag )
06756             qml_text += ch;
06757           else
06758             tag += ch;
06759         }
06760       }
06761 
06762       if( !qml_link.isEmpty() )
06763         d->extra()->link = qml_link;
06764       d->strText = qml_text;
06765       setValue( d->strText );
06766   }
06767   else
06768   {
06769     bool newStyleLoading = true;
06770     QString dataType;
06771 
06772     if ( text.hasAttribute( "dataType" ) ) // new docs
06773     {
06774         dataType = text.attribute( "dataType" );
06775     }
06776     else // old docs: do the ugly solution of calling checkTextInput to parse the text
06777     {
06778       // ...except for date/time
06779       if (isDate() && ( t.contains('/') == 2 ))
06780         dataType = "Date";
06781       else if (isTime() && ( t.contains(':') == 2 ) )
06782         dataType = "Time";
06783       else
06784       {
06785         d->strText = pasteOperation( t, d->strText, op );
06786         checkTextInput();
06787         //kdDebug(36001) << "Cell::load called checkTextInput, got dataType=" << dataType << "  t=" << t << endl;
06788         newStyleLoading = false;
06789       }
06790     }
06791 
06792     if ( newStyleLoading )
06793     {
06794       d->value = Value::empty();
06795       clearAllErrors();
06796 
06797       // boolean ?
06798       if( dataType == "Bool" )
06799       {
06800         bool val = (t.lower() == "true");
06801         setCellValue (val);
06802       }
06803 
06804       // number ?
06805       else if( dataType == "Num" )
06806       {
06807         bool ok = false;
06808         if (t.contains('.'))
06809           setValue ( Value( t.toDouble(&ok) ) ); // We save in non-localized format
06810         else
06811           setValue ( Value( t.toLong(&ok) ) );
06812         if ( !ok )
06813   {
06814           kdWarning(36001) << "Couldn't parse '" << t << "' as number." << endl;
06815   }
06816   /* We will need to localize the text version of the number */
06817   KLocale* locale = format()->sheet()->doc()->locale();
06818 
06819         /* KLocale::formatNumber requires the precision we want to return.
06820         */
06821         int precision = t.length() - t.find('.') - 1;
06822 
06823   if ( formatType() == Percentage_format )
06824         {
06825           if (value().isInteger())
06826             t = locale->formatNumber( value().asInteger() * 100 );
06827           else
06828             t = locale->formatNumber( value().asFloat() * 100.0, precision );
06829           d->strText = pasteOperation( t, d->strText, op );
06830           d->strText += '%';
06831         }
06832         else
06833         {
06834           if (value().isInteger())
06835             t = locale->formatLong(value().asInteger());
06836           else
06837             t = locale->formatNumber(value().asFloat(), precision);
06838           d->strText = pasteOperation( t, d->strText, op );
06839         }
06840       }
06841 
06842       // date ?
06843       else if( dataType == "Date" )
06844       {
06845         int pos = t.find('/');
06846         int year = t.mid(0,pos).toInt();
06847         int pos1 = t.find('/',pos+1);
06848         int month = t.mid(pos+1,((pos1-1)-pos)).toInt();
06849         int day = t.right(t.length()-pos1-1).toInt();
06850         setValue( QDate(year,month,day) );
06851         if ( value().asDate().isValid() ) // Should always be the case for new docs
06852           d->strText = locale()->formatDate( value().asDate(), true );
06853         else // This happens with old docs, when format is set wrongly to date
06854         {
06855           d->strText = pasteOperation( t, d->strText, op );
06856           checkTextInput();
06857         }
06858       }
06859 
06860       // time ?
06861       else if( dataType == "Time" )
06862       {
06863         int hours = -1;
06864         int minutes = -1;
06865         int second = -1;
06866         int pos, pos1;
06867         pos = t.find(':');
06868         hours = t.mid(0,pos).toInt();
06869         pos1 = t.find(':',pos+1);
06870         minutes = t.mid(pos+1,((pos1-1)-pos)).toInt();
06871         second = t.right(t.length()-pos1-1).toInt();
06872         setValue( QTime(hours,minutes,second) );
06873         if ( value().asTime().isValid() ) // Should always be the case for new docs
06874           d->strText = locale()->formatTime( value().asTime(), true );
06875         else  // This happens with old docs, when format is set wrongly to time
06876         {
06877           d->strText = pasteOperation( t, d->strText, op );
06878           checkTextInput();
06879         }
06880       }
06881 
06882       else
06883       {
06884         // Set the cell's text
06885         d->strText = pasteOperation( t, d->strText, op );
06886         setValue( d->strText );
06887       }
06888     }
06889   }
06890 
06891   if ( text.hasAttribute( "outStr" ) ) // very new docs
06892   {
06893     d->strOutText = text.attribute( "outStr" );
06894     if ( !d->strOutText.isEmpty() )
06895       clearFlag( Flag_TextFormatDirty );
06896   }
06897 
06898   if ( !format()->sheet()->isLoading() )
06899     setCellText( d->strText );
06900 
06901   if ( d->hasExtra() && d->extra()->conditions )
06902     d->extra()->conditions->checkMatches();
06903 
06904   return true;
06905 }
06906 
06907 QTime Cell::toTime(const QDomElement &element)
06908 {
06909     //TODO: can't we use tryParseTime (after modification) instead?
06910     QString t = element.text();
06911     t = t.stripWhiteSpace();
06912     int hours = -1;
06913     int minutes = -1;
06914     int second = -1;
06915     int pos, pos1;
06916     pos = t.find(':');
06917     hours = t.mid(0,pos).toInt();
06918     pos1 = t.find(':',pos+1);
06919     minutes = t.mid(pos+1,((pos1-1)-pos)).toInt();
06920     second = t.right(t.length()-pos1-1).toInt();
06921     setValue( Value( QTime(hours,minutes,second)) );
06922     return value().asTime();
06923 }
06924 
06925 QDate Cell::toDate(const QDomElement &element)
06926 {
06927     QString t = element.text();
06928     int pos;
06929     int pos1;
06930     int year = -1;
06931     int month = -1;
06932     int day = -1;
06933     pos = t.find('/');
06934     year = t.mid(0,pos).toInt();
06935     pos1 = t.find('/',pos+1);
06936     month = t.mid(pos+1,((pos1-1)-pos)).toInt();
06937     day = t.right(t.length()-pos1-1).toInt();
06938     setValue( Value( QDate(year,month,day) ) );
06939     return value().asDate();
06940 }
06941 
06942 QString Cell::pasteOperation( const QString &new_text, const QString &old_text, Paste::Operation op )
06943 {
06944   if ( op == Paste::OverWrite )
06945         return new_text;
06946 
06947     QString tmp_op;
06948     QString tmp;
06949     QString old;
06950 
06951     if( !new_text.isEmpty() && new_text[0] == '=' )
06952     {
06953         tmp = new_text.right( new_text.length() - 1 );
06954     }
06955     else
06956     {
06957         tmp = new_text;
06958     }
06959 
06960     if ( old_text.isEmpty() &&
06961          ( op == Paste::Add || op == Paste::Mul || op == Paste::Sub || op == Paste::Div ) )
06962     {
06963       old = "=0";
06964     }
06965 
06966     if( !old_text.isEmpty() && old_text[0] == '=' )
06967     {
06968         old = old_text.right( old_text.length() - 1 );
06969     }
06970     else
06971     {
06972         old = old_text;
06973     }
06974 
06975     bool b1, b2;
06976     tmp.toDouble( &b1 );
06977     old.toDouble( &b2 );
06978     if (b1 && !b2 && old.length() == 0)
06979     {
06980       old = "0";
06981       b2 = true;
06982     }
06983 
06984     if( b1 && b2 )
06985     {
06986         switch( op )
06987         {
06988           case  Paste::Add:
06989             tmp_op = QString::number(old.toDouble()+tmp.toDouble());
06990             break;
06991           case Paste::Mul :
06992             tmp_op = QString::number(old.toDouble()*tmp.toDouble());
06993             break;
06994           case Paste::Sub:
06995             tmp_op = QString::number(old.toDouble()-tmp.toDouble());
06996             break;
06997           case Paste::Div:
06998             tmp_op = QString::number(old.toDouble()/tmp.toDouble());
06999             break;
07000         default:
07001             Q_ASSERT( 0 );
07002         }
07003 
07004         setFlag(Flag_LayoutDirty);
07005         clearAllErrors();
07006 
07007         return tmp_op;
07008     }
07009     else if ( ( new_text[0] == '=' && old_text[0] == '=' ) ||
07010               ( b1 && old_text[0] == '=' ) || ( new_text[0] == '=' && b2 ) )
07011     {
07012         switch( op )
07013         {
07014           case Paste::Add :
07015             tmp_op="=("+old+")+"+"("+tmp+")";
07016             break;
07017           case Paste::Mul :
07018             tmp_op="=("+old+")*"+"("+tmp+")";
07019             break;
07020           case Paste::Sub:
07021             tmp_op="=("+old+")-"+"("+tmp+")";
07022             break;
07023           case Paste::Div:
07024             tmp_op="=("+old+")/"+"("+tmp+")";
07025             break;
07026         default :
07027             Q_ASSERT( 0 );
07028         }
07029 
07030         tmp_op = decodeFormula( tmp_op, d->column, d->row );
07031         setFlag(Flag_LayoutDirty);
07032         clearAllErrors();
07033 
07034         return tmp_op;
07035     }
07036 
07037     tmp = decodeFormula( new_text, d->column, d->row );
07038     setFlag(Flag_LayoutDirty);
07039     clearAllErrors();
07040 
07041     return tmp;
07042 }
07043 
07044 QString Cell::testAnchor( int x, int y ) const
07045 {
07046   if( link().isEmpty() )
07047     return QString::null;
07048 
07049   const Doc* doc = format()->sheet()->doc();
07050   int x1 = doc->zoomItX( d->textX );
07051   int y1 = doc->zoomItX( d->textY - d->textHeight );
07052   int x2 = doc->zoomItX( d->textX + d->textWidth );
07053   int y2 = doc->zoomItX( d->textY );
07054 
07055   if( x > x1 ) if( x < x2 )
07056   if( y > y1 ) if( y < y2 )
07057     return link();
07058 
07059   return QString::null;
07060 }
07061 
07062 void Cell::sheetDies()
07063 {
07064     // Avoid unobscuring the cells in the destructor.
07065     if (d->hasExtra())
07066     {
07067       d->extra()->extraXCells = 0;
07068       d->extra()->extraYCells = 0;
07069       d->extra()->mergedXCells = 0;
07070       d->extra()->mergedYCells = 0;
07071     }
07072 
07073     //d->nextCell = 0;
07074     //d->previousCell = 0;
07075 }
07076 
07077 Cell::~Cell()
07078 {
07079     if ( d->nextCell )
07080         d->nextCell->setPreviousCell( d->previousCell );
07081     if ( d->previousCell )
07082         d->previousCell->setNextCell( d->nextCell );
07083 
07084     if (d->hasExtra())
07085     {
07086       delete d->extra()->validity;
07087     }
07088 
07089     // Unobscure cells.
07090     int extraXCells = d->hasExtra() ? d->extra()->extraXCells : 0;
07091     int extraYCells = d->hasExtra() ? d->extra()->extraYCells : 0;
07092     for( int x = 0; x <= extraXCells; ++x )
07093         for( int y = (x == 0) ? 1 : 0; // avoid looking at (+0,+0)
07094              y <= extraYCells; ++y )
07095     {
07096         Cell* cell = format()->sheet()->cellAt( d->column + x, d->row + y );
07097         if ( cell )
07098             cell->unobscure(this);
07099     }
07100 
07101     d->value = Value::empty();
07102 
07103     if (!isDefault())
07104       valueChanged ();  //our value has been changed (is now null), but only if we aren't default
07105 
07106     delete d->format;
07107     delete d;
07108 }
07109 
07110 bool Cell::operator > ( const Cell & cell ) const
07111 {
07112   if ( value().isNumber() ) // ### what about bools ?
07113   {
07114     if ( cell.value().isNumber() )
07115       return value().asFloat() > cell.value().asFloat();
07116     else
07117       return false; // numbers are always < than texts
07118   }
07119   else if(isDate())
07120   {
07121      if( cell.isDate() )
07122         return value().asDate() > cell.value().asDate();
07123      else if (cell.value().isNumber())
07124         return true;
07125      else
07126         return false; //date are always < than texts and time
07127   }
07128   else if(isTime())
07129   {
07130      if( cell.isTime() )
07131         return value().asTime() > cell.value().asTime();
07132      else if( cell.isDate())
07133         return true; //time are always > than date
07134      else if( cell.value().isNumber())
07135         return true;
07136      else
07137         return false; //time are always < than texts
07138   }
07139   else
07140   {
07141       if ( Map::respectCase )
07142           return value().asString().compare(cell.value().asString()) > 0;
07143       else
07144           return ( value().asString() ).lower().compare(cell.value().asString().lower()) > 0;
07145   }
07146 }
07147 
07148 bool Cell::operator < ( const Cell & cell ) const
07149 {
07150   if ( value().isNumber() )
07151   {
07152     if ( cell.value().isNumber() )
07153       return value().asFloat() < cell.value().asFloat();
07154     else
07155       return true; // numbers are always < than texts
07156   }
07157   else if(isDate())
07158   {
07159      if( cell.isDate() )
07160         return value().asDateTime().date() < cell.value().asDateTime().date();
07161      else if( cell.value().isNumber())
07162         return false;
07163      else
07164         return true; //date are always < than texts and time
07165   }
07166   else if(isTime())
07167   {
07168      if( cell.isTime() )
07169         return value().asDateTime().time() < cell.value().asDateTime().time();
07170      else if(cell.isDate())
07171         return false; //time are always > than date
07172      else if( cell.value().isNumber())
07173         return false;
07174      else
07175         return true; //time are always < than texts
07176   }
07177   else
07178   {
07179       if ( Map::respectCase )
07180           return value().asString().compare(cell.value().asString()) < 0;
07181       else
07182           return ( value().asString() ).lower().compare(cell.value().asString().lower()) < 0;
07183   }
07184 }
07185 
07186 bool Cell::operator==( const Cell& other ) const
07187 {
07188   if ( d->strText != other.d->strText )
07189     return false;
07190   if ( d->value != other.d->value )
07191     return false;
07192   if ( *d->format != *other.d->format )
07193     return false;
07194   if ( d->hasExtra() )
07195   {
07196     if ( !other.d->hasExtra() )
07197       return false;
07198     if ( d->extra()->link != other.d->extra()->link )
07199       return false;
07200     if ( d->extra()->mergedXCells != other.d->extra()->mergedXCells )
07201       return false;
07202     if ( d->extra()->mergedYCells != other.d->extra()->mergedYCells )
07203       return false;
07204     if ( d->extra()->conditions )
07205     {
07206       if ( !other.d->extra()->conditions )
07207         return false;
07208       if ( *d->extra()->conditions != *other.d->extra()->conditions )
07209         return false;
07210     }
07211     if ( d->extra()->validity )
07212     {
07213       if ( !other.d->extra()->validity )
07214         return false;
07215       if ( *d->extra()->validity != *other.d->extra()->validity )
07216         return false;
07217     }
07218   }
07219   return true;
07220 }
07221 
07222 QRect Cell::cellRect()
07223 {
07224   Q_ASSERT(!isDefault());
07225   return QRect(QPoint(d->column, d->row), QPoint(d->column, d->row));
07226 }
07227 
07228 QValueList<Conditional> Cell::conditionList() const
07229 {
07230   if ( !d->hasExtra() || !d->extra()->conditions )
07231   {
07232     QValueList<Conditional> emptyList;
07233     return emptyList;
07234   }
07235 
07236   return d->extra()->conditions->conditionList();
07237 }
07238 
07239 void Cell::setConditionList( const QValueList<Conditional> & newList )
07240 {
07241   if (d->hasExtra())
07242     delete d->extra()->conditions;
07243   d->extra()->conditions = new Conditions( this );
07244   d->extra()->conditions->setConditionList( newList );
07245   d->extra()->conditions->checkMatches();
07246 }
07247 
07248 bool Cell::hasError() const
07249 {
07250   return ( testFlag(Flag_ParseError) ||
07251            testFlag(Flag_CircularCalculation) ||
07252            testFlag(Flag_DependancyError));
07253 }
07254 
07255 void Cell::clearAllErrors()
07256 {
07257   clearFlag( Flag_ParseError );
07258   clearFlag( Flag_CircularCalculation );
07259   clearFlag( Flag_DependancyError );
07260 }
07261 
07262 bool Cell::calcDirtyFlag()
07263 {
07264   return isFormula() ? testFlag( Flag_CalcDirty ) : false;
07265 }
07266 
07267 bool Cell::layoutDirtyFlag() const
07268 {
07269   return testFlag( Flag_LayoutDirty );
07270 }
07271 
07272 void Cell::clearDisplayDirtyFlag()
07273 {
07274   clearFlag( Flag_DisplayDirty );
07275 }
07276 
07277 void Cell::setDisplayDirtyFlag()
07278 {
07279   setFlag( Flag_DisplayDirty );
07280 }
07281 
07282 bool Cell::doesMergeCells() const
07283 {
07284   return testFlag( Flag_Merged );
07285 }
07286 
07287 void Cell::clearFlag( CellFlags flag )
07288 {
07289   d->flags &= ~(Q_UINT32)flag;
07290 }
07291 
07292 void Cell::setFlag( CellFlags flag )
07293 {
07294   d->flags |= (Q_UINT32)flag;
07295 }
07296 
07297 bool Cell::testFlag( CellFlags flag ) const
07298 {
07299   return ( d->flags & (Q_UINT32)flag );
07300 }
07301 
07302 
07303 void Cell::checkForNamedAreas( QString & formula ) const
07304 {
07305   KSPLoadingInfo* loadinginfo = sheet()->doc()->loadingInfo();
07306   if(! loadinginfo) {
07307     kdDebug() << "Cell::checkForNamedAreas loadinginfo is NULL" << endl;
07308     return;
07309   }
07310 
07311   int l = formula.length();
07312   int i = 0;
07313   QString word;
07314   int start = 0;
07315   while ( i < l )
07316   {
07317     if ( formula[i].isLetterOrNumber() )
07318     {
07319       word += formula[i];
07320       ++i;
07321       continue;
07322     }
07323     if ( !word.isEmpty() )
07324     {
07325       if ( loadinginfo->findWordInAreaList(word) )
07326       {
07327         formula = formula.replace( start, word.length(), "'" + word + "'" );
07328         l = formula.length();
07329         ++i;
07330         kdDebug() << "Formula: " << formula << ", L: " << l << ", i: " << i + 1 <<endl;
07331       }
07332     }
07333 
07334     ++i;
07335     word = "";
07336     start = i;
07337   }
07338   if ( !word.isEmpty() )
07339   {
07340     if ( loadinginfo->findWordInAreaList(word) )
07341     {
07342       formula = formula.replace( start, word.length(), "'" + word + "'" );
07343       l = formula.length();
07344       ++i;
07345       kdDebug() << "Formula: " << formula << ", L: " << l << ", i: " << i + 1 <<endl;
07346     }
07347   }
07348 }
KDE Home | KDE Accessibility Home | Description of Access Keys