kspread

kspread_cell.cpp
1/* This file is part of the KDE project
2
3 Copyright 2006 Stefan Nikolaus <stefan.nikolaus@kdemail.net>
4 Copyright 2005 Raphael Langerhorst <raphael.langerhorst@kdemail.net>
5 Copyright 2004-2005 Tomas Mecir <mecirt@gmail.com>
6 Copyright 2004-2006 Inge Wallin <inge@lysator.liu.se>
7 Copyright 1999-2002,2004,2005 Laurent Montel <montel@kde.org>
8 Copyright 2002-2005 Ariya Hidayat <ariya@kde.org>
9 Copyright 2001-2003 Philipp Mueller <philipp.mueller@gmx.de>
10 Copyright 2002-2003 Norbert Andres <nandres@web.de>
11 Copyright 2003 Reinhart Geiser <geiseri@kde.org>
12 Copyright 2003-2005 Meni Livne <livne@kde.org>
13 Copyright 2003 Peter Simonsson <psn@linux.se>
14 Copyright 1999-2002 David Faure <faure@kde.org>
15 Copyright 2000-2002 Werner Trobin <trobin@kde.org>
16 Copyright 1999,2002 Harri Porten <porten@kde.org>
17 Copyright 2002 John Dailey <dailey@vt.edu>
18 Copyright 1998-2000 Torben Weis <weis@kde.org>
19 Copyright 2000 Bernd Wuebben <wuebben@kde.org>
20 Copyright 2000 Simon Hausmann <hausmann@kde.org
21 Copyright 1999 Stephan Kulow <coolo@kde.org>
22 Copyright 1999 Michael Reiher <michael.reiher.gmx.de>
23 Copyright 1999 Boris Wedl <boris.wedl@kfunigraz.ac.at>
24 Copyright 1998-1999 Reginald Stadlbauer <reggie@kde.org>
25
26
27 This library is free software; you can redistribute it and/or
28 modify it under the terms of the GNU Library General Public
29 License as published by the Free Software Foundation; either
30 version 2 of the License, or (at your option) any later version.
31
32 This library is distributed in the hope that it will be useful,
33 but WITHOUT ANY WARRANTY; without even the implied warranty of
34 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
35 Library General Public License for more details.
36
37 You should have received a copy of the GNU Library General Public License
38 along with this library; see the file COPYING.LIB. If not, write to
39 the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
40 * Boston, MA 02110-1301, USA.
41*/
42
43#include <stdlib.h>
44#include <ctype.h>
45#include <float.h>
46#include <math.h>
47
48#include <tqapplication.h>
49#include <tqpopupmenu.h>
50#include <tqregexp.h>
51
52#include "kspread_canvas.h"
53#include "kspread_condition.h"
54#include "kspread_doc.h"
55#include "kspread_format.h"
56#include "kspread_global.h"
57#include "kspread_map.h"
58#include "kspread_sheet.h"
59#include "kspread_sheetprint.h"
60#include "kspread_style.h"
61#include "kspread_style_manager.h"
62#include "kspread_util.h"
63#include "ksploadinginfo.h"
64#include "kspread_genvalidationstyle.h"
65#include "kspread_locale.h"
66#include "kspread_value.h"
67#include "kspread_view.h"
68#include "kspread_value.h"
69#include "formula.h"
70#include "selection.h"
71#include "valueconverter.h"
72#include "valueformatter.h"
73#include "valueparser.h"
74
75#include <KoStyleStack.h>
76#include <KoRect.h>
77#include <KoXmlNS.h>
78#include <KoDom.h>
79#include <KoXmlWriter.h>
80#include <KoOasisStyles.h>
81
82#include <tdemessagebox.h>
83
84#include <kdebug.h>
85
86using namespace KSpread;
87
88#define BORDER_SPACE 1
89
90
95namespace Cell_LNS
96{
97 TQChar decimal_point = '\0';
98}
99
100using namespace Cell_LNS;
101
102
103// Some variables are placed in Cell::Extra because normally they're
104// not required in simple case of cell(s). For example, most plain
105// text cells don't need to store information about spanned columns
106// and rows, as this is only the case with merged cells.
107//
108// When the cell is getting complex (e.g. merged with other cells,
109// contains rich text, has validation criteria, etc), this Cell::Extra
110// is allocated by Cell::Private and starts to be
111// available. Otherwise, it won't exist at all.
112
113class Cell::Extra
114{
115public:
116 Extra() {}
117
118 // Not empty when the cell holds a link
119 TQString link;
120
121 // Number of cells explicitly merged by the user in X and Y directions.
122 int mergedXCells;
123 int mergedYCells;
124
125 // Number of additional cells.
126 int extraXCells;
127 int extraYCells;
128
129 // If this cell overlaps other cells, then we have the cells width and
130 // height stored here. These values do not mean anything unless
131 // extraXCells and/or extraYCells are different from 0.
132 double extraWidth;
133 double extraHeight;
134
135 // A list of cells that obscure this one.
136 // If this list is not empty, then this cell is obscured by another
137 // enlarged object. This means that we have to call this object in order
138 // of painting it for example instead of painting 'this'.
139 //
140 // FIXME (comment): If the list consists of more than one obscuring
141 // element, then is there an order between them that
142 // is important?
143 TQValueList<Cell*> obscuringCells;
144
145 // If non-NULL, contains a pointer to a condition or a validity test.
146 Conditions *conditions;
147 Validity *validity;
148
149 // Store the number of line when multirow is used (default is 0)
150 int nbLines;
151
152private:
153 // Don't allow implicit copy.
154 Extra& operator=( const Extra& );
155};
156
157
158class Cell::Private
159{
160public:
161
162 Private();
163 ~Private();
164
165public:
166
167 // This cell's row and column. If either of them is 0, this is the
168 // default cell and its row/column can not be determined. Note that
169 // in the isDefault() method, only column is tested.
170 int row;
171 int column;
172
173 // Value of the cell, either typed by user or as result of formula
174 Value value;
175
176 // Holds the user's input.
177 //
178 // FIXME:
179 // Eventually, we'll want to get rid of strText and generate
180 // user's input on-the-fly. Then, for normal cells, we'll generate
181 // this string using converter()->asString
182 // (value()).
183 //
184 // Here the problem is, that strText also holds the formula -
185 // we'll need to provide some method to generate it from the
186 // parsed version, created in KSpread::Formula. Hence, we won't be
187 // able to get rid of strText until we switch to the new formula
188 // parser and until we write some method that re-generates the
189 // input formula...
190 //
191 // Alternately, we can keep using strText for formulas and
192 // generate it dynamically for static cells...
193 //
194 // /Tomas
195 //
196 TQString strText;
197
198 // This is the text we want to display. Not necessarily the same
199 // as strText, e.g. strText="1" and strOutText="1.00" Also holds
200 // value that we got from calculation, formerly known as
201 // strFormulaOut
202 TQString strOutText;
203
204 // the Formula object for the cell
205 KSpread::Formula *formula;
206
207 // Position and dimension of displayed text.
208 // FIXME (comment): Which coordinate system? pixels? mm/cm? zoom?
209 double textX;
210 double textY;
211 double textWidth;
212 double textHeight;
213
214 // result of "fm.ascent()" in makeLayout. used in offsetAlign.
215 int fmAscent;
216
217 // Pointers to neighboring cells.
218 // FIXME (comment): Which order?
219 Cell *nextCell;
220 Cell *previousCell;
221
222 bool hasExtra() const { return (cellExtra != 0); };
223 Extra *extra();
224
225 Format *format;
226 TQ_UINT32 flags;
227
228private:
229 // "Extra stuff", see explanation for Cell::Extra.
230 Extra *cellExtra;
231};
232
233
234Cell::Private::Private()
235{
236 // Some basic data.
237 row = 0;
238 column = 0;
239 value = Value::empty();
240 formula = 0;
241
242 // Formatting
243 textX = 0.0;
244 textY = 0.0;
245 textWidth = 0.0;
246 textHeight = 0.0;
247 fmAscent = 0;
248
249 nextCell = 0;
250 previousCell = 0;
251
252 // Default is to not have the "extra" stuff in a cell.
253 cellExtra = 0;
254 format = 0;
255 flags = 0;
256}
257
258
259Cell::Private::~Private()
260{
261 delete cellExtra;
262 delete formula;
263}
264
265
266Cell::Extra* Cell::Private::extra()
267{
268 if ( !cellExtra ) {
269 cellExtra = new Extra;
270 cellExtra->conditions = 0;
271 cellExtra->validity = 0;
272
273 cellExtra->mergedXCells = 0;
274 cellExtra->mergedYCells = 0;
275 cellExtra->extraXCells = 0;
276 cellExtra->extraYCells = 0;
277 cellExtra->extraWidth = 0.0;
278 cellExtra->extraHeight = 0.0;
279 cellExtra->nbLines = 0;
280// cellExtra->highlight = TQColor(0,0,0);
281 }
282
283 return cellExtra;
284}
285
286
287/*****************************************************************************
288 *
289 * Cell
290 *
291 *****************************************************************************/
292
293
294Cell::Cell( Sheet * _sheet, int _column, int _row )
295{
296 d = new Private;
297 d->row = _row;
298 d->column = _column;
299 d->format = new Format(_sheet, _sheet->doc()->styleManager()->defaultStyle());
300 d->format->setCell(this);
302}
303
304
305Cell::Cell( Sheet * _sheet, Style * _style, int _column, int _row )
306{
307 d = new Private;
308 d->row = _row;
309 d->column = _column;
310 d->format = new Format( _sheet, _style );
311 d->format->setCell(this);
312
314}
315
316Format* Cell::format() const
317{
318 return d->format;
319}
320
321// Return the sheet that this cell belongs to.
322Sheet * Cell::sheet() const
323{
324 return d->format->sheet();
325}
326
327// Return true if this is the default cell.
328bool Cell::isDefault() const
329{
330 return ( d->column == 0 );
331}
332
333// Return the row number of this cell.
334int Cell::row() const
335{
336 // Make sure this isn't called for the default cell. This assert
337 // can save you (could have saved me!) the hassle of some very
338 // obscure bugs.
339
340 if ( isDefault() )
341 {
342 kdWarning(36001) << "Error: Calling Cell::row() for default cell" << endl;
343 return 0;
344 }
345
346 return d->row;
347}
348
349
350// Return the column number of this cell.
351//
352int Cell::column() const
353{
354 // Make sure this isn't called for the default cell. This assert
355 // can save you (could have saved me!) the hassle of some very
356 // obscure bugs.
357 if ( isDefault() )
358 {
359 kdWarning(36001) << "Error: Calling Cell::column() for default cell" << endl;
360 return 0;
361 }
362 return d->column;
363}
364
365
366// Return the name of this cell, i.e. the string that the user would
367// use to reference it. Example: A1, BZ16
368//
369TQString Cell::name() const
370{
371 return name( d->column, d->row );
372}
373
374
375// Return the name of any cell given by (col, row).
376//
377TQString Cell::name( int col, int row )
378{
379 return columnName( col ) + TQString::number( row );
380}
381
382
383// Return the name of this cell, including the sheet name.
384// Example: sheet1!A5
385//
386TQString Cell::fullName() const
387{
388 return fullName( sheet(), d->column, d->row );
389}
390
391
392// Return the full name of any cell given a sheet and (col, row).
393//
394TQString Cell::fullName( const Sheet* s, int col, int row )
395{
396 return s->sheetName() + "!" + name( col, row );
397}
398
399
400// Return the symbolic name of the column of this cell. Examples: A, BB.
401//
402TQString Cell::columnName() const
403{
404 return columnName( d->column );
405}
406
407TDELocale* Cell::locale() const
408{
409 return d->format->sheet()->doc()->locale();
410}
411
412// Return the symbolic name of any column.
413//
414TQString Cell::columnName( uint column )
415{
416 TQString str;
417 unsigned digits = 1;
418 unsigned offset = 0;
419
420 column--;
421
422 if( column > 4058115285U ) return TQString("@@@");
423
424 for( unsigned limit = 26; column >= limit+offset; limit *= 26, digits++ )
425 offset += limit;
426
427 for( unsigned c = column - offset; digits; --digits, c/=26 )
428 str.prepend( TQChar( 'A' + (c%26) ) );
429
430 return str;
431}
432
433
434// Return true if this cell is a formula.
435//
436bool Cell::isFormula() const
437{
438 return d->strText[0] == '=';
439}
440
441
442// Return the input text of this cell. This could, for instance, be a
443// formula.
444//
445// FIXME: These two functions are inconsistently named. It should be
446// either text() and outText() or strText() and strOutText().
447//
448TQString Cell::text() const
449{
450 return d->strText;
451}
452
453
454// Return the out text, i.e. the text that is visible in the cells
455// square when shown. This could, for instance, be the calculated
456// result of a formula.
457//
458TQString Cell::strOutText() const
459{
460 return d->strOutText;
461}
462
463Formula *Cell::formula () const
464{
465 return d->formula;
466}
467
468
469// Return the value of this cell.
470//
471const Value Cell::value() const
472{
473 return d->value;
474}
475
476
477// Set the value of this cell. It also clears all errors if the value
478// itself is not an error.
479//
480// In addition to this, it calculates the outstring and sets the dirty
481// flags so that a redraw is forced.
482//
483void Cell::setValue( const Value& v )
484{
485 if (v.type() != Value::Error)
487
488 //If the value has not changed then we don't need to do anything
489 //(ie. no need to relayout, update dependant cells etc.),
490 //unless this cell contains a formula, in which case its dependancies might have changed
491 //even though the value has not. For example, if this cell was previously empty (and its value is
492 //therefore empty) and a new dependency upon an empty cell has been added. The new value would still
493 //be empty, but the dependencies need to be updated (via the call to valueChanged() below).
494 if ( ( d->value == v ) && ( !isFormula() ) )
495 return;
496
497 d->value = v;
498
499 setFlag(Flag_LayoutDirty);
500 setFlag(Flag_TextFormatDirty);
501
502 // Format and set the outText.
503 setOutputText();
504
505 // Set the displayed text, if we hold an error value.
506 if (d->value.type() == Value::Error)
507 d->strOutText = d->value.errorMessage ();
508
509 // Value of the cell has changed - trigger necessary actions
510 valueChanged ();
511
512 if ( !format()->sheet()->isLoading() )
513 format()->sheet()->setRegionPaintDirty(cellRect());
514}
515
516void Cell::setCellValue (const Value &v, FormatType fmtType, const TQString &txt)
517{
518 if ( !txt.isNull() )
519 {
520 d->strText = txt;
521 if ( isFormula() )
522 makeFormula();
523 }
524 else if ( !isFormula() )
525 d->strText = sheet()->doc()->converter()->asString (v).asString();
526 if (fmtType != No_format)
527 format()->setFormatType (fmtType);
528 setValue (v);
529}
530
531// FIXME: Continue commenting and cleaning here (ingwa)
532
533
534Cell* Cell::previousCell() const
535{
536 return d->previousCell;
537}
538
539Cell* Cell::nextCell() const
540{
541 return d->nextCell;
542}
543
544void Cell::setPreviousCell( Cell* c )
545{
546 d->previousCell = c;
547}
548
549void Cell::setNextCell( Cell* c )
550{
551 d->nextCell = c;
552}
553
554Validity* Cell::getValidity( int newStruct )
555{
556 if ( (!newStruct) && (!d->hasExtra()))
557 //we don't have validity struct and we don't want one
558 return 0;
559
560 if( ( d->extra()->validity == 0 ) && ( newStruct == -1 ) )
561 d->extra()->validity = new Validity;
562 return d->extra()->validity;
563}
564
565void Cell::removeValidity()
566{
567 if (!d->hasExtra())
568 return;
569
570 delete d->extra()->validity;
571 d->extra()->validity = 0;
572}
573
574
575void Cell::copyFormat( const int column , const int row )
576{
577 const Cell * cell = format()->sheet()->cellAt( column , row );
578
579 copyFormat( cell );
580}
581
582void Cell::copyFormat( const Cell* cell )
583{
584
585 Q_ASSERT(cell);
586
587 d->value.setFormat(cell->d->value.format());
588 format()->copy(*(cell->format()));
589
590 /*format()->setAlign( cell->format()->align( _column, _row ) );
591 format()->setAlignY( cell->format()->alignY( _column, _row ) );
592 format()->setTextFont( cell->format()->textFont( _column, _row ) );
593 format()->setTextColor( cell->format()->textColor( _column, _row ) );
594 format()->setBgColor( cell->bgColor( _column, _row) );
595 setLeftBorderPen( cell->leftBorderPen( _column, _row ) );
596 setTopBorderPen( cell->topBorderPen( _column, _row ) );
597 setBottomBorderPen( cell->bottomBorderPen( _column, _row ) );
598 setRightBorderPen( cell->rightBorderPen( _column, _row ) );
599 format()->setFallDiagonalPen( cell->format()->fallDiagonalPen( _column, _row ) );
600 format()->setGoUpDiagonalPen( cell->format()->goUpDiagonalPen( _column, _row ) );
601 format()->setBackGroundBrush( cell->backGroundBrush( _column, _row) );
602 format()->setPrecision( cell->format()->precision( _column, _row ) );
603 format()->setPrefix( cell->format()->prefix( _column, _row ) );
604 format()->setPostfix( cell->format()->postfix( _column, _row ) );
605 format()->setFloatFormat( cell->format()->floatFormat( _column, _row ) );
606 format()->setFloatColor( cell->format()->floatColor( _column, _row ) );
607 format()->setMultiRow( cell->format()->multiRow( _column, _row ) );
608 format()->setVerticalText( cell->format()->verticalText( _column, _row ) );
609 format()->setDontPrintText( cell->format()->getDontprintText(_column, _row ) );
610 format()->setNotProtected( cell->format()->notProtected(_column, _row ) );
611 format()->setHideAll(cell->format()->isHideAll(_column, _row ) );
612 format()->setHideFormula(cell->format()->isHideFormula(_column, _row ) );
613 format()->setIndent( cell->format()->getIndent(_column, _row ) );
614 format()->setAngle( cell->format()->getAngle(_column, _row) );
615 format()->setFormatType( cell->format()->getFormatType(_column, _row) );
616 Format::Currency c;
617 if ( cell->format()->currencyInfo( c ) )
618 format()->setCurrency( c );*/
619
620 TQValueList<Conditional> conditionList = cell->conditionList();
621 if (d->hasExtra())
622 delete d->extra()->conditions;
623 if ( cell->d->hasExtra() && cell->d->extra()->conditions )
625 else
626 if (d->hasExtra())
627 d->extra()->conditions = 0;
628
629 /*format()->setComment( cell->format()->comment( _column, _row ) );*/
630}
631
632void Cell::copyAll( Cell *cell )
633{
634 Q_ASSERT( !isDefault() ); // trouble ahead...
635 copyFormat( cell );
636 copyContent( cell );
637}
638
639void Cell::copyContent( const Cell* cell )
640{
641 Q_ASSERT( !isDefault() ); // trouble ahead...
642
643 if (cell->isFormula() && cell->column() > 0 && cell->row() > 0)
644 {
645 // change all the references, e.g. from A1 to A3 if copying
646 // from e.g. B2 to B4
647 TQString d = cell->encodeFormula();
648 setCellText( cell->decodeFormula( d ) );
649 }
650 else
651 setCellText( cell->text() );
652
653}
654
655void Cell::defaultStyle()
656{
657 format()->defaultStyleFormat();
658
659 if (!d->hasExtra())
660 return;
661
662 if ( d->extra()->conditions )
663 {
664 delete d->extra()->conditions;
665 d->extra()->conditions = 0;
666 }
667
668 delete d->extra()->validity;
669 d->extra()->validity = 0L;
670}
671
672
673// Merge a number of cells, i.e. make this cell obscure a number of
674// other cells. If _x and _y == 0, then the merging is removed.
675
676void Cell::mergeCells( int _col, int _row, int _x, int _y )
677{
678 // Start by unobscuring the cells that we obscure right now
679 int extraXCells = d->hasExtra() ? d->extra()->extraXCells : 0;
680 int extraYCells = d->hasExtra() ? d->extra()->extraYCells : 0;
681 for ( int x = _col; x <= _col + extraXCells; ++x ) {
682 for ( int y = _row; y <= _row + extraYCells; ++y ) {
683 if ( x != _col || y != _row )
684 format()->sheet()->nonDefaultCell( x, y )->unobscure(this);
685 }
686 }
687
688 // If no merging, then remove all traces, and return.
689 if ( _x == 0 && _y == 0 ) {
690 clearFlag( Flag_Merged );
691 if (d->hasExtra()) {
692 d->extra()->extraXCells = 0;
693 d->extra()->extraYCells = 0;
694 d->extra()->extraWidth = 0.0;
695 d->extra()->extraHeight = 0.0;
696 d->extra()->mergedXCells = 0;
697 d->extra()->mergedYCells = 0;
698 }
699
700 // Refresh the layout
701 setFlag( Flag_LayoutDirty );
702 return;
703 }
704
705 // At this point, we know that we will merge some cells.
706 setFlag(Flag_Merged);
707 d->extra()->extraXCells = _x;
708 d->extra()->extraYCells = _y;
709 d->extra()->mergedXCells = _x;
710 d->extra()->mergedYCells = _y;
711
712 // Obscure the cells
713 for ( int x = _col; x <= _col + _x; ++x ) {
714 for ( int y = _row; y <= _row + _y; ++y ) {
715 if ( x != _col || y != _row )
716 format()->sheet()->nonDefaultCell( x, y )->obscure( this, true );
717 }
718 }
719
720 // Refresh the layout
721 setFlag( Flag_LayoutDirty );
722}
723
724void Cell::move( int col, int row )
725{
728 setDisplayDirtyFlag();
729
730 //int ex = extraXCells();
731 //int ey = d->extra()->extraYCells();
732
733 if (d->hasExtra())
734 d->extra()->obscuringCells.clear();
735
736 // Unobscure the objects we obscure right now
737 int extraXCells = d->hasExtra() ? d->extra()->extraXCells : 0;
738 int extraYCells = d->hasExtra() ? d->extra()->extraYCells : 0;
739 for( int x = d->column; x <= d->column + extraXCells; ++x )
740 for( int y = d->row; y <= d->row + extraYCells; ++y )
741 if ( x != d->column || y != d->row )
742 {
743 Cell *cell = format()->sheet()->nonDefaultCell( x, y );
744 cell->unobscure(this);
745 }
746
747 d->column = col;
748 d->row = row;
749
750 if (d->hasExtra())
751 {
752 // d->extra()->extraXCells = 0;
753 // d->extra()->extraYCells = 0;
754 d->extra()->mergedXCells = 0;
755 d->extra()->mergedYCells = 0;
756 }
757
758 // Cell value has been changed (because we're another cell now).
759 valueChanged ();
760}
761
762void Cell::setLayoutDirtyFlag( bool format )
763{
764 setFlag( Flag_LayoutDirty );
765 if ( format )
766 setFlag( Flag_TextFormatDirty );
767
768 if (!d->hasExtra())
769 return;
770
771 TQValueList<Cell*>::iterator it = d->extra()->obscuringCells.begin();
772 TQValueList<Cell*>::iterator end = d->extra()->obscuringCells.end();
773 for ( ; it != end; ++it ) {
774 (*it)->setLayoutDirtyFlag( format );
775 }
776}
777
779{
780 if ( isDefault() )
781 return false;
782
783 if ( !d->strText.stripWhiteSpace().isEmpty() ) {
784 return true;
785 }
786
787 // Cell borders?
788 if ( format()->hasProperty( Format::PTopBorder )
789 || format()->hasProperty( Format::PLeftBorder )
790 || format()->hasProperty( Format::PRightBorder )
791 || format()->hasProperty( Format::PBottomBorder )
792 || format()->hasProperty( Format::PFallDiagonal )
793 || format()->hasProperty( Format::PGoUpDiagonal ) ) {
794 return true;
795 }
796
797 // Background color or brush?
798 if ( format()->hasProperty( Format::PBackgroundBrush ) ) {
799
800 const TQBrush& brush=backGroundBrush(column(),row());
801
802 //Only brushes that are visible (ie. they have a brush style and are not white)
803 //need to be drawn
804 if ( (brush.style() != TQt::NoBrush) &&
805 (brush.color() != TQt::white || brush.pixmap()) )
806 return true;
807
808 }
809
810 if ( format()->hasProperty( Format::PBackgroundColor ) ) {
811 kdDebug() << "needsPrinting: Has background colour" << endl;
812 TQColor backgroundColor=bgColor(column(),row());
813
814 //We don't need to print anything if the background is white
815 if (backgroundColor != TQt::white)
816 return true;
817 }
818
819 return false;
820}
821
822bool Cell::isEmpty() const
823{
824 return isDefault() || d->strText.isEmpty();
825}
826
827
828// Return true if this cell is obscured by some other cell.
829
831{
832 if (!d->hasExtra())
833 return false;
834
835 return !( d->extra()->obscuringCells.isEmpty() );
836}
837
838
839// Return true if this cell is part of a merged cell, but not the
840// master cell.
841
843{
844 if (!d->hasExtra())
845 return false;
846
847 TQValueList<Cell*>::const_iterator it = d->extra()->obscuringCells.begin();
848 TQValueList<Cell*>::const_iterator end = d->extra()->obscuringCells.end();
849 for ( ; it != end; ++it ) {
850 Cell *cell = *it;
851
852 if (cell->doesMergeCells()) {
853 // The cell might merge extra cells, and then overlap even
854 // beyond that so just knowing that the obscuring cell merges
855 // extra isn't enough. We have to know that this cell is one of
856 // the ones it is forcing over.
857 if (column() <= cell->column() + cell->d->extra()->mergedXCells
858 && row() <= cell->row() + cell->mergedYCells() )
859 return true;
860 }
861 }
862
863 return false;
864}
865
866
867// Return the cell that obscures this one. If no cell is obscuring,
868// then return this. This method is slightly complicated because we
869// can have several layers of obscuring.
870//
871// Update: it seems that if we do an actual merge, then the obscuring
872// cell is prepended and if just expanding, then it is appended. This
873// means that we should be able to just look at the first one.
874
876{
877 if (!d->hasExtra())
878 return (Cell *) this;
879
880 else if (d->extra()->obscuringCells.isEmpty())
881 return (Cell *) this;
882
883 else
884 return d->extra()->obscuringCells.first();
885
886#if 0
887 TQValueList<Cell*>::const_iterator it = d->extra()->obscuringCells.begin();
888 TQValueList<Cell*>::const_iterator end = d->extra()->obscuringCells.end();
889 for ( ; it != end; ++it ) {
890 Cell *cell = *it;
891
892 if (cell->doesMergeCells()) {
893 // The cell might merge extra cells, and then overlap even
894 // beyond that so just knowing that the obscuring cell merges
895 // extra isn't enough. We have to know that this cell is one of
896 // the ones it is forcing over.
897 if (column() <= cell->column() + cell->d->extra()->mergedXCells
898 && row() <= cell->row() + cell->mergedYCells() )
899 return true;
900 }
901 }
902
903 return false;
904#endif
905}
906
907
908TQValueList<Cell*> Cell::obscuringCells() const
909{
910 if (!d->hasExtra())
911 {
912 TQValueList<Cell*> empty;
913 return empty;
914 }
915 return d->extra()->obscuringCells;
916}
917
918void Cell::clearObscuringCells()
919{
920 if (!d->hasExtra())
921 return;
922 d->extra()->obscuringCells.clear();
923}
924
925void Cell::obscure( Cell *cell, bool isForcing )
926{
927 if (d->hasExtra())
928 {
929 d->extra()->obscuringCells.remove( cell ); // removes *all* occurrences
930 cell->clearObscuringCells();
931 }
932 if ( isForcing )
933 {
934 d->extra()->obscuringCells.prepend( cell );
935 }
936 else
937 {
938 d->extra()->obscuringCells.append( cell );
939 }
940 setFlag(Flag_LayoutDirty);
941 format()->sheet()->setRegionPaintDirty( cellRect() );
942}
943
944void Cell::unobscure( Cell * cell )
945{
946 if (d->hasExtra())
947 d->extra()->obscuringCells.remove( cell );
948 setFlag( Flag_LayoutDirty );
949 format()->sheet()->setRegionPaintDirty( cellRect() );
950}
951
952TQString Cell::encodeFormula( bool _era, int _col, int _row ) const
953{
954 if ( _col == -1 )
955 _col = d->column;
956 if ( _row == -1 )
957 _row = d->row;
958
959 TQString erg = "";
960
961 if(d->strText.isEmpty())
962 return d->strText;
963
964 bool fix1 = false;
965 bool fix2 = false;
966 bool onNumber = false;
967 unsigned int pos = 0;
968 const unsigned int length = d->strText.length();
969
970 // All this can surely be made 10 times faster, but I just "ported" it to TQString
971 // without any attempt to optimize things -- this is really brittle (Werner)
972 while ( pos < length )
973 {
974 if ( d->strText[pos] == '"' )
975 {
976 erg += d->strText[pos++];
977 while ( pos < length && d->strText[pos] != '"' ) // till the end of the world^H^H^H "string"
978 {
979 erg += d->strText[pos++];
980 // Allow escaped double quotes (\")
981 if ( pos < length && d->strText[pos] == '\\' && d->strText[pos+1] == '"' )
982 {
983 erg += d->strText[pos++];
984 erg += d->strText[pos++];
985 }
986 }
987 if ( pos < length ) // also copy the trailing double quote
988 erg += d->strText[pos++];
989
990 onNumber = false;
991 }
992 else if ( d->strText[pos].isDigit() )
993 {
994 erg += d->strText[pos++];
995 fix1 = fix2 = false;
996 onNumber = true;
997 }
998 else if ( d->strText[pos] != '$' && !d->strText[pos].isLetter() )
999 {
1000 erg += d->strText[pos++];
1001 fix1 = fix2 = false;
1002 onNumber = false;
1003 }
1004 else
1005 {
1006 TQString tmp = "";
1007 if ( d->strText[pos] == '$' )
1008 {
1009 tmp = "$";
1010 pos++;
1011 fix1 = true;
1012 }
1013 if ( d->strText[pos].isLetter() )
1014 {
1015 TQString buffer;
1016 unsigned int pos2 = 0;
1017 while ( pos < length && d->strText[pos].isLetter() )
1018 {
1019 tmp += d->strText[pos];
1020 buffer[pos2++] = d->strText[pos++];
1021 }
1022 if ( d->strText[pos] == '$' )
1023 {
1024 tmp += "$";
1025 pos++;
1026 fix2 = true;
1027 }
1028 if ( d->strText[pos].isDigit() )
1029 {
1030 const unsigned int oldPos = pos;
1031 while ( pos < length && d->strText[pos].isDigit() ) ++pos;
1032 int row = 0;
1033 if ( pos != oldPos )
1034 row = d->strText.mid(oldPos, pos-oldPos).toInt();
1035 // Is it a sheet name || is it a function name like DEC2HEX
1036 /* or if we're parsing a number, this could just be the
1037 exponential part of it (1.23E4) */
1038 if ( ( d->strText[pos] == '!' ) ||
1039 d->strText[pos].isLetter() ||
1040 onNumber )
1041 {
1042 erg += tmp;
1043 fix1 = fix2 = false;
1044 pos = oldPos;
1045 }
1046 else // It must be a cell identifier
1047 {
1048 //now calculate the row as integer value
1049 int col = 0;
1050 col = util_decodeColumnLabelText( buffer );
1051 if ( fix1 )
1052 erg += TQString( "$%1" ).arg( col );
1053 else
1054 if (_era)
1055 erg += TQChar(0xA7) + TQString( "%1" ).arg( col );
1056 else
1057 erg += TQString( "#%1" ).arg( col - _col );
1058
1059 if ( fix2 )
1060 erg += TQString( "$%1#").arg( row );
1061 else
1062 if (_era)
1063 erg += TQChar(0xA7) + TQString( "%1#" ).arg( row );
1064 else
1065 erg += TQString( "#%1#" ).arg( row - _row );
1066 }
1067 }
1068 else
1069 {
1070 erg += tmp;
1071 fix1 = fix2 = false;
1072 }
1073 }
1074 else
1075 {
1076 erg += tmp;
1077 fix1 = false;
1078 }
1079 onNumber = false;
1080 }
1081 }
1082
1083 return erg;
1084}
1085
1086TQString Cell::decodeFormula( const TQString &_text, int _col, int _row) const
1087{
1088 if ( _col == -1 )
1089 _col = d->column;
1090 if ( _row == -1 )
1091 _row = d->row;
1092
1093 TQString erg = "";
1094 unsigned int pos = 0;
1095 const unsigned int length = _text.length();
1096
1097 if ( _text.isEmpty() )
1098 return TQString();
1099
1100 while ( pos < length )
1101 {
1102 if ( _text[pos] == '"' )
1103 {
1104 erg += _text[pos++];
1105 while ( pos < length && _text[pos] != '"' )
1106 {
1107 erg += _text[pos++];
1108 // Allow escaped double quotes (\")
1109 if ( pos < length && _text[pos] == '\\' && _text[pos+1] == '"' )
1110 {
1111 erg += _text[pos++];
1112 erg += _text[pos++];
1113 }
1114 }
1115 if ( pos < length )
1116 erg += _text[pos++];
1117 }
1118 else if ( _text[pos] == '#' || _text[pos] == '$' || _text[pos] == TQChar(0xA7))
1119 {
1120 bool abs1 = false;
1121 bool abs2 = false;
1122 bool era1 = false; // if 1st is relative but encoded absolutely
1123 bool era2 = false;
1124
1125 TQChar _t = _text[pos++];
1126 if ( _t == '$' )
1127 abs1 = true;
1128 else if ( _t == TQChar(0xA7) )
1129 era1 = true;
1130
1131 int col = 0;
1132 unsigned int oldPos = pos;
1133 while ( pos < length && ( _text[pos].isDigit() || _text[pos] == '-' ) ) ++pos;
1134 if ( pos != oldPos )
1135 col = _text.mid(oldPos, pos-oldPos).toInt();
1136 if ( !abs1 && !era1 )
1137 col += _col;
1138 // Skip '#' or '$'
1139
1140 _t = _text[pos++];
1141 if ( _t == '$' )
1142 abs2 = true;
1143 else if ( _t == TQChar(0xA7) )
1144 era2 = true;
1145
1146 int row = 0;
1147 oldPos = pos;
1148 while ( pos < length && ( _text[pos].isDigit() || _text[pos] == '-' ) ) ++pos;
1149 if ( pos != oldPos )
1150 row = _text.mid(oldPos, pos-oldPos).toInt();
1151 if ( !abs2 && !era2)
1152 row += _row;
1153 // Skip '#' or '$'
1154 ++pos;
1155 if ( row < 1 || col < 1 || row > KS_rowMax || col > KS_colMax )
1156 {
1157 kdDebug(36001) << "Cell::decodeFormula: row or column out of range (col: " << col << " | row: " << row << ")" << endl;
1158 erg = "=\"#### " + i18n("REFERENCE TO COLUMN OR ROW IS OUT OF RANGE") + "\"";
1159 return erg;
1160 }
1161 if ( abs1 )
1162 erg += "$";
1163 erg += Cell::columnName(col); //Get column text
1164
1165 if ( abs2 )
1166 erg += "$";
1167 erg += TQString::number( row );
1168 }
1169 else
1170 erg += _text[pos++];
1171 }
1172
1173 return erg;
1174}
1175
1176
1177void Cell::freeAllObscuredCells()
1178{
1179 //
1180 // Free all obscured cells.
1181 //
1182
1183 if (!d->hasExtra())
1184 return;
1185
1186 for ( int x = d->column + d->extra()->mergedXCells;
1187 x <= d->column + d->extra()->extraXCells; ++x ) {
1188 for ( int y = d->row + d->extra()->mergedYCells;
1189 y <= d->row + d->extra()->extraYCells; ++y ) {
1190 if ( x != d->column || y != d->row ) {
1191 Cell *cell = format()->sheet()->cellAt( x, y );
1192 cell->unobscure(this);
1193 }
1194 }
1195 }
1196
1197 d->extra()->extraXCells = d->extra()->mergedXCells;
1198 d->extra()->extraYCells = d->extra()->mergedYCells;
1199
1200}
1201
1202
1203// ----------------------------------------------------------------
1204// Layout
1205
1206
1207// Recalculate the entire layout. This includes the following members:
1208//
1209// d->textX, d->textY
1210// d->textWidth, d->textHeight
1211// d->fmAscent
1212// d->extra()->extraXCells, d->extra()->extraYCells
1213// d->extra()->extraWidth, d->extra()->extraHeight
1214// d->extra()->nbLines (if multirow)
1215//
1216// and, of course,
1217//
1218// d->strOutText
1219//
1220
1221void Cell::makeLayout( TQPainter &_painter, int _col, int _row )
1222{
1223 // Are _col and _row really needed ?
1224 //
1225 // Yes they are: they are useful if this is the default layout, in
1226 // which case d->row and d->column are 0 and 0, but _col and _row
1227 // are the real coordinates of the cell.
1228
1229 // There is no need to remake the layout if it hasn't changed.
1230 if ( !testFlag( Flag_LayoutDirty ) )
1231 return;
1232
1233 // Some initializations.
1234 if (d->hasExtra())
1235 d->extra()->nbLines = 0;
1236 clearFlag( Flag_CellTooShortX );
1237 clearFlag( Flag_CellTooShortY );
1238
1239 // Initiate the cells that this one is obscuring to the ones that
1240 // are actually merged.
1241 freeAllObscuredCells();
1242 if (d->hasExtra())
1243 mergeCells( d->column, d->row,
1244 d->extra()->mergedXCells, d->extra()->mergedYCells );
1245
1246 // If the column for this cell is hidden or the row is too low,
1247 // there is no use in remaking the layout.
1248 ColumnFormat *cl1 = format()->sheet()->columnFormat( _col );
1249 RowFormat *rl1 = format()->sheet()->rowFormat( _row );
1250 if ( cl1->isHide()
1251 || ( rl1->dblHeight() <= format()->sheet()->doc()->unzoomItY( 2 ) ) ) {
1252 clearFlag( Flag_LayoutDirty );
1253 return;
1254 }
1255
1256 // Recalculate the output text, d->strOutText.
1257 setOutputText();
1258
1259 // Empty text? Reset the outstring and, if this is the default
1260 // cell, return.
1261 if ( d->strOutText.isEmpty() ) {
1262 d->strOutText = TQString();
1263
1264 if ( isDefault() ) {
1265 clearFlag( Flag_LayoutDirty );
1266 return;
1267 }
1268 }
1269
1270 // Up to here, we have just cared about the contents, not the
1271 // painting of it. Now it is time to see if the contents fits into
1272 // the cell and, if not, maybe rearrange the outtext a bit.
1273 //
1274 // First, Determine the correct font with zoom taken into account,
1275 // and apply it to _painter. Then calculate text dimensions, i.e.
1276 // d->textWidth and d->textHeight.
1277 applyZoomedFont( _painter, _col, _row );
1278 textSize( _painter );
1279
1280 //
1281 // Calculate the size of the cell
1282 //
1283 RowFormat *rl = format()->sheet()->rowFormat( d->row );
1284 ColumnFormat *cl = format()->sheet()->columnFormat( d->column );
1285
1286 double width = cl->dblWidth();
1287 double height = rl->dblHeight();
1288
1289 // Calculate extraWidth and extraHeight if we have a merged cell.
1290 if ( testFlag( Flag_Merged ) ) {
1291 int extraXCells = d->hasExtra() ? d->extra()->extraXCells : 0;
1292 int extraYCells = d->hasExtra() ? d->extra()->extraYCells : 0;
1293
1294 // FIXME: Introduce double extraWidth/Height here and use them
1295 // instead (see FIXME about this in paintCell()).
1296
1297 for ( int x = _col + 1; x <= _col + extraXCells; x++ )
1298 width += format()->sheet()->columnFormat( x )->dblWidth();
1299
1300 for ( int y = _row + 1; y <= _row + extraYCells; y++ )
1301 height += format()->sheet()->rowFormat( y )->dblHeight();
1302 }
1303
1304 // Cache the newly calculated extraWidth and extraHeight if we have
1305 // already allocated a struct for it. Otherwise it will be zero, so
1306 // don't bother.
1307 if (d->hasExtra()) {
1308 d->extra()->extraWidth = width;
1309 d->extra()->extraHeight = height;
1310 }
1311
1312 TQFontMetrics fm = _painter.fontMetrics();
1313 d->fmAscent = fm.ascent();
1314
1315 // Check if we need to break the line into multiple lines and are
1316 // allowed to do so. If so, set `lines' to the number of lines that
1317 // are needed to fit into the total width of the combined cell.
1318 //
1319 // Also recalculate d->textHeight, d->textWidth, d->extra->nbLines
1320 // and d->strOutText.
1321 //
1322 int lines = 1;
1323 if ( d->textWidth > (width - 2 * BORDER_SPACE
1324 - format()->leftBorderWidth( _col, _row )
1325 - format()->rightBorderWidth( _col, _row ) )
1326 && format()->multiRow( _col, _row ) )
1327 {
1328 // Copy of d->strOutText but without the newlines.
1329// TQString o = d->strOutText.replace( TQChar('\n'), " " );
1330
1331 // don't remove the existing LF, these are intended line wraps (whishlist #9881)
1332 TQString o = d->strOutText;
1333
1334 // Break the line at appropriate places, i.e. spaces, if
1335 // necessary. This means to change the spaces where breaks occur
1336 // into newlines.
1337 if ( o.find(' ') != -1 )
1338 {
1339 d->strOutText = "";
1340
1341 // Make sure that we have a space at the end.
1342 o += ' ';
1343
1344 int start = 0; // Start of the line we are handling now
1345 int breakpos = 0; // The next candidate pos to break the string
1346 int pos1 = 0;
1347 int availableWidth = (int) ( width - 2 * BORDER_SPACE
1348 - format()->leftBorderWidth( _col, _row )
1349 - format()->rightBorderWidth( _col, _row ) );
1350
1351 do {
1352
1353 breakpos = o.find( ' ', breakpos );
1354 int linefeed = o.find( '\n', pos1 );
1355
1356// kdDebug() << "start: " << start << "; breakpos: " << breakpos << "; pos1: " << pos1 << "; linefeed: " << linefeed << endl;
1357
1358 //don't miss LF as a position to calculate current lineWidth
1359 int work_breakpos = breakpos;
1360 if (pos1 < linefeed && linefeed < breakpos)
1361 work_breakpos = linefeed;
1362
1363 double lineWidth = format()->sheet()->doc()
1364 ->unzoomItX( fm.width( d->strOutText.mid( start, (pos1 - start) )
1365 + o.mid( pos1, work_breakpos - pos1 ) ) );
1366
1367 //linefeed could be -1 when no linefeed is found!
1368 if (breakpos > linefeed && linefeed > 0)
1369 {
1370// kdDebug() << "applying linefeed to start;" << endl;
1371 start = linefeed;
1372 lines++;
1373 }
1374
1375 if ( lineWidth <= availableWidth ) {
1376 // We have room for the rest of the line. End it here.
1377 d->strOutText += o.mid( pos1, breakpos - pos1 );
1378 pos1 = breakpos;
1379 }
1380 else {
1381 // Still not enough room. Try to split further.
1382 if ( o.at( pos1 ) == ' ' )
1383 pos1++;
1384
1385 if ( pos1 != 0 && breakpos != -1 ) {
1386 d->strOutText += "\n" + o.mid( pos1, breakpos - pos1 );
1387 lines++;
1388 }
1389 else
1390 d->strOutText += o.mid( pos1, breakpos - pos1 );
1391
1392 start = pos1;
1393 pos1 = breakpos;
1394 }
1395
1396 breakpos++;
1397 } while( o.find( ' ', breakpos ) != -1 );
1398 }
1399 else
1400 {
1401 lines = o.contains('\n');
1402 }
1403
1404 d->textHeight *= lines;
1405 if (lines > 1)
1406 d->extra()->nbLines = lines;
1407
1408 d->textX = 0.0;
1409
1410 // Calculate the maximum width, taking into account linebreaks,
1411 // and put it in d->textWidth.
1412 TQString t;
1413 int i;
1414 int pos = 0;
1415 d->textWidth = 0.0;
1416 do {
1417 i = d->strOutText.find( "\n", pos );
1418
1419 if ( i == -1 )
1420 t = d->strOutText.mid( pos, d->strOutText.length() - pos );
1421 else {
1422 t = d->strOutText.mid( pos, i - pos );
1423 pos = i + 1;
1424 }
1425
1426 double tw = format()->sheet()->doc()->unzoomItX( fm.width( t ) );
1427 if ( tw > d->textWidth )
1428 d->textWidth = tw;
1429 } while ( i != -1 );
1430 }
1431
1432 // Calculate d->textX and d->textY
1433 offsetAlign( _col, _row );
1434
1435 int a = effAlignX();
1436
1437 // Get indentation. This is only used for left aligned text.
1438 double indent = 0.0;
1439 if ( a == Format::Left && !isEmpty() )
1440 indent = format()->getIndent( _col, _row );
1441
1442 // Set Flag_CellTooShortX if the text is vertical or angled, and too
1443 // high for the cell.
1444 if ( format()->verticalText( _col, _row ) || format()->getAngle( _col, _row ) != 0 ) {
1445 //RowFormat *rl = format()->sheet()->rowFormat( _row );
1446
1447 if ( d->textHeight >= height/*rl->dblHeight()*/ )
1448 setFlag( Flag_CellTooShortX );
1449 }
1450
1451 // Do we have to occupy additional cells to the right? This is only
1452 // done for cells that have no merged cells in the Y direction.
1453 //
1454 // FIXME: Check if all cells along the merged edge to the right are
1455 // empty and use the extra space? No, probably not.
1456 //
1457 if ( d->textWidth + indent > ( width - 2 * BORDER_SPACE
1458 - format()->leftBorderWidth( _col, _row )
1459 - format()->rightBorderWidth( _col, _row ) )
1460 && ( !d->hasExtra() || d->extra()->mergedYCells == 0 ) )
1461 {
1462 int c = d->column;
1463
1464 // Find free cells to the right of this one.
1465 int end = 0;
1466 while ( !end ) {
1467 ColumnFormat *cl2 = format()->sheet()->columnFormat( c + 1 );
1468 Cell *cell = format()->sheet()->visibleCellAt( c + 1, d->row );
1469
1470 if ( cell->isEmpty() ) {
1471 width += cl2->dblWidth() - 1;
1472 c++;
1473
1474 // Enough space?
1475 if ( d->textWidth + indent <= ( width - 2 * BORDER_SPACE
1476 - format()->leftBorderWidth( _col, _row )
1477 - format()->rightBorderWidth( _col, _row ) ) )
1478 end = 1;
1479 }
1480 else
1481 // Not enough space, but the next cell is not empty
1482 end = -1;
1483 }
1484
1485 // Try to use additional space from the neighboring cells that
1486 // were calculated in the last step. This is the place that we
1487 // set d->extra()->extraXCells and d->extra()->extraWidth.
1488 //
1489 // Currently this is only done for left aligned cells. We have to
1490 // check to make sure we haven't already force-merged enough cells
1491 //
1492 // FIXME: Why not right/center aligned text?
1493 //
1494 // FIXME: Shouldn't we check to see if end == -1 here before
1495 // setting Flag_CellTooShortX?
1496 //
1497 if ( format()->align( _col, _row ) == Format::Left
1498 || ( format()->align( _col, _row ) == Format::Undefined
1499 && !value().isNumber() ) )
1500 {
1501 if ( c - d->column > d->extra()->mergedXCells ) {
1502 d->extra()->extraXCells = c - d->column;
1503 d->extra()->extraWidth = width;
1504 for ( int i = d->column + 1; i <= c; ++i ) {
1505 Cell *cell = format()->sheet()->nonDefaultCell( i, d->row );
1506 cell->obscure( this );
1507 }
1508
1509 // Not enough space
1510 if ( end == -1 )
1511 setFlag( Flag_CellTooShortX );
1512 }
1513 else
1514 setFlag( Flag_CellTooShortX );
1515 }
1516 else
1517 setFlag( Flag_CellTooShortX );
1518 }
1519
1520 // Do we have to occupy additional cells at the bottom ?
1521 //
1522 // FIXME: Setting to make the current cell grow.
1523 //
1524 if ( format()->multiRow( _col, _row )
1525 && d->textHeight > ( height - 2 * BORDER_SPACE
1526 - format()->topBorderWidth( _col, _row )
1527 - format()->bottomBorderWidth( _col, _row ) ) )
1528 {
1529 int r = d->row;
1530 int end = 0;
1531
1532 // Find free cells bottom to this one
1533 while ( !end ) {
1534 RowFormat *rl2 = format()->sheet()->rowFormat( r + 1 );
1535 Cell *cell = format()->sheet()->visibleCellAt( d->column, r + 1 );
1536
1537 if ( cell->isEmpty() ) {
1538 height += rl2->dblHeight() - 1.0;
1539 r++;
1540
1541 // Enough space ?
1542 if ( d->textHeight <= ( height - 2 * BORDER_SPACE
1543 - format()->topBorderWidth( _col, _row )
1544 - format()->bottomBorderWidth( _col, _row ) ) )
1545 end = 1;
1546 }
1547 else
1548 // Not enough space, but the next cell is not empty.
1549 end = -1;
1550 }
1551
1552 // Check to make sure we haven't already force-merged enough cells.
1553 if ( r - d->row > d->extra()->mergedYCells )
1554 {
1555 d->extra()->extraYCells = r - d->row;
1556 d->extra()->extraHeight = height;
1557
1558 for ( int i = d->row + 1; i <= r; ++i )
1559 {
1560 Cell *cell = format()->sheet()->nonDefaultCell( d->column, i );
1561 cell->obscure( this );
1562 }
1563
1564 // Not enough space?
1565 if ( end == -1 )
1566 setFlag( Flag_CellTooShortY );
1567 }
1568 else
1569 setFlag( Flag_CellTooShortY );
1570 }
1571
1572 clearFlag( Flag_LayoutDirty );
1573
1574 return;
1575}
1576
1577
1578void Cell::valueChanged ()
1579{
1580 update();
1581
1582 format()->sheet()->valueChanged (this);
1583}
1584
1585
1586// Recalculate d->strOutText.
1587//
1588
1589void Cell::setOutputText()
1590{
1591 if ( isDefault() ) {
1592 d->strOutText = TQString();
1593
1594 if ( d->hasExtra() && d->extra()->conditions )
1595 d->extra()->conditions->checkMatches();
1596
1597 return;
1598 }
1599
1600 // If nothing has changed, we don't need to remake the text layout.
1601 if ( !testFlag(Flag_TextFormatDirty) )
1602 return;
1603
1604 // We don't want to remake the layout unnecessarily.
1605 clearFlag( Flag_TextFormatDirty );
1606
1607 // Display a formula if warranted. If not, display the value instead;
1608 // this is the most common case.
1609 if ( (!hasError()) && isFormula() && format()->sheet()->getShowFormula()
1610 && !( format()->sheet()->isProtected() && format()->isHideFormula( d->column, d->row ) )
1611 || isEmpty() )
1612 d->strOutText = d->strText;
1613 else {
1614 d->strOutText = sheet()->doc()->formatter()->formatText (this,
1615 formatType());
1616 }
1617
1618 // Check conditions if needed.
1619 if ( d->hasExtra() && d->extra()->conditions )
1620 d->extra()->conditions->checkMatches();
1621}
1622
1623
1624// Recalculate d->textX and d->textY.
1625//
1626// Used in makeLayout() and calculateTextParameters().
1627//
1628
1629void Cell::offsetAlign( int _col, int _row )
1630{
1631 int a;
1632 Format::AlignY ay;
1633 int tmpAngle;
1634 bool tmpVerticalText;
1635 bool tmpMultiRow;
1636
1637 if ( d->hasExtra()
1638 && d->extra()->conditions
1639 && d->extra()->conditions->matchedStyle() )
1640 {
1641 Style *style = d->extra()->conditions->matchedStyle();
1642
1643 if ( style->hasFeature( Style::SAlignX, true ) )
1644 a = style->alignX();
1645 else
1646 a = format()->align( _col, _row );
1647
1648 if ( style->hasFeature( Style::SVerticalText, true ) )
1649 tmpVerticalText = style->hasProperty( Style::PVerticalText );
1650 else
1651 tmpVerticalText = format()->verticalText( _col, _row );
1652
1653 if ( style->hasFeature( Style::SMultiRow, true ) )
1654 tmpMultiRow = style->hasProperty( Style::PMultiRow );
1655 else
1656 tmpMultiRow = format()->multiRow( _col, _row );
1657
1658 if ( style->hasFeature( Style::SAlignY, true ) )
1659 ay = style->alignY();
1660 else
1661 ay = format()->alignY( _col, _row );
1662
1663 if ( style->hasFeature( Style::SAngle, true ) )
1664 tmpAngle = style->rotateAngle();
1665 else
1666 tmpAngle = format()->getAngle( _col, _row );
1667 }
1668 else {
1669 a = format()->align( _col, _row );
1670 ay = format()->alignY( _col, _row );
1671 tmpAngle = format()->getAngle( _col, _row );
1672 tmpVerticalText = format()->verticalText( _col, _row );
1673 tmpMultiRow = format()->multiRow( _col, _row );
1674 }
1675
1676 RowFormat *rl = format()->sheet()->rowFormat( _row );
1677 ColumnFormat *cl = format()->sheet()->columnFormat( _col );
1678
1679 double w = cl->dblWidth();
1680 double h = rl->dblHeight();
1681
1682 if ( d->hasExtra() ) {
1683 if ( d->extra()->extraXCells ) w = d->extra()->extraWidth;
1684 if ( d->extra()->extraYCells ) h = d->extra()->extraHeight;
1685 }
1686
1687 const double effTop = BORDER_SPACE + 0.5 * effTopBorderPen( _col, _row ).width();
1688 const double effBottom = h - BORDER_SPACE - 0.5 * effBottomBorderPen( _col, _row ).width();
1689
1690 // Calculate d->textY based on the vertical alignment and a few
1691 // other inputs.
1692 switch( ay )
1693 {
1694 case Format::Top:
1695 {
1696 if ( tmpAngle == 0 )
1697 {
1698 d->textY = effTop + (double) d->fmAscent / format()->sheet()->doc()->zoomedResolutionY();
1699 }
1700 else if ( tmpAngle < 0 )
1701 {
1702 d->textY = effTop;
1703 }
1704 else
1705 {
1706 d->textY = effTop
1707 + (double) d->fmAscent * cos( tmpAngle * M_PI / 180 ) / format()->sheet()->doc()->zoomedResolutionY();
1708 }
1709 break;
1710 }
1711 case Format::Bottom:
1712 {
1713 if ( !tmpVerticalText && !tmpMultiRow && !tmpAngle )
1714 {
1715 d->textY = effBottom;
1716 }
1717 else if ( tmpAngle != 0 )
1718 {
1719 // Is enough place available?
1720 if ( effBottom - effTop - d->textHeight > 0 )
1721 {
1722 if ( tmpAngle < 0 )
1723 {
1724 d->textY = effBottom - d->textHeight;
1725 }
1726 else
1727 {
1728 d->textY = effBottom - d->textHeight
1729 + ( (double) d->fmAscent * cos( tmpAngle * M_PI / 180 ) / format()->sheet()->doc()->zoomedResolutionY() );
1730 }
1731 }
1732 else
1733 {
1734 if ( tmpAngle < 0 )
1735 {
1736 d->textY = effTop;
1737 }
1738 else
1739 {
1740 d->textY = effTop
1741 + ( (double) d->fmAscent * cos( tmpAngle * M_PI / 180 )
1742 / format()->sheet()->doc()->zoomedResolutionY() );
1743 }
1744 }
1745 }
1746 else if ( tmpMultiRow && !tmpVerticalText )
1747 {
1748 // Is enough place available?
1749 if ( effBottom - effTop - d->textHeight > 0 )
1750 {
1751 d->textY = effBottom - d->textHeight
1752 + (double)d->fmAscent / format()->sheet()->doc()->zoomedResolutionY();
1753 }
1754 else
1755 {
1756 d->textY = effTop
1757 + (double) d->fmAscent / format()->sheet()->doc()->zoomedResolutionY();
1758 }
1759 }
1760 else
1761 {
1762 // Is enough place available?
1763 if ( effBottom - effTop - d->textHeight > 0 )
1764 {
1765 d->textY = effBottom - d->textHeight
1766 + (double)d->fmAscent / format()->sheet()->doc()->zoomedResolutionY();
1767 }
1768 else
1769 {
1770 d->textY = effTop
1771 + (double) d->fmAscent / format()->sheet()->doc()->zoomedResolutionY();
1772 }
1773 }
1774 break;
1775 }
1776 case Format::Middle:
1777 case Format::UndefinedY:
1778 {
1779 if ( !tmpVerticalText && !tmpMultiRow && !tmpAngle )
1780 {
1781 d->textY = ( h - d->textHeight ) / 2
1782 + (double) d->fmAscent / format()->sheet()->doc()->zoomedResolutionY();
1783 }
1784 else if ( tmpAngle != 0 )
1785 {
1786 // Is enough place available?
1787 if ( effBottom - effTop - d->textHeight > 0 )
1788 {
1789 if ( tmpAngle < 0 )
1790 {
1791 d->textY = ( h - d->textHeight ) / 2;
1792 }
1793 else
1794 {
1795 d->textY = ( h - d->textHeight ) / 2
1796 + (double) d->fmAscent * cos( tmpAngle * M_PI / 180 ) / format()->sheet()->doc()->zoomedResolutionY();
1797 }
1798 }
1799 else
1800 {
1801 if ( tmpAngle < 0 )
1802 {
1803 d->textY = effTop;
1804 }
1805 else
1806 {
1807 d->textY = effTop
1808 + ( (double)d->fmAscent * cos( tmpAngle * M_PI / 180 ) / format()->sheet()->doc()->zoomedResolutionY() );
1809 }
1810 }
1811 }
1812 else if ( tmpMultiRow && !tmpVerticalText )
1813 {
1814 // Is enough place available?
1815 if ( effBottom - effTop - d->textHeight > 0 )
1816 {
1817 d->textY = ( h - d->textHeight ) / 2
1818 + (double) d->fmAscent / format()->sheet()->doc()->zoomedResolutionY();
1819 }
1820 else
1821 {
1822 d->textY = effTop
1823 + (double) d->fmAscent / format()->sheet()->doc()->zoomedResolutionY();
1824 }
1825 }
1826 else
1827 {
1828 // Is enough place available?
1829 if ( effBottom - effTop - d->textHeight > 0 )
1830 {
1831 d->textY = ( h - d->textHeight ) / 2
1832 + (double) d->fmAscent / format()->sheet()->doc()->zoomedResolutionY();
1833 }
1834 else
1835 d->textY = effTop
1836 + (double)d->fmAscent / format()->sheet()->doc()->zoomedResolutionY();
1837 }
1838 break;
1839 }
1840 }
1841
1842 a = effAlignX();
1843 if ( format()->sheet()->getShowFormula() &&
1844 !( format()->sheet()->isProtected() && format()->isHideFormula( _col, _row ) ) )
1845 {
1846 a = Format::Left;
1847 }
1848
1849 // Calculate d->textX based on alignment and textwidth.
1850 switch ( a ) {
1851 case Format::Left:
1852 d->textX = 0.5 * effLeftBorderPen( _col, _row ).width() + BORDER_SPACE;
1853 break;
1854 case Format::Right:
1855 d->textX = w - BORDER_SPACE - d->textWidth
1856 - 0.5 * effRightBorderPen( _col, _row ).width();
1857 break;
1858 case Format::Center:
1859 d->textX = 0.5 * ( w - BORDER_SPACE - d->textWidth -
1860 0.5 * effRightBorderPen( _col, _row ).width() );
1861 break;
1862 }
1863}
1864
1865
1866// Recalculate the current text dimensions, i.e. d->textWidth and
1867// d->textHeight.
1868//
1869// Used in makeLayout() and calculateTextParameters().
1870//
1871void Cell::textSize( TQPainter &_paint )
1872{
1873 TQFontMetrics fm = _paint.fontMetrics();
1874 // Horizontal text ?
1875
1876 int tmpAngle;
1877 int _row = row();
1878 int _col = column();
1879 bool tmpVerticalText;
1880 bool fontUnderlined;
1881 Format::AlignY ay;
1882
1883 // Set tmpAngle, tmpeVerticalText, ay and fontUnderlined according
1884 // to if there is a matching condition or not.
1885 if ( d->hasExtra()
1886 && d->extra()->conditions
1887 && d->extra()->conditions->matchedStyle() )
1888 {
1889 Style *style = d->extra()->conditions->matchedStyle();
1890
1891 if ( style->hasFeature( Style::SAngle, true ) )
1892 tmpAngle = style->rotateAngle();
1893 else
1894 tmpAngle = format()->getAngle( _col, _row );
1895
1896 if ( style->hasFeature( Style::SVerticalText, true ) )
1897 tmpVerticalText = style->hasProperty( Style::PVerticalText );
1898 else
1899 tmpVerticalText = format()->verticalText( _col, _row );
1900
1901 if ( style->hasFeature( Style::SAlignY, true ) )
1902 ay = style->alignY();
1903 else
1904 ay = format()->alignY( _col, _row );
1905
1906 if ( style->hasFeature( Style::SFontFlag, true ) )
1907 fontUnderlined = ( style->fontFlags() & (uint) Style::FUnderline );
1908 else
1909 fontUnderlined = format()->textFontUnderline( _col, _row );
1910 }
1911 else {
1912 // The cell has no condition with a maxed style.
1913 tmpAngle = format()->getAngle( _col, _row );
1914 tmpVerticalText = format()->verticalText( _col, _row );
1915 ay = format()->alignY( _col, _row );
1916 fontUnderlined = format()->textFontUnderline( _col, _row );
1917 }
1918
1919 // Set d->textWidth and d->textHeight to correct values according to
1920 // if the text is horizontal, vertical or rotated.
1921 if ( !tmpVerticalText && !tmpAngle ) {
1922 // Horizontal text.
1923
1924 d->textWidth = format()->sheet()->doc()->unzoomItX( fm.width( d->strOutText ) );
1925 int offsetFont = 0;
1926 if ( ( ay == Format::Bottom ) && fontUnderlined ) {
1927 offsetFont = fm.underlinePos() + 1;
1928 }
1929
1930 d->textHeight = format()->sheet()->doc()->unzoomItY( fm.ascent() + fm.descent()
1931 + offsetFont );
1932 }
1933 else if ( tmpAngle!= 0 ) {
1934 // Rotated text.
1935
1936 d->textHeight = format()->sheet()->doc()
1937 ->unzoomItY( int( cos( tmpAngle * M_PI / 180 )
1938 * ( fm.ascent() + fm.descent() )
1939 + abs( int( ( fm.width( d->strOutText )
1940 * sin( tmpAngle * M_PI / 180 ) ) ) ) ) );
1941
1942 d->textWidth = format()->sheet()->doc()
1943 ->unzoomItX( int( abs( int( ( sin( tmpAngle * M_PI / 180 )
1944 * ( fm.ascent() + fm.descent() ) ) ) )
1945 + fm.width( d->strOutText )
1946 * cos ( tmpAngle * M_PI / 180 ) ) );
1947 }
1948 else {
1949 // Vertical text.
1950 int width = 0;
1951 for ( unsigned int i = 0; i < d->strOutText.length(); i++ )
1952 width = TQMAX( width, fm.width( d->strOutText.at( i ) ) );
1953
1954 d->textWidth = format()->sheet()->doc()->unzoomItX( width );
1955 d->textHeight = format()->sheet()->doc()->unzoomItY( ( fm.ascent() + fm.descent() )
1956 * d->strOutText.length() );
1957 }
1958}
1959
1960
1961// Get the effective font to use after the zooming and apply it to `painter'.
1962//
1963// Used in makeLayout() and calculateTextParameters().
1964//
1965
1966void Cell::applyZoomedFont( TQPainter &painter, int _col, int _row )
1967{
1968 TQFont tmpFont( format()->textFont( _col, _row ) );
1969
1970 // If there is a matching condition on this cell then set the
1971 // according style parameters.
1972 if ( d->hasExtra()
1973 && d->extra()->conditions
1974 && d->extra()->conditions->matchedStyle() ) {
1975
1976 Style * s = d->extra()->conditions->matchedStyle();
1977
1978 // Other size?
1979 if ( s->hasFeature( Style::SFontSize, true ) )
1980 tmpFont.setPointSizeFloat( s->fontSize() );
1981
1982 // Other attributes?
1983 if ( s->hasFeature( Style::SFontFlag, true ) ) {
1984 uint flags = s->fontFlags();
1985
1986 tmpFont.setBold( flags & (uint) Style::FBold );
1987 tmpFont.setUnderline( flags & (uint) Style::FUnderline );
1988 tmpFont.setItalic( flags & (uint) Style::FItalic );
1989 tmpFont.setStrikeOut( flags & (uint) Style::FStrike );
1990 }
1991
1992 // Other family?
1993 if ( s->hasFeature( Style::SFontFamily, true ) )
1994 tmpFont.setFamily( s->fontFamily() );
1995 }
1996#if 0
1997 else
1998 /*
1999 * could somebody please explaint why we check for isProtected or isHideFormula here
2000 */
2001 if ( d->extra()->conditions
2002 && d->extra()->conditions->currentCondition( condition )
2003 && !(format()->sheet()->getShowFormula()
2004 && !( format()->sheet()->isProtected()
2005 && format()->isHideFormula( d->column, d->row ) ) ) )
2006 {
2007 if ( condition.fontcond )
2008 tmpFont = *(condition.fontcond);
2009 else
2010 tmpFont = condition.style->font();
2011 }
2012#endif
2013
2014 // Scale the font size according to the current zoom.
2015 tmpFont.setPointSizeFloat( 0.01 * format()->sheet()->doc()->zoom()
2016 * tmpFont.pointSizeFloat() );
2017
2018 painter.setFont( tmpFont );
2019}
2020
2021
2022//used in Sheet::adjustColumnHelper and Sheet::adjustRow
2023void Cell::calculateTextParameters( TQPainter &_painter,
2024 int _col, int _row )
2025{
2026 // Apply the correct font to _painter.
2027 applyZoomedFont( _painter, _col, _row );
2028
2029 // Recalculate d->textWidth and d->textHeight
2030 textSize( _painter );
2031
2032 // Recalculate d->textX and d->textY.
2033 offsetAlign( _col, _row );
2034}
2035
2036
2037// ----------------------------------------------------------------
2038// Formula handling
2039
2040
2042{
2043 clearFormula ();
2044
2045 d->formula = new KSpread::Formula (sheet(), this);
2046 d->formula->setExpression (d->strText);
2047
2048 if (!d->formula->isValid ()) {
2049 // Did a syntax error occur ?
2050 clearFormula();
2051
2052 if (format()->sheet()->doc()->getShowMessageError())
2053 {
2054 TQString tmp(i18n("Error in cell %1\n\n"));
2055 tmp = tmp.arg( fullName() );
2056 KMessageBox::error( (TQWidget*)0L, tmp);
2057 }
2058 setFlag(Flag_ParseError);
2059 Value v;
2060 v.setError ( "####" );
2061 setValue (v);
2062 return false;
2063 }
2064
2065 // we must recalc
2067
2068 return true;
2069}
2070
2072{
2073 delete d->formula;
2074 d->formula = 0L;
2075}
2076
2077bool Cell::calc(bool delay)
2078{
2079 if ( !isFormula() )
2080 return true;
2081
2082 if (d->formula == 0)
2083 {
2084 if ( testFlag( Flag_ParseError ) ) // there was a parse error
2085 return false;
2086 else
2087 {
2088 /* we were probably at a "isLoading() = true" state when we originally
2089 * parsed
2090 */
2091 makeFormula ();
2092
2093 if ( d->formula == 0 ) // there was a parse error
2094 return false;
2095 }
2096 }
2097
2098 if ( !testFlag( Flag_CalcDirty ) )
2099 return true;
2100
2101 if ( delay )
2102 {
2103 if ( format()->sheet()->doc()->delayCalculation() )
2104 return true;
2105 }
2106
2107 setFlag(Flag_LayoutDirty);
2108 setFlag(Flag_TextFormatDirty);
2109 clearFlag(Flag_CalcDirty);
2110
2111 Value result = d->formula->eval ();
2112 setValue (result);
2113 if (result.isNumber())
2114 checkNumberFormat(); // auto-chooses number or scientific
2115
2116 clearFlag(Flag_CalcDirty);
2117 setFlag(Flag_LayoutDirty);
2118
2119 return true;
2120}
2121
2122
2123// ================================================================
2124// Painting
2125
2126
2127// Paint the cell. This is the main function that calls a lot of
2128// helper functions.
2129//
2130// `rect' is the rectangle that we should paint on. If the cell
2131// does not overlap this rectangle, we can return immediately.
2132// `coordinate' is the origin (the upper left) of the cell in document
2133// coordinates.
2134//
2135
2136void Cell::paintCell( const KoRect &rect, TQPainter & painter,
2137 View *view,
2138 const KoPoint &coordinate,
2139 const TQPoint &cellRef,
2140 int paintBorder,
2141 TQPen & rightPen, TQPen & bottomPen,
2142 TQPen & leftPen, TQPen & topPen,
2143 TQValueList<TQPoint> &mergedCellsPainted,
2144 bool drawCursor )
2145{
2146 bool paintBorderRight = paintBorder & Border_Right;
2147 bool paintBorderBottom = paintBorder & Border_Bottom;
2148 bool paintBorderLeft = paintBorder & Border_Left;
2149 bool paintBorderTop = paintBorder & Border_Top;
2150
2151 // If we are already painting this cell, then return immediately.
2152 // This avoids infinite recursion.
2153 if ( testFlag( Flag_PaintingCell ) )
2154 return;
2155
2156 // Indicate that we are painting this cell now.
2157 setFlag( Flag_PaintingCell );
2158
2159 // This flag indicates that we are working on drawing the cells that
2160 // another cell is obscuring. The value is the number of levels down we
2161 // are currently working -- i.e. a cell obscured by a cell which is
2162 // obscured by a cell.
2163 static int paintingObscured = 0;
2164
2165#if 0
2166 if (paintingObscured == 0)
2167 kdDebug(36001) << "painting cell " << name() << endl;
2168 else
2169 kdDebug(36001) << " painting obscured cell " << name() << endl;
2170#endif
2171
2172 // Sanity check: If we're working on drawing an obscured cell, that
2173 // means this cell should have a cell that obscures it.
2174 Q_ASSERT(!(paintingObscured > 0 && d->extra()->obscuringCells.isEmpty()));
2175
2176 // The parameter cellref should be *this, unless this is the default cell.
2177 Q_ASSERT(isDefault()
2178 || (((cellRef.x() == d->column) && (cellRef.y() == d->row))));
2179
2180 Sheet::LayoutDirection sheetDir = format()->sheet()->layoutDirection();
2181
2182 double left = coordinate.x();
2183
2184 ColumnFormat * colFormat = format()->sheet()->columnFormat( cellRef.x() );
2185 RowFormat * rowFormat = format()->sheet()->rowFormat( cellRef.y() );
2186
2187 // Set width, height to the total width and height that this cell
2188 // covers, including obscured cells, and width0, height0 to the
2189 // width and height of this cell, maybe merged but never implicitly
2190 // extended.
2191 double width0 = colFormat->dblWidth();
2192 double height0 = rowFormat->dblHeight();
2193 double width = width0;
2194 double height = height0;
2195
2196 // Handle right-to-left layout.
2197 // In an RTL sheet the cells have to be painted at their opposite horizontal
2198 // location on the canvas, meaning that column A will be the rightmost column
2199 // on screen, column B will be to the left of it and so on. Here we change
2200 // the horizontal coordinate at which we start painting the cell in case the
2201 // sheet's direction is RTL. We do this only if paintingObscured is 0,
2202 // otherwise the cell's painting location will flip back and forth in
2203 // consecutive calls to paintCell when painting obscured cells.
2204 if ( sheetDir == Sheet::RightToLeft && paintingObscured == 0
2205 && view && view->canvasWidget() )
2206 {
2207 double dwidth = view->doc()->unzoomItX(view->canvasWidget()->width());
2208 left = dwidth - coordinate.x() - width;
2209 }
2210
2211 // See if this cell is merged or has overflown into neighbor cells.
2212 // In that case, the width/height is greater than just the cell
2213 // itself.
2214 if (d->hasExtra()) {
2215 if (d->extra()->mergedXCells > 0 || d->extra()->mergedYCells > 0) {
2216 // merged cell extends to the left if sheet is RTL
2217 if ( sheetDir == Sheet::RightToLeft ) {
2218 left -= d->extra()->extraWidth - width;
2219 }
2220 width0 = d->extra()->extraWidth;
2221 height0 = d->extra()->extraHeight;
2222 width = d->extra()->extraWidth;
2223 height = d->extra()->extraHeight;
2224 }
2225 else {
2226#if 0
2227 width += d->extra()->extraXCells ? d->extra()->extraWidth : 0;
2228 height += d->extra()->extraYCells ? d->extra()->extraHeight : 0;
2229#else
2230 // FIXME: Make extraWidth/Height really contain the *extra* width/height.
2231 if ( d->extra()->extraXCells )
2232 width = d->extra()->extraWidth;
2233 if ( d->extra()->extraYCells )
2234 height = d->extra()->extraHeight;
2235#endif
2236 }
2237 }
2238
2239 // Check if the cell is "selected", i.e. it should be drawn with the
2240 // color that indicates selection (dark blue). If more than one
2241 // square is selected, the last one uses the ordinary colors. In
2242 // that case, "selected" will be set to false even though the cell
2243 // itself really is selected.
2244 bool selected = false;
2245 if ( view != NULL ) {
2246 selected = view->selectionInfo()->contains( cellRef );
2247
2248 // But the cell doesn't look selected if this is the marker cell.
2249 Cell *cell = format()->sheet()->cellAt( view->selectionInfo()->marker() );
2250 TQPoint bottomRight( view->selectionInfo()->marker().x() + cell->extraXCells(),
2251 view->selectionInfo()->marker().y() + cell->extraYCells() );
2252 TQRect markerArea( view->selectionInfo()->marker(), bottomRight );
2253 selected = selected && !( markerArea.contains( cellRef ) );
2254
2255 // Don't draw any selection at all when printing.
2256 if ( painter.device()->isExtDev() || !drawCursor )
2257 selected = false;
2258 }
2259
2260 // Need to make a new layout ?
2261 //
2262 // FIXME: We have already used (at least) extraWidth/Height above,
2263 // and now we are recalculating the layout. This has to be
2264 // moved up above all uses.
2265 //
2266 // FIXME: This needs to be taken out eventually - it is done in
2267 // canvas::paintUpdates().
2268 if ( testFlag( Flag_LayoutDirty ) )
2269 makeLayout( painter, cellRef.x(), cellRef.y() );
2270
2271 // ---------------- Start the actual painting. ----------------
2272
2273 // If the rect of this cell doesn't intersect the rect that should
2274 // be painted, we can skip the rest and return. (Note that we need
2275 // to calculate `left' first before we can do this.)
2276 const KoRect cellRect( left, coordinate.y(), width, height );
2277 const KoRect cellRect0( left, coordinate.y(), width0, height0 );
2278 if ( !cellRect.intersects( rect ) ) {
2279 clearFlag( Flag_PaintingCell );
2280 return;
2281 }
2282
2283 // Get the background color.
2284 //
2285 // If there is a condition giving the background color for this cell
2286 // (and it matches), use that one, otherwise get the standard
2287 // background.
2288 TQColor backgroundColor;
2289 if ( d->hasExtra() && d->extra()->conditions
2290 && d->extra()->conditions->matchedStyle()
2291 && d->extra()->conditions->matchedStyle()->hasFeature( Style::SBackgroundColor, true ) )
2292 backgroundColor = d->extra()->conditions->matchedStyle()->bgColor();
2293 else
2294 backgroundColor = bgColor( cellRef.x(), cellRef.y() );
2295
2296 // 1. Paint the background.
2297 if ( !isPartOfMerged() )
2298 paintBackground( painter, cellRect0, cellRef, selected, backgroundColor );
2299
2300 // 2. Paint the default borders if we are on screen or if we are printing
2301 // and the checkbox to do this is checked.
2302 if ( painter.device()->devType() != TQInternal::Printer
2303 || format()->sheet()->print()->printGrid())
2304 paintDefaultBorders( painter, rect, cellRect, cellRef,
2305 paintBorderRight, paintBorderBottom,
2306 paintBorderLeft, paintBorderTop,
2307 rightPen, bottomPen, leftPen, topPen );
2308
2309 // 3. Paint all the cells that this one obscures. They may only be
2310 // partially obscured.
2311 //
2312 // The `paintingObscured' variable is used to avoid infinite
2313 // recursion since cells sometimes paint their obscuring cell as
2314 // well.
2315 paintingObscured++;
2316
2317 if (d->hasExtra() && (d->extra()->extraXCells > 0
2318 || d->extra()->extraYCells > 0)) {
2319 //kdDebug(36001) << "painting obscured cells for " << name() << endl;
2320
2321 paintObscuredCells( rect, painter, view, cellRect, cellRef,
2322 paintBorderRight, paintBorderBottom,
2323 paintBorderLeft, paintBorderTop,
2324 rightPen, bottomPen, leftPen, topPen,
2325 mergedCellsPainted);
2326
2327 // FIXME: Is this the right place for this?
2328 if ( d->extra()->mergedXCells > 0 || d->extra()->mergedYCells > 0 )
2329 mergedCellsPainted.prepend( cellRef );
2330 }
2331 paintingObscured--;
2332
2333 // 4. Paint the borders of the cell if no other cell is forcing this
2334 // one, i.e. this cell is not part of a merged cell.
2335 //
2336
2337 // If we print pages, then we disable clipping, otherwise borders are
2338 // cut in the middle at the page borders.
2339 if ( painter.device()->isExtDev() )
2340 painter.setClipping( false );
2341
2342 // Paint the borders if this cell is not part of another merged cell.
2343 if ( !isPartOfMerged() ) {
2344 // if (!testFlag(Flag_Highlight))
2345 paintCellBorders( painter, rect, cellRect0,
2346 cellRef,
2347 paintBorderRight, paintBorderBottom,
2348 paintBorderLeft, paintBorderTop,
2349 rightPen, bottomPen, leftPen, topPen );
2350 }
2351
2352 // Turn clipping back on.
2353 if ( painter.device()->isExtDev() )
2354 painter.setClipping( true );
2355
2356 // 5. Paint diagonal lines and page borders.
2357 paintCellDiagonalLines( painter, cellRect0, cellRef );
2358
2359 paintPageBorders( painter, cellRect0, cellRef,
2360 paintBorderRight, paintBorderBottom );
2361
2362
2363 // 6. Now paint the content, if this cell isn't obscured.
2364 if ( !isObscured() ) {
2365
2366 // 6a. Paint possible comment indicator.
2367 if ( !painter.device()->isExtDev()
2368 || format()->sheet()->print()->printCommentIndicator() )
2369 paintCommentIndicator( painter, cellRect, cellRef, backgroundColor );
2370
2371 // 6b. Paint possible formula indicator.
2372 if ( !painter.device()->isExtDev()
2373 || format()->sheet()->print()->printFormulaIndicator() )
2374 paintFormulaIndicator( painter, cellRect, backgroundColor );
2375
2376 // 6c. Paint possible indicator for clipped text.
2377 paintMoreTextIndicator( painter, cellRect, backgroundColor );
2378
2379 //6c. Paint cell highlight
2380#if 0
2381 if (highlightBorder != Border_None)
2382 paintCellHighlight ( painter, cellRect, cellRef, highlightBorder,
2383 rightHighlightPen, bottomHighlightPen,
2384 leftHighlightPen, topHighlightPen );
2385#endif
2386
2387 // 6d. Paint the text in the cell unless:
2388 // a) it is empty
2389 // b) something indicates that the text should not be painted
2390 // c) the sheet is protected and the cell is hidden.
2391 if ( !d->strOutText.isEmpty()
2392 && ( !painter.device()->isExtDev()
2393 || !format()->getDontprintText( cellRef.x(), cellRef.y() ) )
2394 && !( format()->sheet()->isProtected()
2395 && format()->isHideAll( cellRef.x(), cellRef.y() ) ) )
2396 {
2397 paintText( painter, cellRect, cellRef );
2398 }
2399 }
2400
2401 // 7. If this cell is obscured and we are not already painting obscured
2402 // cells, then paint the obscuring cell(s). Otherwise don't do
2403 // anything so that we don't cause an infinite loop.
2404 if ( isObscured() && paintingObscured == 0 &&
2405 !( sheetDir == Sheet::RightToLeft && painter.device()->isExtDev() ) )
2406 {
2407
2408 //kdDebug(36001) << "painting cells that obscure " << name() << endl;
2409
2410 // Store the obscuringCells list in a list of TQPoint(column, row)
2411 // This avoids crashes during the iteration through
2412 // obscuringCells, when the cells may get non valid or the list
2413 // itself gets changed during a call of obscuringCell->paintCell
2414 // (this happens e.g. when there is an updateDepend)
2415 if (d->hasExtra()) {
2416 TQValueList<TQPoint> listPoints;
2417 TQValueList<Cell*>::iterator it = d->extra()->obscuringCells.begin();
2418 TQValueList<Cell*>::iterator end = d->extra()->obscuringCells.end();
2419 for ( ; it != end; ++it ) {
2420 Cell *obscuringCell = *it;
2421
2422 listPoints.append( TQPoint( obscuringCell->column(), obscuringCell->row() ) );
2423 }
2424
2425 TQValueList<TQPoint>::iterator it1 = listPoints.begin();
2426 TQValueList<TQPoint>::iterator end1 = listPoints.end();
2427 for ( ; it1 != end1; ++it1 ) {
2428 TQPoint obscuringCellRef = *it1;
2429
2430 // Only paint those obscuring cells that haven't been already
2431 // painted yet.
2432 //
2433 // This optimization removes an O(n^4) behaviour where n is
2434 // the number of cells on one edge in a merged cell.
2435 if ( mergedCellsPainted.contains( obscuringCellRef ) )
2436 continue;
2437
2438 Cell *obscuringCell = format()->sheet()->cellAt( obscuringCellRef.x(),
2439 obscuringCellRef.y() );
2440
2441 if ( obscuringCell != 0 ) {
2442 double x = format()->sheet()->dblColumnPos( obscuringCellRef.x() );
2443 double y = format()->sheet()->dblRowPos( obscuringCellRef.y() );
2444 if ( view != 0 ) {
2445 x -= view->canvasWidget()->xOffset();
2446 y -= view->canvasWidget()->yOffset();
2447 }
2448
2449 KoPoint corner( x, y );
2450 painter.save();
2451
2452 // Get the effective pens for the borders. These are
2453 // determined by possible conditions on the cell with
2454 // associated styles.
2455 TQPen rp( obscuringCell->effRightBorderPen( obscuringCellRef.x(),
2456 obscuringCellRef.y() ) );
2457 TQPen bp( obscuringCell->effBottomBorderPen( obscuringCellRef.x(),
2458 obscuringCellRef.y() ) );
2459 TQPen lp( obscuringCell->effLeftBorderPen( obscuringCellRef.x(),
2460 obscuringCellRef.y() ) );
2461 TQPen tp( obscuringCell->effTopBorderPen( obscuringCellRef.x(),
2462 obscuringCellRef.y() ) );
2463
2464
2465 //kdDebug(36001) << " painting obscuring cell "
2466 // << obscuringCell->name() << endl;
2467 // TQPen highlightPen;
2468
2469 //Note: Painting of highlight isn't quite right. If several
2470 // cells are merged, then the whole merged cell will be
2471 // painted with the colour of the last cell referenced
2472 // which is inside the merged range.
2473 obscuringCell->paintCell( rect, painter, view,
2474 corner, obscuringCellRef,
2475 Border_Left|Border_Top|Border_Right|Border_Bottom,
2476 rp, bp, lp, tp,
2477 mergedCellsPainted); // new pens
2478 painter.restore();
2479 }
2480 }
2481 }
2482 }
2483
2484 // We are done with the painting, so remove the flag on the cell.
2485 clearFlag( Flag_PaintingCell );
2486}
2487
2488
2489
2490// The following code was commented out in the above function. I'll
2491// leave it here in case this functionality is ever re-implemented and
2492// someone wants some code to start from.
2493//
2494#if 0
2495
2499 if ( d->style == Cell::ST_Button ) {
2500 TQBrush fill( TQt::lightGray );
2501 TQApplication::style().drawControl( TQStyle::CE_PushButton, &_painter, this,
2502 TQRect( _tx + 1, _ty + 1, w2 - 1, h2 - 1 ),
2503 defaultColorGroup ); //, selected, &fill );
2504 }
2505
2509 else if ( d->style == Cell::ST_Select ) {
2510 TQApplication::style().drawComboButton( &_painter, _tx + 1, _ty + 1,
2511 w2 - 1, h2 - 1,
2512 defaultColorGroup, selected );
2513 }
2514#endif
2515
2516
2517#if 0
2518 void Cell::paintCellHighlight(TQPainter& painter,
2519 const KoRect& cellRect,
2520 const TQPoint& cellRef,
2521 const int highlightBorder,
2522 const TQPen& rightPen,
2523 const TQPen& bottomPen,
2524 const TQPen& leftPen,
2525 const TQPen& topPen
2526 )
2527{
2528 //painter.drawLine(cellRect.left(),cellRect.top(),cellRect.right(),cellRect.bottom());
2529 //TQPen pen(d->extra()->highlight);
2530 //painter.setPen(highlightPen);
2531
2532 TQBrush nullBrush;
2533 painter.setBrush(nullBrush);
2534
2535 TQRect zoomedCellRect = sheet()->doc()->zoomRect( cellRect );
2536
2537 //The highlight rect is just inside the main cell rect
2538 //This saves the hassle of repainting nearby cells when the highlight is changed as the highlight areas
2539 //do not overlap
2540 zoomedCellRect.setLeft(zoomedCellRect.left()+1);
2541 //zoomedCellRect.setRight(zoomedCellRect.right()-1);
2542 zoomedCellRect.setTop(zoomedCellRect.top()+1);
2543 //zoomedCellRect.setBottom(zoomedCellRect.bottom()-1);
2544
2545 if ( cellRef.x() != KS_colMax )
2546 zoomedCellRect.setWidth( zoomedCellRect.width() - 1 );
2547 if ( cellRef.y() != KS_rowMax )
2548 zoomedCellRect.setHeight( zoomedCellRect.height() - 1 );
2549
2550 if (highlightBorder & Border_Top)
2551 {
2552 painter.setPen(topPen);
2553 painter.drawLine(zoomedCellRect.left(),zoomedCellRect.top(),zoomedCellRect.right(),zoomedCellRect.top());
2554 }
2555 if (highlightBorder & Border_Left)
2556 {
2557 painter.setPen(leftPen);
2558 painter.drawLine(zoomedCellRect.left(),zoomedCellRect.top(),zoomedCellRect.left(),zoomedCellRect.bottom());
2559 }
2560 if (highlightBorder & Border_Right)
2561 {
2562 painter.setPen(rightPen);
2563 painter.drawLine(zoomedCellRect.right(),zoomedCellRect.top(),zoomedCellRect.right(),zoomedCellRect.bottom());
2564 }
2565 if (highlightBorder & Border_Bottom)
2566 {
2567 painter.setPen(bottomPen);
2568 painter.drawLine(zoomedCellRect.left(),zoomedCellRect.bottom(),zoomedCellRect.right(),zoomedCellRect.bottom());
2569 }
2570
2571 if (highlightBorder & Border_SizeGrip)
2572 {
2573 TQBrush brush(rightPen.color());
2574 painter.setBrush(brush);
2575 painter.setPen(rightPen);
2576 painter.drawRect(zoomedCellRect.right()-3,zoomedCellRect.bottom()-3,4,4);
2577 }
2578
2579 //painter.drawRect(zoomedCellRect.left(),zoomedCellRect.top(),zoomedCellRect.width(),zoomedCellRect.height());
2580}
2581#endif
2582
2583
2584// Paint all the cells that this cell obscures (helper function to paintCell).
2585//
2586void Cell::paintObscuredCells(const KoRect& rect, TQPainter& painter,
2587 View* view,
2588 const KoRect &cellRect,
2589 const TQPoint &cellRef,
2590 bool paintBorderRight,
2591 bool _paintBorderBottom,
2592 bool paintBorderLeft,
2593 bool _paintBorderTop,
2594 TQPen & rightPen, TQPen & _bottomPen,
2595 TQPen & leftPen, TQPen & _topPen,
2596 TQValueList<TQPoint> &mergedCellsPainted)
2597{
2598 // If there are no obscured cells, return.
2599 if ( !extraXCells() && !extraYCells() )
2600 return;
2601
2602 double ypos = cellRect.y();
2603 int maxY = extraYCells();
2604 int maxX = extraXCells();
2605
2606 // Loop through the rectangle of squares that we obscure and paint them.
2607 for ( int y = 0; y <= maxY; ++y ) {
2608 double xpos = cellRect.x();
2609 RowFormat* rl = format()->sheet()->rowFormat( cellRef.y() + y );
2610
2611 for( int x = 0; x <= maxX; ++ x ) {
2612 ColumnFormat * cl = format()->sheet()->columnFormat( cellRef.x() + x );
2613 if ( y != 0 || x != 0 ) {
2614 uint column = cellRef.x() + x;
2615 uint row = cellRef.y() + y;
2616
2617 TQPen topPen;
2618 TQPen bottomPen;
2619 bool paintBorderTop;
2620 bool paintBorderBottom;
2621
2622 Cell *cell = format()->sheet()->cellAt( column, row );
2623 KoPoint corner( xpos, ypos );
2624
2625 // Check if the upper and lower borders should be painted, and
2626 // if so which pens we should use. There used to be a nasty
2627 // bug here (#61452).
2628 // Check top pen. Only check if this is not on the top row.
2629 topPen = _topPen;
2630 paintBorderTop = _paintBorderTop;
2631 if ( row > 1 && !cell->isPartOfMerged() ) {
2632 Cell *cellUp = format()->sheet()->cellAt( column, row - 1 );
2633
2634 if ( cellUp->isDefault() )
2635 paintBorderTop = false;
2636 else {
2637 // If the cell towards the top is part of a merged cell, get
2638 // the pointer to the master cell.
2639 cellUp = cellUp->ultimateObscuringCell();
2640
2641 topPen = cellUp->effBottomBorderPen( cellUp->column(),
2642 cellUp->row() );
2643
2644#if 0
2645 int penWidth = TQMAX(1, sheet()->doc()->zoomItY( topPen.width() ));
2646 topPen.setWidth( penWidth );
2647#endif
2648 }
2649 }
2650
2651 // FIXME: I thought we had to check bottom pen as well.
2652 // However, it looks as if we don't need to. It works anyway.
2653 bottomPen = _bottomPen;
2654 paintBorderBottom = _paintBorderBottom;
2655
2656 int paintBorder = Border_None;
2657 if (paintBorderLeft) paintBorder |= Cell::Border_Left;
2658 if (paintBorderRight) paintBorder |= Cell::Border_Right;
2659 if (paintBorderTop) paintBorder |= Cell::Border_Top;
2660 if (paintBorderBottom) paintBorder |= Cell::Border_Bottom;
2661
2662 /*Cell::BorderSides highlightBorder = Border_None;
2663 TQPen highlightPen;*/
2664
2665
2666 //kdDebug(36001) << "calling paintcell for obscured cell "
2667 // << cell->name() << endl;
2668 cell->paintCell( rect, painter, view,
2669 corner,
2670 TQPoint( cellRef.x() + x, cellRef.y() + y ),
2671 paintBorder,
2672 rightPen, bottomPen, leftPen, topPen,
2673 mergedCellsPainted);
2674 }
2675 xpos += cl->dblWidth();
2676 }
2677
2678 ypos += rl->dblHeight();
2679 }
2680}
2681
2682
2683// Paint the background of this cell.
2684//
2685void Cell::paintBackground( TQPainter& painter, const KoRect &cellRect,
2686 const TQPoint &cellRef, bool selected,
2687 TQColor &backgroundColor )
2688{
2689 TQColorGroup defaultColorGroup = TQApplication::palette().active();
2690 TQRect zoomedCellRect = sheet()->doc()->zoomRect( cellRect );
2691
2692 // If this is not the KS_rowMax and/or KS_colMax, then we reduce
2693 // width and/or height by one. This is due to the fact that the
2694 // right/bottom most pixel is shared with the left/top most pixel of
2695 // the following cell. Only in the case of KS_colMax/KS_rowMax we
2696 // need to draw even this pixel, as there isn't a following cell to
2697 // draw the background pixel.
2698 if ( cellRef.x() != KS_colMax )
2699 zoomedCellRect.setWidth( zoomedCellRect.width() - 1 );
2700 if ( cellRef.y() != KS_rowMax )
2701 zoomedCellRect.setHeight( zoomedCellRect.height() - 1 );
2702
2703 // Determine the correct background color
2704 if ( selected )
2705 {
2706 //If the cell's background color is too bright, use the default highlight color
2707 //Otherwise use a lighter version of the cell's background color.
2708 TQColor c;
2709
2710 int averageColor = (backgroundColor.red() + backgroundColor.green() + backgroundColor.blue()) / 3;
2711
2712 if (averageColor > 180)
2713 if (averageColor > 225)
2715 else
2716 c = backgroundColor.light( 115 ); //15% lighter
2717 else
2718 c = backgroundColor.light( 125 ); //25% lighter
2719
2720 painter.setBackgroundColor( c );
2721 }
2722 else {
2723 TQColor bg( backgroundColor );
2724
2725 // Handle printers separately.
2726 if ( !painter.device()->isExtDev() ) {
2727 if ( bg.isValid() )
2728 painter.setBackgroundColor( bg );
2729 else
2730 painter.setBackgroundColor( defaultColorGroup.base() );
2731 }
2732 else {
2733 //bad hack but there is a tqt bug
2734 //so I can print backgroundcolor
2735 TQBrush bb( bg );
2736 if ( !bg.isValid() )
2737 bb.setColor( TQt::white );
2738
2739 painter.fillRect( zoomedCellRect, bb );
2740 return;
2741 }
2742 }
2743
2744 // Erase the background of the cell.
2745 if ( !painter.device()->isExtDev() )
2746 painter.eraseRect( zoomedCellRect );
2747
2748 // Get a background brush
2749 TQBrush bb;
2750 if ( d->hasExtra()
2751 && d->extra()->conditions
2752 && d->extra()->conditions->matchedStyle()
2753 && d->extra()->conditions->matchedStyle()->hasFeature( Style::SBackgroundBrush, true ) )
2754 bb = d->extra()->conditions->matchedStyle()->backGroundBrush();
2755 else
2756 bb = backGroundBrush( cellRef.x(), cellRef.y() );
2757
2758 // Draw background pattern if necessary.
2759 if ( bb.style() != TQt::NoBrush )
2760 painter.fillRect( zoomedCellRect, bb );
2761
2762 backgroundColor = painter.backgroundColor();
2763}
2764
2765
2766// Paint the standard light grey borders that are always visible.
2767//
2768void Cell::paintDefaultBorders( TQPainter& painter, const KoRect &rect,
2769 const KoRect &cellRect,
2770 const TQPoint &cellRef,
2771 bool paintBorderRight, bool /*paintBorderBottom*/,
2772 bool paintBorderLeft, bool paintBorderTop,
2773 TQPen const & rightPen, TQPen const & /*bottomPen*/,
2774 TQPen const & leftPen, TQPen const & topPen )
2775{
2776 /*
2777 *** Notes about optimisation ***
2778
2779 This function was painting the top , left , right & bottom lines in almost all cells previously, contrary to what the comment
2780 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,
2781 so I have disabled drawing of right and bottom borders for all cells.
2782
2783 I also couldn't work out under what conditions the variables dt / db would come out as anything other than 0 in the code
2784 for painting the various borders. The effTopBorderPen / effBottomBorderPen calls were taking up a lot of time
2785 according some profiling I did. If that code really is necessary, we need to find a more efficient way of getting the widths
2786 than grabbing the whole TQPen object and asking it.
2787
2788
2789 --Robert Knight (robertknight@gmail.com)
2790 */
2791 Doc* doc = sheet()->doc();
2792
2793 Sheet::LayoutDirection sheetDir = format()->sheet()->layoutDirection();
2794 bool paintingToExternalDevice = painter.device()->isExtDev();
2795
2796 // Each cell is responsible for drawing it's top and left portions
2797 // of the "default" grid. --Or not drawing it if it shouldn't be
2798 // there. It's also responsible to paint the right and bottom, if
2799 // it is the last cell on a print out.
2800
2801 bool paintTop;
2802 bool paintLeft;
2803 bool paintBottom=false;
2804 bool paintRight=false;
2805
2806 paintLeft = ( paintBorderLeft && leftPen.style() == TQt::NoPen
2807 && sheet()->getShowGrid() && sheetDir==Sheet::LeftToRight );
2808 paintRight = ( paintBorderRight && rightPen.style() == TQt::NoPen
2809 && sheet()->getShowGrid() && sheetDir==Sheet::RightToLeft );
2810 paintTop = ( paintBorderTop && topPen.style() == TQt::NoPen
2811 && sheet()->getShowGrid() );
2812// paintBottom = ( paintBorderBottom && sheet()->getShowGrid()
2813// && bottomPen.style() == TQt::NoPen );
2814
2815
2816 //Set the single-pixel with pen for drawing the borders with.
2817 painter.setPen( TQPen( sheet()->doc()->gridColor(), 1, TQt::SolidLine ) );
2818
2819 // If there are extra cells, there might be more conditions.
2820 if (d->hasExtra()) {
2821 TQValueList<Cell*>::const_iterator it = d->extra()->obscuringCells.begin();
2822 TQValueList<Cell*>::const_iterator end = d->extra()->obscuringCells.end();
2823 for ( ; it != end; ++it ) {
2824 Cell *cell = *it;
2825
2826 paintTop = paintTop && ( cell->row() == cellRef.y() );
2827 paintBottom = false;
2828
2829 if ( sheetDir == Sheet::RightToLeft ) {
2830 paintRight = paintRight && ( cell->column() == cellRef.x() );
2831 paintLeft = false;
2832 }
2833 else {
2834 paintLeft = paintLeft && ( cell->column() == cellRef.x() );
2835 paintRight = false;
2836 }
2837 }
2838 }
2839
2840 // The left border.
2841 if ( paintLeft ) {
2842 int dt = 0;
2843 int db = 0;
2844
2845 #if 0
2846 if ( cellRef.x() > 1 ) {
2847 Cell *cell_west = format()->sheet()->cellAt( cellRef.x() - 1,
2848 cellRef.y() );
2849 TQPen t = cell_west->effTopBorderPen( cellRef.x() - 1, cellRef.y() );
2850 TQPen b = cell_west->effBottomBorderPen( cellRef.x() - 1, cellRef.y() );
2851
2852 if ( t.style() != TQt::NoPen )
2853 dt = ( t.width() + 1 )/2;
2854 if ( b.style() != TQt::NoPen )
2855 db = ( t.width() / 2);
2856 }
2857 #endif
2858
2859 // If we are on paper printout, we limit the length of the lines.
2860 // On paper, we always have full cells, on screen not.
2861 if ( paintingToExternalDevice ) {
2862 if ( sheetDir == Sheet::RightToLeft )
2863 painter.drawLine( doc->zoomItX( TQMAX( rect.left(), cellRect.right() ) ),
2864 doc->zoomItY( TQMAX( rect.top(), cellRect.y() + dt ) ),
2865 doc->zoomItX( TQMIN( rect.right(), cellRect.right() ) ),
2866 doc->zoomItY( TQMIN( rect.bottom(), cellRect.bottom() - db ) ) );
2867 else
2868 painter.drawLine( doc->zoomItX( TQMAX( rect.left(), cellRect.x() ) ),
2869 doc->zoomItY( TQMAX( rect.top(), cellRect.y() + dt ) ),
2870 doc->zoomItX( TQMIN( rect.right(), cellRect.x() ) ),
2871 doc->zoomItY( TQMIN( rect.bottom(), cellRect.bottom() - db ) ) );
2872 }
2873 else {
2874 if ( sheetDir == Sheet::RightToLeft )
2875 painter.drawLine( doc->zoomItX( cellRect.right() ),
2876 doc->zoomItY( cellRect.y() + dt ),
2877 doc->zoomItX( cellRect.right() ),
2878 doc->zoomItY( cellRect.bottom() - db ) );
2879 else
2880 painter.drawLine( doc->zoomItX( cellRect.x() ),
2881 doc->zoomItY( cellRect.y() + dt ),
2882 doc->zoomItX( cellRect.x() ),
2883 doc->zoomItY( cellRect.bottom() - db ) );
2884 }
2885 }
2886
2887
2888 // The top border.
2889 if ( paintTop ) {
2890 int dl = 0;
2891 int dr = 0;
2892
2893 #if 0
2894 if ( cellRef.y() > 1 ) {
2895 Cell *cell_north = format()->sheet()->cellAt( cellRef.x(),
2896 cellRef.y() - 1 );
2897
2898 TQPen l = cell_north->effLeftBorderPen( cellRef.x(), cellRef.y() - 1 );
2899 TQPen r = cell_north->effRightBorderPen( cellRef.x(), cellRef.y() - 1 );
2900
2901 if ( l.style() != TQt::NoPen )
2902 dl = ( l.width() - 1 ) / 2 + 1;
2903 if ( r.style() != TQt::NoPen )
2904 dr = r.width() / 2;
2905 }
2906 #endif
2907
2908
2909
2910 // If we are on paper printout, we limit the length of the lines.
2911 // On paper, we always have full cells, on screen not.
2912 if ( paintingToExternalDevice ) {
2913 painter.drawLine( doc->zoomItX( TQMAX( rect.left(), cellRect.x() + dl ) ),
2914 doc->zoomItY( TQMAX( rect.top(), cellRect.y() ) ),
2915 doc->zoomItX( TQMIN( rect.right(), cellRect.right() - dr ) ),
2916 doc->zoomItY( TQMIN( rect.bottom(), cellRect.y() ) ) );
2917 }
2918 else {
2919 painter.drawLine( doc->zoomItX( cellRect.x() + dl ),
2920 doc->zoomItY( cellRect.y() ),
2921 doc->zoomItX( cellRect.right() - dr ),
2922 doc->zoomItY( cellRect.y() ) );
2923 }
2924 }
2925
2926
2927 // The right border.
2928 if ( paintRight ) {
2929 int dt = 0;
2930 int db = 0;
2931
2932 #if 0
2933 if ( cellRef.x() < KS_colMax ) {
2934 Cell *cell_east = format()->sheet()->cellAt( cellRef.x() + 1,
2935 cellRef.y() );
2936
2937 TQPen t = cell_east->effTopBorderPen( cellRef.x() + 1, cellRef.y() );
2938 TQPen b = cell_east->effBottomBorderPen( cellRef.x() + 1, cellRef.y() );
2939
2940 if ( t.style() != TQt::NoPen )
2941 dt = ( t.width() + 1 ) / 2;
2942 if ( b.style() != TQt::NoPen )
2943 db = ( t.width() / 2);
2944 }
2945 #endif
2946
2947 //painter.setPen( TQPen( sheet()->doc()->gridColor(), 1, TQt::SolidLine ) );
2948
2949 // If we are on paper printout, we limit the length of the lines.
2950 // On paper, we always have full cells, on screen not.
2951 if ( painter.device()->isExtDev() ) {
2952 if ( sheetDir == Sheet::RightToLeft )
2953 painter.drawLine( doc->zoomItX( TQMAX( rect.left(), cellRect.x() ) ),
2954 doc->zoomItY( TQMAX( rect.top(), cellRect.y() + dt ) ),
2955 doc->zoomItX( TQMIN( rect.right(), cellRect.x() ) ),
2956 doc->zoomItY( TQMIN( rect.bottom(), cellRect.bottom() - db ) ) );
2957 else
2958 painter.drawLine( doc->zoomItX( TQMAX( rect.left(), cellRect.right() ) ),
2959 doc->zoomItY( TQMAX( rect.top(), cellRect.y() + dt ) ),
2960 doc->zoomItX( TQMIN( rect.right(), cellRect.right() ) ),
2961 doc->zoomItY( TQMIN( rect.bottom(), cellRect.bottom() - db ) ) );
2962 }
2963 else {
2964 if ( sheetDir == Sheet::RightToLeft )
2965 painter.drawLine( doc->zoomItX( cellRect.x() ),
2966 doc->zoomItY( cellRect.y() + dt ),
2967 doc->zoomItX( cellRect.x() ),
2968 doc->zoomItY( cellRect.bottom() - db ) );
2969 else
2970 painter.drawLine( doc->zoomItX( cellRect.right() ),
2971 doc->zoomItY( cellRect.y() + dt ),
2972 doc->zoomItX( cellRect.right() ),
2973 doc->zoomItY( cellRect.bottom() - db ) );
2974 }
2975 }
2976
2977 // The bottom border.
2978 /*if ( paintBottom ) {
2979 int dl = 0;
2980 int dr = 0;
2981 if ( cellRef.y() < KS_rowMax ) {
2982 Cell *cell_south = format()->sheet()->cellAt( cellRef.x(),
2983 cellRef.y() + 1 );
2984
2985 TQPen l = cell_south->effLeftBorderPen( cellRef.x(), cellRef.y() + 1 );
2986 TQPen r = cell_south->effRightBorderPen( cellRef.x(), cellRef.y() + 1 );
2987
2988 if ( l.style() != TQt::NoPen )
2989 dl = ( l.width() - 1 ) / 2 + 1;
2990 if ( r.style() != TQt::NoPen )
2991 dr = r.width() / 2;
2992 }
2993
2994 painter.setPen( TQPen( sheet()->doc()->gridColor(), 1, TQt::SolidLine ) );
2995
2996 // If we are on paper printout, we limit the length of the lines.
2997 // On paper, we always have full cells, on screen not.
2998 if ( painter.device()->isExtDev() ) {
2999 painter.drawLine( doc->zoomItX( TQMAX( rect.left(), cellRect.x() + dl ) ),
3000 doc->zoomItY( TQMAX( rect.top(), cellRect.bottom() ) ),
3001 doc->zoomItX( TQMIN( rect.right(), cellRect.right() - dr ) ),
3002 doc->zoomItY( TQMIN( rect.bottom(), cellRect.bottom() ) ) );
3003 }
3004 else {
3005 painter.drawLine( doc->zoomItX( cellRect.x() + dl ),
3006 doc->zoomItY( cellRect.bottom() ),
3007 doc->zoomItX( cellRect.right() - dr ),
3008 doc->zoomItY( cellRect.bottom() ) );
3009 }
3010 }*/
3011}
3012
3013
3014// Paint a comment indicator if the cell has a comment.
3015//
3016void Cell::paintCommentIndicator( TQPainter& painter,
3017 const KoRect &cellRect,
3018 const TQPoint &/*cellRef*/,
3019 TQColor &backgroundColor )
3020{
3021 Doc * doc = sheet()->doc();
3022
3023 // Point the little corner if there is a comment attached
3024 // to this cell.
3025 if ( ( format()->propertiesMask() & (uint) Format::PComment )
3026 && cellRect.width() > 10.0
3027 && cellRect.height() > 10.0
3028 && ( sheet()->print()->printCommentIndicator()
3029 || ( !painter.device()->isExtDev() && sheet()->getShowCommentIndicator() ) ) ) {
3030 TQColor penColor = TQt::red;
3031
3032 // If background has high red part, switch to blue.
3033 if ( tqRed( backgroundColor.rgb() ) > 127 &&
3034 tqGreen( backgroundColor.rgb() ) < 80 &&
3035 tqBlue( backgroundColor.rgb() ) < 80 )
3036 {
3037 penColor = TQt::blue;
3038 }
3039
3040 // Get the triangle.
3041 TQPointArray point( 3 );
3042 if ( format()->sheet()->layoutDirection()==Sheet::RightToLeft ) {
3043 point.setPoint( 0, doc->zoomItX( cellRect.x() + 6.0 ),
3044 doc->zoomItY( cellRect.y() ) );
3045 point.setPoint( 1, doc->zoomItX( cellRect.x() ),
3046 doc->zoomItY( cellRect.y() ) );
3047 point.setPoint( 2, doc->zoomItX( cellRect.x() ),
3048 doc->zoomItY( cellRect.y() + 6.0 ) );
3049 }
3050 else {
3051 point.setPoint( 0, doc->zoomItX( cellRect.right() - 5.0 ),
3052 doc->zoomItY( cellRect.y() ) );
3053 point.setPoint( 1, doc->zoomItX( cellRect.right() ),
3054 doc->zoomItY( cellRect.y() ) );
3055 point.setPoint( 2, doc->zoomItX( cellRect.right() ),
3056 doc->zoomItY( cellRect.y() + 5.0 ) );
3057 }
3058
3059 // And draw it.
3060 painter.setBrush( TQBrush( penColor ) );
3061 painter.setPen( TQt::NoPen );
3062 painter.drawPolygon( point );
3063 }
3064}
3065
3066
3067
3068// Paint a small rectangle if this cell holds a formula.
3069//
3070void Cell::paintFormulaIndicator( TQPainter& painter,
3071 const KoRect &cellRect,
3072 TQColor &backgroundColor )
3073{
3074 if ( isFormula() &&
3075 format()->sheet()->getShowFormulaIndicator() &&
3076 cellRect.width() > 10.0 &&
3077 cellRect.height() > 10.0 )
3078 {
3079 Doc* doc = sheet()->doc();
3080
3081 TQColor penColor = TQt::blue;
3082 // If background has high blue part, switch to red.
3083 if ( tqRed( backgroundColor.rgb() ) < 80 &&
3084 tqGreen( backgroundColor.rgb() ) < 80 &&
3085 tqBlue( backgroundColor.rgb() ) > 127 )
3086 {
3087 penColor = TQt::red;
3088 }
3089
3090 // Get the triangle...
3091 TQPointArray point( 3 );
3092 if ( format()->sheet()->layoutDirection()==Sheet::RightToLeft ) {
3093 point.setPoint( 0, doc->zoomItX( cellRect.right() - 6.0 ),
3094 doc->zoomItY( cellRect.bottom() ) );
3095 point.setPoint( 1, doc->zoomItX( cellRect.right() ),
3096 doc->zoomItY( cellRect.bottom() ) );
3097 point.setPoint( 2, doc->zoomItX( cellRect.right() ),
3098 doc->zoomItY( cellRect.bottom() - 6.0 ) );
3099 }
3100 else {
3101 point.setPoint( 0, doc->zoomItX( cellRect.x() ),
3102 doc->zoomItY( cellRect.bottom() - 6.0 ) );
3103 point.setPoint( 1, doc->zoomItX( cellRect.x() ),
3104 doc->zoomItY( cellRect.bottom() ) );
3105 point.setPoint( 2, doc->zoomItX( cellRect.x() + 6.0 ),
3106 doc->zoomItY( cellRect.bottom() ) );
3107 }
3108
3109 // ...and draw it.
3110 painter.setBrush( TQBrush( penColor ) );
3111 painter.setPen( TQt::NoPen );
3112 painter.drawPolygon( point );
3113 }
3114}
3115
3116
3117// Paint an indicator that the text in the cell is cut.
3118//
3119void Cell::paintMoreTextIndicator( TQPainter& painter,
3120 const KoRect &cellRect,
3121 TQColor &backgroundColor )
3122{
3123 // Show a red triangle when it's not possible to write all text in cell.
3124 // Don't print the red triangle if we're printing.
3125 if( testFlag( Flag_CellTooShortX ) &&
3126 !painter.device()->isExtDev() &&
3127 cellRect.height() > 4.0 &&
3128 cellRect.width() > 4.0 )
3129 {
3130 Doc* doc = sheet()->doc();
3131
3132 TQColor penColor = TQt::red;
3133 // If background has high red part, switch to blue.
3134 if ( tqRed( backgroundColor.rgb() ) > 127
3135 && tqGreen( backgroundColor.rgb() ) < 80
3136 && tqBlue( backgroundColor.rgb() ) < 80 )
3137 {
3138 penColor = TQt::blue;
3139 }
3140
3141 // Get the triangle...
3142 TQPointArray point( 3 );
3143 if ( d->strOutText.isRightToLeft() ) {
3144 point.setPoint( 0, doc->zoomItX( cellRect.left() + 4.0 ),
3145 doc->zoomItY( cellRect.y() + cellRect.height() / 2.0 -4.0 ) );
3146 point.setPoint( 1, doc->zoomItX( cellRect.left() ),
3147 doc->zoomItY( cellRect.y() + cellRect.height() / 2.0 ));
3148 point.setPoint( 2, doc->zoomItX( cellRect.left() + 4.0 ),
3149 doc->zoomItY( cellRect.y() + cellRect.height() / 2.0 +4.0 ) );
3150 }
3151 else {
3152 point.setPoint( 0, doc->zoomItX( cellRect.right() - 4.0 ),
3153 doc->zoomItY( cellRect.y() + cellRect.height() / 2.0 - 4.0 ) );
3154 point.setPoint( 1, doc->zoomItX( cellRect.right() ),
3155 doc->zoomItY( cellRect.y() + cellRect.height() / 2.0 ) );
3156 point.setPoint( 2, doc->zoomItX( cellRect.right() - 4.0 ),
3157 doc->zoomItY( cellRect.y() + cellRect.height() / 2.0 + 4.0 ) );
3158 }
3159
3160 // ...and paint it.
3161 painter.setBrush( TQBrush( penColor ) );
3162 painter.setPen( TQt::NoPen );
3163 painter.drawPolygon( point );
3164 }
3165}
3166
3167
3168// Paint the real contents of a cell - the text.
3169//
3170void Cell::paintText( TQPainter& painter,
3171 const KoRect &cellRect,
3172 const TQPoint &cellRef )
3173{
3174 Doc *doc = sheet()->doc();
3175
3176 ColumnFormat *colFormat = format()->sheet()->columnFormat( cellRef.x() );
3177
3178 TQColorGroup defaultColorGroup = TQApplication::palette().active();
3179 TQColor textColorPrint = effTextColor( cellRef.x(), cellRef.y() );
3180
3181 // Resolve the text color if invalid (=default).
3182 if ( !textColorPrint.isValid() ) {
3183 if ( painter.device()->isExtDev() )
3184 textColorPrint = TQt::black;
3185 else
3186 textColorPrint = TQApplication::palette().active().text();
3187 }
3188
3189 TQPen tmpPen( textColorPrint );
3190
3191 // Set the font according to the current zoom.
3192 applyZoomedFont( painter, cellRef.x(), cellRef.y() );
3193
3194 // Check for red font color for negative values.
3195 if ( !d->hasExtra()
3196 || !d->extra()->conditions
3197 || !d->extra()->conditions->matchedStyle() ) {
3198 if ( value().isNumber()
3199 && !( format()->sheet()->getShowFormula()
3200 && !( format()->sheet()->isProtected()
3201 && format()->isHideFormula( d->column, d->row ) ) ) )
3202 {
3203 double v = value().asFloat();
3204 if ( format()->floatColor( cellRef.x(), cellRef.y()) == Format::NegRed
3205 && v < 0.0 )
3206 tmpPen.setColor( TQt::red );
3207 }
3208 }
3209
3210 // Check for blue color, for hyperlink.
3211 if ( !link().isEmpty() ) {
3212 tmpPen.setColor( TQApplication::palette().active().link() );
3213 TQFont f = painter.font();
3214 f.setUnderline( true );
3215 painter.setFont( f );
3216 }
3217
3218#if 0
3219/****
3220
3221 For now I am commenting this out -- with the default color display you
3222 can read normal text through a highlighted background. Maybe this isn't
3223 always the case, though, and we can put the highlighted text color back in.
3224 In that case, we need to somewhere in here figure out if the text overlaps
3225 another cell outside of the selection, otherwise that portion of the text
3226 will be printed white on white. So just that portion would need to be
3227 painted again in the normal color.
3228
3229 This should probably be done eventually, anyway, because I like using the
3230 reverse text color for highlighted cells. I just don't like extending the
3231 cell 'highlight' background outside of the selection rectangle because it
3232 looks REALLY ugly.
3233*/
3234
3235 if ( selected && ( cellRef.x() != marker.x() || cellRef.y() != marker.y() ) )
3236 {
3237 TQPen p( tmpPen );
3238 p.setColor( defaultColorGroup.highlightedText() );
3239 painter.setPen( p );
3240 }
3241 else {
3242 painter.setPen(tmpPen);
3243 }
3244#endif
3245 painter.setPen( tmpPen );
3246
3247 TQString tmpText = d->strOutText;
3248 double tmpHeight = d->textHeight;
3249 double tmpWidth = d->textWidth;
3250
3251 // If the cell is to narrow to paint the whole contents, then pick
3252 // out a part of the content that we paint. The result of this is
3253 // dependent on the data type of the content.
3254 //
3255 // FIXME: Make this dependent on the height as well.
3256 //
3257 if ( testFlag( Flag_CellTooShortX ) ) {
3258 d->strOutText = textDisplaying( painter );
3259
3260 // Recalculate the text width and the offset.
3261 textSize( painter );
3262 offsetAlign( column(), row() );
3263 }
3264
3265 // Hide zero.
3266 if ( format()->sheet()->getHideZero()
3267 && value().isNumber()
3268 && value().asFloat() == 0 ) {
3269 d->strOutText = TQString();
3270 }
3271
3272 // Clear extra cell if column or row is hidden
3273 //
3274 // FIXME: I think this should be done before the call to
3275 // textDisplaying() above.
3276 //
3277 if ( colFormat->isHide() || ( cellRect.height() <= 2 ) ) {
3278 freeAllObscuredCells(); /* TODO: This looks dangerous...must check when I
3279 have time */
3280 d->strOutText = "";
3281 }
3282
3283 double indent = 0.0;
3284 double offsetCellTooShort = 0.0;
3285 int a = effAlignX();
3286
3287 // Apply indent if text is align to left not when text is at right or middle.
3288 if ( a == Format::Left && !isEmpty() ) {
3289 // FIXME: The following condition should be remade into a call to
3290 // a new convenience function:
3291 // if ( hasConditionStyleFeature( Style::SIndent, true )...
3292 // This should be done throughout the entire file.
3293 //
3294 if ( d->hasExtra()
3295 && d->extra()->conditions
3296 && d->extra()->conditions->matchedStyle()
3297 && d->extra()->conditions->matchedStyle()->hasFeature( Style::SIndent, true ) )
3298 indent = d->extra()->conditions->matchedStyle()->indent();
3299 else
3300 indent = format()->getIndent( column(), row() );
3301 }
3302
3303 // Made an offset, otherwise ### is under red triangle.
3304 if ( a == Format::Right && !isEmpty() && testFlag( Flag_CellTooShortX ) )
3305 offsetCellTooShort = format()->sheet()->doc()->unzoomItX( 4 );
3306
3307 TQFontMetrics fm2 = painter.fontMetrics();
3308 double offsetFont = 0.0;
3309
3310 if ( format()->alignY( column(), row() ) == Format::Bottom
3311 && format()->textFontUnderline( column(), row() ) )
3312 offsetFont = format()->sheet()->doc()->unzoomItX( fm2.underlinePos() + 1 );
3313
3314 int tmpAngle;
3315 bool tmpMultiRow;
3316 bool tmpVerticalText;
3317
3318 // Check for angled or vertical text.
3319 if ( d->hasExtra()
3320 && d->extra()->conditions
3321 && d->extra()->conditions->matchedStyle() )
3322 {
3323 Style *matchedStyle = d->extra()->conditions->matchedStyle();
3324
3325 if ( matchedStyle->hasFeature( Style::SAngle, true ) )
3326 tmpAngle = d->extra()->conditions->matchedStyle()->rotateAngle();
3327 else
3328 tmpAngle = format()->getAngle( cellRef.x(), cellRef.y() );
3329
3330 if ( matchedStyle->hasFeature( Style::SVerticalText, true ) )
3331 tmpVerticalText = matchedStyle->hasProperty( Style::PVerticalText );
3332 else
3333 tmpVerticalText = format()->verticalText( cellRef.x(), cellRef.y() );
3334
3335 if ( matchedStyle->hasFeature( Style::SMultiRow, true ) )
3336 tmpMultiRow = matchedStyle->hasProperty( Style::PMultiRow );
3337 else
3338 tmpMultiRow = format()->multiRow( cellRef.x(), cellRef.y() );
3339 }
3340 else {
3341 tmpAngle = format()->getAngle( cellRef.x(), cellRef.y() );
3342 tmpVerticalText = format()->verticalText( cellRef.x(), cellRef.y() );
3343 tmpMultiRow = format()->multiRow( cellRef.x(), cellRef.y() );
3344 }
3345
3346 // Actually paint the text.
3347 // There are 4 possible cases:
3348 // - One line of text , horizontal
3349 // - Angled text
3350 // - Multiple rows of text , horizontal
3351 // - Vertical text
3352 if ( !tmpMultiRow && !tmpVerticalText && !tmpAngle ) {
3353 // Case 1: The simple case, one line, no angle.
3354
3355 painter.drawText( doc->zoomItX( indent + cellRect.x() + d->textX - offsetCellTooShort ),
3356 doc->zoomItY( cellRect.y() + d->textY - offsetFont ), d->strOutText );
3357 }
3358 else if ( tmpAngle != 0 ) {
3359 // Case 2: an angle.
3360
3361 int angle = tmpAngle;
3362 TQFontMetrics fm = painter.fontMetrics();
3363
3364 painter.rotate( angle );
3365 double x;
3366 if ( angle > 0 )
3367 x = indent + cellRect.x() + d->textX;
3368 else
3369 x = indent + cellRect.x() + d->textX
3370 - doc->unzoomItX((int) (( fm.descent() + fm.ascent() ) * sin( angle * M_PI / 180 )));
3371 double y;
3372 if ( angle > 0 )
3373 y = cellRect.y() + d->textY;
3374 else
3375 y = cellRect.y() + d->textY + d->textHeight;
3376 painter.drawText( doc->zoomItX( x * cos( angle * M_PI / 180 ) +
3377 y * sin( angle * M_PI / 180 ) ),
3378 doc->zoomItY( -x * sin( angle * M_PI / 180 ) +
3379 y * cos( angle * M_PI / 180 ) ),
3380 d->strOutText );
3381 painter.rotate( -angle );
3382 }
3383 else if ( tmpMultiRow && !tmpVerticalText ) {
3384 // Case 3: Multiple rows, but horizontal.
3385
3386 TQString t;
3387 int i;
3388 int pos = 0;
3389 double dy = 0.0;
3390 TQFontMetrics fm = painter.fontMetrics();
3391 do {
3392 i = d->strOutText.find( "\n", pos );
3393 if ( i == -1 )
3394 t = d->strOutText.mid( pos, d->strOutText.length() - pos );
3395 else {
3396 t = d->strOutText.mid( pos, i - pos );
3397 pos = i + 1;
3398 }
3399
3400 int align = effAlignX();
3401 if ( format()->sheet()->getShowFormula()
3402 && !( format()->sheet()->isProtected()
3403 && format()->isHideFormula( d->column, d->row ) ) )
3404 align = Format::Left;
3405
3406 // #### Torben: This looks duplicated for me
3407 switch ( align ) {
3408 case Format::Left:
3409 d->textX = effLeftBorderPen( cellRef.x(), cellRef.y() ).width() + BORDER_SPACE;
3410 break;
3411
3412 case Format::Right:
3413 d->textX = cellRect.width() - BORDER_SPACE - doc->unzoomItX( fm.width( t ) )
3414 - effRightBorderPen( cellRef.x(), cellRef.y() ).width();
3415 break;
3416
3417 case Format::Center:
3418 d->textX = ( cellRect.width() - doc->unzoomItX( fm.width( t ) ) ) / 2;
3419 }
3420
3421 painter.drawText( doc->zoomItX( indent + cellRect.x() + d->textX ),
3422 doc->zoomItY( cellRect.y() + d->textY + dy ), t );
3423 dy += doc->unzoomItY( fm.descent() + fm.ascent() );
3424 } while ( i != -1 );
3425 }
3426 else if ( tmpVerticalText && !d->strOutText.isEmpty() ) {
3427 // Case 4: Vertical text.
3428
3429 TQString t;
3430 int i = 0;
3431 int len = 0;
3432 double dy = 0.0;
3433 TQFontMetrics fm = painter.fontMetrics();
3434 do {
3435 len = d->strOutText.length();
3436 t = d->strOutText.at( i );
3437 painter.drawText( doc->zoomItX( indent + cellRect.x() + d->textX ),
3438 doc->zoomItY( cellRect.y() + d->textY + dy ), t );
3439 dy += doc->unzoomItY( fm.descent() + fm.ascent() );
3440 i++;
3441 } while ( i != len );
3442 }
3443
3444 // Check for too short cell and set the outText for future reference.
3445 if ( testFlag( Flag_CellTooShortX ) ) {
3446 d->strOutText = tmpText;
3447 d->textHeight = tmpHeight;
3448 d->textWidth = tmpWidth;
3449 }
3450
3451 if ( format()->sheet()->getHideZero() && value().isNumber()
3452 && value().asFloat() == 0 )
3453 d->strOutText = tmpText;
3454
3455 if ( colFormat->isHide() || ( cellRect.height() <= 2 ) )
3456 d->strOutText = tmpText;
3457}
3458
3459
3460// Paint page borders on the page. Only do this on the screen.
3461//
3462void Cell::paintPageBorders( TQPainter& painter,
3463 const KoRect &cellRect,
3464 const TQPoint &cellRef,
3465 bool paintBorderRight,
3466 bool paintBorderBottom )
3467{
3468 // Not screen? Return immediately.
3469 if ( painter.device()->isExtDev() )
3470 return;
3471
3472 if ( ! format()->sheet()->isShowPageBorders() )
3473 return;
3474
3475 SheetPrint* print = format()->sheet()->print();
3476
3477 Sheet::LayoutDirection sheetDir = format()->sheet()->layoutDirection();
3478
3479 Doc* doc = sheet()->doc();
3480 int zcellRect_left = doc->zoomItX (cellRect.left());
3481 int zcellRect_right = doc->zoomItX (cellRect.right());
3482 int zcellRect_top = doc->zoomItY (cellRect.top());
3483 int zcellRect_bottom = doc->zoomItY (cellRect.bottom());
3484
3485 // Draw page borders
3486
3487 if ( cellRef.x() >= print->printRange().left()
3488 && cellRef.x() <= print->printRange().right() + 1
3489 && cellRef.y() >= print->printRange().top()
3490 && cellRef.y() <= print->printRange().bottom() + 1 )
3491 {
3492 if ( print->isOnNewPageX( cellRef.x() )
3493 && cellRef.y() <= print->printRange().bottom() )
3494 {
3495 painter.setPen( sheet()->doc()->pageBorderColor() );
3496
3497 if ( sheetDir == Sheet::RightToLeft )
3498 painter.drawLine( zcellRect_right, zcellRect_top,
3499 zcellRect_right, zcellRect_bottom );
3500 else
3501 painter.drawLine( zcellRect_left, zcellRect_top,
3502 zcellRect_left, zcellRect_bottom );
3503 }
3504
3505 if ( print->isOnNewPageY( cellRef.y() ) &&
3506 ( cellRef.x() <= print->printRange().right() ) )
3507 {
3508 painter.setPen( sheet()->doc()->pageBorderColor() );
3509 painter.drawLine( zcellRect_left, zcellRect_top,
3510 zcellRect_right, zcellRect_top );
3511 }
3512
3513 if ( paintBorderRight ) {
3514 if ( print->isOnNewPageX( cellRef.x() + 1 )
3515 && cellRef.y() <= print->printRange().bottom() ) {
3516 painter.setPen( sheet()->doc()->pageBorderColor() );
3517
3518 if ( sheetDir == Sheet::RightToLeft )
3519 painter.drawLine( zcellRect_left, zcellRect_top,
3520 zcellRect_left, zcellRect_bottom );
3521 else
3522 painter.drawLine( zcellRect_right, zcellRect_top,
3523 zcellRect_right, zcellRect_bottom );
3524 }
3525 }
3526
3527 if ( paintBorderBottom ) {
3528 if ( print->isOnNewPageY( cellRef.y() + 1 )
3529 && cellRef.x() <= print->printRange().right() ) {
3530 painter.setPen( sheet()->doc()->pageBorderColor() );
3531 painter.drawLine( zcellRect_left, zcellRect_bottom,
3532 zcellRect_right, zcellRect_bottom );
3533 }
3534 }
3535 }
3536}
3537
3538
3539// Paint the cell borders.
3540//
3541void Cell::paintCellBorders( TQPainter& painter, const KoRect& rect,
3542 const KoRect &cellRect,
3543 const TQPoint &cellRef,
3544 bool paintRight, bool paintBottom,
3545 bool paintLeft, bool paintTop,
3546 TQPen & _rightPen, TQPen & _bottomPen,
3547 TQPen & _leftPen, TQPen & _topPen )
3548{
3549
3550 //Sanity check: If we are not painting any of the borders then the function
3551 //really shouldn't be called at all.
3552 if ( (!paintLeft) && (!paintRight) && (!paintTop) && (!paintBottom) )
3553 return;
3554
3555 Doc * doc = sheet()->doc();
3556
3557 Sheet::LayoutDirection sheetDir = format()->sheet()->layoutDirection();
3558
3559 // compute zoomed rectangles
3560 // I don't use KoRect, because that ends up producing lots of warnings
3561 // about double->int conversions in calls to painter.drawLine
3562 int zrect_left (doc->zoomItX (rect.left()));
3563 int zrect_right (doc->zoomItX (rect.right()));
3564 int zrect_top (doc->zoomItY (rect.top()));
3565 int zrect_bottom (doc->zoomItY (rect.bottom()));
3566 int zcellRect_left (doc->zoomItX (cellRect.left()));
3567 int zcellRect_right (doc->zoomItX (cellRect.right()));
3568 int zcellRect_top (doc->zoomItY (cellRect.top()));
3569 int zcellRect_bottom (doc->zoomItY (cellRect.bottom()));
3570
3571 /* we might not paint some borders if this cell is merged with another in
3572 that direction
3573 bool paintLeft = paintBorderLeft;
3574 bool paintRight = paintBorderRight;
3575 bool paintTop = paintBorderTop;
3576 bool paintBottom = paintBorderBottom;
3577 */
3578
3579 // paintRight = paintRight && ( extraXCells() == 0 );
3580 // paintBottom = paintBottom && ( d->extra()->extraYCells() == 0 );
3581
3582 if (d->hasExtra()) {
3583 TQValueList<Cell*>::const_iterator it = d->extra()->obscuringCells.begin();
3584 TQValueList<Cell*>::const_iterator end = d->extra()->obscuringCells.end();
3585 for ( ; it != end; ++it ) {
3586 Cell* cell = *it;
3587
3588 int xDiff = cellRef.x() - cell->column();
3589 int yDiff = cellRef.y() - cell->row();
3590 paintLeft = paintLeft && xDiff == 0;
3591 paintTop = paintTop && yDiff == 0;
3592
3593 // Paint the border(s) if either this one should or if we have a
3594 // merged cell with this cell as its border.
3595 paintRight = paintRight && cell->mergedXCells() == xDiff;
3596 paintBottom = paintBottom && cell->mergedYCells() == yDiff;
3597 }
3598 }
3599
3600 // Must create copies of these since otherwise the zoomIt()
3601 // operation will be performed on them repeatedly.
3602 TQPen leftPen( _leftPen );
3603 TQPen rightPen( _rightPen );
3604 TQPen topPen( _topPen );
3605 TQPen bottomPen( _bottomPen );
3606
3607 // Determine the pens that should be used for drawing
3608 // the borders.
3609 //
3610 int left_penWidth = TQMAX( 1, doc->zoomItX( leftPen.width() ) );
3611 int right_penWidth = TQMAX( 1, doc->zoomItX( rightPen.width() ) );
3612 int top_penWidth = TQMAX( 1, doc->zoomItY( topPen.width() ) );
3613 int bottom_penWidth = TQMAX( 1, doc->zoomItY( bottomPen.width() ) );
3614
3615 leftPen.setWidth( left_penWidth );
3616 rightPen.setWidth( right_penWidth );
3617 topPen.setWidth( top_penWidth );
3618 bottomPen.setWidth( bottom_penWidth );
3619
3620 if ( paintLeft && leftPen.style() != TQt::NoPen ) {
3621 int top = ( TQMAX( 0, -1 + top_penWidth ) ) / 2 +
3622 ( ( TQMAX( 0, -1 + top_penWidth ) ) % 2 );
3623 int bottom = ( TQMAX( 0, -1 + bottom_penWidth ) ) / 2 + 1;
3624
3625 painter.setPen( leftPen );
3626
3627 //kdDebug(36001) << " painting left border of cell " << name() << endl;
3628
3629 // If we are on paper printout, we limit the length of the lines.
3630 // On paper, we always have full cells, on screen not.
3631 if ( painter.device()->isExtDev() ) {
3632 // FIXME: There is probably Cut&Paste bugs here as well as below.
3633 // The TQMIN/TQMAX and left/right pairs don't really make sense.
3634 //
3635 // UPDATE: In fact, most of these TQMIN/TQMAX combinations
3636 // are TOTALLY BOGUS. For one thing, the idea
3637 // that we always have full cells on paper is wrong
3638 // since we can have embedded sheets in e.g. kword,
3639 // and those can be arbitrarily clipped. WE HAVE TO
3640 // REVISE THIS WHOLE BORDER PAINTING SECTION!
3641 //
3642 if ( sheetDir == Sheet::RightToLeft )
3643 painter.drawLine( TQMIN( zrect_right, zcellRect_right ),
3644 TQMAX( zrect_top, zcellRect_top - top ),
3645 TQMIN( zrect_right, zcellRect_right ),
3646 TQMIN( zrect_bottom, zcellRect_bottom + bottom ) );
3647 else
3648 painter.drawLine( TQMAX( zrect_left, zcellRect_left ),
3649 TQMAX( zrect_top, zcellRect_top - top ),
3650 TQMAX( zrect_left, zcellRect_left ),
3651 TQMIN( zrect_bottom, zcellRect_bottom + bottom ) );
3652 }
3653 else {
3654 if ( sheetDir == Sheet::RightToLeft )
3655 painter.drawLine( zcellRect_right,
3656 zcellRect_top - top,
3657 zcellRect_right,
3658 zcellRect_bottom + bottom );
3659 else
3660 painter.drawLine( zcellRect_left,
3661 zcellRect_top - top,
3662 zcellRect_left,
3663 zcellRect_bottom + bottom );
3664 }
3665 }
3666
3667 if ( paintRight && rightPen.style() != TQt::NoPen ) {
3668 int top = ( TQMAX( 0, -1 + top_penWidth ) ) / 2 +
3669 ( ( TQMAX( 0, -1 + top_penWidth ) ) % 2 );
3670 int bottom = ( TQMAX( 0, -1 + bottom_penWidth ) ) / 2 + 1;
3671
3672 painter.setPen( rightPen );
3673
3674 //kdDebug(36001) << " painting right border of cell " << name() << endl;
3675
3676 // If we are on paper printout, we limit the length of the lines.
3677 // On paper, we always have full cells, on screen not.
3678 if ( painter.device()->isExtDev() ) {
3679 if ( sheetDir == Sheet::RightToLeft )
3680 painter.drawLine( TQMAX( zrect_left, zcellRect_left ),
3681 TQMAX( zrect_top, zcellRect_top - top ),
3682 TQMAX( zrect_left, zcellRect_left ),
3683 TQMIN( zrect_bottom, zcellRect_bottom + bottom ) );
3684 else {
3685 // FIXME: This is the way all these things should look.
3686 // Make it so.
3687 //
3688 // Only print the right border if it is visible.
3689 if ( zcellRect_right <= zrect_right + right_penWidth / 2)
3690 painter.drawLine( zcellRect_right,
3691 TQMAX( zrect_top, zcellRect_top - top ),
3692 zcellRect_right,
3693 TQMIN( zrect_bottom, zcellRect_bottom + bottom ) );
3694 }
3695 }
3696 else {
3697 if ( sheetDir == Sheet::RightToLeft )
3698 painter.drawLine( zcellRect_left,
3699 zcellRect_top - top,
3700 zcellRect_left,
3701 zcellRect_bottom + bottom );
3702 else
3703 painter.drawLine( zcellRect_right,
3704 zcellRect_top - top,
3705 zcellRect_right,
3706 zcellRect_bottom + bottom );
3707 }
3708 }
3709
3710 if ( paintTop && topPen.style() != TQt::NoPen ) {
3711 painter.setPen( topPen );
3712
3713 //kdDebug(36001) << " painting top border of cell " << name()
3714 // << " [" << zcellRect_left << "," << zcellRect_right
3715 // << ": " << zcellRect_right - zcellRect_left << "]" << endl;
3716
3717 // If we are on paper printout, we limit the length of the lines.
3718 // On paper, we always have full cells, on screen not.
3719 if ( painter.device()->isExtDev() ) {
3720 if ( zcellRect_top >= zrect_top + top_penWidth / 2)
3721 painter.drawLine( TQMAX( zrect_left, zcellRect_left ),
3722 zcellRect_top,
3723 TQMIN( zrect_right, zcellRect_right ),
3724 zcellRect_top );
3725 }
3726 else {
3727 painter.drawLine( zcellRect_left, zcellRect_top,
3728 zcellRect_right, zcellRect_top );
3729 }
3730 }
3731
3732 if ( paintBottom && bottomPen.style() != TQt::NoPen ) {
3733 painter.setPen( bottomPen );
3734
3735 //kdDebug(36001) << " painting bottom border of cell " << name()
3736 // << " [" << zcellRect_left << "," << zcellRect_right
3737 // << ": " << zcellRect_right - zcellRect_left << "]" << endl;
3738
3739 // If we are on paper printout, we limit the length of the lines.
3740 // On paper, we always have full cells, on screen not.
3741 if ( painter.device()->isExtDev() ) {
3742 if ( zcellRect_bottom <= zrect_bottom + bottom_penWidth / 2)
3743 painter.drawLine( TQMAX( zrect_left, zcellRect_left ),
3744 zcellRect_bottom,
3745 TQMIN( zrect_right, zcellRect_right ),
3746 zcellRect_bottom );
3747 }
3748 else {
3749 painter.drawLine( zcellRect_left, zcellRect_bottom,
3750 zcellRect_right, zcellRect_bottom );
3751 }
3752 }
3753
3754 // FIXME: Look very closely at when the following code is really needed.
3755 // I can't really see any case, but I might be wrong.
3756 // Since the code below is buggy, and incredibly complex,
3757 // I am currently disabling it. If somebody wants to enable
3758 // it again, then please also solve bug 68977: "Embedded KSpread
3759 // document printing problem" at the same time.
3760 return;
3761
3762#if 0
3763 // Look at the cells on our corners. It may happen that we
3764 // just erased parts of their borders corner, so we might need
3765 // to repaint these corners.
3766 //
3767 TQPen vert_pen, horz_pen;
3768 int vert_penWidth, horz_penWidth;
3769
3770 // Some useful referenses.
3771 Cell *cell_north = format()->sheet()->cellAt( cellRef.x(),
3772 cellRef.y() - 1 );
3773 Cell *cell_northwest = format()->sheet()->cellAt( cellRef.x() - 1,
3774 cellRef.y() - 1 );
3775 Cell *cell_west = format()->sheet()->cellAt( cellRef.x() - 1,
3776 cellRef.y() );
3777 Cell *cell_northeast = format()->sheet()->cellAt( cellRef.x() + 1,
3778 cellRef.y() - 1 );
3779 Cell *cell_east = format()->sheet()->cellAt( cellRef.x() + 1,
3780 cellRef.y() );
3781 Cell *cell_south = format()->sheet()->cellAt( cellRef.x(),
3782 cellRef.y() + 1 );
3783 Cell *cell_southwest = format()->sheet()->cellAt( cellRef.x() - 1,
3784 cellRef.y() + 1 );
3785 Cell *cell_southeast = format()->sheet()->cellAt( cellRef.x() + 1,
3786 cellRef.y() + 1 );
3787
3788 // Fix the borders which meet at the top left corner
3789 if ( cell_north->effLeftBorderValue( cellRef.x(), cellRef.y() - 1 )
3790 >= cell_northwest->effRightBorderValue( cellRef.x() - 1, cellRef.y() - 1 ) )
3791 vert_pen = cell_north->effLeftBorderPen( cellRef.x(), cellRef.y() - 1 );
3792 else
3793 vert_pen = cell_northwest->effRightBorderPen( cellRef.x() - 1,
3794 cellRef.y() - 1 );
3795
3796 vert_penWidth = TQMAX( 1, doc->zoomItX( vert_pen.width() ) );
3797 vert_pen.setWidth( vert_penWidth );
3798
3799 if ( vert_pen.style() != TQt::NoPen ) {
3800 if ( cell_west->effTopBorderValue( cellRef.x() - 1, cellRef.y() )
3801 >= cell_northwest->effBottomBorderValue( cellRef.x() - 1, cellRef.y() - 1 ) )
3802 horz_pen = cell_west->effTopBorderPen( cellRef.x() - 1, cellRef.y() );
3803 else
3804 horz_pen = cell_northwest->effBottomBorderPen( cellRef.x() - 1,
3805 cellRef.y() - 1 );
3806
3807 horz_penWidth = TQMAX( 1, doc->zoomItY( horz_pen.width() ) );
3808 int bottom = ( TQMAX( 0, -1 + horz_penWidth ) ) / 2 + 1;
3809
3810 painter.setPen( vert_pen );
3811 // If we are on paper printout, we limit the length of the lines.
3812 // On paper, we always have full cells, on screen not.
3813 if ( painter.device()->isExtDev() ) {
3814 if ( sheetDir == Sheet::RightToLeft )
3815 painter.drawLine( TQMAX( zrect_left, zcellRect_right ),
3816 TQMAX( zrect_top, zcellRect_top ),
3817 TQMIN( zrect_right, zcellRect_right ),
3818 TQMIN( zrect_bottom, zcellRect_top + bottom ) );
3819 else
3820 painter.drawLine( TQMAX( zrect_left, zcellRect_left ),
3821 TQMAX( zrect_top, zcellRect_top ),
3822 TQMIN( zrect_right, zcellRect_left ),
3823 TQMIN( zrect_bottom, zcellRect_top + bottom ) );
3824 }
3825 else {
3826 if ( sheetDir == Sheet::RightToLeft )
3827 painter.drawLine( zcellRect_right, zcellRect_top,
3828 zcellRect_right, zcellRect_top + bottom );
3829 else
3830 painter.drawLine( zcellRect_left, zcellRect_top,
3831 zcellRect_left, zcellRect_top + bottom );
3832 }
3833 }
3834
3835 // Fix the borders which meet at the top right corner
3836 if ( cell_north->effRightBorderValue( cellRef.x(), cellRef.y() - 1 )
3837 >= cell_northeast->effLeftBorderValue( cellRef.x() + 1,
3838 cellRef.y() - 1 ) )
3839 vert_pen = cell_north->effRightBorderPen( cellRef.x(), cellRef.y() - 1 );
3840 else
3841 vert_pen = cell_northeast->effLeftBorderPen( cellRef.x() + 1,
3842 cellRef.y() - 1 );
3843
3844 // vert_pen = effRightBorderPen( cellRef.x(), cellRef.y() - 1 );
3845 vert_penWidth = TQMAX( 1, doc->zoomItX( vert_pen.width() ) );
3846 vert_pen.setWidth( vert_penWidth );
3847 if ( ( vert_pen.style() != TQt::NoPen ) && ( cellRef.x() < KS_colMax ) ) {
3848 if ( cell_east->effTopBorderValue( cellRef.x() + 1, cellRef.y() )
3849 >= cell_northeast->effBottomBorderValue( cellRef.x() + 1,
3850 cellRef.y() - 1 ) )
3851 horz_pen = cell_east->effTopBorderPen( cellRef.x() + 1, cellRef.y() );
3852 else
3853 horz_pen = cell_northeast->effBottomBorderPen( cellRef.x() + 1,
3854 cellRef.y() - 1 );
3855
3856 // horz_pen = effTopBorderPen( cellRef.x() + 1, cellRef.y() );
3857 horz_penWidth = TQMAX( 1, doc->zoomItY( horz_pen.width() ) );
3858 int bottom = ( TQMAX( 0, -1 + horz_penWidth ) ) / 2 + 1;
3859
3860 painter.setPen( vert_pen );
3861 //If we are on paper printout, we limit the length of the lines
3862 //On paper, we always have full cells, on screen not
3863 if ( painter.device()->isExtDev() ) {
3864 if ( sheetDir == Sheet::RightToLeft )
3865 painter.drawLine( TQMAX( zrect_left, zcellRect_left ),
3866 TQMAX( zrect_top, zcellRect_top ),
3867 TQMIN( zrect_right, zcellRect_left ),
3868 TQMIN( zrect_bottom, zcellRect_top + bottom ) );
3869 else
3870 painter.drawLine( TQMAX( zrect_left, zcellRect_right ),
3871 TQMAX( zrect_top, zcellRect_top ),
3872 TQMIN( zrect_right, zcellRect_right ),
3873 TQMIN( zrect_bottom, zcellRect_top + bottom ) );
3874 }
3875 else {
3876 if ( sheetDir == Sheet::RightToLeft )
3877 painter.drawLine( zcellRect_left, zcellRect_top,
3878 zcellRect_left, zcellRect_top + bottom );
3879 else
3880 painter.drawLine( zcellRect_right, zcellRect_top,
3881 zcellRect_right, zcellRect_top + bottom );
3882 }
3883 }
3884
3885 // Bottom
3886 if ( cellRef.y() < KS_rowMax ) {
3887 // Fix the borders which meet at the bottom left corner
3888 if ( cell_south->effLeftBorderValue( cellRef.x(), cellRef.y() + 1 )
3889 >= cell_southwest->effRightBorderValue( cellRef.x() - 1,
3890 cellRef.y() + 1 ) )
3891 vert_pen = cell_south->effLeftBorderPen( cellRef.x(), cellRef.y() + 1 );
3892 else
3893 vert_pen = cell_southwest->effRightBorderPen( cellRef.x() - 1,
3894 cellRef.y() + 1 );
3895
3896 // vert_pen = effLeftBorderPen( cellRef.x(), cellRef.y() + 1 );
3897 vert_penWidth = TQMAX( 1, doc->zoomItY( vert_pen.width() ) );
3898 vert_pen.setWidth( vert_penWidth );
3899 if ( vert_pen.style() != TQt::NoPen ) {
3900 if ( cell_west->effBottomBorderValue( cellRef.x() - 1, cellRef.y() )
3901 >= cell_southwest->effTopBorderValue( cellRef.x() - 1,
3902 cellRef.y() + 1 ) )
3903 horz_pen = cell_west->effBottomBorderPen( cellRef.x() - 1,
3904 cellRef.y() );
3905 else
3906 horz_pen = cell_southwest->effTopBorderPen( cellRef.x() - 1,
3907 cellRef.y() + 1 );
3908
3909 // horz_pen = effBottomBorderPen( cellRef.x() - 1, cellRef.y() );
3910 horz_penWidth = TQMAX( 1, doc->zoomItX( horz_pen.width() ) );
3911 int bottom = ( TQMAX( 0, -1 + horz_penWidth ) ) / 2;
3912
3913 painter.setPen( vert_pen );
3914 // If we are on paper printout, we limit the length of the lines.
3915 // On paper, we always have full cells, on screen not.
3916 if ( painter.device()->isExtDev() ) {
3917 if ( sheetDir == Sheet::RightToLeft )
3918 painter.drawLine( TQMAX( zrect_left, zcellRect_right ),
3919 TQMAX( zrect_top, zcellRect_bottom - bottom ),
3920 TQMIN( zrect_right, zcellRect_right ),
3921 TQMIN( zrect_bottom, zcellRect_bottom ) );
3922 else
3923 painter.drawLine( TQMAX( zrect_left, zcellRect_left ),
3924 TQMAX( zrect_top, zcellRect_bottom - bottom ),
3925 TQMIN( zrect_right, zcellRect_left ),
3926 TQMIN( zrect_bottom, zcellRect_bottom ) );
3927 }
3928 else {
3929 if ( sheetDir == Sheet::RightToLeft )
3930 painter.drawLine( zcellRect_right, zcellRect_bottom - bottom,
3931 zcellRect_right, zcellRect_bottom );
3932 else
3933 painter.drawLine( zcellRect_left, zcellRect_bottom - bottom,
3934 zcellRect_left, zcellRect_bottom );
3935 }
3936 }
3937
3938 // Fix the borders which meet at the bottom right corner
3939 if ( cell_south->effRightBorderValue( cellRef.x(), cellRef.y() + 1 )
3940 >= cell_southeast->effLeftBorderValue( cellRef.x() + 1,
3941 cellRef.y() + 1 ) )
3942 vert_pen = cell_south->effRightBorderPen( cellRef.x(), cellRef.y() + 1 );
3943 else
3944 vert_pen = cell_southeast->effLeftBorderPen( cellRef.x() + 1,
3945 cellRef.y() + 1 );
3946
3947 // vert_pen = effRightBorderPen( cellRef.x(), cellRef.y() + 1 );
3948 vert_penWidth = TQMAX( 1, doc->zoomItY( vert_pen.width() ) );
3949 vert_pen.setWidth( vert_penWidth );
3950 if ( ( vert_pen.style() != TQt::NoPen ) && ( cellRef.x() < KS_colMax ) ) {
3951 if ( cell_east ->effBottomBorderValue( cellRef.x() + 1, cellRef.y() )
3952 >= cell_southeast->effTopBorderValue( cellRef.x() + 1,
3953 cellRef.y() + 1 ) )
3954
3955 horz_pen = format()->sheet()->cellAt( cellRef.x() + 1, cellRef.y() )
3956 ->effBottomBorderPen( cellRef.x() + 1, cellRef.y() );
3957 else
3958 horz_pen = format()->sheet()->cellAt( cellRef.x() + 1, cellRef.y() + 1 )
3959 ->effTopBorderPen( cellRef.x() + 1, cellRef.y() + 1 );
3960
3961 // horz_pen = effBottomBorderPen( cellRef.x() + 1, cellRef.y() );
3962 horz_penWidth = TQMAX( 1, doc->zoomItX( horz_pen.width() ) );
3963 int bottom = ( TQMAX( 0, -1 + horz_penWidth ) ) / 2;
3964
3965 painter.setPen( vert_pen );
3966 // If we are on paper printout, we limit the length of the lines.
3967 // On paper, we always have full cells, on screen not.
3968 if ( painter.device()->isExtDev() ) {
3969 if ( sheetDir == Sheet::RightToLeft )
3970 painter.drawLine( TQMAX( zrect_left, zcellRect_left ),
3971 TQMAX( zrect_top, zcellRect_bottom - bottom ),
3972 TQMIN( zrect_right, zcellRect_left ),
3973 TQMIN( zrect_bottom, zcellRect_bottom ) );
3974 else
3975 painter.drawLine( TQMAX( zrect_left, zcellRect_right ),
3976 TQMAX( zrect_top, zcellRect_bottom - bottom ),
3977 TQMIN( zrect_right, zcellRect_right ),
3978 TQMIN( zrect_bottom, zcellRect_bottom ) );
3979 }
3980 else {
3981 if ( sheetDir == Sheet::RightToLeft )
3982 painter.drawLine( zcellRect_left, zcellRect_bottom - bottom,
3983 zcellRect_left, zcellRect_bottom );
3984 else
3985 painter.drawLine( zcellRect_right, zcellRect_bottom - bottom,
3986 zcellRect_right, zcellRect_bottom );
3987 }
3988 }
3989 }
3990 #endif
3991}
3992
3993
3994// Paint diagonal lines through the cell.
3995//
3996void Cell::paintCellDiagonalLines( TQPainter& painter,
3997 const KoRect &cellRect,
3998 const TQPoint &cellRef )
3999{
4000 if ( isPartOfMerged() )
4001 return;
4002
4003 Doc* doc = sheet()->doc();
4004
4005 if ( effFallDiagonalPen( cellRef.x(), cellRef.y() ).style() != TQt::NoPen ) {
4006 painter.setPen( effFallDiagonalPen( cellRef.x(), cellRef.y() ) );
4007 painter.drawLine( doc->zoomItX( cellRect.x() ),
4008 doc->zoomItY( cellRect.y() ),
4009 doc->zoomItX( cellRect.right() ),
4010 doc->zoomItY( cellRect.bottom() ) );
4011 }
4012
4013 if ( effGoUpDiagonalPen( cellRef.x(), cellRef.y() ).style() != TQt::NoPen ) {
4014 painter.setPen( effGoUpDiagonalPen( cellRef.x(), cellRef.y() ) );
4015 painter.drawLine( doc->zoomItX( cellRect.x() ),
4016 doc->zoomItY( cellRect.bottom() ),
4017 doc->zoomItX( cellRect.right() ),
4018 doc->zoomItY( cellRect.y() ) );
4019 }
4020}
4021
4022
4023// End of Painting
4024// ================================================================
4025
4026
4028{
4029 int a = format()->align( column(), row() );
4030 if ( a == Format::Undefined )
4031 {
4032 //numbers should be right-aligned by default, as well as BiDi text
4033 if ((formatType() == Text_format) || value().isString())
4034 a = (d->strOutText.isRightToLeft()) ?
4035 Format::Right : Format::Left;
4036 else {
4037 Value val = value();
4038 while (val.isArray()) val = val.element (0, 0);
4039 if (val.isBoolean() || val.isNumber())
4040 a = Format::Right;
4041 else
4042 a = Format::Left;
4043 }
4044 }
4045 return a;
4046}
4047
4048int Cell::effAlignX()
4049{
4050 if ( d->hasExtra() && d->extra()->conditions
4051 && d->extra()->conditions->matchedStyle()
4052 && d->extra()->conditions->matchedStyle()->hasFeature( Style::SAlignX, true ) )
4053 return d->extra()->conditions->matchedStyle()->alignX();
4054
4055 return defineAlignX();
4056}
4057
4058// Cut strOutText, so that it only holds the part that can be displayed.
4059//
4060// Used in paintText().
4061//
4062
4063TQString Cell::textDisplaying( TQPainter &_painter )
4064{
4065 TQFontMetrics fm = _painter.fontMetrics();
4066 int a = format()->align( column(), row() );
4067
4068 bool isNumeric = value().isNumber();
4069
4070 if ( !format()->verticalText( column(),row() ) ) {
4071 // Non-vertical text: the ordinary case.
4072
4073 // Not enough space but align to left
4074 double len = 0.0;
4075 int extraXCells = d->hasExtra() ? d->extra()->extraXCells : 0;
4076
4077 for ( int i = column(); i <= column() + extraXCells; i++ ) {
4078 ColumnFormat *cl2 = format()->sheet()->columnFormat( i );
4079 len += cl2->dblWidth() - 1.0; //-1.0 because the pixel in between 2 cells is shared between both cells
4080 }
4081
4082 TQString tmp;
4083 double tmpIndent = 0.0;
4084 if ( !isEmpty() )
4085 tmpIndent = format()->getIndent( column(), row() );
4086
4087 // Start out with the whole text, cut one character at a time, and
4088 // when the text finally fits, return it.
4089 for ( int i = d->strOutText.length(); i != 0; i-- )
4090 {
4091 //Note that numbers are always treated as left-aligned since if we have to cut digits off, they should
4092 //always be the least significant ones at the end of the string
4093 if ( a == Format::Left || a == Format::Undefined || isNumeric)
4094 tmp = d->strOutText.left(i);
4095 else if ( a == Format::Right)
4096 tmp = d->strOutText.right(i);
4097 else
4098 tmp = d->strOutText.mid( ( d->strOutText.length() - i ) / 2, i);
4099
4100 if (isNumeric)
4101 {
4102 //For numeric values, we can cut off digits after the decimal point to make it fit,
4103 //but not the integer part of the number.
4104 //If this number still contains a fraction part then we don't need to do anything, if we have run
4105 //out of space to fit even the integer part of the number then display #########
4106 //TODO Perhaps try to display integer part in standard form if there is not enough room for it?
4107
4108 if (!tmp.contains('.'))
4109 d->strOutText=TQString().fill('#',20);
4110 }
4111
4112 // 4 equal length of red triangle +1 point.
4113 if ( format()->sheet()->doc()->unzoomItX( fm.width( tmp ) ) + tmpIndent
4114 < len - 4.0 - 1.0 )
4115 {
4116 if ( format()->getAngle( column(), row() ) != 0 )
4117 {
4118 TQString tmp2;
4119 RowFormat *rl = format()->sheet()->rowFormat( row() );
4120 if ( d->textHeight > rl->dblHeight() )
4121 {
4122 for ( int j = d->strOutText.length(); j != 0; j-- )
4123 {
4124 tmp2 = d->strOutText.left( j );
4125 if ( format()->sheet()->doc()->unzoomItY( fm.width( tmp2 ) ) < rl->dblHeight() - 1.0 )
4126 {
4127 return d->strOutText.left( TQMIN( tmp.length(), tmp2.length() ) );
4128 }
4129 }
4130 }
4131 else
4132 return tmp;
4133
4134 }
4135 else
4136 return tmp;
4137 }
4138 }
4139 return TQString( "" );
4140 }
4141 else if ( format()->verticalText( column(), row() ) ) {
4142 // Vertical text.
4143
4144 RowFormat *rl = format()->sheet()->rowFormat( row() );
4145 double tmpIndent = 0.0;
4146
4147 // Not enough space but align to left.
4148 double len = 0.0;
4149 int extraXCells = d->hasExtra() ? d->extra()->extraXCells : 0;
4150
4151 for ( int i = column(); i <= column() + extraXCells; i++ ) {
4152 ColumnFormat *cl2 = format()->sheet()->columnFormat( i );
4153
4154 // -1.0 because the pixel in between 2 cells is shared between both cells
4155 len += cl2->dblWidth() - 1.0;
4156 }
4157
4158 if ( !isEmpty() )
4159 tmpIndent = format()->getIndent( column(), row() );
4160
4161 if ( ( d->textWidth + tmpIndent > len ) || d->textWidth == 0.0 )
4162 return TQString( "" );
4163
4164 for ( int i = d->strOutText.length(); i != 0; i-- ) {
4165 if ( format()->sheet()->doc()->unzoomItY( fm.ascent() + fm.descent() ) * i
4166 < rl->dblHeight() - 1.0 )
4167 return d->strOutText.left( i );
4168 }
4169
4170 return TQString( "" );
4171 }
4172
4173 ColumnFormat *cl = format()->sheet()->columnFormat( column() );
4174 double w = cl->dblWidth();
4175
4176 if ( d->hasExtra() && (d->extra()->extraWidth != 0.0) )
4177 w = d->extra()->extraWidth;
4178
4179 TQString tmp;
4180 for ( int i = d->strOutText.length(); i != 0; i-- ) {
4181 tmp = d->strOutText.left( i );
4182
4183 // 4 equals length of red triangle +1 pixel
4184 if ( format()->sheet()->doc()->unzoomItX( fm.width( tmp ) ) < w - 4.0 - 1.0 )
4185 return tmp;
4186 }
4187
4188 return TQString();
4189}
4190
4191
4192double Cell::dblWidth( int _col, const Canvas *_canvas ) const
4193{
4194 if ( _col < 0 )
4195 _col = d->column;
4196
4197 if ( _canvas )
4198 {
4199 if ( testFlag(Flag_Merged) )
4200 return d->extra()->extraWidth;
4201
4202 const ColumnFormat *cl = format()->sheet()->columnFormat( _col );
4203 return cl->dblWidth( _canvas );
4204 }
4205
4206 if ( testFlag(Flag_Merged) )
4207 return d->extra()->extraWidth;
4208
4209 const ColumnFormat *cl = format()->sheet()->columnFormat( _col );
4210 return cl->dblWidth();
4211}
4212
4213int Cell::width( int _col, const Canvas *_canvas ) const
4214{
4215 return int( dblWidth( _col, _canvas ) );
4216}
4217
4218double Cell::dblHeight( int _row, const Canvas *_canvas ) const
4219{
4220 if ( _row < 0 )
4221 _row = d->row;
4222
4223 if ( _canvas )
4224 {
4225 if ( testFlag(Flag_Merged) )
4226 return d->extra()->extraHeight;
4227
4228 const RowFormat *rl = format()->sheet()->rowFormat( _row );
4229 return rl->dblHeight( _canvas );
4230 }
4231
4232 if ( testFlag(Flag_Merged) )
4233 return d->extra()->extraHeight;
4234
4235 const RowFormat *rl = format()->sheet()->rowFormat( _row );
4236 return rl->dblHeight();
4237}
4238
4239int Cell::height( int _row, const Canvas *_canvas ) const
4240{
4241 return int( dblHeight( _row, _canvas ) );
4242}
4243
4245//
4246// Misc Properties.
4247// Reimplementation of Format methods.
4248//
4250
4251const TQBrush& Cell::backGroundBrush( int _col, int _row ) const
4252{
4253 if ( d->hasExtra() && (!d->extra()->obscuringCells.isEmpty()) )
4254 {
4255 const Cell* cell = d->extra()->obscuringCells.first();
4256 return cell->backGroundBrush( cell->column(), cell->row() );
4257 }
4258
4259 return format()->backGroundBrush( _col, _row );
4260}
4261
4262const TQColor& Cell::bgColor( int _col, int _row ) const
4263{
4264 if ( d->hasExtra() && (!d->extra()->obscuringCells.isEmpty()) )
4265 {
4266 const Cell* cell = d->extra()->obscuringCells.first();
4267 return cell->bgColor( cell->column(), cell->row() );
4268 }
4269
4270 return format()->bgColor( _col, _row );
4271}
4272
4274//
4275// Borders.
4276// Reimplementation of Format methods.
4277//
4279
4280void Cell::setLeftBorderPen( const TQPen& p )
4281{
4282 if ( column() == 1 )
4283 {
4284 Cell* cell = format()->sheet()->cellAt( column() - 1, row() );
4285 if ( cell && cell->format()->hasProperty( Format::PRightBorder )
4286 && format()->sheet()->cellAt( column(), row() ) == this )
4287 cell->format()->clearProperty( Format::PRightBorder );
4288 }
4289
4290 format()->setLeftBorderPen( p );
4291}
4292
4293void Cell::setTopBorderPen( const TQPen& p )
4294{
4295 if ( row() == 1 )
4296 {
4297 Cell* cell = format()->sheet()->cellAt( column(), row() - 1 );
4298 if ( cell && cell->format()->hasProperty( Format::PBottomBorder )
4299 && format()->sheet()->cellAt( column(), row() ) == this )
4300 cell->format()->clearProperty( Format::PBottomBorder );
4301 }
4302 format()->setTopBorderPen( p );
4303}
4304
4305void Cell::setRightBorderPen( const TQPen& p )
4306{
4307 Cell* cell = 0L;
4308 if ( column() < KS_colMax )
4309 cell = format()->sheet()->cellAt( column() + 1, row() );
4310
4311 if ( cell && cell->format()->hasProperty( Format::PLeftBorder )
4312 && format()->sheet()->cellAt( column(), row() ) == this )
4313 cell->format()->clearProperty( Format::PLeftBorder );
4314
4315 format()->setRightBorderPen( p );
4316}
4317
4318void Cell::setBottomBorderPen( const TQPen& p )
4319{
4320 Cell* cell = 0L;
4321 if ( row() < KS_rowMax )
4322 cell = format()->sheet()->cellAt( column(), row() + 1 );
4323
4324 if ( cell && cell->format()->hasProperty( Format::PTopBorder )
4325 && format()->sheet()->cellAt( column(), row() ) == this )
4326 cell->format()->clearProperty( Format::PTopBorder );
4327
4328 format()->setBottomBorderPen( p );
4329}
4330
4331const TQPen& Cell::rightBorderPen( int _col, int _row ) const
4332{
4333 if ( !format()->hasProperty( Format::PRightBorder ) && ( _col < KS_colMax ) )
4334 {
4335 Cell * cell = format()->sheet()->cellAt( _col + 1, _row );
4336 if ( cell && cell->format()->hasProperty( Format::PLeftBorder ) )
4337 return cell->leftBorderPen( _col + 1, _row );
4338 }
4339
4340 return format()->rightBorderPen( _col, _row );
4341}
4342
4343const TQPen& Cell::leftBorderPen( int _col, int _row ) const
4344{
4345 if ( !format()->hasProperty( Format::PLeftBorder ) )
4346 {
4347 const Cell * cell = format()->sheet()->cellAt( _col - 1, _row );
4348 if ( cell && cell->format()->hasProperty( Format::PRightBorder ) )
4349 return cell->rightBorderPen( _col - 1, _row );
4350 }
4351
4352 return format()->leftBorderPen( _col, _row );
4353}
4354
4355const TQPen& Cell::bottomBorderPen( int _col, int _row ) const
4356{
4357 if ( !format()->hasProperty( Format::PBottomBorder ) && ( _row < KS_rowMax ) )
4358 {
4359 const Cell * cell = format()->sheet()->cellAt( _col, _row + 1 );
4360 if ( cell && cell->format()->hasProperty( Format::PTopBorder ) )
4361 return cell->topBorderPen( _col, _row + 1 );
4362 }
4363
4364 return format()->bottomBorderPen( _col, _row );
4365}
4366
4367const TQPen& Cell::topBorderPen( int _col, int _row ) const
4368{
4369 if ( !format()->hasProperty( Format::PTopBorder ) )
4370 {
4371 const Cell * cell = format()->sheet()->cellAt( _col, _row - 1 );
4372 if ( cell->format()->hasProperty( Format::PBottomBorder ) )
4373 return cell->bottomBorderPen( _col, _row - 1 );
4374 }
4375
4376 return format()->topBorderPen( _col, _row );
4377}
4378
4379const TQColor & Cell::effTextColor( int col, int row ) const
4380{
4381 if ( d->hasExtra() && d->extra()->conditions
4382 && d->extra()->conditions->matchedStyle()
4383 && d->extra()->conditions->matchedStyle()->hasFeature( Style::STextPen, true ) )
4384 return d->extra()->conditions->matchedStyle()->pen().color();
4385
4386 return format()->textColor( col, row );
4387}
4388
4389const TQPen& Cell::effLeftBorderPen( int col, int row ) const
4390{
4391 if ( isPartOfMerged() )
4392 {
4393 Cell * cell = d->extra()->obscuringCells.first();
4394 return cell->effLeftBorderPen( cell->column(), cell->row() );
4395 }
4396
4397 if ( d->hasExtra() && d->extra()->conditions
4398 && d->extra()->conditions->matchedStyle()
4399 && d->extra()->conditions->matchedStyle()->hasFeature( Style::SLeftBorder, true ) )
4400 return d->extra()->conditions->matchedStyle()->leftBorderPen();
4401
4402 return leftBorderPen( col, row );
4403}
4404
4405const TQPen& Cell::effTopBorderPen( int col, int row ) const
4406{
4407 if ( isPartOfMerged() )
4408 {
4409 Cell * cell = d->extra()->obscuringCells.first();
4410 return cell->effTopBorderPen( cell->column(), cell->row() );
4411 }
4412
4413 if ( d->hasExtra() && d->extra()->conditions
4414 && d->extra()->conditions->matchedStyle()
4415 && d->extra()->conditions->matchedStyle()->hasFeature( Style::STopBorder, true ) )
4416 return d->extra()->conditions->matchedStyle()->topBorderPen();
4417
4418 return topBorderPen( col, row );
4419}
4420
4421const TQPen& Cell::effRightBorderPen( int col, int row ) const
4422{
4423 if ( isPartOfMerged() )
4424 {
4425 Cell * cell = d->extra()->obscuringCells.first();
4426 return cell->effRightBorderPen( cell->column(), cell->row() );
4427 }
4428
4429 if ( d->hasExtra() && d->extra()->conditions
4430 && d->extra()->conditions->matchedStyle()
4431 && d->extra()->conditions->matchedStyle()->hasFeature( Style::SRightBorder, true ) )
4432 return d->extra()->conditions->matchedStyle()->rightBorderPen();
4433
4434 return rightBorderPen( col, row );
4435}
4436
4437const TQPen& Cell::effBottomBorderPen( int col, int row ) const
4438{
4439 if ( isPartOfMerged() )
4440 {
4441 Cell * cell = d->extra()->obscuringCells.first();
4442 return cell->effBottomBorderPen( cell->column(), cell->row() );
4443 }
4444
4445 if ( d->hasExtra() && d->extra()->conditions
4446 && d->extra()->conditions->matchedStyle()
4447 && d->extra()->conditions->matchedStyle()->hasFeature( Style::SBottomBorder, true ) )
4448 return d->extra()->conditions->matchedStyle()->bottomBorderPen();
4449
4450 return bottomBorderPen( col, row );
4451}
4452
4453const TQPen & Cell::effGoUpDiagonalPen( int col, int row ) const
4454{
4455 if ( d->hasExtra() && d->extra()->conditions
4456 && d->extra()->conditions->matchedStyle()
4457 && d->extra()->conditions->matchedStyle()->hasFeature( Style::SGoUpDiagonal, true ) )
4458 return d->extra()->conditions->matchedStyle()->goUpDiagonalPen();
4459
4460 return format()->goUpDiagonalPen( col, row );
4461}
4462
4463const TQPen & Cell::effFallDiagonalPen( int col, int row ) const
4464{
4465 if ( d->hasExtra() && d->extra()->conditions
4466 && d->extra()->conditions->matchedStyle()
4467 && d->extra()->conditions->matchedStyle()->hasFeature( Style::SFallDiagonal, true ) )
4468 return d->extra()->conditions->matchedStyle()->fallDiagonalPen();
4469
4470 return format()->fallDiagonalPen( col, row );
4471}
4472
4473uint Cell::effBottomBorderValue( int col, int row ) const
4474{
4475 if ( isPartOfMerged() )
4476 {
4477 Cell * cell = d->extra()->obscuringCells.first();
4478 return cell->effBottomBorderValue( cell->column(), cell->row() );
4479 }
4480
4481 if ( d->hasExtra() && d->extra()->conditions
4482 && d->extra()->conditions->matchedStyle() )
4483 return d->extra()->conditions->matchedStyle()->bottomPenValue();
4484
4485 return format()->bottomBorderValue( col, row );
4486}
4487
4488uint Cell::effRightBorderValue( int col, int row ) const
4489{
4490 if ( isPartOfMerged() )
4491 {
4492 Cell * cell = d->extra()->obscuringCells.first();
4493 return cell->effRightBorderValue( cell->column(), cell->row() );
4494 }
4495
4496 if ( d->hasExtra() && d->extra()->conditions
4497 && d->extra()->conditions->matchedStyle() )
4498 return d->extra()->conditions->matchedStyle()->rightPenValue();
4499
4500 return format()->rightBorderValue( col, row );
4501}
4502
4503uint Cell::effLeftBorderValue( int col, int row ) const
4504{
4505 if ( isPartOfMerged() )
4506 {
4507 Cell * cell = d->extra()->obscuringCells.first();
4508 return cell->effLeftBorderValue( cell->column(), cell->row() );
4509 }
4510
4511 if ( d->hasExtra() && d->extra()->conditions
4512 && d->extra()->conditions->matchedStyle() )
4513 return d->extra()->conditions->matchedStyle()->leftPenValue();
4514
4515 return format()->leftBorderValue( col, row );
4516}
4517
4518uint Cell::effTopBorderValue( int col, int row ) const
4519{
4520 if ( isPartOfMerged() )
4521 {
4522 Cell * cell = d->extra()->obscuringCells.first();
4523 return cell->effTopBorderValue( cell->column(), cell->row() );
4524 }
4525
4526 if ( d->hasExtra() && d->extra()->conditions
4527 && d->extra()->conditions->matchedStyle() )
4528 return d->extra()->conditions->matchedStyle()->topPenValue();
4529
4530 return format()->topBorderValue( col, row );
4531}
4532
4534//
4535// Precision
4536//
4538
4540{
4541 //TODO: This is ugly. Why not simply regenerate the text to display? Tomas
4542
4543 if ( !value().isNumber() )
4544 return;
4545 int tmpPreci = format()->precision( column(), row() );
4546
4547 if ( tmpPreci == -1 )
4548 {
4549 int pos = d->strOutText.find(decimal_point);
4550 if ( pos == -1 )
4551 pos = d->strOutText.find('.');
4552 if ( pos == -1 )
4553 format()->setPrecision(1);
4554 else
4555 {
4556 int start = 0;
4557 if ( d->strOutText.find('%') != -1 )
4558 start = 2;
4559 else if ( d->strOutText.find(locale()->currencySymbol()) == ((int)(d->strOutText.length()-locale()->currencySymbol().length())) )
4560 start = locale()->currencySymbol().length() + 1;
4561 else if ( (start=d->strOutText.find('E')) != -1 )
4562 start = d->strOutText.length() - start;
4563
4564 //kdDebug(36001) << "start=" << start << " pos=" << pos << " length=" << d->strOutText.length() << endl;
4565 format()->setPrecision( TQMAX( 0, (int)d->strOutText.length() - start - pos ) );
4566 }
4567 }
4568 else if ( tmpPreci < 10 )
4569 {
4570 format()->setPrecision( ++tmpPreci );
4571 }
4572 setFlag(Flag_LayoutDirty);
4573}
4574
4576{
4577 //TODO: This is ugly. Why not simply regenerate the text to display? Tomas
4578
4579 if ( !value().isNumber() )
4580 return;
4581 int preciTmp = format()->precision( column(), row() );
4582// kdDebug(36001) << "decPrecision: tmpPreci = " << tmpPreci << endl;
4583 if ( format()->precision(column(),row()) == -1 )
4584 {
4585 int pos = d->strOutText.find( decimal_point );
4586 int start = 0;
4587 if ( d->strOutText.find('%') != -1 )
4588 start = 2;
4589 else if ( d->strOutText.find(locale()->currencySymbol()) == ((int)(d->strOutText.length()-locale()->currencySymbol().length())) )
4590 start = locale()->currencySymbol().length() + 1;
4591 else if ( (start = d->strOutText.find('E')) != -1 )
4592 start = d->strOutText.length() - start;
4593 else
4594 start = 0;
4595
4596 if ( pos == -1 )
4597 return;
4598
4599 format()->setPrecision(d->strOutText.length() - pos - 2 - start);
4600 // if ( preciTmp < 0 )
4601 // format()->setPrecision( preciTmp );
4602 }
4603 else if ( preciTmp > 0 )
4604 {
4605 format()->setPrecision( --preciTmp );
4606 }
4607 setFlag( Flag_LayoutDirty );
4608}
4609
4610//set numerical value
4611//used in Sheet::setSeries (nowhere else yet)
4612void Cell::setNumber( double number )
4613{
4614 setValue( Value( number ) );
4615
4616 d->strText.setNum( number );
4617 setDisplayText(d->strText);
4619}
4620
4621void Cell::setCellText( const TQString& _text, bool asText )
4622{
4623 // TQString ctext = _text;
4624
4625// (Tomas) is this trim necessary for anything ?
4626// if( ctext.length() > 5000 )
4627// ctext = ctext.left( 5000 );
4628
4629 // empty string ?
4630 if (_text.length() == 0) {
4631 d->strOutText = d->strText = "";
4633 return;
4634 }
4635
4636 // as text ?
4637 if (asText) {
4638 d->strOutText = _text;
4639 d->strText = _text;
4640 setValue (Value (_text));
4641
4642 return;
4643 }
4644
4645 TQString oldText = d->strText;
4646 setDisplayText( _text );
4647 if(!format()->sheet()->isLoading() && !testValidity() )
4648 {
4649 //reapply old value if action == stop
4650 setDisplayText( oldText );
4651 }
4652}
4653
4654void Cell::setDisplayText( const TQString& _text )
4655{
4656 bool isLoading = format()->sheet()->isLoading();
4657
4658 if (!isLoading)
4659 format()->sheet()->doc()->emitBeginOperation( false );
4660
4661 d->strText = _text;
4662
4666 if ( !d->strText.isEmpty() && d->strText[0] == '=' )
4667 {
4668 setFlag(Flag_LayoutDirty);
4669 setFlag(Flag_TextFormatDirty);
4670
4671 if ( !makeFormula() )
4672 kdError(36001) << "ERROR: Syntax ERROR" << endl;
4674 }
4675
4679 else
4680 {
4681 // Find out what data type it is
4683
4684 setFlag(Flag_LayoutDirty);
4685 setFlag(Flag_TextFormatDirty);
4686 }
4687
4688 if ( !isLoading )
4689 format()->sheet()->doc()->emitEndOperation( TQRect( d->column, d->row, 1, 1 ) );
4690}
4691
4692void Cell::setLink( const TQString& link )
4693{
4694 d->extra()->link = link;
4695
4696 if( !link.isEmpty() && d->strText.isEmpty() )
4697 setCellText( link );
4698}
4699
4700TQString Cell::link() const
4701{
4702 return d->hasExtra() ? d->extra()->link : TQString();
4703}
4704
4705void Cell::update()
4706{
4707 /* those obscuring us need to redo their layout cause they can't obscure us
4708 now that we've got text.
4709 This includes cells obscuring cells that we are obscuring
4710 */
4711 for (int x = d->column; x <= d->column + extraXCells(); x++)
4712 {
4713 for (int y = d->row; y <= d->row + extraYCells(); y++)
4714 {
4715 Cell* cell = format()->sheet()->cellAt(x,y);
4716 cell->setLayoutDirtyFlag();
4717 }
4718 }
4719
4721
4722 /* TODO - is this a good place for this? */
4723 updateChart(true);
4724}
4725
4727{
4728 bool valid = false;
4729 if( d->hasExtra() && d->extra()->validity && d->extra()->validity->m_restriction != Restriction::None )
4730 {
4731 //fixme
4732 if ( d->extra()->validity->allowEmptyCell && d->strText.isEmpty() )
4733 return true;
4734
4735 if( value().isNumber() &&
4736 (d->extra()->validity->m_restriction == Restriction::Number ||
4737 (d->extra()->validity->m_restriction == Restriction::Integer &&
4738 value().asFloat() == ceil(value().asFloat()))))
4739 {
4740 switch( d->extra()->validity->m_cond)
4741 {
4742 case Conditional::Equal:
4743 valid = ( value().asFloat() - d->extra()->validity->valMin < DBL_EPSILON
4744 && value().asFloat() - d->extra()->validity->valMin >
4745 (0.0 - DBL_EPSILON));
4746 break;
4747 case Conditional::DifferentTo:
4748 valid = !( ( value().asFloat() - d->extra()->validity->valMin < DBL_EPSILON
4749 && value().asFloat() - d->extra()->validity->valMin >
4750 (0.0 - DBL_EPSILON)) );
4751 break;
4752 case Conditional::Superior:
4753 valid = ( value().asFloat() > d->extra()->validity->valMin);
4754 break;
4755 case Conditional::Inferior:
4756 valid = ( value().asFloat() <d->extra()->validity->valMin);
4757 break;
4758 case Conditional::SuperiorEqual:
4759 valid = ( value().asFloat() >= d->extra()->validity->valMin);
4760 break;
4761 case Conditional::InferiorEqual:
4762 valid = (value().asFloat() <= d->extra()->validity->valMin);
4763 break;
4764 case Conditional::Between:
4765 valid = ( value().asFloat() >= d->extra()->validity->valMin &&
4766 value().asFloat() <= d->extra()->validity->valMax);
4767 break;
4768 case Conditional::Different:
4769 valid = (value().asFloat() < d->extra()->validity->valMin ||
4770 value().asFloat() > d->extra()->validity->valMax);
4771 break;
4772 default :
4773 break;
4774 }
4775 }
4776 else if(d->extra()->validity->m_restriction==Restriction::Text)
4777 {
4778 valid = value().isString();
4779 }
4780 else if ( d->extra()->validity->m_restriction == Restriction::List )
4781 {
4782 //test int value
4783 if ( value().isString() && d->extra()->validity->listValidity.contains( value().asString() ) )
4784 valid = true;
4785 }
4786 else if(d->extra()->validity->m_restriction==Restriction::TextLength)
4787 {
4788 if( value().isString() )
4789 {
4790 int len = d->strOutText.length();
4791 switch( d->extra()->validity->m_cond)
4792 {
4793 case Conditional::Equal:
4794 if (len == d->extra()->validity->valMin)
4795 valid = true;
4796 break;
4797 case Conditional::DifferentTo:
4798 if (len != d->extra()->validity->valMin)
4799 valid = true;
4800 break;
4801 case Conditional::Superior:
4802 if(len > d->extra()->validity->valMin)
4803 valid = true;
4804 break;
4805 case Conditional::Inferior:
4806 if(len < d->extra()->validity->valMin)
4807 valid = true;
4808 break;
4809 case Conditional::SuperiorEqual:
4810 if(len >= d->extra()->validity->valMin)
4811 valid = true;
4812 break;
4813 case Conditional::InferiorEqual:
4814 if(len <= d->extra()->validity->valMin)
4815 valid = true;
4816 break;
4817 case Conditional::Between:
4818 if(len >= d->extra()->validity->valMin && len <= d->extra()->validity->valMax)
4819 valid = true;
4820 break;
4821 case Conditional::Different:
4822 if(len <d->extra()->validity->valMin || len >d->extra()->validity->valMax)
4823 valid = true;
4824 break;
4825 default :
4826 break;
4827 }
4828 }
4829 }
4830 else if(d->extra()->validity->m_restriction == Restriction::Time && isTime())
4831 {
4832 switch( d->extra()->validity->m_cond)
4833 {
4834 case Conditional::Equal:
4835 valid = (value().asTime() == d->extra()->validity->timeMin);
4836 break;
4837 case Conditional::DifferentTo:
4838 valid = (value().asTime() != d->extra()->validity->timeMin);
4839 break;
4840 case Conditional::Superior:
4841 valid = (value().asTime() > d->extra()->validity->timeMin);
4842 break;
4843 case Conditional::Inferior:
4844 valid = (value().asTime() < d->extra()->validity->timeMin);
4845 break;
4846 case Conditional::SuperiorEqual:
4847 valid = (value().asTime() >= d->extra()->validity->timeMin);
4848 break;
4849 case Conditional::InferiorEqual:
4850 valid = (value().asTime() <= d->extra()->validity->timeMin);
4851 break;
4852 case Conditional::Between:
4853 valid = (value().asTime() >= d->extra()->validity->timeMin &&
4854 value().asTime() <= d->extra()->validity->timeMax);
4855 break;
4856 case Conditional::Different:
4857 valid = (value().asTime() < d->extra()->validity->timeMin ||
4858 value().asTime() > d->extra()->validity->timeMax);
4859 break;
4860 default :
4861 break;
4862
4863 }
4864 }
4865 else if(d->extra()->validity->m_restriction == Restriction::Date && isDate())
4866 {
4867 switch( d->extra()->validity->m_cond)
4868 {
4869 case Conditional::Equal:
4870 valid = (value().asDate() == d->extra()->validity->dateMin);
4871 break;
4872 case Conditional::DifferentTo:
4873 valid = (value().asDate() != d->extra()->validity->dateMin);
4874 break;
4875 case Conditional::Superior:
4876 valid = (value().asDate() > d->extra()->validity->dateMin);
4877 break;
4878 case Conditional::Inferior:
4879 valid = (value().asDate() < d->extra()->validity->dateMin);
4880 break;
4881 case Conditional::SuperiorEqual:
4882 valid = (value().asDate() >= d->extra()->validity->dateMin);
4883 break;
4884 case Conditional::InferiorEqual:
4885 valid = (value().asDate() <= d->extra()->validity->dateMin);
4886 break;
4887 case Conditional::Between:
4888 valid = (value().asDate() >= d->extra()->validity->dateMin &&
4889 value().asDate() <= d->extra()->validity->dateMax);
4890 break;
4891 case Conditional::Different:
4892 valid = (value().asDate() < d->extra()->validity->dateMin ||
4893 value().asDate() > d->extra()->validity->dateMax);
4894 break;
4895 default :
4896 break;
4897
4898 }
4899 }
4900 }
4901 else
4902 {
4903 valid= true;
4904 }
4905
4906 if(!valid &&d->extra()->validity != NULL && d->extra()->validity->displayMessage)
4907 {
4908 switch (d->extra()->validity->m_action )
4909 {
4910 case Action::Stop:
4911 KMessageBox::error((TQWidget*)0L, d->extra()->validity->message,
4912 d->extra()->validity->title);
4913 break;
4914 case Action::Warning:
4915 KMessageBox::warningYesNo((TQWidget*)0L, d->extra()->validity->message,
4916 d->extra()->validity->title);
4917 break;
4918 case Action::Information:
4919 KMessageBox::information((TQWidget*)0L, d->extra()->validity->message,
4920 d->extra()->validity->title);
4921 break;
4922 }
4923 }
4924 if (!d->hasExtra())
4925 return true; //okay if there's no validity
4926 return (valid || d->extra()->validity == NULL || d->extra()->validity->m_action != Action::Stop);
4927}
4928
4929FormatType Cell::formatType() const
4930{
4931 return format()->getFormatType( d->column, d->row );
4932}
4933
4934double Cell::textWidth() const
4935{
4936 return d->textWidth;
4937}
4938
4939double Cell::textHeight() const
4940{
4941 return d->textHeight;
4942}
4943
4945{
4946 return d->hasExtra() ? d->extra()->mergedXCells : 0;
4947}
4948
4950{
4951 return d->hasExtra() ? d->extra()->mergedYCells : 0;
4952}
4953
4955{
4956 return d->hasExtra() ? d->extra()->extraXCells : 0;
4957}
4958
4960{
4961 return d->hasExtra() ? d->extra()->extraYCells : 0;
4962}
4963
4964double Cell::extraWidth() const
4965{
4966 return d->hasExtra() ? d->extra()->extraWidth : 0;
4967}
4968
4969double Cell::extraHeight() const
4970{
4971 return d->hasExtra() ? d->extra()->extraHeight : 0;
4972}
4973
4974
4975bool Cell::isDate() const
4976{
4977 FormatType ft = formatType();
4978
4979 return (formatIsDate (ft) || ((ft == Generic_format) &&
4980 (value().format() == Value::fmt_Date)));
4981}
4982
4983bool Cell::isTime() const
4984{
4985 FormatType ft = formatType();
4986
4987 return (formatIsTime (ft) || ((ft == Generic_format) &&
4988 (value().format() == Value::fmt_Time)));
4989}
4990
4992{
4993 if ( !isFormula() )
4994 {
4995 //don't set the flag if we don't hold a formula
4996 clearFlag(Flag_CalcDirty);
4997 return;
4998 }
4999 setFlag(Flag_CalcDirty);
5000 format()->sheet()->setRegionPaintDirty(cellRect());
5001}
5002
5003
5004bool Cell::updateChart(bool refresh)
5005{
5006 // Update a chart for example if it depends on this cell.
5007 if ( d->row != 0 && d->column != 0 )
5008 {
5009 CellBinding *bind;
5010 for ( bind = format()->sheet()->firstCellBinding(); bind != 0L; bind = format()->sheet()->nextCellBinding() )
5011 {
5012 if ( bind->contains( d->column, d->row ) )
5013 {
5014 if (!refresh)
5015 return true;
5016
5017 bind->cellChanged( this );
5018 }
5019 }
5020 return true;
5021 }
5022 return false;
5023
5024}
5025
5027{
5028 if (isDefault())
5029 return 0.0;
5030 //(Tomas) umm can't we simply call value().asFloat() ?
5031 if (isDate())
5032 {
5033 TQDate date = value().asDate();
5034 TQDate dummy (1900, 1, 1);
5035 return (dummy.daysTo (date) + 1);
5036 }
5037 if (isTime())
5038 {
5039 TQTime time = value().asTime();
5040 TQTime dummy;
5041 return dummy.secsTo( time );
5042 }
5043 if (value().isNumber())
5044 return value().asFloat();
5045
5046 return 0.0;
5047}
5048
5050{
5051 if (isDefault())
5052 return;
5053
5054 setValue (getDouble ());
5055}
5056
5058{
5059 if (isDefault())
5060 return;
5061
5062 setValue (getDouble ());
5063 d->value.setFormat (Value::fmt_Percent);
5064}
5065
5067{
5068 if (isDefault())
5069 return;
5070
5071 setValue (getDouble ());
5072 d->value.setFormat (Value::fmt_Money);
5073 format()->setPrecision (locale()->fracDigits());
5074}
5075
5077{
5078 //(Tomas) This is weird. And I mean *REALLY* weird. First, we
5079 //generate a time (TQTime), then we convert it to text, then
5080 //we give the text to the cell and ask it to parse it. Weird...
5081
5082 if (isDefault() || isEmpty())
5083 return;
5084
5085 setValue (getDouble ());
5086 TQTime time = value().asDateTime().time();
5087 int msec = (int) ( (value().asFloat() - (int) value().asFloat()) * 1000 );
5088 time = time.addMSecs( msec );
5089 setCellText( time.toString() );
5090}
5091
5093{
5094 //(Tomas) This is weird. And I mean *REALLY* weird. First, we
5095 //generate a date (TQDate), then we convert it to text, then
5096 //we give the text to the cell and ask it to parse it. Weird...
5097
5098 if (isDefault() || isEmpty())
5099 return;
5100
5101 setValue (getDouble ());
5102
5103 //TODO: why did we call setValue(), when we override it here?
5104 TQDate date(1900, 1, 1);
5105 date = date.addDays( (int) value().asFloat() - 1 );
5106 date = value().asDateTime().date();
5107 setCellText (locale()->formatDate (date, true));
5108}
5109
5111{
5112 // Goal of this method: determine the value of the cell
5114
5115 d->value = Value::empty();
5116
5117 // Get the text from that cell
5118 TQString str = d->strText;
5119
5120 sheet()->doc()->parser()->parse (str, this);
5121
5122 // Parsing as time acts like an autoformat: we even change d->strText
5123 // [h]:mm:ss -> might get set by ValueParser
5124 if (isTime() && (formatType() != Time_format7))
5125 d->strText = locale()->formatTime( value().asDateTime().time(), true);
5126
5127 // convert first letter to uppercase ?
5128 if (format()->sheet()->getFirstLetterUpper() && value().isString() &&
5129 (!d->strText.isEmpty()))
5130 {
5131 TQString str = value().asString();
5132 setValue( Value( str[0].upper() + str.right( str.length()-1 ) ) );
5133 }
5134}
5135
5136//used in calc, setNumber, ValueParser
5138{
5139 if ( formatType() == Number_format && value().isNumber() )
5140 {
5141 if ( value().asFloat() > 1e+10 )
5142 format()->setFormatType( Scientific_format );
5143 }
5144}
5145
5146
5147// ================================================================
5148// Saving and loading
5149
5150
5151TQDomElement Cell::save( TQDomDocument& doc,
5152 int _x_offset, int _y_offset,
5153 bool force, bool copy, bool era )
5154{
5155 // Save the position of this cell
5156 TQDomElement cell = doc.createElement( "cell" );
5157 cell.setAttribute( "row", d->row - _y_offset );
5158 cell.setAttribute( "column", d->column - _x_offset );
5159 //
5160 // Save the formatting information
5161 //
5162 TQDomElement formatElement = format()->save( doc, d->column, d->row, force, copy );
5163 if ( formatElement.hasChildNodes() || formatElement.attributes().length() ) // don't save empty tags
5164 cell.appendChild( formatElement );
5165
5166 if ( doesMergeCells() )
5167 {
5168 if ( extraXCells() )
5169 formatElement.setAttribute( "colspan", extraXCells() );
5170 if ( extraYCells() )
5171 formatElement.setAttribute( "rowspan", extraYCells() );
5172 }
5173
5174 if ( d->hasExtra() && d->extra()->conditions )
5175 {
5176 TQDomElement conditionElement = d->extra()->conditions->saveConditions( doc );
5177
5178 if ( !conditionElement.isNull() )
5179 cell.appendChild( conditionElement );
5180 }
5181
5182 if ( d->hasExtra() && (d->extra()->validity != 0) )
5183 {
5184 TQDomElement validity = doc.createElement("validity");
5185
5186 TQDomElement param=doc.createElement("param");
5187 param.setAttribute("cond",(int)d->extra()->validity->m_cond);
5188 param.setAttribute("action",(int)d->extra()->validity->m_action);
5189 param.setAttribute("allow",(int)d->extra()->validity->m_restriction);
5190 param.setAttribute("valmin",d->extra()->validity->valMin);
5191 param.setAttribute("valmax",d->extra()->validity->valMax);
5192 param.setAttribute("displaymessage",d->extra()->validity->displayMessage);
5193 param.setAttribute("displayvalidationinformation",d->extra()->validity->displayValidationInformation);
5194 param.setAttribute("allowemptycell",d->extra()->validity->allowEmptyCell);
5195 if ( !d->extra()->validity->listValidity.isEmpty() )
5196 param.setAttribute( "listvalidity", d->extra()->validity->listValidity.join( ";" ) );
5197 validity.appendChild(param);
5198 TQDomElement title = doc.createElement( "title" );
5199 title.appendChild( doc.createTextNode( d->extra()->validity->title ) );
5200 validity.appendChild( title );
5201 TQDomElement message = doc.createElement( "message" );
5202 message.appendChild( doc.createCDATASection( d->extra()->validity->message ) );
5203 validity.appendChild( message );
5204
5205 TQDomElement inputTitle = doc.createElement( "inputtitle" );
5206 inputTitle.appendChild( doc.createTextNode( d->extra()->validity->titleInfo ) );
5207 validity.appendChild( inputTitle );
5208
5209 TQDomElement inputMessage = doc.createElement( "inputmessage" );
5210 inputMessage.appendChild( doc.createTextNode( d->extra()->validity->messageInfo ) );
5211 validity.appendChild( inputMessage );
5212
5213
5214
5215 TQString tmp;
5216 if ( d->extra()->validity->timeMin.isValid() )
5217 {
5218 TQDomElement timeMin = doc.createElement( "timemin" );
5219 tmp=d->extra()->validity->timeMin.toString();
5220 timeMin.appendChild( doc.createTextNode( tmp ) );
5221 validity.appendChild( timeMin );
5222 }
5223 if ( d->extra()->validity->timeMax.isValid() )
5224 {
5225 TQDomElement timeMax = doc.createElement( "timemax" );
5226 tmp=d->extra()->validity->timeMax.toString();
5227 timeMax.appendChild( doc.createTextNode( tmp ) );
5228 validity.appendChild( timeMax );
5229 }
5230
5231 if ( d->extra()->validity->dateMin.isValid() )
5232 {
5233 TQDomElement dateMin = doc.createElement( "datemin" );
5234 TQString tmp("%1/%2/%3");
5235 tmp = tmp.arg(d->extra()->validity->dateMin.year()).arg(d->extra()->validity->dateMin.month()).arg(d->extra()->validity->dateMin.day());
5236 dateMin.appendChild( doc.createTextNode( tmp ) );
5237 validity.appendChild( dateMin );
5238 }
5239 if ( d->extra()->validity->dateMax.isValid() )
5240 {
5241 TQDomElement dateMax = doc.createElement( "datemax" );
5242 TQString tmp("%1/%2/%3");
5243 tmp = tmp.arg(d->extra()->validity->dateMax.year()).arg(d->extra()->validity->dateMax.month()).arg(d->extra()->validity->dateMax.day());
5244 dateMax.appendChild( doc.createTextNode( tmp ) );
5245 validity.appendChild( dateMax );
5246 }
5247
5248 cell.appendChild( validity );
5249 }
5250
5251 if ( format()->comment() )
5252 {
5253 TQDomElement comment = doc.createElement( "comment" );
5254 comment.appendChild( doc.createCDATASection( *format()->comment() ) );
5255 cell.appendChild( comment );
5256 }
5257
5258 //
5259 // Save the text
5260 //
5261 if ( !d->strText.isEmpty() )
5262 {
5263 // Formulas need to be encoded to ensure that they
5264 // are position independent.
5265 if ( isFormula() )
5266 {
5267 TQDomElement text = doc.createElement( "text" );
5268 // if we are cutting to the clipboard, relative references need to be encoded absolutely
5269 text.appendChild( doc.createTextNode( encodeFormula( era ) ) );
5270 cell.appendChild( text );
5271
5272 /* we still want to save the results of the formula */
5273 TQDomElement formulaResult = doc.createElement( "result" );
5274 saveCellResult( doc, formulaResult, d->strOutText );
5275 cell.appendChild( formulaResult );
5276
5277 }
5278 else if ( !link().isEmpty() )
5279 {
5280 // KSpread pre 1.4 saves link as rich text, marked with first char '
5281 // Have to be saved in some CDATA section because of too many special charatcers.
5282 TQDomElement text = doc.createElement( "text" );
5283 TQString qml = "!<a href=\"" + link() + "\">" + d->strText + "</a>";
5284 text.appendChild( doc.createCDATASection( qml ) );
5285 cell.appendChild( text );
5286 }
5287 else
5288 {
5289 // Save the cell contents (in a locale-independent way)
5290 TQDomElement text = doc.createElement( "text" );
5291 saveCellResult( doc, text, d->strText );
5292 cell.appendChild( text );
5293 }
5294 }
5295 if ( cell.hasChildNodes() || cell.attributes().length() > 2 ) // don't save empty tags
5296 // (the >2 is due to "row" and "column" attributes)
5297 return cell;
5298 else
5299 return TQDomElement();
5300}
5301
5302bool Cell::saveCellResult( TQDomDocument& doc, TQDomElement& result,
5303 TQString str )
5304{
5305 TQString dataType = "Other"; // fallback
5306
5307 if ( value().isNumber() )
5308 {
5309 if ( isDate() )
5310 {
5311 // serial number of date
5312 TQDate dd = value().asDateTime().date();
5313 dataType = "Date";
5314 str = "%1/%2/%3";
5315 str = str.arg(dd.year()).arg(dd.month()).arg(dd.day());
5316 }
5317 else if( isTime() )
5318 {
5319 // serial number of time
5320 dataType = "Time";
5321 str = value().asDateTime().time().toString();
5322 }
5323 else
5324 {
5325 // real number
5326 dataType = "Num";
5327 if (value().isInteger())
5328 str = TQString::number(value().asInteger());
5329 else
5330 str = TQString::number(value().asFloat(), 'g', DBL_DIG);
5331 }
5332 }
5333
5334 if ( value().isBoolean() )
5335 {
5336 dataType = "Bool";
5337 str = value().asBoolean() ? "true" : "false";
5338 }
5339
5340 if ( value().isString() )
5341 {
5342 dataType = "Str";
5343 str = value().asString();
5344 }
5345
5346 result.setAttribute( "dataType", dataType );
5347 if ( !d->strOutText.isEmpty() )
5348 result.setAttribute( "outStr", d->strOutText );
5349 result.appendChild( doc.createTextNode( str ) );
5350
5351 return true; /* really isn't much of a way for this function to fail */
5352}
5353
5354void Cell::saveOasisAnnotation( KoXmlWriter &xmlwriter )
5355{
5356 if ( format()->comment() )
5357 {
5358 //<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">
5359 xmlwriter.startElement( "office:annotation" );
5360 TQStringList text = TQStringList::split( "\n", *format()->comment() );
5361 for ( TQStringList::Iterator it = text.begin(); it != text.end(); ++it ) {
5362 xmlwriter.startElement( "text:p" );
5363 xmlwriter.addTextNode( *it );
5364 xmlwriter.endElement();
5365 }
5366 xmlwriter.endElement();
5367 }
5368}
5369
5370
5371
5372TQString Cell::saveOasisCellStyle( KoGenStyle &currentCellStyle, KoGenStyles &mainStyles )
5373{
5374 if ( d->hasExtra() && d->extra()->conditions )
5375 {
5376 // this has to be an automatic style
5377 currentCellStyle = KoGenStyle( Doc::STYLE_CELL_AUTO, "table-cell" );
5378 d->extra()->conditions->saveOasisConditions( currentCellStyle );
5379 }
5380 return format()->saveOasisCellStyle( currentCellStyle, mainStyles );
5381}
5382
5383
5384bool Cell::saveOasis( KoXmlWriter& xmlwriter, KoGenStyles &mainStyles,
5385 int row, int column, int &repeated,
5386 GenValidationStyles &valStyle )
5387{
5388 if ( !isPartOfMerged() )
5389 xmlwriter.startElement( "table:table-cell" );
5390 else
5391 xmlwriter.startElement( "table:covered-table-cell" );
5392#if 0
5393 //add font style
5394 TQFont font;
5395 Value const value( cell->value() );
5396 if ( !cell->isDefault() )
5397 {
5398 font = cell->format()->textFont( i, row );
5399 m_styles.addFont( font );
5400
5401 if ( cell->format()->hasProperty( Format::PComment ) )
5402 hasComment = true;
5403 }
5404#endif
5405 // NOTE save the value before the style as long as the Formatter does not work correctly
5406 if ( link().isEmpty() )
5407 saveOasisValue (xmlwriter);
5408
5409 KoGenStyle currentCellStyle; // the type determined in saveOasisCellStyle
5410 saveOasisCellStyle( currentCellStyle,mainStyles );
5411 // skip 'table:style-name' attribute for the default style
5412 if ( !currentCellStyle.isDefaultStyle() )
5413 xmlwriter.addAttribute( "table:style-name", mainStyles.styles()[currentCellStyle] );
5414
5415 // group empty cells with the same style
5416 if ( isEmpty() && !format()->hasProperty( Format::PComment ) &&
5418 {
5419 bool refCellIsDefault = isDefault();
5420 int j = column + 1;
5421 Cell *nextCell = format()->sheet()->getNextCellRight( column, row );
5422 while ( nextCell )
5423 {
5424 // if
5425 // the next cell is not the adjacent one
5426 // or
5427 // the next cell is not empty
5428 if ( nextCell->column() != j || !nextCell->isEmpty() )
5429 {
5430 if ( refCellIsDefault )
5431 {
5432 // if the origin cell was a default cell,
5433 // we count the default cells
5434 repeated = nextCell->column() - j + 1;
5435 }
5436 // otherwise we just stop here to process the adjacent
5437 // cell in the next iteration of the outer loop
5438 // (in Sheet::saveOasisCells)
5439 break;
5440 }
5441
5442 KoGenStyle nextCellStyle; // the type is determined in saveOasisCellStyle
5443 nextCell->saveOasisCellStyle( nextCellStyle,mainStyles );
5444
5445 if ( nextCell->isPartOfMerged() || nextCell->doesMergeCells() ||
5446 nextCell->format()->hasProperty( Format::PComment ) ||
5447 !(nextCellStyle == currentCellStyle) )
5448 {
5449 break;
5450 }
5451 ++repeated;
5452 // get the next cell and set the index to the adjacent cell
5453 nextCell = format()->sheet()->getNextCellRight( j++, row );
5454 }
5455 kdDebug() << "Cell::saveOasis: empty cell in column " << column << " "
5456 << "repeated " << repeated << " time(s)" << endl;
5457
5458 if ( repeated > 1 )
5459 xmlwriter.addAttribute( "table:number-columns-repeated", TQString::number( repeated ) );
5460 }
5461
5462
5463 if (d->hasExtra() && d->extra()->validity)
5464 {
5465 GenValidationStyle styleVal(d->extra()->validity);
5466 xmlwriter.addAttribute( "table:validation-name", valStyle.lookup( styleVal ) );
5467 }
5468 if ( isFormula() )
5469 {
5470 //kdDebug() << "Formula found" << endl;
5471 TQString formula( convertFormulaToOasisFormat( text() ) );
5472 xmlwriter.addAttribute( "table:formula", formula );
5473 }
5474 else if ( !link().isEmpty() )
5475 {
5476 //kdDebug()<<"Link found \n";
5477 xmlwriter.startElement( "text:p" );
5478 xmlwriter.startElement( "text:a" );
5479 //Reference cell is started by "#"
5480 if ( localReferenceAnchor( link() ) )
5481 xmlwriter.addAttribute( " xlink:href", ( "#"+link() ) );
5482 else
5483 xmlwriter.addAttribute( " xlink:href", link() );
5484 xmlwriter.addTextNode( text() );
5485 xmlwriter.endElement();
5486 xmlwriter.endElement();
5487 }
5488
5489 if ( doesMergeCells() )
5490 {
5491 int colSpan = mergedXCells() + 1;
5492 int rowSpan = mergedYCells() + 1;
5493
5494 if ( colSpan > 1 )
5495 xmlwriter.addAttribute( "table:number-columns-spanned", TQString::number( colSpan ) );
5496
5497 if ( rowSpan > 1 )
5498 xmlwriter.addAttribute( "table:number-rows-spanned", TQString::number( rowSpan ) );
5499 }
5500
5501 if ( !isEmpty() && link().isEmpty() )
5502 {
5503 xmlwriter.startElement( "text:p" );
5504 xmlwriter.addTextNode( strOutText().utf8() );
5505 xmlwriter.endElement();
5506 }
5507
5508 saveOasisAnnotation( xmlwriter );
5509
5510 xmlwriter.endElement();
5511 return true;
5512}
5513
5514void Cell::saveOasisValue (KoXmlWriter &xmlWriter)
5515{
5516 switch (value().format())
5517 {
5518 case Value::fmt_None: break; //NOTHING HERE
5519 case Value::fmt_Boolean:
5520 {
5521 xmlWriter.addAttribute( "office:value-type", "boolean" );
5522 xmlWriter.addAttribute( "office:boolean-value", ( value().asBoolean() ?
5523 "true" : "false" ) );
5524 break;
5525 }
5526 case Value::fmt_Number:
5527 {
5528 xmlWriter.addAttribute( "office:value-type", "float" );
5529 if (value().isInteger())
5530 xmlWriter.addAttribute( "office:value", TQString::number( value().asInteger() ) );
5531 else
5532 xmlWriter.addAttribute( "office:value", TQString::number( value().asFloat(), 'g', DBL_DIG ) );
5533 break;
5534 }
5535 case Value::fmt_Percent:
5536 {
5537 xmlWriter.addAttribute( "office:value-type", "percentage" );
5538 xmlWriter.addAttribute( "office:value",
5539 TQString::number( value().asFloat() ) );
5540 break;
5541 }
5542 case Value::fmt_Money:
5543 {
5544 xmlWriter.addAttribute( "office:value-type", "currency" );
5545 Format::Currency currency;
5546 if (format()->currencyInfo(currency))
5547 xmlWriter.addAttribute( "office:currency", Currency::getCurrencyCode(currency.type) );
5548 xmlWriter.addAttribute( "office:value",
5549 TQString::number( value().asFloat() ) );
5550 break;
5551 }
5552 case Value::fmt_DateTime: break; //NOTHING HERE
5553 case Value::fmt_Date:
5554 {
5555 xmlWriter.addAttribute( "office:value-type", "date" );
5556 xmlWriter.addAttribute( "office:date-value",
5557 value().asDate().toString( TQt::ISODate ) );
5558 break;
5559 }
5560 case Value::fmt_Time:
5561 {
5562 xmlWriter.addAttribute( "office:value-type", "time" );
5563 xmlWriter.addAttribute( "office:time-value",
5564 value().asTime().toString( "PThhHmmMssS" ) );
5565 break;
5566 }
5567 case Value::fmt_String:
5568 {
5569 xmlWriter.addAttribute( "office:value-type", "string" );
5570 xmlWriter.addAttribute( "office:string-value", value().asString() );
5571 break;
5572 }
5573 };
5574}
5575
5576TQString Cell::convertFormulaToOasisFormat( const TQString & formula ) const
5577{
5578 TQString s;
5579 TQRegExp exp("(\\$?)([a-zA-Z]+)(\\$?)([0-9]+)");
5580 int n = exp.search( formula, 0 );
5581 kdDebug() << "Exp: " << formula << ", n: " << n << ", Length: " << formula.length()
5582 << ", Matched length: " << exp.matchedLength() << endl;
5583
5584 bool inQuote1 = false;
5585 bool inQuote2 = false;
5586 int i = 0;
5587 int l = (int) formula.length();
5588 if ( l <= 0 )
5589 return formula;
5590 while ( i < l )
5591 {
5592 if ( ( n != -1 ) && ( n < i ) )
5593 {
5594 n = exp.search( formula, i );
5595 kdDebug() << "Exp: " << formula.right( l - i ) << ", n: " << n << endl;
5596 }
5597 if ( formula[i] == '"' )
5598 {
5599 inQuote1 = !inQuote1;
5600 s += formula[i];
5601 ++i;
5602 continue;
5603 }
5604 if ( formula[i] == '\'' )
5605 {
5606 // named area
5607 inQuote2 = !inQuote2;
5608 ++i;
5609 continue;
5610 }
5611 if ( inQuote1 || inQuote2 )
5612 {
5613 s += formula[i];
5614 ++i;
5615 continue;
5616 }
5617 if ( ( formula[i] == '=' ) && ( formula[i + 1] == '=' ) )
5618 {
5619 s += '=';
5620 ++i;++i;
5621 continue;
5622 }
5623 if ( formula[i] == '!' )
5624 {
5625 insertBracket( s );
5626 s += '.';
5627 ++i;
5628 continue;
5629 }
5630 if ( formula[i] == ',' )
5631 {
5632 s += '.';
5633 ++i;
5634 continue;
5635 }
5636 if ( n == i )
5637 {
5638 int ml = exp.matchedLength();
5639 if ( formula[ i + ml ] == '!' )
5640 {
5641 kdDebug() << "No cell ref but sheet name" << endl;
5642 s += formula[i];
5643 ++i;
5644 continue;
5645 }
5646 if ( ( i > 0 ) && ( formula[i - 1] != '!' ) )
5647 s += "[.";
5648 for ( int j = 0; j < ml; ++j )
5649 {
5650 s += formula[i];
5651 ++i;
5652 }
5653 s += ']';
5654 continue;
5655 }
5656
5657 s += formula[i];
5658 ++i;
5659 }
5660
5661 return s;
5662}
5663
5664void Cell::loadOasisConditional( TQDomElement * style )
5665{
5666 if ( style )//safe
5667 {
5668 TQDomElement e;
5669 forEachElement( e, style->toElement() )
5670 {
5671 if ( e.localName() == "map" && e.namespaceURI() == KoXmlNS::style )
5672 {
5673 if (d->hasExtra())
5674 delete d->extra()->conditions;
5675 d->extra()->conditions = new Conditions( this );
5676 d->extra()->conditions->loadOasisConditions( e );
5677 d->extra()->conditions->checkMatches();
5678 // break here
5679 // Conditions::loadOasisConditions finishes the iteration
5680 break;
5681 }
5682 }
5683 }
5684}
5685
5686bool Cell::loadOasis( const TQDomElement& element , KoOasisLoadingContext& oasisContext , Style* style )
5687{
5688 kdDebug() << "*** Loading cell properties ***** at " << column() << "," << row () << endl;
5689
5690 if ( element.hasAttributeNS( KoXmlNS::table, "style-name" ) )
5691 {
5692 kdDebug()<<" table:style-name: "<<element.attributeNS( KoXmlNS::table, "style-name", TQString() )<<endl;
5693 oasisContext.fillStyleStack( element, KoXmlNS::table, "styleName", "table-cell" );
5694
5695 TQString str = element.attributeNS( KoXmlNS::table, "style-name", TQString() );
5696 const TQDomElement* cellStyle = oasisContext.oasisStyles().findStyle( str, "table-cell" );
5697
5698 if ( cellStyle )
5699 loadOasisConditional( const_cast<TQDomElement *>( cellStyle ) );
5700 }
5701
5702 if (style)
5703 {
5704 format()->setStyle( style );
5705 }
5706
5707 //Search and load each paragraph of text. Each paragraph is separated by a line break.
5708 loadOasisCellText( element );
5709
5710 //
5711 // formula
5712 //
5713 bool isFormula = false;
5714 if ( element.hasAttributeNS( KoXmlNS::table, "formula" ) )
5715 {
5716 kdDebug()<<" formula :"<<element.attributeNS( KoXmlNS::table, "formula", TQString() )<<endl;
5717 isFormula = true;
5718 TQString oasisFormula( element.attributeNS( KoXmlNS::table, "formula", TQString() ) );
5719 //necessary to remove it to load formula from oocalc2.0 (use namespace)
5720 if (oasisFormula.startsWith( "oooc:" ) )
5721 oasisFormula= oasisFormula.mid( 5 );
5722 else if (oasisFormula.startsWith( "kspr:" ) )
5723 oasisFormula= oasisFormula.mid( 5 );
5724 // TODO Stefan: merge this into Oasis::decodeFormula
5725 checkForNamedAreas( oasisFormula );
5726 oasisFormula = Oasis::decodeFormula( oasisFormula, locale() );
5727 setCellText( oasisFormula );
5728 }
5729 else if ( d->strText.at(0) == '=' ) //prepend ' to the text to avoid = to be painted
5730 d->strText.prepend('\'');
5731
5732 //
5733 // validation
5734 //
5735 if ( element.hasAttributeNS( KoXmlNS::table, "validation-name" ) )
5736 {
5737 kdDebug()<<" validation-name: "<<element.attributeNS( KoXmlNS::table, "validation-name", TQString() )<<endl;
5738 loadOasisValidation( element.attributeNS( KoXmlNS::table, "validation-name", TQString() ) );
5739 }
5740
5741 //
5742 // value type
5743 //
5744 if( element.hasAttributeNS( KoXmlNS::office, "value-type" ) )
5745 {
5746 TQString valuetype = element.attributeNS( KoXmlNS::office, "value-type", TQString() );
5747 kdDebug()<<" value-type: " << valuetype << endl;
5748 if( valuetype == "boolean" )
5749 {
5750 TQString val = element.attributeNS( KoXmlNS::office, "boolean-value", TQString() ).lower();
5751 if( ( val == "true" ) || ( val == "false" ) )
5752 {
5753 bool value = val == "true";
5755 }
5756 }
5757
5758 // integer and floating-point value
5759 else if( valuetype == "float" )
5760 {
5761 bool ok = false;
5762 double value = element.attributeNS( KoXmlNS::office, "value", TQString() ).toDouble( &ok );
5763 if( ok )
5765
5766 if ( !isFormula && d->strText.isEmpty())
5767 {
5768 TQString str = locale()->formatNumber( value, 15 );
5769 setCellText( str );
5770 }
5771 }
5772
5773 // currency value
5774 else if( valuetype == "currency" )
5775 {
5776 bool ok = false;
5777 double value = element.attributeNS( KoXmlNS::office, "value", TQString() ).toDouble( &ok );
5778 if( ok )
5779 {
5780 setCellValue( value, Money_format );
5781
5782 if (element.hasAttributeNS( KoXmlNS::office, "currency" ) )
5783 {
5784 Currency currency(element.attributeNS( KoXmlNS::office, "currency", TQString() ) );
5785 format()->setCurrency( currency.getIndex(), currency.getDisplayCode() );
5786 }
5787 }
5788 }
5789 else if( valuetype == "percentage" )
5790 {
5791 bool ok = false;
5792 double v = element.attributeNS( KoXmlNS::office, "value", TQString() ).toDouble( &ok );
5793 if( ok )
5794 {
5795 Value value;
5796 value.setValue (v);
5797 value.setFormat (Value::fmt_Percent);
5799
5800 if ( !isFormula && d->strText.isEmpty())
5801 {
5802 TQString str = locale()->formatNumber( v, 15 );
5803 setCellText( str );
5804 }
5805
5806 format()->setFormatType (Percentage_format);
5807 }
5808 }
5809 else if ( valuetype == "date" )
5810 {
5811 TQString value = element.attributeNS( KoXmlNS::office, "value", TQString() );
5812 if ( value.isEmpty() )
5813 value = element.attributeNS( KoXmlNS::office, "date-value", TQString() );
5814 kdDebug() << "Type: date, value: " << value << endl;
5815
5816 // "1980-10-15"
5817 int year = 0, month = 0, day = 0;
5818 bool ok = false;
5819
5820 int p1 = value.find( '-' );
5821 if ( p1 > 0 )
5822 year = value.left( p1 ).toInt( &ok );
5823
5824 kdDebug() << "year: " << value.left( p1 ) << endl;
5825
5826 int p2 = value.find( '-', ++p1 );
5827
5828 if ( ok )
5829 month = value.mid( p1, p2 - p1 ).toInt( &ok );
5830
5831 kdDebug() << "month: " << value.mid( p1, p2 - p1 ) << endl;
5832
5833 if ( ok )
5834 day = value.right( value.length() - p2 - 1 ).toInt( &ok );
5835
5836 kdDebug() << "day: " << value.right( value.length() - p2 ) << endl;
5837
5838 if ( ok )
5839 {
5840 setCellValue( TQDate( year, month, day ) );
5841 if ( style )
5842 format()->setFormatType (style->formatType());
5843 kdDebug() << "Set TQDate: " << year << " - " << month << " - " << day << endl;
5844 }
5845
5846 }
5847 else if ( valuetype == "time" )
5848 {
5849 TQString value = element.attributeNS( KoXmlNS::office, "value", TQString() );
5850 if ( value.isEmpty() )
5851 value = element.attributeNS( KoXmlNS::office, "time-value", TQString() );
5852 kdDebug() << "Type: time: " << value << endl;
5853 // "PT15H10M12S"
5854 int hours = 0, minutes = 0, seconds = 0;
5855 int l = value.length();
5856 TQString num;
5857 bool ok = false;
5858 for ( int i = 0; i < l; ++i )
5859 {
5860 if ( value[i].isNumber() )
5861 {
5862 num += value[i];
5863 continue;
5864 }
5865 else if ( value[i] == 'H' )
5866 hours = num.toInt( &ok );
5867 else if ( value[i] == 'M' )
5868 minutes = num.toInt( &ok );
5869 else if ( value[i] == 'S' )
5870 seconds = num.toInt( &ok );
5871 else
5872 continue;
5873
5874 kdDebug() << "Num: " << num << endl;
5875
5876 num = "";
5877 if ( !ok )
5878 break;
5879 }
5880 kdDebug() << "Hours: " << hours << ", " << minutes << ", " << seconds << endl;
5881
5882 if ( ok )
5883 {
5884 // Value kval( timeToNum( hours, minutes, seconds ) );
5885 // cell->setValue( kval );
5886 setCellValue( TQTime( hours % 24, minutes, seconds ) );
5887 if ( style )
5888 format()->setFormatType (style->formatType());
5889 }
5890 }
5891 else if( valuetype == "string" )
5892 {
5893 TQString value = element.attributeNS( KoXmlNS::office, "value", TQString() );
5894 if ( value.isEmpty() && element.hasAttributeNS( KoXmlNS::office, "string-value" ))
5895 {
5896 //if there is not string-value entry don't overwrite value stored into <text:p>
5897 value = element.attributeNS( KoXmlNS::office, "string-value", TQString() );
5899 }
5900 format()->setFormatType (Text_format);
5901 }
5902 else
5903 kdDebug()<<" type of value found : "<<valuetype<<endl;
5904 }
5905
5906 //
5907 // merged cells ?
5908 //
5909 int colSpan = 1;
5910 int rowSpan = 1;
5911 if ( element.hasAttributeNS( KoXmlNS::table, "number-columns-spanned" ) )
5912 {
5913 bool ok = false;
5914 int span = element.attributeNS( KoXmlNS::table, "number-columns-spanned", TQString() ).toInt( &ok );
5915 if( ok ) colSpan = span;
5916 }
5917 if ( element.hasAttributeNS( KoXmlNS::table, "number-rows-spanned" ) )
5918 {
5919 bool ok = false;
5920 int span = element.attributeNS( KoXmlNS::table, "number-rows-spanned", TQString() ).toInt( &ok );
5921 if( ok ) rowSpan = span;
5922 }
5923 if ( colSpan > 1 || rowSpan > 1 )
5924 mergeCells( d->column, d->row, colSpan - 1, rowSpan - 1 );
5925
5926 //
5927 // cell comment/annotation
5928 //
5929 TQDomElement annotationElement = KoDom::namedItemNS( element, KoXmlNS::office, "annotation" );
5930 if ( !annotationElement.isNull() )
5931 {
5932 TQString comment;
5933 TQDomNode node = annotationElement.firstChild();
5934 while( !node.isNull() )
5935 {
5936 TQDomElement commentElement = node.toElement();
5937 if( !commentElement.isNull() )
5938 if( commentElement.localName() == "p" && commentElement.namespaceURI() == KoXmlNS::text )
5939 {
5940 if( !comment.isEmpty() ) comment.append( '\n' );
5941 comment.append( commentElement.text() );
5942 }
5943
5944 node = node.nextSibling();
5945 }
5946
5947 if( !comment.isEmpty() )
5948 format()->setComment( comment );
5949 }
5950
5951 TQDomElement frame = KoDom::namedItemNS( element, KoXmlNS::draw, "frame" );
5952 if ( !frame.isNull() )
5953 loadOasisObjects( frame, oasisContext );
5954
5955 if (isFormula)
5956 setCalcDirtyFlag (); // formulas must be recalculated
5957
5958 return true;
5959}
5960
5961void Cell::loadOasisCellText( const TQDomElement& parent )
5962{
5963 //Search and load each paragraph of text. Each paragraph is separated by a line break
5964 TQDomElement textParagraphElement;
5965 TQString cellText;
5966
5967 bool multipleTextParagraphsFound=false;
5968
5969 forEachElement( textParagraphElement , parent )
5970 {
5971 if ( textParagraphElement.localName()=="p" &&
5972 textParagraphElement.namespaceURI()== KoXmlNS::text )
5973 {
5974 // our text, could contain formating for value or result of formul
5975 if (cellText.isEmpty())
5976 cellText = textParagraphElement.text();
5977 else
5978 {
5979 cellText += "\n"+textParagraphElement.text();
5980 multipleTextParagraphsFound=true;
5981 }
5982
5983 TQDomElement textA = KoDom::namedItemNS( textParagraphElement, KoXmlNS::text, "a" );
5984 if( !textA.isNull() )
5985 {
5986 if ( textA.hasAttributeNS( KoXmlNS::xlink, "href" ) )
5987 {
5988 TQString link = textA.attributeNS( KoXmlNS::xlink, "href", TQString() );
5989 cellText = textA.text();
5990 setCellText( cellText );
5991 setValue( cellText );
5992 if ( link[0]=='#' )
5993 link=link.remove( 0, 1 );
5994 setLink( link );
5995 }
5996 }
5997 }
5998 }
5999
6000 if (!cellText.isNull())
6001 {
6002 setCellText( cellText );
6003 setValue( cellText );
6004 }
6005
6006 //Enable word wrapping if multiple lines of text have been found.
6007 if ( multipleTextParagraphsFound )
6008 {
6009 format()->setMultiRow(true);
6010 }
6011}
6012
6013void Cell::loadOasisObjects( const TQDomElement &parent, KoOasisLoadingContext& oasisContext )
6014{
6015 for( TQDomElement e = parent; !e.isNull(); e = e.nextSibling().toElement() )
6016 {
6017 if ( e.localName() == "frame" && e.namespaceURI() == KoXmlNS::draw )
6018 {
6019 EmbeddedObject *obj = 0;
6020 TQDomNode object = KoDom::namedItemNS( e, KoXmlNS::draw, "object" );
6021 if ( !object.isNull() )
6022 {
6023 if ( !object.toElement().attributeNS( KoXmlNS::draw, "notify-on-update-of-ranges", TQString()).isNull() )
6024 obj = new EmbeddedChart( sheet()->doc(), sheet() );
6025 else
6026 obj = new EmbeddedKOfficeObject( sheet()->doc(), sheet() );
6027 }
6028 else
6029 {
6030 TQDomNode image = KoDom::namedItemNS( e, KoXmlNS::draw, "image" );
6031 if ( !image.isNull() )
6032 obj = new EmbeddedPictureObject( sheet(), sheet()->doc()->pictureCollection() );
6033 else
6034 kdDebug() << "Object type wasn't loaded!" << endl;
6035 }
6036
6037 if ( obj )
6038 {
6039 obj->loadOasis( e, oasisContext );
6040 sheet()->doc()->insertObject( obj );
6041
6042 TQString ref = e.attributeNS( KoXmlNS::table, "end-cell-address", TQString() );
6043 if ( ref.isNull() )
6044 continue;
6045
6046 ref = Oasis::decodeFormula( ref );
6047 Point point( ref );
6048 if ( !point.isValid() )
6049 continue;
6050
6051 KoRect geometry = obj->geometry();
6052 geometry.setLeft( geometry.left() + sheet()->columnPos( d->column, 0 ) );
6053 geometry.setTop( geometry.top() + sheet()->rowPos( d->row, 0 ) );
6054
6055 TQString str = e.attributeNS( KoXmlNS::table, "end-x", TQString() );
6056 if ( !str.isNull() )
6057 {
6058 uint end_x = (uint) KoUnit::parseValue( str );
6059 geometry.setRight( sheet()->columnPos( point.column(), 0) + end_x );
6060 }
6061
6062 str = e.attributeNS( KoXmlNS::table, "end-y", TQString() );
6063 if ( !str.isNull() )
6064 {
6065 uint end_y = (uint) KoUnit::parseValue( str );
6066 geometry.setBottom( sheet()->rowPos( point.row(), 0) + end_y );
6067 }
6068
6069 obj->setGeometry( geometry );
6070 }
6071 }
6072 }
6073}
6074
6075void Cell::loadOasisValidation( const TQString& validationName )
6076{
6077 TQDomElement element = sheet()->doc()->loadingInfo()->validation( validationName);
6078 if (d->hasExtra())
6079 delete d->extra()->validity;
6080 d->extra()->validity = new Validity;
6081 if ( element.hasAttributeNS( KoXmlNS::table, "condition" ) )
6082 {
6083 TQString valExpression = element.attributeNS( KoXmlNS::table, "condition", TQString() );
6084 kdDebug()<<" element.attribute( table:condition ) "<<valExpression<<endl;
6085 //Condition ::= ExtendedTrueCondition | TrueFunction 'and' TrueCondition
6086 //TrueFunction ::= cell-content-is-whole-number() | cell-content-is-decimal-number() | cell-content-is-date() | cell-content-is-time()
6087 //ExtendedTrueCondition ::= ExtendedGetFunction | cell-content-text-length() Operator Value
6088 //TrueCondition ::= GetFunction | cell-content() Operator Value
6089 //GetFunction ::= cell-content-is-between(Value, Value) | cell-content-is-not-between(Value, Value)
6090 //ExtendedGetFunction ::= cell-content-text-length-is-between(Value, Value) | cell-content-text-length-is-not-between(Value, Value)
6091 //Operator ::= '<' | '>' | '<=' | '>=' | '=' | '!='
6092 //Value ::= NumberValue | String | Formula
6093 //A Formula is a formula without an equals (=) sign at the beginning. See section 8.1.3 for more information.
6094 //A String comprises one or more characters surrounded by quotation marks.
6095 //A NumberValue is a whole or decimal number. It must not contain comma separators for numbers of 1000 or greater.
6096
6097 //ExtendedTrueCondition
6098 if ( valExpression.contains( "cell-content-text-length()" ) )
6099 {
6100 //"cell-content-text-length()>45"
6101 valExpression = valExpression.remove("oooc:cell-content-text-length()" );
6102 kdDebug()<<" valExpression = :"<<valExpression<<endl;
6103 d->extra()->validity->m_restriction = Restriction::TextLength;
6104
6105 loadOasisValidationCondition( valExpression );
6106 }
6107 else if ( valExpression.contains( "cell-content-is-text()" ) )
6108 {
6109 d->extra()->validity->m_restriction = Restriction::Text;
6110 }
6111 //cell-content-text-length-is-between(Value, Value) | cell-content-text-length-is-not-between(Value, Value) | cell-content-is-in-list( StringList )
6112 else if ( valExpression.contains( "cell-content-text-length-is-between" ) )
6113 {
6114 d->extra()->validity->m_restriction = Restriction::TextLength;
6115 d->extra()->validity->m_cond = Conditional::Between;
6116 valExpression = valExpression.remove( "oooc:cell-content-text-length-is-between(" );
6117 kdDebug()<<" valExpression :"<<valExpression<<endl;
6118 valExpression = valExpression.remove( ")" );
6119 TQStringList listVal = TQStringList::split( ",", valExpression );
6120 loadOasisValidationValue( listVal );
6121 }
6122 else if ( valExpression.contains( "cell-content-text-length-is-not-between" ) )
6123 {
6124 d->extra()->validity->m_restriction = Restriction::TextLength;
6125 d->extra()->validity->m_cond = Conditional::Different;
6126 valExpression = valExpression.remove( "oooc:cell-content-text-length-is-not-between(" );
6127 kdDebug()<<" valExpression :"<<valExpression<<endl;
6128 valExpression = valExpression.remove( ")" );
6129 kdDebug()<<" valExpression :"<<valExpression<<endl;
6130 TQStringList listVal = TQStringList::split( ",", valExpression );
6131 loadOasisValidationValue( listVal );
6132 }
6133 else if ( valExpression.contains( "cell-content-is-in-list(" ) )
6134 {
6135 d->extra()->validity->m_restriction = Restriction::List;
6136 valExpression = valExpression.remove( "oooc:cell-content-is-in-list(" );
6137 kdDebug()<<" valExpression :"<<valExpression<<endl;
6138 valExpression = valExpression.remove( ")" );
6139 d->extra()->validity->listValidity = TQStringList::split( ";", valExpression );
6140
6141 }
6142 //TrueFunction ::= cell-content-is-whole-number() | cell-content-is-decimal-number() | cell-content-is-date() | cell-content-is-time()
6143 else
6144 {
6145 if (valExpression.contains( "cell-content-is-whole-number()" ) )
6146 {
6147 d->extra()->validity->m_restriction = Restriction::Number;
6148 valExpression = valExpression.remove( "oooc:cell-content-is-whole-number() and " );
6149 }
6150 else if (valExpression.contains( "cell-content-is-decimal-number()" ) )
6151 {
6152 d->extra()->validity->m_restriction = Restriction::Integer;
6153 valExpression = valExpression.remove( "oooc:cell-content-is-decimal-number() and " );
6154 }
6155 else if (valExpression.contains( "cell-content-is-date()" ) )
6156 {
6157 d->extra()->validity->m_restriction = Restriction::Date;
6158 valExpression = valExpression.remove( "oooc:cell-content-is-date() and " );
6159 }
6160 else if (valExpression.contains( "cell-content-is-time()" ) )
6161 {
6162 d->extra()->validity->m_restriction = Restriction::Time;
6163 valExpression = valExpression.remove( "oooc:cell-content-is-time() and " );
6164 }
6165 kdDebug()<<"valExpression :"<<valExpression<<endl;
6166
6167 if ( valExpression.contains( "cell-content()" ) )
6168 {
6169 valExpression = valExpression.remove( "cell-content()" );
6170 loadOasisValidationCondition( valExpression );
6171 }
6172 //GetFunction ::= cell-content-is-between(Value, Value) | cell-content-is-not-between(Value, Value)
6173 //for the moment we support just int/double value, not text/date/time :(
6174 if ( valExpression.contains( "cell-content-is-between(" ) )
6175 {
6176 valExpression = valExpression.remove( "cell-content-is-between(" );
6177 valExpression = valExpression.remove( ")" );
6178 TQStringList listVal = TQStringList::split( "," , valExpression );
6179 loadOasisValidationValue( listVal );
6180 d->extra()->validity->m_cond = Conditional::Between;
6181 }
6182 if ( valExpression.contains( "cell-content-is-not-between(" ) )
6183 {
6184 valExpression = valExpression.remove( "cell-content-is-not-between(" );
6185 valExpression = valExpression.remove( ")" );
6186 TQStringList listVal = TQStringList::split( ",", valExpression );
6187 loadOasisValidationValue( listVal );
6188 d->extra()->validity->m_cond = Conditional::Different;
6189 }
6190 }
6191 }
6192 if ( element.hasAttributeNS( KoXmlNS::table, "allow-empty-cell" ) )
6193 {
6194 kdDebug()<<" element.hasAttribute( table:allow-empty-cell ) :"<<element.hasAttributeNS( KoXmlNS::table, "allow-empty-cell" )<<endl;
6195 d->extra()->validity->allowEmptyCell = ( ( element.attributeNS( KoXmlNS::table, "allow-empty-cell", TQString() )=="true" ) ? true : false );
6196 }
6197 if ( element.hasAttributeNS( KoXmlNS::table, "base-cell-address" ) )
6198 {
6199 //todo what is it ?
6200 }
6201
6202 TQDomElement help = KoDom::namedItemNS( element, KoXmlNS::table, "help-message" );
6203 if ( !help.isNull() )
6204 {
6205 if ( help.hasAttributeNS( KoXmlNS::table, "title" ) )
6206 {
6207 kdDebug()<<"help.attribute( table:title ) :"<<help.attributeNS( KoXmlNS::table, "title", TQString() )<<endl;
6208 d->extra()->validity->titleInfo = help.attributeNS( KoXmlNS::table, "title", TQString() );
6209 }
6210 if ( help.hasAttributeNS( KoXmlNS::table, "display" ) )
6211 {
6212 kdDebug()<<"help.attribute( table:display ) :"<<help.attributeNS( KoXmlNS::table, "display", TQString() )<<endl;
6213 d->extra()->validity->displayValidationInformation = ( ( help.attributeNS( KoXmlNS::table, "display", TQString() )=="true" ) ? true : false );
6214 }
6215 TQDomElement attrText = KoDom::namedItemNS( help, KoXmlNS::text, "p" );
6216 if ( !attrText.isNull() )
6217 {
6218 kdDebug()<<"help text :"<<attrText.text()<<endl;
6219 d->extra()->validity->messageInfo = attrText.text();
6220 }
6221 }
6222
6223 TQDomElement error = KoDom::namedItemNS( element, KoXmlNS::table, "error-message" );
6224 if ( !error.isNull() )
6225 {
6226 if ( error.hasAttributeNS( KoXmlNS::table, "title" ) )
6227 d->extra()->validity->title = error.attributeNS( KoXmlNS::table, "title", TQString() );
6228 if ( error.hasAttributeNS( KoXmlNS::table, "message-type" ) )
6229 {
6230 TQString str = error.attributeNS( KoXmlNS::table, "message-type", TQString() );
6231 if ( str == "warning" )
6232 d->extra()->validity->m_action = Action::Warning;
6233 else if ( str == "information" )
6234 d->extra()->validity->m_action = Action::Information;
6235 else if ( str == "stop" )
6236 d->extra()->validity->m_action = Action::Stop;
6237 else
6238 kdDebug()<<"validation : message type unknown :"<<str<<endl;
6239 }
6240
6241 if ( error.hasAttributeNS( KoXmlNS::table, "display" ) )
6242 {
6243 kdDebug()<<" display message :"<<error.attributeNS( KoXmlNS::table, "display", TQString() )<<endl;
6244 d->extra()->validity->displayMessage = (error.attributeNS( KoXmlNS::table, "display", TQString() )=="true");
6245 }
6246 TQDomElement attrText = KoDom::namedItemNS( error, KoXmlNS::text, "p" );
6247 if ( !attrText.isNull() )
6248 d->extra()->validity->message = attrText.text();
6249 }
6250}
6251
6252
6253void Cell::loadOasisValidationValue( const TQStringList &listVal )
6254{
6255 bool ok = false;
6256 kdDebug()<<" listVal[0] :"<<listVal[0]<<" listVal[1] :"<<listVal[1]<<endl;
6257
6258 if ( d->extra()->validity->m_restriction == Restriction::Date )
6259 {
6260 d->extra()->validity->dateMin = TQDate::fromString( listVal[0] );
6261 d->extra()->validity->dateMax = TQDate::fromString( listVal[1] );
6262 }
6263 else if ( d->extra()->validity->m_restriction == Restriction::Time )
6264 {
6265 d->extra()->validity->timeMin = TQTime::fromString( listVal[0] );
6266 d->extra()->validity->timeMax = TQTime::fromString( listVal[1] );
6267 }
6268 else
6269 {
6270 d->extra()->validity->valMin = listVal[0].toDouble(&ok);
6271 if ( !ok )
6272 {
6273 d->extra()->validity->valMin = listVal[0].toInt(&ok);
6274 if ( !ok )
6275 kdDebug()<<" Try to parse this value :"<<listVal[0]<<endl;
6276
6277#if 0
6278 if ( !ok )
6279 d->extra()->validity->valMin = listVal[0];
6280#endif
6281 }
6282 ok=false;
6283 d->extra()->validity->valMax = listVal[1].toDouble(&ok);
6284 if ( !ok )
6285 {
6286 d->extra()->validity->valMax = listVal[1].toInt(&ok);
6287 if ( !ok )
6288 kdDebug()<<" Try to parse this value :"<<listVal[1]<<endl;
6289
6290#if 0
6291 if ( !ok )
6292 d->extra()->validity->valMax = listVal[1];
6293#endif
6294 }
6295 }
6296}
6297
6298void Cell::loadOasisValidationCondition( TQString &valExpression )
6299{
6300 TQString value;
6301 if (valExpression.find( "<=" )==0 )
6302 {
6303 value = valExpression.remove( 0,2 );
6304 d->extra()->validity->m_cond = Conditional::InferiorEqual;
6305 }
6306 else if (valExpression.find( ">=" )==0 )
6307 {
6308 value = valExpression.remove( 0,2 );
6309 d->extra()->validity->m_cond = Conditional::SuperiorEqual;
6310 }
6311 else if (valExpression.find( "!=" )==0 )
6312 {
6313 //add Differentto attribute
6314 value = valExpression.remove( 0,2 );
6315 d->extra()->validity->m_cond = Conditional::DifferentTo;
6316 }
6317 else if ( valExpression.find( "<" )==0 )
6318 {
6319 value = valExpression.remove( 0,1 );
6320 d->extra()->validity->m_cond = Conditional::Inferior;
6321 }
6322 else if(valExpression.find( ">" )==0 )
6323 {
6324 value = valExpression.remove( 0,1 );
6325 d->extra()->validity->m_cond = Conditional::Superior;
6326 }
6327 else if (valExpression.find( "=" )==0 )
6328 {
6329 value = valExpression.remove( 0,1 );
6330 d->extra()->validity->m_cond = Conditional::Equal;
6331 }
6332 else
6333 kdDebug()<<" I don't know how to parse it :"<<valExpression<<endl;
6334 if ( d->extra()->validity->m_restriction == Restriction::Date )
6335 {
6336 d->extra()->validity->dateMin = TQDate::fromString( value );
6337 }
6338 else if (d->extra()->validity->m_restriction == Restriction::Date )
6339 {
6340 d->extra()->validity->timeMin = TQTime::fromString( value );
6341 }
6342 else
6343 {
6344 bool ok = false;
6345 d->extra()->validity->valMin = value.toDouble(&ok);
6346 if ( !ok )
6347 {
6348 d->extra()->validity->valMin = value.toInt(&ok);
6349 if ( !ok )
6350 kdDebug()<<" Try to parse this value :"<<value<<endl;
6351
6352#if 0
6353 if ( !ok )
6354 d->extra()->validity->valMin = value;
6355#endif
6356 }
6357 }
6358}
6359
6360
6361bool Cell::load( const TQDomElement & cell, int _xshift, int _yshift,
6362 Paste::Mode pm, Paste::Operation op, bool paste )
6363{
6364 bool ok;
6365
6366 //
6367 // First of all determine in which row and column this
6368 // cell belongs.
6369 //
6370 d->row = cell.attribute( "row" ).toInt( &ok ) + _yshift;
6371 if ( !ok ) return false;
6372 d->column = cell.attribute( "column" ).toInt( &ok ) + _xshift;
6373 if ( !ok ) return false;
6374
6375 // Validation
6376 if ( d->row < 1 || d->row > KS_rowMax )
6377 {
6378 kdDebug(36001) << "Cell::load: Value out of Range Cell:row=" << d->row << endl;
6379 return false;
6380 }
6381 if ( d->column < 1 || d->column > KS_colMax )
6382 {
6383 kdDebug(36001) << "Cell::load: Value out of Range Cell:column=" << d->column << endl;
6384 return false;
6385 }
6386
6387 //
6388 // Load formatting information.
6389 //
6390 TQDomElement f = cell.namedItem( "format" ).toElement();
6391 if ( !f.isNull()
6392 && ( (pm == Paste::Normal) || (pm == Paste::Format) || (pm == Paste::NoBorder) ) )
6393 {
6394 // send pm parameter. Didn't load Borders if pm==NoBorder
6395
6396 if ( !format()->load( f, pm, paste ) )
6397 return false;
6398
6399 if ( f.hasAttribute( "colspan" ) )
6400 {
6401 int i = f.attribute("colspan").toInt( &ok );
6402 if ( !ok ) return false;
6403 // Validation
6404 if ( i < 0 || i > KS_spanMax )
6405 {
6406 kdDebug(36001) << "Value out of range Cell::colspan=" << i << endl;
6407 return false;
6408 }
6409 if (i || d->hasExtra())
6410 d->extra()->extraXCells = i;
6411 if ( i > 0 )
6412 {
6413 setFlag(Flag_Merged);
6414 }
6415 }
6416
6417 if ( f.hasAttribute( "rowspan" ) )
6418 {
6419 int i = f.attribute("rowspan").toInt( &ok );
6420 if ( !ok ) return false;
6421 // Validation
6422 if ( i < 0 || i > KS_spanMax )
6423 {
6424 kdDebug(36001) << "Value out of range Cell::rowspan=" << i << endl;
6425 return false;
6426 }
6427 if (i || d->hasExtra())
6428 d->extra()->extraYCells = i;
6429 if ( i > 0 )
6430 {
6431 setFlag(Flag_Merged);
6432 }
6433 }
6434
6435 if ( testFlag( Flag_Merged ) )
6436 {
6437 if (d->hasExtra())
6438 mergeCells( d->column, d->row, d->extra()->extraXCells, d->extra()->extraYCells );
6439 }
6440
6441 }
6442
6443 //
6444 // Load the condition section of a cell.
6445 //
6446 TQDomElement conditionsElement = cell.namedItem( "condition" ).toElement();
6447 if ( !conditionsElement.isNull())
6448 {
6449 if (d->hasExtra())
6450 delete d->extra()->conditions;
6451 d->extra()->conditions = new Conditions( this );
6452 d->extra()->conditions->loadConditions( conditionsElement );
6453 d->extra()->conditions->checkMatches();
6454 }
6455 else if ((pm == Paste::Normal) || (pm == Paste::NoBorder))
6456 {
6457 //clear the conditional formatting
6458 if (d->hasExtra())
6459 {
6460 delete d->extra()->conditions;
6461 d->extra()->conditions = 0;
6462 }
6463 }
6464
6465 TQDomElement validity = cell.namedItem( "validity" ).toElement();
6466 if ( !validity.isNull())
6467 {
6468 TQDomElement param = validity.namedItem( "param" ).toElement();
6469 if(!param.isNull())
6470 {
6471 d->extra()->validity = new Validity;
6472 if ( param.hasAttribute( "cond" ) )
6473 {
6474 d->extra()->validity->m_cond = (Conditional::Type) param.attribute("cond").toInt( &ok );
6475 if ( !ok )
6476 return false;
6477 }
6478 if ( param.hasAttribute( "action" ) )
6479 {
6480 d->extra()->validity->m_action = (Action::Type) param.attribute("action").toInt( &ok );
6481 if ( !ok )
6482 return false;
6483 }
6484 if ( param.hasAttribute( "allow" ) )
6485 {
6486 d->extra()->validity->m_restriction = (Restriction::Type) param.attribute("allow").toInt( &ok );
6487 if ( !ok )
6488 return false;
6489 }
6490 if ( param.hasAttribute( "valmin" ) )
6491 {
6492 d->extra()->validity->valMin = param.attribute("valmin").toDouble( &ok );
6493 if ( !ok )
6494 return false;
6495 }
6496 if ( param.hasAttribute( "valmax" ) )
6497 {
6498 d->extra()->validity->valMax = param.attribute("valmax").toDouble( &ok );
6499 if ( !ok )
6500 return false;
6501 }
6502 if ( param.hasAttribute( "displaymessage" ) )
6503 {
6504 d->extra()->validity->displayMessage = ( bool )param.attribute("displaymessage").toInt();
6505 }
6506 if ( param.hasAttribute( "displayvalidationinformation" ) )
6507 {
6508 d->extra()->validity->displayValidationInformation = ( bool )param.attribute("displayvalidationinformation").toInt();
6509 }
6510 if ( param.hasAttribute( "allowemptycell" ) )
6511 {
6512 d->extra()->validity->allowEmptyCell = ( bool )param.attribute("allowemptycell").toInt();
6513 }
6514 if ( param.hasAttribute("listvalidity") )
6515 {
6516 d->extra()->validity->listValidity=TQStringList::split(";", param.attribute("listvalidity") );
6517 }
6518 }
6519 TQDomElement inputTitle = validity.namedItem( "inputtitle" ).toElement();
6520 if (!inputTitle.isNull())
6521 {
6522 d->extra()->validity->titleInfo = inputTitle.text();
6523 }
6524 TQDomElement inputMessage = validity.namedItem( "inputmessage" ).toElement();
6525 if (!inputMessage.isNull())
6526 {
6527 d->extra()->validity->messageInfo = inputMessage.text();
6528 }
6529
6530 TQDomElement title = validity.namedItem( "title" ).toElement();
6531 if (!title.isNull())
6532 {
6533 d->extra()->validity->title = title.text();
6534 }
6535 TQDomElement message = validity.namedItem( "message" ).toElement();
6536 if (!message.isNull())
6537 {
6538 d->extra()->validity->message = message.text();
6539 }
6540 TQDomElement timeMin = validity.namedItem( "timemin" ).toElement();
6541 if ( !timeMin.isNull() )
6542 {
6543 d->extra()->validity->timeMin = toTime(timeMin);
6544 }
6545 TQDomElement timeMax = validity.namedItem( "timemax" ).toElement();
6546 if ( !timeMax.isNull() )
6547 {
6548 d->extra()->validity->timeMax = toTime(timeMax);
6549 }
6550 TQDomElement dateMin = validity.namedItem( "datemin" ).toElement();
6551 if ( !dateMin.isNull() )
6552 {
6553 d->extra()->validity->dateMin = toDate(dateMin);
6554 }
6555 TQDomElement dateMax = validity.namedItem( "datemax" ).toElement();
6556 if ( !dateMax.isNull() )
6557 {
6558 d->extra()->validity->dateMax = toDate(dateMax);
6559 }
6560 }
6561 else if ((pm == Paste::Normal) || (pm == Paste::NoBorder))
6562 {
6563 // clear the validity
6564 removeValidity();
6565 }
6566
6567 //
6568 // Load the comment
6569 //
6570 TQDomElement comment = cell.namedItem( "comment" ).toElement();
6571 if ( !comment.isNull() && ( pm == Paste::Normal || pm == Paste::Comment || pm == Paste::NoBorder ))
6572 {
6573 TQString t = comment.text();
6574 //t = t.stripWhiteSpace();
6575 format()->setComment( t );
6576 }
6577
6578 //
6579 // The real content of the cell is loaded here. It is stored in
6580 // the "text" tag, which contains either a text or a CDATA section.
6581 //
6582 // TODO: make this suck less. We set data twice, in loadCellData, and
6583 // also here. Not good.
6584 TQDomElement text = cell.namedItem( "text" ).toElement();
6585
6586 if ( !text.isNull() &&
6587 ( pm == Paste::Normal || pm == Paste::Text || pm == Paste::NoBorder || pm == Paste::Result ) )
6588 {
6589 /* older versions mistakenly put the datatype attribute on the cell
6590 instead of the text. Just move it over in case we're parsing
6591 an old document */
6592 if ( cell.hasAttribute( "dataType" ) ) // new docs
6593 text.setAttribute( "dataType", cell.attribute( "dataType" ) );
6594
6595 TQDomElement result = cell.namedItem( "result" ).toElement();
6596 TQString txt = text.text();
6597 if ((pm == Paste::Result) && (txt[0] == '='))
6598 // paste text of the element, if we want to paste result
6599 // and the source cell contains a formula
6600 // note that we mustn't use setCellValue after this, or else we lose
6601 // all the formulas ...
6602 d->strText = result.text();
6603 else
6604 //otherwise copy everything
6605 loadCellData(text, op);
6606
6607 if ( !result.isNull() )
6608 {
6609 TQString dataType;
6610 TQString t = result.text();
6611
6612 if ( result.hasAttribute( "dataType" ) )
6613 dataType = result.attribute( "dataType" );
6614 if ( result.hasAttribute( "outStr" ) )
6615 {
6616 d->strOutText = result.attribute( "outStr" );
6617 if ( !d->strOutText.isEmpty() )
6618 clearFlag( Flag_TextFormatDirty );
6619 }
6620
6621 bool clear = true;
6622 // boolean ?
6623 if( dataType == "Bool" )
6624 {
6625 if ( t == "false" )
6626 setValue( false );
6627 else if ( t == "true" )
6628 setValue( true );
6629 else
6630 clear = false;
6631 }
6632 else if( dataType == "Num" )
6633 {
6634 bool ok = false;
6635 double dd = t.toDouble( &ok );
6636 if ( ok )
6637 setValue ( dd );
6638 else
6639 clear = false;
6640 }
6641 else if( dataType == "Date" )
6642 {
6643 bool ok = false;
6644 double dd = t.toDouble( &ok );
6645 if ( ok )
6646 setValue ( dd );
6647 else
6648 {
6649 int pos = t.find( '/' );
6650 int year = t.mid( 0, pos ).toInt();
6651 int pos1 = t.find( '/', pos + 1 );
6652 int month = t.mid( pos + 1, ( ( pos1 - 1 ) - pos ) ).toInt();
6653 int day = t.right( t.length() - pos1 - 1 ).toInt();
6654 TQDate date( year, month, day );
6655 if ( date.isValid() )
6656 setValue( date );
6657 else
6658 clear = false;
6659 }
6660 }
6661 else if( dataType == "Time" )
6662 {
6663 bool ok = false;
6664 double dd = t.toDouble( &ok );
6665 if ( ok )
6666 setCellValue( dd );
6667 else
6668 {
6669 int hours = -1;
6670 int minutes = -1;
6671 int second = -1;
6672 int pos, pos1;
6673 pos = t.find( ':' );
6674 hours = t.mid( 0, pos ).toInt();
6675 pos1 = t.find( ':', pos + 1 );
6676 minutes = t.mid( pos + 1, ( ( pos1 - 1 ) - pos ) ).toInt();
6677 second = t.right( t.length() - pos1 - 1 ).toInt();
6678 TQTime time( hours, minutes, second );
6679 if ( time.isValid() )
6680 setValue( time );
6681 else
6682 clear = false;
6683 }
6684 }
6685 else
6686 {
6687 setValue( t );
6688 }
6689
6690 // if ( clear )
6691 // clearFlag( Flag_CalcDirty );
6692 }
6693 }
6694
6695 return true;
6696}
6697
6698bool Cell::loadCellData(const TQDomElement & text, Paste::Operation op )
6699{
6700 //TODO: use converter()->asString() to generate strText
6701
6702 TQString t = text.text();
6703 t = t.stripWhiteSpace();
6704
6705 setFlag(Flag_LayoutDirty);
6706 setFlag(Flag_TextFormatDirty);
6707
6708 // A formula like =A1+A2 ?
6709 if( t[0] == '=' )
6710 {
6711 t = decodeFormula( t, d->column, d->row );
6712 setCellText (pasteOperation( t, d->strText, op ));
6713
6714 setFlag(Flag_CalcDirty);
6716
6717 if ( !makeFormula() )
6718 kdError(36001) << "ERROR: Syntax ERROR" << endl;
6719 }
6720 // rich text ?
6721 else if (t[0] == '!' )
6722 {
6723 // KSpread pre 1.4 stores hyperlink as rich text (first char is '!')
6724 // extract the link and the correspoding text
6725 // This is a rather dirty hack, but enough for KSpread generated XML
6726 bool inside_tag = false;
6727 TQString qml_text;
6728 TQString tag;
6729 TQString qml_link;
6730
6731 for( unsigned i = 1; i < t.length(); i++ )
6732 {
6733 TQChar ch = t[i];
6734 if( ch == '<' )
6735 {
6736 if( !inside_tag )
6737 {
6738 inside_tag = true;
6739 tag = TQString();
6740 }
6741 }
6742 else if( ch == '>' )
6743 {
6744 if( inside_tag )
6745 {
6746 inside_tag = false;
6747 if( tag.startsWith( "a href=\"", true ) )
6748 if( tag.endsWith( "\"" ) )
6749 qml_link = tag.mid( 8, tag.length()-9 );
6750 tag = TQString();
6751 }
6752 }
6753 else
6754 {
6755 if( !inside_tag )
6756 qml_text += ch;
6757 else
6758 tag += ch;
6759 }
6760 }
6761
6762 if( !qml_link.isEmpty() )
6763 d->extra()->link = qml_link;
6764 d->strText = qml_text;
6765 setValue( d->strText );
6766 }
6767 else
6768 {
6769 bool newStyleLoading = true;
6770 TQString dataType;
6771
6772 if ( text.hasAttribute( "dataType" ) ) // new docs
6773 {
6774 dataType = text.attribute( "dataType" );
6775 }
6776 else // old docs: do the ugly solution of calling checkTextInput to parse the text
6777 {
6778 // ...except for date/time
6779 if (isDate() && ( t.contains('/') == 2 ))
6780 dataType = "Date";
6781 else if (isTime() && ( t.contains(':') == 2 ) )
6782 dataType = "Time";
6783 else
6784 {
6785 d->strText = pasteOperation( t, d->strText, op );
6787 //kdDebug(36001) << "Cell::load called checkTextInput, got dataType=" << dataType << " t=" << t << endl;
6788 newStyleLoading = false;
6789 }
6790 }
6791
6792 if ( newStyleLoading )
6793 {
6794 d->value = Value::empty();
6796
6797 // boolean ?
6798 if( dataType == "Bool" )
6799 {
6800 bool val = (t.lower() == "true");
6801 setCellValue (val);
6802 }
6803
6804 // number ?
6805 else if( dataType == "Num" )
6806 {
6807 bool ok = false;
6808 if (t.contains('.'))
6809 setValue ( Value( t.toDouble(&ok) ) ); // We save in non-localized format
6810 else
6811 setValue ( Value( t.toLong(&ok) ) );
6812 if ( !ok )
6813 {
6814 kdWarning(36001) << "Couldn't parse '" << t << "' as number." << endl;
6815 }
6816 /* We will need to localize the text version of the number */
6817 TDELocale* locale = format()->sheet()->doc()->locale();
6818
6819 /* TDELocale::formatNumber requires the precision we want to return.
6820 */
6821 int precision = t.length() - t.find('.') - 1;
6822
6823 if ( formatType() == Percentage_format )
6824 {
6825 if (value().isInteger())
6826 t = locale->formatNumber( value().asInteger() * 100 );
6827 else
6828 t = locale->formatNumber( value().asFloat() * 100.0, precision );
6829 d->strText = pasteOperation( t, d->strText, op );
6830 d->strText += '%';
6831 }
6832 else
6833 {
6834 if (value().isInteger())
6835 t = locale->formatLong(value().asInteger());
6836 else
6837 t = locale->formatNumber(value().asFloat(), precision);
6838 d->strText = pasteOperation( t, d->strText, op );
6839 }
6840 }
6841
6842 // date ?
6843 else if( dataType == "Date" )
6844 {
6845 int pos = t.find('/');
6846 int year = t.mid(0,pos).toInt();
6847 int pos1 = t.find('/',pos+1);
6848 int month = t.mid(pos+1,((pos1-1)-pos)).toInt();
6849 int day = t.right(t.length()-pos1-1).toInt();
6850 setValue( TQDate(year,month,day) );
6851 if ( value().asDate().isValid() ) // Should always be the case for new docs
6852 d->strText = locale()->formatDate( value().asDate(), true );
6853 else // This happens with old docs, when format is set wrongly to date
6854 {
6855 d->strText = pasteOperation( t, d->strText, op );
6857 }
6858 }
6859
6860 // time ?
6861 else if( dataType == "Time" )
6862 {
6863 int hours = -1;
6864 int minutes = -1;
6865 int second = -1;
6866 int pos, pos1;
6867 pos = t.find(':');
6868 hours = t.mid(0,pos).toInt();
6869 pos1 = t.find(':',pos+1);
6870 minutes = t.mid(pos+1,((pos1-1)-pos)).toInt();
6871 second = t.right(t.length()-pos1-1).toInt();
6872 setValue( TQTime(hours,minutes,second) );
6873 if ( value().asTime().isValid() ) // Should always be the case for new docs
6874 d->strText = locale()->formatTime( value().asTime(), true );
6875 else // This happens with old docs, when format is set wrongly to time
6876 {
6877 d->strText = pasteOperation( t, d->strText, op );
6879 }
6880 }
6881
6882 else
6883 {
6884 // Set the cell's text
6885 d->strText = pasteOperation( t, d->strText, op );
6886 setValue( d->strText );
6887 }
6888 }
6889 }
6890
6891 if ( text.hasAttribute( "outStr" ) ) // very new docs
6892 {
6893 d->strOutText = text.attribute( "outStr" );
6894 if ( !d->strOutText.isEmpty() )
6895 clearFlag( Flag_TextFormatDirty );
6896 }
6897
6898 if ( !format()->sheet()->isLoading() )
6899 setCellText( d->strText );
6900
6901 if ( d->hasExtra() && d->extra()->conditions )
6902 d->extra()->conditions->checkMatches();
6903
6904 return true;
6905}
6906
6907TQTime Cell::toTime(const TQDomElement &element)
6908{
6909 //TODO: can't we use tryParseTime (after modification) instead?
6910 TQString t = element.text();
6911 t = t.stripWhiteSpace();
6912 int hours = -1;
6913 int minutes = -1;
6914 int second = -1;
6915 int pos, pos1;
6916 pos = t.find(':');
6917 hours = t.mid(0,pos).toInt();
6918 pos1 = t.find(':',pos+1);
6919 minutes = t.mid(pos+1,((pos1-1)-pos)).toInt();
6920 second = t.right(t.length()-pos1-1).toInt();
6921 setValue( Value( TQTime(hours,minutes,second)) );
6922 return value().asTime();
6923}
6924
6925TQDate Cell::toDate(const TQDomElement &element)
6926{
6927 TQString t = element.text();
6928 int pos;
6929 int pos1;
6930 int year = -1;
6931 int month = -1;
6932 int day = -1;
6933 pos = t.find('/');
6934 year = t.mid(0,pos).toInt();
6935 pos1 = t.find('/',pos+1);
6936 month = t.mid(pos+1,((pos1-1)-pos)).toInt();
6937 day = t.right(t.length()-pos1-1).toInt();
6938 setValue( Value( TQDate(year,month,day) ) );
6939 return value().asDate();
6940}
6941
6942TQString Cell::pasteOperation( const TQString &new_text, const TQString &old_text, Paste::Operation op )
6943{
6944 if ( op == Paste::OverWrite )
6945 return new_text;
6946
6947 TQString tmp_op;
6948 TQString tmp;
6949 TQString old;
6950
6951 if( !new_text.isEmpty() && new_text[0] == '=' )
6952 {
6953 tmp = new_text.right( new_text.length() - 1 );
6954 }
6955 else
6956 {
6957 tmp = new_text;
6958 }
6959
6960 if ( old_text.isEmpty() &&
6961 ( op == Paste::Add || op == Paste::Mul || op == Paste::Sub || op == Paste::Div ) )
6962 {
6963 old = "=0";
6964 }
6965
6966 if( !old_text.isEmpty() && old_text[0] == '=' )
6967 {
6968 old = old_text.right( old_text.length() - 1 );
6969 }
6970 else
6971 {
6972 old = old_text;
6973 }
6974
6975 bool b1, b2;
6976 tmp.toDouble( &b1 );
6977 old.toDouble( &b2 );
6978 if (b1 && !b2 && old.length() == 0)
6979 {
6980 old = "0";
6981 b2 = true;
6982 }
6983
6984 if( b1 && b2 )
6985 {
6986 switch( op )
6987 {
6988 case Paste::Add:
6989 tmp_op = TQString::number(old.toDouble()+tmp.toDouble());
6990 break;
6991 case Paste::Mul :
6992 tmp_op = TQString::number(old.toDouble()*tmp.toDouble());
6993 break;
6994 case Paste::Sub:
6995 tmp_op = TQString::number(old.toDouble()-tmp.toDouble());
6996 break;
6997 case Paste::Div:
6998 tmp_op = TQString::number(old.toDouble()/tmp.toDouble());
6999 break;
7000 default:
7001 Q_ASSERT( 0 );
7002 }
7003
7004 setFlag(Flag_LayoutDirty);
7006
7007 return tmp_op;
7008 }
7009 else if ( ( new_text[0] == '=' && old_text[0] == '=' ) ||
7010 ( b1 && old_text[0] == '=' ) || ( new_text[0] == '=' && b2 ) )
7011 {
7012 switch( op )
7013 {
7014 case Paste::Add :
7015 tmp_op="=("+old+")+"+"("+tmp+")";
7016 break;
7017 case Paste::Mul :
7018 tmp_op="=("+old+")*"+"("+tmp+")";
7019 break;
7020 case Paste::Sub:
7021 tmp_op="=("+old+")-"+"("+tmp+")";
7022 break;
7023 case Paste::Div:
7024 tmp_op="=("+old+")/"+"("+tmp+")";
7025 break;
7026 default :
7027 Q_ASSERT( 0 );
7028 }
7029
7030 tmp_op = decodeFormula( tmp_op, d->column, d->row );
7031 setFlag(Flag_LayoutDirty);
7033
7034 return tmp_op;
7035 }
7036
7037 tmp = decodeFormula( new_text, d->column, d->row );
7038 setFlag(Flag_LayoutDirty);
7040
7041 return tmp;
7042}
7043
7044TQString Cell::testAnchor( int x, int y ) const
7045{
7046 if( link().isEmpty() )
7047 return TQString();
7048
7049 const Doc* doc = format()->sheet()->doc();
7050 int x1 = doc->zoomItX( d->textX );
7051 int y1 = doc->zoomItX( d->textY - d->textHeight );
7052 int x2 = doc->zoomItX( d->textX + d->textWidth );
7053 int y2 = doc->zoomItX( d->textY );
7054
7055 if( x > x1 ) if( x < x2 )
7056 if( y > y1 ) if( y < y2 )
7057 return link();
7058
7059 return TQString();
7060}
7061
7063{
7064 // Avoid unobscuring the cells in the destructor.
7065 if (d->hasExtra())
7066 {
7067 d->extra()->extraXCells = 0;
7068 d->extra()->extraYCells = 0;
7069 d->extra()->mergedXCells = 0;
7070 d->extra()->mergedYCells = 0;
7071 }
7072
7073 //d->nextCell = 0;
7074 //d->previousCell = 0;
7075}
7076
7078{
7079 if ( d->nextCell )
7080 d->nextCell->setPreviousCell( d->previousCell );
7081 if ( d->previousCell )
7082 d->previousCell->setNextCell( d->nextCell );
7083
7084 if (d->hasExtra())
7085 {
7086 delete d->extra()->validity;
7087 }
7088
7089 // Unobscure cells.
7090 int extraXCells = d->hasExtra() ? d->extra()->extraXCells : 0;
7091 int extraYCells = d->hasExtra() ? d->extra()->extraYCells : 0;
7092 for( int x = 0; x <= extraXCells; ++x )
7093 for( int y = (x == 0) ? 1 : 0; // avoid looking at (+0,+0)
7094 y <= extraYCells; ++y )
7095 {
7096 Cell* cell = format()->sheet()->cellAt( d->column + x, d->row + y );
7097 if ( cell )
7098 cell->unobscure(this);
7099 }
7100
7101 d->value = Value::empty();
7102
7103 if (!isDefault())
7104 valueChanged (); //our value has been changed (is now null), but only if we aren't default
7105
7106 delete d->format;
7107 delete d;
7108}
7109
7110bool Cell::operator > ( const Cell & cell ) const
7111{
7112 if ( value().isNumber() ) // ### what about bools ?
7113 {
7114 if ( cell.value().isNumber() )
7115 return value().asFloat() > cell.value().asFloat();
7116 else
7117 return false; // numbers are always < than texts
7118 }
7119 else if(isDate())
7120 {
7121 if( cell.isDate() )
7122 return value().asDate() > cell.value().asDate();
7123 else if (cell.value().isNumber())
7124 return true;
7125 else
7126 return false; //date are always < than texts and time
7127 }
7128 else if(isTime())
7129 {
7130 if( cell.isTime() )
7131 return value().asTime() > cell.value().asTime();
7132 else if( cell.isDate())
7133 return true; //time are always > than date
7134 else if( cell.value().isNumber())
7135 return true;
7136 else
7137 return false; //time are always < than texts
7138 }
7139 else
7140 {
7141 if ( Map::respectCase )
7142 return value().asString().compare(cell.value().asString()) > 0;
7143 else
7144 return ( value().asString() ).lower().compare(cell.value().asString().lower()) > 0;
7145 }
7146}
7147
7148bool Cell::operator < ( const Cell & cell ) const
7149{
7150 if ( value().isNumber() )
7151 {
7152 if ( cell.value().isNumber() )
7153 return value().asFloat() < cell.value().asFloat();
7154 else
7155 return true; // numbers are always < than texts
7156 }
7157 else if(isDate())
7158 {
7159 if( cell.isDate() )
7160 return value().asDateTime().date() < cell.value().asDateTime().date();
7161 else if( cell.value().isNumber())
7162 return false;
7163 else
7164 return true; //date are always < than texts and time
7165 }
7166 else if(isTime())
7167 {
7168 if( cell.isTime() )
7169 return value().asDateTime().time() < cell.value().asDateTime().time();
7170 else if(cell.isDate())
7171 return false; //time are always > than date
7172 else if( cell.value().isNumber())
7173 return false;
7174 else
7175 return true; //time are always < than texts
7176 }
7177 else
7178 {
7179 if ( Map::respectCase )
7180 return value().asString().compare(cell.value().asString()) < 0;
7181 else
7182 return ( value().asString() ).lower().compare(cell.value().asString().lower()) < 0;
7183 }
7184}
7185
7186bool Cell::operator==( const Cell& other ) const
7187{
7188 if ( d->strText != other.d->strText )
7189 return false;
7190 if ( d->value != other.d->value )
7191 return false;
7192 if ( *d->format != *other.d->format )
7193 return false;
7194 if ( d->hasExtra() )
7195 {
7196 if ( !other.d->hasExtra() )
7197 return false;
7198 if ( d->extra()->link != other.d->extra()->link )
7199 return false;
7200 if ( d->extra()->mergedXCells != other.d->extra()->mergedXCells )
7201 return false;
7202 if ( d->extra()->mergedYCells != other.d->extra()->mergedYCells )
7203 return false;
7204 if ( d->extra()->conditions )
7205 {
7206 if ( !other.d->extra()->conditions )
7207 return false;
7208 if ( *d->extra()->conditions != *other.d->extra()->conditions )
7209 return false;
7210 }
7211 if ( d->extra()->validity )
7212 {
7213 if ( !other.d->extra()->validity )
7214 return false;
7215 if ( *d->extra()->validity != *other.d->extra()->validity )
7216 return false;
7217 }
7218 }
7219 return true;
7220}
7221
7223{
7224 Q_ASSERT(!isDefault());
7225 return TQRect(TQPoint(d->column, d->row), TQPoint(d->column, d->row));
7226}
7227
7228TQValueList<Conditional> Cell::conditionList() const
7229{
7230 if ( !d->hasExtra() || !d->extra()->conditions )
7231 {
7232 TQValueList<Conditional> emptyList;
7233 return emptyList;
7234 }
7235
7236 return d->extra()->conditions->conditionList();
7237}
7238
7239void Cell::setConditionList( const TQValueList<Conditional> & newList )
7240{
7241 if (d->hasExtra())
7242 delete d->extra()->conditions;
7243 d->extra()->conditions = new Conditions( this );
7244 d->extra()->conditions->setConditionList( newList );
7245 d->extra()->conditions->checkMatches();
7246}
7247
7248bool Cell::hasError() const
7249{
7250 return ( testFlag(Flag_ParseError) ||
7251 testFlag(Flag_CircularCalculation) ||
7252 testFlag(Flag_DependancyError));
7253}
7254
7256{
7257 clearFlag( Flag_ParseError );
7258 clearFlag( Flag_CircularCalculation );
7259 clearFlag( Flag_DependancyError );
7260}
7261
7263{
7264 return isFormula() ? testFlag( Flag_CalcDirty ) : false;
7265}
7266
7267bool Cell::layoutDirtyFlag() const
7268{
7269 return testFlag( Flag_LayoutDirty );
7270}
7271
7272void Cell::clearDisplayDirtyFlag()
7273{
7274 clearFlag( Flag_DisplayDirty );
7275}
7276
7277void Cell::setDisplayDirtyFlag()
7278{
7279 setFlag( Flag_DisplayDirty );
7280}
7281
7283{
7284 return testFlag( Flag_Merged );
7285}
7286
7287void Cell::clearFlag( CellFlags flag )
7288{
7289 d->flags &= ~(TQ_UINT32)flag;
7290}
7291
7292void Cell::setFlag( CellFlags flag )
7293{
7294 d->flags |= (TQ_UINT32)flag;
7295}
7296
7297bool Cell::testFlag( CellFlags flag ) const
7298{
7299 return ( d->flags & (TQ_UINT32)flag );
7300}
7301
7302
7303void Cell::checkForNamedAreas( TQString & formula ) const
7304{
7305 KSPLoadingInfo* loadinginfo = sheet()->doc()->loadingInfo();
7306 if(! loadinginfo) {
7307 kdDebug() << "Cell::checkForNamedAreas loadinginfo is NULL" << endl;
7308 return;
7309 }
7310
7311 int l = formula.length();
7312 int i = 0;
7313 TQString word;
7314 int start = 0;
7315 while ( i < l )
7316 {
7317 if ( formula[i].isLetterOrNumber() )
7318 {
7319 word += formula[i];
7320 ++i;
7321 continue;
7322 }
7323 if ( !word.isEmpty() )
7324 {
7325 if ( loadinginfo->findWordInAreaList(word) )
7326 {
7327 formula = formula.replace( start, word.length(), "'" + word + "'" );
7328 l = formula.length();
7329 ++i;
7330 kdDebug() << "Formula: " << formula << ", L: " << l << ", i: " << i + 1 <<endl;
7331 }
7332 }
7333
7334 ++i;
7335 word = "";
7336 start = i;
7337 }
7338 if ( !word.isEmpty() )
7339 {
7340 if ( loadinginfo->findWordInAreaList(word) )
7341 {
7342 formula = formula.replace( start, word.length(), "'" + word + "'" );
7343 l = formula.length();
7344 ++i;
7345 kdDebug() << "Formula: " << formula << ", L: " << l << ", i: " << i + 1 <<endl;
7346 }
7347 }
7348}
The canvas builds a part of the GUI of KSpread.
double yOffset() const
double xOffset() const
This is an abstract base class only.
virtual void cellChanged(Cell *_obj)
Call this function if one of the cells covered by this binding (.
For every cell in the spread sheet there is a Cell object.
double dblHeight(int _row=-1, const Canvas *_canvas=0L) const
FormatType formatType() const
Return the format of this cell.
void setLayoutDirtyFlag(bool format=false)
Causes the format to be recalculated when the cell is drawn next time.
const TQPen & effBottomBorderPen(int col, int row) const
void loadOasisCellText(const TQDomElement &parent)
Load the text paragraphs from an OASIS XML cell description.
TQString encodeFormula(bool _era=false, int _col=-1, int _row=-1) const
encode a formula into a text representation
TQString link() const
Returns the link associated with cell.
TQValueList< Conditional > conditionList() const
Gets a copy of the list of current conditions.
bool calc(bool delay=true)
Starts calculating.
TQString text() const
Return the text the user entered.
TQString name() const
Returns the name of the cell.
int row() const
Returns the cell's row.
TQString fullName() const
Returns the full name of the cell, i.e.
void setRightBorderPen(const TQPen &p)
int defineAlignX()
return align X when align is undefined
void clearAllErrors()
Clear all error flags from the cell.
void convertToDate()
converts content to date format
const Value value() const
Returns the value that this cell holds.
TQValueList< Cell * > obscuringCells() const
void clearFormula()
Cleans up formula stuff.
bool isObscured() const
bool updateChart(bool refresh=true)
Refreshing chart.
const TQPen & topBorderPen(int col, int row) const
Sheet * sheet() const
Returns the worksheet which owns this cell.
bool doesMergeCells() const
void setLink(const TQString &link)
Sets a link for this cell.
void setConditionList(const TQValueList< Conditional > &newList)
Replace the old set of conditions with a new one.
void setValue(const Value &value)
Sets the value for this cell.
int column() const
Returns the cell's column.
bool calcDirtyFlag()
Checks the calcDirtyFlag.
void copyAll(Cell *cell)
Copies the format and the content.
void 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=true)
Paints the cell.
double getDouble()
return the cell's value as a double
void sheetDies()
This method notifies the cell that the parent sheet is being deleted.
double textHeight() const
return height of the text
TQString textDisplaying(TQPainter &painter)
Called from paintCell to determine the text wich can be displaying.
bool isEmpty() const
Returns true if this cell has no content, i.e no text and no formula.
void setCellValue(const Value &value, FormatType fmtType=No_format, const TQString &inputText=TQString())
Sets the value for this cell and its formatting and input text, if appropriate.
TQString pasteOperation(const TQString &new_text, const TQString &old_text, Paste::Operation op)
Merges the new_text with old_text during a paste operation.
TDELocale * locale() const
Returns the locale setting of this cell.
void calculateTextParameters(TQPainter &painter, int _col, int _row)
Calculates the text parameters stored in cell Applies font to use to painter.
const TQPen & effFallDiagonalPen(int col, int row) const
Cell * ultimateObscuringCell() const
Return the cell that is obscuring this one (merged cells only).
uint effRightBorderValue(int col, int row) const
const TQPen & effTopBorderPen(int col, int row) const
int extraXCells() const
int mergedXCells() const
void setDisplayText(const TQString &_text)
Sets the text in the cell when the user inputs it.
const TQPen & leftBorderPen(int col, int row) const
void setTopBorderPen(const TQPen &p)
const TQPen & effRightBorderPen(int col, int row) const
void obscure(Cell *cell, bool isForcing=false)
Tells this cell that the Cell 'cell' obscures this one.
void move(int column, int row)
Moves around the cell.
bool loadOasis(const TQDomElement &element, KoOasisLoadingContext &oasisContext, Style *style)
Loads a cell from an OASIS XML element.
bool needsPrinting() const
void checkTextInput()
Check the input from the user, and determine the contents of the cell accordingly (in particular the ...
void convertToPercent()
converts content to percentageformat
bool isFormula() const
Returns true if this cell holds a formula.
uint effLeftBorderValue(int col, int row) const
uint effTopBorderValue(int col, int row) const
int extraYCells() const
int height(int _row=-1, const Canvas *_canvas=0L) const
TQDomElement save(TQDomDocument &doc, int _x_offset=0, int _y_offset=0, bool force=false, bool copy=false, bool era=false)
Save this cell.
TQString saveOasisCellStyle(KoGenStyle &currentCellStyle, KoGenStyles &mainStyles)
const TQPen & bottomBorderPen(int col, int row) const
void setCalcDirtyFlag()
Set the calcDirtyFlag.
void setCellText(const TQString &_text, bool asString=false)
The high-level method for setting text, when the user inputs it.
void incPrecision()
Increases the precison of the value displayed.
void convertToMoney()
converts content to money format
const TQPen & effLeftBorderPen(int col, int row) const
Format * format() const
Returns the format object of this cell.
void setBottomBorderPen(const TQPen &p)
bool operator>(const Cell &) const
Used for comparing cells (when sorting)
void convertToTime()
converts content to time format
void makeLayout(TQPainter &_painter, int _col, int _row)
Calculates the layout of the cell, i,e, determines what should be shown for this cell,...
double dblWidth(int _col=-1, const Canvas *_canvas=0L) const
bool hasError() const
uint effBottomBorderValue(int col, int row) const
void convertToDouble()
converts content to double format
void mergeCells(int _col, int _row, int _x, int _y)
Merge a number of cells, i.e.
void unobscure(Cell *cell)
Tells this cell that it is no longer obscured.
void copyFormat(const int column, const int row)
Copyies the format from the cell at the position (_column|_row).
const TQPen & rightBorderPen(int col, int row) const
void checkNumberFormat()
Automatically chooses between a number format and a scientific format (if the number is too big)
TQString columnName() const
Returns the column name of the cell.
const TQBrush & backGroundBrush(int col, int row) const
bool isDefault() const
Returns true if this is a default cell (with row and column equal to zero).
const TQColor & bgColor(int col, int row) const
void textSize(TQPainter &_paint)
Called from makeFormat() to determine the space needed for the text.
bool isPartOfMerged() const
If this cell is part of a merged cell, then the marker may never reside on this cell.
void applyZoomedFont(TQPainter &painter, int _col, int _row)
Applies the font to use to painter.
bool makeFormula()
Parses the formula.
bool isDate() const
returns true, if cell format is of date type or content is a date
bool isTime() const
returns true, if cell format is of time type or content is a time
int mergedYCells() const
const TQPen & effGoUpDiagonalPen(int col, int row) const
bool testValidity() const
return true if value is good else show a messagebox
void decPrecision()
Decreases the precison of the value displayed.
void setLeftBorderPen(const TQPen &p)
double textWidth() const
return width of the text
int width(int _col=-1, const Canvas *_canvas=0L) const
Manages a set of conditions for a cell.
This class holds the data that makes up a spreadsheet.
Definition kspread_doc.h:98
Holds an embedded chart object.
Holds an embedded koffice object.
Contains basic information for every object.
Holds a picture object.
Class Formula encapsulates a formula for a cell.
Definition formula.h:239
Temporary information used only during loading.
Represents the position of a single cell in a workbook.
The Style class represents a cell style.
Provides a wrapper for cell value.
static const Value & empty()
Returns constant reference to empty value.
Value element(unsigned column, unsigned row) const
Return an element in the array value.
void setError(const TQString &msg)
Sets this value to hold error message.
void setFormat(Format fmt)
Sets format information for this value.
void setValue(const Value &v)
Sets this value to another value.
bool isBoolean() const
Returns true if the type of this value is Boolean.
bool isEmpty() const
Returns true if empty.
bool isNumber() const
Returns true if the type of this value is either integer or floating-point.
bool isArray() const
Returns true if the type of this value is array.
TQDate asDate() const
Returns the date representation of this value.
Type type() const
Returns the type of the value.
double asFloat() const
Returns the floating-point value of this value.
TQDateTime asDateTime() const
Returns the date/time representation of this value.
TQString asString() const
Returns the string value of this value.
bool isString() const
Returns true if the type of this value is string.
TQTime asTime() const
Returns the time representation of this value.
bool asBoolean() const
Returns the boolean value of this value.
The View class displays a KSpread document.
static TQColor highlightColor()
Returns the default color for highlighting cells and column / row headers.
Canvas * canvasWidget() const
Returns the canvas of the view.
A pointer to the decimal separator.
Operation
The current cell value treatment.
@ Sub
Substract.
@ OverWrite
Overwrite.
Mode
The pasted content.
@ Result
Result only, no formula.
@ Format
Format only.
@ Comment
Comment only.
@ Text
Text only.
@ NoBorder
not the borders
@ Normal
Everything.
Type
The type of the restriction.
@ Text
Restrict to texts.
@ Date
Restrict to dates.
@ Number
Restrict to numbers.
@ Integer
Restrict to integers
@ List
Restrict to lists.
@ TextLength
Restrict text length.
@ None
No restriction.
@ Time
Restrict to times.
The KSpread namespace.
Definition commands.h:39
KSPREAD_EXPORT int util_decodeColumnLabelText(const TQString &_col)
Call this function to decode the text of a column label to an integer i.e.