kspread Library API Documentation

kspread_cell.cc

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