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 
01632   if ( d->hasExtra()
01633        && d->extra()->conditions
01634        && d->extra()->conditions->matchedStyle() )
01635   {
01636     Style  *style = d->extra()->conditions->matchedStyle();
01637 
01638     if ( style->hasFeature( Style::SAlignX, true ) )
01639       a = style->alignX();
01640     else
01641       a = format()->align( _col, _row );
01642 
01643     if ( style->hasFeature( Style::SVerticalText, true ) )
01644       tmpVerticalText = style->hasProperty( Style::PVerticalText );
01645     else
01646       tmpVerticalText = format()->verticalText( _col, _row );
01647 
01648     if ( style->hasFeature( Style::SMultiRow, true ) )
01649       tmpMultiRow = style->hasProperty( Style::PMultiRow );
01650     else
01651       tmpMultiRow = format()->multiRow( _col, _row );
01652 
01653     if ( style->hasFeature( Style::SAlignY, true ) )
01654       ay = style->alignY();
01655     else
01656       ay = format()->alignY( _col, _row );
01657 
01658     if ( style->hasFeature( Style::SAngle, true ) )
01659       tmpAngle = style->rotateAngle();
01660     else
01661       tmpAngle = format()->getAngle( _col, _row );
01662   }
01663   else {
01664     a               = format()->align( _col, _row );
01665     ay              = format()->alignY( _col, _row );
01666     tmpAngle        = format()->getAngle( _col, _row );
01667     tmpVerticalText = format()->verticalText( _col, _row );
01668     tmpMultiRow     = format()->multiRow( _col, _row );
01669   }
01670 
01671   RowFormat     *rl = format()->sheet()->rowFormat( _row );
01672   ColumnFormat  *cl = format()->sheet()->columnFormat( _col );
01673 
01674   double  w = cl->dblWidth();
01675   double  h = rl->dblHeight();
01676 
01677   if ( d->hasExtra() ) {
01678     if ( d->extra()->extraXCells )  w = d->extra()->extraWidth;
01679     if ( d->extra()->extraYCells )  h = d->extra()->extraHeight;
01680   }
01681 
01682   const double effTop = BORDER_SPACE + 0.5 * effTopBorderPen( _col, _row ).width();
01683   const double effBottom = h - BORDER_SPACE - 0.5 * effBottomBorderPen( _col, _row ).width();
01684 
01685   // Calculate d->textY based on the vertical alignment and a few
01686   // other inputs.
01687   switch( ay )
01688   {
01689   case Format::Top:
01690   {
01691     if ( tmpAngle == 0 )
01692     {
01693       d->textY = effTop + (double) d->fmAscent / format()->sheet()->doc()->zoomedResolutionY();
01694     }
01695     else if ( tmpAngle < 0 )
01696     {
01697       d->textY = effTop;
01698     }
01699     else
01700     {
01701       d->textY = effTop
01702                + (double) d->fmAscent * cos( tmpAngle * M_PI / 180 ) / format()->sheet()->doc()->zoomedResolutionY();
01703     }
01704     break;
01705   }
01706   case Format::Bottom:
01707   {
01708     if ( !tmpVerticalText && !tmpMultiRow && !tmpAngle )
01709     {
01710       d->textY = effBottom;
01711     }
01712     else if ( tmpAngle != 0 )
01713     {
01714       // Is enough place available?
01715       if ( effBottom - effTop - d->textHeight > 0 )
01716       {
01717         if ( tmpAngle < 0 )
01718         {
01719           d->textY = effBottom - d->textHeight;
01720         }
01721         else
01722         {
01723           d->textY = effBottom - d->textHeight
01724                    + ( (double) d->fmAscent * cos( tmpAngle * M_PI / 180 ) / format()->sheet()->doc()->zoomedResolutionY() );
01725         }
01726       }
01727       else
01728       {
01729         if ( tmpAngle < 0 )
01730         {
01731           d->textY = effTop;
01732         }
01733         else
01734         {
01735           d->textY = effTop
01736                   + ( (double) d->fmAscent * cos( tmpAngle * M_PI / 180 )
01737                   / format()->sheet()->doc()->zoomedResolutionY() );
01738         }
01739       }
01740     }
01741     else if ( tmpMultiRow && !tmpVerticalText )
01742     {
01743       // Is enough place available?
01744       if ( effBottom - effTop - d->textHeight > 0 )
01745       {
01746         d->textY = effBottom - d->textHeight
01747                  + (double)d->fmAscent / format()->sheet()->doc()->zoomedResolutionY();
01748       }
01749       else
01750       {
01751         d->textY = effTop
01752                  + (double) d->fmAscent / format()->sheet()->doc()->zoomedResolutionY();
01753       }
01754     }
01755     else
01756     {
01757       // Is enough place available?
01758       if ( effBottom - effTop - d->textHeight > 0 )
01759       {
01760         d->textY = effBottom - d->textHeight
01761                  + (double)d->fmAscent / format()->sheet()->doc()->zoomedResolutionY();
01762       }
01763       else
01764       {
01765         d->textY = effTop
01766                  + (double) d->fmAscent / format()->sheet()->doc()->zoomedResolutionY();
01767       }
01768     }
01769     break;
01770   }
01771   case Format::Middle:
01772   case Format::UndefinedY:
01773   {
01774     if ( !tmpVerticalText && !tmpMultiRow && !tmpAngle )
01775     {
01776       d->textY = ( h - d->textHeight ) / 2
01777                + (double) d->fmAscent / format()->sheet()->doc()->zoomedResolutionY();
01778     }
01779     else if ( tmpAngle != 0 )
01780     {
01781       // Is enough place available?
01782       if ( effBottom - effTop - d->textHeight > 0 )
01783       {
01784         if ( tmpAngle < 0 )
01785         {
01786           d->textY = ( h - d->textHeight ) / 2;
01787         }
01788         else
01789         {
01790           d->textY = ( h - d->textHeight ) / 2
01791                    + (double) d->fmAscent * cos( tmpAngle * M_PI / 180 ) / format()->sheet()->doc()->zoomedResolutionY();
01792         }
01793       }
01794       else
01795       {
01796         if ( tmpAngle < 0 )
01797         {
01798           d->textY = effTop;
01799         }
01800         else
01801         {
01802           d->textY = effTop
01803                    + ( (double)d->fmAscent * cos( tmpAngle * M_PI / 180 ) / format()->sheet()->doc()->zoomedResolutionY() );
01804         }
01805       }
01806     }
01807     else if ( tmpMultiRow && !tmpVerticalText )
01808     {
01809       // Is enough place available?
01810       if ( effBottom - effTop - d->textHeight > 0 )
01811       {
01812         d->textY = ( h - d->textHeight ) / 2
01813                  + (double) d->fmAscent / format()->sheet()->doc()->zoomedResolutionY();
01814       }
01815       else
01816       {
01817         d->textY = effTop
01818                  + (double) d->fmAscent / format()->sheet()->doc()->zoomedResolutionY();
01819       }
01820     }
01821     else
01822     {
01823       // Is enough place available?
01824       if ( effBottom - effTop - d->textHeight > 0 )
01825       {
01826         d->textY = ( h - d->textHeight ) / 2
01827                  + (double) d->fmAscent / format()->sheet()->doc()->zoomedResolutionY();
01828       }
01829       else
01830         d->textY = effTop
01831                  + (double)d->fmAscent / format()->sheet()->doc()->zoomedResolutionY();
01832     }
01833     break;
01834   }
01835   }
01836 
01837   a = effAlignX();
01838   if ( format()->sheet()->getShowFormula() &&
01839        !( format()->sheet()->isProtected() && format()->isHideFormula( _col, _row ) ) )
01840   {
01841     a = Format::Left;
01842   }
01843 
01844   // Calculate d->textX based on alignment and textwidth.
01845   switch ( a ) {
01846   case Format::Left:
01847     d->textX = 0.5 * effLeftBorderPen( _col, _row ).width() + BORDER_SPACE;
01848     break;
01849   case Format::Right:
01850     d->textX = w - BORDER_SPACE - d->textWidth
01851              - 0.5 * effRightBorderPen( _col, _row ).width();
01852     break;
01853   case Format::Center:
01854     d->textX = 0.5 * ( w - BORDER_SPACE - d->textWidth -
01855                        0.5 * effRightBorderPen( _col, _row ).width() );
01856     break;
01857   }
01858 }
01859 
01860 
01861 // Recalculate the current text dimensions, i.e. d->textWidth and
01862 // d->textHeight.
01863 //
01864 // Used in makeLayout() and calculateTextParameters().
01865 //
01866 void Cell::textSize( QPainter &_paint )
01867 {
01868   QFontMetrics  fm = _paint.fontMetrics();
01869   // Horizontal text ?
01870 
01871   int    tmpAngle;
01872   int    _row = row();
01873   int    _col = column();
01874   bool   tmpVerticalText;
01875   bool   fontUnderlined;
01876   Format::AlignY ay;
01877 
01878   // Set tmpAngle, tmpeVerticalText, ay and fontUnderlined according
01879   // to if there is a matching condition or not.
01880   if ( d->hasExtra()
01881        && d->extra()->conditions
01882        && d->extra()->conditions->matchedStyle() )
01883   {
01884     Style  *style = d->extra()->conditions->matchedStyle();
01885 
01886     if ( style->hasFeature( Style::SAngle, true ) )
01887       tmpAngle = style->rotateAngle();
01888     else
01889       tmpAngle = format()->getAngle( _col, _row );
01890 
01891     if ( style->hasFeature( Style::SVerticalText, true ) )
01892       tmpVerticalText = style->hasProperty( Style::PVerticalText );
01893     else
01894       tmpVerticalText = format()->verticalText( _col, _row );
01895 
01896     if ( style->hasFeature( Style::SAlignY, true ) )
01897       ay = style->alignY();
01898     else
01899       ay = format()->alignY( _col, _row );
01900 
01901     if ( style->hasFeature( Style::SFontFlag, true ) )
01902       fontUnderlined = ( style->fontFlags() & (uint) Style::FUnderline );
01903     else
01904       fontUnderlined = format()->textFontUnderline( _col, _row );
01905   }
01906   else {
01907     // The cell has no condition with a maxed style.
01908     tmpAngle        = format()->getAngle( _col, _row );
01909     tmpVerticalText = format()->verticalText( _col, _row );
01910     ay              = format()->alignY( _col, _row );
01911     fontUnderlined  = format()->textFontUnderline( _col, _row );
01912   }
01913 
01914   // Set d->textWidth and d->textHeight to correct values according to
01915   // if the text is horizontal, vertical or rotated.
01916   if ( !tmpVerticalText && !tmpAngle ) {
01917     // Horizontal text.
01918 
01919     d->textWidth = format()->sheet()->doc()->unzoomItX( fm.width( d->strOutText ) );
01920     int offsetFont = 0;
01921     if ( ( ay == Format::Bottom ) && fontUnderlined ) {
01922       offsetFont = fm.underlinePos() + 1;
01923     }
01924 
01925     d->textHeight = format()->sheet()->doc()->unzoomItY( fm.ascent() + fm.descent()
01926             + offsetFont );
01927   }
01928   else if ( tmpAngle!= 0 ) {
01929     // Rotated text.
01930 
01931     d->textHeight = format()->sheet()->doc()
01932       ->unzoomItY( int( cos( tmpAngle * M_PI / 180 )
01933       * ( fm.ascent() + fm.descent() )
01934       + abs( int( ( fm.width( d->strOutText )
01935               * sin( tmpAngle * M_PI / 180 ) ) ) ) ) );
01936 
01937     d->textWidth = format()->sheet()->doc()
01938       ->unzoomItX( int( abs( int( ( sin( tmpAngle * M_PI / 180 )
01939             * ( fm.ascent() + fm.descent() ) ) ) )
01940       + fm.width( d->strOutText )
01941         * cos ( tmpAngle * M_PI / 180 ) ) );
01942   }
01943   else {
01944     // Vertical text.
01945     int width = 0;
01946     for ( unsigned int i = 0; i < d->strOutText.length(); i++ )
01947       width = QMAX( width, fm.width( d->strOutText.at( i ) ) );
01948 
01949     d->textWidth  = format()->sheet()->doc()->unzoomItX( width );
01950     d->textHeight = format()->sheet()->doc()->unzoomItY( ( fm.ascent() + fm.descent() )
01951             * d->strOutText.length() );
01952   }
01953 }
01954 
01955 
01956 // Get the effective font to use after the zooming and apply it to `painter'.
01957 //
01958 // Used in makeLayout() and calculateTextParameters().
01959 //
01960 
01961 void Cell::applyZoomedFont( QPainter &painter, int _col, int _row )
01962 {
01963   QFont  tmpFont( format()->textFont( _col, _row ) );
01964 
01965   // If there is a matching condition on this cell then set the
01966   // according style parameters.
01967   if ( d->hasExtra()
01968        && d->extra()->conditions
01969        && d->extra()->conditions->matchedStyle() ) {
01970 
01971     Style * s = d->extra()->conditions->matchedStyle();
01972 
01973     // Other size?
01974     if ( s->hasFeature( Style::SFontSize, true ) )
01975       tmpFont.setPointSizeFloat( s->fontSize() );
01976 
01977     // Other attributes?
01978     if ( s->hasFeature( Style::SFontFlag, true ) ) {
01979       uint flags = s->fontFlags();
01980 
01981       tmpFont.setBold(      flags & (uint) Style::FBold );
01982       tmpFont.setUnderline( flags & (uint) Style::FUnderline );
01983       tmpFont.setItalic(    flags & (uint) Style::FItalic );
01984       tmpFont.setStrikeOut( flags & (uint) Style::FStrike );
01985     }
01986 
01987     // Other family?
01988     if ( s->hasFeature( Style::SFontFamily, true ) )
01989       tmpFont.setFamily( s->fontFamily() );
01990   }
01991 #if 0
01992   else
01993   /*
01994    * could somebody please explaint why we check for isProtected or isHideFormula here
01995    */
01996    if ( d->extra()->conditions
01997   && d->extra()->conditions->currentCondition( condition )
01998   && !(format()->sheet()->getShowFormula()
01999        && !( format()->sheet()->isProtected()
02000        && format()->isHideFormula( d->column, d->row ) ) ) )
02001    {
02002      if ( condition.fontcond )
02003        tmpFont = *(condition.fontcond);
02004      else
02005        tmpFont = condition.style->font();
02006    }
02007 #endif
02008 
02009   // Scale the font size according to the current zoom.
02010   tmpFont.setPointSizeFloat( 0.01 * format()->sheet()->doc()->zoom()
02011            * tmpFont.pointSizeFloat() );
02012 
02013   painter.setFont( tmpFont );
02014 }
02015 
02016 
02017 //used in Sheet::adjustColumnHelper and Sheet::adjustRow
02018 void Cell::calculateTextParameters( QPainter &_painter,
02019              int _col, int _row )
02020 {
02021   // Apply the correct font to _painter.
02022   applyZoomedFont( _painter, _col, _row );
02023 
02024   // Recalculate d->textWidth and d->textHeight
02025   textSize( _painter );
02026 
02027   // Recalculate d->textX and d->textY.
02028   offsetAlign( _col, _row );
02029 }
02030 
02031 
02032 // ----------------------------------------------------------------
02033 //                          Formula handling
02034 
02035 
02036 bool Cell::makeFormula()
02037 {
02038   clearFormula ();
02039 
02040   d->formula = new KSpread::Formula (sheet(), this);
02041   d->formula->setExpression (d->strText);
02042 
02043   if (!d->formula->isValid ()) {
02044   // Did a syntax error occur ?
02045     clearFormula();
02046 
02047     if (format()->sheet()->doc()->getShowMessageError())
02048     {
02049       QString tmp(i18n("Error in cell %1\n\n"));
02050       tmp = tmp.arg( fullName() );
02051       KMessageBox::error( (QWidget*)0L, tmp);
02052     }
02053     setFlag(Flag_ParseError);
02054     Value v;
02055     v.setError ( "####" );
02056     setValue (v);
02057     return false;
02058   }
02059 
02060   // we must recalc
02061   setCalcDirtyFlag ();
02062 
02063   return true;
02064 }
02065 
02066 void Cell::clearFormula()
02067 {
02068   delete d->formula;
02069   d->formula = 0L;
02070 }
02071 
02072 bool Cell::calc(bool delay)
02073 {
02074   if ( !isFormula() )
02075     return true;
02076 
02077   if (d->formula == 0)
02078   {
02079     if ( testFlag( Flag_ParseError ) )  // there was a parse error
02080       return false;
02081     else
02082     {
02083       /* we were probably at a "isLoading() = true" state when we originally
02084        * parsed
02085        */
02086       makeFormula ();
02087 
02088       if ( d->formula == 0 ) // there was a parse error
02089         return false;
02090     }
02091   }
02092 
02093   if ( !testFlag( Flag_CalcDirty ) )
02094     return true;
02095 
02096   if ( delay )
02097   {
02098     if ( format()->sheet()->doc()->delayCalculation() )
02099       return true;
02100   }
02101 
02102   setFlag(Flag_LayoutDirty);
02103   setFlag(Flag_TextFormatDirty);
02104   clearFlag(Flag_CalcDirty);
02105 
02106   Value result = d->formula->eval ();
02107   setValue (result);
02108   if (result.isNumber())
02109     checkNumberFormat(); // auto-chooses number or scientific
02110 
02111   clearFlag(Flag_CalcDirty);
02112   setFlag(Flag_LayoutDirty);
02113 
02114   return true;
02115 }
02116 
02117 
02118 // ================================================================
02119 //                            Painting
02120 
02121 
02122 // Paint the cell.  This is the main function that calls a lot of
02123 //                  helper functions.
02124 //
02125 // `rect'       is the rectangle that we should paint on.  If the cell
02126 //              does not overlap this rectangle, we can return immediately.
02127 // `coordinate' is the origin (the upper left) of the cell in document
02128 //              coordinates.
02129 //
02130 
02131 void Cell::paintCell( const KoRect   &rect, QPainter & painter,
02132           View    *view,
02133           const KoPoint  &coordinate,
02134           const QPoint   &cellRef,
02135           int paintBorder,
02136           QPen & rightPen, QPen & bottomPen,
02137           QPen & leftPen,  QPen & topPen,
02138           QValueList<QPoint> &mergedCellsPainted,
02139           bool drawCursor )
02140 {
02141   bool paintBorderRight  = paintBorder & Border_Right;
02142   bool paintBorderBottom = paintBorder & Border_Bottom;
02143   bool paintBorderLeft   = paintBorder & Border_Left;
02144   bool paintBorderTop    = paintBorder & Border_Top;
02145 
02146   // If we are already painting this cell, then return immediately.
02147   // This avoids infinite recursion.
02148   if ( testFlag( Flag_PaintingCell ) )
02149     return;
02150 
02151   // Indicate that we are painting this cell now.
02152   setFlag( Flag_PaintingCell );
02153 
02154   // This flag indicates that we are working on drawing the cells that
02155   // another cell is obscuring.  The value is the number of levels down we
02156   // are currently working -- i.e. a cell obscured by a cell which is
02157   // obscured by a cell.
02158   static int  paintingObscured = 0;
02159 
02160 #if 0
02161   if (paintingObscured == 0)
02162     kdDebug(36001) << "painting cell " << name() << endl;
02163   else
02164     kdDebug(36001) << "  painting obscured cell " << name() << endl;
02165 #endif
02166 
02167   // Sanity check: If we're working on drawing an obscured cell, that
02168   // means this cell should have a cell that obscures it.
02169   Q_ASSERT(!(paintingObscured > 0 && d->extra()->obscuringCells.isEmpty()));
02170 
02171   // The parameter cellref should be *this, unless this is the default cell.
02172   Q_ASSERT(isDefault()
02173      || (((cellRef.x() == d->column) && (cellRef.y() == d->row))));
02174 
02175   Sheet::LayoutDirection sheetDir =  format()->sheet()->layoutDirection();
02176 
02177   double left = coordinate.x();
02178 
02179   ColumnFormat * colFormat = format()->sheet()->columnFormat( cellRef.x() );
02180   RowFormat    * rowFormat = format()->sheet()->rowFormat( cellRef.y() );
02181 
02182   // Set width, height to the total width and height that this cell
02183   // covers, including obscured cells, and width0, height0 to the
02184   // width and height of this cell, maybe merged but never implicitly
02185   // extended.
02186   double  width0  = colFormat->dblWidth();
02187   double  height0 = rowFormat->dblHeight();
02188   double  width   = width0;
02189   double  height  = height0;
02190 
02191   // Handle right-to-left layout.
02192   // In an RTL sheet the cells have to be painted at their opposite horizontal
02193   // location on the canvas, meaning that column A will be the rightmost column
02194   // on screen, column B will be to the left of it and so on. Here we change
02195   // the horizontal coordinate at which we start painting the cell in case the
02196   // sheet's direction is RTL. We do this only if paintingObscured is 0,
02197   // otherwise the cell's painting location will flip back and forth in
02198   // consecutive calls to paintCell when painting obscured cells.
02199   if ( sheetDir == Sheet::RightToLeft && paintingObscured == 0
02200        && view && view->canvasWidget() )
02201   {
02202     double  dwidth = view->doc()->unzoomItX(view->canvasWidget()->width());
02203     left = dwidth - coordinate.x() - width;
02204   }
02205 
02206   // See if this cell is merged or has overflown into neighbor cells.
02207   // In that case, the width/height is greater than just the cell
02208   // itself.
02209   if (d->hasExtra()) {
02210     if (d->extra()->mergedXCells > 0 || d->extra()->mergedYCells > 0) {
02211       // merged cell extends to the left if sheet is RTL
02212       if ( sheetDir == Sheet::RightToLeft ) {
02213         left -= d->extra()->extraWidth - width;
02214       }
02215       width0  = d->extra()->extraWidth;
02216       height0 = d->extra()->extraHeight;
02217       width   = d->extra()->extraWidth;
02218       height  = d->extra()->extraHeight;
02219     }
02220     else {
02221 #if 0
02222       width  += d->extra()->extraXCells ? d->extra()->extraWidth  : 0;
02223       height += d->extra()->extraYCells ? d->extra()->extraHeight : 0;
02224 #else
02225       // FIXME: Make extraWidth/Height really contain the *extra* width/height.
02226       if ( d->extra()->extraXCells )
02227   width  = d->extra()->extraWidth;
02228       if ( d->extra()->extraYCells )
02229   height = d->extra()->extraHeight;
02230 #endif
02231     }
02232   }
02233 
02234   // Check if the cell is "selected", i.e. it should be drawn with the
02235   // color that indicates selection (dark blue).  If more than one
02236   // square is selected, the last one uses the ordinary colors.  In
02237   // that case, "selected" will be set to false even though the cell
02238   // itself really is selected.
02239   bool  selected = false;
02240   if ( view != NULL ) {
02241     selected = view->selectionInfo()->contains( cellRef );
02242 
02243     // But the cell doesn't look selected if this is the marker cell.
02244     Cell  *cell = format()->sheet()->cellAt( view->selectionInfo()->marker() );
02245     QPoint bottomRight( view->selectionInfo()->marker().x() + cell->extraXCells(),
02246        view->selectionInfo()->marker().y() + cell->extraYCells() );
02247     QRect markerArea( view->selectionInfo()->marker(), bottomRight );
02248     selected = selected && !( markerArea.contains( cellRef ) );
02249 
02250     // Don't draw any selection at all when printing.
02251     if ( painter.device()->isExtDev() || !drawCursor )
02252       selected = false;
02253   }
02254 
02255   // Need to make a new layout ?
02256   //
02257   // FIXME: We have already used (at least) extraWidth/Height above,
02258   //        and now we are recalculating the layout.  This has to be
02259   //        moved up above all uses.
02260   //
02261   // FIXME: This needs to be taken out eventually - it is done in
02262   //        canvas::paintUpdates().
02263   if ( testFlag( Flag_LayoutDirty ) )
02264     makeLayout( painter, cellRef.x(), cellRef.y() );
02265 
02266   // ----------------  Start the actual painting.  ----------------
02267 
02268   // If the rect of this cell doesn't intersect the rect that should
02269   // be painted, we can skip the rest and return. (Note that we need
02270   // to calculate `left' first before we can do this.)
02271   const KoRect  cellRect( left, coordinate.y(), width, height );
02272   const KoRect  cellRect0( left, coordinate.y(), width0, height0 );
02273   if ( !cellRect.intersects( rect ) ) {
02274     clearFlag( Flag_PaintingCell );
02275     return;
02276   }
02277 
02278   // Get the background color.
02279   //
02280   // If there is a condition giving the background color for this cell
02281   // (and it matches), use that one, otherwise get the standard
02282   // background.
02283   QColor backgroundColor;
02284   if ( d->hasExtra() && d->extra()->conditions
02285        && d->extra()->conditions->matchedStyle()
02286        && d->extra()->conditions->matchedStyle()->hasFeature( Style::SBackgroundColor, true ) )
02287     backgroundColor = d->extra()->conditions->matchedStyle()->bgColor();
02288   else
02289     backgroundColor = bgColor( cellRef.x(), cellRef.y() );
02290 
02291   // 1. Paint the background.
02292   if ( !isPartOfMerged() )
02293     paintBackground( painter, cellRect0, cellRef, selected, backgroundColor );
02294 
02295   // 2. Paint the default borders if we are on screen or if we are printing
02296   //    and the checkbox to do this is checked.
02297   if ( painter.device()->devType() != QInternal::Printer
02298        || format()->sheet()->print()->printGrid())
02299     paintDefaultBorders( painter, rect, cellRect, cellRef,
02300        paintBorderRight, paintBorderBottom,
02301                          paintBorderLeft,  paintBorderTop,
02302        rightPen, bottomPen, leftPen, topPen );
02303 
02304   // 3. Paint all the cells that this one obscures.  They may only be
02305   //    partially obscured.
02306   //
02307   // The `paintingObscured' variable is used to avoid infinite
02308   // recursion since cells sometimes paint their obscuring cell as
02309   // well.
02310   paintingObscured++;
02311 
02312   if (d->hasExtra() && (d->extra()->extraXCells > 0
02313       || d->extra()->extraYCells > 0)) {
02314     //kdDebug(36001) << "painting obscured cells for " << name() << endl;
02315 
02316     paintObscuredCells( rect, painter, view, cellRect, cellRef,
02317       paintBorderRight, paintBorderBottom,
02318       paintBorderLeft,  paintBorderTop,
02319       rightPen, bottomPen, leftPen, topPen,
02320       mergedCellsPainted);
02321 
02322     // FIXME: Is this the right place for this?
02323     if ( d->extra()->mergedXCells > 0 || d->extra()->mergedYCells > 0 )
02324       mergedCellsPainted.prepend( cellRef );
02325   }
02326   paintingObscured--;
02327 
02328   // 4. Paint the borders of the cell if no other cell is forcing this
02329   // one, i.e. this cell is not part of a merged cell.
02330   //
02331 
02332   // If we print pages, then we disable clipping, otherwise borders are
02333   // cut in the middle at the page borders.
02334   if ( painter.device()->isExtDev() )
02335     painter.setClipping( false );
02336 
02337   // Paint the borders if this cell is not part of another merged cell.
02338   if ( !isPartOfMerged() ) {
02339     // if (!testFlag(Flag_Highlight))
02340     paintCellBorders( painter, rect, cellRect0,
02341           cellRef,
02342           paintBorderRight, paintBorderBottom,
02343                       paintBorderLeft,  paintBorderTop,
02344           rightPen, bottomPen, leftPen, topPen );
02345   }
02346 
02347   // Turn clipping back on.
02348   if ( painter.device()->isExtDev() )
02349     painter.setClipping( true );
02350 
02351   // 5. Paint diagonal lines and page borders.
02352   paintCellDiagonalLines( painter, cellRect0, cellRef );
02353 
02354   paintPageBorders( painter, cellRect0, cellRef,
02355         paintBorderRight, paintBorderBottom );
02356 
02357 
02358   // 6. Now paint the content, if this cell isn't obscured.
02359   if ( !isObscured() ) {
02360 
02361     // 6a. Paint possible comment indicator.
02362     if ( !painter.device()->isExtDev()
02363    || format()->sheet()->print()->printCommentIndicator() )
02364       paintCommentIndicator( painter, cellRect, cellRef, backgroundColor );
02365 
02366     // 6b. Paint possible formula indicator.
02367     if ( !painter.device()->isExtDev()
02368    || format()->sheet()->print()->printFormulaIndicator() )
02369       paintFormulaIndicator( painter, cellRect, backgroundColor );
02370 
02371     // 6c. Paint possible indicator for clipped text.
02372     paintMoreTextIndicator( painter, cellRect, backgroundColor );
02373 
02374      //6c. Paint cell highlight
02375 #if 0
02376     if (highlightBorder != Border_None)
02377       paintCellHighlight ( painter, cellRect, cellRef, highlightBorder,
02378          rightHighlightPen, bottomHighlightPen,
02379          leftHighlightPen,  topHighlightPen );
02380 #endif
02381 
02382     // 6d. Paint the text in the cell unless:
02383     //  a) it is empty
02384     //  b) something indicates that the text should not be painted
02385     //  c) the sheet is protected and the cell is hidden.
02386     if ( !d->strOutText.isEmpty()
02387    && ( !painter.device()->isExtDev()
02388         || !format()->getDontprintText( cellRef.x(), cellRef.y() ) )
02389    && !( format()->sheet()->isProtected()
02390          && format()->isHideAll( cellRef.x(), cellRef.y() ) ) )
02391     {
02392       paintText( painter, cellRect, cellRef );
02393     }
02394   }
02395 
02396   // 7. If this cell is obscured and we are not already painting obscured
02397   //    cells, then paint the obscuring cell(s).  Otherwise don't do
02398   //    anything so that we don't cause an infinite loop.
02399   if ( isObscured() && paintingObscured == 0 &&
02400        !( sheetDir == Sheet::RightToLeft && painter.device()->isExtDev() ) )
02401   {
02402 
02403     //kdDebug(36001) << "painting cells that obscure " << name() << endl;
02404 
02405     // Store the obscuringCells list in a list of QPoint(column, row)
02406     // This avoids crashes during the iteration through
02407     // obscuringCells, when the cells may get non valid or the list
02408     // itself gets changed during a call of obscuringCell->paintCell
02409     // (this happens e.g. when there is an updateDepend)
02410     if (d->hasExtra()) {
02411       QValueList<QPoint>           listPoints;
02412       QValueList<Cell*>::iterator  it = d->extra()->obscuringCells.begin();
02413       QValueList<Cell*>::iterator  end = d->extra()->obscuringCells.end();
02414       for ( ; it != end; ++it ) {
02415         Cell *obscuringCell = *it;
02416 
02417         listPoints.append( QPoint( obscuringCell->column(), obscuringCell->row() ) );
02418       }
02419 
02420       QValueList<QPoint>::iterator  it1  = listPoints.begin();
02421       QValueList<QPoint>::iterator  end1 = listPoints.end();
02422       for ( ; it1 != end1; ++it1 ) {
02423         QPoint obscuringCellRef = *it1;
02424 
02425   // Only paint those obscuring cells that haven't been already
02426   // painted yet.
02427   //
02428   // This optimization removes an O(n^4) behaviour where n is
02429   // the number of cells on one edge in a merged cell.
02430   if ( mergedCellsPainted.contains( obscuringCellRef ) )
02431     continue;
02432 
02433         Cell *obscuringCell = format()->sheet()->cellAt( obscuringCellRef.x(),
02434                obscuringCellRef.y() );
02435 
02436         if ( obscuringCell != 0 ) {
02437           double x = format()->sheet()->dblColumnPos( obscuringCellRef.x() );
02438           double y = format()->sheet()->dblRowPos( obscuringCellRef.y() );
02439           if ( view != 0 ) {
02440             x -= view->canvasWidget()->xOffset();
02441             y -= view->canvasWidget()->yOffset();
02442           }
02443 
02444           KoPoint corner( x, y );
02445           painter.save();
02446 
02447     // Get the effective pens for the borders.  These are
02448     // determined by possible conditions on the cell with
02449     // associated styles.
02450           QPen rp( obscuringCell->effRightBorderPen( obscuringCellRef.x(),
02451                  obscuringCellRef.y() ) );
02452           QPen bp( obscuringCell->effBottomBorderPen( obscuringCellRef.x(),
02453                   obscuringCellRef.y() ) );
02454           QPen lp( obscuringCell->effLeftBorderPen( obscuringCellRef.x(),
02455                 obscuringCellRef.y() ) );
02456           QPen tp( obscuringCell->effTopBorderPen( obscuringCellRef.x(),
02457                obscuringCellRef.y() ) );
02458 
02459 
02460     //kdDebug(36001) << "  painting obscuring cell "
02461     //     << obscuringCell->name() << endl;
02462     // QPen highlightPen;
02463 
02464     //Note: Painting of highlight isn't quite right.  If several
02465     //      cells are merged, then the whole merged cell will be
02466     //      painted with the colour of the last cell referenced
02467     //      which is inside the merged range.
02468           obscuringCell->paintCell( rect, painter, view,
02469                                     corner, obscuringCellRef,
02470             Border_Left|Border_Top|Border_Right|Border_Bottom,
02471             rp, bp, lp, tp,
02472             mergedCellsPainted); // new pens
02473           painter.restore();
02474         }
02475       }
02476     }
02477   }
02478 
02479   // We are done with the painting, so remove the flag on the cell.
02480   clearFlag( Flag_PaintingCell );
02481 }
02482 
02483 
02484 
02485 // The following code was commented out in the above function.  I'll
02486 // leave it here in case this functionality is ever re-implemented and
02487 // someone wants some code to start from.
02488 //
02489 #if 0
02490 
02494   if ( d->style == Cell::ST_Button ) {
02495     QBrush fill( Qt::lightGray );
02496     QApplication::style().drawControl( QStyle::CE_PushButton, &_painter, this,
02497                QRect( _tx + 1, _ty + 1, w2 - 1, h2 - 1 ),
02498                defaultColorGroup ); //, selected, &fill );
02499   }
02500 
02504   else if ( d->style == Cell::ST_Select ) {
02505     QApplication::style().drawComboButton(  &_painter, _tx + 1, _ty + 1,
02506               w2 - 1, h2 - 1,
02507               defaultColorGroup, selected );
02508   }
02509 #endif
02510 
02511 
02512 #if 0
02513  void Cell::paintCellHighlight(QPainter& painter,
02514              const KoRect& cellRect,
02515              const QPoint& cellRef,
02516              const int highlightBorder,
02517              const QPen& rightPen,
02518              const QPen& bottomPen,
02519              const QPen& leftPen,
02520              const QPen& topPen
02521              )
02522 {
02523   //painter.drawLine(cellRect.left(),cellRect.top(),cellRect.right(),cellRect.bottom());
02524   //QPen pen(d->extra()->highlight);
02525   //painter.setPen(highlightPen);
02526 
02527   QBrush nullBrush;
02528   painter.setBrush(nullBrush);
02529 
02530   QRect zoomedCellRect = sheet()->doc()->zoomRect( cellRect );
02531 
02532   //The highlight rect is just inside the main cell rect
02533   //This saves the hassle of repainting nearby cells when the highlight is changed as the highlight areas
02534   //do not overlap
02535   zoomedCellRect.setLeft(zoomedCellRect.left()+1);
02536   //zoomedCellRect.setRight(zoomedCellRect.right()-1);
02537   zoomedCellRect.setTop(zoomedCellRect.top()+1);
02538   //zoomedCellRect.setBottom(zoomedCellRect.bottom()-1);
02539 
02540   if ( cellRef.x() != KS_colMax )
02541     zoomedCellRect.setWidth( zoomedCellRect.width() - 1 );
02542   if ( cellRef.y() != KS_rowMax )
02543   zoomedCellRect.setHeight( zoomedCellRect.height() - 1 );
02544 
02545   if (highlightBorder & Border_Top)
02546   {
02547     painter.setPen(topPen);
02548     painter.drawLine(zoomedCellRect.left(),zoomedCellRect.top(),zoomedCellRect.right(),zoomedCellRect.top());
02549   }
02550   if (highlightBorder & Border_Left)
02551   {
02552     painter.setPen(leftPen);
02553     painter.drawLine(zoomedCellRect.left(),zoomedCellRect.top(),zoomedCellRect.left(),zoomedCellRect.bottom());
02554   }
02555   if (highlightBorder & Border_Right)
02556   {
02557     painter.setPen(rightPen);
02558     painter.drawLine(zoomedCellRect.right(),zoomedCellRect.top(),zoomedCellRect.right(),zoomedCellRect.bottom());
02559   }
02560   if (highlightBorder & Border_Bottom)
02561   {
02562     painter.setPen(bottomPen);
02563     painter.drawLine(zoomedCellRect.left(),zoomedCellRect.bottom(),zoomedCellRect.right(),zoomedCellRect.bottom());
02564   }
02565 
02566   if (highlightBorder & Border_SizeGrip)
02567   {
02568     QBrush brush(rightPen.color());
02569     painter.setBrush(brush);
02570     painter.setPen(rightPen);
02571     painter.drawRect(zoomedCellRect.right()-3,zoomedCellRect.bottom()-3,4,4);
02572   }
02573 
02574   //painter.drawRect(zoomedCellRect.left(),zoomedCellRect.top(),zoomedCellRect.width(),zoomedCellRect.height());
02575 }
02576 #endif
02577 
02578 
02579 // Paint all the cells that this cell obscures (helper function to paintCell).
02580 //
02581 void Cell::paintObscuredCells(const KoRect& rect, QPainter& painter,
02582             View* view,
02583             const KoRect &cellRect,
02584             const QPoint &cellRef,
02585             bool paintBorderRight,
02586             bool _paintBorderBottom,
02587             bool paintBorderLeft,
02588             bool _paintBorderTop,
02589             QPen & rightPen, QPen & _bottomPen,
02590             QPen & leftPen,  QPen & _topPen,
02591             QValueList<QPoint> &mergedCellsPainted)
02592 {
02593   // If there are no obscured cells, return.
02594   if ( !extraXCells() && !extraYCells() )
02595     return;
02596 
02597   double  ypos = cellRect.y();
02598   int     maxY = extraYCells();
02599   int     maxX = extraXCells();
02600 
02601   // Loop through the rectangle of squares that we obscure and paint them.
02602   for ( int y = 0; y <= maxY; ++y ) {
02603     double xpos = cellRect.x();
02604     RowFormat* rl = format()->sheet()->rowFormat( cellRef.y() + y );
02605 
02606     for( int x = 0; x <= maxX; ++ x ) {
02607       ColumnFormat * cl = format()->sheet()->columnFormat( cellRef.x() + x );
02608       if ( y != 0 || x != 0 ) {
02609   uint  column = cellRef.x() + x;
02610   uint  row    = cellRef.y() + y;
02611 
02612   QPen  topPen;
02613   QPen  bottomPen;
02614   bool  paintBorderTop;
02615   bool  paintBorderBottom;
02616 
02617   Cell  *cell = format()->sheet()->cellAt( column, row );
02618   KoPoint       corner( xpos, ypos );
02619 
02620   // Check if the upper and lower borders should be painted, and
02621   // if so which pens we should use.  There used to be a nasty
02622   // bug here (#61452).
02623   // Check top pen.  Only check if this is not on the top row.
02624   topPen         = _topPen;
02625   paintBorderTop = _paintBorderTop;
02626   if ( row > 1 && !cell->isPartOfMerged() ) {
02627     Cell  *cellUp = format()->sheet()->cellAt( column, row - 1 );
02628 
02629     if ( cellUp->isDefault() )
02630       paintBorderTop = false;
02631     else {
02632       // If the cell towards the top is part of a merged cell, get
02633       // the pointer to the master cell.
02634       cellUp = cellUp->ultimateObscuringCell();
02635 
02636       topPen = cellUp->effBottomBorderPen( cellUp->column(),
02637              cellUp->row() );
02638 
02639 #if 0
02640       int  penWidth = QMAX(1, sheet()->doc()->zoomItY( topPen.width() ));
02641       topPen.setWidth( penWidth );
02642 #endif
02643     }
02644   }
02645 
02646   // FIXME: I thought we had to check bottom pen as well.
02647   //        However, it looks as if we don't need to.  It works anyway.
02648   bottomPen         = _bottomPen;
02649   paintBorderBottom = _paintBorderBottom;
02650 
02651   int  paintBorder = Border_None;
02652   if (paintBorderLeft)   paintBorder |= Cell::Border_Left;
02653   if (paintBorderRight)  paintBorder |= Cell::Border_Right;
02654   if (paintBorderTop)    paintBorder |= Cell::Border_Top;
02655   if (paintBorderBottom) paintBorder |= Cell::Border_Bottom;
02656 
02657   /*Cell::BorderSides highlightBorder = Border_None;
02658     QPen highlightPen;*/
02659 
02660 
02661   //kdDebug(36001) << "calling paintcell for obscured cell "
02662   //       << cell->name() << endl;
02663   cell->paintCell( rect, painter, view,
02664        corner,
02665        QPoint( cellRef.x() + x, cellRef.y() + y ),
02666        paintBorder,
02667        rightPen, bottomPen, leftPen, topPen,
02668        mergedCellsPainted);
02669       }
02670       xpos += cl->dblWidth();
02671     }
02672 
02673     ypos += rl->dblHeight();
02674   }
02675 }
02676 
02677 
02678 // Paint the background of this cell.
02679 //
02680 void Cell::paintBackground( QPainter& painter, const KoRect &cellRect,
02681           const QPoint &cellRef, bool selected,
02682           QColor &backgroundColor )
02683 {
02684   QColorGroup  defaultColorGroup = QApplication::palette().active();
02685   QRect        zoomedCellRect    = sheet()->doc()->zoomRect( cellRect );
02686 
02687   // If this is not the KS_rowMax and/or KS_colMax, then we reduce
02688   // width and/or height by one.  This is due to the fact that the
02689   // right/bottom most pixel is shared with the left/top most pixel of
02690   // the following cell.  Only in the case of KS_colMax/KS_rowMax we
02691   // need to draw even this pixel, as there isn't a following cell to
02692   // draw the background pixel.
02693    if ( cellRef.x() != KS_colMax )
02694      zoomedCellRect.setWidth( zoomedCellRect.width() - 1 );
02695    if ( cellRef.y() != KS_rowMax )
02696      zoomedCellRect.setHeight( zoomedCellRect.height() - 1 );
02697 
02698   // Determine the correct background color
02699   if ( selected )
02700   {
02701     //If the cell's background color is too bright, use the default highlight color
02702     //Otherwise use a lighter version of the cell's background color.
02703     QColor c;
02704 
02705     int averageColor = (backgroundColor.red() + backgroundColor.green() + backgroundColor.blue()) / 3;
02706 
02707     if (averageColor > 180)
02708     if (averageColor > 225)
02709             c = View::highlightColor();
02710         else
02711         c = backgroundColor.light( 115 ); //15% lighter
02712     else
02713        c = backgroundColor.light( 125 ); //25% lighter
02714 
02715     painter.setBackgroundColor( c );
02716   }
02717   else {
02718     QColor bg( backgroundColor );
02719 
02720     // Handle printers separately.
02721     if ( !painter.device()->isExtDev() ) {
02722       if ( bg.isValid() )
02723         painter.setBackgroundColor( bg );
02724       else
02725         painter.setBackgroundColor( defaultColorGroup.base() );
02726     }
02727     else {
02728       //bad hack but there is a qt bug
02729       //so I can print backgroundcolor
02730       QBrush bb( bg );
02731       if ( !bg.isValid() )
02732         bb.setColor( Qt::white );
02733 
02734       painter.fillRect( zoomedCellRect, bb );
02735       return;
02736     }
02737   }
02738 
02739   // Erase the background of the cell.
02740   if ( !painter.device()->isExtDev() )
02741     painter.eraseRect( zoomedCellRect );
02742 
02743   // Get a background brush
02744   QBrush bb;
02745   if ( d->hasExtra()
02746        && d->extra()->conditions
02747        && d->extra()->conditions->matchedStyle()
02748        && d->extra()->conditions->matchedStyle()->hasFeature( Style::SBackgroundBrush, true ) )
02749     bb = d->extra()->conditions->matchedStyle()->backGroundBrush();
02750   else
02751     bb = backGroundBrush( cellRef.x(), cellRef.y() );
02752 
02753   // Draw background pattern if necessary.
02754   if ( bb.style() != Qt::NoBrush )
02755     painter.fillRect( zoomedCellRect, bb );
02756 
02757   backgroundColor = painter.backgroundColor();
02758 }
02759 
02760 
02761 // Paint the standard light grey borders that are always visible.
02762 //
02763 void Cell::paintDefaultBorders( QPainter& painter, const KoRect &rect,
02764         const KoRect &cellRect,
02765         const QPoint &cellRef,
02766         bool paintBorderRight, bool /*paintBorderBottom*/,
02767         bool paintBorderLeft,  bool paintBorderTop,
02768         QPen const & rightPen, QPen const & /*bottomPen*/,
02769         QPen const & leftPen, QPen const & topPen )
02770 {
02771     /*
02772         *** Notes about optimisation ***
02773 
02774         This function was painting the top , left , right & bottom lines in almost all cells previously, contrary to what the comment
02775         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,
02776         so I have disabled drawing of right and bottom borders for all cells.
02777 
02778         I also couldn't work out under what conditions the variables dt / db would come out as anything other than 0 in the code
02779         for painting the various borders.  The effTopBorderPen / effBottomBorderPen calls were taking up a lot of time
02780         according some profiling I did.  If that code really is necessary, we need to find a more efficient way of getting the widths
02781         than grabbing the whole QPen object and asking it.
02782 
02783 
02784         --Robert Knight (robertknight@gmail.com)
02785     */
02786   Doc* doc = sheet()->doc();
02787 
02788   Sheet::LayoutDirection sheetDir =  format()->sheet()->layoutDirection();
02789   bool paintingToExternalDevice = painter.device()->isExtDev();
02790 
02791   // Each cell is responsible for drawing it's top and left portions
02792   // of the "default" grid. --Or not drawing it if it shouldn't be
02793   // there.  It's also responsible to paint the right and bottom, if
02794   // it is the last cell on a print out.
02795 
02796   bool paintTop;
02797   bool paintLeft;
02798   bool paintBottom=false;
02799   bool paintRight=false;
02800 
02801   paintLeft   = ( paintBorderLeft && leftPen.style() == Qt::NoPen
02802       && sheet()->getShowGrid() && sheetDir==Sheet::LeftToRight );
02803   paintRight  = ( paintBorderRight && rightPen.style() == Qt::NoPen
02804       && sheet()->getShowGrid() && sheetDir==Sheet::RightToLeft );
02805   paintTop    = ( paintBorderTop && topPen.style() == Qt::NoPen
02806       && sheet()->getShowGrid() );
02807 //  paintBottom = ( paintBorderBottom && sheet()->getShowGrid()
02808 //                  && bottomPen.style() == Qt::NoPen );
02809 
02810 
02811    //Set the single-pixel with pen for drawing the borders with.
02812    painter.setPen( QPen( sheet()->doc()->gridColor(), 1, Qt::SolidLine ) );
02813 
02814   // If there are extra cells, there might be more conditions.
02815   if (d->hasExtra()) {
02816     QValueList<Cell*>::const_iterator it  = d->extra()->obscuringCells.begin();
02817     QValueList<Cell*>::const_iterator end = d->extra()->obscuringCells.end();
02818     for ( ; it != end; ++it ) {
02819       Cell *cell = *it;
02820 
02821       paintTop  = paintTop && ( cell->row() == cellRef.y() );
02822       paintBottom = false;
02823 
02824       if ( sheetDir == Sheet::RightToLeft ) {
02825         paintRight = paintRight && ( cell->column() == cellRef.x() );
02826         paintLeft = false;
02827       }
02828       else {
02829         paintLeft = paintLeft && ( cell->column() == cellRef.x() );
02830         paintRight = false;
02831       }
02832     }
02833   }
02834 
02835   // The left border.
02836   if ( paintLeft ) {
02837     int dt = 0;
02838     int db = 0;
02839 
02840     #if 0
02841     if ( cellRef.x() > 1 ) {
02842       Cell  *cell_west = format()->sheet()->cellAt( cellRef.x() - 1,
02843                 cellRef.y() );
02844       QPen t = cell_west->effTopBorderPen( cellRef.x() - 1, cellRef.y() );
02845       QPen b = cell_west->effBottomBorderPen( cellRef.x() - 1, cellRef.y() );
02846 
02847       if ( t.style() != Qt::NoPen )
02848         dt = ( t.width() + 1 )/2;
02849       if ( b.style() != Qt::NoPen )
02850         db = ( t.width() / 2);
02851     }
02852     #endif
02853 
02854     // If we are on paper printout, we limit the length of the lines.
02855     // On paper, we always have full cells, on screen not.
02856     if ( paintingToExternalDevice ) {
02857       if ( sheetDir == Sheet::RightToLeft )
02858         painter.drawLine( doc->zoomItX( QMAX( rect.left(),   cellRect.right() ) ),
02859                           doc->zoomItY( QMAX( rect.top(),    cellRect.y() + dt ) ),
02860                           doc->zoomItX( QMIN( rect.right(),  cellRect.right() ) ),
02861                           doc->zoomItY( QMIN( rect.bottom(), cellRect.bottom() - db ) ) );
02862       else
02863         painter.drawLine( doc->zoomItX( QMAX( rect.left(),   cellRect.x() ) ),
02864                           doc->zoomItY( QMAX( rect.top(),    cellRect.y() + dt ) ),
02865                           doc->zoomItX( QMIN( rect.right(),  cellRect.x() ) ),
02866                           doc->zoomItY( QMIN( rect.bottom(), cellRect.bottom() - db ) ) );
02867     }
02868     else {
02869       if ( sheetDir == Sheet::RightToLeft )
02870         painter.drawLine( doc->zoomItX( cellRect.right() ),
02871                           doc->zoomItY( cellRect.y() + dt ),
02872                           doc->zoomItX( cellRect.right() ),
02873                           doc->zoomItY( cellRect.bottom() - db ) );
02874       else
02875         painter.drawLine( doc->zoomItX( cellRect.x() ),
02876                           doc->zoomItY( cellRect.y() + dt ),
02877                           doc->zoomItX( cellRect.x() ),
02878                           doc->zoomItY( cellRect.bottom() - db ) );
02879     }
02880   }
02881 
02882 
02883   // The top border.
02884   if ( paintTop ) {
02885     int dl = 0;
02886     int dr = 0;
02887 
02888     #if 0
02889     if ( cellRef.y() > 1 ) {
02890       Cell  *cell_north = format()->sheet()->cellAt( cellRef.x(),
02891                  cellRef.y() - 1 );
02892 
02893       QPen l = cell_north->effLeftBorderPen(  cellRef.x(), cellRef.y() - 1 );
02894       QPen r = cell_north->effRightBorderPen( cellRef.x(), cellRef.y() - 1 );
02895 
02896       if ( l.style() != Qt::NoPen )
02897         dl = ( l.width() - 1 ) / 2 + 1;
02898       if ( r.style() != Qt::NoPen )
02899         dr = r.width() / 2;
02900     }
02901     #endif
02902 
02903 
02904 
02905     // If we are on paper printout, we limit the length of the lines.
02906     // On paper, we always have full cells, on screen not.
02907     if ( paintingToExternalDevice ) {
02908       painter.drawLine( doc->zoomItX( QMAX( rect.left(),   cellRect.x() + dl ) ),
02909                         doc->zoomItY( QMAX( rect.top(),    cellRect.y() ) ),
02910                         doc->zoomItX( QMIN( rect.right(),  cellRect.right() - dr ) ),
02911                         doc->zoomItY( QMIN( rect.bottom(), cellRect.y() ) ) );
02912     }
02913     else {
02914       painter.drawLine( doc->zoomItX( cellRect.x() + dl ),
02915                         doc->zoomItY( cellRect.y() ),
02916                         doc->zoomItX( cellRect.right() - dr ),
02917                         doc->zoomItY( cellRect.y() ) );
02918     }
02919   }
02920 
02921 
02922   // The right border.
02923   if ( paintRight ) {
02924     int dt = 0;
02925     int db = 0;
02926 
02927     #if 0
02928     if ( cellRef.x() < KS_colMax ) {
02929       Cell  *cell_east = format()->sheet()->cellAt( cellRef.x() + 1,
02930                 cellRef.y() );
02931 
02932       QPen t = cell_east->effTopBorderPen(    cellRef.x() + 1, cellRef.y() );
02933       QPen b = cell_east->effBottomBorderPen( cellRef.x() + 1, cellRef.y() );
02934 
02935       if ( t.style() != Qt::NoPen )
02936         dt = ( t.width() + 1 ) / 2;
02937       if ( b.style() != Qt::NoPen )
02938         db = ( t.width() / 2);
02939     }
02940     #endif
02941 
02942     //painter.setPen( QPen( sheet()->doc()->gridColor(), 1, Qt::SolidLine ) );
02943 
02944     // If we are on paper printout, we limit the length of the lines.
02945     // On paper, we always have full cells, on screen not.
02946     if ( painter.device()->isExtDev() )     {
02947       if ( sheetDir == Sheet::RightToLeft )
02948         painter.drawLine( doc->zoomItX( QMAX( rect.left(),   cellRect.x() ) ),
02949                           doc->zoomItY( QMAX( rect.top(),    cellRect.y() + dt ) ),
02950                           doc->zoomItX( QMIN( rect.right(),  cellRect.x() ) ),
02951                           doc->zoomItY( QMIN( rect.bottom(), cellRect.bottom() - db ) ) );
02952       else
02953         painter.drawLine( doc->zoomItX( QMAX( rect.left(),   cellRect.right() ) ),
02954                           doc->zoomItY( QMAX( rect.top(),    cellRect.y() + dt ) ),
02955                           doc->zoomItX( QMIN( rect.right(),  cellRect.right() ) ),
02956                           doc->zoomItY( QMIN( rect.bottom(), cellRect.bottom() - db ) ) );
02957     }
02958     else {
02959       if ( sheetDir == Sheet::RightToLeft )
02960         painter.drawLine( doc->zoomItX( cellRect.x() ),
02961                           doc->zoomItY( cellRect.y() + dt ),
02962                           doc->zoomItX( cellRect.x() ),
02963                           doc->zoomItY( cellRect.bottom() - db ) );
02964       else
02965         painter.drawLine( doc->zoomItX( cellRect.right() ),
02966                           doc->zoomItY( cellRect.y() + dt ),
02967                           doc->zoomItX( cellRect.right() ),
02968                           doc->zoomItY( cellRect.bottom() - db ) );
02969     }
02970   }
02971 
02972   // The bottom border.
02973   /*if ( paintBottom ) {
02974     int dl = 0;
02975     int dr = 0;
02976     if ( cellRef.y() < KS_rowMax ) {
02977       Cell  *cell_south = format()->sheet()->cellAt( cellRef.x(),
02978                  cellRef.y() + 1 );
02979 
02980       QPen l = cell_south->effLeftBorderPen(  cellRef.x(), cellRef.y() + 1 );
02981       QPen r = cell_south->effRightBorderPen( cellRef.x(), cellRef.y() + 1 );
02982 
02983       if ( l.style() != Qt::NoPen )
02984         dl = ( l.width() - 1 ) / 2 + 1;
02985       if ( r.style() != Qt::NoPen )
02986         dr = r.width() / 2;
02987     }
02988 
02989     painter.setPen( QPen( sheet()->doc()->gridColor(), 1, Qt::SolidLine ) );
02990 
02991     // If we are on paper printout, we limit the length of the lines.
02992     // On paper, we always have full cells, on screen not.
02993     if ( painter.device()->isExtDev() ) {
02994       painter.drawLine( doc->zoomItX( QMAX( rect.left(),   cellRect.x() + dl ) ),
02995                         doc->zoomItY( QMAX( rect.top(),    cellRect.bottom() ) ),
02996                         doc->zoomItX( QMIN( rect.right(),  cellRect.right() - dr ) ),
02997                         doc->zoomItY( QMIN( rect.bottom(), cellRect.bottom() ) ) );
02998     }
02999     else {
03000       painter.drawLine( doc->zoomItX( cellRect.x() + dl ),
03001                         doc->zoomItY( cellRect.bottom() ),
03002                         doc->zoomItX( cellRect.right() - dr ),
03003                         doc->zoomItY( cellRect.bottom() ) );
03004     }
03005   }*/
03006 }
03007 
03008 
03009 // Paint a comment indicator if the cell has a comment.
03010 //
03011 void Cell::paintCommentIndicator( QPainter& painter,
03012           const KoRect &cellRect,
03013           const QPoint &/*cellRef*/,
03014           QColor &backgroundColor )
03015 {
03016   Doc * doc = sheet()->doc();
03017 
03018   // Point the little corner if there is a comment attached
03019   // to this cell.
03020   if ( ( format()->propertiesMask() & (uint) Format::PComment )
03021        && cellRect.width() > 10.0
03022        && cellRect.height() > 10.0
03023        && ( sheet()->print()->printCommentIndicator()
03024            || ( !painter.device()->isExtDev() && sheet()->getShowCommentIndicator() ) ) ) {
03025     QColor penColor = Qt::red;
03026 
03027     // If background has high red part, switch to blue.
03028     if ( qRed( backgroundColor.rgb() ) > 127 &&
03029          qGreen( backgroundColor.rgb() ) < 80 &&
03030          qBlue( backgroundColor.rgb() ) < 80 )
03031     {
03032         penColor = Qt::blue;
03033     }
03034 
03035     // Get the triangle.
03036     QPointArray  point( 3 );
03037     if ( format()->sheet()->layoutDirection()==Sheet::RightToLeft ) {
03038       point.setPoint( 0, doc->zoomItX( cellRect.x() + 6.0 ),
03039                          doc->zoomItY( cellRect.y() ) );
03040       point.setPoint( 1, doc->zoomItX( cellRect.x() ),
03041                          doc->zoomItY( cellRect.y() ) );
03042       point.setPoint( 2, doc->zoomItX( cellRect.x() ),
03043                          doc->zoomItY( cellRect.y() + 6.0 ) );
03044     }
03045     else {
03046       point.setPoint( 0, doc->zoomItX( cellRect.right() - 5.0 ),
03047                          doc->zoomItY( cellRect.y() ) );
03048       point.setPoint( 1, doc->zoomItX( cellRect.right() ),
03049                          doc->zoomItY( cellRect.y() ) );
03050       point.setPoint( 2, doc->zoomItX( cellRect.right() ),
03051                          doc->zoomItY( cellRect.y() + 5.0 ) );
03052     }
03053 
03054     // And draw it.
03055     painter.setBrush( QBrush( penColor ) );
03056     painter.setPen( Qt::NoPen );
03057     painter.drawPolygon( point );
03058   }
03059 }
03060 
03061 
03062 
03063 // Paint a small rectangle if this cell holds a formula.
03064 //
03065 void Cell::paintFormulaIndicator( QPainter& painter,
03066           const KoRect &cellRect,
03067           QColor &backgroundColor )
03068 {
03069   if ( isFormula() &&
03070       format()->sheet()->getShowFormulaIndicator() &&
03071       cellRect.width()  > 10.0 &&
03072       cellRect.height() > 10.0 )
03073   {
03074     Doc* doc = sheet()->doc();
03075 
03076     QColor penColor = Qt::blue;
03077     // If background has high blue part, switch to red.
03078     if ( qRed( backgroundColor.rgb() ) < 80 &&
03079         qGreen( backgroundColor.rgb() ) < 80 &&
03080         qBlue( backgroundColor.rgb() ) > 127 )
03081     {
03082         penColor = Qt::red;
03083     }
03084 
03085     // Get the triangle...
03086     QPointArray point( 3 );
03087     if ( format()->sheet()->layoutDirection()==Sheet::RightToLeft ) {
03088       point.setPoint( 0, doc->zoomItX( cellRect.right() - 6.0 ),
03089                          doc->zoomItY( cellRect.bottom() ) );
03090       point.setPoint( 1, doc->zoomItX( cellRect.right() ),
03091                          doc->zoomItY( cellRect.bottom() ) );
03092       point.setPoint( 2, doc->zoomItX( cellRect.right() ),
03093                          doc->zoomItY( cellRect.bottom() - 6.0 ) );
03094     }
03095     else {
03096       point.setPoint( 0, doc->zoomItX( cellRect.x() ),
03097                          doc->zoomItY( cellRect.bottom() - 6.0 ) );
03098       point.setPoint( 1, doc->zoomItX( cellRect.x() ),
03099                          doc->zoomItY( cellRect.bottom() ) );
03100       point.setPoint( 2, doc->zoomItX( cellRect.x() + 6.0 ),
03101                          doc->zoomItY( cellRect.bottom() ) );
03102     }
03103 
03104     // ...and draw it.
03105     painter.setBrush( QBrush( penColor ) );
03106     painter.setPen( Qt::NoPen );
03107     painter.drawPolygon( point );
03108   }
03109 }
03110 
03111 
03112 // Paint an indicator that the text in the cell is cut.
03113 //
03114 void Cell::paintMoreTextIndicator( QPainter& painter,
03115            const KoRect &cellRect,
03116            QColor &backgroundColor )
03117 {
03118   // Show a red triangle when it's not possible to write all text in cell.
03119   // Don't print the red triangle if we're printing.
03120   if( testFlag( Flag_CellTooShortX ) &&
03121       !painter.device()->isExtDev() &&
03122       cellRect.height() > 4.0  &&
03123       cellRect.width()  > 4.0 )
03124   {
03125     Doc* doc = sheet()->doc();
03126 
03127     QColor penColor = Qt::red;
03128     // If background has high red part, switch to blue.
03129     if ( qRed( backgroundColor.rgb() ) > 127
03130    && qGreen( backgroundColor.rgb() ) < 80
03131    && qBlue( backgroundColor.rgb() ) < 80 )
03132     {
03133       penColor = Qt::blue;
03134     }
03135 
03136     // Get the triangle...
03137     QPointArray point( 3 );
03138     if ( d->strOutText.isRightToLeft() ) {
03139       point.setPoint( 0, doc->zoomItX( cellRect.left() + 4.0 ),
03140                          doc->zoomItY( cellRect.y() + cellRect.height() / 2.0 -4.0 ) );
03141       point.setPoint( 1, doc->zoomItX( cellRect.left() ),
03142                          doc->zoomItY( cellRect.y() + cellRect.height() / 2.0 ));
03143       point.setPoint( 2, doc->zoomItX( cellRect.left() + 4.0 ),
03144                          doc->zoomItY( cellRect.y() + cellRect.height() / 2.0 +4.0 ) );
03145     }
03146     else {
03147       point.setPoint( 0, doc->zoomItX( cellRect.right() - 4.0 ),
03148                          doc->zoomItY( cellRect.y() + cellRect.height() / 2.0 - 4.0 ) );
03149       point.setPoint( 1, doc->zoomItX( cellRect.right() ),
03150                          doc->zoomItY( cellRect.y() + cellRect.height() / 2.0 ) );
03151       point.setPoint( 2, doc->zoomItX( cellRect.right() - 4.0 ),
03152                          doc->zoomItY( cellRect.y() + cellRect.height() / 2.0 + 4.0 ) );
03153     }
03154 
03155     // ...and paint it.
03156     painter.setBrush( QBrush( penColor ) );
03157     painter.setPen( Qt::NoPen );
03158     painter.drawPolygon( point );
03159   }
03160 }
03161 
03162 
03163 // Paint the real contents of a cell - the text.
03164 //
03165 void Cell::paintText( QPainter& painter,
03166           const KoRect &cellRect,
03167           const QPoint &cellRef )
03168 {
03169   Doc    *doc = sheet()->doc();
03170 
03171   ColumnFormat  *colFormat         = format()->sheet()->columnFormat( cellRef.x() );
03172 
03173   QColorGroup    defaultColorGroup = QApplication::palette().active();
03174   QColor         textColorPrint    = effTextColor( cellRef.x(), cellRef.y() );
03175 
03176   // Resolve the text color if invalid (=default).
03177   if ( !textColorPrint.isValid() ) {
03178     if ( painter.device()->isExtDev() )
03179       textColorPrint = Qt::black;
03180     else
03181       textColorPrint = QApplication::palette().active().text();
03182   }
03183 
03184   QPen tmpPen( textColorPrint );
03185 
03186   // Set the font according to the current zoom.
03187   applyZoomedFont( painter, cellRef.x(), cellRef.y() );
03188 
03189   // Check for red font color for negative values.
03190   if ( !d->hasExtra()
03191        || !d->extra()->conditions
03192        || !d->extra()->conditions->matchedStyle() ) {
03193     if ( value().isNumber()
03194          && !( format()->sheet()->getShowFormula()
03195                && !( format()->sheet()->isProtected()
03196          && format()->isHideFormula( d->column, d->row ) ) ) )
03197     {
03198       double v = value().asFloat();
03199       if ( format()->floatColor( cellRef.x(), cellRef.y()) == Format::NegRed
03200      && v < 0.0 )
03201         tmpPen.setColor( Qt::red );
03202     }
03203   }
03204 
03205   // Check for blue color, for hyperlink.
03206   if ( !link().isEmpty() ) {
03207     tmpPen.setColor( QApplication::palette().active().link() );
03208     QFont f = painter.font();
03209     f.setUnderline( true );
03210     painter.setFont( f );
03211   }
03212 
03213 #if 0
03214 /****
03215 
03216  For now I am commenting this out -- with the default color display you
03217  can read normal text through a highlighted background.  Maybe this isn't
03218  always the case, though, and we can put the highlighted text color back in.
03219  In that case, we need to somewhere in here figure out if the text overlaps
03220  another cell outside of the selection, otherwise that portion of the text
03221  will be printed white on white.  So just that portion would need to be
03222  painted again in the normal color.
03223 
03224  This should probably be done eventually, anyway, because I like using the
03225  reverse text color for highlighted cells.  I just don't like extending the
03226  cell 'highlight' background outside of the selection rectangle because it
03227  looks REALLY ugly.
03228 */
03229 
03230   if ( selected && ( cellRef.x() != marker.x() || cellRef.y() != marker.y() ) )
03231   {
03232     QPen p( tmpPen );
03233     p.setColor( defaultColorGroup.highlightedText() );
03234     painter.setPen( p );
03235   }
03236   else {
03237     painter.setPen(tmpPen);
03238   }
03239 #endif
03240   painter.setPen( tmpPen );
03241 
03242   QString  tmpText   = d->strOutText;
03243   double   tmpHeight = d->textHeight;
03244   double   tmpWidth  = d->textWidth;
03245 
03246   // If the cell is to narrow to paint the whole contents, then pick
03247   // out a part of the content that we paint.  The result of this is
03248   // dependent on the data type of the content.
03249   //
03250   // FIXME: Make this dependent on the height as well.
03251   //
03252   if ( testFlag( Flag_CellTooShortX ) ) {
03253     d->strOutText = textDisplaying( painter );
03254 
03255     // Recalculate the text width and the offset.
03256     textSize( painter );
03257     offsetAlign( column(), row() );
03258   }
03259 
03260   // Hide zero.
03261   if ( format()->sheet()->getHideZero()
03262        && value().isNumber()
03263        && value().asFloat() == 0 ) {
03264     d->strOutText = QString::null;
03265   }
03266 
03267   // Clear extra cell if column or row is hidden
03268   //
03269   // FIXME: I think this should be done before the call to
03270   // textDisplaying() above.
03271   //
03272   if ( colFormat->isHide() || ( cellRect.height() <= 2 ) ) {
03273     freeAllObscuredCells();  /* TODO: This looks dangerous...must check when I
03274                                 have time */
03275     d->strOutText = "";
03276   }
03277 
03278   double indent = 0.0;
03279   double offsetCellTooShort = 0.0;
03280   int a = effAlignX();
03281 
03282   // Apply indent if text is align to left not when text is at right or middle.
03283   if (  a == Format::Left && !isEmpty() ) {
03284     // FIXME: The following condition should be remade into a call to
03285     //        a new convenience function:
03286     //   if ( hasConditionStyleFeature( Style::SIndent, true )...
03287     //        This should be done throughout the entire file.
03288     //
03289     if ( d->hasExtra()
03290    && d->extra()->conditions
03291          && d->extra()->conditions->matchedStyle()
03292          && d->extra()->conditions->matchedStyle()->hasFeature( Style::SIndent, true ) )
03293       indent = d->extra()->conditions->matchedStyle()->indent();
03294     else
03295       indent = format()->getIndent( column(), row() );
03296   }
03297 
03298   // Made an offset, otherwise ### is under red triangle.
03299   if ( a == Format::Right && !isEmpty() && testFlag( Flag_CellTooShortX ) )
03300     offsetCellTooShort = format()->sheet()->doc()->unzoomItX( 4 );
03301 
03302   QFontMetrics fm2 = painter.fontMetrics();
03303   double offsetFont = 0.0;
03304 
03305   if ( format()->alignY( column(), row() ) == Format::Bottom
03306        && format()->textFontUnderline( column(), row() ) )
03307     offsetFont = format()->sheet()->doc()->unzoomItX( fm2.underlinePos() + 1 );
03308 
03309   int  tmpAngle;
03310   bool tmpMultiRow;
03311   bool tmpVerticalText;
03312 
03313   // Check for angled or vertical text.
03314   if ( d->hasExtra()
03315        && d->extra()->conditions
03316        && d->extra()->conditions->matchedStyle() )
03317   {
03318     Style  *matchedStyle = d->extra()->conditions->matchedStyle();
03319 
03320     if ( matchedStyle->hasFeature( Style::SAngle, true ) )
03321       tmpAngle = d->extra()->conditions->matchedStyle()->rotateAngle();
03322     else
03323       tmpAngle = format()->getAngle( cellRef.x(), cellRef.y() );
03324 
03325     if ( matchedStyle->hasFeature( Style::SVerticalText, true ) )
03326       tmpVerticalText = matchedStyle->hasProperty( Style::PVerticalText );
03327     else
03328       tmpVerticalText = format()->verticalText( cellRef.x(), cellRef.y() );
03329 
03330     if ( matchedStyle->hasFeature( Style::SMultiRow, true ) )
03331       tmpMultiRow = matchedStyle->hasProperty( Style::PMultiRow );
03332     else
03333       tmpMultiRow = format()->multiRow( cellRef.x(), cellRef.y() );
03334   }
03335   else {
03336     tmpAngle        = format()->getAngle( cellRef.x(), cellRef.y() );
03337     tmpVerticalText = format()->verticalText( cellRef.x(), cellRef.y() );
03338     tmpMultiRow     = format()->multiRow( cellRef.x(), cellRef.y() );
03339   }
03340 
03341   // Actually paint the text.
03342   //    There are 4 possible cases:
03343   //        - One line of text , horizontal
03344   //        - Angled text
03345   //        - Multiple rows of text , horizontal
03346   //        - Vertical text
03347   if ( !tmpMultiRow && !tmpVerticalText && !tmpAngle ) {
03348     // Case 1: The simple case, one line, no angle.
03349 
03350     painter.drawText( doc->zoomItX( indent + cellRect.x() + d->textX - offsetCellTooShort ),
03351                       doc->zoomItY( cellRect.y() + d->textY - offsetFont ), d->strOutText );
03352   }
03353   else if ( tmpAngle != 0 ) {
03354     // Case 2: an angle.
03355 
03356     int angle = tmpAngle;
03357     QFontMetrics fm = painter.fontMetrics();
03358 
03359     painter.rotate( angle );
03360     double x;
03361     if ( angle > 0 )
03362       x = indent + cellRect.x() + d->textX;
03363     else
03364       x = indent + cellRect.x() + d->textX
03365         - doc->unzoomItX((int) (( fm.descent() + fm.ascent() ) * sin( angle * M_PI / 180 )));
03366     double y;
03367     if ( angle > 0 )
03368       y = cellRect.y() + d->textY;
03369     else
03370       y = cellRect.y() + d->textY + d->textHeight;
03371     painter.drawText( doc->zoomItX( x * cos( angle * M_PI / 180 ) +
03372                                     y * sin( angle * M_PI / 180 ) ),
03373                       doc->zoomItY( -x * sin( angle * M_PI / 180 ) +
03374                                      y * cos( angle * M_PI / 180 ) ),
03375                       d->strOutText );
03376     painter.rotate( -angle );
03377   }
03378   else if ( tmpMultiRow && !tmpVerticalText ) {
03379     // Case 3: Multiple rows, but horizontal.
03380 
03381     QString t;
03382     int i;
03383     int pos = 0;
03384     double dy = 0.0;
03385     QFontMetrics fm = painter.fontMetrics();
03386     do {
03387       i = d->strOutText.find( "\n", pos );
03388       if ( i == -1 )
03389         t = d->strOutText.mid( pos, d->strOutText.length() - pos );
03390       else {
03391         t = d->strOutText.mid( pos, i - pos );
03392         pos = i + 1;
03393       }
03394 
03395       int align = effAlignX();
03396       if ( format()->sheet()->getShowFormula()
03397      && !( format()->sheet()->isProtected()
03398      && format()->isHideFormula( d->column, d->row ) ) )
03399         align = Format::Left;
03400 
03401       // #### Torben: This looks duplicated for me
03402       switch ( align ) {
03403        case Format::Left:
03404         d->textX = effLeftBorderPen( cellRef.x(), cellRef.y() ).width() + BORDER_SPACE;
03405         break;
03406 
03407        case Format::Right:
03408         d->textX = cellRect.width() - BORDER_SPACE - doc->unzoomItX( fm.width( t ) )
03409           - effRightBorderPen( cellRef.x(), cellRef.y() ).width();
03410         break;
03411 
03412        case Format::Center:
03413         d->textX = ( cellRect.width() - doc->unzoomItX( fm.width( t ) ) ) / 2;
03414       }
03415 
03416       painter.drawText( doc->zoomItX( indent + cellRect.x() + d->textX ),
03417                         doc->zoomItY( cellRect.y() + d->textY + dy ), t );
03418       dy += doc->unzoomItY( fm.descent() + fm.ascent() );
03419     } while ( i != -1 );
03420   }
03421   else if ( tmpVerticalText && !d->strOutText.isEmpty() ) {
03422     // Case 4: Vertical text.
03423 
03424     QString t;
03425     int i = 0;
03426     int len = 0;
03427     double dy = 0.0;
03428     QFontMetrics fm = painter.fontMetrics();
03429     do {
03430       len = d->strOutText.length();
03431       t = d->strOutText.at( i );
03432       painter.drawText( doc->zoomItX( indent + cellRect.x() + d->textX ),
03433                         doc->zoomItY( cellRect.y() + d->textY + dy ), t );
03434       dy += doc->unzoomItY( fm.descent() + fm.ascent() );
03435       i++;
03436     } while ( i != len );
03437   }
03438 
03439   // Check for too short cell and set the outText for future reference.
03440   if ( testFlag( Flag_CellTooShortX ) ) {
03441     d->strOutText = tmpText;
03442     d->textHeight = tmpHeight;
03443     d->textWidth  = tmpWidth;
03444   }
03445 
03446   if ( format()->sheet()->getHideZero() && value().isNumber()
03447        && value().asFloat() == 0 )
03448     d->strOutText = tmpText;
03449 
03450   if ( colFormat->isHide() || ( cellRect.height() <= 2 ) )
03451     d->strOutText = tmpText;
03452 }
03453 
03454 
03455 // Paint page borders on the page.  Only do this on the screen.
03456 //
03457 void Cell::paintPageBorders( QPainter& painter,
03458            const KoRect &cellRect,
03459            const QPoint &cellRef,
03460            bool paintBorderRight,
03461            bool paintBorderBottom )
03462 {
03463   // Not screen?  Return immediately.
03464   if ( painter.device()->isExtDev() )
03465     return;
03466 
03467   if ( ! format()->sheet()->isShowPageBorders() )
03468     return;
03469 
03470   SheetPrint* print = format()->sheet()->print();
03471 
03472   Sheet::LayoutDirection sheetDir =  format()->sheet()->layoutDirection();
03473 
03474   Doc* doc = sheet()->doc();
03475   int zcellRect_left = doc->zoomItX (cellRect.left());
03476   int zcellRect_right = doc->zoomItX (cellRect.right());
03477   int zcellRect_top = doc->zoomItY (cellRect.top());
03478   int zcellRect_bottom = doc->zoomItY (cellRect.bottom());
03479 
03480   // Draw page borders
03481 
03482   if ( cellRef.x() >= print->printRange().left()
03483        && cellRef.x() <= print->printRange().right() + 1
03484        && cellRef.y() >= print->printRange().top()
03485        && cellRef.y() <= print->printRange().bottom() + 1 )
03486   {
03487     if ( print->isOnNewPageX( cellRef.x() )
03488    && cellRef.y() <= print->printRange().bottom() )
03489     {
03490       painter.setPen( sheet()->doc()->pageBorderColor() );
03491 
03492       if ( sheetDir == Sheet::RightToLeft )
03493         painter.drawLine( zcellRect_right, zcellRect_top,
03494                           zcellRect_right, zcellRect_bottom );
03495       else
03496         painter.drawLine( zcellRect_left, zcellRect_top,
03497                           zcellRect_left, zcellRect_bottom );
03498     }
03499 
03500     if ( print->isOnNewPageY( cellRef.y() ) &&
03501           ( cellRef.x() <= print->printRange().right() ) )
03502     {
03503       painter.setPen( sheet()->doc()->pageBorderColor() );
03504       painter.drawLine( zcellRect_left,  zcellRect_top,
03505                         zcellRect_right, zcellRect_top );
03506     }
03507 
03508     if ( paintBorderRight ) {
03509       if ( print->isOnNewPageX( cellRef.x() + 1 )
03510             && cellRef.y() <= print->printRange().bottom() ) {
03511         painter.setPen( sheet()->doc()->pageBorderColor() );
03512 
03513         if ( sheetDir == Sheet::RightToLeft )
03514           painter.drawLine( zcellRect_left, zcellRect_top,
03515                             zcellRect_left, zcellRect_bottom );
03516         else
03517           painter.drawLine( zcellRect_right, zcellRect_top,
03518                             zcellRect_right, zcellRect_bottom );
03519       }
03520     }
03521 
03522     if ( paintBorderBottom ) {
03523       if ( print->isOnNewPageY( cellRef.y() + 1 )
03524           && cellRef.x() <= print->printRange().right() ) {
03525         painter.setPen( sheet()->doc()->pageBorderColor() );
03526         painter.drawLine( zcellRect_left,  zcellRect_bottom,
03527                           zcellRect_right, zcellRect_bottom );
03528       }
03529     }
03530   }
03531 }
03532 
03533 
03534 // Paint the cell borders.
03535 //
03536 void Cell::paintCellBorders( QPainter& painter, const KoRect& rect,
03537            const KoRect &cellRect,
03538            const QPoint &cellRef,
03539            bool paintRight, bool paintBottom,
03540            bool paintLeft,  bool paintTop,
03541            QPen & _rightPen, QPen & _bottomPen,
03542            QPen & _leftPen,  QPen & _topPen )
03543 {
03544 
03545   //Sanity check: If we are not painting any of the borders then the function
03546   //really shouldn't be called at all.
03547   if ( (!paintLeft) && (!paintRight) && (!paintTop) && (!paintBottom) )
03548         return;
03549 
03550   Doc * doc = sheet()->doc();
03551 
03552   Sheet::LayoutDirection sheetDir =  format()->sheet()->layoutDirection();
03553 
03554   // compute zoomed rectangles
03555   // I don't use KoRect, because that ends up producing lots of warnings
03556   // about double->int conversions in calls to painter.drawLine
03557   int zrect_left (doc->zoomItX (rect.left()));
03558   int zrect_right (doc->zoomItX (rect.right()));
03559   int zrect_top (doc->zoomItY (rect.top()));
03560   int zrect_bottom (doc->zoomItY (rect.bottom()));
03561   int zcellRect_left (doc->zoomItX (cellRect.left()));
03562   int zcellRect_right (doc->zoomItX (cellRect.right()));
03563   int zcellRect_top (doc->zoomItY (cellRect.top()));
03564   int zcellRect_bottom (doc->zoomItY (cellRect.bottom()));
03565 
03566   /* we might not paint some borders if this cell is merged with another in
03567      that direction
03568   bool paintLeft   = paintBorderLeft;
03569   bool paintRight  = paintBorderRight;
03570   bool paintTop    = paintBorderTop;
03571   bool paintBottom = paintBorderBottom;
03572   */
03573 
03574   // paintRight  = paintRight  && ( extraXCells() == 0 );
03575   // paintBottom = paintBottom && ( d->extra()->extraYCells() == 0 );
03576 
03577   if (d->hasExtra()) {
03578     QValueList<Cell*>::const_iterator it  = d->extra()->obscuringCells.begin();
03579     QValueList<Cell*>::const_iterator end = d->extra()->obscuringCells.end();
03580     for ( ; it != end; ++it ) {
03581       Cell* cell = *it;
03582 
03583       int xDiff = cellRef.x() - cell->column();
03584       int yDiff = cellRef.y() - cell->row();
03585       paintLeft = paintLeft && xDiff == 0;
03586       paintTop  = paintTop  && yDiff == 0;
03587 
03588       // Paint the border(s) if either this one should or if we have a
03589       // merged cell with this cell as its border.
03590       paintRight  = paintRight  && cell->mergedXCells() == xDiff;
03591       paintBottom = paintBottom && cell->mergedYCells() == yDiff;
03592     }
03593   }
03594 
03595   // Must create copies of these since otherwise the zoomIt()
03596   // operation will be performed on them repeatedly.
03597   QPen  leftPen( _leftPen );
03598   QPen  rightPen( _rightPen );
03599   QPen  topPen( _topPen );
03600   QPen  bottomPen( _bottomPen );
03601 
03602   // Determine the pens that should be used for drawing
03603   // the borders.
03604   //
03605   int left_penWidth   = QMAX( 1, doc->zoomItX( leftPen.width() ) );
03606   int right_penWidth  = QMAX( 1, doc->zoomItX( rightPen.width() ) );
03607   int top_penWidth    = QMAX( 1, doc->zoomItY( topPen.width() ) );
03608   int bottom_penWidth = QMAX( 1, doc->zoomItY( bottomPen.width() ) );
03609 
03610   leftPen.setWidth( left_penWidth );
03611   rightPen.setWidth( right_penWidth );
03612   topPen.setWidth( top_penWidth );
03613   bottomPen.setWidth( bottom_penWidth );
03614 
03615   if ( paintLeft && leftPen.style() != Qt::NoPen ) {
03616     int top = ( QMAX( 0, -1 + top_penWidth ) ) / 2 +
03617               ( ( QMAX( 0, -1 + top_penWidth ) ) % 2 );
03618     int bottom = ( QMAX( 0, -1 + bottom_penWidth ) ) / 2 + 1;
03619 
03620     painter.setPen( leftPen );
03621 
03622     //kdDebug(36001) << "    painting left border of cell " << name() << endl;
03623 
03624     // If we are on paper printout, we limit the length of the lines.
03625     // On paper, we always have full cells, on screen not.
03626     if ( painter.device()->isExtDev() ) {
03627       // FIXME: There is probably Cut&Paste bugs here as well as below.
03628       //        The QMIN/QMAX and left/right pairs don't really make sense.
03629       //
03630       //    UPDATE: In fact, most of these QMIN/QMAX combinations
03631       //            are TOTALLY BOGUS.  For one thing, the idea
03632       //            that we always have full cells on paper is wrong
03633       //            since we can have embedded sheets in e.g. kword,
03634       //            and those can be arbitrarily clipped.  WE HAVE TO
03635       //            REVISE THIS WHOLE BORDER PAINTING SECTION!
03636       //
03637       if ( sheetDir == Sheet::RightToLeft )
03638         painter.drawLine( QMIN( zrect_right,  zcellRect_right ),
03639                           QMAX( zrect_top,    zcellRect_top - top ),
03640                           QMIN( zrect_right,  zcellRect_right ),
03641                           QMIN( zrect_bottom, zcellRect_bottom + bottom ) );
03642       else
03643         painter.drawLine( QMAX( zrect_left,   zcellRect_left ),
03644                           QMAX( zrect_top,    zcellRect_top - top ),
03645                           QMAX( zrect_left,   zcellRect_left ),
03646                           QMIN( zrect_bottom, zcellRect_bottom + bottom ) );
03647     }
03648     else {
03649       if ( sheetDir == Sheet::RightToLeft )
03650         painter.drawLine( zcellRect_right,
03651                           zcellRect_top - top,
03652                           zcellRect_right,
03653                           zcellRect_bottom + bottom );
03654       else
03655         painter.drawLine( zcellRect_left,
03656                           zcellRect_top - top,
03657                           zcellRect_left,
03658                           zcellRect_bottom + bottom );
03659     }
03660   }
03661 
03662   if ( paintRight && rightPen.style() != Qt::NoPen ) {
03663     int top = ( QMAX( 0, -1 + top_penWidth ) ) / 2 +
03664               ( ( QMAX( 0, -1 + top_penWidth ) ) % 2 );
03665     int bottom = ( QMAX( 0, -1 + bottom_penWidth ) ) / 2 + 1;
03666 
03667     painter.setPen( rightPen );
03668 
03669     //kdDebug(36001) << "    painting right border of cell " << name() << endl;
03670 
03671     // If we are on paper printout, we limit the length of the lines.
03672     // On paper, we always have full cells, on screen not.
03673     if ( painter.device()->isExtDev() ) {
03674       if ( sheetDir == Sheet::RightToLeft )
03675         painter.drawLine( QMAX( zrect_left, zcellRect_left ),
03676                           QMAX( zrect_top, zcellRect_top - top ),
03677                           QMAX( zrect_left, zcellRect_left ),
03678                           QMIN( zrect_bottom, zcellRect_bottom + bottom ) );
03679       else {
03680   // FIXME: This is the way all these things should look.
03681   //        Make it so.
03682   //
03683   // Only print the right border if it is visible.
03684   if ( zcellRect_right <= zrect_right + right_penWidth / 2)
03685     painter.drawLine( zcellRect_right,
03686           QMAX( zrect_top, zcellRect_top - top ),
03687           zcellRect_right,
03688           QMIN( zrect_bottom, zcellRect_bottom + bottom ) );
03689       }
03690     }
03691     else {
03692       if ( sheetDir == Sheet::RightToLeft )
03693         painter.drawLine( zcellRect_left,
03694                           zcellRect_top - top,
03695                           zcellRect_left,
03696                           zcellRect_bottom + bottom );
03697       else
03698         painter.drawLine( zcellRect_right,
03699                           zcellRect_top - top,
03700                           zcellRect_right,
03701                           zcellRect_bottom + bottom );
03702     }
03703   }
03704 
03705   if ( paintTop && topPen.style() != Qt::NoPen ) {
03706     painter.setPen( topPen );
03707 
03708     //kdDebug(36001) << "    painting top border of cell " << name()
03709     //       << " [" << zcellRect_left << "," << zcellRect_right
03710     //       << ": " << zcellRect_right - zcellRect_left << "]" << endl;
03711 
03712     // If we are on paper printout, we limit the length of the lines.
03713     // On paper, we always have full cells, on screen not.
03714     if ( painter.device()->isExtDev() ) {
03715       if ( zcellRect_top >= zrect_top + top_penWidth / 2)
03716   painter.drawLine( QMAX( zrect_left,   zcellRect_left ),
03717         zcellRect_top,
03718         QMIN( zrect_right,  zcellRect_right ),
03719         zcellRect_top );
03720     }
03721     else {
03722       painter.drawLine( zcellRect_left, zcellRect_top,
03723                         zcellRect_right, zcellRect_top );
03724     }
03725   }
03726 
03727   if ( paintBottom && bottomPen.style() != Qt::NoPen ) {
03728     painter.setPen( bottomPen );
03729 
03730     //kdDebug(36001) << "    painting bottom border of cell " << name()
03731     //       << " [" << zcellRect_left << "," << zcellRect_right
03732     //       << ": " << zcellRect_right - zcellRect_left << "]" << endl;
03733 
03734     // If we are on paper printout, we limit the length of the lines.
03735     // On paper, we always have full cells, on screen not.
03736     if ( painter.device()->isExtDev() ) {
03737       if ( zcellRect_bottom <= zrect_bottom + bottom_penWidth / 2)
03738   painter.drawLine( QMAX( zrect_left,   zcellRect_left ),
03739         zcellRect_bottom,
03740         QMIN( zrect_right,  zcellRect_right ),
03741         zcellRect_bottom );
03742     }
03743     else {
03744       painter.drawLine( zcellRect_left, zcellRect_bottom,
03745                         zcellRect_right, zcellRect_bottom );
03746     }
03747   }
03748 
03749   // FIXME: Look very closely at when the following code is really needed.
03750   //        I can't really see any case, but I might be wrong.
03751   //        Since the code below is buggy, and incredibly complex,
03752   //        I am currently disabling it.  If somebody wants to enable
03753   //        it again, then please also solve bug 68977: "Embedded KSpread
03754   //        document printing problem" at the same time.
03755   return;
03756 
03757 #if 0
03758   // Look at the cells on our corners. It may happen that we
03759   // just erased parts of their borders corner, so we might need
03760   // to repaint these corners.
03761   //
03762   QPen  vert_pen, horz_pen;
03763   int   vert_penWidth, horz_penWidth;
03764 
03765   // Some useful referenses.
03766   Cell  *cell_north     = format()->sheet()->cellAt( cellRef.x(),
03767                  cellRef.y() - 1 );
03768   Cell  *cell_northwest = format()->sheet()->cellAt( cellRef.x() - 1,
03769                  cellRef.y() - 1 );
03770   Cell  *cell_west      = format()->sheet()->cellAt( cellRef.x() - 1,
03771                  cellRef.y() );
03772   Cell  *cell_northeast = format()->sheet()->cellAt( cellRef.x() + 1,
03773                  cellRef.y() - 1 );
03774   Cell  *cell_east      = format()->sheet()->cellAt( cellRef.x() + 1,
03775                  cellRef.y() );
03776   Cell  *cell_south     = format()->sheet()->cellAt( cellRef.x(),
03777                  cellRef.y() + 1 );
03778   Cell  *cell_southwest = format()->sheet()->cellAt( cellRef.x() - 1,
03779                  cellRef.y() + 1 );
03780   Cell  *cell_southeast = format()->sheet()->cellAt( cellRef.x() + 1,
03781                  cellRef.y() + 1 );
03782 
03783   // Fix the borders which meet at the top left corner
03784   if ( cell_north->effLeftBorderValue( cellRef.x(), cellRef.y() - 1 )
03785        >= cell_northwest->effRightBorderValue( cellRef.x() - 1, cellRef.y() - 1 ) )
03786     vert_pen = cell_north->effLeftBorderPen( cellRef.x(), cellRef.y() - 1 );
03787   else
03788     vert_pen = cell_northwest->effRightBorderPen( cellRef.x() - 1,
03789               cellRef.y() - 1 );
03790 
03791   vert_penWidth = QMAX( 1, doc->zoomItX( vert_pen.width() ) );
03792   vert_pen.setWidth( vert_penWidth );
03793 
03794   if ( vert_pen.style() != Qt::NoPen ) {
03795     if ( cell_west->effTopBorderValue( cellRef.x() - 1, cellRef.y() )
03796          >= cell_northwest->effBottomBorderValue( cellRef.x() - 1, cellRef.y() - 1 ) )
03797       horz_pen = cell_west->effTopBorderPen( cellRef.x() - 1, cellRef.y() );
03798     else
03799       horz_pen = cell_northwest->effBottomBorderPen( cellRef.x() - 1,
03800                  cellRef.y() - 1 );
03801 
03802     horz_penWidth = QMAX( 1, doc->zoomItY( horz_pen.width() ) );
03803     int bottom = ( QMAX( 0, -1 + horz_penWidth ) ) / 2 + 1;
03804 
03805     painter.setPen( vert_pen );
03806     // If we are on paper printout, we limit the length of the lines.
03807     // On paper, we always have full cells, on screen not.
03808     if ( painter.device()->isExtDev() ) {
03809       if ( sheetDir == Sheet::RightToLeft )
03810         painter.drawLine( QMAX( zrect_left, zcellRect_right ),
03811                           QMAX( zrect_top, zcellRect_top ),
03812                           QMIN( zrect_right, zcellRect_right ),
03813                           QMIN( zrect_bottom, zcellRect_top + bottom ) );
03814       else
03815         painter.drawLine( QMAX( zrect_left, zcellRect_left ),
03816                           QMAX( zrect_top, zcellRect_top ),
03817                           QMIN( zrect_right, zcellRect_left ),
03818                           QMIN( zrect_bottom, zcellRect_top + bottom ) );
03819     }
03820     else {
03821       if ( sheetDir == Sheet::RightToLeft )
03822         painter.drawLine( zcellRect_right, zcellRect_top,
03823                           zcellRect_right, zcellRect_top + bottom );
03824       else
03825         painter.drawLine( zcellRect_left, zcellRect_top,
03826                           zcellRect_left, zcellRect_top + bottom );
03827     }
03828   }
03829 
03830   // Fix the borders which meet at the top right corner
03831   if ( cell_north->effRightBorderValue( cellRef.x(), cellRef.y() - 1 )
03832        >= cell_northeast->effLeftBorderValue( cellRef.x() + 1,
03833                 cellRef.y() - 1 ) )
03834     vert_pen = cell_north->effRightBorderPen( cellRef.x(), cellRef.y() - 1 );
03835   else
03836     vert_pen = cell_northeast->effLeftBorderPen( cellRef.x() + 1,
03837              cellRef.y() - 1 );
03838 
03839   // vert_pen = effRightBorderPen( cellRef.x(), cellRef.y() - 1 );
03840   vert_penWidth = QMAX( 1, doc->zoomItX( vert_pen.width() ) );
03841   vert_pen.setWidth( vert_penWidth );
03842   if ( ( vert_pen.style() != Qt::NoPen ) && ( cellRef.x() < KS_colMax ) ) {
03843     if ( cell_east->effTopBorderValue( cellRef.x() + 1, cellRef.y() )
03844          >= cell_northeast->effBottomBorderValue( cellRef.x() + 1,
03845               cellRef.y() - 1 ) )
03846       horz_pen = cell_east->effTopBorderPen( cellRef.x() + 1, cellRef.y() );
03847     else
03848       horz_pen = cell_northeast->effBottomBorderPen( cellRef.x() + 1,
03849                  cellRef.y() - 1 );
03850 
03851     // horz_pen = effTopBorderPen( cellRef.x() + 1, cellRef.y() );
03852     horz_penWidth = QMAX( 1, doc->zoomItY( horz_pen.width() ) );
03853     int bottom = ( QMAX( 0, -1 + horz_penWidth ) ) / 2 + 1;
03854 
03855     painter.setPen( vert_pen );
03856     //If we are on paper printout, we limit the length of the lines
03857     //On paper, we always have full cells, on screen not
03858     if ( painter.device()->isExtDev() ) {
03859       if ( sheetDir == Sheet::RightToLeft )
03860         painter.drawLine( QMAX( zrect_left, zcellRect_left ),
03861                           QMAX( zrect_top, zcellRect_top ),
03862                           QMIN( zrect_right, zcellRect_left ),
03863                           QMIN( zrect_bottom, zcellRect_top + bottom ) );
03864       else
03865         painter.drawLine( QMAX( zrect_left, zcellRect_right ),
03866                           QMAX( zrect_top, zcellRect_top ),
03867                           QMIN( zrect_right, zcellRect_right ),
03868                           QMIN( zrect_bottom, zcellRect_top + bottom ) );
03869     }
03870     else {
03871       if ( sheetDir == Sheet::RightToLeft )
03872         painter.drawLine( zcellRect_left, zcellRect_top,
03873                           zcellRect_left, zcellRect_top + bottom );
03874       else
03875         painter.drawLine( zcellRect_right, zcellRect_top,
03876                           zcellRect_right, zcellRect_top + bottom );
03877     }
03878   }
03879 
03880   // Bottom
03881   if ( cellRef.y() < KS_rowMax ) {
03882     // Fix the borders which meet at the bottom left corner
03883     if ( cell_south->effLeftBorderValue( cellRef.x(), cellRef.y() + 1 )
03884          >= cell_southwest->effRightBorderValue( cellRef.x() - 1,
03885              cellRef.y() + 1 ) )
03886       vert_pen = cell_south->effLeftBorderPen( cellRef.x(), cellRef.y() + 1 );
03887     else
03888       vert_pen = cell_southwest->effRightBorderPen( cellRef.x() - 1,
03889                 cellRef.y() + 1 );
03890 
03891     // vert_pen = effLeftBorderPen( cellRef.x(), cellRef.y() + 1 );
03892     vert_penWidth = QMAX( 1, doc->zoomItY( vert_pen.width() ) );
03893     vert_pen.setWidth( vert_penWidth );
03894     if ( vert_pen.style() != Qt::NoPen ) {
03895       if ( cell_west->effBottomBorderValue( cellRef.x() - 1, cellRef.y() )
03896            >= cell_southwest->effTopBorderValue( cellRef.x() - 1,
03897              cellRef.y() + 1 ) )
03898         horz_pen = cell_west->effBottomBorderPen( cellRef.x() - 1,
03899               cellRef.y() );
03900       else
03901         horz_pen = cell_southwest->effTopBorderPen( cellRef.x() - 1,
03902                 cellRef.y() + 1 );
03903 
03904       // horz_pen = effBottomBorderPen( cellRef.x() - 1, cellRef.y() );
03905       horz_penWidth = QMAX( 1, doc->zoomItX( horz_pen.width() ) );
03906       int bottom = ( QMAX( 0, -1 + horz_penWidth ) ) / 2;
03907 
03908       painter.setPen( vert_pen );
03909       // If we are on paper printout, we limit the length of the lines.
03910       // On paper, we always have full cells, on screen not.
03911       if ( painter.device()->isExtDev() ) {
03912         if ( sheetDir == Sheet::RightToLeft )
03913           painter.drawLine( QMAX( zrect_left, zcellRect_right ),
03914                             QMAX( zrect_top, zcellRect_bottom - bottom ),
03915                             QMIN( zrect_right, zcellRect_right ),
03916                             QMIN( zrect_bottom, zcellRect_bottom ) );
03917         else
03918           painter.drawLine( QMAX( zrect_left, zcellRect_left ),
03919                             QMAX( zrect_top, zcellRect_bottom - bottom ),
03920                             QMIN( zrect_right, zcellRect_left ),
03921                             QMIN( zrect_bottom, zcellRect_bottom ) );
03922       }
03923       else {
03924         if ( sheetDir == Sheet::RightToLeft )
03925           painter.drawLine( zcellRect_right, zcellRect_bottom - bottom,
03926                             zcellRect_right, zcellRect_bottom );
03927         else
03928           painter.drawLine( zcellRect_left, zcellRect_bottom - bottom,
03929                             zcellRect_left, zcellRect_bottom );
03930       }
03931     }
03932 
03933     // Fix the borders which meet at the bottom right corner
03934     if ( cell_south->effRightBorderValue( cellRef.x(), cellRef.y() + 1 )
03935          >= cell_southeast->effLeftBorderValue( cellRef.x() + 1,
03936             cellRef.y() + 1 ) )
03937       vert_pen = cell_south->effRightBorderPen( cellRef.x(), cellRef.y() + 1 );
03938     else
03939       vert_pen = cell_southeast->effLeftBorderPen( cellRef.x() + 1,
03940                cellRef.y() + 1 );
03941 
03942     // vert_pen = effRightBorderPen( cellRef.x(), cellRef.y() + 1 );
03943     vert_penWidth = QMAX( 1, doc->zoomItY( vert_pen.width() ) );
03944     vert_pen.setWidth( vert_penWidth );
03945     if ( ( vert_pen.style() != Qt::NoPen ) && ( cellRef.x() < KS_colMax ) ) {
03946       if ( cell_east ->effBottomBorderValue( cellRef.x() + 1, cellRef.y() )
03947            >= cell_southeast->effTopBorderValue( cellRef.x() + 1,
03948              cellRef.y() + 1 ) )
03949 
03950         horz_pen = format()->sheet()->cellAt( cellRef.x() + 1, cellRef.y() )
03951     ->effBottomBorderPen( cellRef.x() + 1, cellRef.y() );
03952       else
03953         horz_pen = format()->sheet()->cellAt( cellRef.x() + 1, cellRef.y() + 1 )
03954     ->effTopBorderPen( cellRef.x() + 1, cellRef.y() + 1 );
03955 
03956       // horz_pen = effBottomBorderPen( cellRef.x() + 1, cellRef.y() );
03957       horz_penWidth = QMAX( 1, doc->zoomItX( horz_pen.width() ) );
03958       int bottom = ( QMAX( 0, -1 + horz_penWidth ) ) / 2;
03959 
03960       painter.setPen( vert_pen );
03961       // If we are on paper printout, we limit the length of the lines.
03962       // On paper, we always have full cells, on screen not.
03963       if ( painter.device()->isExtDev() )      {
03964         if ( sheetDir == Sheet::RightToLeft )
03965           painter.drawLine( QMAX( zrect_left, zcellRect_left ),
03966                             QMAX( zrect_top, zcellRect_bottom - bottom ),
03967                             QMIN( zrect_right, zcellRect_left ),
03968                             QMIN( zrect_bottom, zcellRect_bottom ) );
03969         else
03970           painter.drawLine( QMAX( zrect_left, zcellRect_right ),
03971                             QMAX( zrect_top, zcellRect_bottom - bottom ),
03972                             QMIN( zrect_right, zcellRect_right ),
03973                             QMIN( zrect_bottom, zcellRect_bottom ) );
03974       }
03975       else {
03976         if ( sheetDir == Sheet::RightToLeft )
03977           painter.drawLine( zcellRect_left, zcellRect_bottom - bottom,
03978                             zcellRect_left, zcellRect_bottom );
03979         else
03980           painter.drawLine( zcellRect_right, zcellRect_bottom - bottom,
03981                             zcellRect_right, zcellRect_bottom );
03982       }
03983     }
03984   }
03985   #endif
03986 }
03987 
03988 
03989 // Paint diagonal lines through the cell.
03990 //
03991 void Cell::paintCellDiagonalLines( QPainter& painter,
03992            const KoRect &cellRect,
03993            const QPoint &cellRef )
03994 {
03995   if ( isPartOfMerged() )
03996     return;
03997 
03998   Doc* doc = sheet()->doc();
03999 
04000   if ( effFallDiagonalPen( cellRef.x(), cellRef.y() ).style() != Qt::NoPen ) {
04001     painter.setPen( effFallDiagonalPen( cellRef.x(), cellRef.y() ) );
04002     painter.drawLine( doc->zoomItX( cellRect.x() ),
04003           doc->zoomItY( cellRect.y() ),
04004           doc->zoomItX( cellRect.right() ),
04005           doc->zoomItY( cellRect.bottom() ) );
04006   }
04007 
04008   if ( effGoUpDiagonalPen( cellRef.x(), cellRef.y() ).style() != Qt::NoPen ) {
04009     painter.setPen( effGoUpDiagonalPen( cellRef.x(), cellRef.y() ) );
04010     painter.drawLine( doc->zoomItX( cellRect.x() ),
04011           doc->zoomItY( cellRect.bottom() ),
04012           doc->zoomItX( cellRect.right() ),
04013           doc->zoomItY( cellRect.y() ) );
04014   }
04015 }
04016 
04017 
04018 //                        End of Painting
04019 // ================================================================
04020 
04021 
04022 int Cell::defineAlignX()
04023 {
04024   int a = format()->align( column(), row() );
04025   if ( a == Format::Undefined )
04026   {
04027     //numbers should be right-aligned by default, as well as BiDi text
04028     if ((formatType() == Text_format) || value().isString())
04029       a = (d->strOutText.isRightToLeft()) ?
04030                                Format::Right : Format::Left;
04031     else {
04032       Value val = value();
04033       while (val.isArray()) val = val.element (0, 0);
04034       if (val.isBoolean() || val.isNumber())
04035         a = Format::Right;
04036       else
04037         a = Format::Left;
04038     }
04039   }
04040   return a;
04041 }
04042 
04043 int Cell::effAlignX()
04044 {
04045   if ( d->hasExtra() && d->extra()->conditions
04046        && d->extra()->conditions->matchedStyle()
04047        && d->extra()->conditions->matchedStyle()->hasFeature( Style::SAlignX, true ) )
04048     return d->extra()->conditions->matchedStyle()->alignX();
04049 
04050   return defineAlignX();
04051 }
04052 
04053 // Cut strOutText, so that it only holds the part that can be displayed.
04054 //
04055 // Used in paintText().
04056 //
04057 
04058 QString Cell::textDisplaying( QPainter &_painter )
04059 {
04060   QFontMetrics  fm = _painter.fontMetrics();
04061   int           a  = format()->align( column(), row() );
04062 
04063   bool isNumeric = value().isNumber();
04064 
04065   if ( !format()->verticalText( column(),row() ) ) {
04066     // Non-vertical text: the ordinary case.
04067 
04068     // Not enough space but align to left
04069     double  len = 0.0;
04070     int     extraXCells = d->hasExtra() ? d->extra()->extraXCells : 0;
04071 
04072     for ( int i = column(); i <= column() + extraXCells; i++ ) {
04073       ColumnFormat *cl2 = format()->sheet()->columnFormat( i );
04074       len += cl2->dblWidth() - 1.0; //-1.0 because the pixel in between 2 cells is shared between both cells
04075     }
04076 
04077     QString  tmp;
04078     double   tmpIndent = 0.0;
04079     if ( !isEmpty() )
04080       tmpIndent = format()->getIndent( column(), row() );
04081 
04082     // Start out with the whole text, cut one character at a time, and
04083     // when the text finally fits, return it.
04084     for ( int i = d->strOutText.length(); i != 0; i-- )
04085     {
04086       //Note that numbers are always treated as left-aligned since if we have to cut digits off, they should
04087       //always be the least significant ones at the end of the string
04088       if ( a == Format::Left || a == Format::Undefined || isNumeric)
04089         tmp = d->strOutText.left(i);
04090       else if ( a == Format::Right)
04091         tmp = d->strOutText.right(i);
04092       else
04093         tmp = d->strOutText.mid( ( d->strOutText.length() - i ) / 2, i);
04094 
04095       if (isNumeric)
04096       {
04097       //For numeric values, we can cut off digits after the decimal point to make it fit,
04098       //but not the integer part of the number.
04099       //If this number still contains a fraction part then we don't need to do anything, if we have run
04100       //out of space to fit even the integer part of the number then display #########
04101       //TODO Perhaps try to display integer part in standard form if there is not enough room for it?
04102 
04103       if (!tmp.contains('.'))
04104         d->strOutText=QString().fill('#',20);
04105       }
04106 
04107       // 4 equal length of red triangle +1 point.
04108       if ( format()->sheet()->doc()->unzoomItX( fm.width( tmp ) ) + tmpIndent
04109      < len - 4.0 - 1.0 )
04110       {
04111     if ( format()->getAngle( column(), row() ) != 0 )
04112     {
04113             QString tmp2;
04114             RowFormat *rl = format()->sheet()->rowFormat( row() );
04115             if ( d->textHeight > rl->dblHeight() )
04116         {
04117                 for ( int j = d->strOutText.length(); j != 0; j-- )
04118             {
04119                     tmp2 = d->strOutText.left( j );
04120                     if ( format()->sheet()->doc()->unzoomItY( fm.width( tmp2 ) ) < rl->dblHeight() - 1.0 )
04121                     {
04122                         return d->strOutText.left( QMIN( tmp.length(), tmp2.length() ) );
04123                     }
04124                 }
04125             }
04126             else
04127                 return tmp;
04128 
04129     }
04130     else
04131             return tmp;
04132       }
04133     }
04134     return QString( "" );
04135   }
04136   else if ( format()->verticalText( column(), row() ) ) {
04137     // Vertical text.
04138 
04139     RowFormat  *rl = format()->sheet()->rowFormat( row() );
04140     double      tmpIndent = 0.0;
04141 
04142     // Not enough space but align to left.
04143     double  len = 0.0;
04144     int     extraXCells = d->hasExtra() ? d->extra()->extraXCells : 0;
04145 
04146     for ( int i = column(); i <= column() + extraXCells; i++ ) {
04147       ColumnFormat  *cl2 = format()->sheet()->columnFormat( i );
04148 
04149       // -1.0 because the pixel in between 2 cells is shared between both cells
04150       len += cl2->dblWidth() - 1.0;
04151     }
04152 
04153     if ( !isEmpty() )
04154       tmpIndent = format()->getIndent( column(), row() );
04155 
04156     if ( ( d->textWidth + tmpIndent > len ) || d->textWidth == 0.0 )
04157       return QString( "" );
04158 
04159     for ( int i = d->strOutText.length(); i != 0; i-- ) {
04160       if ( format()->sheet()->doc()->unzoomItY( fm.ascent() + fm.descent() ) * i
04161      < rl->dblHeight() - 1.0 )
04162   return d->strOutText.left( i );
04163     }
04164 
04165     return QString( "" );
04166   }
04167 
04168   ColumnFormat  *cl = format()->sheet()->columnFormat( column() );
04169   double         w = cl->dblWidth();
04170 
04171   if ( d->hasExtra() && (d->extra()->extraWidth != 0.0) )
04172     w = d->extra()->extraWidth;
04173 
04174   QString tmp;
04175   for ( int i = d->strOutText.length(); i != 0; i-- ) {
04176     tmp = d->strOutText.left( i );
04177 
04178     // 4 equals lenght of red triangle +1 pixel
04179     if ( format()->sheet()->doc()->unzoomItX( fm.width( tmp ) ) < w - 4.0 - 1.0 )
04180       return tmp;
04181   }
04182 
04183   return  QString::null;
04184 }
04185 
04186 
04187 double Cell::dblWidth( int _col, const Canvas *_canvas ) const
04188 {
04189   if ( _col < 0 )
04190     _col = d->column;
04191 
04192   if ( _canvas )
04193   {
04194     if ( testFlag(Flag_Merged) )
04195       return d->extra()->extraWidth;
04196 
04197     const ColumnFormat *cl = format()->sheet()->columnFormat( _col );
04198     return cl->dblWidth( _canvas );
04199   }
04200 
04201   if ( testFlag(Flag_Merged) )
04202     return d->extra()->extraWidth;
04203 
04204   const ColumnFormat *cl = format()->sheet()->columnFormat( _col );
04205   return cl->dblWidth();
04206 }
04207 
04208 int Cell::width( int _col, const Canvas *_canvas ) const
04209 {
04210   return int( dblWidth( _col, _canvas ) );
04211 }
04212 
04213 double Cell::dblHeight( int _row, const Canvas *_canvas ) const
04214 {
04215   if ( _row < 0 )
04216     _row = d->row;
04217 
04218   if ( _canvas )
04219   {
04220     if ( testFlag(Flag_Merged) )
04221       return d->extra()->extraHeight;
04222 
04223     const RowFormat *rl = format()->sheet()->rowFormat( _row );
04224     return rl->dblHeight( _canvas );
04225   }
04226 
04227   if ( testFlag(Flag_Merged) )
04228     return d->extra()->extraHeight;
04229 
04230   const RowFormat *rl = format()->sheet()->rowFormat( _row );
04231   return rl->dblHeight();
04232 }
04233 
04234 int Cell::height( int _row, const Canvas *_canvas ) const
04235 {
04236   return int( dblHeight( _row, _canvas ) );
04237 }
04238 
04240 //
04241 // Misc Properties.
04242 // Reimplementation of Format methods.
04243 //
04245 
04246 const QBrush& Cell::backGroundBrush( int _col, int _row ) const
04247 {
04248   if ( d->hasExtra() && (!d->extra()->obscuringCells.isEmpty()) )
04249   {
04250     const Cell* cell = d->extra()->obscuringCells.first();
04251     return cell->backGroundBrush( cell->column(), cell->row() );
04252   }
04253 
04254   return format()->backGroundBrush( _col, _row );
04255 }
04256 
04257 const QColor& Cell::bgColor( int _col, int _row ) const
04258 {
04259   if ( d->hasExtra() && (!d->extra()->obscuringCells.isEmpty()) )
04260   {
04261     const Cell* cell = d->extra()->obscuringCells.first();
04262     return cell->bgColor( cell->column(), cell->row() );
04263   }
04264 
04265   return format()->bgColor( _col, _row );
04266 }
04267 
04269 //
04270 // Borders.
04271 // Reimplementation of Format methods.
04272 //
04274 
04275 void Cell::setLeftBorderPen( const QPen& p )
04276 {
04277   if ( column() == 1 )
04278   {
04279     Cell* cell = format()->sheet()->cellAt( column() - 1, row() );
04280     if ( cell && cell->format()->hasProperty( Format::PRightBorder )
04281          && format()->sheet()->cellAt( column(), row() ) == this )
04282         cell->format()->clearProperty( Format::PRightBorder );
04283   }
04284 
04285   format()->setLeftBorderPen( p );
04286 }
04287 
04288 void Cell::setTopBorderPen( const QPen& p )
04289 {
04290   if ( row() == 1 )
04291   {
04292     Cell* cell = format()->sheet()->cellAt( column(), row() - 1 );
04293     if ( cell && cell->format()->hasProperty( Format::PBottomBorder )
04294          && format()->sheet()->cellAt( column(), row() ) == this )
04295         cell->format()->clearProperty( Format::PBottomBorder );
04296   }
04297   format()->setTopBorderPen( p );
04298 }
04299 
04300 void Cell::setRightBorderPen( const QPen& p )
04301 {
04302     Cell* cell = 0L;
04303     if ( column() < KS_colMax )
04304         cell = format()->sheet()->cellAt( column() + 1, row() );
04305 
04306     if ( cell && cell->format()->hasProperty( Format::PLeftBorder )
04307          && format()->sheet()->cellAt( column(), row() ) == this )
04308         cell->format()->clearProperty( Format::PLeftBorder );
04309 
04310     format()->setRightBorderPen( p );
04311 }
04312 
04313 void Cell::setBottomBorderPen( const QPen& p )
04314 {
04315     Cell* cell = 0L;
04316     if ( row() < KS_rowMax )
04317         cell = format()->sheet()->cellAt( column(), row() + 1 );
04318 
04319     if ( cell && cell->format()->hasProperty( Format::PTopBorder )
04320          && format()->sheet()->cellAt( column(), row() ) == this )
04321         cell->format()->clearProperty( Format::PTopBorder );
04322 
04323     format()->setBottomBorderPen( p );
04324 }
04325 
04326 const QPen& Cell::rightBorderPen( int _col, int _row ) const
04327 {
04328     if ( !format()->hasProperty( Format::PRightBorder ) && ( _col < KS_colMax ) )
04329     {
04330         Cell * cell = format()->sheet()->cellAt( _col + 1, _row );
04331         if ( cell && cell->format()->hasProperty( Format::PLeftBorder ) )
04332             return cell->leftBorderPen( _col + 1, _row );
04333     }
04334 
04335     return format()->rightBorderPen( _col, _row );
04336 }
04337 
04338 const QPen& Cell::leftBorderPen( int _col, int _row ) const
04339 {
04340     if ( !format()->hasProperty( Format::PLeftBorder ) )
04341     {
04342         const Cell * cell = format()->sheet()->cellAt( _col - 1, _row );
04343         if ( cell && cell->format()->hasProperty( Format::PRightBorder ) )
04344             return cell->rightBorderPen( _col - 1, _row );
04345     }
04346 
04347     return format()->leftBorderPen( _col, _row );
04348 }
04349 
04350 const QPen& Cell::bottomBorderPen( int _col, int _row ) const
04351 {
04352     if ( !format()->hasProperty( Format::PBottomBorder ) && ( _row < KS_rowMax ) )
04353     {
04354         const Cell * cell = format()->sheet()->cellAt( _col, _row + 1 );
04355         if ( cell && cell->format()->hasProperty( Format::PTopBorder ) )
04356             return cell->topBorderPen( _col, _row + 1 );
04357     }
04358 
04359     return format()->bottomBorderPen( _col, _row );
04360 }
04361 
04362 const QPen& Cell::topBorderPen( int _col, int _row ) const
04363 {
04364     if ( !format()->hasProperty( Format::PTopBorder ) )
04365     {
04366         const Cell * cell = format()->sheet()->cellAt( _col, _row - 1 );
04367         if ( cell->format()->hasProperty( Format::PBottomBorder ) )
04368             return cell->bottomBorderPen( _col, _row - 1 );
04369     }
04370 
04371     return format()->topBorderPen( _col, _row );
04372 }
04373 
04374 const QColor & Cell::effTextColor( int col, int row ) const
04375 {
04376   if ( d->hasExtra() && d->extra()->conditions
04377        && d->extra()->conditions->matchedStyle()
04378        && d->extra()->conditions->matchedStyle()->hasFeature( Style::STextPen, true ) )
04379     return d->extra()->conditions->matchedStyle()->pen().color();
04380 
04381   return format()->textColor( col, row );
04382 }
04383 
04384 const QPen& Cell::effLeftBorderPen( int col, int row ) const
04385 {
04386   if ( isPartOfMerged() )
04387   {
04388     Cell * cell = d->extra()->obscuringCells.first();
04389     return cell->effLeftBorderPen( cell->column(), cell->row() );
04390   }
04391 
04392   if ( d->hasExtra() && d->extra()->conditions
04393        && d->extra()->conditions->matchedStyle()
04394        && d->extra()->conditions->matchedStyle()->hasFeature( Style::SLeftBorder, true ) )
04395     return d->extra()->conditions->matchedStyle()->leftBorderPen();
04396 
04397   return format()->leftBorderPen( col, row );
04398 }
04399 
04400 const QPen& Cell::effTopBorderPen( int col, int row ) const
04401 {
04402   if ( isPartOfMerged() )
04403   {
04404     Cell * cell = d->extra()->obscuringCells.first();
04405     return cell->effTopBorderPen( cell->column(), cell->row() );
04406   }
04407 
04408   if ( d->hasExtra() && d->extra()->conditions
04409        && d->extra()->conditions->matchedStyle()
04410        && d->extra()->conditions->matchedStyle()->hasFeature( Style::STopBorder, true ) )
04411     return d->extra()->conditions->matchedStyle()->topBorderPen();
04412 
04413   return format()->topBorderPen( col, row );
04414 }
04415 
04416 const QPen& Cell::effRightBorderPen( int col, int row ) const
04417 {
04418   if ( isPartOfMerged() )
04419   {
04420     Cell * cell = d->extra()->obscuringCells.first();
04421     return cell->effRightBorderPen( cell->column(), cell->row() );
04422   }
04423 
04424   if ( d->hasExtra() && d->extra()->conditions
04425        && d->extra()->conditions->matchedStyle()
04426        && d->extra()->conditions->matchedStyle()->hasFeature( Style::SRightBorder, true ) )
04427     return d->extra()->conditions->matchedStyle()->rightBorderPen();
04428 
04429   return format()->rightBorderPen( col, row );
04430 }
04431 
04432 const QPen& Cell::effBottomBorderPen( int col, int row ) const
04433 {
04434   if ( isPartOfMerged() )
04435   {
04436     Cell * cell = d->extra()->obscuringCells.first();
04437     return cell->effBottomBorderPen( cell->column(), cell->row() );
04438   }
04439 
04440   if ( d->hasExtra() && d->extra()->conditions
04441        && d->extra()->conditions->matchedStyle()
04442        && d->extra()->conditions->matchedStyle()->hasFeature( Style::SBottomBorder, true ) )
04443     return d->extra()->conditions->matchedStyle()->bottomBorderPen();
04444 
04445   return format()->bottomBorderPen( col, row );
04446 }
04447 
04448 const QPen & Cell::effGoUpDiagonalPen( int col, int row ) const
04449 {
04450   if ( d->hasExtra() && d->extra()->conditions
04451        && d->extra()->conditions->matchedStyle()
04452        && d->extra()->conditions->matchedStyle()->hasFeature( Style::SGoUpDiagonal, true ) )
04453     return d->extra()->conditions->matchedStyle()->goUpDiagonalPen();
04454 
04455   return format()->goUpDiagonalPen( col, row );
04456 }
04457 
04458 const QPen & Cell::effFallDiagonalPen( int col, int row ) const
04459 {
04460   if ( d->hasExtra() && d->extra()->conditions
04461        && d->extra()->conditions->matchedStyle()
04462        && d->extra()->conditions->matchedStyle()->hasFeature( Style::SFallDiagonal, true ) )
04463     return d->extra()->conditions->matchedStyle()->fallDiagonalPen();
04464 
04465   return format()->fallDiagonalPen( col, row );
04466 }
04467 
04468 uint Cell::effBottomBorderValue( int col, int row ) const
04469 {
04470   if ( isPartOfMerged() )
04471   {
04472     Cell * cell = d->extra()->obscuringCells.first();
04473     return cell->effBottomBorderValue( cell->column(), cell->row() );
04474   }
04475 
04476   if ( d->hasExtra() && d->extra()->conditions
04477       && d->extra()->conditions->matchedStyle() )
04478     return d->extra()->conditions->matchedStyle()->bottomPenValue();
04479 
04480   return format()->bottomBorderValue( col, row );
04481 }
04482 
04483 uint Cell::effRightBorderValue( int col, int row ) const
04484 {
04485   if ( isPartOfMerged() )
04486   {
04487     Cell * cell = d->extra()->obscuringCells.first();
04488     return cell->effRightBorderValue( cell->column(), cell->row() );
04489   }
04490 
04491   if ( d->hasExtra() && d->extra()->conditions
04492       && d->extra()->conditions->matchedStyle() )
04493     return d->extra()->conditions->matchedStyle()->rightPenValue();
04494 
04495   return format()->rightBorderValue( col, row );
04496 }
04497 
04498 uint Cell::effLeftBorderValue( int col, int row ) const
04499 {
04500   if ( isPartOfMerged() )
04501   {
04502     Cell * cell = d->extra()->obscuringCells.first();
04503     return cell->effLeftBorderValue( cell->column(), cell->row() );
04504   }
04505 
04506   if ( d->hasExtra() && d->extra()->conditions
04507       && d->extra()->conditions->matchedStyle() )
04508     return d->extra()->conditions->matchedStyle()->leftPenValue();
04509 
04510   return format()->leftBorderValue( col, row );
04511 }
04512 
04513 uint Cell::effTopBorderValue( int col, int row ) const
04514 {
04515   if ( isPartOfMerged() )
04516   {
04517     Cell * cell = d->extra()->obscuringCells.first();
04518     return cell->effTopBorderValue( cell->column(), cell->row() );
04519   }
04520 
04521   if ( d->hasExtra() && d->extra()->conditions
04522       && d->extra()->conditions->matchedStyle() )
04523     return d->extra()->conditions->matchedStyle()->topPenValue();
04524 
04525   return format()->topBorderValue( col, row );
04526 }
04527 
04529 //
04530 // Precision
04531 //
04533 
04534 void Cell::incPrecision()
04535 {
04536   //TODO: This is ugly. Why not simply regenerate the text to display? Tomas
04537 
04538   if ( !value().isNumber() )
04539     return;
04540   int tmpPreci = format()->precision( column(), row() );
04541 
04542   if ( tmpPreci == -1 )
04543   {
04544     int pos = d->strOutText.find(decimal_point);
04545     if ( pos == -1 )
04546         pos = d->strOutText.find('.');
04547     if ( pos == -1 )
04548       format()->setPrecision(1);
04549     else
04550     {
04551       int start = 0;
04552       if ( d->strOutText.find('%') != -1 )
04553         start = 2;
04554       else if ( d->strOutText.find(locale()->currencySymbol()) == ((int)(d->strOutText.length()-locale()->currencySymbol().length())) )
04555         start = locale()->currencySymbol().length() + 1;
04556       else if ( (start=d->strOutText.find('E')) != -1 )
04557         start = d->strOutText.length() - start;
04558 
04559       //kdDebug(36001) << "start=" << start << " pos=" << pos << " length=" << d->strOutText.length() << endl;
04560       format()->setPrecision( QMAX( 0, (int)d->strOutText.length() - start - pos ) );
04561     }
04562   }
04563   else if ( tmpPreci < 10 )
04564   {
04565     format()->setPrecision( ++tmpPreci );
04566   }
04567   setFlag(Flag_LayoutDirty);
04568 }
04569 
04570 void Cell::decPrecision()
04571 {
04572   //TODO: This is ugly. Why not simply regenerate the text to display? Tomas
04573 
04574   if ( !value().isNumber() )
04575     return;
04576   int preciTmp = format()->precision( column(), row() );
04577 //  kdDebug(36001) << "decPrecision: tmpPreci = " << tmpPreci << endl;
04578   if ( format()->precision(column(),row()) == -1 )
04579   {
04580     int pos = d->strOutText.find( decimal_point );
04581     int start = 0;
04582     if ( d->strOutText.find('%') != -1 )
04583         start = 2;
04584     else if ( d->strOutText.find(locale()->currencySymbol()) == ((int)(d->strOutText.length()-locale()->currencySymbol().length())) )
04585         start = locale()->currencySymbol().length() + 1;
04586     else if ( (start = d->strOutText.find('E')) != -1 )
04587         start = d->strOutText.length() - start;
04588     else
04589         start = 0;
04590 
04591     if ( pos == -1 )
04592       return;
04593 
04594     format()->setPrecision(d->strOutText.length() - pos - 2 - start);
04595     //   if ( preciTmp < 0 )
04596     //      format()->setPrecision( preciTmp );
04597   }
04598   else if ( preciTmp > 0 )
04599   {
04600     format()->setPrecision( --preciTmp );
04601   }
04602   setFlag( Flag_LayoutDirty );
04603 }
04604 
04605 //set numerical value
04606 //used in Sheet::setSeries (nowhere else yet)
04607 void Cell::setNumber( double number )
04608 {
04609   setValue( Value( number ) );
04610 
04611   d->strText.setNum( number );
04612   setDisplayText(d->strText);
04613   checkNumberFormat();
04614 }
04615 
04616 void Cell::setCellText( const QString& _text, bool asText )
04617 {
04618  // QString ctext = _text;
04619 
04620 // (Tomas) is this trim necessary for anything ?
04621 //  if( ctext.length() > 5000 )
04622 //    ctext = ctext.left( 5000 );
04623 
04624   // empty string ?
04625   if (_text.length() == 0) {
04626     d->strOutText = d->strText = "";
04627     setValue (Value::empty());
04628     return;
04629   }
04630 
04631   // as text ?
04632   if (asText) {
04633     d->strOutText = _text;
04634     d->strText    = _text;
04635     setValue (Value (_text));
04636 
04637     return;
04638   }
04639 
04640   QString oldText = d->strText;
04641   setDisplayText( _text );
04642   if(!format()->sheet()->isLoading() && !testValidity() )
04643   {
04644     //reapply old value if action == stop
04645     setDisplayText( oldText );
04646   }
04647 }
04648 
04649 void Cell::setDisplayText( const QString& _text )
04650 {
04651   bool isLoading = format()->sheet()->isLoading();
04652 
04653   if (!isLoading)
04654     format()->sheet()->doc()->emitBeginOperation( false );
04655 
04656   d->strText = _text;
04657 
04661   if ( !d->strText.isEmpty() && d->strText[0] == '=' )
04662   {
04663     setFlag(Flag_LayoutDirty);
04664     setFlag(Flag_TextFormatDirty);
04665 
04666     if ( !makeFormula() )
04667       kdError(36001) << "ERROR: Syntax ERROR" << endl;
04668     setCalcDirtyFlag ();
04669   }
04670 
04674   else
04675   {
04676     // Find out what data type it is
04677     checkTextInput();
04678 
04679     setFlag(Flag_LayoutDirty);
04680     setFlag(Flag_TextFormatDirty);
04681   }
04682 
04683   if ( !isLoading )
04684     format()->sheet()->doc()->emitEndOperation( QRect( d->column, d->row, 1, 1 ) );
04685 }
04686 
04687 void Cell::setLink( const QString& link )
04688 {
04689   d->extra()->link = link;
04690 
04691   if( !link.isEmpty() && d->strText.isEmpty() )
04692     setCellText( link );
04693 }
04694 
04695 QString Cell::link() const
04696 {
04697   return d->hasExtra() ? d->extra()->link : QString::null;
04698 }
04699 
04700 void Cell::update()
04701 {
04702   /* those obscuring us need to redo their layout cause they can't obscure us
04703      now that we've got text.
04704      This includes cells obscuring cells that we are obscuring
04705   */
04706   for (int x = d->column; x <= d->column + extraXCells(); x++)
04707   {
04708     for (int y = d->row; y <= d->row + extraYCells(); y++)
04709     {
04710       Cell* cell = format()->sheet()->cellAt(x,y);
04711       cell->setLayoutDirtyFlag();
04712     }
04713   }
04714 
04715   setCalcDirtyFlag();
04716 
04717   /* TODO - is this a good place for this? */
04718   updateChart(true);
04719 }
04720 
04721 bool Cell::testValidity() const
04722 {
04723     bool valid = false;
04724     if( d->hasExtra() && d->extra()->validity && d->extra()->validity->m_restriction != Restriction::None )
04725     {
04726         //fixme
04727         if ( d->extra()->validity->allowEmptyCell && d->strText.isEmpty() )
04728             return true;
04729 
04730         if( value().isNumber() &&
04731             (d->extra()->validity->m_restriction == Restriction::Number ||
04732              (d->extra()->validity->m_restriction == Restriction::Integer &&
04733               value().asFloat() == ceil(value().asFloat()))))
04734         {
04735             switch( d->extra()->validity->m_cond)
04736             {
04737               case Conditional::Equal:
04738                 valid = ( value().asFloat() - d->extra()->validity->valMin < DBL_EPSILON
04739                           && value().asFloat() - d->extra()->validity->valMin >
04740                           (0.0 - DBL_EPSILON));
04741                 break;
04742               case Conditional::DifferentTo:
04743                 valid = !(  ( value().asFloat() - d->extra()->validity->valMin < DBL_EPSILON
04744                               && value().asFloat() - d->extra()->validity->valMin >
04745                               (0.0 - DBL_EPSILON)) );
04746                 break;
04747               case Conditional::Superior:
04748                 valid = ( value().asFloat() > d->extra()->validity->valMin);
04749                 break;
04750               case Conditional::Inferior:
04751                 valid = ( value().asFloat()  <d->extra()->validity->valMin);
04752                 break;
04753               case Conditional::SuperiorEqual:
04754                 valid = ( value().asFloat() >= d->extra()->validity->valMin);
04755                 break;
04756               case Conditional::InferiorEqual:
04757                 valid = (value().asFloat() <= d->extra()->validity->valMin);
04758                 break;
04759               case Conditional::Between:
04760                 valid = ( value().asFloat() >= d->extra()->validity->valMin &&
04761                           value().asFloat() <= d->extra()->validity->valMax);
04762                 break;
04763               case Conditional::Different:
04764                 valid = (value().asFloat() < d->extra()->validity->valMin ||
04765                          value().asFloat() > d->extra()->validity->valMax);
04766                 break;
04767             default :
04768                 break;
04769             }
04770         }
04771         else if(d->extra()->validity->m_restriction==Restriction::Text)
04772         {
04773             valid = value().isString();
04774         }
04775         else if ( d->extra()->validity->m_restriction == Restriction::List )
04776         {
04777             //test int value
04778             if ( value().isString() && d->extra()->validity->listValidity.contains( value().asString() ) )
04779                 valid = true;
04780         }
04781         else if(d->extra()->validity->m_restriction==Restriction::TextLength)
04782         {
04783             if( value().isString() )
04784             {
04785                 int len = d->strOutText.length();
04786                 switch( d->extra()->validity->m_cond)
04787                 {
04788                   case Conditional::Equal:
04789                     if (len == d->extra()->validity->valMin)
04790                         valid = true;
04791                     break;
04792                   case Conditional::DifferentTo:
04793                     if (len != d->extra()->validity->valMin)
04794                         valid = true;
04795                     break;
04796                   case Conditional::Superior:
04797                     if(len > d->extra()->validity->valMin)
04798                         valid = true;
04799                     break;
04800                   case Conditional::Inferior:
04801                     if(len < d->extra()->validity->valMin)
04802                         valid = true;
04803                     break;
04804                   case Conditional::SuperiorEqual:
04805                     if(len >= d->extra()->validity->valMin)
04806                         valid = true;
04807                     break;
04808                   case Conditional::InferiorEqual:
04809                     if(len <= d->extra()->validity->valMin)
04810                         valid = true;
04811                     break;
04812                   case Conditional::Between:
04813                     if(len >= d->extra()->validity->valMin && len <= d->extra()->validity->valMax)
04814                         valid = true;
04815                     break;
04816                   case Conditional::Different:
04817                     if(len <d->extra()->validity->valMin || len >d->extra()->validity->valMax)
04818                         valid = true;
04819                     break;
04820                 default :
04821                     break;
04822                 }
04823             }
04824         }
04825         else if(d->extra()->validity->m_restriction == Restriction::Time && isTime())
04826         {
04827             switch( d->extra()->validity->m_cond)
04828             {
04829               case Conditional::Equal:
04830                 valid = (value().asTime() == d->extra()->validity->timeMin);
04831                 break;
04832               case Conditional::DifferentTo:
04833                 valid = (value().asTime() != d->extra()->validity->timeMin);
04834                 break;
04835               case Conditional::Superior:
04836                 valid = (value().asTime() > d->extra()->validity->timeMin);
04837                 break;
04838               case Conditional::Inferior:
04839                 valid = (value().asTime() < d->extra()->validity->timeMin);
04840                 break;
04841               case Conditional::SuperiorEqual:
04842                 valid = (value().asTime() >= d->extra()->validity->timeMin);
04843                 break;
04844               case Conditional::InferiorEqual:
04845                 valid = (value().asTime() <= d->extra()->validity->timeMin);
04846                 break;
04847               case Conditional::Between:
04848                 valid = (value().asTime() >= d->extra()->validity->timeMin &&
04849                          value().asTime() <= d->extra()->validity->timeMax);
04850                 break;
04851               case Conditional::Different:
04852                 valid = (value().asTime() < d->extra()->validity->timeMin ||
04853                          value().asTime() > d->extra()->validity->timeMax);
04854                 break;
04855             default :
04856                 break;
04857 
04858             }
04859         }
04860         else if(d->extra()->validity->m_restriction == Restriction::Date && isDate())
04861         {
04862             switch( d->extra()->validity->m_cond)
04863             {
04864               case Conditional::Equal:
04865                 valid = (value().asDate() == d->extra()->validity->dateMin);
04866                 break;
04867               case Conditional::DifferentTo:
04868                 valid = (value().asDate() != d->extra()->validity->dateMin);
04869                 break;
04870               case Conditional::Superior:
04871                 valid = (value().asDate() > d->extra()->validity->dateMin);
04872                 break;
04873               case Conditional::Inferior:
04874                 valid = (value().asDate() < d->extra()->validity->dateMin);
04875                 break;
04876               case Conditional::SuperiorEqual:
04877                 valid = (value().asDate() >= d->extra()->validity->dateMin);
04878                 break;
04879               case Conditional::InferiorEqual:
04880                 valid = (value().asDate() <= d->extra()->validity->dateMin);
04881                 break;
04882               case Conditional::Between:
04883                 valid = (value().asDate() >= d->extra()->validity->dateMin &&
04884                          value().asDate() <= d->extra()->validity->dateMax);
04885                 break;
04886               case Conditional::Different:
04887                 valid = (value().asDate() < d->extra()->validity->dateMin ||
04888                          value().asDate() > d->extra()->validity->dateMax);
04889                 break;
04890             default :
04891                 break;
04892 
04893             }
04894         }
04895     }
04896     else
04897     {
04898         valid= true;
04899     }
04900 
04901     if(!valid &&d->extra()->validity != NULL && d->extra()->validity->displayMessage)
04902     {
04903         switch (d->extra()->validity->m_action )
04904         {
04905           case Action::Stop:
04906             KMessageBox::error((QWidget*)0L, d->extra()->validity->message,
04907                                d->extra()->validity->title);
04908             break;
04909           case Action::Warning:
04910             KMessageBox::warningYesNo((QWidget*)0L, d->extra()->validity->message,
04911                                       d->extra()->validity->title);
04912             break;
04913           case Action::Information:
04914             KMessageBox::information((QWidget*)0L, d->extra()->validity->message,
04915                                      d->extra()->validity->title);
04916             break;
04917         }
04918     }
04919     if (!d->hasExtra())
04920         return true;  //okay if there's no validity
04921     return (valid || d->extra()->validity == NULL || d->extra()->validity->m_action != Action::Stop);
04922 }
04923 
04924 FormatType Cell::formatType() const
04925 {
04926     return format()->getFormatType( d->column, d->row );
04927 }
04928 
04929 double Cell::textWidth() const
04930 {
04931     return d->textWidth;
04932 }
04933 
04934 double Cell::textHeight() const
04935 {
04936     return d->textHeight;
04937 }
04938 
04939 int Cell::mergedXCells() const
04940 {
04941     return d->hasExtra() ? d->extra()->mergedXCells : 0;
04942 }
04943 
04944 int Cell::mergedYCells() const
04945 {
04946     return d->hasExtra() ? d->extra()->mergedYCells : 0;
04947 }
04948 
04949 int Cell::extraXCells() const
04950 {
04951     return d->hasExtra() ? d->extra()->extraXCells : 0;
04952 }
04953 
04954 int Cell::extraYCells() const
04955 {
04956     return d->hasExtra() ? d->extra()->extraYCells : 0;
04957 }
04958 
04959 double Cell::extraWidth() const
04960 {
04961     return d->hasExtra() ? d->extra()->extraWidth : 0;
04962 }
04963 
04964 double Cell::extraHeight() const
04965 {
04966     return d->hasExtra() ? d->extra()->extraHeight : 0;
04967 }
04968 
04969 
04970 bool Cell::isDate() const
04971 {
04972   FormatType ft = formatType();
04973 
04974   return (formatIsDate (ft) || ((ft == Generic_format) &&
04975       (value().format() == Value::fmt_Date)));
04976 }
04977 
04978 bool Cell::isTime() const
04979 {
04980   FormatType ft = formatType();
04981 
04982   return (formatIsTime (ft) || ((ft == Generic_format) &&
04983       (value().format() == Value::fmt_Time)));
04984 }
04985 
04986 void Cell::setCalcDirtyFlag()
04987 {
04988   if ( !isFormula() )
04989   {
04990     //don't set the flag if we don't hold a formula
04991     clearFlag(Flag_CalcDirty);
04992     return;
04993   }
04994   setFlag(Flag_CalcDirty);
04995   format()->sheet()->setRegionPaintDirty(cellRect());
04996 }
04997 
04998 
04999 bool Cell::updateChart(bool refresh)
05000 {
05001     // Update a chart for example if it depends on this cell.
05002     if ( d->row != 0 && d->column != 0 )
05003     {
05004         CellBinding *bind;
05005         for ( bind = format()->sheet()->firstCellBinding(); bind != 0L; bind = format()->sheet()->nextCellBinding() )
05006         {
05007             if ( bind->contains( d->column, d->row ) )
05008             {
05009                 if (!refresh)
05010                     return true;
05011 
05012                 bind->cellChanged( this );
05013             }
05014         }
05015         return true;
05016     }
05017     return false;
05018 
05019 }
05020 
05021 double Cell::getDouble ()
05022 {
05023   if (isDefault())
05024     return 0.0;
05025   //(Tomas) umm can't we simply call value().asFloat() ?
05026   if (isDate())
05027   {
05028     QDate date = value().asDate();
05029     QDate dummy (1900, 1, 1);
05030     return (dummy.daysTo (date) + 1);
05031   }
05032   if (isTime())
05033   {
05034     QTime time  = value().asTime();
05035     QTime dummy;
05036     return dummy.secsTo( time );
05037   }
05038   if (value().isNumber())
05039     return value().asFloat();
05040 
05041   return 0.0;
05042 }
05043 
05044 void Cell::convertToDouble ()
05045 {
05046   if (isDefault())
05047     return;
05048 
05049   setValue (getDouble ());
05050 }
05051 
05052 void Cell::convertToPercent ()
05053 {
05054   if (isDefault())
05055     return;
05056 
05057   setValue (getDouble ());
05058   d->value.setFormat (Value::fmt_Percent);
05059 }
05060 
05061 void Cell::convertToMoney ()
05062 {
05063   if (isDefault())
05064     return;
05065 
05066   setValue (getDouble ());
05067   d->value.setFormat (Value::fmt_Money);
05068   format()->setPrecision (locale()->fracDigits());
05069 }
05070 
05071 void Cell::convertToTime ()
05072 {
05073   //(Tomas) This is weird. And I mean *REALLY* weird. First, we
05074   //generate a time (QTime), then we convert it to text, then
05075   //we give the text to the cell and ask it to parse it. Weird...
05076 
05077   if (isDefault() || isEmpty())
05078     return;
05079 
05080   setValue (getDouble ());
05081   QTime time = value().asDateTime().time();
05082   int msec = (int) ( (value().asFloat() - (int) value().asFloat()) * 1000 );
05083   time = time.addMSecs( msec );
05084   setCellText( time.toString() );
05085 }
05086 
05087 void Cell::convertToDate ()
05088 {
05089   //(Tomas) This is weird. And I mean *REALLY* weird. First, we
05090   //generate a date (QDate), then we convert it to text, then
05091   //we give the text to the cell and ask it to parse it. Weird...
05092 
05093   if (isDefault() || isEmpty())
05094     return;
05095 
05096   setValue (getDouble ());
05097 
05098   //TODO: why did we call setValue(), when we override it here?
05099   QDate date(1900, 1, 1);
05100   date = date.addDays( (int) value().asFloat() - 1 );
05101   date = value().asDateTime().date();
05102   setCellText (locale()->formatDate (date, true));
05103 }
05104 
05105 void Cell::checkTextInput()
05106 {
05107   // Goal of this method: determine the value of the cell
05108   clearAllErrors();
05109 
05110   d->value = Value::empty();
05111 
05112   // Get the text from that cell
05113   QString str = d->strText;
05114 
05115   sheet()->doc()->parser()->parse (str, this);
05116 
05117   // Parsing as time acts like an autoformat: we even change d->strText
05118   // [h]:mm:ss -> might get set by ValueParser
05119   if (isTime() && (formatType() != Time_format7))
05120     d->strText = locale()->formatTime( value().asDateTime().time(), true);
05121 
05122   // convert first letter to uppercase ?
05123   if (format()->sheet()->getFirstLetterUpper() && value().isString() &&
05124       (!d->strText.isEmpty()))
05125   {
05126     QString str = value().asString();
05127     setValue( Value( str[0].upper() + str.right( str.length()-1 ) ) );
05128   }
05129 }
05130 
05131 //used in calc, setNumber, ValueParser
05132 void Cell::checkNumberFormat()
05133 {
05134     if ( formatType() == Number_format && value().isNumber() )
05135     {
05136         if ( value().asFloat() > 1e+10 )
05137             format()->setFormatType( Scientific_format );
05138     }
05139 }
05140 
05141 
05142 // ================================================================
05143 //                       Saving and loading
05144 
05145 
05146 QDomElement Cell::save( QDomDocument& doc,
05147              int _x_offset, int _y_offset,
05148              bool force, bool copy, bool era )
05149 {
05150     // Save the position of this cell
05151     QDomElement cell = doc.createElement( "cell" );
05152     cell.setAttribute( "row", d->row - _y_offset );
05153     cell.setAttribute( "column", d->column - _x_offset );
05154     //
05155     // Save the formatting information
05156     //
05157     QDomElement formatElement = format()->save( doc, d->column, d->row, force, copy );
05158     if ( formatElement.hasChildNodes() || formatElement.attributes().length() ) // don't save empty tags
05159         cell.appendChild( formatElement );
05160 
05161     if ( doesMergeCells() )
05162     {
05163         if ( extraXCells() )
05164             formatElement.setAttribute( "colspan", extraXCells() );
05165         if ( extraYCells() )
05166             formatElement.setAttribute( "rowspan", extraYCells() );
05167     }
05168 
05169     if ( d->hasExtra() && d->extra()->conditions )
05170     {
05171       QDomElement conditionElement = d->extra()->conditions->saveConditions( doc );
05172 
05173       if ( !conditionElement.isNull() )
05174         cell.appendChild( conditionElement );
05175     }
05176 
05177     if ( d->hasExtra() && (d->extra()->validity != 0) )
05178     {
05179         QDomElement validity = doc.createElement("validity");
05180 
05181         QDomElement param=doc.createElement("param");
05182         param.setAttribute("cond",(int)d->extra()->validity->m_cond);
05183         param.setAttribute("action",(int)d->extra()->validity->m_action);
05184         param.setAttribute("allow",(int)d->extra()->validity->m_restriction);
05185         param.setAttribute("valmin",d->extra()->validity->valMin);
05186         param.setAttribute("valmax",d->extra()->validity->valMax);
05187         param.setAttribute("displaymessage",d->extra()->validity->displayMessage);
05188         param.setAttribute("displayvalidationinformation",d->extra()->validity->displayValidationInformation);
05189         param.setAttribute("allowemptycell",d->extra()->validity->allowEmptyCell);
05190         if ( !d->extra()->validity->listValidity.isEmpty() )
05191             param.setAttribute( "listvalidity", d->extra()->validity->listValidity.join( ";" ) );
05192         validity.appendChild(param);
05193         QDomElement title = doc.createElement( "title" );
05194         title.appendChild( doc.createTextNode( d->extra()->validity->title ) );
05195         validity.appendChild( title );
05196         QDomElement message = doc.createElement( "message" );
05197         message.appendChild( doc.createCDATASection( d->extra()->validity->message ) );
05198         validity.appendChild( message );
05199 
05200         QDomElement inputTitle = doc.createElement( "inputtitle" );
05201         inputTitle.appendChild( doc.createTextNode( d->extra()->validity->titleInfo ) );
05202         validity.appendChild( inputTitle );
05203 
05204         QDomElement inputMessage = doc.createElement( "inputmessage" );
05205         inputMessage.appendChild( doc.createTextNode( d->extra()->validity->messageInfo ) );
05206         validity.appendChild( inputMessage );
05207 
05208 
05209 
05210         QString tmp;
05211         if ( d->extra()->validity->timeMin.isValid() )
05212         {
05213                 QDomElement timeMin = doc.createElement( "timemin" );
05214                 tmp=d->extra()->validity->timeMin.toString();
05215                 timeMin.appendChild( doc.createTextNode( tmp ) );
05216                 validity.appendChild( timeMin );
05217         }
05218         if ( d->extra()->validity->timeMax.isValid() )
05219         {
05220                 QDomElement timeMax = doc.createElement( "timemax" );
05221                 tmp=d->extra()->validity->timeMax.toString();
05222                 timeMax.appendChild( doc.createTextNode( tmp ) );
05223                 validity.appendChild( timeMax );
05224         }
05225 
05226         if ( d->extra()->validity->dateMin.isValid() )
05227         {
05228                 QDomElement dateMin = doc.createElement( "datemin" );
05229                 QString tmp("%1/%2/%3");
05230                 tmp = tmp.arg(d->extra()->validity->dateMin.year()).arg(d->extra()->validity->dateMin.month()).arg(d->extra()->validity->dateMin.day());
05231                 dateMin.appendChild( doc.createTextNode( tmp ) );
05232                 validity.appendChild( dateMin );
05233         }
05234         if ( d->extra()->validity->dateMax.isValid() )
05235         {
05236                 QDomElement dateMax = doc.createElement( "datemax" );
05237                 QString tmp("%1/%2/%3");
05238                 tmp = tmp.arg(d->extra()->validity->dateMax.year()).arg(d->extra()->validity->dateMax.month()).arg(d->extra()->validity->dateMax.day());
05239                 dateMax.appendChild( doc.createTextNode( tmp ) );
05240                 validity.appendChild( dateMax );
05241         }
05242 
05243         cell.appendChild( validity );
05244     }
05245 
05246     if ( format()->comment() )
05247     {
05248         QDomElement comment = doc.createElement( "comment" );
05249         comment.appendChild( doc.createCDATASection( *format()->comment() ) );
05250         cell.appendChild( comment );
05251     }
05252 
05253     //
05254     // Save the text
05255     //
05256     if ( !d->strText.isEmpty() )
05257     {
05258         // Formulas need to be encoded to ensure that they
05259         // are position independent.
05260         if ( isFormula() )
05261         {
05262             QDomElement text = doc.createElement( "text" );
05263             // if we are cutting to the clipboard, relative references need to be encoded absolutely
05264             text.appendChild( doc.createTextNode( encodeFormula( era ) ) );
05265             cell.appendChild( text );
05266 
05267             /* we still want to save the results of the formula */
05268             QDomElement formulaResult = doc.createElement( "result" );
05269             saveCellResult( doc, formulaResult, d->strOutText );
05270             cell.appendChild( formulaResult );
05271 
05272         }
05273         else if ( !link().isEmpty() )
05274         {
05275             // KSpread pre 1.4 saves link as rich text, marked with first char '
05276             // Have to be saved in some CDATA section because of too many special charatcers.
05277             QDomElement text = doc.createElement( "text" );
05278             QString qml = "!<a href=\"" + link() + "\">" + d->strText + "</a>";
05279             text.appendChild( doc.createCDATASection( qml ) );
05280             cell.appendChild( text );
05281         }
05282         else
05283         {
05284             // Save the cell contents (in a locale-independent way)
05285             QDomElement text = doc.createElement( "text" );
05286             saveCellResult( doc, text, d->strText );
05287             cell.appendChild( text );
05288         }
05289     }
05290     if ( cell.hasChildNodes() || cell.attributes().length() > 2 ) // don't save empty tags
05291         // (the >2 is due to "row" and "column" attributes)
05292         return cell;
05293     else
05294         return QDomElement();
05295 }
05296 
05297 bool Cell::saveCellResult( QDomDocument& doc, QDomElement& result,
05298                                   QString str )
05299 {
05300   QString dataType = "Other"; // fallback
05301 
05302   if ( value().isNumber() )
05303   {
05304       if ( isDate() )
05305       {
05306           // serial number of date
05307           QDate dd = value().asDateTime().date();
05308           dataType = "Date";
05309           str = "%1/%2/%3";
05310           str = str.arg(dd.year()).arg(dd.month()).arg(dd.day());
05311       }
05312       else if( isTime() )
05313       {
05314           // serial number of time
05315           dataType = "Time";
05316           str = value().asDateTime().time().toString();
05317       }
05318       else
05319       {
05320           // real number
05321           dataType = "Num";
05322           if (value().isInteger())
05323             str = QString::number(value().asInteger());
05324           else
05325             str = QString::number(value().asFloat(), 'g', DBL_DIG);
05326       }
05327   }
05328 
05329   if ( value().isBoolean() )
05330   {
05331       dataType = "Bool";
05332       str = value().asBoolean() ? "true" : "false";
05333   }
05334 
05335   if ( value().isString() )
05336   {
05337       dataType = "Str";
05338       str = value().asString();
05339   }
05340 
05341   result.setAttribute( "dataType", dataType );
05342   if ( !d->strOutText.isEmpty() )
05343     result.setAttribute( "outStr", d->strOutText );
05344   result.appendChild( doc.createTextNode( str ) );
05345 
05346   return true; /* really isn't much of a way for this function to fail */
05347 }
05348 
05349 void Cell::saveOasisAnnotation( KoXmlWriter &xmlwriter )
05350 {
05351     if ( format()->comment() )
05352     {
05353         //<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">
05354         xmlwriter.startElement( "office:annotation" );
05355         QStringList text = QStringList::split( "\n", *format()->comment() );
05356         for ( QStringList::Iterator it = text.begin(); it != text.end(); ++it ) {
05357             xmlwriter.startElement( "text:p" );
05358             xmlwriter.addTextNode( *it );
05359             xmlwriter.endElement();
05360         }
05361         xmlwriter.endElement();
05362     }
05363 }
05364 
05365 
05366 
05367 QString Cell::saveOasisCellStyle( KoGenStyle &currentCellStyle, KoGenStyles &mainStyles )
05368 {
05369     if ( d->hasExtra() && d->extra()->conditions )
05370     {
05371         // this has to be an automatic style
05372         currentCellStyle = KoGenStyle( Doc::STYLE_CELL_AUTO, "table-cell" );
05373         d->extra()->conditions->saveOasisConditions( currentCellStyle );
05374     }
05375     return format()->saveOasisCellStyle( currentCellStyle, mainStyles );
05376 }
05377 
05378 
05379 bool Cell::saveOasis( KoXmlWriter& xmlwriter, KoGenStyles &mainStyles, int row, int column, int maxCols, int &repeated, GenValidationStyles &valStyle )
05380 {
05381     if ( !isPartOfMerged() )
05382         xmlwriter.startElement( "table:table-cell" );
05383     else
05384         xmlwriter.startElement( "table:covered-table-cell" );
05385 #if 0
05386     //add font style
05387     QFont font;
05388     Value const value( cell->value() );
05389     if ( !cell->isDefault() )
05390     {
05391       font = cell->format()->textFont( i, row );
05392       m_styles.addFont( font );
05393 
05394       if ( cell->format()->hasProperty( Format::PComment ) )
05395         hasComment = true;
05396     }
05397 #endif
05398     // NOTE save the value before the style as long as the Formatter does not work correctly
05399     if ( link().isEmpty() )
05400       saveOasisValue (xmlwriter);
05401 
05402     KoGenStyle currentCellStyle; // the type determined in saveOasisCellStyle
05403     saveOasisCellStyle( currentCellStyle,mainStyles );
05404     // skip 'table:style-name' attribute for the default style
05405     if ( !currentCellStyle.isDefaultStyle() )
05406       xmlwriter.addAttribute( "table:style-name", mainStyles.styles()[currentCellStyle] );
05407 
05408     // group empty cells with the same style
05409     if ( isEmpty() && !format()->hasProperty( Format::PComment ) && !isPartOfMerged() && !doesMergeCells() )
05410     {
05411       int j = column + 1;
05412       while ( j <= maxCols )
05413       {
05414         Cell *nextCell = format()->sheet()->cellAt( j, row );
05415         KoGenStyle nextCellStyle; // the type is determined in saveOasisCellStyle
05416         nextCell->saveOasisCellStyle( nextCellStyle,mainStyles );
05417 
05418         if ( nextCell->isEmpty() && !nextCell->format()->hasProperty( Format::PComment )
05419              && ( nextCellStyle==currentCellStyle ) && !isPartOfMerged() && !doesMergeCells() )
05420           ++repeated;
05421         else
05422           break;
05423         ++j;
05424       }
05425       if ( repeated > 1 )
05426         xmlwriter.addAttribute( "table:number-columns-repeated", QString::number( repeated ) );
05427     }
05428 
05429 
05430     if (d->hasExtra() && d->extra()->validity)
05431     {
05432         GenValidationStyle styleVal(d->extra()->validity);
05433         xmlwriter.addAttribute( "table:validation-name", valStyle.lookup( styleVal ) );
05434     }
05435     if ( isFormula() )
05436     {
05437         //kdDebug() << "Formula found" << endl;
05438       QString formula( convertFormulaToOasisFormat( text() ) );
05439       xmlwriter.addAttribute( "table:formula", formula );
05440     }
05441     else if ( !link().isEmpty() )
05442     {
05443         //kdDebug()<<"Link found \n";
05444         xmlwriter.startElement( "text:p" );
05445         xmlwriter.startElement( "text:a" );
05446         //Reference cell is started by "#"
05447         if ( localReferenceAnchor( link() ) )
05448             xmlwriter.addAttribute( " xlink:href", ( "#"+link() ) );
05449         else
05450             xmlwriter.addAttribute( " xlink:href", link() );
05451         xmlwriter.addTextNode( text() );
05452         xmlwriter.endElement();
05453         xmlwriter.endElement();
05454     }
05455 
05456     if ( doesMergeCells() )
05457     {
05458       int colSpan = mergedXCells() + 1;
05459       int rowSpan = mergedYCells() + 1;
05460 
05461       if ( colSpan > 1 )
05462         xmlwriter.addAttribute( "table:number-columns-spanned", QString::number( colSpan ) );
05463 
05464       if ( rowSpan > 1 )
05465         xmlwriter.addAttribute( "table:number-rows-spanned", QString::number( rowSpan ) );
05466     }
05467 
05468     if ( !isEmpty() && link().isEmpty() )
05469     {
05470         xmlwriter.startElement( "text:p" );
05471         xmlwriter.addTextNode(strOutText());
05472         xmlwriter.endElement();
05473     }
05474 
05475     saveOasisAnnotation( xmlwriter );
05476 
05477     xmlwriter.endElement();
05478     return true;
05479 }
05480 
05481 void Cell::saveOasisValue (KoXmlWriter &xmlWriter)
05482 {
05483   switch (value().format())
05484   {
05485     case Value::fmt_None: break;  //NOTHING HERE
05486     case Value::fmt_Boolean:
05487     {
05488       xmlWriter.addAttribute( "office:value-type", "boolean" );
05489       xmlWriter.addAttribute( "office:boolean-value", ( value().asBoolean() ?
05490           "true" : "false" ) );
05491       break;
05492     }
05493     case Value::fmt_Number:
05494     {
05495       xmlWriter.addAttribute( "office:value-type", "float" );
05496       if (value().isInteger())
05497         xmlWriter.addAttribute( "office:value", QString::number( value().asInteger() ) );
05498       else
05499         xmlWriter.addAttribute( "office:value", QString::number( value().asFloat(), 'g', DBL_DIG ) );
05500       break;
05501     }
05502     case Value::fmt_Percent:
05503     {
05504       xmlWriter.addAttribute( "office:value-type", "percentage" );
05505       xmlWriter.addAttribute( "office:value",
05506           QString::number( value().asFloat() ) );
05507       break;
05508     }
05509     case Value::fmt_Money:
05510     {
05511       xmlWriter.addAttribute( "office:value-type", "currency" );
05512       Format::Currency currency;
05513       format()->currencyInfo(currency);
05514       xmlWriter.addAttribute( "office:currency",
05515                               Currency::getCurrencyCode(currency.type) );
05516       xmlWriter.addAttribute( "office:value",
05517           QString::number( value().asFloat() ) );
05518       break;
05519     }
05520     case Value::fmt_DateTime: break;  //NOTHING HERE
05521     case Value::fmt_Date:
05522     {
05523       xmlWriter.addAttribute( "office:value-type", "date" );
05524       xmlWriter.addAttribute( "office:date-value",
05525           value().asDate().toString( Qt::ISODate ) );
05526       break;
05527     }
05528     case Value::fmt_Time:
05529     {
05530       xmlWriter.addAttribute( "office:value-type", "time" );
05531       xmlWriter.addAttribute( "office:time-value",
05532           value().asTime().toString( "PThhHmmMssS" ) );
05533       break;
05534     }
05535     case Value::fmt_String:
05536     {
05537       xmlWriter.addAttribute( "office:value-type", "string" );
05538       xmlWriter.addAttribute( "office:string-value", value().asString() );
05539       break;
05540     }
05541   };
05542 }
05543 
05544 QString Cell::convertFormulaToOasisFormat( const QString & formula ) const
05545 {
05546     QString s;
05547     QRegExp exp("(\\$?)([a-zA-Z]+)(\\$?)([0-9]+)");
05548     int n = exp.search( formula, 0 );
05549     kdDebug() << "Exp: " << formula << ", n: " << n << ", Length: " << formula.length()
05550               << ", Matched length: " << exp.matchedLength() << endl;
05551 
05552     bool inQuote1 = false;
05553     bool inQuote2 = false;
05554     int i = 0;
05555     int l = (int) formula.length();
05556     if ( l <= 0 )
05557         return formula;
05558     while ( i < l )
05559     {
05560         if ( ( n != -1 ) && ( n < i ) )
05561         {
05562             n = exp.search( formula, i );
05563             kdDebug() << "Exp: " << formula.right( l - i ) << ", n: " << n << endl;
05564         }
05565         if ( formula[i] == '"' )
05566         {
05567             inQuote1 = !inQuote1;
05568             s += formula[i];
05569             ++i;
05570             continue;
05571         }
05572         if ( formula[i] == '\'' )
05573         {
05574             // named area
05575             inQuote2 = !inQuote2;
05576             ++i;
05577             continue;
05578         }
05579         if ( inQuote1 || inQuote2 )
05580         {
05581             s += formula[i];
05582             ++i;
05583             continue;
05584         }
05585         if ( ( formula[i] == '=' ) && ( formula[i + 1] == '=' ) )
05586         {
05587             s += '=';
05588             ++i;++i;
05589             continue;
05590         }
05591         if ( formula[i] == '!' )
05592         {
05593             insertBracket( s );
05594             s += '.';
05595             ++i;
05596             continue;
05597         }
05598         if ( formula[i] == ',' )
05599         {
05600             s += '.';
05601             ++i;
05602             continue;
05603         }
05604         if ( n == i )
05605         {
05606             int ml = exp.matchedLength();
05607             if ( formula[ i + ml ] == '!' )
05608             {
05609                 kdDebug() << "No cell ref but sheet name" << endl;
05610                 s += formula[i];
05611                 ++i;
05612                 continue;
05613             }
05614             if ( ( i > 0 ) && ( formula[i - 1] != '!' ) )
05615                 s += "[.";
05616             for ( int j = 0; j < ml; ++j )
05617             {
05618                 s += formula[i];
05619                 ++i;
05620             }
05621             s += ']';
05622             continue;
05623         }
05624 
05625         s += formula[i];
05626         ++i;
05627     }
05628 
05629     return s;
05630 }
05631 
05632 void Cell::loadOasisConditional( QDomElement * style )
05633 {
05634     //kdDebug()<<" void Cell::loadOasisConditional( QDomElement * style  :"<<style<<endl;
05635     if ( style )//safe
05636     {
05637         //TODO fixme it doesn't work :(((
05638         QDomElement e;
05639         forEachElement( e, style->toElement() )
05640         {
05641 //             kdDebug()<<"e.localName() :"<<e.localName()<<endl;
05642             if ( e.localName() == "map" && e.namespaceURI() == KoXmlNS::style )
05643             {
05644                 if (d->hasExtra())
05645                     delete d->extra()->conditions;
05646                 d->extra()->conditions = new Conditions( this );
05647                 d->extra()->conditions->loadOasisConditions( e );
05648                 d->extra()->conditions->checkMatches();
05649                 break;
05650             }
05651         }
05652 
05653     }
05654 }
05655 
05656 bool Cell::loadOasis( const QDomElement& element , KoOasisLoadingContext& oasisContext , Style* style )
05657 {
05658   kdDebug() << "*** Loading cell properties ***** at " << column() << "," << row () << endl;
05659 
05660     QString text;
05661     kdDebug()<<" table:style-name: "<<element.attributeNS( KoXmlNS::table, "style-name", QString::null )<<endl;
05662 
05663     QDomElement* cellStyle=0;
05664 
05665     if ( element.hasAttributeNS( KoXmlNS::table, "style-name" ) )
05666     {
05667         oasisContext.fillStyleStack( element, KoXmlNS::table, "styleName", "table-cell" );
05668 
05669         QString str = element.attributeNS( KoXmlNS::table, "style-name", QString::null );
05670         cellStyle = const_cast<QDomElement*>( oasisContext.oasisStyles().findStyle( str, "table-cell" ) );
05671 
05672     if ( cellStyle )
05673         loadOasisConditional( const_cast<QDomElement *>( cellStyle ) );
05674    }
05675 
05676     if (style)
05677     {
05678         format()->setStyle( style );
05679     }
05680 
05681     //Search and load each paragraph of text. Each paragraph is separated by a line break.
05682     loadOasisCellText( element );
05683 
05684     //
05685     // formula
05686     //
05687     bool isFormula = false;
05688     if ( element.hasAttributeNS( KoXmlNS::table, "formula" ) )
05689     {
05690         kdDebug()<<" formula :"<<element.attributeNS( KoXmlNS::table, "formula", QString::null )<<endl;
05691         isFormula = true;
05692         QString oasisFormula( element.attributeNS( KoXmlNS::table, "formula", QString::null ) );
05693         //necessary to remove it to load formula from oocalc2.0 (use namespace)
05694         if (oasisFormula.startsWith( "oooc:" ) )
05695             oasisFormula= oasisFormula.mid( 5 );
05696         else if (oasisFormula.startsWith( "kspr:" ) )
05697             oasisFormula= oasisFormula.mid( 5 );
05698         // TODO Stefan: merge this into Oasis::decodeFormula
05699         checkForNamedAreas( oasisFormula );
05700         oasisFormula = Oasis::decodeFormula( oasisFormula, locale() );
05701         setCellText( oasisFormula );
05702     }
05703     else if ( d->strText.at(0) == '=' ) //prepend ' to the text to avoid = to be painted
05704         d->strText.prepend('\'');
05705 
05706     //
05707     // validation
05708     //
05709     if ( element.hasAttributeNS( KoXmlNS::table, "validation-name" ) )
05710     {
05711         kdDebug()<<" validation-name: "<<element.attributeNS( KoXmlNS::table, "validation-name", QString::null )<<endl;
05712         loadOasisValidation( element.attributeNS( KoXmlNS::table, "validation-name", QString::null ) );
05713     }
05714 
05715     //
05716     // value type
05717     //
05718     if( element.hasAttributeNS( KoXmlNS::office, "value-type" ) )
05719     {
05720         QString valuetype = element.attributeNS( KoXmlNS::office, "value-type", QString::null );
05721         kdDebug()<<"  value-type: " << valuetype << endl;
05722         if( valuetype == "boolean" )
05723         {
05724           QString val = element.attributeNS( KoXmlNS::office, "boolean-value", QString::null ).lower();
05725             if( ( val == "true" ) || ( val == "false" ) )
05726             {
05727                 bool value = val == "true";
05728                 setValue( value );
05729                 if (!isFormula) setCellText( value ? i18n("True") : i18n("False" ) );
05730             }
05731         }
05732 
05733         // integer and floating-point value
05734         else if( valuetype == "float" )
05735         {
05736             bool ok = false;
05737             double value = element.attributeNS( KoXmlNS::office, "value", QString::null ).toDouble( &ok );
05738             if ( !isFormula )
05739                 if( ok )
05740                     setCellValue( value );
05741 
05742             if ( !isFormula && d->strText.isEmpty())
05743             {
05744                 QString str = locale()->formatNumber( value, 15 );
05745                 setCellText( str );
05746             }
05747         }
05748 
05749         // currency value
05750         else if( valuetype == "currency" )
05751         {
05752             bool ok = false;
05753             double value = element.attributeNS( KoXmlNS::office, "value", QString::null ).toDouble( &ok );
05754             if( ok )
05755             {
05756                 if ( !isFormula )
05757                     setCellValue( value );
05758                 if (element.hasAttributeNS( KoXmlNS::office, "currency" ) )
05759                 {
05760                   Currency currency(element.attributeNS( KoXmlNS::office, "currency", QString::null ) );
05761                   format()->setCurrency( currency.getIndex(), currency.getDisplayCode() );
05762                 }
05763                 format()->setFormatType (Money_format);
05764             }
05765         }
05766         else if( valuetype == "percentage" )
05767         {
05768             bool ok = false;
05769             double v = element.attributeNS( KoXmlNS::office, "value", QString::null ).toDouble( &ok );
05770             if( ok )
05771             {
05772                 Value value;
05773                 value.setValue (v);
05774                 value.setFormat (Value::fmt_Percent);
05775                 setCellValue( value );
05776 
05777                 if ( !isFormula && d->strText.isEmpty())
05778                 {
05779                     QString str = locale()->formatNumber( v, 15 );
05780                     setCellText( str );
05781                 }
05782 
05783                 format()->setFormatType (Percentage_format);
05784             }
05785         }
05786         else if ( valuetype == "date" )
05787         {
05788             QString value = element.attributeNS( KoXmlNS::office, "value", QString::null );
05789             if ( value.isEmpty() )
05790                 value = element.attributeNS( KoXmlNS::office, "date-value", QString::null );
05791             kdDebug() << "Type: date, value: " << value << endl;
05792 
05793             // "1980-10-15"
05794             int year = 0, month = 0, day = 0;
05795             bool ok = false;
05796 
05797             int p1 = value.find( '-' );
05798             if ( p1 > 0 )
05799                 year  = value.left( p1 ).toInt( &ok );
05800 
05801             kdDebug() << "year: " << value.left( p1 ) << endl;
05802 
05803             int p2 = value.find( '-', ++p1 );
05804 
05805             if ( ok )
05806                 month = value.mid( p1, p2 - p1  ).toInt( &ok );
05807 
05808             kdDebug() << "month: " << value.mid( p1, p2 - p1 ) << endl;
05809 
05810             if ( ok )
05811                 day = value.right( value.length() - p2 - 1 ).toInt( &ok );
05812 
05813             kdDebug() << "day: " << value.right( value.length() - p2 ) << endl;
05814 
05815             if ( ok )
05816             {
05817                 setCellValue( QDate( year, month, day ) );
05818                 format()->setFormatType (ShortDate_format);
05819                 kdDebug() << "Set QDate: " << year << " - " << month << " - " << day << endl;
05820             }
05821 
05822         }
05823         else if ( valuetype == "time" )
05824         {
05825             QString value = element.attributeNS( KoXmlNS::office, "value", QString::null );
05826             if ( value.isEmpty() )
05827                 value = element.attributeNS( KoXmlNS::office, "time-value", QString::null );
05828             kdDebug() << "Type: time: " << value << endl;
05829             // "PT15H10M12S"
05830             int hours = 0, minutes = 0, seconds = 0;
05831             int l = value.length();
05832             QString num;
05833             bool ok = false;
05834             for ( int i = 0; i < l; ++i )
05835             {
05836                 if ( value[i].isNumber() )
05837                 {
05838                     num += value[i];
05839                     continue;
05840                 }
05841                 else if ( value[i] == 'H' )
05842                     hours   = num.toInt( &ok );
05843                 else if ( value[i] == 'M' )
05844                     minutes = num.toInt( &ok );
05845                 else if ( value[i] == 'S' )
05846                     seconds = num.toInt( &ok );
05847                 else
05848                     continue;
05849 
05850                 kdDebug() << "Num: " << num << endl;
05851 
05852                 num = "";
05853                 if ( !ok )
05854                     break;
05855             }
05856             kdDebug() << "Hours: " << hours << ", " << minutes << ", " << seconds << endl;
05857 
05858             if ( ok )
05859             {
05860                 // Value kval( timeToNum( hours, minutes, seconds ) );
05861                 // cell->setValue( kval );
05862                 setCellValue( QTime( hours % 24, minutes, seconds ) );
05863                 format()->setFormatType (Time_format);
05864             }
05865         }
05866         else if( valuetype == "string" )
05867         {
05868             QString value = element.attributeNS( KoXmlNS::office, "value", QString::null );
05869             if ( value.isEmpty() && element.hasAttributeNS( KoXmlNS::office, "string-value" ))
05870             {
05871                 //if there is not string-value entry don't overwrite value stored into <text:p>
05872                 value = element.attributeNS( KoXmlNS::office, "string-value", QString::null );
05873                 setCellValue( value );
05874             }
05875             format()->setFormatType (Text_format);
05876         }
05877         else
05878             kdDebug()<<" type of value found : "<<valuetype<<endl;
05879     }
05880 
05881     //
05882     // merged cells ?
05883     //
05884     int colSpan = 1;
05885     int rowSpan = 1;
05886     if ( element.hasAttributeNS( KoXmlNS::table, "number-columns-spanned" ) )
05887     {
05888         bool ok = false;
05889         int span = element.attributeNS( KoXmlNS::table, "number-columns-spanned", QString::null ).toInt( &ok );
05890         if( ok ) colSpan = span;
05891     }
05892     if ( element.hasAttributeNS( KoXmlNS::table, "number-rows-spanned" ) )
05893     {
05894         bool ok = false;
05895         int span = element.attributeNS( KoXmlNS::table, "number-rows-spanned", QString::null ).toInt( &ok );
05896         if( ok ) rowSpan = span;
05897     }
05898     if ( colSpan > 1 || rowSpan > 1 )
05899         mergeCells( d->column, d->row, colSpan - 1, rowSpan - 1 );
05900 
05901     //
05902     // cell comment/annotation
05903     //
05904     QDomElement annotationElement = KoDom::namedItemNS( element, KoXmlNS::office, "annotation" );
05905     if ( !annotationElement.isNull() )
05906     {
05907         QString comment;
05908         QDomNode node = annotationElement.firstChild();
05909         while( !node.isNull() )
05910         {
05911             QDomElement commentElement = node.toElement();
05912             if( !commentElement.isNull() )
05913                 if( commentElement.localName() == "p" && commentElement.namespaceURI() == KoXmlNS::text )
05914                 {
05915                     if( !comment.isEmpty() ) comment.append( '\n' );
05916                     comment.append( commentElement.text() );
05917                 }
05918 
05919             node = node.nextSibling();
05920         }
05921 
05922         if( !comment.isEmpty() )
05923             format()->setComment( comment );
05924     }
05925 
05926     QDomElement frame = KoDom::namedItemNS( element, KoXmlNS::draw, "frame" );
05927     if ( !frame.isNull() )
05928       loadOasisObjects( frame, oasisContext );
05929 
05930     if (isFormula)
05931       setCalcDirtyFlag ();   // formulas must be recalculated
05932 
05933     return true;
05934 }
05935 
05936 void Cell::loadOasisCellText( const QDomElement& parent )
05937 {
05938     //Search and load each paragraph of text. Each paragraph is separated by a line break
05939     QDomElement textParagraphElement;
05940     QString cellText;
05941 
05942     bool multipleTextParagraphsFound=false;
05943 
05944     forEachElement( textParagraphElement , parent )
05945     {
05946         if ( textParagraphElement.localName()=="p" &&
05947              textParagraphElement.namespaceURI()== KoXmlNS::text )
05948         {
05949             // our text, could contain formating for value or result of formul
05950             if (cellText.isEmpty())
05951                 cellText = textParagraphElement.text();
05952             else
05953             {
05954                 cellText += "\n"+textParagraphElement.text();
05955                 multipleTextParagraphsFound=true;
05956             }
05957 
05958             QDomElement textA = KoDom::namedItemNS( textParagraphElement, KoXmlNS::text, "a" );
05959             if( !textA.isNull() )
05960             {
05961                 if ( textA.hasAttributeNS( KoXmlNS::xlink, "href" ) )
05962                 {
05963                     QString link = textA.attributeNS( KoXmlNS::xlink, "href", QString::null );
05964                     cellText = textA.text();
05965                     setCellText( cellText );
05966                     setValue( cellText );
05967                     if ( link[0]=='#' )
05968                         link=link.remove( 0, 1 );
05969                     setLink( link );
05970                 }
05971             }
05972         }
05973     }
05974 
05975     if (!cellText.isNull())
05976     {
05977         setCellText( cellText );
05978         setValue( cellText );
05979     }
05980 
05981     //Enable word wrapping if multiple lines of text have been found.
05982     if ( multipleTextParagraphsFound )
05983     {
05984         format()->setMultiRow(true);
05985     }
05986 }
05987 
05988 void Cell::loadOasisObjects( const QDomElement &parent, KoOasisLoadingContext& oasisContext )
05989 {
05990     for( QDomElement e = parent; !e.isNull(); e = e.nextSibling().toElement() )
05991     {
05992         if ( e.localName() == "frame" && e.namespaceURI() == KoXmlNS::draw )
05993         {
05994           EmbeddedObject *obj = 0;
05995           QDomNode object = KoDom::namedItemNS( e, KoXmlNS::draw, "object" );
05996           if ( !object.isNull() )
05997           {
05998             if ( !object.toElement().attributeNS( KoXmlNS::draw, "notify-on-update-of-ranges", QString::null).isNull() )
05999               obj = new EmbeddedChart( sheet()->doc(), sheet() );
06000             else
06001               obj = new EmbeddedKOfficeObject( sheet()->doc(), sheet() );
06002           }
06003           else
06004           {
06005             QDomNode image = KoDom::namedItemNS( e, KoXmlNS::draw, "image" );
06006             if ( !image.isNull() )
06007               obj = new EmbeddedPictureObject( sheet(), sheet()->doc()->pictureCollection() );
06008             else
06009               kdDebug() << "Object type wasn't loaded!" << endl;
06010           }
06011 
06012           if ( obj )
06013           {
06014             obj->loadOasis( e, oasisContext );
06015             sheet()->doc()->insertObject( obj );
06016 
06017             QString ref = e.attributeNS( KoXmlNS::table, "end-cell-address", QString::null );
06018             if ( ref.isNull() )
06019               continue;
06020 
06021             ref = Oasis::decodeFormula( ref );
06022             Point point( ref );
06023             if ( !point.isValid() )
06024               continue;
06025 
06026             KoRect geometry = obj->geometry();
06027             geometry.setLeft( geometry.left() + sheet()->columnPos( d->column, 0 ) );
06028             geometry.setTop( geometry.top() + sheet()->rowPos( d->row, 0 ) );
06029 
06030             QString str = e.attributeNS( KoXmlNS::table, "end-x", QString::null );
06031             if ( !str.isNull() )
06032             {
06033               uint end_x = (uint) KoUnit::parseValue( str );
06034               geometry.setRight( sheet()->columnPos( point.column(), 0) + end_x );
06035             }
06036 
06037             str = e.attributeNS( KoXmlNS::table, "end-y", QString::null );
06038             if ( !str.isNull() )
06039             {
06040               uint end_y = (uint) KoUnit::parseValue( str );
06041               geometry.setBottom( sheet()->rowPos( point.row(), 0) + end_y );
06042             }
06043 
06044             obj->setGeometry( geometry );
06045           }
06046         }
06047     }
06048 }
06049 
06050 void Cell::loadOasisValidation( const QString& validationName )
06051 {
06052     QDomElement element = sheet()->doc()->loadingInfo()->validation( validationName);
06053     if (d->hasExtra())
06054       delete d->extra()->validity;
06055     d->extra()->validity = new Validity;
06056     if ( element.hasAttributeNS( KoXmlNS::table, "condition" ) )
06057     {
06058         QString valExpression = element.attributeNS( KoXmlNS::table, "condition", QString::null );
06059         kdDebug()<<" element.attribute( table:condition ) "<<valExpression<<endl;
06060         //Condition ::= ExtendedTrueCondition | TrueFunction 'and' TrueCondition
06061         //TrueFunction ::= cell-content-is-whole-number() | cell-content-is-decimal-number() | cell-content-is-date() | cell-content-is-time()
06062         //ExtendedTrueCondition ::= ExtendedGetFunction | cell-content-text-length() Operator Value
06063         //TrueCondition ::= GetFunction | cell-content() Operator Value
06064         //GetFunction ::= cell-content-is-between(Value, Value) | cell-content-is-not-between(Value, Value)
06065         //ExtendedGetFunction ::= cell-content-text-length-is-between(Value, Value) | cell-content-text-length-is-not-between(Value, Value)
06066         //Operator ::= '<' | '>' | '<=' | '>=' | '=' | '!='
06067         //Value ::= NumberValue | String | Formula
06068         //A Formula is a formula without an equals (=) sign at the beginning. See section 8.1.3 for more information.
06069         //A String comprises one or more characters surrounded by quotation marks.
06070         //A NumberValue is a whole or decimal number. It must not contain comma separators for numbers of 1000 or greater.
06071 
06072         //ExtendedTrueCondition
06073         if ( valExpression.contains( "cell-content-text-length()" ) )
06074         {
06075             //"cell-content-text-length()>45"
06076             valExpression = valExpression.remove("oooc:cell-content-text-length()" );
06077             kdDebug()<<" valExpression = :"<<valExpression<<endl;
06078             d->extra()->validity->m_restriction = Restriction::TextLength;
06079 
06080             loadOasisValidationCondition( valExpression );
06081         }
06082         else if ( valExpression.contains( "cell-content-is-text()" ) )
06083         {
06084             d->extra()->validity->m_restriction = Restriction::Text;
06085         }
06086         //cell-content-text-length-is-between(Value, Value) | cell-content-text-length-is-not-between(Value, Value) | cell-content-is-in-list( StringList )
06087         else if ( valExpression.contains( "cell-content-text-length-is-between" ) )
06088         {
06089             d->extra()->validity->m_restriction = Restriction::TextLength;
06090             d->extra()->validity->m_cond = Conditional::Between;
06091             valExpression = valExpression.remove( "oooc:cell-content-text-length-is-between(" );
06092             kdDebug()<<" valExpression :"<<valExpression<<endl;
06093             valExpression = valExpression.remove( ")" );
06094             QStringList listVal = QStringList::split( ",", valExpression );
06095             loadOasisValidationValue( listVal );
06096         }
06097         else if ( valExpression.contains( "cell-content-text-length-is-not-between" ) )
06098         {
06099             d->extra()->validity->m_restriction = Restriction::TextLength;
06100             d->extra()->validity->m_cond = Conditional::Different;
06101             valExpression = valExpression.remove( "oooc:cell-content-text-length-is-not-between(" );
06102             kdDebug()<<" valExpression :"<<valExpression<<endl;
06103             valExpression = valExpression.remove( ")" );
06104             kdDebug()<<" valExpression :"<<valExpression<<endl;
06105             QStringList listVal = QStringList::split( ",", valExpression );
06106             loadOasisValidationValue( listVal );
06107         }
06108         else if ( valExpression.contains( "cell-content-is-in-list(" ) )
06109         {
06110             d->extra()->validity->m_restriction = Restriction::List;
06111             valExpression = valExpression.remove( "oooc:cell-content-is-in-list(" );
06112             kdDebug()<<" valExpression :"<<valExpression<<endl;
06113             valExpression = valExpression.remove( ")" );
06114             d->extra()->validity->listValidity = QStringList::split( ";", valExpression );
06115 
06116         }
06117         //TrueFunction ::= cell-content-is-whole-number() | cell-content-is-decimal-number() | cell-content-is-date() | cell-content-is-time()
06118         else
06119         {
06120             if (valExpression.contains( "cell-content-is-whole-number()" ) )
06121             {
06122                 d->extra()->validity->m_restriction =  Restriction::Number;
06123                 valExpression = valExpression.remove( "oooc:cell-content-is-whole-number() and " );
06124             }
06125             else if (valExpression.contains( "cell-content-is-decimal-number()" ) )
06126             {
06127                 d->extra()->validity->m_restriction = Restriction::Integer;
06128                 valExpression = valExpression.remove( "oooc:cell-content-is-decimal-number() and " );
06129             }
06130             else if (valExpression.contains( "cell-content-is-date()" ) )
06131             {
06132                 d->extra()->validity->m_restriction = Restriction::Date;
06133                 valExpression = valExpression.remove( "oooc:cell-content-is-date() and " );
06134             }
06135             else if (valExpression.contains( "cell-content-is-time()" ) )
06136             {
06137                 d->extra()->validity->m_restriction = Restriction::Time;
06138                 valExpression = valExpression.remove( "oooc:cell-content-is-time() and " );
06139             }
06140             kdDebug()<<"valExpression :"<<valExpression<<endl;
06141 
06142             if ( valExpression.contains( "cell-content()" ) )
06143             {
06144                 valExpression = valExpression.remove( "cell-content()" );
06145                 loadOasisValidationCondition( valExpression );
06146             }
06147             //GetFunction ::= cell-content-is-between(Value, Value) | cell-content-is-not-between(Value, Value)
06148             //for the moment we support just int/double value, not text/date/time :(
06149             if ( valExpression.contains( "cell-content-is-between(" ) )
06150             {
06151                 valExpression = valExpression.remove( "cell-content-is-between(" );
06152                 valExpression = valExpression.remove( ")" );
06153                 QStringList listVal = QStringList::split( "," , valExpression );
06154                 loadOasisValidationValue( listVal );
06155                 d->extra()->validity->m_cond = Conditional::Between;
06156             }
06157             if ( valExpression.contains( "cell-content-is-not-between(" ) )
06158             {
06159                 valExpression = valExpression.remove( "cell-content-is-not-between(" );
06160                 valExpression = valExpression.remove( ")" );
06161                 QStringList listVal = QStringList::split( ",", valExpression );
06162                 loadOasisValidationValue( listVal );
06163                 d->extra()->validity->m_cond = Conditional::Different;
06164             }
06165         }
06166     }
06167     if ( element.hasAttributeNS( KoXmlNS::table, "allow-empty-cell" ) )
06168     {
06169         kdDebug()<<" element.hasAttribute( table:allow-empty-cell ) :"<<element.hasAttributeNS( KoXmlNS::table, "allow-empty-cell" )<<endl;
06170         d->extra()->validity->allowEmptyCell = ( ( element.attributeNS( KoXmlNS::table, "allow-empty-cell", QString::null )=="true" ) ? true : false );
06171     }
06172     if ( element.hasAttributeNS( KoXmlNS::table, "base-cell-address" ) )
06173     {
06174         //todo what is it ?
06175     }
06176 
06177     QDomElement help = KoDom::namedItemNS( element, KoXmlNS::table, "help-message" );
06178     if ( !help.isNull() )
06179     {
06180         if ( help.hasAttributeNS( KoXmlNS::table, "title" ) )
06181         {
06182             kdDebug()<<"help.attribute( table:title ) :"<<help.attributeNS( KoXmlNS::table, "title", QString::null )<<endl;
06183             d->extra()->validity->titleInfo = help.attributeNS( KoXmlNS::table, "title", QString::null );
06184         }
06185         if ( help.hasAttributeNS( KoXmlNS::table, "display" ) )
06186         {
06187             kdDebug()<<"help.attribute( table:display ) :"<<help.attributeNS( KoXmlNS::table, "display", QString::null )<<endl;
06188             d->extra()->validity->displayValidationInformation = ( ( help.attributeNS( KoXmlNS::table, "display", QString::null )=="true" ) ? true : false );
06189         }
06190         QDomElement attrText = KoDom::namedItemNS( help, KoXmlNS::text, "p" );
06191         if ( !attrText.isNull() )
06192         {
06193             kdDebug()<<"help text :"<<attrText.text()<<endl;
06194             d->extra()->validity->messageInfo = attrText.text();
06195         }
06196     }
06197 
06198     QDomElement error = KoDom::namedItemNS( element, KoXmlNS::table, "error-message" );
06199     if ( !error.isNull() )
06200     {
06201         if ( error.hasAttributeNS( KoXmlNS::table, "title" ) )
06202             d->extra()->validity->title = error.attributeNS( KoXmlNS::table, "title", QString::null );
06203         if ( error.hasAttributeNS( KoXmlNS::table, "message-type" ) )
06204         {
06205             QString str = error.attributeNS( KoXmlNS::table, "message-type", QString::null );
06206             if ( str == "warning" )
06207               d->extra()->validity->m_action = Action::Warning;
06208             else if ( str == "information" )
06209               d->extra()->validity->m_action = Action::Information;
06210             else if ( str == "stop" )
06211               d->extra()->validity->m_action = Action::Stop;
06212             else
06213                 kdDebug()<<"validation : message type unknown  :"<<str<<endl;
06214         }
06215 
06216         if ( error.hasAttributeNS( KoXmlNS::table, "display" ) )
06217         {
06218             kdDebug()<<" display message :"<<error.attributeNS( KoXmlNS::table, "display", QString::null )<<endl;
06219             d->extra()->validity->displayMessage = (error.attributeNS( KoXmlNS::table, "display", QString::null )=="true");
06220         }
06221         QDomElement attrText = KoDom::namedItemNS( error, KoXmlNS::text, "p" );
06222         if ( !attrText.isNull() )
06223             d->extra()->validity->message = attrText.text();
06224     }
06225 }
06226 
06227 
06228 void Cell::loadOasisValidationValue( const QStringList &listVal )
06229 {
06230     bool ok = false;
06231     kdDebug()<<" listVal[0] :"<<listVal[0]<<" listVal[1] :"<<listVal[1]<<endl;
06232 
06233     if ( d->extra()->validity->m_restriction == Restriction::Date )
06234     {
06235         d->extra()->validity->dateMin = QDate::fromString( listVal[0] );
06236         d->extra()->validity->dateMax = QDate::fromString( listVal[1] );
06237     }
06238     else if ( d->extra()->validity->m_restriction == Restriction::Time )
06239     {
06240         d->extra()->validity->timeMin = QTime::fromString( listVal[0] );
06241         d->extra()->validity->timeMax = QTime::fromString( listVal[1] );
06242     }
06243     else
06244     {
06245         d->extra()->validity->valMin = listVal[0].toDouble(&ok);
06246         if ( !ok )
06247         {
06248             d->extra()->validity->valMin = listVal[0].toInt(&ok);
06249             if ( !ok )
06250                 kdDebug()<<" Try to parse this value :"<<listVal[0]<<endl;
06251 
06252 #if 0
06253             if ( !ok )
06254                 d->extra()->validity->valMin = listVal[0];
06255 #endif
06256         }
06257         ok=false;
06258         d->extra()->validity->valMax = listVal[1].toDouble(&ok);
06259         if ( !ok )
06260         {
06261             d->extra()->validity->valMax = listVal[1].toInt(&ok);
06262             if ( !ok )
06263                 kdDebug()<<" Try to parse this value :"<<listVal[1]<<endl;
06264 
06265 #if 0
06266             if ( !ok )
06267                 d->extra()->validity->valMax = listVal[1];
06268 #endif
06269         }
06270     }
06271 }
06272 
06273 void Cell::loadOasisValidationCondition( QString &valExpression )
06274 {
06275     QString value;
06276     if (valExpression.find( "<=" )==0 )
06277     {
06278         value = valExpression.remove( 0,2 );
06279         d->extra()->validity->m_cond = Conditional::InferiorEqual;
06280     }
06281     else if (valExpression.find( ">=" )==0 )
06282     {
06283         value = valExpression.remove( 0,2 );
06284         d->extra()->validity->m_cond = Conditional::SuperiorEqual;
06285     }
06286     else if (valExpression.find( "!=" )==0 )
06287     {
06288         //add Differentto attribute
06289         value = valExpression.remove( 0,2 );
06290         d->extra()->validity->m_cond = Conditional::DifferentTo;
06291     }
06292     else if ( valExpression.find( "<" )==0 )
06293     {
06294         value = valExpression.remove( 0,1 );
06295         d->extra()->validity->m_cond = Conditional::Inferior;
06296     }
06297     else if(valExpression.find( ">" )==0 )
06298     {
06299         value = valExpression.remove( 0,1 );
06300         d->extra()->validity->m_cond = Conditional::Superior;
06301     }
06302     else if (valExpression.find( "=" )==0 )
06303     {
06304         value = valExpression.remove( 0,1 );
06305         d->extra()->validity->m_cond = Conditional::Equal;
06306     }
06307     else
06308         kdDebug()<<" I don't know how to parse it :"<<valExpression<<endl;
06309     if ( d->extra()->validity->m_restriction == Restriction::Date )
06310     {
06311         d->extra()->validity->dateMin = QDate::fromString( value );
06312     }
06313     else if (d->extra()->validity->m_restriction == Restriction::Date )
06314     {
06315         d->extra()->validity->timeMin = QTime::fromString( value );
06316     }
06317     else
06318     {
06319         bool ok = false;
06320         d->extra()->validity->valMin = value.toDouble(&ok);
06321         if ( !ok )
06322         {
06323             d->extra()->validity->valMin = value.toInt(&ok);
06324             if ( !ok )
06325                 kdDebug()<<" Try to parse this value :"<<value<<endl;
06326 
06327 #if 0
06328             if ( !ok )
06329                 d->extra()->validity->valMin = value;
06330 #endif
06331         }
06332     }
06333 }
06334 
06335 
06336 bool Cell::load( const QDomElement & cell, int _xshift, int _yshift,
06337                  Paste::Mode pm, Paste::Operation op, bool paste )
06338 {
06339     bool ok;
06340 
06341     //
06342     // First of all determine in which row and column this
06343     // cell belongs.
06344     //
06345     d->row = cell.attribute( "row" ).toInt( &ok ) + _yshift;
06346     if ( !ok ) return false;
06347     d->column = cell.attribute( "column" ).toInt( &ok ) + _xshift;
06348     if ( !ok ) return false;
06349 
06350     // Validation
06351     if ( d->row < 1 || d->row > KS_rowMax )
06352     {
06353         kdDebug(36001) << "Cell::load: Value out of Range Cell:row=" << d->row << endl;
06354         return false;
06355     }
06356     if ( d->column < 1 || d->column > KS_colMax )
06357     {
06358         kdDebug(36001) << "Cell::load: Value out of Range Cell:column=" << d->column << endl;
06359         return false;
06360     }
06361 
06362     //
06363     // Load formatting information.
06364     //
06365     QDomElement f = cell.namedItem( "format" ).toElement();
06366     if ( !f.isNull()
06367           && ( (pm == Paste::Normal) || (pm == Paste::Format) || (pm == Paste::NoBorder) ) )
06368     {
06369         // send pm parameter. Didn't load Borders if pm==NoBorder
06370 
06371       if ( !format()->load( f, pm, paste ) )
06372             return false;
06373 
06374         if ( f.hasAttribute( "colspan" ) )
06375         {
06376             int i = f.attribute("colspan").toInt( &ok );
06377             if ( !ok ) return false;
06378             // Validation
06379             if ( i < 0 || i > KS_spanMax )
06380             {
06381                 kdDebug(36001) << "Value out of range Cell::colspan=" << i << endl;
06382                 return false;
06383             }
06384             if (i || d->hasExtra())
06385               d->extra()->extraXCells = i;
06386             if ( i > 0 )
06387             {
06388               setFlag(Flag_Merged);
06389             }
06390         }
06391 
06392         if ( f.hasAttribute( "rowspan" ) )
06393         {
06394             int i = f.attribute("rowspan").toInt( &ok );
06395             if ( !ok ) return false;
06396             // Validation
06397             if ( i < 0 || i > KS_spanMax )
06398             {
06399                 kdDebug(36001) << "Value out of range Cell::rowspan=" << i << endl;
06400                 return false;
06401             }
06402             if (i || d->hasExtra())
06403               d->extra()->extraYCells = i;
06404             if ( i > 0 )
06405             {
06406               setFlag(Flag_Merged);
06407             }
06408         }
06409 
06410         if ( testFlag( Flag_Merged ) )
06411         {
06412             if (d->hasExtra())
06413               mergeCells( d->column, d->row, d->extra()->extraXCells, d->extra()->extraYCells );
06414         }
06415 
06416     }
06417 
06418     //
06419     // Load the condition section of a cell.
06420     //
06421     QDomElement conditionsElement = cell.namedItem( "condition" ).toElement();
06422     if ( !conditionsElement.isNull())
06423     {
06424       if (d->hasExtra())
06425         delete d->extra()->conditions;
06426       d->extra()->conditions = new Conditions( this );
06427       d->extra()->conditions->loadConditions( conditionsElement );
06428       d->extra()->conditions->checkMatches();
06429     }
06430     else if ((pm == Paste::Normal) || (pm == Paste::NoBorder))
06431     {
06432       //clear the conditional formatting
06433       if (d->hasExtra())
06434       {
06435         delete d->extra()->conditions;
06436         d->extra()->conditions = 0;
06437       }
06438     }
06439 
06440     QDomElement validity = cell.namedItem( "validity" ).toElement();
06441     if ( !validity.isNull())
06442     {
06443         QDomElement param = validity.namedItem( "param" ).toElement();
06444         if(!param.isNull())
06445         {
06446           d->extra()->validity = new Validity;
06447           if ( param.hasAttribute( "cond" ) )
06448           {
06449             d->extra()->validity->m_cond = (Conditional::Type) param.attribute("cond").toInt( &ok );
06450             if ( !ok )
06451               return false;
06452           }
06453           if ( param.hasAttribute( "action" ) )
06454           {
06455             d->extra()->validity->m_action = (Action::Type) param.attribute("action").toInt( &ok );
06456             if ( !ok )
06457               return false;
06458           }
06459           if ( param.hasAttribute( "allow" ) )
06460           {
06461             d->extra()->validity->m_restriction = (Restriction::Type) param.attribute("allow").toInt( &ok );
06462             if ( !ok )
06463               return false;
06464           }
06465           if ( param.hasAttribute( "valmin" ) )
06466           {
06467             d->extra()->validity->valMin = param.attribute("valmin").toDouble( &ok );
06468             if ( !ok )
06469               return false;
06470           }
06471           if ( param.hasAttribute( "valmax" ) )
06472           {
06473             d->extra()->validity->valMax = param.attribute("valmax").toDouble( &ok );
06474             if ( !ok )
06475               return false;
06476           }
06477           if ( param.hasAttribute( "displaymessage" ) )
06478           {
06479               d->extra()->validity->displayMessage = ( bool )param.attribute("displaymessage").toInt();
06480           }
06481           if ( param.hasAttribute( "displayvalidationinformation" ) )
06482           {
06483               d->extra()->validity->displayValidationInformation = ( bool )param.attribute("displayvalidationinformation").toInt();
06484           }
06485           if ( param.hasAttribute( "allowemptycell" ) )
06486           {
06487               d->extra()->validity->allowEmptyCell = ( bool )param.attribute("allowemptycell").toInt();
06488           }
06489           if ( param.hasAttribute("listvalidity") )
06490           {
06491               d->extra()->validity->listValidity=QStringList::split(";", param.attribute("listvalidity") );
06492           }
06493         }
06494         QDomElement inputTitle = validity.namedItem( "inputtitle" ).toElement();
06495         if (!inputTitle.isNull())
06496         {
06497             d->extra()->validity->titleInfo = inputTitle.text();
06498         }
06499         QDomElement inputMessage = validity.namedItem( "inputmessage" ).toElement();
06500         if (!inputMessage.isNull())
06501         {
06502             d->extra()->validity->messageInfo = inputMessage.text();
06503         }
06504 
06505         QDomElement title = validity.namedItem( "title" ).toElement();
06506         if (!title.isNull())
06507         {
06508             d->extra()->validity->title = title.text();
06509         }
06510         QDomElement message = validity.namedItem( "message" ).toElement();
06511         if (!message.isNull())
06512         {
06513             d->extra()->validity->message = message.text();
06514         }
06515         QDomElement timeMin = validity.namedItem( "timemin" ).toElement();
06516         if ( !timeMin.isNull()  )
06517         {
06518             d->extra()->validity->timeMin = toTime(timeMin);
06519         }
06520         QDomElement timeMax = validity.namedItem( "timemax" ).toElement();
06521         if ( !timeMax.isNull()  )
06522         {
06523             d->extra()->validity->timeMax = toTime(timeMax);
06524          }
06525         QDomElement dateMin = validity.namedItem( "datemin" ).toElement();
06526         if ( !dateMin.isNull()  )
06527         {
06528             d->extra()->validity->dateMin = toDate(dateMin);
06529          }
06530         QDomElement dateMax = validity.namedItem( "datemax" ).toElement();
06531         if ( !dateMax.isNull()  )
06532         {
06533             d->extra()->validity->dateMax = toDate(dateMax);
06534          }
06535     }
06536     else if ((pm == Paste::Normal) || (pm == Paste::NoBorder))
06537     {
06538       // clear the validity
06539       removeValidity();
06540     }
06541 
06542     //
06543     // Load the comment
06544     //
06545     QDomElement comment = cell.namedItem( "comment" ).toElement();
06546     if ( !comment.isNull() && ( pm == Paste::Normal || pm == Paste::Comment || pm == Paste::NoBorder ))
06547     {
06548         QString t = comment.text();
06549         //t = t.stripWhiteSpace();
06550         format()->setComment( t );
06551     }
06552 
06553     //
06554     // The real content of the cell is loaded here. It is stored in
06555     // the "text" tag, which contains either a text or a CDATA section.
06556     //
06557     // TODO: make this suck less. We set data twice, in loadCellData, and
06558     // also here. Not good.
06559     QDomElement text = cell.namedItem( "text" ).toElement();
06560 
06561     if ( !text.isNull() &&
06562           ( pm == Paste::Normal || pm == Paste::Text || pm == Paste::NoBorder || pm == Paste::Result ) )
06563     {
06564       /* older versions mistakenly put the datatype attribute on the cell
06565          instead of the text.  Just move it over in case we're parsing
06566          an old document */
06567       if ( cell.hasAttribute( "dataType" ) ) // new docs
06568         text.setAttribute( "dataType", cell.attribute( "dataType" ) );
06569 
06570       QDomElement result = cell.namedItem( "result" ).toElement();
06571       QString txt = text.text();
06572       if ((pm == Paste::Result) && (txt[0] == '='))
06573         // paste text of the element, if we want to paste result
06574         // and the source cell contains a formula
06575         // note that we mustn't use setCellValue after this, or else we lose
06576         // all the formulas ...
06577           d->strText = result.text();
06578       else
06579           //otherwise copy everything
06580           loadCellData(text, op);
06581 
06582       if ( !result.isNull() )
06583       {
06584         QString dataType;
06585         QString t = result.text();
06586 
06587         if ( result.hasAttribute( "dataType" ) )
06588           dataType = result.attribute( "dataType" );
06589         if ( result.hasAttribute( "outStr" ) )
06590         {
06591           d->strOutText = result.attribute( "outStr" );
06592           if ( !d->strOutText.isEmpty() )
06593             clearFlag( Flag_TextFormatDirty );
06594         }
06595 
06596         bool clear = true;
06597         // boolean ?
06598         if( dataType == "Bool" )
06599         {
06600           if ( t == "false" )
06601             setValue( false );
06602           else if ( t == "true" )
06603             setValue( true );
06604           else
06605             clear = false;
06606         }
06607         else if( dataType == "Num" )
06608         {
06609           bool ok = false;
06610           double dd = t.toDouble( &ok );
06611           if ( ok )
06612             setValue ( dd );
06613           else
06614             clear = false;
06615         }
06616         else if( dataType == "Date" )
06617         {
06618           bool ok = false;
06619           double dd = t.toDouble( &ok );
06620           if ( ok )
06621             setValue ( dd );
06622           else
06623           {
06624             int pos   = t.find( '/' );
06625             int year  = t.mid( 0, pos ).toInt();
06626             int pos1  = t.find( '/', pos + 1 );
06627             int month = t.mid( pos + 1, ( ( pos1 - 1 ) - pos ) ).toInt();
06628             int day   = t.right( t.length() - pos1 - 1 ).toInt();
06629             QDate date( year, month, day );
06630             if ( date.isValid() )
06631               setValue( date );
06632             else
06633               clear = false;
06634           }
06635         }
06636         else if( dataType == "Time" )
06637         {
06638           bool ok = false;
06639           double dd = t.toDouble( &ok );
06640           if ( ok )
06641             setCellValue( dd );
06642           else
06643           {
06644             int hours   = -1;
06645             int minutes = -1;
06646             int second  = -1;
06647             int pos, pos1;
06648             pos   = t.find( ':' );
06649             hours = t.mid( 0, pos ).toInt();
06650             pos1  = t.find( ':', pos + 1 );
06651             minutes = t.mid( pos + 1, ( ( pos1 - 1 ) - pos ) ).toInt();
06652             second  = t.right( t.length() - pos1 - 1 ).toInt();
06653             QTime time( hours, minutes, second );
06654             if ( time.isValid() )
06655               setValue( time );
06656             else
06657               clear = false;
06658           }
06659         }
06660         else
06661         {
06662           setValue( t );
06663         }
06664 
06665         // if ( clear )
06666         //   clearFlag( Flag_CalcDirty );
06667       }
06668     }
06669 
06670     return true;
06671 }
06672 
06673 bool Cell::loadCellData(const QDomElement & text, Paste::Operation op )
06674 {
06675   //TODO: use converter()->asString() to generate strText
06676 
06677   QString t = text.text();
06678   t = t.stripWhiteSpace();
06679 
06680   setFlag(Flag_LayoutDirty);
06681   setFlag(Flag_TextFormatDirty);
06682 
06683   // A formula like =A1+A2 ?
06684   if( t[0] == '=' )
06685   {
06686     t = decodeFormula( t, d->column, d->row );
06687     setCellText (pasteOperation( t, d->strText, op ));
06688 
06689     setFlag(Flag_CalcDirty);
06690     clearAllErrors();
06691 
06692     if ( !makeFormula() )
06693       kdError(36001) << "ERROR: Syntax ERROR" << endl;
06694   }
06695   // rich text ?
06696   else if (t[0] == '!' )
06697   {
06698       // KSpread pre 1.4 stores hyperlink as rich text (first char is '!')
06699       // extract the link and the correspoding text
06700       // This is a rather dirty hack, but enough for KSpread generated XML
06701       bool inside_tag = false;
06702       QString qml_text;
06703       QString tag;
06704       QString qml_link;
06705 
06706       for( unsigned i = 1; i < t.length(); i++ )
06707       {
06708         QChar ch = t[i];
06709         if( ch == '<' )
06710         {
06711           if( !inside_tag )
06712           {
06713             inside_tag = true;
06714             tag = QString::null;
06715           }
06716         }
06717         else if( ch == '>' )
06718         {
06719           if( inside_tag )
06720           {
06721             inside_tag = false;
06722             if( tag.startsWith( "a href=\"", true ) )
06723             if( tag.endsWith( "\"" ) )
06724               qml_link = tag.mid( 8, tag.length()-9 );
06725             tag = QString::null;
06726           }
06727         }
06728         else
06729         {
06730           if( !inside_tag )
06731             qml_text += ch;
06732           else
06733             tag += ch;
06734         }
06735       }
06736 
06737       if( !qml_link.isEmpty() )
06738         d->extra()->link = qml_link;
06739       d->strText = qml_text;
06740       setValue( d->strText );
06741   }
06742   else
06743   {
06744     bool newStyleLoading = true;
06745     QString dataType;
06746 
06747     if ( text.hasAttribute( "dataType" ) ) // new docs
06748     {
06749         dataType = text.attribute( "dataType" );
06750     }
06751     else // old docs: do the ugly solution of calling checkTextInput to parse the text
06752     {
06753       // ...except for date/time
06754       if (isDate() && ( t.contains('/') == 2 ))
06755         dataType = "Date";
06756       else if (isTime() && ( t.contains(':') == 2 ) )
06757         dataType = "Time";
06758       else
06759       {
06760         d->strText = pasteOperation( t, d->strText, op );
06761         checkTextInput();
06762         //kdDebug(36001) << "Cell::load called checkTextInput, got dataType=" << dataType << "  t=" << t << endl;
06763         newStyleLoading = false;
06764       }
06765     }
06766 
06767     if ( newStyleLoading )
06768     {
06769       d->value = Value::empty();
06770       clearAllErrors();
06771 
06772       // boolean ?
06773       if( dataType == "Bool" )
06774       {
06775         bool val = (t.lower() == "true");
06776         setCellValue (val);
06777       }
06778 
06779       // number ?
06780       else if( dataType == "Num" )
06781       {
06782         bool ok = false;
06783         if (t.contains('.'))
06784           setValue ( Value( t.toDouble(&ok) ) ); // We save in non-localized format
06785         else
06786           setValue ( Value( t.toLong(&ok) ) );
06787         if ( !ok )
06788   {
06789           kdWarning(36001) << "Couldn't parse '" << t << "' as number." << endl;
06790   }
06791   /* We will need to localize the text version of the number */
06792   KLocale* locale = format()->sheet()->doc()->locale();
06793 
06794         /* KLocale::formatNumber requires the precision we want to return.
06795         */
06796         int precision = t.length() - t.find('.') - 1;
06797 
06798   if ( formatType() == Percentage_format )
06799         {
06800           if (value().isInteger())
06801             t = locale->formatNumber( value().asInteger() * 100 );
06802           else
06803             t = locale->formatNumber( value().asFloat() * 100.0, precision );
06804           d->strText = pasteOperation( t, d->strText, op );
06805           d->strText += '%';
06806         }
06807         else
06808         {
06809           if (value().isInteger())
06810             t = locale->formatLong(value().asInteger());
06811           else
06812             t = locale->formatNumber(value().asFloat(), precision);
06813           d->strText = pasteOperation( t, d->strText, op );
06814         }
06815       }
06816 
06817       // date ?
06818       else if( dataType == "Date" )
06819       {
06820         int pos = t.find('/');
06821         int year = t.mid(0,pos).toInt();
06822         int pos1 = t.find('/',pos+1);
06823         int month = t.mid(pos+1,((pos1-1)-pos)).toInt();
06824         int day = t.right(t.length()-pos1-1).toInt();
06825         setValue( QDate(year,month,day) );
06826         if ( value().asDate().isValid() ) // Should always be the case for new docs
06827           d->strText = locale()->formatDate( value().asDate(), true );
06828         else // This happens with old docs, when format is set wrongly to date
06829         {
06830           d->strText = pasteOperation( t, d->strText, op );
06831           checkTextInput();
06832         }
06833       }
06834 
06835       // time ?
06836       else if( dataType == "Time" )
06837       {
06838         int hours = -1;
06839         int minutes = -1;
06840         int second = -1;
06841         int pos, pos1;
06842         pos = t.find(':');
06843         hours = t.mid(0,pos).toInt();
06844         pos1 = t.find(':',pos+1);
06845         minutes = t.mid(pos+1,((pos1-1)-pos)).toInt();
06846         second = t.right(t.length()-pos1-1).toInt();
06847         setValue( QTime(hours,minutes,second) );
06848         if ( value().asTime().isValid() ) // Should always be the case for new docs
06849           d->strText = locale()->formatTime( value().asTime(), true );
06850         else  // This happens with old docs, when format is set wrongly to time
06851         {
06852           d->strText = pasteOperation( t, d->strText, op );
06853           checkTextInput();
06854         }
06855       }
06856 
06857       else
06858       {
06859         // Set the cell's text
06860         d->strText = pasteOperation( t, d->strText, op );
06861         setValue( d->strText );
06862       }
06863     }
06864   }
06865 
06866   if ( text.hasAttribute( "outStr" ) ) // very new docs
06867   {
06868     d->strOutText = text.attribute( "outStr" );
06869     if ( !d->strOutText.isEmpty() )
06870       clearFlag( Flag_TextFormatDirty );
06871   }
06872 
06873   if ( !format()->sheet()->isLoading() )
06874     setCellText( d->strText );
06875 
06876   if ( d->hasExtra() && d->extra()->conditions )
06877     d->extra()->conditions->checkMatches();
06878 
06879   return true;
06880 }
06881 
06882 QTime Cell::toTime(const QDomElement &element)
06883 {
06884     //TODO: can't we use tryParseTime (after modification) instead?
06885     QString t = element.text();
06886     t = t.stripWhiteSpace();
06887     int hours = -1;
06888     int minutes = -1;
06889     int second = -1;
06890     int pos, pos1;
06891     pos = t.find(':');
06892     hours = t.mid(0,pos).toInt();
06893     pos1 = t.find(':',pos+1);
06894     minutes = t.mid(pos+1,((pos1-1)-pos)).toInt();
06895     second = t.right(t.length()-pos1-1).toInt();
06896     setValue( Value( QTime(hours,minutes,second)) );
06897     return value().asTime();
06898 }
06899 
06900 QDate Cell::toDate(const QDomElement &element)
06901 {
06902     QString t = element.text();
06903     int pos;
06904     int pos1;
06905     int year = -1;
06906     int month = -1;
06907     int day = -1;
06908     pos = t.find('/');
06909     year = t.mid(0,pos).toInt();
06910     pos1 = t.find('/',pos+1);
06911     month = t.mid(pos+1,((pos1-1)-pos)).toInt();
06912     day = t.right(t.length()-pos1-1).toInt();
06913     setValue( Value( QDate(year,month,day) ) );
06914     return value().asDate();
06915 }
06916 
06917 QString Cell::pasteOperation( const QString &new_text, const QString &old_text, Paste::Operation op )
06918 {
06919   if ( op == Paste::OverWrite )
06920         return new_text;
06921 
06922     QString tmp_op;
06923     QString tmp;
06924     QString old;
06925 
06926     if( !new_text.isEmpty() && new_text[0] == '=' )
06927     {
06928         tmp = new_text.right( new_text.length() - 1 );
06929     }
06930     else
06931     {
06932         tmp = new_text;
06933     }
06934 
06935     if ( old_text.isEmpty() &&
06936          ( op == Paste::Add || op == Paste::Mul || op == Paste::Sub || op == Paste::Div ) )
06937     {
06938       old = "=0";
06939     }
06940 
06941     if( !old_text.isEmpty() && old_text[0] == '=' )
06942     {
06943         old = old_text.right( old_text.length() - 1 );
06944     }
06945     else
06946     {
06947         old = old_text;
06948     }
06949 
06950     bool b1, b2;
06951     tmp.toDouble( &b1 );
06952     old.toDouble( &b2 );
06953     if (b1 && !b2 && old.length() == 0)
06954     {
06955       old = "0";
06956       b2 = true;
06957     }
06958 
06959     if( b1 && b2 )
06960     {
06961         switch( op )
06962         {
06963           case  Paste::Add:
06964             tmp_op = QString::number(old.toDouble()+tmp.toDouble());
06965             break;
06966           case Paste::Mul :
06967             tmp_op = QString::number(old.toDouble()*tmp.toDouble());
06968             break;
06969           case Paste::Sub:
06970             tmp_op = QString::number(old.toDouble()-tmp.toDouble());
06971             break;
06972           case Paste::Div:
06973             tmp_op = QString::number(old.toDouble()/tmp.toDouble());
06974             break;
06975         default:
06976             Q_ASSERT( 0 );
06977         }
06978 
06979         setFlag(Flag_LayoutDirty);
06980         clearAllErrors();
06981 
06982         return tmp_op;
06983     }
06984     else if ( ( new_text[0] == '=' && old_text[0] == '=' ) ||
06985               ( b1 && old_text[0] == '=' ) || ( new_text[0] == '=' && b2 ) )
06986     {
06987         switch( op )
06988         {
06989           case Paste::Add :
06990             tmp_op="=("+old+")+"+"("+tmp+")";
06991             break;
06992           case Paste::Mul :
06993             tmp_op="=("+old+")*"+"("+tmp+")";
06994             break;
06995           case Paste::Sub:
06996             tmp_op="=("+old+")-"+"("+tmp+")";
06997             break;
06998           case Paste::Div:
06999             tmp_op="=("+old+")/"+"("+tmp+")";
07000             break;
07001         default :
07002             Q_ASSERT( 0 );
07003         }
07004 
07005         tmp_op = decodeFormula( tmp_op, d->column, d->row );
07006         setFlag(Flag_LayoutDirty);
07007         clearAllErrors();
07008 
07009         return tmp_op;
07010     }
07011 
07012     tmp = decodeFormula( new_text, d->column, d->row );
07013     setFlag(Flag_LayoutDirty);
07014     clearAllErrors();
07015 
07016     return tmp;
07017 }
07018 
07019 QString Cell::testAnchor( int x, int y ) const
07020 {
07021   if( link().isEmpty() )
07022     return QString::null;
07023 
07024   const Doc* doc = format()->sheet()->doc();
07025   int x1 = doc->zoomItX( d->textX );
07026   int y1 = doc->zoomItX( d->textY - d->textHeight );
07027   int x2 = doc->zoomItX( d->textX + d->textWidth );
07028   int y2 = doc->zoomItX( d->textY );
07029 
07030   if( x > x1 ) if( x < x2 )
07031   if( y > y1 ) if( y < y2 )
07032     return link();
07033 
07034   return QString::null;
07035 }
07036 
07037 void Cell::sheetDies()
07038 {
07039     // Avoid unobscuring the cells in the destructor.
07040     if (d->hasExtra())
07041     {
07042       d->extra()->extraXCells = 0;
07043       d->extra()->extraYCells = 0;
07044       d->extra()->mergedXCells = 0;
07045       d->extra()->mergedYCells = 0;
07046     }
07047 
07048     //d->nextCell = 0;
07049     //d->previousCell = 0;
07050 }
07051 
07052 Cell::~Cell()
07053 {
07054     if ( d->nextCell )
07055         d->nextCell->setPreviousCell( d->previousCell );
07056     if ( d->previousCell )
07057         d->previousCell->setNextCell( d->nextCell );
07058 
07059     if (d->hasExtra())
07060     {
07061       delete d->extra()->validity;
07062     }
07063 
07064     // Unobscure cells.
07065     int extraXCells = d->hasExtra() ? d->extra()->extraXCells : 0;
07066     int extraYCells = d->hasExtra() ? d->extra()->extraYCells : 0;
07067     for( int x = 0; x <= extraXCells; ++x )
07068         for( int y = (x == 0) ? 1 : 0; // avoid looking at (+0,+0)
07069              y <= extraYCells; ++y )
07070     {
07071         Cell* cell = format()->sheet()->cellAt( d->column + x, d->row + y );
07072         if ( cell )
07073             cell->unobscure(this);
07074     }
07075 
07076     d->value = Value::empty();
07077 
07078     if (!isDefault())
07079       valueChanged ();  //our value has been changed (is now null), but only if we aren't default
07080 
07081     delete d->format;
07082     delete d;
07083 }
07084 
07085 bool Cell::operator > ( const Cell & cell ) const
07086 {
07087   if ( value().isNumber() ) // ### what about bools ?
07088   {
07089     if ( cell.value().isNumber() )
07090       return value().asFloat() > cell.value().asFloat();
07091     else
07092       return false; // numbers are always < than texts
07093   }
07094   else if(isDate())
07095   {
07096      if( cell.isDate() )
07097         return value().asDate() > cell.value().asDate();
07098      else if (cell.value().isNumber())
07099         return true;
07100      else
07101         return false; //date are always < than texts and time
07102   }
07103   else if(isTime())
07104   {
07105      if( cell.isTime() )
07106         return value().asTime() > cell.value().asTime();
07107      else if( cell.isDate())
07108         return true; //time are always > than date
07109      else if( cell.value().isNumber())
07110         return true;
07111      else
07112         return false; //time are always < than texts
07113   }
07114   else
07115   {
07116       if ( Map::respectCase )
07117           return value().asString().compare(cell.value().asString()) > 0;
07118       else
07119           return ( value().asString() ).lower().compare(cell.value().asString().lower()) > 0;
07120   }
07121 }
07122 
07123 bool Cell::operator < ( const Cell & cell ) const
07124 {
07125   if ( value().isNumber() )
07126   {
07127     if ( cell.value().isNumber() )
07128       return value().asFloat() < cell.value().asFloat();
07129     else
07130       return true; // numbers are always < than texts
07131   }
07132   else if(isDate())
07133   {
07134      if( cell.isDate() )
07135         return value().asDateTime().date() < cell.value().asDateTime().date();
07136      else if( cell.value().isNumber())
07137         return false;
07138      else
07139         return true; //date are always < than texts and time
07140   }
07141   else if(isTime())
07142   {
07143      if( cell.isTime() )
07144         return value().asDateTime().time() < cell.value().asDateTime().time();
07145      else if(cell.isDate())
07146         return false; //time are always > than date
07147      else if( cell.value().isNumber())
07148         return false;
07149      else
07150         return true; //time are always < than texts
07151   }
07152   else
07153   {
07154       if ( Map::respectCase )
07155           return value().asString().compare(cell.value().asString()) < 0;
07156       else
07157           return ( value().asString() ).lower().compare(cell.value().asString().lower()) < 0;
07158   }
07159 }
07160 
07161 QRect Cell::cellRect()
07162 {
07163   Q_ASSERT(!isDefault());
07164   return QRect(QPoint(d->column, d->row), QPoint(d->column, d->row));
07165 }
07166 
07167 QValueList<Conditional> Cell::conditionList() const
07168 {
07169   if ( !d->hasExtra() || !d->extra()->conditions )
07170   {
07171     QValueList<Conditional> emptyList;
07172     return emptyList;
07173   }
07174 
07175   return d->extra()->conditions->conditionList();
07176 }
07177 
07178 void Cell::setConditionList( const QValueList<Conditional> & newList )
07179 {
07180   if (d->hasExtra())
07181     delete d->extra()->conditions;
07182   d->extra()->conditions = new Conditions( this );
07183   d->extra()->conditions->setConditionList( newList );
07184   d->extra()->conditions->checkMatches();
07185 }
07186 
07187 bool Cell::hasError() const
07188 {
07189   return ( testFlag(Flag_ParseError) ||
07190            testFlag(Flag_CircularCalculation) ||
07191            testFlag(Flag_DependancyError));
07192 }
07193 
07194 void Cell::clearAllErrors()
07195 {
07196   clearFlag( Flag_ParseError );
07197   clearFlag( Flag_CircularCalculation );
07198   clearFlag( Flag_DependancyError );
07199 }
07200 
07201 bool Cell::calcDirtyFlag()
07202 {
07203   return isFormula() ? testFlag( Flag_CalcDirty ) : false;
07204 }
07205 
07206 bool Cell::layoutDirtyFlag() const
07207 {
07208   return testFlag( Flag_LayoutDirty );
07209 }
07210 
07211 void Cell::clearDisplayDirtyFlag()
07212 {
07213   clearFlag( Flag_DisplayDirty );
07214 }
07215 
07216 void Cell::setDisplayDirtyFlag()
07217 {
07218   setFlag( Flag_DisplayDirty );
07219 }
07220 
07221 bool Cell::doesMergeCells() const
07222 {
07223   return testFlag( Flag_Merged );
07224 }
07225 
07226 void Cell::clearFlag( CellFlags flag )
07227 {
07228   d->flags &= ~(Q_UINT32)flag;
07229 }
07230 
07231 void Cell::setFlag( CellFlags flag )
07232 {
07233   d->flags |= (Q_UINT32)flag;
07234 }
07235 
07236 bool Cell::testFlag( CellFlags flag ) const
07237 {
07238   return ( d->flags & (Q_UINT32)flag );
07239 }
07240 
07241 
07242 void Cell::checkForNamedAreas( QString & formula ) const
07243 {
07244   int l = formula.length();
07245   int i = 0;
07246   QString word;
07247   int start = 0;
07248   while ( i < l )
07249   {
07250     if ( formula[i].isLetterOrNumber() )
07251     {
07252       word += formula[i];
07253       ++i;
07254       continue;
07255     }
07256     if ( !word.isEmpty() )
07257     {
07258       if ( sheet()->doc()->loadingInfo()->findWordInAreaList(word) )
07259       {
07260         formula = formula.replace( start, word.length(), "'" + word + "'" );
07261         l = formula.length();
07262         ++i;
07263         kdDebug() << "Formula: " << formula << ", L: " << l << ", i: " << i + 1 <<endl;
07264       }
07265     }
07266 
07267     ++i;
07268     word = "";
07269     start = i;
07270   }
07271   if ( !word.isEmpty() )
07272   {
07273     if ( sheet()->doc()->loadingInfo()->findWordInAreaList(word) )
07274     {
07275       formula = formula.replace( start, word.length(), "'" + word + "'" );
07276       l = formula.length();
07277       ++i;
07278       kdDebug() << "Formula: " << formula << ", L: " << l << ", i: " << i + 1 <<endl;
07279     }
07280   }
07281 }
KDE Home | KDE Accessibility Home | Description of Access Keys