kspread

kspread_cell.cc

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