summaryrefslogtreecommitdiffstats
path: root/kspread/kspread_cell.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'kspread/kspread_cell.cpp')
-rw-r--r--kspread/kspread_cell.cpp7348
1 files changed, 7348 insertions, 0 deletions
diff --git a/kspread/kspread_cell.cpp b/kspread/kspread_cell.cpp
new file mode 100644
index 00000000..fca76bbe
--- /dev/null
+++ b/kspread/kspread_cell.cpp
@@ -0,0 +1,7348 @@
+/* This file is part of the KDE project
+
+ Copyright 2006 Stefan Nikolaus <[email protected]>
+ Copyright 2005 Raphael Langerhorst <[email protected]>
+ Copyright 2004-2005 Tomas Mecir <[email protected]>
+ Copyright 2004-2006 Inge Wallin <[email protected]>
+ Copyright 1999-2002,2004,2005 Laurent Montel <[email protected]>
+ Copyright 2002-2005 Ariya Hidayat <[email protected]>
+ Copyright 2001-2003 Philipp Mueller <[email protected]>
+ Copyright 2002-2003 Norbert Andres <[email protected]>
+ Copyright 2003 Reinhart Geiser <[email protected]>
+ Copyright 2003-2005 Meni Livne <[email protected]>
+ Copyright 2003 Peter Simonsson <[email protected]>
+ Copyright 1999-2002 David Faure <[email protected]>
+ Copyright 2000-2002 Werner Trobin <[email protected]>
+ Copyright 1999,2002 Harri Porten <[email protected]>
+ Copyright 2002 John Dailey <[email protected]>
+ Copyright 1998-2000 Torben Weis <[email protected]>
+ Copyright 2000 Bernd Wuebben <[email protected]>
+ Copyright 2000 Simon Hausmann <[email protected]
+ Copyright 1999 Stephan Kulow <[email protected]>
+ Copyright 1999 Michael Reiher <michael.reiher.gmx.de>
+ Copyright 1999 Boris Wedl <[email protected]>
+ Copyright 1998-1999 Reginald Stadlbauer <[email protected]>
+
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+*/
+
+#include <stdlib.h>
+#include <ctype.h>
+#include <float.h>
+#include <math.h>
+
+#include <tqapplication.h>
+#include <tqpopupmenu.h>
+#include <tqregexp.h>
+
+#include "kspread_canvas.h"
+#include "kspread_condition.h"
+#include "kspread_doc.h"
+#include "kspread_format.h"
+#include "kspread_global.h"
+#include "kspread_map.h"
+#include "kspread_sheet.h"
+#include "kspread_sheetprint.h"
+#include "kspread_style.h"
+#include "kspread_style_manager.h"
+#include "kspread_util.h"
+#include "ksploadinginfo.h"
+#include "kspread_genvalidationstyle.h"
+#include "kspread_locale.h"
+#include "kspread_value.h"
+#include "kspread_view.h"
+#include "kspread_value.h"
+#include "formula.h"
+#include "selection.h"
+#include "valueconverter.h"
+#include "valueformatter.h"
+#include "valueparser.h"
+
+#include <KoStyleStack.h>
+#include <KoRect.h>
+#include <KoXmlNS.h>
+#include <KoDom.h>
+#include <KoXmlWriter.h>
+#include <KoOasisStyles.h>
+
+#include <tdemessagebox.h>
+
+#include <kdebug.h>
+
+using namespace KSpread;
+
+#define BORDER_SPACE 1
+
+
+/**
+ * A pointer to the decimal separator
+ */
+
+namespace Cell_LNS
+{
+ TQChar decimal_point = '\0';
+}
+
+using namespace Cell_LNS;
+
+
+// Some variables are placed in Cell::Extra because normally they're
+// not required in simple case of cell(s). For example, most plain
+// text cells don't need to store information about spanned columns
+// and rows, as this is only the case with merged cells.
+//
+// When the cell is getting complex (e.g. merged with other cells,
+// contains rich text, has validation criteria, etc), this Cell::Extra
+// is allocated by Cell::Private and starts to be
+// available. Otherwise, it won't exist at all.
+
+class Cell::Extra
+{
+public:
+ Extra() {}
+
+ // Not empty when the cell holds a link
+ TQString link;
+
+ // Number of cells explicitly merged by the user in X and Y directions.
+ int mergedXCells;
+ int mergedYCells;
+
+ // Number of additional cells.
+ int extraXCells;
+ int extraYCells;
+
+ // If this cell overlaps other cells, then we have the cells width and
+ // height stored here. These values do not mean anything unless
+ // extraXCells and/or extraYCells are different from 0.
+ double extraWidth;
+ double extraHeight;
+
+ // A list of cells that obscure this one.
+ // If this list is not empty, then this cell is obscured by another
+ // enlarged object. This means that we have to call this object in order
+ // of painting it for example instead of painting 'this'.
+ //
+ // FIXME (comment): If the list consists of more than one obscuring
+ // element, then is there an order between them that
+ // is important?
+ TQValueList<Cell*> obscuringCells;
+
+ // If non-NULL, contains a pointer to a condition or a validity test.
+ Conditions *conditions;
+ Validity *validity;
+
+ // Store the number of line when multirow is used (default is 0)
+ int nbLines;
+
+private:
+ // Don't allow implicit copy.
+ Extra& operator=( const Extra& );
+};
+
+
+class Cell::Private
+{
+public:
+
+ Private();
+ ~Private();
+
+public:
+
+ // This cell's row and column. If either of them is 0, this is the
+ // default cell and its row/column can not be determined. Note that
+ // in the isDefault() method, only column is tested.
+ int row;
+ int column;
+
+ // Value of the cell, either typed by user or as result of formula
+ Value value;
+
+ // Holds the user's input.
+ //
+ // FIXME:
+ // Eventually, we'll want to get rid of strText and generate
+ // user's input on-the-fly. Then, for normal cells, we'll generate
+ // this string using converter()->asString
+ // (value()).
+ //
+ // Here the problem is, that strText also holds the formula -
+ // we'll need to provide some method to generate it from the
+ // parsed version, created in KSpread::Formula. Hence, we won't be
+ // able to get rid of strText until we switch to the new formula
+ // parser and until we write some method that re-generates the
+ // input formula...
+ //
+ // Alternately, we can keep using strText for formulas and
+ // generate it dynamically for static cells...
+ //
+ // /Tomas
+ //
+ TQString strText;
+
+ // This is the text we want to display. Not necessarily the same
+ // as strText, e.g. strText="1" and strOutText="1.00" Also holds
+ // value that we got from calculation, formerly known as
+ // strFormulaOut
+ TQString strOutText;
+
+ // the Formula object for the cell
+ KSpread::Formula *formula;
+
+ // Position and dimension of displayed text.
+ // FIXME (comment): Which coordinate system? pixels? mm/cm? zoom?
+ double textX;
+ double textY;
+ double textWidth;
+ double textHeight;
+
+ // result of "fm.ascent()" in makeLayout. used in offsetAlign.
+ int fmAscent;
+
+ // Pointers to neighboring cells.
+ // FIXME (comment): Which order?
+ Cell *nextCell;
+ Cell *previousCell;
+
+ bool hasExtra() const { return (cellExtra != 0); };
+ Extra *extra();
+
+ Format *format;
+ TQ_UINT32 flags;
+
+private:
+ // "Extra stuff", see explanation for Cell::Extra.
+ Extra *cellExtra;
+};
+
+
+Cell::Private::Private()
+{
+ // Some basic data.
+ row = 0;
+ column = 0;
+ value = Value::empty();
+ formula = 0;
+
+ // Formatting
+ textX = 0.0;
+ textY = 0.0;
+ textWidth = 0.0;
+ textHeight = 0.0;
+ fmAscent = 0;
+
+ nextCell = 0;
+ previousCell = 0;
+
+ // Default is to not have the "extra" stuff in a cell.
+ cellExtra = 0;
+ format = 0;
+ flags = 0;
+}
+
+
+Cell::Private::~Private()
+{
+ delete cellExtra;
+ delete formula;
+}
+
+
+Cell::Extra* Cell::Private::extra()
+{
+ if ( !cellExtra ) {
+ cellExtra = new Extra;
+ cellExtra->conditions = 0;
+ cellExtra->validity = 0;
+
+ cellExtra->mergedXCells = 0;
+ cellExtra->mergedYCells = 0;
+ cellExtra->extraXCells = 0;
+ cellExtra->extraYCells = 0;
+ cellExtra->extraWidth = 0.0;
+ cellExtra->extraHeight = 0.0;
+ cellExtra->nbLines = 0;
+// cellExtra->highlight = TQColor(0,0,0);
+ }
+
+ return cellExtra;
+}
+
+
+/*****************************************************************************
+ *
+ * Cell
+ *
+ *****************************************************************************/
+
+
+Cell::Cell( Sheet * _sheet, int _column, int _row )
+{
+ d = new Private;
+ d->row = _row;
+ d->column = _column;
+ d->format = new Format(_sheet, _sheet->doc()->styleManager()->defaultStyle());
+ d->format->setCell(this);
+ clearAllErrors();
+}
+
+
+Cell::Cell( Sheet * _sheet, Style * _style, int _column, int _row )
+{
+ d = new Private;
+ d->row = _row;
+ d->column = _column;
+ d->format = new Format( _sheet, _style );
+ d->format->setCell(this);
+
+ clearAllErrors();
+}
+
+Format* Cell::format() const
+{
+ return d->format;
+}
+
+// Return the sheet that this cell belongs to.
+Sheet * Cell::sheet() const
+{
+ return d->format->sheet();
+}
+
+// Return true if this is the default cell.
+bool Cell::isDefault() const
+{
+ return ( d->column == 0 );
+}
+
+// Return the row number of this cell.
+int Cell::row() const
+{
+ // Make sure this isn't called for the default cell. This assert
+ // can save you (could have saved me!) the hassle of some very
+ // obscure bugs.
+
+ if ( isDefault() )
+ {
+ kdWarning(36001) << "Error: Calling Cell::row() for default cell" << endl;
+ return 0;
+ }
+
+ return d->row;
+}
+
+
+// Return the column number of this cell.
+//
+int Cell::column() const
+{
+ // Make sure this isn't called for the default cell. This assert
+ // can save you (could have saved me!) the hassle of some very
+ // obscure bugs.
+ if ( isDefault() )
+ {
+ kdWarning(36001) << "Error: Calling Cell::column() for default cell" << endl;
+ return 0;
+ }
+ return d->column;
+}
+
+
+// Return the name of this cell, i.e. the string that the user would
+// use to reference it. Example: A1, BZ16
+//
+TQString Cell::name() const
+{
+ return name( d->column, d->row );
+}
+
+
+// Return the name of any cell given by (col, row).
+//
+TQString Cell::name( int col, int row )
+{
+ return columnName( col ) + TQString::number( row );
+}
+
+
+// Return the name of this cell, including the sheet name.
+// Example: sheet1!A5
+//
+TQString Cell::fullName() const
+{
+ return fullName( sheet(), d->column, d->row );
+}
+
+
+// Return the full name of any cell given a sheet and (col, row).
+//
+TQString Cell::fullName( const Sheet* s, int col, int row )
+{
+ return s->sheetName() + "!" + name( col, row );
+}
+
+
+// Return the symbolic name of the column of this cell. Examples: A, BB.
+//
+TQString Cell::columnName() const
+{
+ return columnName( d->column );
+}
+
+TDELocale* Cell::locale() const
+{
+ return d->format->sheet()->doc()->locale();
+}
+
+// Return the symbolic name of any column.
+//
+TQString Cell::columnName( uint column )
+{
+ TQString str;
+ unsigned digits = 1;
+ unsigned offset = 0;
+
+ column--;
+
+ if( column > 4058115285U ) return TQString("@@@");
+
+ for( unsigned limit = 26; column >= limit+offset; limit *= 26, digits++ )
+ offset += limit;
+
+ for( unsigned c = column - offset; digits; --digits, c/=26 )
+ str.prepend( TQChar( 'A' + (c%26) ) );
+
+ return str;
+}
+
+
+// Return true if this cell is a formula.
+//
+bool Cell::isFormula() const
+{
+ return d->strText[0] == '=';
+}
+
+
+// Return the input text of this cell. This could, for instance, be a
+// formula.
+//
+// FIXME: These two functions are inconsistently named. It should be
+// either text() and outText() or strText() and strOutText().
+//
+TQString Cell::text() const
+{
+ return d->strText;
+}
+
+
+// Return the out text, i.e. the text that is visible in the cells
+// square when shown. This could, for instance, be the calculated
+// result of a formula.
+//
+TQString Cell::strOutText() const
+{
+ return d->strOutText;
+}
+
+Formula *Cell::formula () const
+{
+ return d->formula;
+}
+
+
+// Return the value of this cell.
+//
+const Value Cell::value() const
+{
+ return d->value;
+}
+
+
+// Set the value of this cell. It also clears all errors if the value
+// itself is not an error.
+//
+// In addition to this, it calculates the outstring and sets the dirty
+// flags so that a redraw is forced.
+//
+void Cell::setValue( const Value& v )
+{
+ if (v.type() != Value::Error)
+ clearAllErrors();
+
+ //If the value has not changed then we don't need to do anything
+ //(ie. no need to relayout, update dependant cells etc.),
+ //unless this cell contains a formula, in which case its dependancies might have changed
+ //even though the value has not. For example, if this cell was previously empty (and its value is
+ //therefore empty) and a new dependency upon an empty cell has been added. The new value would still
+ //be empty, but the dependencies need to be updated (via the call to valueChanged() below).
+ if ( ( d->value == v ) && ( !isFormula() ) )
+ return;
+
+ d->value = v;
+
+ setFlag(Flag_LayoutDirty);
+ setFlag(Flag_TextFormatDirty);
+
+ // Format and set the outText.
+ setOutputText();
+
+ // Set the displayed text, if we hold an error value.
+ if (d->value.type() == Value::Error)
+ d->strOutText = d->value.errorMessage ();
+
+ // Value of the cell has changed - trigger necessary actions
+ valueChanged ();
+
+ if ( !format()->sheet()->isLoading() )
+ format()->sheet()->setRegionPaintDirty(cellRect());
+}
+
+void Cell::setCellValue (const Value &v, FormatType fmtType, const TQString &txt)
+{
+ if ( !txt.isNull() )
+ {
+ d->strText = txt;
+ if ( isFormula() )
+ makeFormula();
+ }
+ else if ( !isFormula() )
+ d->strText = sheet()->doc()->converter()->asString (v).asString();
+ if (fmtType != No_format)
+ format()->setFormatType (fmtType);
+ setValue (v);
+}
+
+// FIXME: Continue commenting and cleaning here (ingwa)
+
+
+Cell* Cell::previousCell() const
+{
+ return d->previousCell;
+}
+
+Cell* Cell::nextCell() const
+{
+ return d->nextCell;
+}
+
+void Cell::setPreviousCell( Cell* c )
+{
+ d->previousCell = c;
+}
+
+void Cell::setNextCell( Cell* c )
+{
+ d->nextCell = c;
+}
+
+Validity* Cell::getValidity( int newStruct )
+{
+ if ( (!newStruct) && (!d->hasExtra()))
+ //we don't have validity struct and we don't want one
+ return 0;
+
+ if( ( d->extra()->validity == 0 ) && ( newStruct == -1 ) )
+ d->extra()->validity = new Validity;
+ return d->extra()->validity;
+}
+
+void Cell::removeValidity()
+{
+ if (!d->hasExtra())
+ return;
+
+ delete d->extra()->validity;
+ d->extra()->validity = 0;
+}
+
+
+void Cell::copyFormat( const int column , const int row )
+{
+ const Cell * cell = format()->sheet()->cellAt( column , row );
+
+ copyFormat( cell );
+}
+
+void Cell::copyFormat( const Cell* cell )
+{
+
+ Q_ASSERT(cell);
+
+ d->value.setFormat(cell->d->value.format());
+ format()->copy(*(cell->format()));
+
+ /*format()->setAlign( cell->format()->align( _column, _row ) );
+ format()->setAlignY( cell->format()->alignY( _column, _row ) );
+ format()->setTextFont( cell->format()->textFont( _column, _row ) );
+ format()->setTextColor( cell->format()->textColor( _column, _row ) );
+ format()->setBgColor( cell->bgColor( _column, _row) );
+ setLeftBorderPen( cell->leftBorderPen( _column, _row ) );
+ setTopBorderPen( cell->topBorderPen( _column, _row ) );
+ setBottomBorderPen( cell->bottomBorderPen( _column, _row ) );
+ setRightBorderPen( cell->rightBorderPen( _column, _row ) );
+ format()->setFallDiagonalPen( cell->format()->fallDiagonalPen( _column, _row ) );
+ format()->setGoUpDiagonalPen( cell->format()->goUpDiagonalPen( _column, _row ) );
+ format()->setBackGroundBrush( cell->backGroundBrush( _column, _row) );
+ format()->setPrecision( cell->format()->precision( _column, _row ) );
+ format()->setPrefix( cell->format()->prefix( _column, _row ) );
+ format()->setPostfix( cell->format()->postfix( _column, _row ) );
+ format()->setFloatFormat( cell->format()->floatFormat( _column, _row ) );
+ format()->setFloatColor( cell->format()->floatColor( _column, _row ) );
+ format()->setMultiRow( cell->format()->multiRow( _column, _row ) );
+ format()->setVerticalText( cell->format()->verticalText( _column, _row ) );
+ format()->setDontPrintText( cell->format()->getDontprintText(_column, _row ) );
+ format()->setNotProtected( cell->format()->notProtected(_column, _row ) );
+ format()->setHideAll(cell->format()->isHideAll(_column, _row ) );
+ format()->setHideFormula(cell->format()->isHideFormula(_column, _row ) );
+ format()->setIndent( cell->format()->getIndent(_column, _row ) );
+ format()->setAngle( cell->format()->getAngle(_column, _row) );
+ format()->setFormatType( cell->format()->getFormatType(_column, _row) );
+ Format::Currency c;
+ if ( cell->format()->currencyInfo( c ) )
+ format()->setCurrency( c );*/
+
+ TQValueList<Conditional> conditionList = cell->conditionList();
+ if (d->hasExtra())
+ delete d->extra()->conditions;
+ if ( cell->d->hasExtra() && cell->d->extra()->conditions )
+ setConditionList( conditionList );
+ else
+ if (d->hasExtra())
+ d->extra()->conditions = 0;
+
+ /*format()->setComment( cell->format()->comment( _column, _row ) );*/
+}
+
+void Cell::copyAll( Cell *cell )
+{
+ Q_ASSERT( !isDefault() ); // trouble ahead...
+ copyFormat( cell );
+ copyContent( cell );
+}
+
+void Cell::copyContent( const Cell* cell )
+{
+ Q_ASSERT( !isDefault() ); // trouble ahead...
+
+ if (cell->isFormula() && cell->column() > 0 && cell->row() > 0)
+ {
+ // change all the references, e.g. from A1 to A3 if copying
+ // from e.g. B2 to B4
+ TQString d = cell->encodeFormula();
+ setCellText( cell->decodeFormula( d ) );
+ }
+ else
+ setCellText( cell->text() );
+
+}
+
+void Cell::defaultStyle()
+{
+ format()->defaultStyleFormat();
+
+ if (!d->hasExtra())
+ return;
+
+ if ( d->extra()->conditions )
+ {
+ delete d->extra()->conditions;
+ d->extra()->conditions = 0;
+ }
+
+ delete d->extra()->validity;
+ d->extra()->validity = 0L;
+}
+
+
+// Merge a number of cells, i.e. make this cell obscure a number of
+// other cells. If _x and _y == 0, then the merging is removed.
+
+void Cell::mergeCells( int _col, int _row, int _x, int _y )
+{
+ // Start by unobscuring the cells that we obscure right now
+ int extraXCells = d->hasExtra() ? d->extra()->extraXCells : 0;
+ int extraYCells = d->hasExtra() ? d->extra()->extraYCells : 0;
+ for ( int x = _col; x <= _col + extraXCells; ++x ) {
+ for ( int y = _row; y <= _row + extraYCells; ++y ) {
+ if ( x != _col || y != _row )
+ format()->sheet()->nonDefaultCell( x, y )->unobscure(this);
+ }
+ }
+
+ // If no merging, then remove all traces, and return.
+ if ( _x == 0 && _y == 0 ) {
+ clearFlag( Flag_Merged );
+ if (d->hasExtra()) {
+ d->extra()->extraXCells = 0;
+ d->extra()->extraYCells = 0;
+ d->extra()->extraWidth = 0.0;
+ d->extra()->extraHeight = 0.0;
+ d->extra()->mergedXCells = 0;
+ d->extra()->mergedYCells = 0;
+ }
+
+ // Refresh the layout
+ setFlag( Flag_LayoutDirty );
+ return;
+ }
+
+ // At this point, we know that we will merge some cells.
+ setFlag(Flag_Merged);
+ d->extra()->extraXCells = _x;
+ d->extra()->extraYCells = _y;
+ d->extra()->mergedXCells = _x;
+ d->extra()->mergedYCells = _y;
+
+ // Obscure the cells
+ for ( int x = _col; x <= _col + _x; ++x ) {
+ for ( int y = _row; y <= _row + _y; ++y ) {
+ if ( x != _col || y != _row )
+ format()->sheet()->nonDefaultCell( x, y )->obscure( this, true );
+ }
+ }
+
+ // Refresh the layout
+ setFlag( Flag_LayoutDirty );
+}
+
+void Cell::move( int col, int row )
+{
+ setLayoutDirtyFlag();
+ setCalcDirtyFlag();
+ setDisplayDirtyFlag();
+
+ //int ex = extraXCells();
+ //int ey = d->extra()->extraYCells();
+
+ if (d->hasExtra())
+ d->extra()->obscuringCells.clear();
+
+ // Unobscure the objects we obscure right now
+ int extraXCells = d->hasExtra() ? d->extra()->extraXCells : 0;
+ int extraYCells = d->hasExtra() ? d->extra()->extraYCells : 0;
+ for( int x = d->column; x <= d->column + extraXCells; ++x )
+ for( int y = d->row; y <= d->row + extraYCells; ++y )
+ if ( x != d->column || y != d->row )
+ {
+ Cell *cell = format()->sheet()->nonDefaultCell( x, y );
+ cell->unobscure(this);
+ }
+
+ d->column = col;
+ d->row = row;
+
+ if (d->hasExtra())
+ {
+ // d->extra()->extraXCells = 0;
+ // d->extra()->extraYCells = 0;
+ d->extra()->mergedXCells = 0;
+ d->extra()->mergedYCells = 0;
+ }
+
+ // Cell value has been changed (because we're another cell now).
+ valueChanged ();
+}
+
+void Cell::setLayoutDirtyFlag( bool format )
+{
+ setFlag( Flag_LayoutDirty );
+ if ( format )
+ setFlag( Flag_TextFormatDirty );
+
+ if (!d->hasExtra())
+ return;
+
+ TQValueList<Cell*>::iterator it = d->extra()->obscuringCells.begin();
+ TQValueList<Cell*>::iterator end = d->extra()->obscuringCells.end();
+ for ( ; it != end; ++it ) {
+ (*it)->setLayoutDirtyFlag( format );
+ }
+}
+
+bool Cell::needsPrinting() const
+{
+ if ( isDefault() )
+ return false;
+
+ if ( !d->strText.stripWhiteSpace().isEmpty() ) {
+ return true;
+ }
+
+ // Cell borders?
+ if ( format()->hasProperty( Format::PTopBorder )
+ || format()->hasProperty( Format::PLeftBorder )
+ || format()->hasProperty( Format::PRightBorder )
+ || format()->hasProperty( Format::PBottomBorder )
+ || format()->hasProperty( Format::PFallDiagonal )
+ || format()->hasProperty( Format::PGoUpDiagonal ) ) {
+ return true;
+ }
+
+ // Background color or brush?
+ if ( format()->hasProperty( Format::PBackgroundBrush ) ) {
+
+ const TQBrush& brush=backGroundBrush(column(),row());
+
+ //Only brushes that are visible (ie. they have a brush style and are not white)
+ //need to be drawn
+ if ( (brush.style() != TQt::NoBrush) &&
+ (brush.color() != TQt::white || brush.pixmap()) )
+ return true;
+
+ }
+
+ if ( format()->hasProperty( Format::PBackgroundColor ) ) {
+ kdDebug() << "needsPrinting: Has background colour" << endl;
+ TQColor backgroundColor=bgColor(column(),row());
+
+ //We don't need to print anything if the background is white
+ if (backgroundColor != TQt::white)
+ return true;
+ }
+
+ return false;
+}
+
+bool Cell::isEmpty() const
+{
+ return isDefault() || d->strText.isEmpty();
+}
+
+
+// Return true if this cell is obscured by some other cell.
+
+bool Cell::isObscured() const
+{
+ if (!d->hasExtra())
+ return false;
+
+ return !( d->extra()->obscuringCells.isEmpty() );
+}
+
+
+// Return true if this cell is part of a merged cell, but not the
+// master cell.
+
+bool Cell::isPartOfMerged() const
+{
+ if (!d->hasExtra())
+ return false;
+
+ TQValueList<Cell*>::const_iterator it = d->extra()->obscuringCells.begin();
+ TQValueList<Cell*>::const_iterator end = d->extra()->obscuringCells.end();
+ for ( ; it != end; ++it ) {
+ Cell *cell = *it;
+
+ if (cell->doesMergeCells()) {
+ // The cell might merge extra cells, and then overlap even
+ // beyond that so just knowing that the obscuring cell merges
+ // extra isn't enough. We have to know that this cell is one of
+ // the ones it is forcing over.
+ if (column() <= cell->column() + cell->d->extra()->mergedXCells
+ && row() <= cell->row() + cell->mergedYCells() )
+ return true;
+ }
+ }
+
+ return false;
+}
+
+
+// Return the cell that obscures this one. If no cell is obscuring,
+// then return this. This method is slightly complicated because we
+// can have several layers of obscuring.
+//
+// Update: it seems that if we do an actual merge, then the obscuring
+// cell is prepended and if just expanding, then it is appended. This
+// means that we should be able to just look at the first one.
+
+Cell *Cell::ultimateObscuringCell() const
+{
+ if (!d->hasExtra())
+ return (Cell *) this;
+
+ else if (d->extra()->obscuringCells.isEmpty())
+ return (Cell *) this;
+
+ else
+ return d->extra()->obscuringCells.first();
+
+#if 0
+ TQValueList<Cell*>::const_iterator it = d->extra()->obscuringCells.begin();
+ TQValueList<Cell*>::const_iterator end = d->extra()->obscuringCells.end();
+ for ( ; it != end; ++it ) {
+ Cell *cell = *it;
+
+ if (cell->doesMergeCells()) {
+ // The cell might merge extra cells, and then overlap even
+ // beyond that so just knowing that the obscuring cell merges
+ // extra isn't enough. We have to know that this cell is one of
+ // the ones it is forcing over.
+ if (column() <= cell->column() + cell->d->extra()->mergedXCells
+ && row() <= cell->row() + cell->mergedYCells() )
+ return true;
+ }
+ }
+
+ return false;
+#endif
+}
+
+
+TQValueList<Cell*> Cell::obscuringCells() const
+{
+ if (!d->hasExtra())
+ {
+ TQValueList<Cell*> empty;
+ return empty;
+ }
+ return d->extra()->obscuringCells;
+}
+
+void Cell::clearObscuringCells()
+{
+ if (!d->hasExtra())
+ return;
+ d->extra()->obscuringCells.clear();
+}
+
+void Cell::obscure( Cell *cell, bool isForcing )
+{
+ if (d->hasExtra())
+ {
+ d->extra()->obscuringCells.remove( cell ); // removes *all* occurrences
+ cell->clearObscuringCells();
+ }
+ if ( isForcing )
+ {
+ d->extra()->obscuringCells.prepend( cell );
+ }
+ else
+ {
+ d->extra()->obscuringCells.append( cell );
+ }
+ setFlag(Flag_LayoutDirty);
+ format()->sheet()->setRegionPaintDirty( cellRect() );
+}
+
+void Cell::unobscure( Cell * cell )
+{
+ if (d->hasExtra())
+ d->extra()->obscuringCells.remove( cell );
+ setFlag( Flag_LayoutDirty );
+ format()->sheet()->setRegionPaintDirty( cellRect() );
+}
+
+TQString Cell::encodeFormula( bool _era, int _col, int _row ) const
+{
+ if ( _col == -1 )
+ _col = d->column;
+ if ( _row == -1 )
+ _row = d->row;
+
+ TQString erg = "";
+
+ if(d->strText.isEmpty())
+ return d->strText;
+
+ bool fix1 = false;
+ bool fix2 = false;
+ bool onNumber = false;
+ unsigned int pos = 0;
+ const unsigned int length = d->strText.length();
+
+ // All this can surely be made 10 times faster, but I just "ported" it to TQString
+ // without any attempt to optimize things -- this is really brittle (Werner)
+ while ( pos < length )
+ {
+ if ( d->strText[pos] == '"' )
+ {
+ erg += d->strText[pos++];
+ while ( pos < length && d->strText[pos] != '"' ) // till the end of the world^H^H^H "string"
+ {
+ erg += d->strText[pos++];
+ // Allow escaped double quotes (\")
+ if ( pos < length && d->strText[pos] == '\\' && d->strText[pos+1] == '"' )
+ {
+ erg += d->strText[pos++];
+ erg += d->strText[pos++];
+ }
+ }
+ if ( pos < length ) // also copy the trailing double quote
+ erg += d->strText[pos++];
+
+ onNumber = false;
+ }
+ else if ( d->strText[pos].isDigit() )
+ {
+ erg += d->strText[pos++];
+ fix1 = fix2 = false;
+ onNumber = true;
+ }
+ else if ( d->strText[pos] != '$' && !d->strText[pos].isLetter() )
+ {
+ erg += d->strText[pos++];
+ fix1 = fix2 = false;
+ onNumber = false;
+ }
+ else
+ {
+ TQString tmp = "";
+ if ( d->strText[pos] == '$' )
+ {
+ tmp = "$";
+ pos++;
+ fix1 = true;
+ }
+ if ( d->strText[pos].isLetter() )
+ {
+ TQString buffer;
+ unsigned int pos2 = 0;
+ while ( pos < length && d->strText[pos].isLetter() )
+ {
+ tmp += d->strText[pos];
+ buffer[pos2++] = d->strText[pos++];
+ }
+ if ( d->strText[pos] == '$' )
+ {
+ tmp += "$";
+ pos++;
+ fix2 = true;
+ }
+ if ( d->strText[pos].isDigit() )
+ {
+ const unsigned int oldPos = pos;
+ while ( pos < length && d->strText[pos].isDigit() ) ++pos;
+ int row = 0;
+ if ( pos != oldPos )
+ row = d->strText.mid(oldPos, pos-oldPos).toInt();
+ // Is it a sheet name || is it a function name like DEC2HEX
+ /* or if we're parsing a number, this could just be the
+ exponential part of it (1.23E4) */
+ if ( ( d->strText[pos] == '!' ) ||
+ d->strText[pos].isLetter() ||
+ onNumber )
+ {
+ erg += tmp;
+ fix1 = fix2 = false;
+ pos = oldPos;
+ }
+ else // It must be a cell identifier
+ {
+ //now calculate the row as integer value
+ int col = 0;
+ col = util_decodeColumnLabelText( buffer );
+ if ( fix1 )
+ erg += TQString( "$%1" ).arg( col );
+ else
+ if (_era)
+ erg += TQChar(0xA7) + TQString( "%1" ).arg( col );
+ else
+ erg += TQString( "#%1" ).arg( col - _col );
+
+ if ( fix2 )
+ erg += TQString( "$%1#").arg( row );
+ else
+ if (_era)
+ erg += TQChar(0xA7) + TQString( "%1#" ).arg( row );
+ else
+ erg += TQString( "#%1#" ).arg( row - _row );
+ }
+ }
+ else
+ {
+ erg += tmp;
+ fix1 = fix2 = false;
+ }
+ }
+ else
+ {
+ erg += tmp;
+ fix1 = false;
+ }
+ onNumber = false;
+ }
+ }
+
+ return erg;
+}
+
+TQString Cell::decodeFormula( const TQString &_text, int _col, int _row) const
+{
+ if ( _col == -1 )
+ _col = d->column;
+ if ( _row == -1 )
+ _row = d->row;
+
+ TQString erg = "";
+ unsigned int pos = 0;
+ const unsigned int length = _text.length();
+
+ if ( _text.isEmpty() )
+ return TQString();
+
+ while ( pos < length )
+ {
+ if ( _text[pos] == '"' )
+ {
+ erg += _text[pos++];
+ while ( pos < length && _text[pos] != '"' )
+ {
+ erg += _text[pos++];
+ // Allow escaped double quotes (\")
+ if ( pos < length && _text[pos] == '\\' && _text[pos+1] == '"' )
+ {
+ erg += _text[pos++];
+ erg += _text[pos++];
+ }
+ }
+ if ( pos < length )
+ erg += _text[pos++];
+ }
+ else if ( _text[pos] == '#' || _text[pos] == '$' || _text[pos] == TQChar(0xA7))
+ {
+ bool abs1 = false;
+ bool abs2 = false;
+ bool era1 = false; // if 1st is relative but encoded absolutely
+ bool era2 = false;
+
+ TQChar _t = _text[pos++];
+ if ( _t == '$' )
+ abs1 = true;
+ else if ( _t == TQChar(0xA7) )
+ era1 = true;
+
+ int col = 0;
+ unsigned int oldPos = pos;
+ while ( pos < length && ( _text[pos].isDigit() || _text[pos] == '-' ) ) ++pos;
+ if ( pos != oldPos )
+ col = _text.mid(oldPos, pos-oldPos).toInt();
+ if ( !abs1 && !era1 )
+ col += _col;
+ // Skip '#' or '$'
+
+ _t = _text[pos++];
+ if ( _t == '$' )
+ abs2 = true;
+ else if ( _t == TQChar(0xA7) )
+ era2 = true;
+
+ int row = 0;
+ oldPos = pos;
+ while ( pos < length && ( _text[pos].isDigit() || _text[pos] == '-' ) ) ++pos;
+ if ( pos != oldPos )
+ row = _text.mid(oldPos, pos-oldPos).toInt();
+ if ( !abs2 && !era2)
+ row += _row;
+ // Skip '#' or '$'
+ ++pos;
+ if ( row < 1 || col < 1 || row > KS_rowMax || col > KS_colMax )
+ {
+ kdDebug(36001) << "Cell::decodeFormula: row or column out of range (col: " << col << " | row: " << row << ")" << endl;
+ erg = "=\"#### " + i18n("REFERENCE TO COLUMN OR ROW IS OUT OF RANGE") + "\"";
+ return erg;
+ }
+ if ( abs1 )
+ erg += "$";
+ erg += Cell::columnName(col); //Get column text
+
+ if ( abs2 )
+ erg += "$";
+ erg += TQString::number( row );
+ }
+ else
+ erg += _text[pos++];
+ }
+
+ return erg;
+}
+
+
+void Cell::freeAllObscuredCells()
+{
+ //
+ // Free all obscured cells.
+ //
+
+ if (!d->hasExtra())
+ return;
+
+ for ( int x = d->column + d->extra()->mergedXCells;
+ x <= d->column + d->extra()->extraXCells; ++x ) {
+ for ( int y = d->row + d->extra()->mergedYCells;
+ y <= d->row + d->extra()->extraYCells; ++y ) {
+ if ( x != d->column || y != d->row ) {
+ Cell *cell = format()->sheet()->cellAt( x, y );
+ cell->unobscure(this);
+ }
+ }
+ }
+
+ d->extra()->extraXCells = d->extra()->mergedXCells;
+ d->extra()->extraYCells = d->extra()->mergedYCells;
+
+}
+
+
+// ----------------------------------------------------------------
+// Layout
+
+
+// Recalculate the entire layout. This includes the following members:
+//
+// d->textX, d->textY
+// d->textWidth, d->textHeight
+// d->fmAscent
+// d->extra()->extraXCells, d->extra()->extraYCells
+// d->extra()->extraWidth, d->extra()->extraHeight
+// d->extra()->nbLines (if multirow)
+//
+// and, of course,
+//
+// d->strOutText
+//
+
+void Cell::makeLayout( TQPainter &_painter, int _col, int _row )
+{
+ // Are _col and _row really needed ?
+ //
+ // Yes they are: they are useful if this is the default layout, in
+ // which case d->row and d->column are 0 and 0, but _col and _row
+ // are the real coordinates of the cell.
+
+ // There is no need to remake the layout if it hasn't changed.
+ if ( !testFlag( Flag_LayoutDirty ) )
+ return;
+
+ // Some initializations.
+ if (d->hasExtra())
+ d->extra()->nbLines = 0;
+ clearFlag( Flag_CellTooShortX );
+ clearFlag( Flag_CellTooShortY );
+
+ // Initiate the cells that this one is obscuring to the ones that
+ // are actually merged.
+ freeAllObscuredCells();
+ if (d->hasExtra())
+ mergeCells( d->column, d->row,
+ d->extra()->mergedXCells, d->extra()->mergedYCells );
+
+ // If the column for this cell is hidden or the row is too low,
+ // there is no use in remaking the layout.
+ ColumnFormat *cl1 = format()->sheet()->columnFormat( _col );
+ RowFormat *rl1 = format()->sheet()->rowFormat( _row );
+ if ( cl1->isHide()
+ || ( rl1->dblHeight() <= format()->sheet()->doc()->unzoomItY( 2 ) ) ) {
+ clearFlag( Flag_LayoutDirty );
+ return;
+ }
+
+ // Recalculate the output text, d->strOutText.
+ setOutputText();
+
+ // Empty text? Reset the outstring and, if this is the default
+ // cell, return.
+ if ( d->strOutText.isEmpty() ) {
+ d->strOutText = TQString();
+
+ if ( isDefault() ) {
+ clearFlag( Flag_LayoutDirty );
+ return;
+ }
+ }
+
+ // Up to here, we have just cared about the contents, not the
+ // painting of it. Now it is time to see if the contents fits into
+ // the cell and, if not, maybe rearrange the outtext a bit.
+ //
+ // First, Determine the correct font with zoom taken into account,
+ // and apply it to _painter. Then calculate text dimensions, i.e.
+ // d->textWidth and d->textHeight.
+ applyZoomedFont( _painter, _col, _row );
+ textSize( _painter );
+
+ //
+ // Calculate the size of the cell
+ //
+ RowFormat *rl = format()->sheet()->rowFormat( d->row );
+ ColumnFormat *cl = format()->sheet()->columnFormat( d->column );
+
+ double width = cl->dblWidth();
+ double height = rl->dblHeight();
+
+ // Calculate extraWidth and extraHeight if we have a merged cell.
+ if ( testFlag( Flag_Merged ) ) {
+ int extraXCells = d->hasExtra() ? d->extra()->extraXCells : 0;
+ int extraYCells = d->hasExtra() ? d->extra()->extraYCells : 0;
+
+ // FIXME: Introduce double extraWidth/Height here and use them
+ // instead (see FIXME about this in paintCell()).
+
+ for ( int x = _col + 1; x <= _col + extraXCells; x++ )
+ width += format()->sheet()->columnFormat( x )->dblWidth();
+
+ for ( int y = _row + 1; y <= _row + extraYCells; y++ )
+ height += format()->sheet()->rowFormat( y )->dblHeight();
+ }
+
+ // Cache the newly calculated extraWidth and extraHeight if we have
+ // already allocated a struct for it. Otherwise it will be zero, so
+ // don't bother.
+ if (d->hasExtra()) {
+ d->extra()->extraWidth = width;
+ d->extra()->extraHeight = height;
+ }
+
+ TQFontMetrics fm = _painter.fontMetrics();
+ d->fmAscent = fm.ascent();
+
+ // Check if we need to break the line into multiple lines and are
+ // allowed to do so. If so, set `lines' to the number of lines that
+ // are needed to fit into the total width of the combined cell.
+ //
+ // Also recalculate d->textHeight, d->textWidth, d->extra->nbLines
+ // and d->strOutText.
+ //
+ int lines = 1;
+ if ( d->textWidth > (width - 2 * BORDER_SPACE
+ - format()->leftBorderWidth( _col, _row )
+ - format()->rightBorderWidth( _col, _row ) )
+ && format()->multiRow( _col, _row ) )
+ {
+ // Copy of d->strOutText but without the newlines.
+// TQString o = d->strOutText.replace( TQChar('\n'), " " );
+
+ // don't remove the existing LF, these are intended line wraps (whishlist #9881)
+ TQString o = d->strOutText;
+
+ // Break the line at appropriate places, i.e. spaces, if
+ // necessary. This means to change the spaces where breaks occur
+ // into newlines.
+ if ( o.find(' ') != -1 )
+ {
+ d->strOutText = "";
+
+ // Make sure that we have a space at the end.
+ o += ' ';
+
+ int start = 0; // Start of the line we are handling now
+ int breakpos = 0; // The next candidate pos to break the string
+ int pos1 = 0;
+ int availableWidth = (int) ( width - 2 * BORDER_SPACE
+ - format()->leftBorderWidth( _col, _row )
+ - format()->rightBorderWidth( _col, _row ) );
+
+ do {
+
+ breakpos = o.find( ' ', breakpos );
+ int linefeed = o.find( '\n', pos1 );
+
+// kdDebug() << "start: " << start << "; breakpos: " << breakpos << "; pos1: " << pos1 << "; linefeed: " << linefeed << endl;
+
+ //don't miss LF as a position to calculate current lineWidth
+ int work_breakpos = breakpos;
+ if (pos1 < linefeed && linefeed < breakpos)
+ work_breakpos = linefeed;
+
+ double lineWidth = format()->sheet()->doc()
+ ->unzoomItX( fm.width( d->strOutText.mid( start, (pos1 - start) )
+ + o.mid( pos1, work_breakpos - pos1 ) ) );
+
+ //linefeed could be -1 when no linefeed is found!
+ if (breakpos > linefeed && linefeed > 0)
+ {
+// kdDebug() << "applying linefeed to start;" << endl;
+ start = linefeed;
+ lines++;
+ }
+
+ if ( lineWidth <= availableWidth ) {
+ // We have room for the rest of the line. End it here.
+ d->strOutText += o.mid( pos1, breakpos - pos1 );
+ pos1 = breakpos;
+ }
+ else {
+ // Still not enough room. Try to split further.
+ if ( o.at( pos1 ) == ' ' )
+ pos1++;
+
+ if ( pos1 != 0 && breakpos != -1 ) {
+ d->strOutText += "\n" + o.mid( pos1, breakpos - pos1 );
+ lines++;
+ }
+ else
+ d->strOutText += o.mid( pos1, breakpos - pos1 );
+
+ start = pos1;
+ pos1 = breakpos;
+ }
+
+ breakpos++;
+ } while( o.find( ' ', breakpos ) != -1 );
+ }
+ else
+ {
+ lines = o.contains('\n');
+ }
+
+ d->textHeight *= lines;
+ if (lines > 1)
+ d->extra()->nbLines = lines;
+
+ d->textX = 0.0;
+
+ // Calculate the maximum width, taking into account linebreaks,
+ // and put it in d->textWidth.
+ TQString t;
+ int i;
+ int pos = 0;
+ d->textWidth = 0.0;
+ do {
+ i = d->strOutText.find( "\n", pos );
+
+ if ( i == -1 )
+ t = d->strOutText.mid( pos, d->strOutText.length() - pos );
+ else {
+ t = d->strOutText.mid( pos, i - pos );
+ pos = i + 1;
+ }
+
+ double tw = format()->sheet()->doc()->unzoomItX( fm.width( t ) );
+ if ( tw > d->textWidth )
+ d->textWidth = tw;
+ } while ( i != -1 );
+ }
+
+ // Calculate d->textX and d->textY
+ offsetAlign( _col, _row );
+
+ int a = effAlignX();
+
+ // Get indentation. This is only used for left aligned text.
+ double indent = 0.0;
+ if ( a == Format::Left && !isEmpty() )
+ indent = format()->getIndent( _col, _row );
+
+ // Set Flag_CellTooShortX if the text is vertical or angled, and too
+ // high for the cell.
+ if ( format()->verticalText( _col, _row ) || format()->getAngle( _col, _row ) != 0 ) {
+ //RowFormat *rl = format()->sheet()->rowFormat( _row );
+
+ if ( d->textHeight >= height/*rl->dblHeight()*/ )
+ setFlag( Flag_CellTooShortX );
+ }
+
+ // Do we have to occupy additional cells to the right? This is only
+ // done for cells that have no merged cells in the Y direction.
+ //
+ // FIXME: Check if all cells along the merged edge to the right are
+ // empty and use the extra space? No, probably not.
+ //
+ if ( d->textWidth + indent > ( width - 2 * BORDER_SPACE
+ - format()->leftBorderWidth( _col, _row )
+ - format()->rightBorderWidth( _col, _row ) )
+ && ( !d->hasExtra() || d->extra()->mergedYCells == 0 ) )
+ {
+ int c = d->column;
+
+ // Find free cells to the right of this one.
+ int end = 0;
+ while ( !end ) {
+ ColumnFormat *cl2 = format()->sheet()->columnFormat( c + 1 );
+ Cell *cell = format()->sheet()->visibleCellAt( c + 1, d->row );
+
+ if ( cell->isEmpty() ) {
+ width += cl2->dblWidth() - 1;
+ c++;
+
+ // Enough space?
+ if ( d->textWidth + indent <= ( width - 2 * BORDER_SPACE
+ - format()->leftBorderWidth( _col, _row )
+ - format()->rightBorderWidth( _col, _row ) ) )
+ end = 1;
+ }
+ else
+ // Not enough space, but the next cell is not empty
+ end = -1;
+ }
+
+ // Try to use additional space from the neighboring cells that
+ // were calculated in the last step. This is the place that we
+ // set d->extra()->extraXCells and d->extra()->extraWidth.
+ //
+ // Currently this is only done for left aligned cells. We have to
+ // check to make sure we haven't already force-merged enough cells
+ //
+ // FIXME: Why not right/center aligned text?
+ //
+ // FIXME: Shouldn't we check to see if end == -1 here before
+ // setting Flag_CellTooShortX?
+ //
+ if ( format()->align( _col, _row ) == Format::Left
+ || ( format()->align( _col, _row ) == Format::Undefined
+ && !value().isNumber() ) )
+ {
+ if ( c - d->column > d->extra()->mergedXCells ) {
+ d->extra()->extraXCells = c - d->column;
+ d->extra()->extraWidth = width;
+ for ( int i = d->column + 1; i <= c; ++i ) {
+ Cell *cell = format()->sheet()->nonDefaultCell( i, d->row );
+ cell->obscure( this );
+ }
+
+ // Not enough space
+ if ( end == -1 )
+ setFlag( Flag_CellTooShortX );
+ }
+ else
+ setFlag( Flag_CellTooShortX );
+ }
+ else
+ setFlag( Flag_CellTooShortX );
+ }
+
+ // Do we have to occupy additional cells at the bottom ?
+ //
+ // FIXME: Setting to make the current cell grow.
+ //
+ if ( format()->multiRow( _col, _row )
+ && d->textHeight > ( height - 2 * BORDER_SPACE
+ - format()->topBorderWidth( _col, _row )
+ - format()->bottomBorderWidth( _col, _row ) ) )
+ {
+ int r = d->row;
+ int end = 0;
+
+ // Find free cells bottom to this one
+ while ( !end ) {
+ RowFormat *rl2 = format()->sheet()->rowFormat( r + 1 );
+ Cell *cell = format()->sheet()->visibleCellAt( d->column, r + 1 );
+
+ if ( cell->isEmpty() ) {
+ height += rl2->dblHeight() - 1.0;
+ r++;
+
+ // Enough space ?
+ if ( d->textHeight <= ( height - 2 * BORDER_SPACE
+ - format()->topBorderWidth( _col, _row )
+ - format()->bottomBorderWidth( _col, _row ) ) )
+ end = 1;
+ }
+ else
+ // Not enough space, but the next cell is not empty.
+ end = -1;
+ }
+
+ // Check to make sure we haven't already force-merged enough cells.
+ if ( r - d->row > d->extra()->mergedYCells )
+ {
+ d->extra()->extraYCells = r - d->row;
+ d->extra()->extraHeight = height;
+
+ for ( int i = d->row + 1; i <= r; ++i )
+ {
+ Cell *cell = format()->sheet()->nonDefaultCell( d->column, i );
+ cell->obscure( this );
+ }
+
+ // Not enough space?
+ if ( end == -1 )
+ setFlag( Flag_CellTooShortY );
+ }
+ else
+ setFlag( Flag_CellTooShortY );
+ }
+
+ clearFlag( Flag_LayoutDirty );
+
+ return;
+}
+
+
+void Cell::valueChanged ()
+{
+ update();
+
+ format()->sheet()->valueChanged (this);
+}
+
+
+// Recalculate d->strOutText.
+//
+
+void Cell::setOutputText()
+{
+ if ( isDefault() ) {
+ d->strOutText = TQString();
+
+ if ( d->hasExtra() && d->extra()->conditions )
+ d->extra()->conditions->checkMatches();
+
+ return;
+ }
+
+ // If nothing has changed, we don't need to remake the text layout.
+ if ( !testFlag(Flag_TextFormatDirty) )
+ return;
+
+ // We don't want to remake the layout unnecessarily.
+ clearFlag( Flag_TextFormatDirty );
+
+ // Display a formula if warranted. If not, display the value instead;
+ // this is the most common case.
+ if ( (!hasError()) && isFormula() && format()->sheet()->getShowFormula()
+ && !( format()->sheet()->isProtected() && format()->isHideFormula( d->column, d->row ) )
+ || isEmpty() )
+ d->strOutText = d->strText;
+ else {
+ d->strOutText = sheet()->doc()->formatter()->formatText (this,
+ formatType());
+ }
+
+ // Check conditions if needed.
+ if ( d->hasExtra() && d->extra()->conditions )
+ d->extra()->conditions->checkMatches();
+}
+
+
+// Recalculate d->textX and d->textY.
+//
+// Used in makeLayout() and calculateTextParameters().
+//
+
+void Cell::offsetAlign( int _col, int _row )
+{
+ int a;
+ Format::AlignY ay;
+ int tmpAngle;
+ bool tmpVerticalText;
+ bool tmpMultiRow;
+
+ if ( d->hasExtra()
+ && d->extra()->conditions
+ && d->extra()->conditions->matchedStyle() )
+ {
+ Style *style = d->extra()->conditions->matchedStyle();
+
+ if ( style->hasFeature( Style::SAlignX, true ) )
+ a = style->alignX();
+ else
+ a = format()->align( _col, _row );
+
+ if ( style->hasFeature( Style::SVerticalText, true ) )
+ tmpVerticalText = style->hasProperty( Style::PVerticalText );
+ else
+ tmpVerticalText = format()->verticalText( _col, _row );
+
+ if ( style->hasFeature( Style::SMultiRow, true ) )
+ tmpMultiRow = style->hasProperty( Style::PMultiRow );
+ else
+ tmpMultiRow = format()->multiRow( _col, _row );
+
+ if ( style->hasFeature( Style::SAlignY, true ) )
+ ay = style->alignY();
+ else
+ ay = format()->alignY( _col, _row );
+
+ if ( style->hasFeature( Style::SAngle, true ) )
+ tmpAngle = style->rotateAngle();
+ else
+ tmpAngle = format()->getAngle( _col, _row );
+ }
+ else {
+ a = format()->align( _col, _row );
+ ay = format()->alignY( _col, _row );
+ tmpAngle = format()->getAngle( _col, _row );
+ tmpVerticalText = format()->verticalText( _col, _row );
+ tmpMultiRow = format()->multiRow( _col, _row );
+ }
+
+ RowFormat *rl = format()->sheet()->rowFormat( _row );
+ ColumnFormat *cl = format()->sheet()->columnFormat( _col );
+
+ double w = cl->dblWidth();
+ double h = rl->dblHeight();
+
+ if ( d->hasExtra() ) {
+ if ( d->extra()->extraXCells ) w = d->extra()->extraWidth;
+ if ( d->extra()->extraYCells ) h = d->extra()->extraHeight;
+ }
+
+ const double effTop = BORDER_SPACE + 0.5 * effTopBorderPen( _col, _row ).width();
+ const double effBottom = h - BORDER_SPACE - 0.5 * effBottomBorderPen( _col, _row ).width();
+
+ // Calculate d->textY based on the vertical alignment and a few
+ // other inputs.
+ switch( ay )
+ {
+ case Format::Top:
+ {
+ if ( tmpAngle == 0 )
+ {
+ d->textY = effTop + (double) d->fmAscent / format()->sheet()->doc()->zoomedResolutionY();
+ }
+ else if ( tmpAngle < 0 )
+ {
+ d->textY = effTop;
+ }
+ else
+ {
+ d->textY = effTop
+ + (double) d->fmAscent * cos( tmpAngle * M_PI / 180 ) / format()->sheet()->doc()->zoomedResolutionY();
+ }
+ break;
+ }
+ case Format::Bottom:
+ {
+ if ( !tmpVerticalText && !tmpMultiRow && !tmpAngle )
+ {
+ d->textY = effBottom;
+ }
+ else if ( tmpAngle != 0 )
+ {
+ // Is enough place available?
+ if ( effBottom - effTop - d->textHeight > 0 )
+ {
+ if ( tmpAngle < 0 )
+ {
+ d->textY = effBottom - d->textHeight;
+ }
+ else
+ {
+ d->textY = effBottom - d->textHeight
+ + ( (double) d->fmAscent * cos( tmpAngle * M_PI / 180 ) / format()->sheet()->doc()->zoomedResolutionY() );
+ }
+ }
+ else
+ {
+ if ( tmpAngle < 0 )
+ {
+ d->textY = effTop;
+ }
+ else
+ {
+ d->textY = effTop
+ + ( (double) d->fmAscent * cos( tmpAngle * M_PI / 180 )
+ / format()->sheet()->doc()->zoomedResolutionY() );
+ }
+ }
+ }
+ else if ( tmpMultiRow && !tmpVerticalText )
+ {
+ // Is enough place available?
+ if ( effBottom - effTop - d->textHeight > 0 )
+ {
+ d->textY = effBottom - d->textHeight
+ + (double)d->fmAscent / format()->sheet()->doc()->zoomedResolutionY();
+ }
+ else
+ {
+ d->textY = effTop
+ + (double) d->fmAscent / format()->sheet()->doc()->zoomedResolutionY();
+ }
+ }
+ else
+ {
+ // Is enough place available?
+ if ( effBottom - effTop - d->textHeight > 0 )
+ {
+ d->textY = effBottom - d->textHeight
+ + (double)d->fmAscent / format()->sheet()->doc()->zoomedResolutionY();
+ }
+ else
+ {
+ d->textY = effTop
+ + (double) d->fmAscent / format()->sheet()->doc()->zoomedResolutionY();
+ }
+ }
+ break;
+ }
+ case Format::Middle:
+ case Format::UndefinedY:
+ {
+ if ( !tmpVerticalText && !tmpMultiRow && !tmpAngle )
+ {
+ d->textY = ( h - d->textHeight ) / 2
+ + (double) d->fmAscent / format()->sheet()->doc()->zoomedResolutionY();
+ }
+ else if ( tmpAngle != 0 )
+ {
+ // Is enough place available?
+ if ( effBottom - effTop - d->textHeight > 0 )
+ {
+ if ( tmpAngle < 0 )
+ {
+ d->textY = ( h - d->textHeight ) / 2;
+ }
+ else
+ {
+ d->textY = ( h - d->textHeight ) / 2
+ + (double) d->fmAscent * cos( tmpAngle * M_PI / 180 ) / format()->sheet()->doc()->zoomedResolutionY();
+ }
+ }
+ else
+ {
+ if ( tmpAngle < 0 )
+ {
+ d->textY = effTop;
+ }
+ else
+ {
+ d->textY = effTop
+ + ( (double)d->fmAscent * cos( tmpAngle * M_PI / 180 ) / format()->sheet()->doc()->zoomedResolutionY() );
+ }
+ }
+ }
+ else if ( tmpMultiRow && !tmpVerticalText )
+ {
+ // Is enough place available?
+ if ( effBottom - effTop - d->textHeight > 0 )
+ {
+ d->textY = ( h - d->textHeight ) / 2
+ + (double) d->fmAscent / format()->sheet()->doc()->zoomedResolutionY();
+ }
+ else
+ {
+ d->textY = effTop
+ + (double) d->fmAscent / format()->sheet()->doc()->zoomedResolutionY();
+ }
+ }
+ else
+ {
+ // Is enough place available?
+ if ( effBottom - effTop - d->textHeight > 0 )
+ {
+ d->textY = ( h - d->textHeight ) / 2
+ + (double) d->fmAscent / format()->sheet()->doc()->zoomedResolutionY();
+ }
+ else
+ d->textY = effTop
+ + (double)d->fmAscent / format()->sheet()->doc()->zoomedResolutionY();
+ }
+ break;
+ }
+ }
+
+ a = effAlignX();
+ if ( format()->sheet()->getShowFormula() &&
+ !( format()->sheet()->isProtected() && format()->isHideFormula( _col, _row ) ) )
+ {
+ a = Format::Left;
+ }
+
+ // Calculate d->textX based on alignment and textwidth.
+ switch ( a ) {
+ case Format::Left:
+ d->textX = 0.5 * effLeftBorderPen( _col, _row ).width() + BORDER_SPACE;
+ break;
+ case Format::Right:
+ d->textX = w - BORDER_SPACE - d->textWidth
+ - 0.5 * effRightBorderPen( _col, _row ).width();
+ break;
+ case Format::Center:
+ d->textX = 0.5 * ( w - BORDER_SPACE - d->textWidth -
+ 0.5 * effRightBorderPen( _col, _row ).width() );
+ break;
+ }
+}
+
+
+// Recalculate the current text dimensions, i.e. d->textWidth and
+// d->textHeight.
+//
+// Used in makeLayout() and calculateTextParameters().
+//
+void Cell::textSize( TQPainter &_paint )
+{
+ TQFontMetrics fm = _paint.fontMetrics();
+ // Horizontal text ?
+
+ int tmpAngle;
+ int _row = row();
+ int _col = column();
+ bool tmpVerticalText;
+ bool fontUnderlined;
+ Format::AlignY ay;
+
+ // Set tmpAngle, tmpeVerticalText, ay and fontUnderlined according
+ // to if there is a matching condition or not.
+ if ( d->hasExtra()
+ && d->extra()->conditions
+ && d->extra()->conditions->matchedStyle() )
+ {
+ Style *style = d->extra()->conditions->matchedStyle();
+
+ if ( style->hasFeature( Style::SAngle, true ) )
+ tmpAngle = style->rotateAngle();
+ else
+ tmpAngle = format()->getAngle( _col, _row );
+
+ if ( style->hasFeature( Style::SVerticalText, true ) )
+ tmpVerticalText = style->hasProperty( Style::PVerticalText );
+ else
+ tmpVerticalText = format()->verticalText( _col, _row );
+
+ if ( style->hasFeature( Style::SAlignY, true ) )
+ ay = style->alignY();
+ else
+ ay = format()->alignY( _col, _row );
+
+ if ( style->hasFeature( Style::SFontFlag, true ) )
+ fontUnderlined = ( style->fontFlags() & (uint) Style::FUnderline );
+ else
+ fontUnderlined = format()->textFontUnderline( _col, _row );
+ }
+ else {
+ // The cell has no condition with a maxed style.
+ tmpAngle = format()->getAngle( _col, _row );
+ tmpVerticalText = format()->verticalText( _col, _row );
+ ay = format()->alignY( _col, _row );
+ fontUnderlined = format()->textFontUnderline( _col, _row );
+ }
+
+ // Set d->textWidth and d->textHeight to correct values according to
+ // if the text is horizontal, vertical or rotated.
+ if ( !tmpVerticalText && !tmpAngle ) {
+ // Horizontal text.
+
+ d->textWidth = format()->sheet()->doc()->unzoomItX( fm.width( d->strOutText ) );
+ int offsetFont = 0;
+ if ( ( ay == Format::Bottom ) && fontUnderlined ) {
+ offsetFont = fm.underlinePos() + 1;
+ }
+
+ d->textHeight = format()->sheet()->doc()->unzoomItY( fm.ascent() + fm.descent()
+ + offsetFont );
+ }
+ else if ( tmpAngle!= 0 ) {
+ // Rotated text.
+
+ d->textHeight = format()->sheet()->doc()
+ ->unzoomItY( int( cos( tmpAngle * M_PI / 180 )
+ * ( fm.ascent() + fm.descent() )
+ + abs( int( ( fm.width( d->strOutText )
+ * sin( tmpAngle * M_PI / 180 ) ) ) ) ) );
+
+ d->textWidth = format()->sheet()->doc()
+ ->unzoomItX( int( abs( int( ( sin( tmpAngle * M_PI / 180 )
+ * ( fm.ascent() + fm.descent() ) ) ) )
+ + fm.width( d->strOutText )
+ * cos ( tmpAngle * M_PI / 180 ) ) );
+ }
+ else {
+ // Vertical text.
+ int width = 0;
+ for ( unsigned int i = 0; i < d->strOutText.length(); i++ )
+ width = TQMAX( width, fm.width( d->strOutText.at( i ) ) );
+
+ d->textWidth = format()->sheet()->doc()->unzoomItX( width );
+ d->textHeight = format()->sheet()->doc()->unzoomItY( ( fm.ascent() + fm.descent() )
+ * d->strOutText.length() );
+ }
+}
+
+
+// Get the effective font to use after the zooming and apply it to `painter'.
+//
+// Used in makeLayout() and calculateTextParameters().
+//
+
+void Cell::applyZoomedFont( TQPainter &painter, int _col, int _row )
+{
+ TQFont tmpFont( format()->textFont( _col, _row ) );
+
+ // If there is a matching condition on this cell then set the
+ // according style parameters.
+ if ( d->hasExtra()
+ && d->extra()->conditions
+ && d->extra()->conditions->matchedStyle() ) {
+
+ Style * s = d->extra()->conditions->matchedStyle();
+
+ // Other size?
+ if ( s->hasFeature( Style::SFontSize, true ) )
+ tmpFont.setPointSizeFloat( s->fontSize() );
+
+ // Other attributes?
+ if ( s->hasFeature( Style::SFontFlag, true ) ) {
+ uint flags = s->fontFlags();
+
+ tmpFont.setBold( flags & (uint) Style::FBold );
+ tmpFont.setUnderline( flags & (uint) Style::FUnderline );
+ tmpFont.setItalic( flags & (uint) Style::FItalic );
+ tmpFont.setStrikeOut( flags & (uint) Style::FStrike );
+ }
+
+ // Other family?
+ if ( s->hasFeature( Style::SFontFamily, true ) )
+ tmpFont.setFamily( s->fontFamily() );
+ }
+#if 0
+ else
+ /*
+ * could somebody please explaint why we check for isProtected or isHideFormula here
+ */
+ if ( d->extra()->conditions
+ && d->extra()->conditions->currentCondition( condition )
+ && !(format()->sheet()->getShowFormula()
+ && !( format()->sheet()->isProtected()
+ && format()->isHideFormula( d->column, d->row ) ) ) )
+ {
+ if ( condition.fontcond )
+ tmpFont = *(condition.fontcond);
+ else
+ tmpFont = condition.style->font();
+ }
+#endif
+
+ // Scale the font size according to the current zoom.
+ tmpFont.setPointSizeFloat( 0.01 * format()->sheet()->doc()->zoom()
+ * tmpFont.pointSizeFloat() );
+
+ painter.setFont( tmpFont );
+}
+
+
+//used in Sheet::adjustColumnHelper and Sheet::adjustRow
+void Cell::calculateTextParameters( TQPainter &_painter,
+ int _col, int _row )
+{
+ // Apply the correct font to _painter.
+ applyZoomedFont( _painter, _col, _row );
+
+ // Recalculate d->textWidth and d->textHeight
+ textSize( _painter );
+
+ // Recalculate d->textX and d->textY.
+ offsetAlign( _col, _row );
+}
+
+
+// ----------------------------------------------------------------
+// Formula handling
+
+
+bool Cell::makeFormula()
+{
+ clearFormula ();
+
+ d->formula = new KSpread::Formula (sheet(), this);
+ d->formula->setExpression (d->strText);
+
+ if (!d->formula->isValid ()) {
+ // Did a syntax error occur ?
+ clearFormula();
+
+ if (format()->sheet()->doc()->getShowMessageError())
+ {
+ TQString tmp(i18n("Error in cell %1\n\n"));
+ tmp = tmp.arg( fullName() );
+ KMessageBox::error( (TQWidget*)0L, tmp);
+ }
+ setFlag(Flag_ParseError);
+ Value v;
+ v.setError ( "####" );
+ setValue (v);
+ return false;
+ }
+
+ // we must recalc
+ setCalcDirtyFlag ();
+
+ return true;
+}
+
+void Cell::clearFormula()
+{
+ delete d->formula;
+ d->formula = 0L;
+}
+
+bool Cell::calc(bool delay)
+{
+ if ( !isFormula() )
+ return true;
+
+ if (d->formula == 0)
+ {
+ if ( testFlag( Flag_ParseError ) ) // there was a parse error
+ return false;
+ else
+ {
+ /* we were probably at a "isLoading() = true" state when we originally
+ * parsed
+ */
+ makeFormula ();
+
+ if ( d->formula == 0 ) // there was a parse error
+ return false;
+ }
+ }
+
+ if ( !testFlag( Flag_CalcDirty ) )
+ return true;
+
+ if ( delay )
+ {
+ if ( format()->sheet()->doc()->delayCalculation() )
+ return true;
+ }
+
+ setFlag(Flag_LayoutDirty);
+ setFlag(Flag_TextFormatDirty);
+ clearFlag(Flag_CalcDirty);
+
+ Value result = d->formula->eval ();
+ setValue (result);
+ if (result.isNumber())
+ checkNumberFormat(); // auto-chooses number or scientific
+
+ clearFlag(Flag_CalcDirty);
+ setFlag(Flag_LayoutDirty);
+
+ return true;
+}
+
+
+// ================================================================
+// Painting
+
+
+// Paint the cell. This is the main function that calls a lot of
+// helper functions.
+//
+// `rect' is the rectangle that we should paint on. If the cell
+// does not overlap this rectangle, we can return immediately.
+// `coordinate' is the origin (the upper left) of the cell in document
+// coordinates.
+//
+
+void Cell::paintCell( const KoRect &rect, TQPainter & painter,
+ View *view,
+ const KoPoint &coordinate,
+ const TQPoint &cellRef,
+ int paintBorder,
+ TQPen & rightPen, TQPen & bottomPen,
+ TQPen & leftPen, TQPen & topPen,
+ TQValueList<TQPoint> &mergedCellsPainted,
+ bool drawCursor )
+{
+ bool paintBorderRight = paintBorder & Border_Right;
+ bool paintBorderBottom = paintBorder & Border_Bottom;
+ bool paintBorderLeft = paintBorder & Border_Left;
+ bool paintBorderTop = paintBorder & Border_Top;
+
+ // If we are already painting this cell, then return immediately.
+ // This avoids infinite recursion.
+ if ( testFlag( Flag_PaintingCell ) )
+ return;
+
+ // Indicate that we are painting this cell now.
+ setFlag( Flag_PaintingCell );
+
+ // This flag indicates that we are working on drawing the cells that
+ // another cell is obscuring. The value is the number of levels down we
+ // are currently working -- i.e. a cell obscured by a cell which is
+ // obscured by a cell.
+ static int paintingObscured = 0;
+
+#if 0
+ if (paintingObscured == 0)
+ kdDebug(36001) << "painting cell " << name() << endl;
+ else
+ kdDebug(36001) << " painting obscured cell " << name() << endl;
+#endif
+
+ // Sanity check: If we're working on drawing an obscured cell, that
+ // means this cell should have a cell that obscures it.
+ Q_ASSERT(!(paintingObscured > 0 && d->extra()->obscuringCells.isEmpty()));
+
+ // The parameter cellref should be *this, unless this is the default cell.
+ Q_ASSERT(isDefault()
+ || (((cellRef.x() == d->column) && (cellRef.y() == d->row))));
+
+ Sheet::LayoutDirection sheetDir = format()->sheet()->layoutDirection();
+
+ double left = coordinate.x();
+
+ ColumnFormat * colFormat = format()->sheet()->columnFormat( cellRef.x() );
+ RowFormat * rowFormat = format()->sheet()->rowFormat( cellRef.y() );
+
+ // Set width, height to the total width and height that this cell
+ // covers, including obscured cells, and width0, height0 to the
+ // width and height of this cell, maybe merged but never implicitly
+ // extended.
+ double width0 = colFormat->dblWidth();
+ double height0 = rowFormat->dblHeight();
+ double width = width0;
+ double height = height0;
+
+ // Handle right-to-left layout.
+ // In an RTL sheet the cells have to be painted at their opposite horizontal
+ // location on the canvas, meaning that column A will be the rightmost column
+ // on screen, column B will be to the left of it and so on. Here we change
+ // the horizontal coordinate at which we start painting the cell in case the
+ // sheet's direction is RTL. We do this only if paintingObscured is 0,
+ // otherwise the cell's painting location will flip back and forth in
+ // consecutive calls to paintCell when painting obscured cells.
+ if ( sheetDir == Sheet::RightToLeft && paintingObscured == 0
+ && view && view->canvasWidget() )
+ {
+ double dwidth = view->doc()->unzoomItX(view->canvasWidget()->width());
+ left = dwidth - coordinate.x() - width;
+ }
+
+ // See if this cell is merged or has overflown into neighbor cells.
+ // In that case, the width/height is greater than just the cell
+ // itself.
+ if (d->hasExtra()) {
+ if (d->extra()->mergedXCells > 0 || d->extra()->mergedYCells > 0) {
+ // merged cell extends to the left if sheet is RTL
+ if ( sheetDir == Sheet::RightToLeft ) {
+ left -= d->extra()->extraWidth - width;
+ }
+ width0 = d->extra()->extraWidth;
+ height0 = d->extra()->extraHeight;
+ width = d->extra()->extraWidth;
+ height = d->extra()->extraHeight;
+ }
+ else {
+#if 0
+ width += d->extra()->extraXCells ? d->extra()->extraWidth : 0;
+ height += d->extra()->extraYCells ? d->extra()->extraHeight : 0;
+#else
+ // FIXME: Make extraWidth/Height really contain the *extra* width/height.
+ if ( d->extra()->extraXCells )
+ width = d->extra()->extraWidth;
+ if ( d->extra()->extraYCells )
+ height = d->extra()->extraHeight;
+#endif
+ }
+ }
+
+ // Check if the cell is "selected", i.e. it should be drawn with the
+ // color that indicates selection (dark blue). If more than one
+ // square is selected, the last one uses the ordinary colors. In
+ // that case, "selected" will be set to false even though the cell
+ // itself really is selected.
+ bool selected = false;
+ if ( view != NULL ) {
+ selected = view->selectionInfo()->contains( cellRef );
+
+ // But the cell doesn't look selected if this is the marker cell.
+ Cell *cell = format()->sheet()->cellAt( view->selectionInfo()->marker() );
+ TQPoint bottomRight( view->selectionInfo()->marker().x() + cell->extraXCells(),
+ view->selectionInfo()->marker().y() + cell->extraYCells() );
+ TQRect markerArea( view->selectionInfo()->marker(), bottomRight );
+ selected = selected && !( markerArea.contains( cellRef ) );
+
+ // Don't draw any selection at all when printing.
+ if ( painter.device()->isExtDev() || !drawCursor )
+ selected = false;
+ }
+
+ // Need to make a new layout ?
+ //
+ // FIXME: We have already used (at least) extraWidth/Height above,
+ // and now we are recalculating the layout. This has to be
+ // moved up above all uses.
+ //
+ // FIXME: This needs to be taken out eventually - it is done in
+ // canvas::paintUpdates().
+ if ( testFlag( Flag_LayoutDirty ) )
+ makeLayout( painter, cellRef.x(), cellRef.y() );
+
+ // ---------------- Start the actual painting. ----------------
+
+ // If the rect of this cell doesn't intersect the rect that should
+ // be painted, we can skip the rest and return. (Note that we need
+ // to calculate `left' first before we can do this.)
+ const KoRect cellRect( left, coordinate.y(), width, height );
+ const KoRect cellRect0( left, coordinate.y(), width0, height0 );
+ if ( !cellRect.intersects( rect ) ) {
+ clearFlag( Flag_PaintingCell );
+ return;
+ }
+
+ // Get the background color.
+ //
+ // If there is a condition giving the background color for this cell
+ // (and it matches), use that one, otherwise get the standard
+ // background.
+ TQColor backgroundColor;
+ if ( d->hasExtra() && d->extra()->conditions
+ && d->extra()->conditions->matchedStyle()
+ && d->extra()->conditions->matchedStyle()->hasFeature( Style::SBackgroundColor, true ) )
+ backgroundColor = d->extra()->conditions->matchedStyle()->bgColor();
+ else
+ backgroundColor = bgColor( cellRef.x(), cellRef.y() );
+
+ // 1. Paint the background.
+ if ( !isPartOfMerged() )
+ paintBackground( painter, cellRect0, cellRef, selected, backgroundColor );
+
+ // 2. Paint the default borders if we are on screen or if we are printing
+ // and the checkbox to do this is checked.
+ if ( painter.device()->devType() != TQInternal::Printer
+ || format()->sheet()->print()->printGrid())
+ paintDefaultBorders( painter, rect, cellRect, cellRef,
+ paintBorderRight, paintBorderBottom,
+ paintBorderLeft, paintBorderTop,
+ rightPen, bottomPen, leftPen, topPen );
+
+ // 3. Paint all the cells that this one obscures. They may only be
+ // partially obscured.
+ //
+ // The `paintingObscured' variable is used to avoid infinite
+ // recursion since cells sometimes paint their obscuring cell as
+ // well.
+ paintingObscured++;
+
+ if (d->hasExtra() && (d->extra()->extraXCells > 0
+ || d->extra()->extraYCells > 0)) {
+ //kdDebug(36001) << "painting obscured cells for " << name() << endl;
+
+ paintObscuredCells( rect, painter, view, cellRect, cellRef,
+ paintBorderRight, paintBorderBottom,
+ paintBorderLeft, paintBorderTop,
+ rightPen, bottomPen, leftPen, topPen,
+ mergedCellsPainted);
+
+ // FIXME: Is this the right place for this?
+ if ( d->extra()->mergedXCells > 0 || d->extra()->mergedYCells > 0 )
+ mergedCellsPainted.prepend( cellRef );
+ }
+ paintingObscured--;
+
+ // 4. Paint the borders of the cell if no other cell is forcing this
+ // one, i.e. this cell is not part of a merged cell.
+ //
+
+ // If we print pages, then we disable clipping, otherwise borders are
+ // cut in the middle at the page borders.
+ if ( painter.device()->isExtDev() )
+ painter.setClipping( false );
+
+ // Paint the borders if this cell is not part of another merged cell.
+ if ( !isPartOfMerged() ) {
+ // if (!testFlag(Flag_Highlight))
+ paintCellBorders( painter, rect, cellRect0,
+ cellRef,
+ paintBorderRight, paintBorderBottom,
+ paintBorderLeft, paintBorderTop,
+ rightPen, bottomPen, leftPen, topPen );
+ }
+
+ // Turn clipping back on.
+ if ( painter.device()->isExtDev() )
+ painter.setClipping( true );
+
+ // 5. Paint diagonal lines and page borders.
+ paintCellDiagonalLines( painter, cellRect0, cellRef );
+
+ paintPageBorders( painter, cellRect0, cellRef,
+ paintBorderRight, paintBorderBottom );
+
+
+ // 6. Now paint the content, if this cell isn't obscured.
+ if ( !isObscured() ) {
+
+ // 6a. Paint possible comment indicator.
+ if ( !painter.device()->isExtDev()
+ || format()->sheet()->print()->printCommentIndicator() )
+ paintCommentIndicator( painter, cellRect, cellRef, backgroundColor );
+
+ // 6b. Paint possible formula indicator.
+ if ( !painter.device()->isExtDev()
+ || format()->sheet()->print()->printFormulaIndicator() )
+ paintFormulaIndicator( painter, cellRect, backgroundColor );
+
+ // 6c. Paint possible indicator for clipped text.
+ paintMoreTextIndicator( painter, cellRect, backgroundColor );
+
+ //6c. Paint cell highlight
+#if 0
+ if (highlightBorder != Border_None)
+ paintCellHighlight ( painter, cellRect, cellRef, highlightBorder,
+ rightHighlightPen, bottomHighlightPen,
+ leftHighlightPen, topHighlightPen );
+#endif
+
+ // 6d. Paint the text in the cell unless:
+ // a) it is empty
+ // b) something indicates that the text should not be painted
+ // c) the sheet is protected and the cell is hidden.
+ if ( !d->strOutText.isEmpty()
+ && ( !painter.device()->isExtDev()
+ || !format()->getDontprintText( cellRef.x(), cellRef.y() ) )
+ && !( format()->sheet()->isProtected()
+ && format()->isHideAll( cellRef.x(), cellRef.y() ) ) )
+ {
+ paintText( painter, cellRect, cellRef );
+ }
+ }
+
+ // 7. If this cell is obscured and we are not already painting obscured
+ // cells, then paint the obscuring cell(s). Otherwise don't do
+ // anything so that we don't cause an infinite loop.
+ if ( isObscured() && paintingObscured == 0 &&
+ !( sheetDir == Sheet::RightToLeft && painter.device()->isExtDev() ) )
+ {
+
+ //kdDebug(36001) << "painting cells that obscure " << name() << endl;
+
+ // Store the obscuringCells list in a list of TQPoint(column, row)
+ // This avoids crashes during the iteration through
+ // obscuringCells, when the cells may get non valid or the list
+ // itself gets changed during a call of obscuringCell->paintCell
+ // (this happens e.g. when there is an updateDepend)
+ if (d->hasExtra()) {
+ TQValueList<TQPoint> listPoints;
+ TQValueList<Cell*>::iterator it = d->extra()->obscuringCells.begin();
+ TQValueList<Cell*>::iterator end = d->extra()->obscuringCells.end();
+ for ( ; it != end; ++it ) {
+ Cell *obscuringCell = *it;
+
+ listPoints.append( TQPoint( obscuringCell->column(), obscuringCell->row() ) );
+ }
+
+ TQValueList<TQPoint>::iterator it1 = listPoints.begin();
+ TQValueList<TQPoint>::iterator end1 = listPoints.end();
+ for ( ; it1 != end1; ++it1 ) {
+ TQPoint obscuringCellRef = *it1;
+
+ // Only paint those obscuring cells that haven't been already
+ // painted yet.
+ //
+ // This optimization removes an O(n^4) behaviour where n is
+ // the number of cells on one edge in a merged cell.
+ if ( mergedCellsPainted.contains( obscuringCellRef ) )
+ continue;
+
+ Cell *obscuringCell = format()->sheet()->cellAt( obscuringCellRef.x(),
+ obscuringCellRef.y() );
+
+ if ( obscuringCell != 0 ) {
+ double x = format()->sheet()->dblColumnPos( obscuringCellRef.x() );
+ double y = format()->sheet()->dblRowPos( obscuringCellRef.y() );
+ if ( view != 0 ) {
+ x -= view->canvasWidget()->xOffset();
+ y -= view->canvasWidget()->yOffset();
+ }
+
+ KoPoint corner( x, y );
+ painter.save();
+
+ // Get the effective pens for the borders. These are
+ // determined by possible conditions on the cell with
+ // associated styles.
+ TQPen rp( obscuringCell->effRightBorderPen( obscuringCellRef.x(),
+ obscuringCellRef.y() ) );
+ TQPen bp( obscuringCell->effBottomBorderPen( obscuringCellRef.x(),
+ obscuringCellRef.y() ) );
+ TQPen lp( obscuringCell->effLeftBorderPen( obscuringCellRef.x(),
+ obscuringCellRef.y() ) );
+ TQPen tp( obscuringCell->effTopBorderPen( obscuringCellRef.x(),
+ obscuringCellRef.y() ) );
+
+
+ //kdDebug(36001) << " painting obscuring cell "
+ // << obscuringCell->name() << endl;
+ // TQPen highlightPen;
+
+ //Note: Painting of highlight isn't quite right. If several
+ // cells are merged, then the whole merged cell will be
+ // painted with the colour of the last cell referenced
+ // which is inside the merged range.
+ obscuringCell->paintCell( rect, painter, view,
+ corner, obscuringCellRef,
+ Border_Left|Border_Top|Border_Right|Border_Bottom,
+ rp, bp, lp, tp,
+ mergedCellsPainted); // new pens
+ painter.restore();
+ }
+ }
+ }
+ }
+
+ // We are done with the painting, so remove the flag on the cell.
+ clearFlag( Flag_PaintingCell );
+}
+
+
+
+// The following code was commented out in the above function. I'll
+// leave it here in case this functionality is ever re-implemented and
+// someone wants some code to start from.
+//
+#if 0
+
+ /**
+ * Modification for drawing the button
+ */
+ if ( d->style == Cell::ST_Button ) {
+ TQBrush fill( TQt::lightGray );
+ TQApplication::style().drawControl( TQStyle::CE_PushButton, &_painter, this,
+ TQRect( _tx + 1, _ty + 1, w2 - 1, h2 - 1 ),
+ defaultColorGroup ); //, selected, &fill );
+ }
+
+ /**
+ * Modification for drawing the combo box
+ */
+ else if ( d->style == Cell::ST_Select ) {
+ TQApplication::style().drawComboButton( &_painter, _tx + 1, _ty + 1,
+ w2 - 1, h2 - 1,
+ defaultColorGroup, selected );
+ }
+#endif
+
+
+#if 0
+ void Cell::paintCellHighlight(TQPainter& painter,
+ const KoRect& cellRect,
+ const TQPoint& cellRef,
+ const int highlightBorder,
+ const TQPen& rightPen,
+ const TQPen& bottomPen,
+ const TQPen& leftPen,
+ const TQPen& topPen
+ )
+{
+ //painter.drawLine(cellRect.left(),cellRect.top(),cellRect.right(),cellRect.bottom());
+ //TQPen pen(d->extra()->highlight);
+ //painter.setPen(highlightPen);
+
+ TQBrush nullBrush;
+ painter.setBrush(nullBrush);
+
+ TQRect zoomedCellRect = sheet()->doc()->zoomRect( cellRect );
+
+ //The highlight rect is just inside the main cell rect
+ //This saves the hassle of repainting nearby cells when the highlight is changed as the highlight areas
+ //do not overlap
+ zoomedCellRect.setLeft(zoomedCellRect.left()+1);
+ //zoomedCellRect.setRight(zoomedCellRect.right()-1);
+ zoomedCellRect.setTop(zoomedCellRect.top()+1);
+ //zoomedCellRect.setBottom(zoomedCellRect.bottom()-1);
+
+ if ( cellRef.x() != KS_colMax )
+ zoomedCellRect.setWidth( zoomedCellRect.width() - 1 );
+ if ( cellRef.y() != KS_rowMax )
+ zoomedCellRect.setHeight( zoomedCellRect.height() - 1 );
+
+ if (highlightBorder & Border_Top)
+ {
+ painter.setPen(topPen);
+ painter.drawLine(zoomedCellRect.left(),zoomedCellRect.top(),zoomedCellRect.right(),zoomedCellRect.top());
+ }
+ if (highlightBorder & Border_Left)
+ {
+ painter.setPen(leftPen);
+ painter.drawLine(zoomedCellRect.left(),zoomedCellRect.top(),zoomedCellRect.left(),zoomedCellRect.bottom());
+ }
+ if (highlightBorder & Border_Right)
+ {
+ painter.setPen(rightPen);
+ painter.drawLine(zoomedCellRect.right(),zoomedCellRect.top(),zoomedCellRect.right(),zoomedCellRect.bottom());
+ }
+ if (highlightBorder & Border_Bottom)
+ {
+ painter.setPen(bottomPen);
+ painter.drawLine(zoomedCellRect.left(),zoomedCellRect.bottom(),zoomedCellRect.right(),zoomedCellRect.bottom());
+ }
+
+ if (highlightBorder & Border_SizeGrip)
+ {
+ TQBrush brush(rightPen.color());
+ painter.setBrush(brush);
+ painter.setPen(rightPen);
+ painter.drawRect(zoomedCellRect.right()-3,zoomedCellRect.bottom()-3,4,4);
+ }
+
+ //painter.drawRect(zoomedCellRect.left(),zoomedCellRect.top(),zoomedCellRect.width(),zoomedCellRect.height());
+}
+#endif
+
+
+// Paint all the cells that this cell obscures (helper function to paintCell).
+//
+void Cell::paintObscuredCells(const KoRect& rect, TQPainter& painter,
+ View* view,
+ const KoRect &cellRect,
+ const TQPoint &cellRef,
+ bool paintBorderRight,
+ bool _paintBorderBottom,
+ bool paintBorderLeft,
+ bool _paintBorderTop,
+ TQPen & rightPen, TQPen & _bottomPen,
+ TQPen & leftPen, TQPen & _topPen,
+ TQValueList<TQPoint> &mergedCellsPainted)
+{
+ // If there are no obscured cells, return.
+ if ( !extraXCells() && !extraYCells() )
+ return;
+
+ double ypos = cellRect.y();
+ int maxY = extraYCells();
+ int maxX = extraXCells();
+
+ // Loop through the rectangle of squares that we obscure and paint them.
+ for ( int y = 0; y <= maxY; ++y ) {
+ double xpos = cellRect.x();
+ RowFormat* rl = format()->sheet()->rowFormat( cellRef.y() + y );
+
+ for( int x = 0; x <= maxX; ++ x ) {
+ ColumnFormat * cl = format()->sheet()->columnFormat( cellRef.x() + x );
+ if ( y != 0 || x != 0 ) {
+ uint column = cellRef.x() + x;
+ uint row = cellRef.y() + y;
+
+ TQPen topPen;
+ TQPen bottomPen;
+ bool paintBorderTop;
+ bool paintBorderBottom;
+
+ Cell *cell = format()->sheet()->cellAt( column, row );
+ KoPoint corner( xpos, ypos );
+
+ // Check if the upper and lower borders should be painted, and
+ // if so which pens we should use. There used to be a nasty
+ // bug here (#61452).
+ // Check top pen. Only check if this is not on the top row.
+ topPen = _topPen;
+ paintBorderTop = _paintBorderTop;
+ if ( row > 1 && !cell->isPartOfMerged() ) {
+ Cell *cellUp = format()->sheet()->cellAt( column, row - 1 );
+
+ if ( cellUp->isDefault() )
+ paintBorderTop = false;
+ else {
+ // If the cell towards the top is part of a merged cell, get
+ // the pointer to the master cell.
+ cellUp = cellUp->ultimateObscuringCell();
+
+ topPen = cellUp->effBottomBorderPen( cellUp->column(),
+ cellUp->row() );
+
+#if 0
+ int penWidth = TQMAX(1, sheet()->doc()->zoomItY( topPen.width() ));
+ topPen.setWidth( penWidth );
+#endif
+ }
+ }
+
+ // FIXME: I thought we had to check bottom pen as well.
+ // However, it looks as if we don't need to. It works anyway.
+ bottomPen = _bottomPen;
+ paintBorderBottom = _paintBorderBottom;
+
+ int paintBorder = Border_None;
+ if (paintBorderLeft) paintBorder |= Cell::Border_Left;
+ if (paintBorderRight) paintBorder |= Cell::Border_Right;
+ if (paintBorderTop) paintBorder |= Cell::Border_Top;
+ if (paintBorderBottom) paintBorder |= Cell::Border_Bottom;
+
+ /*Cell::BorderSides highlightBorder = Border_None;
+ TQPen highlightPen;*/
+
+
+ //kdDebug(36001) << "calling paintcell for obscured cell "
+ // << cell->name() << endl;
+ cell->paintCell( rect, painter, view,
+ corner,
+ TQPoint( cellRef.x() + x, cellRef.y() + y ),
+ paintBorder,
+ rightPen, bottomPen, leftPen, topPen,
+ mergedCellsPainted);
+ }
+ xpos += cl->dblWidth();
+ }
+
+ ypos += rl->dblHeight();
+ }
+}
+
+
+// Paint the background of this cell.
+//
+void Cell::paintBackground( TQPainter& painter, const KoRect &cellRect,
+ const TQPoint &cellRef, bool selected,
+ TQColor &backgroundColor )
+{
+ TQColorGroup defaultColorGroup = TQApplication::palette().active();
+ TQRect zoomedCellRect = sheet()->doc()->zoomRect( cellRect );
+
+ // If this is not the KS_rowMax and/or KS_colMax, then we reduce
+ // width and/or height by one. This is due to the fact that the
+ // right/bottom most pixel is shared with the left/top most pixel of
+ // the following cell. Only in the case of KS_colMax/KS_rowMax we
+ // need to draw even this pixel, as there isn't a following cell to
+ // draw the background pixel.
+ if ( cellRef.x() != KS_colMax )
+ zoomedCellRect.setWidth( zoomedCellRect.width() - 1 );
+ if ( cellRef.y() != KS_rowMax )
+ zoomedCellRect.setHeight( zoomedCellRect.height() - 1 );
+
+ // Determine the correct background color
+ if ( selected )
+ {
+ //If the cell's background color is too bright, use the default highlight color
+ //Otherwise use a lighter version of the cell's background color.
+ TQColor c;
+
+ int averageColor = (backgroundColor.red() + backgroundColor.green() + backgroundColor.blue()) / 3;
+
+ if (averageColor > 180)
+ if (averageColor > 225)
+ c = View::highlightColor();
+ else
+ c = backgroundColor.light( 115 ); //15% lighter
+ else
+ c = backgroundColor.light( 125 ); //25% lighter
+
+ painter.setBackgroundColor( c );
+ }
+ else {
+ TQColor bg( backgroundColor );
+
+ // Handle printers separately.
+ if ( !painter.device()->isExtDev() ) {
+ if ( bg.isValid() )
+ painter.setBackgroundColor( bg );
+ else
+ painter.setBackgroundColor( defaultColorGroup.base() );
+ }
+ else {
+ //bad hack but there is a qt bug
+ //so I can print backgroundcolor
+ TQBrush bb( bg );
+ if ( !bg.isValid() )
+ bb.setColor( TQt::white );
+
+ painter.fillRect( zoomedCellRect, bb );
+ return;
+ }
+ }
+
+ // Erase the background of the cell.
+ if ( !painter.device()->isExtDev() )
+ painter.eraseRect( zoomedCellRect );
+
+ // Get a background brush
+ TQBrush bb;
+ if ( d->hasExtra()
+ && d->extra()->conditions
+ && d->extra()->conditions->matchedStyle()
+ && d->extra()->conditions->matchedStyle()->hasFeature( Style::SBackgroundBrush, true ) )
+ bb = d->extra()->conditions->matchedStyle()->backGroundBrush();
+ else
+ bb = backGroundBrush( cellRef.x(), cellRef.y() );
+
+ // Draw background pattern if necessary.
+ if ( bb.style() != TQt::NoBrush )
+ painter.fillRect( zoomedCellRect, bb );
+
+ backgroundColor = painter.backgroundColor();
+}
+
+
+// Paint the standard light grey borders that are always visible.
+//
+void Cell::paintDefaultBorders( TQPainter& painter, const KoRect &rect,
+ const KoRect &cellRect,
+ const TQPoint &cellRef,
+ bool paintBorderRight, bool /*paintBorderBottom*/,
+ bool paintBorderLeft, bool paintBorderTop,
+ TQPen const & rightPen, TQPen const & /*bottomPen*/,
+ TQPen const & leftPen, TQPen const & topPen )
+{
+ /*
+ *** Notes about optimisation ***
+
+ This function was painting the top , left , right & bottom lines in almost all cells previously, contrary to what the comment
+ below says should happen. There doesn't appear to be a UI option to enable or disable showing of the grid when printing at the moment,
+ so I have disabled drawing of right and bottom borders for all cells.
+
+ I also couldn't work out under what conditions the variables dt / db would come out as anything other than 0 in the code
+ for painting the various borders. The effTopBorderPen / effBottomBorderPen calls were taking up a lot of time
+ according some profiling I did. If that code really is necessary, we need to find a more efficient way of getting the widths
+ than grabbing the whole TQPen object and asking it.
+
+
+ --Robert Knight ([email protected])
+ */
+ Doc* doc = sheet()->doc();
+
+ Sheet::LayoutDirection sheetDir = format()->sheet()->layoutDirection();
+ bool paintingToExternalDevice = painter.device()->isExtDev();
+
+ // Each cell is responsible for drawing it's top and left portions
+ // of the "default" grid. --Or not drawing it if it shouldn't be
+ // there. It's also responsible to paint the right and bottom, if
+ // it is the last cell on a print out.
+
+ bool paintTop;
+ bool paintLeft;
+ bool paintBottom=false;
+ bool paintRight=false;
+
+ paintLeft = ( paintBorderLeft && leftPen.style() == TQt::NoPen
+ && sheet()->getShowGrid() && sheetDir==Sheet::LeftToRight );
+ paintRight = ( paintBorderRight && rightPen.style() == TQt::NoPen
+ && sheet()->getShowGrid() && sheetDir==Sheet::RightToLeft );
+ paintTop = ( paintBorderTop && topPen.style() == TQt::NoPen
+ && sheet()->getShowGrid() );
+// paintBottom = ( paintBorderBottom && sheet()->getShowGrid()
+// && bottomPen.style() == TQt::NoPen );
+
+
+ //Set the single-pixel with pen for drawing the borders with.
+ painter.setPen( TQPen( sheet()->doc()->gridColor(), 1, TQt::SolidLine ) );
+
+ // If there are extra cells, there might be more conditions.
+ if (d->hasExtra()) {
+ TQValueList<Cell*>::const_iterator it = d->extra()->obscuringCells.begin();
+ TQValueList<Cell*>::const_iterator end = d->extra()->obscuringCells.end();
+ for ( ; it != end; ++it ) {
+ Cell *cell = *it;
+
+ paintTop = paintTop && ( cell->row() == cellRef.y() );
+ paintBottom = false;
+
+ if ( sheetDir == Sheet::RightToLeft ) {
+ paintRight = paintRight && ( cell->column() == cellRef.x() );
+ paintLeft = false;
+ }
+ else {
+ paintLeft = paintLeft && ( cell->column() == cellRef.x() );
+ paintRight = false;
+ }
+ }
+ }
+
+ // The left border.
+ if ( paintLeft ) {
+ int dt = 0;
+ int db = 0;
+
+ #if 0
+ if ( cellRef.x() > 1 ) {
+ Cell *cell_west = format()->sheet()->cellAt( cellRef.x() - 1,
+ cellRef.y() );
+ TQPen t = cell_west->effTopBorderPen( cellRef.x() - 1, cellRef.y() );
+ TQPen b = cell_west->effBottomBorderPen( cellRef.x() - 1, cellRef.y() );
+
+ if ( t.style() != TQt::NoPen )
+ dt = ( t.width() + 1 )/2;
+ if ( b.style() != TQt::NoPen )
+ db = ( t.width() / 2);
+ }
+ #endif
+
+ // If we are on paper printout, we limit the length of the lines.
+ // On paper, we always have full cells, on screen not.
+ if ( paintingToExternalDevice ) {
+ if ( sheetDir == Sheet::RightToLeft )
+ painter.drawLine( doc->zoomItX( TQMAX( rect.left(), cellRect.right() ) ),
+ doc->zoomItY( TQMAX( rect.top(), cellRect.y() + dt ) ),
+ doc->zoomItX( TQMIN( rect.right(), cellRect.right() ) ),
+ doc->zoomItY( TQMIN( rect.bottom(), cellRect.bottom() - db ) ) );
+ else
+ painter.drawLine( doc->zoomItX( TQMAX( rect.left(), cellRect.x() ) ),
+ doc->zoomItY( TQMAX( rect.top(), cellRect.y() + dt ) ),
+ doc->zoomItX( TQMIN( rect.right(), cellRect.x() ) ),
+ doc->zoomItY( TQMIN( rect.bottom(), cellRect.bottom() - db ) ) );
+ }
+ else {
+ if ( sheetDir == Sheet::RightToLeft )
+ painter.drawLine( doc->zoomItX( cellRect.right() ),
+ doc->zoomItY( cellRect.y() + dt ),
+ doc->zoomItX( cellRect.right() ),
+ doc->zoomItY( cellRect.bottom() - db ) );
+ else
+ painter.drawLine( doc->zoomItX( cellRect.x() ),
+ doc->zoomItY( cellRect.y() + dt ),
+ doc->zoomItX( cellRect.x() ),
+ doc->zoomItY( cellRect.bottom() - db ) );
+ }
+ }
+
+
+ // The top border.
+ if ( paintTop ) {
+ int dl = 0;
+ int dr = 0;
+
+ #if 0
+ if ( cellRef.y() > 1 ) {
+ Cell *cell_north = format()->sheet()->cellAt( cellRef.x(),
+ cellRef.y() - 1 );
+
+ TQPen l = cell_north->effLeftBorderPen( cellRef.x(), cellRef.y() - 1 );
+ TQPen r = cell_north->effRightBorderPen( cellRef.x(), cellRef.y() - 1 );
+
+ if ( l.style() != TQt::NoPen )
+ dl = ( l.width() - 1 ) / 2 + 1;
+ if ( r.style() != TQt::NoPen )
+ dr = r.width() / 2;
+ }
+ #endif
+
+
+
+ // If we are on paper printout, we limit the length of the lines.
+ // On paper, we always have full cells, on screen not.
+ if ( paintingToExternalDevice ) {
+ painter.drawLine( doc->zoomItX( TQMAX( rect.left(), cellRect.x() + dl ) ),
+ doc->zoomItY( TQMAX( rect.top(), cellRect.y() ) ),
+ doc->zoomItX( TQMIN( rect.right(), cellRect.right() - dr ) ),
+ doc->zoomItY( TQMIN( rect.bottom(), cellRect.y() ) ) );
+ }
+ else {
+ painter.drawLine( doc->zoomItX( cellRect.x() + dl ),
+ doc->zoomItY( cellRect.y() ),
+ doc->zoomItX( cellRect.right() - dr ),
+ doc->zoomItY( cellRect.y() ) );
+ }
+ }
+
+
+ // The right border.
+ if ( paintRight ) {
+ int dt = 0;
+ int db = 0;
+
+ #if 0
+ if ( cellRef.x() < KS_colMax ) {
+ Cell *cell_east = format()->sheet()->cellAt( cellRef.x() + 1,
+ cellRef.y() );
+
+ TQPen t = cell_east->effTopBorderPen( cellRef.x() + 1, cellRef.y() );
+ TQPen b = cell_east->effBottomBorderPen( cellRef.x() + 1, cellRef.y() );
+
+ if ( t.style() != TQt::NoPen )
+ dt = ( t.width() + 1 ) / 2;
+ if ( b.style() != TQt::NoPen )
+ db = ( t.width() / 2);
+ }
+ #endif
+
+ //painter.setPen( TQPen( sheet()->doc()->gridColor(), 1, TQt::SolidLine ) );
+
+ // If we are on paper printout, we limit the length of the lines.
+ // On paper, we always have full cells, on screen not.
+ if ( painter.device()->isExtDev() ) {
+ if ( sheetDir == Sheet::RightToLeft )
+ painter.drawLine( doc->zoomItX( TQMAX( rect.left(), cellRect.x() ) ),
+ doc->zoomItY( TQMAX( rect.top(), cellRect.y() + dt ) ),
+ doc->zoomItX( TQMIN( rect.right(), cellRect.x() ) ),
+ doc->zoomItY( TQMIN( rect.bottom(), cellRect.bottom() - db ) ) );
+ else
+ painter.drawLine( doc->zoomItX( TQMAX( rect.left(), cellRect.right() ) ),
+ doc->zoomItY( TQMAX( rect.top(), cellRect.y() + dt ) ),
+ doc->zoomItX( TQMIN( rect.right(), cellRect.right() ) ),
+ doc->zoomItY( TQMIN( rect.bottom(), cellRect.bottom() - db ) ) );
+ }
+ else {
+ if ( sheetDir == Sheet::RightToLeft )
+ painter.drawLine( doc->zoomItX( cellRect.x() ),
+ doc->zoomItY( cellRect.y() + dt ),
+ doc->zoomItX( cellRect.x() ),
+ doc->zoomItY( cellRect.bottom() - db ) );
+ else
+ painter.drawLine( doc->zoomItX( cellRect.right() ),
+ doc->zoomItY( cellRect.y() + dt ),
+ doc->zoomItX( cellRect.right() ),
+ doc->zoomItY( cellRect.bottom() - db ) );
+ }
+ }
+
+ // The bottom border.
+ /*if ( paintBottom ) {
+ int dl = 0;
+ int dr = 0;
+ if ( cellRef.y() < KS_rowMax ) {
+ Cell *cell_south = format()->sheet()->cellAt( cellRef.x(),
+ cellRef.y() + 1 );
+
+ TQPen l = cell_south->effLeftBorderPen( cellRef.x(), cellRef.y() + 1 );
+ TQPen r = cell_south->effRightBorderPen( cellRef.x(), cellRef.y() + 1 );
+
+ if ( l.style() != TQt::NoPen )
+ dl = ( l.width() - 1 ) / 2 + 1;
+ if ( r.style() != TQt::NoPen )
+ dr = r.width() / 2;
+ }
+
+ painter.setPen( TQPen( sheet()->doc()->gridColor(), 1, TQt::SolidLine ) );
+
+ // If we are on paper printout, we limit the length of the lines.
+ // On paper, we always have full cells, on screen not.
+ if ( painter.device()->isExtDev() ) {
+ painter.drawLine( doc->zoomItX( TQMAX( rect.left(), cellRect.x() + dl ) ),
+ doc->zoomItY( TQMAX( rect.top(), cellRect.bottom() ) ),
+ doc->zoomItX( TQMIN( rect.right(), cellRect.right() - dr ) ),
+ doc->zoomItY( TQMIN( rect.bottom(), cellRect.bottom() ) ) );
+ }
+ else {
+ painter.drawLine( doc->zoomItX( cellRect.x() + dl ),
+ doc->zoomItY( cellRect.bottom() ),
+ doc->zoomItX( cellRect.right() - dr ),
+ doc->zoomItY( cellRect.bottom() ) );
+ }
+ }*/
+}
+
+
+// Paint a comment indicator if the cell has a comment.
+//
+void Cell::paintCommentIndicator( TQPainter& painter,
+ const KoRect &cellRect,
+ const TQPoint &/*cellRef*/,
+ TQColor &backgroundColor )
+{
+ Doc * doc = sheet()->doc();
+
+ // Point the little corner if there is a comment attached
+ // to this cell.
+ if ( ( format()->propertiesMask() & (uint) Format::PComment )
+ && cellRect.width() > 10.0
+ && cellRect.height() > 10.0
+ && ( sheet()->print()->printCommentIndicator()
+ || ( !painter.device()->isExtDev() && sheet()->getShowCommentIndicator() ) ) ) {
+ TQColor penColor = TQt::red;
+
+ // If background has high red part, switch to blue.
+ if ( tqRed( backgroundColor.rgb() ) > 127 &&
+ tqGreen( backgroundColor.rgb() ) < 80 &&
+ tqBlue( backgroundColor.rgb() ) < 80 )
+ {
+ penColor = TQt::blue;
+ }
+
+ // Get the triangle.
+ TQPointArray point( 3 );
+ if ( format()->sheet()->layoutDirection()==Sheet::RightToLeft ) {
+ point.setPoint( 0, doc->zoomItX( cellRect.x() + 6.0 ),
+ doc->zoomItY( cellRect.y() ) );
+ point.setPoint( 1, doc->zoomItX( cellRect.x() ),
+ doc->zoomItY( cellRect.y() ) );
+ point.setPoint( 2, doc->zoomItX( cellRect.x() ),
+ doc->zoomItY( cellRect.y() + 6.0 ) );
+ }
+ else {
+ point.setPoint( 0, doc->zoomItX( cellRect.right() - 5.0 ),
+ doc->zoomItY( cellRect.y() ) );
+ point.setPoint( 1, doc->zoomItX( cellRect.right() ),
+ doc->zoomItY( cellRect.y() ) );
+ point.setPoint( 2, doc->zoomItX( cellRect.right() ),
+ doc->zoomItY( cellRect.y() + 5.0 ) );
+ }
+
+ // And draw it.
+ painter.setBrush( TQBrush( penColor ) );
+ painter.setPen( TQt::NoPen );
+ painter.drawPolygon( point );
+ }
+}
+
+
+
+// Paint a small rectangle if this cell holds a formula.
+//
+void Cell::paintFormulaIndicator( TQPainter& painter,
+ const KoRect &cellRect,
+ TQColor &backgroundColor )
+{
+ if ( isFormula() &&
+ format()->sheet()->getShowFormulaIndicator() &&
+ cellRect.width() > 10.0 &&
+ cellRect.height() > 10.0 )
+ {
+ Doc* doc = sheet()->doc();
+
+ TQColor penColor = TQt::blue;
+ // If background has high blue part, switch to red.
+ if ( tqRed( backgroundColor.rgb() ) < 80 &&
+ tqGreen( backgroundColor.rgb() ) < 80 &&
+ tqBlue( backgroundColor.rgb() ) > 127 )
+ {
+ penColor = TQt::red;
+ }
+
+ // Get the triangle...
+ TQPointArray point( 3 );
+ if ( format()->sheet()->layoutDirection()==Sheet::RightToLeft ) {
+ point.setPoint( 0, doc->zoomItX( cellRect.right() - 6.0 ),
+ doc->zoomItY( cellRect.bottom() ) );
+ point.setPoint( 1, doc->zoomItX( cellRect.right() ),
+ doc->zoomItY( cellRect.bottom() ) );
+ point.setPoint( 2, doc->zoomItX( cellRect.right() ),
+ doc->zoomItY( cellRect.bottom() - 6.0 ) );
+ }
+ else {
+ point.setPoint( 0, doc->zoomItX( cellRect.x() ),
+ doc->zoomItY( cellRect.bottom() - 6.0 ) );
+ point.setPoint( 1, doc->zoomItX( cellRect.x() ),
+ doc->zoomItY( cellRect.bottom() ) );
+ point.setPoint( 2, doc->zoomItX( cellRect.x() + 6.0 ),
+ doc->zoomItY( cellRect.bottom() ) );
+ }
+
+ // ...and draw it.
+ painter.setBrush( TQBrush( penColor ) );
+ painter.setPen( TQt::NoPen );
+ painter.drawPolygon( point );
+ }
+}
+
+
+// Paint an indicator that the text in the cell is cut.
+//
+void Cell::paintMoreTextIndicator( TQPainter& painter,
+ const KoRect &cellRect,
+ TQColor &backgroundColor )
+{
+ // Show a red triangle when it's not possible to write all text in cell.
+ // Don't print the red triangle if we're printing.
+ if( testFlag( Flag_CellTooShortX ) &&
+ !painter.device()->isExtDev() &&
+ cellRect.height() > 4.0 &&
+ cellRect.width() > 4.0 )
+ {
+ Doc* doc = sheet()->doc();
+
+ TQColor penColor = TQt::red;
+ // If background has high red part, switch to blue.
+ if ( tqRed( backgroundColor.rgb() ) > 127
+ && tqGreen( backgroundColor.rgb() ) < 80
+ && tqBlue( backgroundColor.rgb() ) < 80 )
+ {
+ penColor = TQt::blue;
+ }
+
+ // Get the triangle...
+ TQPointArray point( 3 );
+ if ( d->strOutText.isRightToLeft() ) {
+ point.setPoint( 0, doc->zoomItX( cellRect.left() + 4.0 ),
+ doc->zoomItY( cellRect.y() + cellRect.height() / 2.0 -4.0 ) );
+ point.setPoint( 1, doc->zoomItX( cellRect.left() ),
+ doc->zoomItY( cellRect.y() + cellRect.height() / 2.0 ));
+ point.setPoint( 2, doc->zoomItX( cellRect.left() + 4.0 ),
+ doc->zoomItY( cellRect.y() + cellRect.height() / 2.0 +4.0 ) );
+ }
+ else {
+ point.setPoint( 0, doc->zoomItX( cellRect.right() - 4.0 ),
+ doc->zoomItY( cellRect.y() + cellRect.height() / 2.0 - 4.0 ) );
+ point.setPoint( 1, doc->zoomItX( cellRect.right() ),
+ doc->zoomItY( cellRect.y() + cellRect.height() / 2.0 ) );
+ point.setPoint( 2, doc->zoomItX( cellRect.right() - 4.0 ),
+ doc->zoomItY( cellRect.y() + cellRect.height() / 2.0 + 4.0 ) );
+ }
+
+ // ...and paint it.
+ painter.setBrush( TQBrush( penColor ) );
+ painter.setPen( TQt::NoPen );
+ painter.drawPolygon( point );
+ }
+}
+
+
+// Paint the real contents of a cell - the text.
+//
+void Cell::paintText( TQPainter& painter,
+ const KoRect &cellRect,
+ const TQPoint &cellRef )
+{
+ Doc *doc = sheet()->doc();
+
+ ColumnFormat *colFormat = format()->sheet()->columnFormat( cellRef.x() );
+
+ TQColorGroup defaultColorGroup = TQApplication::palette().active();
+ TQColor textColorPrint = effTextColor( cellRef.x(), cellRef.y() );
+
+ // Resolve the text color if invalid (=default).
+ if ( !textColorPrint.isValid() ) {
+ if ( painter.device()->isExtDev() )
+ textColorPrint = TQt::black;
+ else
+ textColorPrint = TQApplication::palette().active().text();
+ }
+
+ TQPen tmpPen( textColorPrint );
+
+ // Set the font according to the current zoom.
+ applyZoomedFont( painter, cellRef.x(), cellRef.y() );
+
+ // Check for red font color for negative values.
+ if ( !d->hasExtra()
+ || !d->extra()->conditions
+ || !d->extra()->conditions->matchedStyle() ) {
+ if ( value().isNumber()
+ && !( format()->sheet()->getShowFormula()
+ && !( format()->sheet()->isProtected()
+ && format()->isHideFormula( d->column, d->row ) ) ) )
+ {
+ double v = value().asFloat();
+ if ( format()->floatColor( cellRef.x(), cellRef.y()) == Format::NegRed
+ && v < 0.0 )
+ tmpPen.setColor( TQt::red );
+ }
+ }
+
+ // Check for blue color, for hyperlink.
+ if ( !link().isEmpty() ) {
+ tmpPen.setColor( TQApplication::palette().active().link() );
+ TQFont f = painter.font();
+ f.setUnderline( true );
+ painter.setFont( f );
+ }
+
+#if 0
+/****
+
+ For now I am commenting this out -- with the default color display you
+ can read normal text through a highlighted background. Maybe this isn't
+ always the case, though, and we can put the highlighted text color back in.
+ In that case, we need to somewhere in here figure out if the text overlaps
+ another cell outside of the selection, otherwise that portion of the text
+ will be printed white on white. So just that portion would need to be
+ painted again in the normal color.
+
+ This should probably be done eventually, anyway, because I like using the
+ reverse text color for highlighted cells. I just don't like extending the
+ cell 'highlight' background outside of the selection rectangle because it
+ looks REALLY ugly.
+*/
+
+ if ( selected && ( cellRef.x() != marker.x() || cellRef.y() != marker.y() ) )
+ {
+ TQPen p( tmpPen );
+ p.setColor( defaultColorGroup.highlightedText() );
+ painter.setPen( p );
+ }
+ else {
+ painter.setPen(tmpPen);
+ }
+#endif
+ painter.setPen( tmpPen );
+
+ TQString tmpText = d->strOutText;
+ double tmpHeight = d->textHeight;
+ double tmpWidth = d->textWidth;
+
+ // If the cell is to narrow to paint the whole contents, then pick
+ // out a part of the content that we paint. The result of this is
+ // dependent on the data type of the content.
+ //
+ // FIXME: Make this dependent on the height as well.
+ //
+ if ( testFlag( Flag_CellTooShortX ) ) {
+ d->strOutText = textDisplaying( painter );
+
+ // Recalculate the text width and the offset.
+ textSize( painter );
+ offsetAlign( column(), row() );
+ }
+
+ // Hide zero.
+ if ( format()->sheet()->getHideZero()
+ && value().isNumber()
+ && value().asFloat() == 0 ) {
+ d->strOutText = TQString();
+ }
+
+ // Clear extra cell if column or row is hidden
+ //
+ // FIXME: I think this should be done before the call to
+ // textDisplaying() above.
+ //
+ if ( colFormat->isHide() || ( cellRect.height() <= 2 ) ) {
+ freeAllObscuredCells(); /* TODO: This looks dangerous...must check when I
+ have time */
+ d->strOutText = "";
+ }
+
+ double indent = 0.0;
+ double offsetCellTooShort = 0.0;
+ int a = effAlignX();
+
+ // Apply indent if text is align to left not when text is at right or middle.
+ if ( a == Format::Left && !isEmpty() ) {
+ // FIXME: The following condition should be remade into a call to
+ // a new convenience function:
+ // if ( hasConditionStyleFeature( Style::SIndent, true )...
+ // This should be done throughout the entire file.
+ //
+ if ( d->hasExtra()
+ && d->extra()->conditions
+ && d->extra()->conditions->matchedStyle()
+ && d->extra()->conditions->matchedStyle()->hasFeature( Style::SIndent, true ) )
+ indent = d->extra()->conditions->matchedStyle()->indent();
+ else
+ indent = format()->getIndent( column(), row() );
+ }
+
+ // Made an offset, otherwise ### is under red triangle.
+ if ( a == Format::Right && !isEmpty() && testFlag( Flag_CellTooShortX ) )
+ offsetCellTooShort = format()->sheet()->doc()->unzoomItX( 4 );
+
+ TQFontMetrics fm2 = painter.fontMetrics();
+ double offsetFont = 0.0;
+
+ if ( format()->alignY( column(), row() ) == Format::Bottom
+ && format()->textFontUnderline( column(), row() ) )
+ offsetFont = format()->sheet()->doc()->unzoomItX( fm2.underlinePos() + 1 );
+
+ int tmpAngle;
+ bool tmpMultiRow;
+ bool tmpVerticalText;
+
+ // Check for angled or vertical text.
+ if ( d->hasExtra()
+ && d->extra()->conditions
+ && d->extra()->conditions->matchedStyle() )
+ {
+ Style *matchedStyle = d->extra()->conditions->matchedStyle();
+
+ if ( matchedStyle->hasFeature( Style::SAngle, true ) )
+ tmpAngle = d->extra()->conditions->matchedStyle()->rotateAngle();
+ else
+ tmpAngle = format()->getAngle( cellRef.x(), cellRef.y() );
+
+ if ( matchedStyle->hasFeature( Style::SVerticalText, true ) )
+ tmpVerticalText = matchedStyle->hasProperty( Style::PVerticalText );
+ else
+ tmpVerticalText = format()->verticalText( cellRef.x(), cellRef.y() );
+
+ if ( matchedStyle->hasFeature( Style::SMultiRow, true ) )
+ tmpMultiRow = matchedStyle->hasProperty( Style::PMultiRow );
+ else
+ tmpMultiRow = format()->multiRow( cellRef.x(), cellRef.y() );
+ }
+ else {
+ tmpAngle = format()->getAngle( cellRef.x(), cellRef.y() );
+ tmpVerticalText = format()->verticalText( cellRef.x(), cellRef.y() );
+ tmpMultiRow = format()->multiRow( cellRef.x(), cellRef.y() );
+ }
+
+ // Actually paint the text.
+ // There are 4 possible cases:
+ // - One line of text , horizontal
+ // - Angled text
+ // - Multiple rows of text , horizontal
+ // - Vertical text
+ if ( !tmpMultiRow && !tmpVerticalText && !tmpAngle ) {
+ // Case 1: The simple case, one line, no angle.
+
+ painter.drawText( doc->zoomItX( indent + cellRect.x() + d->textX - offsetCellTooShort ),
+ doc->zoomItY( cellRect.y() + d->textY - offsetFont ), d->strOutText );
+ }
+ else if ( tmpAngle != 0 ) {
+ // Case 2: an angle.
+
+ int angle = tmpAngle;
+ TQFontMetrics fm = painter.fontMetrics();
+
+ painter.rotate( angle );
+ double x;
+ if ( angle > 0 )
+ x = indent + cellRect.x() + d->textX;
+ else
+ x = indent + cellRect.x() + d->textX
+ - doc->unzoomItX((int) (( fm.descent() + fm.ascent() ) * sin( angle * M_PI / 180 )));
+ double y;
+ if ( angle > 0 )
+ y = cellRect.y() + d->textY;
+ else
+ y = cellRect.y() + d->textY + d->textHeight;
+ painter.drawText( doc->zoomItX( x * cos( angle * M_PI / 180 ) +
+ y * sin( angle * M_PI / 180 ) ),
+ doc->zoomItY( -x * sin( angle * M_PI / 180 ) +
+ y * cos( angle * M_PI / 180 ) ),
+ d->strOutText );
+ painter.rotate( -angle );
+ }
+ else if ( tmpMultiRow && !tmpVerticalText ) {
+ // Case 3: Multiple rows, but horizontal.
+
+ TQString t;
+ int i;
+ int pos = 0;
+ double dy = 0.0;
+ TQFontMetrics fm = painter.fontMetrics();
+ do {
+ i = d->strOutText.find( "\n", pos );
+ if ( i == -1 )
+ t = d->strOutText.mid( pos, d->strOutText.length() - pos );
+ else {
+ t = d->strOutText.mid( pos, i - pos );
+ pos = i + 1;
+ }
+
+ int align = effAlignX();
+ if ( format()->sheet()->getShowFormula()
+ && !( format()->sheet()->isProtected()
+ && format()->isHideFormula( d->column, d->row ) ) )
+ align = Format::Left;
+
+ // #### Torben: This looks duplicated for me
+ switch ( align ) {
+ case Format::Left:
+ d->textX = effLeftBorderPen( cellRef.x(), cellRef.y() ).width() + BORDER_SPACE;
+ break;
+
+ case Format::Right:
+ d->textX = cellRect.width() - BORDER_SPACE - doc->unzoomItX( fm.width( t ) )
+ - effRightBorderPen( cellRef.x(), cellRef.y() ).width();
+ break;
+
+ case Format::Center:
+ d->textX = ( cellRect.width() - doc->unzoomItX( fm.width( t ) ) ) / 2;
+ }
+
+ painter.drawText( doc->zoomItX( indent + cellRect.x() + d->textX ),
+ doc->zoomItY( cellRect.y() + d->textY + dy ), t );
+ dy += doc->unzoomItY( fm.descent() + fm.ascent() );
+ } while ( i != -1 );
+ }
+ else if ( tmpVerticalText && !d->strOutText.isEmpty() ) {
+ // Case 4: Vertical text.
+
+ TQString t;
+ int i = 0;
+ int len = 0;
+ double dy = 0.0;
+ TQFontMetrics fm = painter.fontMetrics();
+ do {
+ len = d->strOutText.length();
+ t = d->strOutText.at( i );
+ painter.drawText( doc->zoomItX( indent + cellRect.x() + d->textX ),
+ doc->zoomItY( cellRect.y() + d->textY + dy ), t );
+ dy += doc->unzoomItY( fm.descent() + fm.ascent() );
+ i++;
+ } while ( i != len );
+ }
+
+ // Check for too short cell and set the outText for future reference.
+ if ( testFlag( Flag_CellTooShortX ) ) {
+ d->strOutText = tmpText;
+ d->textHeight = tmpHeight;
+ d->textWidth = tmpWidth;
+ }
+
+ if ( format()->sheet()->getHideZero() && value().isNumber()
+ && value().asFloat() == 0 )
+ d->strOutText = tmpText;
+
+ if ( colFormat->isHide() || ( cellRect.height() <= 2 ) )
+ d->strOutText = tmpText;
+}
+
+
+// Paint page borders on the page. Only do this on the screen.
+//
+void Cell::paintPageBorders( TQPainter& painter,
+ const KoRect &cellRect,
+ const TQPoint &cellRef,
+ bool paintBorderRight,
+ bool paintBorderBottom )
+{
+ // Not screen? Return immediately.
+ if ( painter.device()->isExtDev() )
+ return;
+
+ if ( ! format()->sheet()->isShowPageBorders() )
+ return;
+
+ SheetPrint* print = format()->sheet()->print();
+
+ Sheet::LayoutDirection sheetDir = format()->sheet()->layoutDirection();
+
+ Doc* doc = sheet()->doc();
+ int zcellRect_left = doc->zoomItX (cellRect.left());
+ int zcellRect_right = doc->zoomItX (cellRect.right());
+ int zcellRect_top = doc->zoomItY (cellRect.top());
+ int zcellRect_bottom = doc->zoomItY (cellRect.bottom());
+
+ // Draw page borders
+
+ if ( cellRef.x() >= print->printRange().left()
+ && cellRef.x() <= print->printRange().right() + 1
+ && cellRef.y() >= print->printRange().top()
+ && cellRef.y() <= print->printRange().bottom() + 1 )
+ {
+ if ( print->isOnNewPageX( cellRef.x() )
+ && cellRef.y() <= print->printRange().bottom() )
+ {
+ painter.setPen( sheet()->doc()->pageBorderColor() );
+
+ if ( sheetDir == Sheet::RightToLeft )
+ painter.drawLine( zcellRect_right, zcellRect_top,
+ zcellRect_right, zcellRect_bottom );
+ else
+ painter.drawLine( zcellRect_left, zcellRect_top,
+ zcellRect_left, zcellRect_bottom );
+ }
+
+ if ( print->isOnNewPageY( cellRef.y() ) &&
+ ( cellRef.x() <= print->printRange().right() ) )
+ {
+ painter.setPen( sheet()->doc()->pageBorderColor() );
+ painter.drawLine( zcellRect_left, zcellRect_top,
+ zcellRect_right, zcellRect_top );
+ }
+
+ if ( paintBorderRight ) {
+ if ( print->isOnNewPageX( cellRef.x() + 1 )
+ && cellRef.y() <= print->printRange().bottom() ) {
+ painter.setPen( sheet()->doc()->pageBorderColor() );
+
+ if ( sheetDir == Sheet::RightToLeft )
+ painter.drawLine( zcellRect_left, zcellRect_top,
+ zcellRect_left, zcellRect_bottom );
+ else
+ painter.drawLine( zcellRect_right, zcellRect_top,
+ zcellRect_right, zcellRect_bottom );
+ }
+ }
+
+ if ( paintBorderBottom ) {
+ if ( print->isOnNewPageY( cellRef.y() + 1 )
+ && cellRef.x() <= print->printRange().right() ) {
+ painter.setPen( sheet()->doc()->pageBorderColor() );
+ painter.drawLine( zcellRect_left, zcellRect_bottom,
+ zcellRect_right, zcellRect_bottom );
+ }
+ }
+ }
+}
+
+
+// Paint the cell borders.
+//
+void Cell::paintCellBorders( TQPainter& painter, const KoRect& rect,
+ const KoRect &cellRect,
+ const TQPoint &cellRef,
+ bool paintRight, bool paintBottom,
+ bool paintLeft, bool paintTop,
+ TQPen & _rightPen, TQPen & _bottomPen,
+ TQPen & _leftPen, TQPen & _topPen )
+{
+
+ //Sanity check: If we are not painting any of the borders then the function
+ //really shouldn't be called at all.
+ if ( (!paintLeft) && (!paintRight) && (!paintTop) && (!paintBottom) )
+ return;
+
+ Doc * doc = sheet()->doc();
+
+ Sheet::LayoutDirection sheetDir = format()->sheet()->layoutDirection();
+
+ // compute zoomed rectangles
+ // I don't use KoRect, because that ends up producing lots of warnings
+ // about double->int conversions in calls to painter.drawLine
+ int zrect_left (doc->zoomItX (rect.left()));
+ int zrect_right (doc->zoomItX (rect.right()));
+ int zrect_top (doc->zoomItY (rect.top()));
+ int zrect_bottom (doc->zoomItY (rect.bottom()));
+ int zcellRect_left (doc->zoomItX (cellRect.left()));
+ int zcellRect_right (doc->zoomItX (cellRect.right()));
+ int zcellRect_top (doc->zoomItY (cellRect.top()));
+ int zcellRect_bottom (doc->zoomItY (cellRect.bottom()));
+
+ /* we might not paint some borders if this cell is merged with another in
+ that direction
+ bool paintLeft = paintBorderLeft;
+ bool paintRight = paintBorderRight;
+ bool paintTop = paintBorderTop;
+ bool paintBottom = paintBorderBottom;
+ */
+
+ // paintRight = paintRight && ( extraXCells() == 0 );
+ // paintBottom = paintBottom && ( d->extra()->extraYCells() == 0 );
+
+ if (d->hasExtra()) {
+ TQValueList<Cell*>::const_iterator it = d->extra()->obscuringCells.begin();
+ TQValueList<Cell*>::const_iterator end = d->extra()->obscuringCells.end();
+ for ( ; it != end; ++it ) {
+ Cell* cell = *it;
+
+ int xDiff = cellRef.x() - cell->column();
+ int yDiff = cellRef.y() - cell->row();
+ paintLeft = paintLeft && xDiff == 0;
+ paintTop = paintTop && yDiff == 0;
+
+ // Paint the border(s) if either this one should or if we have a
+ // merged cell with this cell as its border.
+ paintRight = paintRight && cell->mergedXCells() == xDiff;
+ paintBottom = paintBottom && cell->mergedYCells() == yDiff;
+ }
+ }
+
+ // Must create copies of these since otherwise the zoomIt()
+ // operation will be performed on them repeatedly.
+ TQPen leftPen( _leftPen );
+ TQPen rightPen( _rightPen );
+ TQPen topPen( _topPen );
+ TQPen bottomPen( _bottomPen );
+
+ // Determine the pens that should be used for drawing
+ // the borders.
+ //
+ int left_penWidth = TQMAX( 1, doc->zoomItX( leftPen.width() ) );
+ int right_penWidth = TQMAX( 1, doc->zoomItX( rightPen.width() ) );
+ int top_penWidth = TQMAX( 1, doc->zoomItY( topPen.width() ) );
+ int bottom_penWidth = TQMAX( 1, doc->zoomItY( bottomPen.width() ) );
+
+ leftPen.setWidth( left_penWidth );
+ rightPen.setWidth( right_penWidth );
+ topPen.setWidth( top_penWidth );
+ bottomPen.setWidth( bottom_penWidth );
+
+ if ( paintLeft && leftPen.style() != TQt::NoPen ) {
+ int top = ( TQMAX( 0, -1 + top_penWidth ) ) / 2 +
+ ( ( TQMAX( 0, -1 + top_penWidth ) ) % 2 );
+ int bottom = ( TQMAX( 0, -1 + bottom_penWidth ) ) / 2 + 1;
+
+ painter.setPen( leftPen );
+
+ //kdDebug(36001) << " painting left border of cell " << name() << endl;
+
+ // If we are on paper printout, we limit the length of the lines.
+ // On paper, we always have full cells, on screen not.
+ if ( painter.device()->isExtDev() ) {
+ // FIXME: There is probably Cut&Paste bugs here as well as below.
+ // The TQMIN/TQMAX and left/right pairs don't really make sense.
+ //
+ // UPDATE: In fact, most of these TQMIN/TQMAX combinations
+ // are TOTALLY BOGUS. For one thing, the idea
+ // that we always have full cells on paper is wrong
+ // since we can have embedded sheets in e.g. kword,
+ // and those can be arbitrarily clipped. WE HAVE TO
+ // REVISE THIS WHOLE BORDER PAINTING SECTION!
+ //
+ if ( sheetDir == Sheet::RightToLeft )
+ painter.drawLine( TQMIN( zrect_right, zcellRect_right ),
+ TQMAX( zrect_top, zcellRect_top - top ),
+ TQMIN( zrect_right, zcellRect_right ),
+ TQMIN( zrect_bottom, zcellRect_bottom + bottom ) );
+ else
+ painter.drawLine( TQMAX( zrect_left, zcellRect_left ),
+ TQMAX( zrect_top, zcellRect_top - top ),
+ TQMAX( zrect_left, zcellRect_left ),
+ TQMIN( zrect_bottom, zcellRect_bottom + bottom ) );
+ }
+ else {
+ if ( sheetDir == Sheet::RightToLeft )
+ painter.drawLine( zcellRect_right,
+ zcellRect_top - top,
+ zcellRect_right,
+ zcellRect_bottom + bottom );
+ else
+ painter.drawLine( zcellRect_left,
+ zcellRect_top - top,
+ zcellRect_left,
+ zcellRect_bottom + bottom );
+ }
+ }
+
+ if ( paintRight && rightPen.style() != TQt::NoPen ) {
+ int top = ( TQMAX( 0, -1 + top_penWidth ) ) / 2 +
+ ( ( TQMAX( 0, -1 + top_penWidth ) ) % 2 );
+ int bottom = ( TQMAX( 0, -1 + bottom_penWidth ) ) / 2 + 1;
+
+ painter.setPen( rightPen );
+
+ //kdDebug(36001) << " painting right border of cell " << name() << endl;
+
+ // If we are on paper printout, we limit the length of the lines.
+ // On paper, we always have full cells, on screen not.
+ if ( painter.device()->isExtDev() ) {
+ if ( sheetDir == Sheet::RightToLeft )
+ painter.drawLine( TQMAX( zrect_left, zcellRect_left ),
+ TQMAX( zrect_top, zcellRect_top - top ),
+ TQMAX( zrect_left, zcellRect_left ),
+ TQMIN( zrect_bottom, zcellRect_bottom + bottom ) );
+ else {
+ // FIXME: This is the way all these things should look.
+ // Make it so.
+ //
+ // Only print the right border if it is visible.
+ if ( zcellRect_right <= zrect_right + right_penWidth / 2)
+ painter.drawLine( zcellRect_right,
+ TQMAX( zrect_top, zcellRect_top - top ),
+ zcellRect_right,
+ TQMIN( zrect_bottom, zcellRect_bottom + bottom ) );
+ }
+ }
+ else {
+ if ( sheetDir == Sheet::RightToLeft )
+ painter.drawLine( zcellRect_left,
+ zcellRect_top - top,
+ zcellRect_left,
+ zcellRect_bottom + bottom );
+ else
+ painter.drawLine( zcellRect_right,
+ zcellRect_top - top,
+ zcellRect_right,
+ zcellRect_bottom + bottom );
+ }
+ }
+
+ if ( paintTop && topPen.style() != TQt::NoPen ) {
+ painter.setPen( topPen );
+
+ //kdDebug(36001) << " painting top border of cell " << name()
+ // << " [" << zcellRect_left << "," << zcellRect_right
+ // << ": " << zcellRect_right - zcellRect_left << "]" << endl;
+
+ // If we are on paper printout, we limit the length of the lines.
+ // On paper, we always have full cells, on screen not.
+ if ( painter.device()->isExtDev() ) {
+ if ( zcellRect_top >= zrect_top + top_penWidth / 2)
+ painter.drawLine( TQMAX( zrect_left, zcellRect_left ),
+ zcellRect_top,
+ TQMIN( zrect_right, zcellRect_right ),
+ zcellRect_top );
+ }
+ else {
+ painter.drawLine( zcellRect_left, zcellRect_top,
+ zcellRect_right, zcellRect_top );
+ }
+ }
+
+ if ( paintBottom && bottomPen.style() != TQt::NoPen ) {
+ painter.setPen( bottomPen );
+
+ //kdDebug(36001) << " painting bottom border of cell " << name()
+ // << " [" << zcellRect_left << "," << zcellRect_right
+ // << ": " << zcellRect_right - zcellRect_left << "]" << endl;
+
+ // If we are on paper printout, we limit the length of the lines.
+ // On paper, we always have full cells, on screen not.
+ if ( painter.device()->isExtDev() ) {
+ if ( zcellRect_bottom <= zrect_bottom + bottom_penWidth / 2)
+ painter.drawLine( TQMAX( zrect_left, zcellRect_left ),
+ zcellRect_bottom,
+ TQMIN( zrect_right, zcellRect_right ),
+ zcellRect_bottom );
+ }
+ else {
+ painter.drawLine( zcellRect_left, zcellRect_bottom,
+ zcellRect_right, zcellRect_bottom );
+ }
+ }
+
+ // FIXME: Look very closely at when the following code is really needed.
+ // I can't really see any case, but I might be wrong.
+ // Since the code below is buggy, and incredibly complex,
+ // I am currently disabling it. If somebody wants to enable
+ // it again, then please also solve bug 68977: "Embedded KSpread
+ // document printing problem" at the same time.
+ return;
+
+#if 0
+ // Look at the cells on our corners. It may happen that we
+ // just erased parts of their borders corner, so we might need
+ // to repaint these corners.
+ //
+ TQPen vert_pen, horz_pen;
+ int vert_penWidth, horz_penWidth;
+
+ // Some useful referenses.
+ Cell *cell_north = format()->sheet()->cellAt( cellRef.x(),
+ cellRef.y() - 1 );
+ Cell *cell_northwest = format()->sheet()->cellAt( cellRef.x() - 1,
+ cellRef.y() - 1 );
+ Cell *cell_west = format()->sheet()->cellAt( cellRef.x() - 1,
+ cellRef.y() );
+ Cell *cell_northeast = format()->sheet()->cellAt( cellRef.x() + 1,
+ cellRef.y() - 1 );
+ Cell *cell_east = format()->sheet()->cellAt( cellRef.x() + 1,
+ cellRef.y() );
+ Cell *cell_south = format()->sheet()->cellAt( cellRef.x(),
+ cellRef.y() + 1 );
+ Cell *cell_southwest = format()->sheet()->cellAt( cellRef.x() - 1,
+ cellRef.y() + 1 );
+ Cell *cell_southeast = format()->sheet()->cellAt( cellRef.x() + 1,
+ cellRef.y() + 1 );
+
+ // Fix the borders which meet at the top left corner
+ if ( cell_north->effLeftBorderValue( cellRef.x(), cellRef.y() - 1 )
+ >= cell_northwest->effRightBorderValue( cellRef.x() - 1, cellRef.y() - 1 ) )
+ vert_pen = cell_north->effLeftBorderPen( cellRef.x(), cellRef.y() - 1 );
+ else
+ vert_pen = cell_northwest->effRightBorderPen( cellRef.x() - 1,
+ cellRef.y() - 1 );
+
+ vert_penWidth = TQMAX( 1, doc->zoomItX( vert_pen.width() ) );
+ vert_pen.setWidth( vert_penWidth );
+
+ if ( vert_pen.style() != TQt::NoPen ) {
+ if ( cell_west->effTopBorderValue( cellRef.x() - 1, cellRef.y() )
+ >= cell_northwest->effBottomBorderValue( cellRef.x() - 1, cellRef.y() - 1 ) )
+ horz_pen = cell_west->effTopBorderPen( cellRef.x() - 1, cellRef.y() );
+ else
+ horz_pen = cell_northwest->effBottomBorderPen( cellRef.x() - 1,
+ cellRef.y() - 1 );
+
+ horz_penWidth = TQMAX( 1, doc->zoomItY( horz_pen.width() ) );
+ int bottom = ( TQMAX( 0, -1 + horz_penWidth ) ) / 2 + 1;
+
+ painter.setPen( vert_pen );
+ // If we are on paper printout, we limit the length of the lines.
+ // On paper, we always have full cells, on screen not.
+ if ( painter.device()->isExtDev() ) {
+ if ( sheetDir == Sheet::RightToLeft )
+ painter.drawLine( TQMAX( zrect_left, zcellRect_right ),
+ TQMAX( zrect_top, zcellRect_top ),
+ TQMIN( zrect_right, zcellRect_right ),
+ TQMIN( zrect_bottom, zcellRect_top + bottom ) );
+ else
+ painter.drawLine( TQMAX( zrect_left, zcellRect_left ),
+ TQMAX( zrect_top, zcellRect_top ),
+ TQMIN( zrect_right, zcellRect_left ),
+ TQMIN( zrect_bottom, zcellRect_top + bottom ) );
+ }
+ else {
+ if ( sheetDir == Sheet::RightToLeft )
+ painter.drawLine( zcellRect_right, zcellRect_top,
+ zcellRect_right, zcellRect_top + bottom );
+ else
+ painter.drawLine( zcellRect_left, zcellRect_top,
+ zcellRect_left, zcellRect_top + bottom );
+ }
+ }
+
+ // Fix the borders which meet at the top right corner
+ if ( cell_north->effRightBorderValue( cellRef.x(), cellRef.y() - 1 )
+ >= cell_northeast->effLeftBorderValue( cellRef.x() + 1,
+ cellRef.y() - 1 ) )
+ vert_pen = cell_north->effRightBorderPen( cellRef.x(), cellRef.y() - 1 );
+ else
+ vert_pen = cell_northeast->effLeftBorderPen( cellRef.x() + 1,
+ cellRef.y() - 1 );
+
+ // vert_pen = effRightBorderPen( cellRef.x(), cellRef.y() - 1 );
+ vert_penWidth = TQMAX( 1, doc->zoomItX( vert_pen.width() ) );
+ vert_pen.setWidth( vert_penWidth );
+ if ( ( vert_pen.style() != TQt::NoPen ) && ( cellRef.x() < KS_colMax ) ) {
+ if ( cell_east->effTopBorderValue( cellRef.x() + 1, cellRef.y() )
+ >= cell_northeast->effBottomBorderValue( cellRef.x() + 1,
+ cellRef.y() - 1 ) )
+ horz_pen = cell_east->effTopBorderPen( cellRef.x() + 1, cellRef.y() );
+ else
+ horz_pen = cell_northeast->effBottomBorderPen( cellRef.x() + 1,
+ cellRef.y() - 1 );
+
+ // horz_pen = effTopBorderPen( cellRef.x() + 1, cellRef.y() );
+ horz_penWidth = TQMAX( 1, doc->zoomItY( horz_pen.width() ) );
+ int bottom = ( TQMAX( 0, -1 + horz_penWidth ) ) / 2 + 1;
+
+ painter.setPen( vert_pen );
+ //If we are on paper printout, we limit the length of the lines
+ //On paper, we always have full cells, on screen not
+ if ( painter.device()->isExtDev() ) {
+ if ( sheetDir == Sheet::RightToLeft )
+ painter.drawLine( TQMAX( zrect_left, zcellRect_left ),
+ TQMAX( zrect_top, zcellRect_top ),
+ TQMIN( zrect_right, zcellRect_left ),
+ TQMIN( zrect_bottom, zcellRect_top + bottom ) );
+ else
+ painter.drawLine( TQMAX( zrect_left, zcellRect_right ),
+ TQMAX( zrect_top, zcellRect_top ),
+ TQMIN( zrect_right, zcellRect_right ),
+ TQMIN( zrect_bottom, zcellRect_top + bottom ) );
+ }
+ else {
+ if ( sheetDir == Sheet::RightToLeft )
+ painter.drawLine( zcellRect_left, zcellRect_top,
+ zcellRect_left, zcellRect_top + bottom );
+ else
+ painter.drawLine( zcellRect_right, zcellRect_top,
+ zcellRect_right, zcellRect_top + bottom );
+ }
+ }
+
+ // Bottom
+ if ( cellRef.y() < KS_rowMax ) {
+ // Fix the borders which meet at the bottom left corner
+ if ( cell_south->effLeftBorderValue( cellRef.x(), cellRef.y() + 1 )
+ >= cell_southwest->effRightBorderValue( cellRef.x() - 1,
+ cellRef.y() + 1 ) )
+ vert_pen = cell_south->effLeftBorderPen( cellRef.x(), cellRef.y() + 1 );
+ else
+ vert_pen = cell_southwest->effRightBorderPen( cellRef.x() - 1,
+ cellRef.y() + 1 );
+
+ // vert_pen = effLeftBorderPen( cellRef.x(), cellRef.y() + 1 );
+ vert_penWidth = TQMAX( 1, doc->zoomItY( vert_pen.width() ) );
+ vert_pen.setWidth( vert_penWidth );
+ if ( vert_pen.style() != TQt::NoPen ) {
+ if ( cell_west->effBottomBorderValue( cellRef.x() - 1, cellRef.y() )
+ >= cell_southwest->effTopBorderValue( cellRef.x() - 1,
+ cellRef.y() + 1 ) )
+ horz_pen = cell_west->effBottomBorderPen( cellRef.x() - 1,
+ cellRef.y() );
+ else
+ horz_pen = cell_southwest->effTopBorderPen( cellRef.x() - 1,
+ cellRef.y() + 1 );
+
+ // horz_pen = effBottomBorderPen( cellRef.x() - 1, cellRef.y() );
+ horz_penWidth = TQMAX( 1, doc->zoomItX( horz_pen.width() ) );
+ int bottom = ( TQMAX( 0, -1 + horz_penWidth ) ) / 2;
+
+ painter.setPen( vert_pen );
+ // If we are on paper printout, we limit the length of the lines.
+ // On paper, we always have full cells, on screen not.
+ if ( painter.device()->isExtDev() ) {
+ if ( sheetDir == Sheet::RightToLeft )
+ painter.drawLine( TQMAX( zrect_left, zcellRect_right ),
+ TQMAX( zrect_top, zcellRect_bottom - bottom ),
+ TQMIN( zrect_right, zcellRect_right ),
+ TQMIN( zrect_bottom, zcellRect_bottom ) );
+ else
+ painter.drawLine( TQMAX( zrect_left, zcellRect_left ),
+ TQMAX( zrect_top, zcellRect_bottom - bottom ),
+ TQMIN( zrect_right, zcellRect_left ),
+ TQMIN( zrect_bottom, zcellRect_bottom ) );
+ }
+ else {
+ if ( sheetDir == Sheet::RightToLeft )
+ painter.drawLine( zcellRect_right, zcellRect_bottom - bottom,
+ zcellRect_right, zcellRect_bottom );
+ else
+ painter.drawLine( zcellRect_left, zcellRect_bottom - bottom,
+ zcellRect_left, zcellRect_bottom );
+ }
+ }
+
+ // Fix the borders which meet at the bottom right corner
+ if ( cell_south->effRightBorderValue( cellRef.x(), cellRef.y() + 1 )
+ >= cell_southeast->effLeftBorderValue( cellRef.x() + 1,
+ cellRef.y() + 1 ) )
+ vert_pen = cell_south->effRightBorderPen( cellRef.x(), cellRef.y() + 1 );
+ else
+ vert_pen = cell_southeast->effLeftBorderPen( cellRef.x() + 1,
+ cellRef.y() + 1 );
+
+ // vert_pen = effRightBorderPen( cellRef.x(), cellRef.y() + 1 );
+ vert_penWidth = TQMAX( 1, doc->zoomItY( vert_pen.width() ) );
+ vert_pen.setWidth( vert_penWidth );
+ if ( ( vert_pen.style() != TQt::NoPen ) && ( cellRef.x() < KS_colMax ) ) {
+ if ( cell_east ->effBottomBorderValue( cellRef.x() + 1, cellRef.y() )
+ >= cell_southeast->effTopBorderValue( cellRef.x() + 1,
+ cellRef.y() + 1 ) )
+
+ horz_pen = format()->sheet()->cellAt( cellRef.x() + 1, cellRef.y() )
+ ->effBottomBorderPen( cellRef.x() + 1, cellRef.y() );
+ else
+ horz_pen = format()->sheet()->cellAt( cellRef.x() + 1, cellRef.y() + 1 )
+ ->effTopBorderPen( cellRef.x() + 1, cellRef.y() + 1 );
+
+ // horz_pen = effBottomBorderPen( cellRef.x() + 1, cellRef.y() );
+ horz_penWidth = TQMAX( 1, doc->zoomItX( horz_pen.width() ) );
+ int bottom = ( TQMAX( 0, -1 + horz_penWidth ) ) / 2;
+
+ painter.setPen( vert_pen );
+ // If we are on paper printout, we limit the length of the lines.
+ // On paper, we always have full cells, on screen not.
+ if ( painter.device()->isExtDev() ) {
+ if ( sheetDir == Sheet::RightToLeft )
+ painter.drawLine( TQMAX( zrect_left, zcellRect_left ),
+ TQMAX( zrect_top, zcellRect_bottom - bottom ),
+ TQMIN( zrect_right, zcellRect_left ),
+ TQMIN( zrect_bottom, zcellRect_bottom ) );
+ else
+ painter.drawLine( TQMAX( zrect_left, zcellRect_right ),
+ TQMAX( zrect_top, zcellRect_bottom - bottom ),
+ TQMIN( zrect_right, zcellRect_right ),
+ TQMIN( zrect_bottom, zcellRect_bottom ) );
+ }
+ else {
+ if ( sheetDir == Sheet::RightToLeft )
+ painter.drawLine( zcellRect_left, zcellRect_bottom - bottom,
+ zcellRect_left, zcellRect_bottom );
+ else
+ painter.drawLine( zcellRect_right, zcellRect_bottom - bottom,
+ zcellRect_right, zcellRect_bottom );
+ }
+ }
+ }
+ #endif
+}
+
+
+// Paint diagonal lines through the cell.
+//
+void Cell::paintCellDiagonalLines( TQPainter& painter,
+ const KoRect &cellRect,
+ const TQPoint &cellRef )
+{
+ if ( isPartOfMerged() )
+ return;
+
+ Doc* doc = sheet()->doc();
+
+ if ( effFallDiagonalPen( cellRef.x(), cellRef.y() ).style() != TQt::NoPen ) {
+ painter.setPen( effFallDiagonalPen( cellRef.x(), cellRef.y() ) );
+ painter.drawLine( doc->zoomItX( cellRect.x() ),
+ doc->zoomItY( cellRect.y() ),
+ doc->zoomItX( cellRect.right() ),
+ doc->zoomItY( cellRect.bottom() ) );
+ }
+
+ if ( effGoUpDiagonalPen( cellRef.x(), cellRef.y() ).style() != TQt::NoPen ) {
+ painter.setPen( effGoUpDiagonalPen( cellRef.x(), cellRef.y() ) );
+ painter.drawLine( doc->zoomItX( cellRect.x() ),
+ doc->zoomItY( cellRect.bottom() ),
+ doc->zoomItX( cellRect.right() ),
+ doc->zoomItY( cellRect.y() ) );
+ }
+}
+
+
+// End of Painting
+// ================================================================
+
+
+int Cell::defineAlignX()
+{
+ int a = format()->align( column(), row() );
+ if ( a == Format::Undefined )
+ {
+ //numbers should be right-aligned by default, as well as BiDi text
+ if ((formatType() == Text_format) || value().isString())
+ a = (d->strOutText.isRightToLeft()) ?
+ Format::Right : Format::Left;
+ else {
+ Value val = value();
+ while (val.isArray()) val = val.element (0, 0);
+ if (val.isBoolean() || val.isNumber())
+ a = Format::Right;
+ else
+ a = Format::Left;
+ }
+ }
+ return a;
+}
+
+int Cell::effAlignX()
+{
+ if ( d->hasExtra() && d->extra()->conditions
+ && d->extra()->conditions->matchedStyle()
+ && d->extra()->conditions->matchedStyle()->hasFeature( Style::SAlignX, true ) )
+ return d->extra()->conditions->matchedStyle()->alignX();
+
+ return defineAlignX();
+}
+
+// Cut strOutText, so that it only holds the part that can be displayed.
+//
+// Used in paintText().
+//
+
+TQString Cell::textDisplaying( TQPainter &_painter )
+{
+ TQFontMetrics fm = _painter.fontMetrics();
+ int a = format()->align( column(), row() );
+
+ bool isNumeric = value().isNumber();
+
+ if ( !format()->verticalText( column(),row() ) ) {
+ // Non-vertical text: the ordinary case.
+
+ // Not enough space but align to left
+ double len = 0.0;
+ int extraXCells = d->hasExtra() ? d->extra()->extraXCells : 0;
+
+ for ( int i = column(); i <= column() + extraXCells; i++ ) {
+ ColumnFormat *cl2 = format()->sheet()->columnFormat( i );
+ len += cl2->dblWidth() - 1.0; //-1.0 because the pixel in between 2 cells is shared between both cells
+ }
+
+ TQString tmp;
+ double tmpIndent = 0.0;
+ if ( !isEmpty() )
+ tmpIndent = format()->getIndent( column(), row() );
+
+ // Start out with the whole text, cut one character at a time, and
+ // when the text finally fits, return it.
+ for ( int i = d->strOutText.length(); i != 0; i-- )
+ {
+ //Note that numbers are always treated as left-aligned since if we have to cut digits off, they should
+ //always be the least significant ones at the end of the string
+ if ( a == Format::Left || a == Format::Undefined || isNumeric)
+ tmp = d->strOutText.left(i);
+ else if ( a == Format::Right)
+ tmp = d->strOutText.right(i);
+ else
+ tmp = d->strOutText.mid( ( d->strOutText.length() - i ) / 2, i);
+
+ if (isNumeric)
+ {
+ //For numeric values, we can cut off digits after the decimal point to make it fit,
+ //but not the integer part of the number.
+ //If this number still contains a fraction part then we don't need to do anything, if we have run
+ //out of space to fit even the integer part of the number then display #########
+ //TODO Perhaps try to display integer part in standard form if there is not enough room for it?
+
+ if (!tmp.contains('.'))
+ d->strOutText=TQString().fill('#',20);
+ }
+
+ // 4 equal length of red triangle +1 point.
+ if ( format()->sheet()->doc()->unzoomItX( fm.width( tmp ) ) + tmpIndent
+ < len - 4.0 - 1.0 )
+ {
+ if ( format()->getAngle( column(), row() ) != 0 )
+ {
+ TQString tmp2;
+ RowFormat *rl = format()->sheet()->rowFormat( row() );
+ if ( d->textHeight > rl->dblHeight() )
+ {
+ for ( int j = d->strOutText.length(); j != 0; j-- )
+ {
+ tmp2 = d->strOutText.left( j );
+ if ( format()->sheet()->doc()->unzoomItY( fm.width( tmp2 ) ) < rl->dblHeight() - 1.0 )
+ {
+ return d->strOutText.left( TQMIN( tmp.length(), tmp2.length() ) );
+ }
+ }
+ }
+ else
+ return tmp;
+
+ }
+ else
+ return tmp;
+ }
+ }
+ return TQString( "" );
+ }
+ else if ( format()->verticalText( column(), row() ) ) {
+ // Vertical text.
+
+ RowFormat *rl = format()->sheet()->rowFormat( row() );
+ double tmpIndent = 0.0;
+
+ // Not enough space but align to left.
+ double len = 0.0;
+ int extraXCells = d->hasExtra() ? d->extra()->extraXCells : 0;
+
+ for ( int i = column(); i <= column() + extraXCells; i++ ) {
+ ColumnFormat *cl2 = format()->sheet()->columnFormat( i );
+
+ // -1.0 because the pixel in between 2 cells is shared between both cells
+ len += cl2->dblWidth() - 1.0;
+ }
+
+ if ( !isEmpty() )
+ tmpIndent = format()->getIndent( column(), row() );
+
+ if ( ( d->textWidth + tmpIndent > len ) || d->textWidth == 0.0 )
+ return TQString( "" );
+
+ for ( int i = d->strOutText.length(); i != 0; i-- ) {
+ if ( format()->sheet()->doc()->unzoomItY( fm.ascent() + fm.descent() ) * i
+ < rl->dblHeight() - 1.0 )
+ return d->strOutText.left( i );
+ }
+
+ return TQString( "" );
+ }
+
+ ColumnFormat *cl = format()->sheet()->columnFormat( column() );
+ double w = cl->dblWidth();
+
+ if ( d->hasExtra() && (d->extra()->extraWidth != 0.0) )
+ w = d->extra()->extraWidth;
+
+ TQString tmp;
+ for ( int i = d->strOutText.length(); i != 0; i-- ) {
+ tmp = d->strOutText.left( i );
+
+ // 4 equals length of red triangle +1 pixel
+ if ( format()->sheet()->doc()->unzoomItX( fm.width( tmp ) ) < w - 4.0 - 1.0 )
+ return tmp;
+ }
+
+ return TQString();
+}
+
+
+double Cell::dblWidth( int _col, const Canvas *_canvas ) const
+{
+ if ( _col < 0 )
+ _col = d->column;
+
+ if ( _canvas )
+ {
+ if ( testFlag(Flag_Merged) )
+ return d->extra()->extraWidth;
+
+ const ColumnFormat *cl = format()->sheet()->columnFormat( _col );
+ return cl->dblWidth( _canvas );
+ }
+
+ if ( testFlag(Flag_Merged) )
+ return d->extra()->extraWidth;
+
+ const ColumnFormat *cl = format()->sheet()->columnFormat( _col );
+ return cl->dblWidth();
+}
+
+int Cell::width( int _col, const Canvas *_canvas ) const
+{
+ return int( dblWidth( _col, _canvas ) );
+}
+
+double Cell::dblHeight( int _row, const Canvas *_canvas ) const
+{
+ if ( _row < 0 )
+ _row = d->row;
+
+ if ( _canvas )
+ {
+ if ( testFlag(Flag_Merged) )
+ return d->extra()->extraHeight;
+
+ const RowFormat *rl = format()->sheet()->rowFormat( _row );
+ return rl->dblHeight( _canvas );
+ }
+
+ if ( testFlag(Flag_Merged) )
+ return d->extra()->extraHeight;
+
+ const RowFormat *rl = format()->sheet()->rowFormat( _row );
+ return rl->dblHeight();
+}
+
+int Cell::height( int _row, const Canvas *_canvas ) const
+{
+ return int( dblHeight( _row, _canvas ) );
+}
+
+///////////////////////////////////////////
+//
+// Misc Properties.
+// Reimplementation of Format methods.
+//
+///////////////////////////////////////////
+
+const TQBrush& Cell::backGroundBrush( int _col, int _row ) const
+{
+ if ( d->hasExtra() && (!d->extra()->obscuringCells.isEmpty()) )
+ {
+ const Cell* cell = d->extra()->obscuringCells.first();
+ return cell->backGroundBrush( cell->column(), cell->row() );
+ }
+
+ return format()->backGroundBrush( _col, _row );
+}
+
+const TQColor& Cell::bgColor( int _col, int _row ) const
+{
+ if ( d->hasExtra() && (!d->extra()->obscuringCells.isEmpty()) )
+ {
+ const Cell* cell = d->extra()->obscuringCells.first();
+ return cell->bgColor( cell->column(), cell->row() );
+ }
+
+ return format()->bgColor( _col, _row );
+}
+
+///////////////////////////////////////////
+//
+// Borders.
+// Reimplementation of Format methods.
+//
+///////////////////////////////////////////
+
+void Cell::setLeftBorderPen( const TQPen& p )
+{
+ if ( column() == 1 )
+ {
+ Cell* cell = format()->sheet()->cellAt( column() - 1, row() );
+ if ( cell && cell->format()->hasProperty( Format::PRightBorder )
+ && format()->sheet()->cellAt( column(), row() ) == this )
+ cell->format()->clearProperty( Format::PRightBorder );
+ }
+
+ format()->setLeftBorderPen( p );
+}
+
+void Cell::setTopBorderPen( const TQPen& p )
+{
+ if ( row() == 1 )
+ {
+ Cell* cell = format()->sheet()->cellAt( column(), row() - 1 );
+ if ( cell && cell->format()->hasProperty( Format::PBottomBorder )
+ && format()->sheet()->cellAt( column(), row() ) == this )
+ cell->format()->clearProperty( Format::PBottomBorder );
+ }
+ format()->setTopBorderPen( p );
+}
+
+void Cell::setRightBorderPen( const TQPen& p )
+{
+ Cell* cell = 0L;
+ if ( column() < KS_colMax )
+ cell = format()->sheet()->cellAt( column() + 1, row() );
+
+ if ( cell && cell->format()->hasProperty( Format::PLeftBorder )
+ && format()->sheet()->cellAt( column(), row() ) == this )
+ cell->format()->clearProperty( Format::PLeftBorder );
+
+ format()->setRightBorderPen( p );
+}
+
+void Cell::setBottomBorderPen( const TQPen& p )
+{
+ Cell* cell = 0L;
+ if ( row() < KS_rowMax )
+ cell = format()->sheet()->cellAt( column(), row() + 1 );
+
+ if ( cell && cell->format()->hasProperty( Format::PTopBorder )
+ && format()->sheet()->cellAt( column(), row() ) == this )
+ cell->format()->clearProperty( Format::PTopBorder );
+
+ format()->setBottomBorderPen( p );
+}
+
+const TQPen& Cell::rightBorderPen( int _col, int _row ) const
+{
+ if ( !format()->hasProperty( Format::PRightBorder ) && ( _col < KS_colMax ) )
+ {
+ Cell * cell = format()->sheet()->cellAt( _col + 1, _row );
+ if ( cell && cell->format()->hasProperty( Format::PLeftBorder ) )
+ return cell->leftBorderPen( _col + 1, _row );
+ }
+
+ return format()->rightBorderPen( _col, _row );
+}
+
+const TQPen& Cell::leftBorderPen( int _col, int _row ) const
+{
+ if ( !format()->hasProperty( Format::PLeftBorder ) )
+ {
+ const Cell * cell = format()->sheet()->cellAt( _col - 1, _row );
+ if ( cell && cell->format()->hasProperty( Format::PRightBorder ) )
+ return cell->rightBorderPen( _col - 1, _row );
+ }
+
+ return format()->leftBorderPen( _col, _row );
+}
+
+const TQPen& Cell::bottomBorderPen( int _col, int _row ) const
+{
+ if ( !format()->hasProperty( Format::PBottomBorder ) && ( _row < KS_rowMax ) )
+ {
+ const Cell * cell = format()->sheet()->cellAt( _col, _row + 1 );
+ if ( cell && cell->format()->hasProperty( Format::PTopBorder ) )
+ return cell->topBorderPen( _col, _row + 1 );
+ }
+
+ return format()->bottomBorderPen( _col, _row );
+}
+
+const TQPen& Cell::topBorderPen( int _col, int _row ) const
+{
+ if ( !format()->hasProperty( Format::PTopBorder ) )
+ {
+ const Cell * cell = format()->sheet()->cellAt( _col, _row - 1 );
+ if ( cell->format()->hasProperty( Format::PBottomBorder ) )
+ return cell->bottomBorderPen( _col, _row - 1 );
+ }
+
+ return format()->topBorderPen( _col, _row );
+}
+
+const TQColor & Cell::effTextColor( int col, int row ) const
+{
+ if ( d->hasExtra() && d->extra()->conditions
+ && d->extra()->conditions->matchedStyle()
+ && d->extra()->conditions->matchedStyle()->hasFeature( Style::STextPen, true ) )
+ return d->extra()->conditions->matchedStyle()->pen().color();
+
+ return format()->textColor( col, row );
+}
+
+const TQPen& Cell::effLeftBorderPen( int col, int row ) const
+{
+ if ( isPartOfMerged() )
+ {
+ Cell * cell = d->extra()->obscuringCells.first();
+ return cell->effLeftBorderPen( cell->column(), cell->row() );
+ }
+
+ if ( d->hasExtra() && d->extra()->conditions
+ && d->extra()->conditions->matchedStyle()
+ && d->extra()->conditions->matchedStyle()->hasFeature( Style::SLeftBorder, true ) )
+ return d->extra()->conditions->matchedStyle()->leftBorderPen();
+
+ return leftBorderPen( col, row );
+}
+
+const TQPen& Cell::effTopBorderPen( int col, int row ) const
+{
+ if ( isPartOfMerged() )
+ {
+ Cell * cell = d->extra()->obscuringCells.first();
+ return cell->effTopBorderPen( cell->column(), cell->row() );
+ }
+
+ if ( d->hasExtra() && d->extra()->conditions
+ && d->extra()->conditions->matchedStyle()
+ && d->extra()->conditions->matchedStyle()->hasFeature( Style::STopBorder, true ) )
+ return d->extra()->conditions->matchedStyle()->topBorderPen();
+
+ return topBorderPen( col, row );
+}
+
+const TQPen& Cell::effRightBorderPen( int col, int row ) const
+{
+ if ( isPartOfMerged() )
+ {
+ Cell * cell = d->extra()->obscuringCells.first();
+ return cell->effRightBorderPen( cell->column(), cell->row() );
+ }
+
+ if ( d->hasExtra() && d->extra()->conditions
+ && d->extra()->conditions->matchedStyle()
+ && d->extra()->conditions->matchedStyle()->hasFeature( Style::SRightBorder, true ) )
+ return d->extra()->conditions->matchedStyle()->rightBorderPen();
+
+ return rightBorderPen( col, row );
+}
+
+const TQPen& Cell::effBottomBorderPen( int col, int row ) const
+{
+ if ( isPartOfMerged() )
+ {
+ Cell * cell = d->extra()->obscuringCells.first();
+ return cell->effBottomBorderPen( cell->column(), cell->row() );
+ }
+
+ if ( d->hasExtra() && d->extra()->conditions
+ && d->extra()->conditions->matchedStyle()
+ && d->extra()->conditions->matchedStyle()->hasFeature( Style::SBottomBorder, true ) )
+ return d->extra()->conditions->matchedStyle()->bottomBorderPen();
+
+ return bottomBorderPen( col, row );
+}
+
+const TQPen & Cell::effGoUpDiagonalPen( int col, int row ) const
+{
+ if ( d->hasExtra() && d->extra()->conditions
+ && d->extra()->conditions->matchedStyle()
+ && d->extra()->conditions->matchedStyle()->hasFeature( Style::SGoUpDiagonal, true ) )
+ return d->extra()->conditions->matchedStyle()->goUpDiagonalPen();
+
+ return format()->goUpDiagonalPen( col, row );
+}
+
+const TQPen & Cell::effFallDiagonalPen( int col, int row ) const
+{
+ if ( d->hasExtra() && d->extra()->conditions
+ && d->extra()->conditions->matchedStyle()
+ && d->extra()->conditions->matchedStyle()->hasFeature( Style::SFallDiagonal, true ) )
+ return d->extra()->conditions->matchedStyle()->fallDiagonalPen();
+
+ return format()->fallDiagonalPen( col, row );
+}
+
+uint Cell::effBottomBorderValue( int col, int row ) const
+{
+ if ( isPartOfMerged() )
+ {
+ Cell * cell = d->extra()->obscuringCells.first();
+ return cell->effBottomBorderValue( cell->column(), cell->row() );
+ }
+
+ if ( d->hasExtra() && d->extra()->conditions
+ && d->extra()->conditions->matchedStyle() )
+ return d->extra()->conditions->matchedStyle()->bottomPenValue();
+
+ return format()->bottomBorderValue( col, row );
+}
+
+uint Cell::effRightBorderValue( int col, int row ) const
+{
+ if ( isPartOfMerged() )
+ {
+ Cell * cell = d->extra()->obscuringCells.first();
+ return cell->effRightBorderValue( cell->column(), cell->row() );
+ }
+
+ if ( d->hasExtra() && d->extra()->conditions
+ && d->extra()->conditions->matchedStyle() )
+ return d->extra()->conditions->matchedStyle()->rightPenValue();
+
+ return format()->rightBorderValue( col, row );
+}
+
+uint Cell::effLeftBorderValue( int col, int row ) const
+{
+ if ( isPartOfMerged() )
+ {
+ Cell * cell = d->extra()->obscuringCells.first();
+ return cell->effLeftBorderValue( cell->column(), cell->row() );
+ }
+
+ if ( d->hasExtra() && d->extra()->conditions
+ && d->extra()->conditions->matchedStyle() )
+ return d->extra()->conditions->matchedStyle()->leftPenValue();
+
+ return format()->leftBorderValue( col, row );
+}
+
+uint Cell::effTopBorderValue( int col, int row ) const
+{
+ if ( isPartOfMerged() )
+ {
+ Cell * cell = d->extra()->obscuringCells.first();
+ return cell->effTopBorderValue( cell->column(), cell->row() );
+ }
+
+ if ( d->hasExtra() && d->extra()->conditions
+ && d->extra()->conditions->matchedStyle() )
+ return d->extra()->conditions->matchedStyle()->topPenValue();
+
+ return format()->topBorderValue( col, row );
+}
+
+///////////////////////////////////////////
+//
+// Precision
+//
+///////////////////////////////////////////
+
+void Cell::incPrecision()
+{
+ //TODO: This is ugly. Why not simply regenerate the text to display? Tomas
+
+ if ( !value().isNumber() )
+ return;
+ int tmpPreci = format()->precision( column(), row() );
+
+ if ( tmpPreci == -1 )
+ {
+ int pos = d->strOutText.find(decimal_point);
+ if ( pos == -1 )
+ pos = d->strOutText.find('.');
+ if ( pos == -1 )
+ format()->setPrecision(1);
+ else
+ {
+ int start = 0;
+ if ( d->strOutText.find('%') != -1 )
+ start = 2;
+ else if ( d->strOutText.find(locale()->currencySymbol()) == ((int)(d->strOutText.length()-locale()->currencySymbol().length())) )
+ start = locale()->currencySymbol().length() + 1;
+ else if ( (start=d->strOutText.find('E')) != -1 )
+ start = d->strOutText.length() - start;
+
+ //kdDebug(36001) << "start=" << start << " pos=" << pos << " length=" << d->strOutText.length() << endl;
+ format()->setPrecision( TQMAX( 0, (int)d->strOutText.length() - start - pos ) );
+ }
+ }
+ else if ( tmpPreci < 10 )
+ {
+ format()->setPrecision( ++tmpPreci );
+ }
+ setFlag(Flag_LayoutDirty);
+}
+
+void Cell::decPrecision()
+{
+ //TODO: This is ugly. Why not simply regenerate the text to display? Tomas
+
+ if ( !value().isNumber() )
+ return;
+ int preciTmp = format()->precision( column(), row() );
+// kdDebug(36001) << "decPrecision: tmpPreci = " << tmpPreci << endl;
+ if ( format()->precision(column(),row()) == -1 )
+ {
+ int pos = d->strOutText.find( decimal_point );
+ int start = 0;
+ if ( d->strOutText.find('%') != -1 )
+ start = 2;
+ else if ( d->strOutText.find(locale()->currencySymbol()) == ((int)(d->strOutText.length()-locale()->currencySymbol().length())) )
+ start = locale()->currencySymbol().length() + 1;
+ else if ( (start = d->strOutText.find('E')) != -1 )
+ start = d->strOutText.length() - start;
+ else
+ start = 0;
+
+ if ( pos == -1 )
+ return;
+
+ format()->setPrecision(d->strOutText.length() - pos - 2 - start);
+ // if ( preciTmp < 0 )
+ // format()->setPrecision( preciTmp );
+ }
+ else if ( preciTmp > 0 )
+ {
+ format()->setPrecision( --preciTmp );
+ }
+ setFlag( Flag_LayoutDirty );
+}
+
+//set numerical value
+//used in Sheet::setSeries (nowhere else yet)
+void Cell::setNumber( double number )
+{
+ setValue( Value( number ) );
+
+ d->strText.setNum( number );
+ setDisplayText(d->strText);
+ checkNumberFormat();
+}
+
+void Cell::setCellText( const TQString& _text, bool asText )
+{
+ // TQString ctext = _text;
+
+// (Tomas) is this trim necessary for anything ?
+// if( ctext.length() > 5000 )
+// ctext = ctext.left( 5000 );
+
+ // empty string ?
+ if (_text.length() == 0) {
+ d->strOutText = d->strText = "";
+ setValue (Value::empty());
+ return;
+ }
+
+ // as text ?
+ if (asText) {
+ d->strOutText = _text;
+ d->strText = _text;
+ setValue (Value (_text));
+
+ return;
+ }
+
+ TQString oldText = d->strText;
+ setDisplayText( _text );
+ if(!format()->sheet()->isLoading() && !testValidity() )
+ {
+ //reapply old value if action == stop
+ setDisplayText( oldText );
+ }
+}
+
+void Cell::setDisplayText( const TQString& _text )
+{
+ bool isLoading = format()->sheet()->isLoading();
+
+ if (!isLoading)
+ format()->sheet()->doc()->emitBeginOperation( false );
+
+ d->strText = _text;
+
+ /**
+ * A real formula "=A1+A2*3" was entered.
+ */
+ if ( !d->strText.isEmpty() && d->strText[0] == '=' )
+ {
+ setFlag(Flag_LayoutDirty);
+ setFlag(Flag_TextFormatDirty);
+
+ if ( !makeFormula() )
+ kdError(36001) << "ERROR: Syntax ERROR" << endl;
+ setCalcDirtyFlag ();
+ }
+
+ /**
+ * Some numeric value or a string.
+ */
+ else
+ {
+ // Find out what data type it is
+ checkTextInput();
+
+ setFlag(Flag_LayoutDirty);
+ setFlag(Flag_TextFormatDirty);
+ }
+
+ if ( !isLoading )
+ format()->sheet()->doc()->emitEndOperation( TQRect( d->column, d->row, 1, 1 ) );
+}
+
+void Cell::setLink( const TQString& link )
+{
+ d->extra()->link = link;
+
+ if( !link.isEmpty() && d->strText.isEmpty() )
+ setCellText( link );
+}
+
+TQString Cell::link() const
+{
+ return d->hasExtra() ? d->extra()->link : TQString();
+}
+
+void Cell::update()
+{
+ /* those obscuring us need to redo their layout cause they can't obscure us
+ now that we've got text.
+ This includes cells obscuring cells that we are obscuring
+ */
+ for (int x = d->column; x <= d->column + extraXCells(); x++)
+ {
+ for (int y = d->row; y <= d->row + extraYCells(); y++)
+ {
+ Cell* cell = format()->sheet()->cellAt(x,y);
+ cell->setLayoutDirtyFlag();
+ }
+ }
+
+ setCalcDirtyFlag();
+
+ /* TODO - is this a good place for this? */
+ updateChart(true);
+}
+
+bool Cell::testValidity() const
+{
+ bool valid = false;
+ if( d->hasExtra() && d->extra()->validity && d->extra()->validity->m_restriction != Restriction::None )
+ {
+ //fixme
+ if ( d->extra()->validity->allowEmptyCell && d->strText.isEmpty() )
+ return true;
+
+ if( value().isNumber() &&
+ (d->extra()->validity->m_restriction == Restriction::Number ||
+ (d->extra()->validity->m_restriction == Restriction::Integer &&
+ value().asFloat() == ceil(value().asFloat()))))
+ {
+ switch( d->extra()->validity->m_cond)
+ {
+ case Conditional::Equal:
+ valid = ( value().asFloat() - d->extra()->validity->valMin < DBL_EPSILON
+ && value().asFloat() - d->extra()->validity->valMin >
+ (0.0 - DBL_EPSILON));
+ break;
+ case Conditional::DifferentTo:
+ valid = !( ( value().asFloat() - d->extra()->validity->valMin < DBL_EPSILON
+ && value().asFloat() - d->extra()->validity->valMin >
+ (0.0 - DBL_EPSILON)) );
+ break;
+ case Conditional::Superior:
+ valid = ( value().asFloat() > d->extra()->validity->valMin);
+ break;
+ case Conditional::Inferior:
+ valid = ( value().asFloat() <d->extra()->validity->valMin);
+ break;
+ case Conditional::SuperiorEqual:
+ valid = ( value().asFloat() >= d->extra()->validity->valMin);
+ break;
+ case Conditional::InferiorEqual:
+ valid = (value().asFloat() <= d->extra()->validity->valMin);
+ break;
+ case Conditional::Between:
+ valid = ( value().asFloat() >= d->extra()->validity->valMin &&
+ value().asFloat() <= d->extra()->validity->valMax);
+ break;
+ case Conditional::Different:
+ valid = (value().asFloat() < d->extra()->validity->valMin ||
+ value().asFloat() > d->extra()->validity->valMax);
+ break;
+ default :
+ break;
+ }
+ }
+ else if(d->extra()->validity->m_restriction==Restriction::Text)
+ {
+ valid = value().isString();
+ }
+ else if ( d->extra()->validity->m_restriction == Restriction::List )
+ {
+ //test int value
+ if ( value().isString() && d->extra()->validity->listValidity.contains( value().asString() ) )
+ valid = true;
+ }
+ else if(d->extra()->validity->m_restriction==Restriction::TextLength)
+ {
+ if( value().isString() )
+ {
+ int len = d->strOutText.length();
+ switch( d->extra()->validity->m_cond)
+ {
+ case Conditional::Equal:
+ if (len == d->extra()->validity->valMin)
+ valid = true;
+ break;
+ case Conditional::DifferentTo:
+ if (len != d->extra()->validity->valMin)
+ valid = true;
+ break;
+ case Conditional::Superior:
+ if(len > d->extra()->validity->valMin)
+ valid = true;
+ break;
+ case Conditional::Inferior:
+ if(len < d->extra()->validity->valMin)
+ valid = true;
+ break;
+ case Conditional::SuperiorEqual:
+ if(len >= d->extra()->validity->valMin)
+ valid = true;
+ break;
+ case Conditional::InferiorEqual:
+ if(len <= d->extra()->validity->valMin)
+ valid = true;
+ break;
+ case Conditional::Between:
+ if(len >= d->extra()->validity->valMin && len <= d->extra()->validity->valMax)
+ valid = true;
+ break;
+ case Conditional::Different:
+ if(len <d->extra()->validity->valMin || len >d->extra()->validity->valMax)
+ valid = true;
+ break;
+ default :
+ break;
+ }
+ }
+ }
+ else if(d->extra()->validity->m_restriction == Restriction::Time && isTime())
+ {
+ switch( d->extra()->validity->m_cond)
+ {
+ case Conditional::Equal:
+ valid = (value().asTime() == d->extra()->validity->timeMin);
+ break;
+ case Conditional::DifferentTo:
+ valid = (value().asTime() != d->extra()->validity->timeMin);
+ break;
+ case Conditional::Superior:
+ valid = (value().asTime() > d->extra()->validity->timeMin);
+ break;
+ case Conditional::Inferior:
+ valid = (value().asTime() < d->extra()->validity->timeMin);
+ break;
+ case Conditional::SuperiorEqual:
+ valid = (value().asTime() >= d->extra()->validity->timeMin);
+ break;
+ case Conditional::InferiorEqual:
+ valid = (value().asTime() <= d->extra()->validity->timeMin);
+ break;
+ case Conditional::Between:
+ valid = (value().asTime() >= d->extra()->validity->timeMin &&
+ value().asTime() <= d->extra()->validity->timeMax);
+ break;
+ case Conditional::Different:
+ valid = (value().asTime() < d->extra()->validity->timeMin ||
+ value().asTime() > d->extra()->validity->timeMax);
+ break;
+ default :
+ break;
+
+ }
+ }
+ else if(d->extra()->validity->m_restriction == Restriction::Date && isDate())
+ {
+ switch( d->extra()->validity->m_cond)
+ {
+ case Conditional::Equal:
+ valid = (value().asDate() == d->extra()->validity->dateMin);
+ break;
+ case Conditional::DifferentTo:
+ valid = (value().asDate() != d->extra()->validity->dateMin);
+ break;
+ case Conditional::Superior:
+ valid = (value().asDate() > d->extra()->validity->dateMin);
+ break;
+ case Conditional::Inferior:
+ valid = (value().asDate() < d->extra()->validity->dateMin);
+ break;
+ case Conditional::SuperiorEqual:
+ valid = (value().asDate() >= d->extra()->validity->dateMin);
+ break;
+ case Conditional::InferiorEqual:
+ valid = (value().asDate() <= d->extra()->validity->dateMin);
+ break;
+ case Conditional::Between:
+ valid = (value().asDate() >= d->extra()->validity->dateMin &&
+ value().asDate() <= d->extra()->validity->dateMax);
+ break;
+ case Conditional::Different:
+ valid = (value().asDate() < d->extra()->validity->dateMin ||
+ value().asDate() > d->extra()->validity->dateMax);
+ break;
+ default :
+ break;
+
+ }
+ }
+ }
+ else
+ {
+ valid= true;
+ }
+
+ if(!valid &&d->extra()->validity != NULL && d->extra()->validity->displayMessage)
+ {
+ switch (d->extra()->validity->m_action )
+ {
+ case Action::Stop:
+ KMessageBox::error((TQWidget*)0L, d->extra()->validity->message,
+ d->extra()->validity->title);
+ break;
+ case Action::Warning:
+ KMessageBox::warningYesNo((TQWidget*)0L, d->extra()->validity->message,
+ d->extra()->validity->title);
+ break;
+ case Action::Information:
+ KMessageBox::information((TQWidget*)0L, d->extra()->validity->message,
+ d->extra()->validity->title);
+ break;
+ }
+ }
+ if (!d->hasExtra())
+ return true; //okay if there's no validity
+ return (valid || d->extra()->validity == NULL || d->extra()->validity->m_action != Action::Stop);
+}
+
+FormatType Cell::formatType() const
+{
+ return format()->getFormatType( d->column, d->row );
+}
+
+double Cell::textWidth() const
+{
+ return d->textWidth;
+}
+
+double Cell::textHeight() const
+{
+ return d->textHeight;
+}
+
+int Cell::mergedXCells() const
+{
+ return d->hasExtra() ? d->extra()->mergedXCells : 0;
+}
+
+int Cell::mergedYCells() const
+{
+ return d->hasExtra() ? d->extra()->mergedYCells : 0;
+}
+
+int Cell::extraXCells() const
+{
+ return d->hasExtra() ? d->extra()->extraXCells : 0;
+}
+
+int Cell::extraYCells() const
+{
+ return d->hasExtra() ? d->extra()->extraYCells : 0;
+}
+
+double Cell::extraWidth() const
+{
+ return d->hasExtra() ? d->extra()->extraWidth : 0;
+}
+
+double Cell::extraHeight() const
+{
+ return d->hasExtra() ? d->extra()->extraHeight : 0;
+}
+
+
+bool Cell::isDate() const
+{
+ FormatType ft = formatType();
+
+ return (formatIsDate (ft) || ((ft == Generic_format) &&
+ (value().format() == Value::fmt_Date)));
+}
+
+bool Cell::isTime() const
+{
+ FormatType ft = formatType();
+
+ return (formatIsTime (ft) || ((ft == Generic_format) &&
+ (value().format() == Value::fmt_Time)));
+}
+
+void Cell::setCalcDirtyFlag()
+{
+ if ( !isFormula() )
+ {
+ //don't set the flag if we don't hold a formula
+ clearFlag(Flag_CalcDirty);
+ return;
+ }
+ setFlag(Flag_CalcDirty);
+ format()->sheet()->setRegionPaintDirty(cellRect());
+}
+
+
+bool Cell::updateChart(bool refresh)
+{
+ // Update a chart for example if it depends on this cell.
+ if ( d->row != 0 && d->column != 0 )
+ {
+ CellBinding *bind;
+ for ( bind = format()->sheet()->firstCellBinding(); bind != 0L; bind = format()->sheet()->nextCellBinding() )
+ {
+ if ( bind->contains( d->column, d->row ) )
+ {
+ if (!refresh)
+ return true;
+
+ bind->cellChanged( this );
+ }
+ }
+ return true;
+ }
+ return false;
+
+}
+
+double Cell::getDouble ()
+{
+ if (isDefault())
+ return 0.0;
+ //(Tomas) umm can't we simply call value().asFloat() ?
+ if (isDate())
+ {
+ TQDate date = value().asDate();
+ TQDate dummy (1900, 1, 1);
+ return (dummy.daysTo (date) + 1);
+ }
+ if (isTime())
+ {
+ TQTime time = value().asTime();
+ TQTime dummy;
+ return dummy.secsTo( time );
+ }
+ if (value().isNumber())
+ return value().asFloat();
+
+ return 0.0;
+}
+
+void Cell::convertToDouble ()
+{
+ if (isDefault())
+ return;
+
+ setValue (getDouble ());
+}
+
+void Cell::convertToPercent ()
+{
+ if (isDefault())
+ return;
+
+ setValue (getDouble ());
+ d->value.setFormat (Value::fmt_Percent);
+}
+
+void Cell::convertToMoney ()
+{
+ if (isDefault())
+ return;
+
+ setValue (getDouble ());
+ d->value.setFormat (Value::fmt_Money);
+ format()->setPrecision (locale()->fracDigits());
+}
+
+void Cell::convertToTime ()
+{
+ //(Tomas) This is weird. And I mean *REALLY* weird. First, we
+ //generate a time (TQTime), then we convert it to text, then
+ //we give the text to the cell and ask it to parse it. Weird...
+
+ if (isDefault() || isEmpty())
+ return;
+
+ setValue (getDouble ());
+ TQTime time = value().asDateTime().time();
+ int msec = (int) ( (value().asFloat() - (int) value().asFloat()) * 1000 );
+ time = time.addMSecs( msec );
+ setCellText( time.toString() );
+}
+
+void Cell::convertToDate ()
+{
+ //(Tomas) This is weird. And I mean *REALLY* weird. First, we
+ //generate a date (TQDate), then we convert it to text, then
+ //we give the text to the cell and ask it to parse it. Weird...
+
+ if (isDefault() || isEmpty())
+ return;
+
+ setValue (getDouble ());
+
+ //TODO: why did we call setValue(), when we override it here?
+ TQDate date(1900, 1, 1);
+ date = date.addDays( (int) value().asFloat() - 1 );
+ date = value().asDateTime().date();
+ setCellText (locale()->formatDate (date, true));
+}
+
+void Cell::checkTextInput()
+{
+ // Goal of this method: determine the value of the cell
+ clearAllErrors();
+
+ d->value = Value::empty();
+
+ // Get the text from that cell
+ TQString str = d->strText;
+
+ sheet()->doc()->parser()->parse (str, this);
+
+ // Parsing as time acts like an autoformat: we even change d->strText
+ // [h]:mm:ss -> might get set by ValueParser
+ if (isTime() && (formatType() != Time_format7))
+ d->strText = locale()->formatTime( value().asDateTime().time(), true);
+
+ // convert first letter to uppercase ?
+ if (format()->sheet()->getFirstLetterUpper() && value().isString() &&
+ (!d->strText.isEmpty()))
+ {
+ TQString str = value().asString();
+ setValue( Value( str[0].upper() + str.right( str.length()-1 ) ) );
+ }
+}
+
+//used in calc, setNumber, ValueParser
+void Cell::checkNumberFormat()
+{
+ if ( formatType() == Number_format && value().isNumber() )
+ {
+ if ( value().asFloat() > 1e+10 )
+ format()->setFormatType( Scientific_format );
+ }
+}
+
+
+// ================================================================
+// Saving and loading
+
+
+TQDomElement Cell::save( TQDomDocument& doc,
+ int _x_offset, int _y_offset,
+ bool force, bool copy, bool era )
+{
+ // Save the position of this cell
+ TQDomElement cell = doc.createElement( "cell" );
+ cell.setAttribute( "row", d->row - _y_offset );
+ cell.setAttribute( "column", d->column - _x_offset );
+ //
+ // Save the formatting information
+ //
+ TQDomElement formatElement = format()->save( doc, d->column, d->row, force, copy );
+ if ( formatElement.hasChildNodes() || formatElement.attributes().length() ) // don't save empty tags
+ cell.appendChild( formatElement );
+
+ if ( doesMergeCells() )
+ {
+ if ( extraXCells() )
+ formatElement.setAttribute( "colspan", extraXCells() );
+ if ( extraYCells() )
+ formatElement.setAttribute( "rowspan", extraYCells() );
+ }
+
+ if ( d->hasExtra() && d->extra()->conditions )
+ {
+ TQDomElement conditionElement = d->extra()->conditions->saveConditions( doc );
+
+ if ( !conditionElement.isNull() )
+ cell.appendChild( conditionElement );
+ }
+
+ if ( d->hasExtra() && (d->extra()->validity != 0) )
+ {
+ TQDomElement validity = doc.createElement("validity");
+
+ TQDomElement param=doc.createElement("param");
+ param.setAttribute("cond",(int)d->extra()->validity->m_cond);
+ param.setAttribute("action",(int)d->extra()->validity->m_action);
+ param.setAttribute("allow",(int)d->extra()->validity->m_restriction);
+ param.setAttribute("valmin",d->extra()->validity->valMin);
+ param.setAttribute("valmax",d->extra()->validity->valMax);
+ param.setAttribute("displaymessage",d->extra()->validity->displayMessage);
+ param.setAttribute("displayvalidationinformation",d->extra()->validity->displayValidationInformation);
+ param.setAttribute("allowemptycell",d->extra()->validity->allowEmptyCell);
+ if ( !d->extra()->validity->listValidity.isEmpty() )
+ param.setAttribute( "listvalidity", d->extra()->validity->listValidity.join( ";" ) );
+ validity.appendChild(param);
+ TQDomElement title = doc.createElement( "title" );
+ title.appendChild( doc.createTextNode( d->extra()->validity->title ) );
+ validity.appendChild( title );
+ TQDomElement message = doc.createElement( "message" );
+ message.appendChild( doc.createCDATASection( d->extra()->validity->message ) );
+ validity.appendChild( message );
+
+ TQDomElement inputTitle = doc.createElement( "inputtitle" );
+ inputTitle.appendChild( doc.createTextNode( d->extra()->validity->titleInfo ) );
+ validity.appendChild( inputTitle );
+
+ TQDomElement inputMessage = doc.createElement( "inputmessage" );
+ inputMessage.appendChild( doc.createTextNode( d->extra()->validity->messageInfo ) );
+ validity.appendChild( inputMessage );
+
+
+
+ TQString tmp;
+ if ( d->extra()->validity->timeMin.isValid() )
+ {
+ TQDomElement timeMin = doc.createElement( "timemin" );
+ tmp=d->extra()->validity->timeMin.toString();
+ timeMin.appendChild( doc.createTextNode( tmp ) );
+ validity.appendChild( timeMin );
+ }
+ if ( d->extra()->validity->timeMax.isValid() )
+ {
+ TQDomElement timeMax = doc.createElement( "timemax" );
+ tmp=d->extra()->validity->timeMax.toString();
+ timeMax.appendChild( doc.createTextNode( tmp ) );
+ validity.appendChild( timeMax );
+ }
+
+ if ( d->extra()->validity->dateMin.isValid() )
+ {
+ TQDomElement dateMin = doc.createElement( "datemin" );
+ TQString tmp("%1/%2/%3");
+ tmp = tmp.arg(d->extra()->validity->dateMin.year()).arg(d->extra()->validity->dateMin.month()).arg(d->extra()->validity->dateMin.day());
+ dateMin.appendChild( doc.createTextNode( tmp ) );
+ validity.appendChild( dateMin );
+ }
+ if ( d->extra()->validity->dateMax.isValid() )
+ {
+ TQDomElement dateMax = doc.createElement( "datemax" );
+ TQString tmp("%1/%2/%3");
+ tmp = tmp.arg(d->extra()->validity->dateMax.year()).arg(d->extra()->validity->dateMax.month()).arg(d->extra()->validity->dateMax.day());
+ dateMax.appendChild( doc.createTextNode( tmp ) );
+ validity.appendChild( dateMax );
+ }
+
+ cell.appendChild( validity );
+ }
+
+ if ( format()->comment() )
+ {
+ TQDomElement comment = doc.createElement( "comment" );
+ comment.appendChild( doc.createCDATASection( *format()->comment() ) );
+ cell.appendChild( comment );
+ }
+
+ //
+ // Save the text
+ //
+ if ( !d->strText.isEmpty() )
+ {
+ // Formulas need to be encoded to ensure that they
+ // are position independent.
+ if ( isFormula() )
+ {
+ TQDomElement text = doc.createElement( "text" );
+ // if we are cutting to the clipboard, relative references need to be encoded absolutely
+ text.appendChild( doc.createTextNode( encodeFormula( era ) ) );
+ cell.appendChild( text );
+
+ /* we still want to save the results of the formula */
+ TQDomElement formulaResult = doc.createElement( "result" );
+ saveCellResult( doc, formulaResult, d->strOutText );
+ cell.appendChild( formulaResult );
+
+ }
+ else if ( !link().isEmpty() )
+ {
+ // KSpread pre 1.4 saves link as rich text, marked with first char '
+ // Have to be saved in some CDATA section because of too many special charatcers.
+ TQDomElement text = doc.createElement( "text" );
+ TQString qml = "!<a href=\"" + link() + "\">" + d->strText + "</a>";
+ text.appendChild( doc.createCDATASection( qml ) );
+ cell.appendChild( text );
+ }
+ else
+ {
+ // Save the cell contents (in a locale-independent way)
+ TQDomElement text = doc.createElement( "text" );
+ saveCellResult( doc, text, d->strText );
+ cell.appendChild( text );
+ }
+ }
+ if ( cell.hasChildNodes() || cell.attributes().length() > 2 ) // don't save empty tags
+ // (the >2 is due to "row" and "column" attributes)
+ return cell;
+ else
+ return TQDomElement();
+}
+
+bool Cell::saveCellResult( TQDomDocument& doc, TQDomElement& result,
+ TQString str )
+{
+ TQString dataType = "Other"; // fallback
+
+ if ( value().isNumber() )
+ {
+ if ( isDate() )
+ {
+ // serial number of date
+ TQDate dd = value().asDateTime().date();
+ dataType = "Date";
+ str = "%1/%2/%3";
+ str = str.arg(dd.year()).arg(dd.month()).arg(dd.day());
+ }
+ else if( isTime() )
+ {
+ // serial number of time
+ dataType = "Time";
+ str = value().asDateTime().time().toString();
+ }
+ else
+ {
+ // real number
+ dataType = "Num";
+ if (value().isInteger())
+ str = TQString::number(value().asInteger());
+ else
+ str = TQString::number(value().asFloat(), 'g', DBL_DIG);
+ }
+ }
+
+ if ( value().isBoolean() )
+ {
+ dataType = "Bool";
+ str = value().asBoolean() ? "true" : "false";
+ }
+
+ if ( value().isString() )
+ {
+ dataType = "Str";
+ str = value().asString();
+ }
+
+ result.setAttribute( "dataType", dataType );
+ if ( !d->strOutText.isEmpty() )
+ result.setAttribute( "outStr", d->strOutText );
+ result.appendChild( doc.createTextNode( str ) );
+
+ return true; /* really isn't much of a way for this function to fail */
+}
+
+void Cell::saveOasisAnnotation( KoXmlWriter &xmlwriter )
+{
+ if ( format()->comment() )
+ {
+ //<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">
+ xmlwriter.startElement( "office:annotation" );
+ TQStringList text = TQStringList::split( "\n", *format()->comment() );
+ for ( TQStringList::Iterator it = text.begin(); it != text.end(); ++it ) {
+ xmlwriter.startElement( "text:p" );
+ xmlwriter.addTextNode( *it );
+ xmlwriter.endElement();
+ }
+ xmlwriter.endElement();
+ }
+}
+
+
+
+TQString Cell::saveOasisCellStyle( KoGenStyle &currentCellStyle, KoGenStyles &mainStyles )
+{
+ if ( d->hasExtra() && d->extra()->conditions )
+ {
+ // this has to be an automatic style
+ currentCellStyle = KoGenStyle( Doc::STYLE_CELL_AUTO, "table-cell" );
+ d->extra()->conditions->saveOasisConditions( currentCellStyle );
+ }
+ return format()->saveOasisCellStyle( currentCellStyle, mainStyles );
+}
+
+
+bool Cell::saveOasis( KoXmlWriter& xmlwriter, KoGenStyles &mainStyles,
+ int row, int column, int &repeated,
+ GenValidationStyles &valStyle )
+{
+ if ( !isPartOfMerged() )
+ xmlwriter.startElement( "table:table-cell" );
+ else
+ xmlwriter.startElement( "table:covered-table-cell" );
+#if 0
+ //add font style
+ TQFont font;
+ Value const value( cell->value() );
+ if ( !cell->isDefault() )
+ {
+ font = cell->format()->textFont( i, row );
+ m_styles.addFont( font );
+
+ if ( cell->format()->hasProperty( Format::PComment ) )
+ hasComment = true;
+ }
+#endif
+ // NOTE save the value before the style as long as the Formatter does not work correctly
+ if ( link().isEmpty() )
+ saveOasisValue (xmlwriter);
+
+ KoGenStyle currentCellStyle; // the type determined in saveOasisCellStyle
+ saveOasisCellStyle( currentCellStyle,mainStyles );
+ // skip 'table:style-name' attribute for the default style
+ if ( !currentCellStyle.isDefaultStyle() )
+ xmlwriter.addAttribute( "table:style-name", mainStyles.styles()[currentCellStyle] );
+
+ // group empty cells with the same style
+ if ( isEmpty() && !format()->hasProperty( Format::PComment ) &&
+ !isPartOfMerged() && !doesMergeCells() )
+ {
+ bool refCellIsDefault = isDefault();
+ int j = column + 1;
+ Cell *nextCell = format()->sheet()->getNextCellRight( column, row );
+ while ( nextCell )
+ {
+ // if
+ // the next cell is not the adjacent one
+ // or
+ // the next cell is not empty
+ if ( nextCell->column() != j || !nextCell->isEmpty() )
+ {
+ if ( refCellIsDefault )
+ {
+ // if the origin cell was a default cell,
+ // we count the default cells
+ repeated = nextCell->column() - j + 1;
+ }
+ // otherwise we just stop here to process the adjacent
+ // cell in the next iteration of the outer loop
+ // (in Sheet::saveOasisCells)
+ break;
+ }
+
+ KoGenStyle nextCellStyle; // the type is determined in saveOasisCellStyle
+ nextCell->saveOasisCellStyle( nextCellStyle,mainStyles );
+
+ if ( nextCell->isPartOfMerged() || nextCell->doesMergeCells() ||
+ nextCell->format()->hasProperty( Format::PComment ) ||
+ !(nextCellStyle == currentCellStyle) )
+ {
+ break;
+ }
+ ++repeated;
+ // get the next cell and set the index to the adjacent cell
+ nextCell = format()->sheet()->getNextCellRight( j++, row );
+ }
+ kdDebug() << "Cell::saveOasis: empty cell in column " << column << " "
+ << "repeated " << repeated << " time(s)" << endl;
+
+ if ( repeated > 1 )
+ xmlwriter.addAttribute( "table:number-columns-repeated", TQString::number( repeated ) );
+ }
+
+
+ if (d->hasExtra() && d->extra()->validity)
+ {
+ GenValidationStyle styleVal(d->extra()->validity);
+ xmlwriter.addAttribute( "table:validation-name", valStyle.lookup( styleVal ) );
+ }
+ if ( isFormula() )
+ {
+ //kdDebug() << "Formula found" << endl;
+ TQString formula( convertFormulaToOasisFormat( text() ) );
+ xmlwriter.addAttribute( "table:formula", formula );
+ }
+ else if ( !link().isEmpty() )
+ {
+ //kdDebug()<<"Link found \n";
+ xmlwriter.startElement( "text:p" );
+ xmlwriter.startElement( "text:a" );
+ //Reference cell is started by "#"
+ if ( localReferenceAnchor( link() ) )
+ xmlwriter.addAttribute( " xlink:href", ( "#"+link() ) );
+ else
+ xmlwriter.addAttribute( " xlink:href", link() );
+ xmlwriter.addTextNode( text() );
+ xmlwriter.endElement();
+ xmlwriter.endElement();
+ }
+
+ if ( doesMergeCells() )
+ {
+ int colSpan = mergedXCells() + 1;
+ int rowSpan = mergedYCells() + 1;
+
+ if ( colSpan > 1 )
+ xmlwriter.addAttribute( "table:number-columns-spanned", TQString::number( colSpan ) );
+
+ if ( rowSpan > 1 )
+ xmlwriter.addAttribute( "table:number-rows-spanned", TQString::number( rowSpan ) );
+ }
+
+ if ( !isEmpty() && link().isEmpty() )
+ {
+ xmlwriter.startElement( "text:p" );
+ xmlwriter.addTextNode( strOutText().utf8() );
+ xmlwriter.endElement();
+ }
+
+ saveOasisAnnotation( xmlwriter );
+
+ xmlwriter.endElement();
+ return true;
+}
+
+void Cell::saveOasisValue (KoXmlWriter &xmlWriter)
+{
+ switch (value().format())
+ {
+ case Value::fmt_None: break; //NOTHING HERE
+ case Value::fmt_Boolean:
+ {
+ xmlWriter.addAttribute( "office:value-type", "boolean" );
+ xmlWriter.addAttribute( "office:boolean-value", ( value().asBoolean() ?
+ "true" : "false" ) );
+ break;
+ }
+ case Value::fmt_Number:
+ {
+ xmlWriter.addAttribute( "office:value-type", "float" );
+ if (value().isInteger())
+ xmlWriter.addAttribute( "office:value", TQString::number( value().asInteger() ) );
+ else
+ xmlWriter.addAttribute( "office:value", TQString::number( value().asFloat(), 'g', DBL_DIG ) );
+ break;
+ }
+ case Value::fmt_Percent:
+ {
+ xmlWriter.addAttribute( "office:value-type", "percentage" );
+ xmlWriter.addAttribute( "office:value",
+ TQString::number( value().asFloat() ) );
+ break;
+ }
+ case Value::fmt_Money:
+ {
+ xmlWriter.addAttribute( "office:value-type", "currency" );
+ Format::Currency currency;
+ if (format()->currencyInfo(currency))
+ xmlWriter.addAttribute( "office:currency", Currency::getCurrencyCode(currency.type) );
+ xmlWriter.addAttribute( "office:value",
+ TQString::number( value().asFloat() ) );
+ break;
+ }
+ case Value::fmt_DateTime: break; //NOTHING HERE
+ case Value::fmt_Date:
+ {
+ xmlWriter.addAttribute( "office:value-type", "date" );
+ xmlWriter.addAttribute( "office:date-value",
+ value().asDate().toString( Qt::ISODate ) );
+ break;
+ }
+ case Value::fmt_Time:
+ {
+ xmlWriter.addAttribute( "office:value-type", "time" );
+ xmlWriter.addAttribute( "office:time-value",
+ value().asTime().toString( "PThhHmmMssS" ) );
+ break;
+ }
+ case Value::fmt_String:
+ {
+ xmlWriter.addAttribute( "office:value-type", "string" );
+ xmlWriter.addAttribute( "office:string-value", value().asString() );
+ break;
+ }
+ };
+}
+
+TQString Cell::convertFormulaToOasisFormat( const TQString & formula ) const
+{
+ TQString s;
+ TQRegExp exp("(\\$?)([a-zA-Z]+)(\\$?)([0-9]+)");
+ int n = exp.search( formula, 0 );
+ kdDebug() << "Exp: " << formula << ", n: " << n << ", Length: " << formula.length()
+ << ", Matched length: " << exp.matchedLength() << endl;
+
+ bool inQuote1 = false;
+ bool inQuote2 = false;
+ int i = 0;
+ int l = (int) formula.length();
+ if ( l <= 0 )
+ return formula;
+ while ( i < l )
+ {
+ if ( ( n != -1 ) && ( n < i ) )
+ {
+ n = exp.search( formula, i );
+ kdDebug() << "Exp: " << formula.right( l - i ) << ", n: " << n << endl;
+ }
+ if ( formula[i] == '"' )
+ {
+ inQuote1 = !inQuote1;
+ s += formula[i];
+ ++i;
+ continue;
+ }
+ if ( formula[i] == '\'' )
+ {
+ // named area
+ inQuote2 = !inQuote2;
+ ++i;
+ continue;
+ }
+ if ( inQuote1 || inQuote2 )
+ {
+ s += formula[i];
+ ++i;
+ continue;
+ }
+ if ( ( formula[i] == '=' ) && ( formula[i + 1] == '=' ) )
+ {
+ s += '=';
+ ++i;++i;
+ continue;
+ }
+ if ( formula[i] == '!' )
+ {
+ insertBracket( s );
+ s += '.';
+ ++i;
+ continue;
+ }
+ if ( formula[i] == ',' )
+ {
+ s += '.';
+ ++i;
+ continue;
+ }
+ if ( n == i )
+ {
+ int ml = exp.matchedLength();
+ if ( formula[ i + ml ] == '!' )
+ {
+ kdDebug() << "No cell ref but sheet name" << endl;
+ s += formula[i];
+ ++i;
+ continue;
+ }
+ if ( ( i > 0 ) && ( formula[i - 1] != '!' ) )
+ s += "[.";
+ for ( int j = 0; j < ml; ++j )
+ {
+ s += formula[i];
+ ++i;
+ }
+ s += ']';
+ continue;
+ }
+
+ s += formula[i];
+ ++i;
+ }
+
+ return s;
+}
+
+void Cell::loadOasisConditional( TQDomElement * style )
+{
+ if ( style )//safe
+ {
+ TQDomElement e;
+ forEachElement( e, style->toElement() )
+ {
+ if ( e.localName() == "map" && e.namespaceURI() == KoXmlNS::style )
+ {
+ if (d->hasExtra())
+ delete d->extra()->conditions;
+ d->extra()->conditions = new Conditions( this );
+ d->extra()->conditions->loadOasisConditions( e );
+ d->extra()->conditions->checkMatches();
+ // break here
+ // Conditions::loadOasisConditions finishes the iteration
+ break;
+ }
+ }
+ }
+}
+
+bool Cell::loadOasis( const TQDomElement& element , KoOasisLoadingContext& oasisContext , Style* style )
+{
+ kdDebug() << "*** Loading cell properties ***** at " << column() << "," << row () << endl;
+
+ if ( element.hasAttributeNS( KoXmlNS::table, "style-name" ) )
+ {
+ kdDebug()<<" table:style-name: "<<element.attributeNS( KoXmlNS::table, "style-name", TQString() )<<endl;
+ oasisContext.fillStyleStack( element, KoXmlNS::table, "styleName", "table-cell" );
+
+ TQString str = element.attributeNS( KoXmlNS::table, "style-name", TQString() );
+ const TQDomElement* cellStyle = oasisContext.oasisStyles().findStyle( str, "table-cell" );
+
+ if ( cellStyle )
+ loadOasisConditional( const_cast<TQDomElement *>( cellStyle ) );
+ }
+
+ if (style)
+ {
+ format()->setStyle( style );
+ }
+
+ //Search and load each paragraph of text. Each paragraph is separated by a line break.
+ loadOasisCellText( element );
+
+ //
+ // formula
+ //
+ bool isFormula = false;
+ if ( element.hasAttributeNS( KoXmlNS::table, "formula" ) )
+ {
+ kdDebug()<<" formula :"<<element.attributeNS( KoXmlNS::table, "formula", TQString() )<<endl;
+ isFormula = true;
+ TQString oasisFormula( element.attributeNS( KoXmlNS::table, "formula", TQString() ) );
+ //necessary to remove it to load formula from oocalc2.0 (use namespace)
+ if (oasisFormula.startsWith( "oooc:" ) )
+ oasisFormula= oasisFormula.mid( 5 );
+ else if (oasisFormula.startsWith( "kspr:" ) )
+ oasisFormula= oasisFormula.mid( 5 );
+ // TODO Stefan: merge this into Oasis::decodeFormula
+ checkForNamedAreas( oasisFormula );
+ oasisFormula = Oasis::decodeFormula( oasisFormula, locale() );
+ setCellText( oasisFormula );
+ }
+ else if ( d->strText.at(0) == '=' ) //prepend ' to the text to avoid = to be painted
+ d->strText.prepend('\'');
+
+ //
+ // validation
+ //
+ if ( element.hasAttributeNS( KoXmlNS::table, "validation-name" ) )
+ {
+ kdDebug()<<" validation-name: "<<element.attributeNS( KoXmlNS::table, "validation-name", TQString() )<<endl;
+ loadOasisValidation( element.attributeNS( KoXmlNS::table, "validation-name", TQString() ) );
+ }
+
+ //
+ // value type
+ //
+ if( element.hasAttributeNS( KoXmlNS::office, "value-type" ) )
+ {
+ TQString valuetype = element.attributeNS( KoXmlNS::office, "value-type", TQString() );
+ kdDebug()<<" value-type: " << valuetype << endl;
+ if( valuetype == "boolean" )
+ {
+ TQString val = element.attributeNS( KoXmlNS::office, "boolean-value", TQString() ).lower();
+ if( ( val == "true" ) || ( val == "false" ) )
+ {
+ bool value = val == "true";
+ setCellValue( value );
+ }
+ }
+
+ // integer and floating-point value
+ else if( valuetype == "float" )
+ {
+ bool ok = false;
+ double value = element.attributeNS( KoXmlNS::office, "value", TQString() ).toDouble( &ok );
+ if( ok )
+ setCellValue( value );
+
+ if ( !isFormula && d->strText.isEmpty())
+ {
+ TQString str = locale()->formatNumber( value, 15 );
+ setCellText( str );
+ }
+ }
+
+ // currency value
+ else if( valuetype == "currency" )
+ {
+ bool ok = false;
+ double value = element.attributeNS( KoXmlNS::office, "value", TQString() ).toDouble( &ok );
+ if( ok )
+ {
+ setCellValue( value, Money_format );
+
+ if (element.hasAttributeNS( KoXmlNS::office, "currency" ) )
+ {
+ Currency currency(element.attributeNS( KoXmlNS::office, "currency", TQString() ) );
+ format()->setCurrency( currency.getIndex(), currency.getDisplayCode() );
+ }
+ }
+ }
+ else if( valuetype == "percentage" )
+ {
+ bool ok = false;
+ double v = element.attributeNS( KoXmlNS::office, "value", TQString() ).toDouble( &ok );
+ if( ok )
+ {
+ Value value;
+ value.setValue (v);
+ value.setFormat (Value::fmt_Percent);
+ setCellValue( value );
+
+ if ( !isFormula && d->strText.isEmpty())
+ {
+ TQString str = locale()->formatNumber( v, 15 );
+ setCellText( str );
+ }
+
+ format()->setFormatType (Percentage_format);
+ }
+ }
+ else if ( valuetype == "date" )
+ {
+ TQString value = element.attributeNS( KoXmlNS::office, "value", TQString() );
+ if ( value.isEmpty() )
+ value = element.attributeNS( KoXmlNS::office, "date-value", TQString() );
+ kdDebug() << "Type: date, value: " << value << endl;
+
+ // "1980-10-15"
+ int year = 0, month = 0, day = 0;
+ bool ok = false;
+
+ int p1 = value.find( '-' );
+ if ( p1 > 0 )
+ year = value.left( p1 ).toInt( &ok );
+
+ kdDebug() << "year: " << value.left( p1 ) << endl;
+
+ int p2 = value.find( '-', ++p1 );
+
+ if ( ok )
+ month = value.mid( p1, p2 - p1 ).toInt( &ok );
+
+ kdDebug() << "month: " << value.mid( p1, p2 - p1 ) << endl;
+
+ if ( ok )
+ day = value.right( value.length() - p2 - 1 ).toInt( &ok );
+
+ kdDebug() << "day: " << value.right( value.length() - p2 ) << endl;
+
+ if ( ok )
+ {
+ setCellValue( TQDate( year, month, day ) );
+ if ( style )
+ format()->setFormatType (style->formatType());
+ kdDebug() << "Set TQDate: " << year << " - " << month << " - " << day << endl;
+ }
+
+ }
+ else if ( valuetype == "time" )
+ {
+ TQString value = element.attributeNS( KoXmlNS::office, "value", TQString() );
+ if ( value.isEmpty() )
+ value = element.attributeNS( KoXmlNS::office, "time-value", TQString() );
+ kdDebug() << "Type: time: " << value << endl;
+ // "PT15H10M12S"
+ int hours = 0, minutes = 0, seconds = 0;
+ int l = value.length();
+ TQString num;
+ bool ok = false;
+ for ( int i = 0; i < l; ++i )
+ {
+ if ( value[i].isNumber() )
+ {
+ num += value[i];
+ continue;
+ }
+ else if ( value[i] == 'H' )
+ hours = num.toInt( &ok );
+ else if ( value[i] == 'M' )
+ minutes = num.toInt( &ok );
+ else if ( value[i] == 'S' )
+ seconds = num.toInt( &ok );
+ else
+ continue;
+
+ kdDebug() << "Num: " << num << endl;
+
+ num = "";
+ if ( !ok )
+ break;
+ }
+ kdDebug() << "Hours: " << hours << ", " << minutes << ", " << seconds << endl;
+
+ if ( ok )
+ {
+ // Value kval( timeToNum( hours, minutes, seconds ) );
+ // cell->setValue( kval );
+ setCellValue( TQTime( hours % 24, minutes, seconds ) );
+ if ( style )
+ format()->setFormatType (style->formatType());
+ }
+ }
+ else if( valuetype == "string" )
+ {
+ TQString value = element.attributeNS( KoXmlNS::office, "value", TQString() );
+ if ( value.isEmpty() && element.hasAttributeNS( KoXmlNS::office, "string-value" ))
+ {
+ //if there is not string-value entry don't overwrite value stored into <text:p>
+ value = element.attributeNS( KoXmlNS::office, "string-value", TQString() );
+ setCellValue( value );
+ }
+ format()->setFormatType (Text_format);
+ }
+ else
+ kdDebug()<<" type of value found : "<<valuetype<<endl;
+ }
+
+ //
+ // merged cells ?
+ //
+ int colSpan = 1;
+ int rowSpan = 1;
+ if ( element.hasAttributeNS( KoXmlNS::table, "number-columns-spanned" ) )
+ {
+ bool ok = false;
+ int span = element.attributeNS( KoXmlNS::table, "number-columns-spanned", TQString() ).toInt( &ok );
+ if( ok ) colSpan = span;
+ }
+ if ( element.hasAttributeNS( KoXmlNS::table, "number-rows-spanned" ) )
+ {
+ bool ok = false;
+ int span = element.attributeNS( KoXmlNS::table, "number-rows-spanned", TQString() ).toInt( &ok );
+ if( ok ) rowSpan = span;
+ }
+ if ( colSpan > 1 || rowSpan > 1 )
+ mergeCells( d->column, d->row, colSpan - 1, rowSpan - 1 );
+
+ //
+ // cell comment/annotation
+ //
+ TQDomElement annotationElement = KoDom::namedItemNS( element, KoXmlNS::office, "annotation" );
+ if ( !annotationElement.isNull() )
+ {
+ TQString comment;
+ TQDomNode node = annotationElement.firstChild();
+ while( !node.isNull() )
+ {
+ TQDomElement commentElement = node.toElement();
+ if( !commentElement.isNull() )
+ if( commentElement.localName() == "p" && commentElement.namespaceURI() == KoXmlNS::text )
+ {
+ if( !comment.isEmpty() ) comment.append( '\n' );
+ comment.append( commentElement.text() );
+ }
+
+ node = node.nextSibling();
+ }
+
+ if( !comment.isEmpty() )
+ format()->setComment( comment );
+ }
+
+ TQDomElement frame = KoDom::namedItemNS( element, KoXmlNS::draw, "frame" );
+ if ( !frame.isNull() )
+ loadOasisObjects( frame, oasisContext );
+
+ if (isFormula)
+ setCalcDirtyFlag (); // formulas must be recalculated
+
+ return true;
+}
+
+void Cell::loadOasisCellText( const TQDomElement& parent )
+{
+ //Search and load each paragraph of text. Each paragraph is separated by a line break
+ TQDomElement textParagraphElement;
+ TQString cellText;
+
+ bool multipleTextParagraphsFound=false;
+
+ forEachElement( textParagraphElement , parent )
+ {
+ if ( textParagraphElement.localName()=="p" &&
+ textParagraphElement.namespaceURI()== KoXmlNS::text )
+ {
+ // our text, could contain formating for value or result of formul
+ if (cellText.isEmpty())
+ cellText = textParagraphElement.text();
+ else
+ {
+ cellText += "\n"+textParagraphElement.text();
+ multipleTextParagraphsFound=true;
+ }
+
+ TQDomElement textA = KoDom::namedItemNS( textParagraphElement, KoXmlNS::text, "a" );
+ if( !textA.isNull() )
+ {
+ if ( textA.hasAttributeNS( KoXmlNS::xlink, "href" ) )
+ {
+ TQString link = textA.attributeNS( KoXmlNS::xlink, "href", TQString() );
+ cellText = textA.text();
+ setCellText( cellText );
+ setValue( cellText );
+ if ( link[0]=='#' )
+ link=link.remove( 0, 1 );
+ setLink( link );
+ }
+ }
+ }
+ }
+
+ if (!cellText.isNull())
+ {
+ setCellText( cellText );
+ setValue( cellText );
+ }
+
+ //Enable word wrapping if multiple lines of text have been found.
+ if ( multipleTextParagraphsFound )
+ {
+ format()->setMultiRow(true);
+ }
+}
+
+void Cell::loadOasisObjects( const TQDomElement &parent, KoOasisLoadingContext& oasisContext )
+{
+ for( TQDomElement e = parent; !e.isNull(); e = e.nextSibling().toElement() )
+ {
+ if ( e.localName() == "frame" && e.namespaceURI() == KoXmlNS::draw )
+ {
+ EmbeddedObject *obj = 0;
+ TQDomNode object = KoDom::namedItemNS( e, KoXmlNS::draw, "object" );
+ if ( !object.isNull() )
+ {
+ if ( !object.toElement().attributeNS( KoXmlNS::draw, "notify-on-update-of-ranges", TQString()).isNull() )
+ obj = new EmbeddedChart( sheet()->doc(), sheet() );
+ else
+ obj = new EmbeddedKOfficeObject( sheet()->doc(), sheet() );
+ }
+ else
+ {
+ TQDomNode image = KoDom::namedItemNS( e, KoXmlNS::draw, "image" );
+ if ( !image.isNull() )
+ obj = new EmbeddedPictureObject( sheet(), sheet()->doc()->pictureCollection() );
+ else
+ kdDebug() << "Object type wasn't loaded!" << endl;
+ }
+
+ if ( obj )
+ {
+ obj->loadOasis( e, oasisContext );
+ sheet()->doc()->insertObject( obj );
+
+ TQString ref = e.attributeNS( KoXmlNS::table, "end-cell-address", TQString() );
+ if ( ref.isNull() )
+ continue;
+
+ ref = Oasis::decodeFormula( ref );
+ Point point( ref );
+ if ( !point.isValid() )
+ continue;
+
+ KoRect geometry = obj->geometry();
+ geometry.setLeft( geometry.left() + sheet()->columnPos( d->column, 0 ) );
+ geometry.setTop( geometry.top() + sheet()->rowPos( d->row, 0 ) );
+
+ TQString str = e.attributeNS( KoXmlNS::table, "end-x", TQString() );
+ if ( !str.isNull() )
+ {
+ uint end_x = (uint) KoUnit::parseValue( str );
+ geometry.setRight( sheet()->columnPos( point.column(), 0) + end_x );
+ }
+
+ str = e.attributeNS( KoXmlNS::table, "end-y", TQString() );
+ if ( !str.isNull() )
+ {
+ uint end_y = (uint) KoUnit::parseValue( str );
+ geometry.setBottom( sheet()->rowPos( point.row(), 0) + end_y );
+ }
+
+ obj->setGeometry( geometry );
+ }
+ }
+ }
+}
+
+void Cell::loadOasisValidation( const TQString& validationName )
+{
+ TQDomElement element = sheet()->doc()->loadingInfo()->validation( validationName);
+ if (d->hasExtra())
+ delete d->extra()->validity;
+ d->extra()->validity = new Validity;
+ if ( element.hasAttributeNS( KoXmlNS::table, "condition" ) )
+ {
+ TQString valExpression = element.attributeNS( KoXmlNS::table, "condition", TQString() );
+ kdDebug()<<" element.attribute( table:condition ) "<<valExpression<<endl;
+ //Condition ::= ExtendedTrueCondition | TrueFunction 'and' TrueCondition
+ //TrueFunction ::= cell-content-is-whole-number() | cell-content-is-decimal-number() | cell-content-is-date() | cell-content-is-time()
+ //ExtendedTrueCondition ::= ExtendedGetFunction | cell-content-text-length() Operator Value
+ //TrueCondition ::= GetFunction | cell-content() Operator Value
+ //GetFunction ::= cell-content-is-between(Value, Value) | cell-content-is-not-between(Value, Value)
+ //ExtendedGetFunction ::= cell-content-text-length-is-between(Value, Value) | cell-content-text-length-is-not-between(Value, Value)
+ //Operator ::= '<' | '>' | '<=' | '>=' | '=' | '!='
+ //Value ::= NumberValue | String | Formula
+ //A Formula is a formula without an equals (=) sign at the beginning. See section 8.1.3 for more information.
+ //A String comprises one or more characters surrounded by quotation marks.
+ //A NumberValue is a whole or decimal number. It must not contain comma separators for numbers of 1000 or greater.
+
+ //ExtendedTrueCondition
+ if ( valExpression.contains( "cell-content-text-length()" ) )
+ {
+ //"cell-content-text-length()>45"
+ valExpression = valExpression.remove("oooc:cell-content-text-length()" );
+ kdDebug()<<" valExpression = :"<<valExpression<<endl;
+ d->extra()->validity->m_restriction = Restriction::TextLength;
+
+ loadOasisValidationCondition( valExpression );
+ }
+ else if ( valExpression.contains( "cell-content-is-text()" ) )
+ {
+ d->extra()->validity->m_restriction = Restriction::Text;
+ }
+ //cell-content-text-length-is-between(Value, Value) | cell-content-text-length-is-not-between(Value, Value) | cell-content-is-in-list( StringList )
+ else if ( valExpression.contains( "cell-content-text-length-is-between" ) )
+ {
+ d->extra()->validity->m_restriction = Restriction::TextLength;
+ d->extra()->validity->m_cond = Conditional::Between;
+ valExpression = valExpression.remove( "oooc:cell-content-text-length-is-between(" );
+ kdDebug()<<" valExpression :"<<valExpression<<endl;
+ valExpression = valExpression.remove( ")" );
+ TQStringList listVal = TQStringList::split( ",", valExpression );
+ loadOasisValidationValue( listVal );
+ }
+ else if ( valExpression.contains( "cell-content-text-length-is-not-between" ) )
+ {
+ d->extra()->validity->m_restriction = Restriction::TextLength;
+ d->extra()->validity->m_cond = Conditional::Different;
+ valExpression = valExpression.remove( "oooc:cell-content-text-length-is-not-between(" );
+ kdDebug()<<" valExpression :"<<valExpression<<endl;
+ valExpression = valExpression.remove( ")" );
+ kdDebug()<<" valExpression :"<<valExpression<<endl;
+ TQStringList listVal = TQStringList::split( ",", valExpression );
+ loadOasisValidationValue( listVal );
+ }
+ else if ( valExpression.contains( "cell-content-is-in-list(" ) )
+ {
+ d->extra()->validity->m_restriction = Restriction::List;
+ valExpression = valExpression.remove( "oooc:cell-content-is-in-list(" );
+ kdDebug()<<" valExpression :"<<valExpression<<endl;
+ valExpression = valExpression.remove( ")" );
+ d->extra()->validity->listValidity = TQStringList::split( ";", valExpression );
+
+ }
+ //TrueFunction ::= cell-content-is-whole-number() | cell-content-is-decimal-number() | cell-content-is-date() | cell-content-is-time()
+ else
+ {
+ if (valExpression.contains( "cell-content-is-whole-number()" ) )
+ {
+ d->extra()->validity->m_restriction = Restriction::Number;
+ valExpression = valExpression.remove( "oooc:cell-content-is-whole-number() and " );
+ }
+ else if (valExpression.contains( "cell-content-is-decimal-number()" ) )
+ {
+ d->extra()->validity->m_restriction = Restriction::Integer;
+ valExpression = valExpression.remove( "oooc:cell-content-is-decimal-number() and " );
+ }
+ else if (valExpression.contains( "cell-content-is-date()" ) )
+ {
+ d->extra()->validity->m_restriction = Restriction::Date;
+ valExpression = valExpression.remove( "oooc:cell-content-is-date() and " );
+ }
+ else if (valExpression.contains( "cell-content-is-time()" ) )
+ {
+ d->extra()->validity->m_restriction = Restriction::Time;
+ valExpression = valExpression.remove( "oooc:cell-content-is-time() and " );
+ }
+ kdDebug()<<"valExpression :"<<valExpression<<endl;
+
+ if ( valExpression.contains( "cell-content()" ) )
+ {
+ valExpression = valExpression.remove( "cell-content()" );
+ loadOasisValidationCondition( valExpression );
+ }
+ //GetFunction ::= cell-content-is-between(Value, Value) | cell-content-is-not-between(Value, Value)
+ //for the moment we support just int/double value, not text/date/time :(
+ if ( valExpression.contains( "cell-content-is-between(" ) )
+ {
+ valExpression = valExpression.remove( "cell-content-is-between(" );
+ valExpression = valExpression.remove( ")" );
+ TQStringList listVal = TQStringList::split( "," , valExpression );
+ loadOasisValidationValue( listVal );
+ d->extra()->validity->m_cond = Conditional::Between;
+ }
+ if ( valExpression.contains( "cell-content-is-not-between(" ) )
+ {
+ valExpression = valExpression.remove( "cell-content-is-not-between(" );
+ valExpression = valExpression.remove( ")" );
+ TQStringList listVal = TQStringList::split( ",", valExpression );
+ loadOasisValidationValue( listVal );
+ d->extra()->validity->m_cond = Conditional::Different;
+ }
+ }
+ }
+ if ( element.hasAttributeNS( KoXmlNS::table, "allow-empty-cell" ) )
+ {
+ kdDebug()<<" element.hasAttribute( table:allow-empty-cell ) :"<<element.hasAttributeNS( KoXmlNS::table, "allow-empty-cell" )<<endl;
+ d->extra()->validity->allowEmptyCell = ( ( element.attributeNS( KoXmlNS::table, "allow-empty-cell", TQString() )=="true" ) ? true : false );
+ }
+ if ( element.hasAttributeNS( KoXmlNS::table, "base-cell-address" ) )
+ {
+ //todo what is it ?
+ }
+
+ TQDomElement help = KoDom::namedItemNS( element, KoXmlNS::table, "help-message" );
+ if ( !help.isNull() )
+ {
+ if ( help.hasAttributeNS( KoXmlNS::table, "title" ) )
+ {
+ kdDebug()<<"help.attribute( table:title ) :"<<help.attributeNS( KoXmlNS::table, "title", TQString() )<<endl;
+ d->extra()->validity->titleInfo = help.attributeNS( KoXmlNS::table, "title", TQString() );
+ }
+ if ( help.hasAttributeNS( KoXmlNS::table, "display" ) )
+ {
+ kdDebug()<<"help.attribute( table:display ) :"<<help.attributeNS( KoXmlNS::table, "display", TQString() )<<endl;
+ d->extra()->validity->displayValidationInformation = ( ( help.attributeNS( KoXmlNS::table, "display", TQString() )=="true" ) ? true : false );
+ }
+ TQDomElement attrText = KoDom::namedItemNS( help, KoXmlNS::text, "p" );
+ if ( !attrText.isNull() )
+ {
+ kdDebug()<<"help text :"<<attrText.text()<<endl;
+ d->extra()->validity->messageInfo = attrText.text();
+ }
+ }
+
+ TQDomElement error = KoDom::namedItemNS( element, KoXmlNS::table, "error-message" );
+ if ( !error.isNull() )
+ {
+ if ( error.hasAttributeNS( KoXmlNS::table, "title" ) )
+ d->extra()->validity->title = error.attributeNS( KoXmlNS::table, "title", TQString() );
+ if ( error.hasAttributeNS( KoXmlNS::table, "message-type" ) )
+ {
+ TQString str = error.attributeNS( KoXmlNS::table, "message-type", TQString() );
+ if ( str == "warning" )
+ d->extra()->validity->m_action = Action::Warning;
+ else if ( str == "information" )
+ d->extra()->validity->m_action = Action::Information;
+ else if ( str == "stop" )
+ d->extra()->validity->m_action = Action::Stop;
+ else
+ kdDebug()<<"validation : message type unknown :"<<str<<endl;
+ }
+
+ if ( error.hasAttributeNS( KoXmlNS::table, "display" ) )
+ {
+ kdDebug()<<" display message :"<<error.attributeNS( KoXmlNS::table, "display", TQString() )<<endl;
+ d->extra()->validity->displayMessage = (error.attributeNS( KoXmlNS::table, "display", TQString() )=="true");
+ }
+ TQDomElement attrText = KoDom::namedItemNS( error, KoXmlNS::text, "p" );
+ if ( !attrText.isNull() )
+ d->extra()->validity->message = attrText.text();
+ }
+}
+
+
+void Cell::loadOasisValidationValue( const TQStringList &listVal )
+{
+ bool ok = false;
+ kdDebug()<<" listVal[0] :"<<listVal[0]<<" listVal[1] :"<<listVal[1]<<endl;
+
+ if ( d->extra()->validity->m_restriction == Restriction::Date )
+ {
+ d->extra()->validity->dateMin = TQDate::fromString( listVal[0] );
+ d->extra()->validity->dateMax = TQDate::fromString( listVal[1] );
+ }
+ else if ( d->extra()->validity->m_restriction == Restriction::Time )
+ {
+ d->extra()->validity->timeMin = TQTime::fromString( listVal[0] );
+ d->extra()->validity->timeMax = TQTime::fromString( listVal[1] );
+ }
+ else
+ {
+ d->extra()->validity->valMin = listVal[0].toDouble(&ok);
+ if ( !ok )
+ {
+ d->extra()->validity->valMin = listVal[0].toInt(&ok);
+ if ( !ok )
+ kdDebug()<<" Try to parse this value :"<<listVal[0]<<endl;
+
+#if 0
+ if ( !ok )
+ d->extra()->validity->valMin = listVal[0];
+#endif
+ }
+ ok=false;
+ d->extra()->validity->valMax = listVal[1].toDouble(&ok);
+ if ( !ok )
+ {
+ d->extra()->validity->valMax = listVal[1].toInt(&ok);
+ if ( !ok )
+ kdDebug()<<" Try to parse this value :"<<listVal[1]<<endl;
+
+#if 0
+ if ( !ok )
+ d->extra()->validity->valMax = listVal[1];
+#endif
+ }
+ }
+}
+
+void Cell::loadOasisValidationCondition( TQString &valExpression )
+{
+ TQString value;
+ if (valExpression.find( "<=" )==0 )
+ {
+ value = valExpression.remove( 0,2 );
+ d->extra()->validity->m_cond = Conditional::InferiorEqual;
+ }
+ else if (valExpression.find( ">=" )==0 )
+ {
+ value = valExpression.remove( 0,2 );
+ d->extra()->validity->m_cond = Conditional::SuperiorEqual;
+ }
+ else if (valExpression.find( "!=" )==0 )
+ {
+ //add Differentto attribute
+ value = valExpression.remove( 0,2 );
+ d->extra()->validity->m_cond = Conditional::DifferentTo;
+ }
+ else if ( valExpression.find( "<" )==0 )
+ {
+ value = valExpression.remove( 0,1 );
+ d->extra()->validity->m_cond = Conditional::Inferior;
+ }
+ else if(valExpression.find( ">" )==0 )
+ {
+ value = valExpression.remove( 0,1 );
+ d->extra()->validity->m_cond = Conditional::Superior;
+ }
+ else if (valExpression.find( "=" )==0 )
+ {
+ value = valExpression.remove( 0,1 );
+ d->extra()->validity->m_cond = Conditional::Equal;
+ }
+ else
+ kdDebug()<<" I don't know how to parse it :"<<valExpression<<endl;
+ if ( d->extra()->validity->m_restriction == Restriction::Date )
+ {
+ d->extra()->validity->dateMin = TQDate::fromString( value );
+ }
+ else if (d->extra()->validity->m_restriction == Restriction::Date )
+ {
+ d->extra()->validity->timeMin = TQTime::fromString( value );
+ }
+ else
+ {
+ bool ok = false;
+ d->extra()->validity->valMin = value.toDouble(&ok);
+ if ( !ok )
+ {
+ d->extra()->validity->valMin = value.toInt(&ok);
+ if ( !ok )
+ kdDebug()<<" Try to parse this value :"<<value<<endl;
+
+#if 0
+ if ( !ok )
+ d->extra()->validity->valMin = value;
+#endif
+ }
+ }
+}
+
+
+bool Cell::load( const TQDomElement & cell, int _xshift, int _yshift,
+ Paste::Mode pm, Paste::Operation op, bool paste )
+{
+ bool ok;
+
+ //
+ // First of all determine in which row and column this
+ // cell belongs.
+ //
+ d->row = cell.attribute( "row" ).toInt( &ok ) + _yshift;
+ if ( !ok ) return false;
+ d->column = cell.attribute( "column" ).toInt( &ok ) + _xshift;
+ if ( !ok ) return false;
+
+ // Validation
+ if ( d->row < 1 || d->row > KS_rowMax )
+ {
+ kdDebug(36001) << "Cell::load: Value out of Range Cell:row=" << d->row << endl;
+ return false;
+ }
+ if ( d->column < 1 || d->column > KS_colMax )
+ {
+ kdDebug(36001) << "Cell::load: Value out of Range Cell:column=" << d->column << endl;
+ return false;
+ }
+
+ //
+ // Load formatting information.
+ //
+ TQDomElement f = cell.namedItem( "format" ).toElement();
+ if ( !f.isNull()
+ && ( (pm == Paste::Normal) || (pm == Paste::Format) || (pm == Paste::NoBorder) ) )
+ {
+ // send pm parameter. Didn't load Borders if pm==NoBorder
+
+ if ( !format()->load( f, pm, paste ) )
+ return false;
+
+ if ( f.hasAttribute( "colspan" ) )
+ {
+ int i = f.attribute("colspan").toInt( &ok );
+ if ( !ok ) return false;
+ // Validation
+ if ( i < 0 || i > KS_spanMax )
+ {
+ kdDebug(36001) << "Value out of range Cell::colspan=" << i << endl;
+ return false;
+ }
+ if (i || d->hasExtra())
+ d->extra()->extraXCells = i;
+ if ( i > 0 )
+ {
+ setFlag(Flag_Merged);
+ }
+ }
+
+ if ( f.hasAttribute( "rowspan" ) )
+ {
+ int i = f.attribute("rowspan").toInt( &ok );
+ if ( !ok ) return false;
+ // Validation
+ if ( i < 0 || i > KS_spanMax )
+ {
+ kdDebug(36001) << "Value out of range Cell::rowspan=" << i << endl;
+ return false;
+ }
+ if (i || d->hasExtra())
+ d->extra()->extraYCells = i;
+ if ( i > 0 )
+ {
+ setFlag(Flag_Merged);
+ }
+ }
+
+ if ( testFlag( Flag_Merged ) )
+ {
+ if (d->hasExtra())
+ mergeCells( d->column, d->row, d->extra()->extraXCells, d->extra()->extraYCells );
+ }
+
+ }
+
+ //
+ // Load the condition section of a cell.
+ //
+ TQDomElement conditionsElement = cell.namedItem( "condition" ).toElement();
+ if ( !conditionsElement.isNull())
+ {
+ if (d->hasExtra())
+ delete d->extra()->conditions;
+ d->extra()->conditions = new Conditions( this );
+ d->extra()->conditions->loadConditions( conditionsElement );
+ d->extra()->conditions->checkMatches();
+ }
+ else if ((pm == Paste::Normal) || (pm == Paste::NoBorder))
+ {
+ //clear the conditional formatting
+ if (d->hasExtra())
+ {
+ delete d->extra()->conditions;
+ d->extra()->conditions = 0;
+ }
+ }
+
+ TQDomElement validity = cell.namedItem( "validity" ).toElement();
+ if ( !validity.isNull())
+ {
+ TQDomElement param = validity.namedItem( "param" ).toElement();
+ if(!param.isNull())
+ {
+ d->extra()->validity = new Validity;
+ if ( param.hasAttribute( "cond" ) )
+ {
+ d->extra()->validity->m_cond = (Conditional::Type) param.attribute("cond").toInt( &ok );
+ if ( !ok )
+ return false;
+ }
+ if ( param.hasAttribute( "action" ) )
+ {
+ d->extra()->validity->m_action = (Action::Type) param.attribute("action").toInt( &ok );
+ if ( !ok )
+ return false;
+ }
+ if ( param.hasAttribute( "allow" ) )
+ {
+ d->extra()->validity->m_restriction = (Restriction::Type) param.attribute("allow").toInt( &ok );
+ if ( !ok )
+ return false;
+ }
+ if ( param.hasAttribute( "valmin" ) )
+ {
+ d->extra()->validity->valMin = param.attribute("valmin").toDouble( &ok );
+ if ( !ok )
+ return false;
+ }
+ if ( param.hasAttribute( "valmax" ) )
+ {
+ d->extra()->validity->valMax = param.attribute("valmax").toDouble( &ok );
+ if ( !ok )
+ return false;
+ }
+ if ( param.hasAttribute( "displaymessage" ) )
+ {
+ d->extra()->validity->displayMessage = ( bool )param.attribute("displaymessage").toInt();
+ }
+ if ( param.hasAttribute( "displayvalidationinformation" ) )
+ {
+ d->extra()->validity->displayValidationInformation = ( bool )param.attribute("displayvalidationinformation").toInt();
+ }
+ if ( param.hasAttribute( "allowemptycell" ) )
+ {
+ d->extra()->validity->allowEmptyCell = ( bool )param.attribute("allowemptycell").toInt();
+ }
+ if ( param.hasAttribute("listvalidity") )
+ {
+ d->extra()->validity->listValidity=TQStringList::split(";", param.attribute("listvalidity") );
+ }
+ }
+ TQDomElement inputTitle = validity.namedItem( "inputtitle" ).toElement();
+ if (!inputTitle.isNull())
+ {
+ d->extra()->validity->titleInfo = inputTitle.text();
+ }
+ TQDomElement inputMessage = validity.namedItem( "inputmessage" ).toElement();
+ if (!inputMessage.isNull())
+ {
+ d->extra()->validity->messageInfo = inputMessage.text();
+ }
+
+ TQDomElement title = validity.namedItem( "title" ).toElement();
+ if (!title.isNull())
+ {
+ d->extra()->validity->title = title.text();
+ }
+ TQDomElement message = validity.namedItem( "message" ).toElement();
+ if (!message.isNull())
+ {
+ d->extra()->validity->message = message.text();
+ }
+ TQDomElement timeMin = validity.namedItem( "timemin" ).toElement();
+ if ( !timeMin.isNull() )
+ {
+ d->extra()->validity->timeMin = toTime(timeMin);
+ }
+ TQDomElement timeMax = validity.namedItem( "timemax" ).toElement();
+ if ( !timeMax.isNull() )
+ {
+ d->extra()->validity->timeMax = toTime(timeMax);
+ }
+ TQDomElement dateMin = validity.namedItem( "datemin" ).toElement();
+ if ( !dateMin.isNull() )
+ {
+ d->extra()->validity->dateMin = toDate(dateMin);
+ }
+ TQDomElement dateMax = validity.namedItem( "datemax" ).toElement();
+ if ( !dateMax.isNull() )
+ {
+ d->extra()->validity->dateMax = toDate(dateMax);
+ }
+ }
+ else if ((pm == Paste::Normal) || (pm == Paste::NoBorder))
+ {
+ // clear the validity
+ removeValidity();
+ }
+
+ //
+ // Load the comment
+ //
+ TQDomElement comment = cell.namedItem( "comment" ).toElement();
+ if ( !comment.isNull() && ( pm == Paste::Normal || pm == Paste::Comment || pm == Paste::NoBorder ))
+ {
+ TQString t = comment.text();
+ //t = t.stripWhiteSpace();
+ format()->setComment( t );
+ }
+
+ //
+ // The real content of the cell is loaded here. It is stored in
+ // the "text" tag, which contains either a text or a CDATA section.
+ //
+ // TODO: make this suck less. We set data twice, in loadCellData, and
+ // also here. Not good.
+ TQDomElement text = cell.namedItem( "text" ).toElement();
+
+ if ( !text.isNull() &&
+ ( pm == Paste::Normal || pm == Paste::Text || pm == Paste::NoBorder || pm == Paste::Result ) )
+ {
+ /* older versions mistakenly put the datatype attribute on the cell
+ instead of the text. Just move it over in case we're parsing
+ an old document */
+ if ( cell.hasAttribute( "dataType" ) ) // new docs
+ text.setAttribute( "dataType", cell.attribute( "dataType" ) );
+
+ TQDomElement result = cell.namedItem( "result" ).toElement();
+ TQString txt = text.text();
+ if ((pm == Paste::Result) && (txt[0] == '='))
+ // paste text of the element, if we want to paste result
+ // and the source cell contains a formula
+ // note that we mustn't use setCellValue after this, or else we lose
+ // all the formulas ...
+ d->strText = result.text();
+ else
+ //otherwise copy everything
+ loadCellData(text, op);
+
+ if ( !result.isNull() )
+ {
+ TQString dataType;
+ TQString t = result.text();
+
+ if ( result.hasAttribute( "dataType" ) )
+ dataType = result.attribute( "dataType" );
+ if ( result.hasAttribute( "outStr" ) )
+ {
+ d->strOutText = result.attribute( "outStr" );
+ if ( !d->strOutText.isEmpty() )
+ clearFlag( Flag_TextFormatDirty );
+ }
+
+ bool clear = true;
+ // boolean ?
+ if( dataType == "Bool" )
+ {
+ if ( t == "false" )
+ setValue( false );
+ else if ( t == "true" )
+ setValue( true );
+ else
+ clear = false;
+ }
+ else if( dataType == "Num" )
+ {
+ bool ok = false;
+ double dd = t.toDouble( &ok );
+ if ( ok )
+ setValue ( dd );
+ else
+ clear = false;
+ }
+ else if( dataType == "Date" )
+ {
+ bool ok = false;
+ double dd = t.toDouble( &ok );
+ if ( ok )
+ setValue ( dd );
+ else
+ {
+ int pos = t.find( '/' );
+ int year = t.mid( 0, pos ).toInt();
+ int pos1 = t.find( '/', pos + 1 );
+ int month = t.mid( pos + 1, ( ( pos1 - 1 ) - pos ) ).toInt();
+ int day = t.right( t.length() - pos1 - 1 ).toInt();
+ TQDate date( year, month, day );
+ if ( date.isValid() )
+ setValue( date );
+ else
+ clear = false;
+ }
+ }
+ else if( dataType == "Time" )
+ {
+ bool ok = false;
+ double dd = t.toDouble( &ok );
+ if ( ok )
+ setCellValue( dd );
+ else
+ {
+ int hours = -1;
+ int minutes = -1;
+ int second = -1;
+ int pos, pos1;
+ pos = t.find( ':' );
+ hours = t.mid( 0, pos ).toInt();
+ pos1 = t.find( ':', pos + 1 );
+ minutes = t.mid( pos + 1, ( ( pos1 - 1 ) - pos ) ).toInt();
+ second = t.right( t.length() - pos1 - 1 ).toInt();
+ TQTime time( hours, minutes, second );
+ if ( time.isValid() )
+ setValue( time );
+ else
+ clear = false;
+ }
+ }
+ else
+ {
+ setValue( t );
+ }
+
+ // if ( clear )
+ // clearFlag( Flag_CalcDirty );
+ }
+ }
+
+ return true;
+}
+
+bool Cell::loadCellData(const TQDomElement & text, Paste::Operation op )
+{
+ //TODO: use converter()->asString() to generate strText
+
+ TQString t = text.text();
+ t = t.stripWhiteSpace();
+
+ setFlag(Flag_LayoutDirty);
+ setFlag(Flag_TextFormatDirty);
+
+ // A formula like =A1+A2 ?
+ if( t[0] == '=' )
+ {
+ t = decodeFormula( t, d->column, d->row );
+ setCellText (pasteOperation( t, d->strText, op ));
+
+ setFlag(Flag_CalcDirty);
+ clearAllErrors();
+
+ if ( !makeFormula() )
+ kdError(36001) << "ERROR: Syntax ERROR" << endl;
+ }
+ // rich text ?
+ else if (t[0] == '!' )
+ {
+ // KSpread pre 1.4 stores hyperlink as rich text (first char is '!')
+ // extract the link and the correspoding text
+ // This is a rather dirty hack, but enough for KSpread generated XML
+ bool inside_tag = false;
+ TQString qml_text;
+ TQString tag;
+ TQString qml_link;
+
+ for( unsigned i = 1; i < t.length(); i++ )
+ {
+ TQChar ch = t[i];
+ if( ch == '<' )
+ {
+ if( !inside_tag )
+ {
+ inside_tag = true;
+ tag = TQString();
+ }
+ }
+ else if( ch == '>' )
+ {
+ if( inside_tag )
+ {
+ inside_tag = false;
+ if( tag.startsWith( "a href=\"", true ) )
+ if( tag.endsWith( "\"" ) )
+ qml_link = tag.mid( 8, tag.length()-9 );
+ tag = TQString();
+ }
+ }
+ else
+ {
+ if( !inside_tag )
+ qml_text += ch;
+ else
+ tag += ch;
+ }
+ }
+
+ if( !qml_link.isEmpty() )
+ d->extra()->link = qml_link;
+ d->strText = qml_text;
+ setValue( d->strText );
+ }
+ else
+ {
+ bool newStyleLoading = true;
+ TQString dataType;
+
+ if ( text.hasAttribute( "dataType" ) ) // new docs
+ {
+ dataType = text.attribute( "dataType" );
+ }
+ else // old docs: do the ugly solution of calling checkTextInput to parse the text
+ {
+ // ...except for date/time
+ if (isDate() && ( t.contains('/') == 2 ))
+ dataType = "Date";
+ else if (isTime() && ( t.contains(':') == 2 ) )
+ dataType = "Time";
+ else
+ {
+ d->strText = pasteOperation( t, d->strText, op );
+ checkTextInput();
+ //kdDebug(36001) << "Cell::load called checkTextInput, got dataType=" << dataType << " t=" << t << endl;
+ newStyleLoading = false;
+ }
+ }
+
+ if ( newStyleLoading )
+ {
+ d->value = Value::empty();
+ clearAllErrors();
+
+ // boolean ?
+ if( dataType == "Bool" )
+ {
+ bool val = (t.lower() == "true");
+ setCellValue (val);
+ }
+
+ // number ?
+ else if( dataType == "Num" )
+ {
+ bool ok = false;
+ if (t.contains('.'))
+ setValue ( Value( t.toDouble(&ok) ) ); // We save in non-localized format
+ else
+ setValue ( Value( t.toLong(&ok) ) );
+ if ( !ok )
+ {
+ kdWarning(36001) << "Couldn't parse '" << t << "' as number." << endl;
+ }
+ /* We will need to localize the text version of the number */
+ TDELocale* locale = format()->sheet()->doc()->locale();
+
+ /* TDELocale::formatNumber requires the precision we want to return.
+ */
+ int precision = t.length() - t.find('.') - 1;
+
+ if ( formatType() == Percentage_format )
+ {
+ if (value().isInteger())
+ t = locale->formatNumber( value().asInteger() * 100 );
+ else
+ t = locale->formatNumber( value().asFloat() * 100.0, precision );
+ d->strText = pasteOperation( t, d->strText, op );
+ d->strText += '%';
+ }
+ else
+ {
+ if (value().isInteger())
+ t = locale->formatLong(value().asInteger());
+ else
+ t = locale->formatNumber(value().asFloat(), precision);
+ d->strText = pasteOperation( t, d->strText, op );
+ }
+ }
+
+ // date ?
+ else if( dataType == "Date" )
+ {
+ int pos = t.find('/');
+ int year = t.mid(0,pos).toInt();
+ int pos1 = t.find('/',pos+1);
+ int month = t.mid(pos+1,((pos1-1)-pos)).toInt();
+ int day = t.right(t.length()-pos1-1).toInt();
+ setValue( TQDate(year,month,day) );
+ if ( value().asDate().isValid() ) // Should always be the case for new docs
+ d->strText = locale()->formatDate( value().asDate(), true );
+ else // This happens with old docs, when format is set wrongly to date
+ {
+ d->strText = pasteOperation( t, d->strText, op );
+ checkTextInput();
+ }
+ }
+
+ // time ?
+ else if( dataType == "Time" )
+ {
+ int hours = -1;
+ int minutes = -1;
+ int second = -1;
+ int pos, pos1;
+ pos = t.find(':');
+ hours = t.mid(0,pos).toInt();
+ pos1 = t.find(':',pos+1);
+ minutes = t.mid(pos+1,((pos1-1)-pos)).toInt();
+ second = t.right(t.length()-pos1-1).toInt();
+ setValue( TQTime(hours,minutes,second) );
+ if ( value().asTime().isValid() ) // Should always be the case for new docs
+ d->strText = locale()->formatTime( value().asTime(), true );
+ else // This happens with old docs, when format is set wrongly to time
+ {
+ d->strText = pasteOperation( t, d->strText, op );
+ checkTextInput();
+ }
+ }
+
+ else
+ {
+ // Set the cell's text
+ d->strText = pasteOperation( t, d->strText, op );
+ setValue( d->strText );
+ }
+ }
+ }
+
+ if ( text.hasAttribute( "outStr" ) ) // very new docs
+ {
+ d->strOutText = text.attribute( "outStr" );
+ if ( !d->strOutText.isEmpty() )
+ clearFlag( Flag_TextFormatDirty );
+ }
+
+ if ( !format()->sheet()->isLoading() )
+ setCellText( d->strText );
+
+ if ( d->hasExtra() && d->extra()->conditions )
+ d->extra()->conditions->checkMatches();
+
+ return true;
+}
+
+TQTime Cell::toTime(const TQDomElement &element)
+{
+ //TODO: can't we use tryParseTime (after modification) instead?
+ TQString t = element.text();
+ t = t.stripWhiteSpace();
+ int hours = -1;
+ int minutes = -1;
+ int second = -1;
+ int pos, pos1;
+ pos = t.find(':');
+ hours = t.mid(0,pos).toInt();
+ pos1 = t.find(':',pos+1);
+ minutes = t.mid(pos+1,((pos1-1)-pos)).toInt();
+ second = t.right(t.length()-pos1-1).toInt();
+ setValue( Value( TQTime(hours,minutes,second)) );
+ return value().asTime();
+}
+
+TQDate Cell::toDate(const TQDomElement &element)
+{
+ TQString t = element.text();
+ int pos;
+ int pos1;
+ int year = -1;
+ int month = -1;
+ int day = -1;
+ pos = t.find('/');
+ year = t.mid(0,pos).toInt();
+ pos1 = t.find('/',pos+1);
+ month = t.mid(pos+1,((pos1-1)-pos)).toInt();
+ day = t.right(t.length()-pos1-1).toInt();
+ setValue( Value( TQDate(year,month,day) ) );
+ return value().asDate();
+}
+
+TQString Cell::pasteOperation( const TQString &new_text, const TQString &old_text, Paste::Operation op )
+{
+ if ( op == Paste::OverWrite )
+ return new_text;
+
+ TQString tmp_op;
+ TQString tmp;
+ TQString old;
+
+ if( !new_text.isEmpty() && new_text[0] == '=' )
+ {
+ tmp = new_text.right( new_text.length() - 1 );
+ }
+ else
+ {
+ tmp = new_text;
+ }
+
+ if ( old_text.isEmpty() &&
+ ( op == Paste::Add || op == Paste::Mul || op == Paste::Sub || op == Paste::Div ) )
+ {
+ old = "=0";
+ }
+
+ if( !old_text.isEmpty() && old_text[0] == '=' )
+ {
+ old = old_text.right( old_text.length() - 1 );
+ }
+ else
+ {
+ old = old_text;
+ }
+
+ bool b1, b2;
+ tmp.toDouble( &b1 );
+ old.toDouble( &b2 );
+ if (b1 && !b2 && old.length() == 0)
+ {
+ old = "0";
+ b2 = true;
+ }
+
+ if( b1 && b2 )
+ {
+ switch( op )
+ {
+ case Paste::Add:
+ tmp_op = TQString::number(old.toDouble()+tmp.toDouble());
+ break;
+ case Paste::Mul :
+ tmp_op = TQString::number(old.toDouble()*tmp.toDouble());
+ break;
+ case Paste::Sub:
+ tmp_op = TQString::number(old.toDouble()-tmp.toDouble());
+ break;
+ case Paste::Div:
+ tmp_op = TQString::number(old.toDouble()/tmp.toDouble());
+ break;
+ default:
+ Q_ASSERT( 0 );
+ }
+
+ setFlag(Flag_LayoutDirty);
+ clearAllErrors();
+
+ return tmp_op;
+ }
+ else if ( ( new_text[0] == '=' && old_text[0] == '=' ) ||
+ ( b1 && old_text[0] == '=' ) || ( new_text[0] == '=' && b2 ) )
+ {
+ switch( op )
+ {
+ case Paste::Add :
+ tmp_op="=("+old+")+"+"("+tmp+")";
+ break;
+ case Paste::Mul :
+ tmp_op="=("+old+")*"+"("+tmp+")";
+ break;
+ case Paste::Sub:
+ tmp_op="=("+old+")-"+"("+tmp+")";
+ break;
+ case Paste::Div:
+ tmp_op="=("+old+")/"+"("+tmp+")";
+ break;
+ default :
+ Q_ASSERT( 0 );
+ }
+
+ tmp_op = decodeFormula( tmp_op, d->column, d->row );
+ setFlag(Flag_LayoutDirty);
+ clearAllErrors();
+
+ return tmp_op;
+ }
+
+ tmp = decodeFormula( new_text, d->column, d->row );
+ setFlag(Flag_LayoutDirty);
+ clearAllErrors();
+
+ return tmp;
+}
+
+TQString Cell::testAnchor( int x, int y ) const
+{
+ if( link().isEmpty() )
+ return TQString();
+
+ const Doc* doc = format()->sheet()->doc();
+ int x1 = doc->zoomItX( d->textX );
+ int y1 = doc->zoomItX( d->textY - d->textHeight );
+ int x2 = doc->zoomItX( d->textX + d->textWidth );
+ int y2 = doc->zoomItX( d->textY );
+
+ if( x > x1 ) if( x < x2 )
+ if( y > y1 ) if( y < y2 )
+ return link();
+
+ return TQString();
+}
+
+void Cell::sheetDies()
+{
+ // Avoid unobscuring the cells in the destructor.
+ if (d->hasExtra())
+ {
+ d->extra()->extraXCells = 0;
+ d->extra()->extraYCells = 0;
+ d->extra()->mergedXCells = 0;
+ d->extra()->mergedYCells = 0;
+ }
+
+ //d->nextCell = 0;
+ //d->previousCell = 0;
+}
+
+Cell::~Cell()
+{
+ if ( d->nextCell )
+ d->nextCell->setPreviousCell( d->previousCell );
+ if ( d->previousCell )
+ d->previousCell->setNextCell( d->nextCell );
+
+ if (d->hasExtra())
+ {
+ delete d->extra()->validity;
+ }
+
+ // Unobscure cells.
+ int extraXCells = d->hasExtra() ? d->extra()->extraXCells : 0;
+ int extraYCells = d->hasExtra() ? d->extra()->extraYCells : 0;
+ for( int x = 0; x <= extraXCells; ++x )
+ for( int y = (x == 0) ? 1 : 0; // avoid looking at (+0,+0)
+ y <= extraYCells; ++y )
+ {
+ Cell* cell = format()->sheet()->cellAt( d->column + x, d->row + y );
+ if ( cell )
+ cell->unobscure(this);
+ }
+
+ d->value = Value::empty();
+
+ if (!isDefault())
+ valueChanged (); //our value has been changed (is now null), but only if we aren't default
+
+ delete d->format;
+ delete d;
+}
+
+bool Cell::operator > ( const Cell & cell ) const
+{
+ if ( value().isNumber() ) // ### what about bools ?
+ {
+ if ( cell.value().isNumber() )
+ return value().asFloat() > cell.value().asFloat();
+ else
+ return false; // numbers are always < than texts
+ }
+ else if(isDate())
+ {
+ if( cell.isDate() )
+ return value().asDate() > cell.value().asDate();
+ else if (cell.value().isNumber())
+ return true;
+ else
+ return false; //date are always < than texts and time
+ }
+ else if(isTime())
+ {
+ if( cell.isTime() )
+ return value().asTime() > cell.value().asTime();
+ else if( cell.isDate())
+ return true; //time are always > than date
+ else if( cell.value().isNumber())
+ return true;
+ else
+ return false; //time are always < than texts
+ }
+ else
+ {
+ if ( Map::respectCase )
+ return value().asString().compare(cell.value().asString()) > 0;
+ else
+ return ( value().asString() ).lower().compare(cell.value().asString().lower()) > 0;
+ }
+}
+
+bool Cell::operator < ( const Cell & cell ) const
+{
+ if ( value().isNumber() )
+ {
+ if ( cell.value().isNumber() )
+ return value().asFloat() < cell.value().asFloat();
+ else
+ return true; // numbers are always < than texts
+ }
+ else if(isDate())
+ {
+ if( cell.isDate() )
+ return value().asDateTime().date() < cell.value().asDateTime().date();
+ else if( cell.value().isNumber())
+ return false;
+ else
+ return true; //date are always < than texts and time
+ }
+ else if(isTime())
+ {
+ if( cell.isTime() )
+ return value().asDateTime().time() < cell.value().asDateTime().time();
+ else if(cell.isDate())
+ return false; //time are always > than date
+ else if( cell.value().isNumber())
+ return false;
+ else
+ return true; //time are always < than texts
+ }
+ else
+ {
+ if ( Map::respectCase )
+ return value().asString().compare(cell.value().asString()) < 0;
+ else
+ return ( value().asString() ).lower().compare(cell.value().asString().lower()) < 0;
+ }
+}
+
+bool Cell::operator==( const Cell& other ) const
+{
+ if ( d->strText != other.d->strText )
+ return false;
+ if ( d->value != other.d->value )
+ return false;
+ if ( *d->format != *other.d->format )
+ return false;
+ if ( d->hasExtra() )
+ {
+ if ( !other.d->hasExtra() )
+ return false;
+ if ( d->extra()->link != other.d->extra()->link )
+ return false;
+ if ( d->extra()->mergedXCells != other.d->extra()->mergedXCells )
+ return false;
+ if ( d->extra()->mergedYCells != other.d->extra()->mergedYCells )
+ return false;
+ if ( d->extra()->conditions )
+ {
+ if ( !other.d->extra()->conditions )
+ return false;
+ if ( *d->extra()->conditions != *other.d->extra()->conditions )
+ return false;
+ }
+ if ( d->extra()->validity )
+ {
+ if ( !other.d->extra()->validity )
+ return false;
+ if ( *d->extra()->validity != *other.d->extra()->validity )
+ return false;
+ }
+ }
+ return true;
+}
+
+TQRect Cell::cellRect()
+{
+ Q_ASSERT(!isDefault());
+ return TQRect(TQPoint(d->column, d->row), TQPoint(d->column, d->row));
+}
+
+TQValueList<Conditional> Cell::conditionList() const
+{
+ if ( !d->hasExtra() || !d->extra()->conditions )
+ {
+ TQValueList<Conditional> emptyList;
+ return emptyList;
+ }
+
+ return d->extra()->conditions->conditionList();
+}
+
+void Cell::setConditionList( const TQValueList<Conditional> & newList )
+{
+ if (d->hasExtra())
+ delete d->extra()->conditions;
+ d->extra()->conditions = new Conditions( this );
+ d->extra()->conditions->setConditionList( newList );
+ d->extra()->conditions->checkMatches();
+}
+
+bool Cell::hasError() const
+{
+ return ( testFlag(Flag_ParseError) ||
+ testFlag(Flag_CircularCalculation) ||
+ testFlag(Flag_DependancyError));
+}
+
+void Cell::clearAllErrors()
+{
+ clearFlag( Flag_ParseError );
+ clearFlag( Flag_CircularCalculation );
+ clearFlag( Flag_DependancyError );
+}
+
+bool Cell::calcDirtyFlag()
+{
+ return isFormula() ? testFlag( Flag_CalcDirty ) : false;
+}
+
+bool Cell::layoutDirtyFlag() const
+{
+ return testFlag( Flag_LayoutDirty );
+}
+
+void Cell::clearDisplayDirtyFlag()
+{
+ clearFlag( Flag_DisplayDirty );
+}
+
+void Cell::setDisplayDirtyFlag()
+{
+ setFlag( Flag_DisplayDirty );
+}
+
+bool Cell::doesMergeCells() const
+{
+ return testFlag( Flag_Merged );
+}
+
+void Cell::clearFlag( CellFlags flag )
+{
+ d->flags &= ~(TQ_UINT32)flag;
+}
+
+void Cell::setFlag( CellFlags flag )
+{
+ d->flags |= (TQ_UINT32)flag;
+}
+
+bool Cell::testFlag( CellFlags flag ) const
+{
+ return ( d->flags & (TQ_UINT32)flag );
+}
+
+
+void Cell::checkForNamedAreas( TQString & formula ) const
+{
+ KSPLoadingInfo* loadinginfo = sheet()->doc()->loadingInfo();
+ if(! loadinginfo) {
+ kdDebug() << "Cell::checkForNamedAreas loadinginfo is NULL" << endl;
+ return;
+ }
+
+ int l = formula.length();
+ int i = 0;
+ TQString word;
+ int start = 0;
+ while ( i < l )
+ {
+ if ( formula[i].isLetterOrNumber() )
+ {
+ word += formula[i];
+ ++i;
+ continue;
+ }
+ if ( !word.isEmpty() )
+ {
+ if ( loadinginfo->findWordInAreaList(word) )
+ {
+ formula = formula.replace( start, word.length(), "'" + word + "'" );
+ l = formula.length();
+ ++i;
+ kdDebug() << "Formula: " << formula << ", L: " << l << ", i: " << i + 1 <<endl;
+ }
+ }
+
+ ++i;
+ word = "";
+ start = i;
+ }
+ if ( !word.isEmpty() )
+ {
+ if ( loadinginfo->findWordInAreaList(word) )
+ {
+ formula = formula.replace( start, word.length(), "'" + word + "'" );
+ l = formula.length();
+ ++i;
+ kdDebug() << "Formula: " << formula << ", L: " << l << ", i: " << i + 1 <<endl;
+ }
+ }
+}