kspread

kspread_util.cpp
1/* This file is part of the KDE project
2 Copyright (C) 2006 Stefan Nikolaus <stefan.nikolaus@kdemail.net>
3 Copyright (C) 1998, 1999 Torben Weis <weis@kde.org>
4
5 This library is free software; you can redistribute it and/or
6 modify it under the terms of the GNU Library General Public
7 License as published by the Free Software Foundation; either
8 version 2 of the License, or (at your option) any later version.
9
10 This library is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Library General Public License for more details.
14
15 You should have received a copy of the GNU Library General Public License
16 along with this library; see the file COPYING.LIB. If not, write to
17 the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
18 * Boston, MA 02110-1301, USA.
19*/
20
21#include <ctype.h>
22
23#include <tqregexp.h>
24
25#include <kdebug.h>
26
27#include "formula.h"
28#include "kspread_doc.h"
29#include "kspread_locale.h"
30#include "kspread_map.h"
31#include "kspread_sheet.h"
32#include "kspread_style.h"
33#include "kspread_util.h"
34
35using namespace KSpread;
36
37//helper functions for the formatting
38bool KSpread::formatIsDate (FormatType fmt)
39{
40 return ((fmt == ShortDate_format) || (fmt == TextDate_format) ||
41 (((int) fmt >= 200) && ((int) fmt < 300)));
42}
43
44bool KSpread::formatIsTime (FormatType fmt)
45{
46 return (((int) fmt >= 50) && ((int) fmt < 70));
47}
48
49bool KSpread::formatIsFraction (FormatType fmt)
50{
51 return (((int) fmt >= 70) && ((int) fmt < 80));
52}
53
54
55//used in Point::init, Cell::encodeFormula and
56// dialogs/kspread_dlg_paperlayout.cpp
57int KSpread::util_decodeColumnLabelText( const TQString &_col )
58{
59 int col = 0;
60 int offset='a'-'A';
61 int counterColumn = 0;
62 for ( uint i=0; i < _col.length(); i++ )
63 {
64 counterColumn = (int) pow(26.0 , static_cast<int>(_col.length() - i - 1));
65 if( (int)(_col[i]) >= 'A' && (int)(_col[i]) <= 'Z' )
66 col += counterColumn * ( _col[i].latin1() - 'A' + 1); // okay here (Werner)
67 else if( (int)(_col[i]) >= 'a' && (int)(_col[i]) <= 'z' )
68 col += counterColumn * ( _col[i].latin1() - 'A' - offset + 1 );
69 else
70 kdDebug(36001) << "util_decodeColumnLabelText: Wrong characters in label text for col:'" << _col << "'" << endl;
71 }
72 return col;
73}
74
75//used in dialogs/kspread_dlg_paperlayout.cpp
76TQString KSpread::util_rangeColumnName( const TQRect &_area)
77{
78 return TQString("%1:%2")
79 .arg( Cell::columnName( _area.left()))
80 .arg( Cell::columnName(_area.right()));
81}
82
83//used in dialogs/kspread_dlg_paperlayout.cpp
84TQString KSpread::util_rangeRowName( const TQRect &_area)
85{
86 return TQString("%1:%2")
87 .arg( _area.top())
88 .arg(_area.bottom());
89}
90
91TQString KSpread::util_rangeName(const TQRect &_area)
92{
93 return Cell::name( _area.left(), _area.top() ) + ":" +
94 Cell::name( _area.right(), _area.bottom() );
95}
96
97TQString KSpread::util_rangeName(Sheet * _sheet, const TQRect &_area)
98{
99 return _sheet->sheetName() + "!" + util_rangeName(_area);
100}
101
102TQDomElement KSpread::util_createElement( const TQString & tagName, const TQFont & font, TQDomDocument & doc )
103{
104 TQDomElement e( doc.createElement( tagName ) );
105
106 e.setAttribute( "family", font.family() );
107 e.setAttribute( "size", font.pointSize() );
108 e.setAttribute( "weight", font.weight() );
109 if ( font.bold() )
110 e.setAttribute( "bold", "yes" );
111 if ( font.italic() )
112 e.setAttribute( "italic", "yes" );
113 if ( font.underline() )
114 e.setAttribute( "underline", "yes" );
115 if ( font.strikeOut() )
116 e.setAttribute( "strikeout", "yes" );
117 //e.setAttribute( "charset", TDEGlobal::charsets()->name( font ) );
118
119 return e;
120}
121
122TQDomElement KSpread::util_createElement( const TQString & tagname, const TQPen & pen, TQDomDocument & doc )
123{
124 TQDomElement e( doc.createElement( tagname ) );
125 e.setAttribute( "color", pen.color().name() );
126 e.setAttribute( "style", (int)pen.style() );
127 e.setAttribute( "width", (int)pen.width() );
128 return e;
129}
130
131TQFont KSpread::util_toFont( TQDomElement & element )
132{
133 TQFont f;
134 f.setFamily( element.attribute( "family" ) );
135
136 bool ok;
137 f.setPointSize( element.attribute("size").toInt( &ok ) );
138 if ( !ok )
139 return TQFont();
140
141 f.setWeight( element.attribute("weight").toInt( &ok ) );
142 if ( !ok )
143 return TQFont();
144
145 if ( element.hasAttribute( "italic" ) && element.attribute("italic") == "yes" )
146 f.setItalic( true );
147
148 if ( element.hasAttribute( "bold" ) && element.attribute("bold") == "yes" )
149 f.setBold( true );
150
151 if ( element.hasAttribute( "underline" ) && element.attribute("underline") == "yes" )
152 f.setUnderline( true );
153
154 if ( element.hasAttribute( "strikeout" ) && element.attribute("strikeout") == "yes" )
155 f.setStrikeOut( true );
156
157 /* Uncomment when charset is added to kspread_dlg_layout
158 + save a document-global charset
159 if ( element.hasAttribute( "charset" ) )
160 TDEGlobal::charsets()->setTQFont( f, element.attribute("charset") );
161 else
162 */
163 // ######## Not needed anymore in 3.0?
164 //TDEGlobal::charsets()->setTQFont( f, TDEGlobal::locale()->charset() );
165
166 return f;
167}
168
169TQPen KSpread::util_toPen( TQDomElement & element )
170{
171 bool ok;
172 TQPen p;
173
174 p.setStyle( (TQt::PenStyle)element.attribute("style").toInt( &ok ) );
175 if ( !ok )
176 return TQPen();
177
178 p.setWidth( element.attribute("width").toInt( &ok ) );
179 if ( !ok )
180 return TQPen();
181
182 p.setColor( TQColor( element.attribute("color") ) );
183
184 return p;
185}
186
187Point::Point(const TQString & _str)
188{
189 _sheet = 0;
190 init(_str);
191}
192
193void Point::setPos(TQPoint pos)
194{
195 _pos=pos;
196}
197TQPoint Point::pos() const
198{
199 return _pos;
200}
201void Point::setSheet(Sheet* sheet)
202{
203 _sheet=sheet;
204}
205KSpread::Sheet* Point::sheet() const
206{
207 return _sheet;
208}
209void Point::setSheetName(TQString name)
210{
211 _sheetName=name;
212}
213TQString Point::sheetName() const
214{
215 return _sheetName;
216}
217void Point::setColumnFixed(bool colFixed)
218{
219 _columnFixed=colFixed;
220}
221bool Point::columnFixed() const
222{
223 return _columnFixed;
224}
225void Point::setRowFixed(bool rowFixed)
226{
227 _rowFixed=rowFixed;
228}
229bool Point::rowFixed() const
230{
231 return _rowFixed;
232}
233
234
235void Point::init(const TQString & _str)
236{
237 _columnFixed=false;
238 _rowFixed=false;
239
240// kdDebug(36001) <<"Point::init ("<<_str<<")"<<endl;
241 _pos.setX(-1);
242
243 uint len = _str.length();
244 if ( !len )
245 {
246 kdDebug(36001) << "Point::init: len = 0" << endl;
247 return;
248 }
249
250 TQString str( _str );
251 int n = _str.find( '!' );
252 if ( n != -1 )
253 {
254 _sheetName = _str.left( n );
255 str = _str.right( len - n - 1 ); // remove the '!'
256 len = str.length();
257 }
258
259 uint p = 0;
260
261 // Fixed ?
262 if ( str[0] == '$' )
263 {
264 _columnFixed = true;
265 p++;
266 }
267 else
268 _columnFixed = false;
269
270 // Malformed ?
271 if ( p == len )
272 {
273 kdDebug(36001) << "Point::init: no point after '$' (str: '" << str.mid( p ) << "'" << endl;
274 return;
275 }
276 if ( str[p] < 'A' || str[p] > 'Z' )
277 {
278 if ( str[p] < 'a' || str[p] > 'z' )
279 {
280 kdDebug(36001) << "Point::init: wrong first character in point (str: '" << str.mid( p ) << "'" << endl;
281 return;
282 }
283 }
284 //default is error
285 int x = -1;
286 //search for the first character != text
287 int result = str.find( TQRegExp("[^A-Za-z]+"), p );
288
289 //get the colomn number for the character between actual position and the first non text charakter
290 if ( result != -1 )
291 x = util_decodeColumnLabelText( str.mid( p, result - p ) ); // x is defined now
292 else // If there isn't any, then this is not a point -> return
293 {
294 kdDebug(36001) << "Point::init: no number in string (str: '" << str.mid( p, result ) << "'" << endl;
295 return;
296 }
297 p = result;
298
299 //limit is KS_colMax
300 if ( x > KS_colMax )
301 {
302 kdDebug(36001) << "Point::init: column value too high (col: " << x << ")" << endl;
303 return;
304 }
305
306 // Malformed ?
307 if (p == len)
308 {
309 kdDebug(36001) << "Point::init: p==len after cols" << endl;
310 return;
311 }
312
313 if (str[p] == '$')
314 {
315 _rowFixed = true;
316 p++;
317 // Malformed ?
318 if ( p == len )
319 {
320 kdDebug(36001) << "Point::init: p==len after $ of row" << endl;
321 return;
322 }
323 }
324 else
325 _rowFixed = false;
326
327 uint p2 = p;
328 while ( p < len )
329 {
330 if ( !isdigit( TQChar(str[p++]) ) )
331 {
332 kdDebug(36001) << "Point::init: no number" << endl;
333 return;
334 }
335 }
336
337 bool ok;
338 int y = str.mid( p2, p-p2 ).toInt( &ok );
339 if ( !ok )
340 {
341 kdDebug(36001) << "Point::init: Invalid number (str: '" << str.mid( p2, p-p2 ) << "'" << endl;
342 return;
343 }
344 if ( y > KS_rowMax )
345 {
346 kdDebug(36001) << "Point::init: row value too high (row: " << y << ")" << endl;
347 return;
348 }
349 if ( y <= 0 )
350 {
351 kdDebug(36001) << "Point::init: y <= 0" << endl;
352 return;
353 }
354 _pos = TQPoint( x, y );
355}
356
357bool util_isPointValid( TQPoint point )
358{
359 if ( point.x() >= 1
360 && point.y() >= 1
361 && point.x() <= KS_colMax
362 && point.y() <= KS_rowMax
363 )
364 return true;
365 else
366 return false;
367}
368
369bool util_isRectValid( TQRect rect )
370{
371 if ( util_isPointValid( rect.topLeft() )
372 && util_isPointValid( rect.bottomRight() )
373 )
374 return true;
375 else
376 return false;
377}
378
379Point::Point( const TQString & str, Map * map,
380 Sheet * sheet )
381{
382
383 uint p = 0;
384 int p2 = str.find( '!' );
385 if ( p2 != -1 )
386 {
387 _sheetName = str.left( p2++ );
388 while ( true )
389 {
390 _sheet = map->findSheet( _sheetName );
391 if ( !sheet && _sheetName[0] == ' ' )
392 {
393 _sheetName = _sheetName.right( _sheetName.length() - 1 );
394 continue;
395 }
396 break;
397 }
398 p = p2;
399
400 //If the loop didn't return a sheet, better keep a string for isValid
401 if ( _sheetName.isEmpty() )
402 {
403 kdDebug(36001) << "Point: tableName is unknown" << endl;
404 _sheetName = "unknown";
405 }
406 }
407 else
408 {
409 if ( sheet != 0 )
410 {
411 _sheet = sheet;
412 _sheetName = sheet->sheetName();
413 }
414 else
415 _sheet = 0;
416 }
417
418 init( str.mid( p ) );
419}
420
421Cell *Point::cell() const
422{
423 return _sheet->cellAt(_pos);
424}
425
426bool Point::operator== (const Point &cell) const
427{
428 //sheet info ignored
429 return (_pos == cell.pos());
430}
431
432bool Point::operator< (const Point &cell) const
433{
434 //sheet info ignored
435 return (pos().y() < cell.pos().y()) ? true :
436 ((pos().y() == cell.pos().y()) && (pos().x() < cell.pos().x()));
437}
438
439bool Range::operator ==(const Range& otherRange) const
440{
441 if ( _range == otherRange._range
442 && _leftFixed == otherRange._leftFixed
443 && _rightFixed == otherRange._rightFixed
444 && _bottomFixed == otherRange._bottomFixed
445 && _topFixed == otherRange._topFixed
446 && _sheet == otherRange._sheet )
447 return true;
448 else
449 return false;
450}
451
452Range::Range()
453{
454 _sheet = 0;
455 _range.setLeft( -1 );
456
457 _leftFixed=false;
458 _rightFixed=false;
459 _topFixed=false;
460 _bottomFixed=false;
461}
462Range::Range(const TQString & _str)
463{
464 _range.setLeft(-1);
465 _sheet = 0;
466
467 int p = _str.find(':');
468 // if (p == -1)
469 // return;
470
471 Point ul;
472 Point lr; ;
473
474 if ( p != -1)
475 {
476 ul = Point(_str.left(p));
477 lr = Point(_str.mid(p + 1));
478 }
479 else
480 {
481 ul = Point(_str);
482 lr = ul;
483 }
484
485 _range = TQRect(ul.pos(), lr.pos());
486 _sheetName = ul.sheetName();
487
488 _leftFixed = ul.columnFixed();
489 _rightFixed = lr.columnFixed();
490 _topFixed = ul.rowFixed();
491 _bottomFixed = lr.rowFixed();
492}
493
494 Range::Range( const Range& r )
495 {
496 _sheet = r.sheet();
497 _sheetName = r.sheetName();
498 _range = r.range();
499 _namedArea = r.namedArea();
500
501 _leftFixed=r._leftFixed;
502 _rightFixed=r._rightFixed;
503 _topFixed=r._topFixed;
504 _bottomFixed=r._bottomFixed;
505 }
506
507 Range::Range( const Point& ul, const Point& lr )
508 {
509 _range = TQRect( ul.pos(), lr.pos() );
510 if ( ul.sheetName() != lr.sheetName() )
511 {
512 _range.setLeft( -1 );
513 return;
514 }
515 _sheetName = ul.sheetName();
516 _sheet = ul.sheet();
517 _leftFixed = ul.columnFixed();
518 _rightFixed = lr.columnFixed();
519 _topFixed = ul.rowFixed();
520 _bottomFixed = lr.rowFixed();
521 }
522
523Range::Range(const TQString & str, Map * map,
524 Sheet * sheet)
525{
526 _range.setLeft(-1);
527 _sheet = 0;
528
529 //try to parse as named area
530 bool gotNamed = false;
531 TQString tmp = str.lower();
532 TQValueList < Reference >::Iterator it;
533 TQValueList < Reference > area = map->doc()->listArea();
534 for (it = area.begin(); it != area.end(); ++it) {
535 if ((*it).ref_name.lower() == tmp) {
536 // success - such named area exists
537 _range = (*it).rect;
538 _sheet = map->findSheet((*it).sheet_name);
539 gotNamed = true;
540 _namedArea = tmp;
541 break;
542 }
543 }
544 if (gotNamed) {
545 // we have a named area - no need to proceed further
546 _leftFixed = false;
547 _rightFixed = false;
548 _topFixed = false;
549 _bottomFixed = false;
550 return;
551 }
552
553 _range.setLeft(-1);
554 _sheet = 0;
555
556 int p = 0;
557 int p2 = str.find('!');
558 if (p2 != -1)
559 {
560 _sheetName = str.left(p2++);
561 while ( true )
562 {
563 _sheet = map->findSheet(_sheetName);
564
565 if ( !_sheet && _sheetName[0] == ' ' )
566 {
567 _sheetName = _sheetName.right( _sheetName.length() - 1 );
568 continue;
569 }
570 break;
571 }
572 p = p2;
573 } else
574 _sheet = sheet;
575
576
577 int p3 = str.find(':', p);
578 if (p3 == -1)
579 return;
580
581 Point ul(str.mid(p, p3 - p));
582 Point lr(str.mid(p3 + 1));
583 _range = TQRect(ul.pos(), lr.pos());
584
585 _leftFixed = ul.columnFixed();
586 _rightFixed = lr.columnFixed();
587 _topFixed = ul.rowFixed();
588 _bottomFixed = lr.rowFixed();
589}
590
591TQString Range::toString() const
592{
593 TQString result;
594
595 if (_sheet)
596 {
597 result=util_rangeName(_sheet,_range);
598 }
599 else
600 {
601 result=util_rangeName(_range);
602 }
603
604 //Insert $ characters to show fixed parts of range
605
606 int pos=result.find("!")+1;
607 Q_ASSERT(pos != -1);
608
609 if (_leftFixed)
610 {
611 result.insert(pos,'$');
612 pos++; //Takes account of extra character added in
613 }
614 if (_topFixed)
615 {
616 result.insert(pos+Cell::columnName(_range.left()).length(),'$');
617 }
618
619 pos=result.find(":")+1;
620 Q_ASSERT(pos != -1);
621
622 if (_rightFixed)
623 {
624 result.insert(pos,'$');
625 pos++; //Takes account of extra character added in
626 }
627 if (_bottomFixed)
628 {
629 result.insert(pos+Cell::columnName(_range.right()).length(),'$');
630 }
631
632
633 return result;
634}
635
637{
638 if (!isValid()) return;
639
640 pt->setRow(startRow());
641 pt->setColumn(startCol());
642 pt->setColumnFixed(_leftFixed);
643 pt->setRowFixed(_topFixed);
644 pt->setSheet(_sheet);
645 pt->setSheetName(_sheetName);
646}
647
649{
650 if (!isValid()) return;
651
652 pt->setRow(endRow());
653 pt->setColumn(endCol());
654 pt->setColumnFixed(_rightFixed);
655 pt->setRowFixed(_bottomFixed);
656 pt->setSheet(_sheet);
657 pt->setSheetName(_sheetName);
658}
659
660bool Range::contains (const Point &cell) const
661{
662 return _range.contains (cell.pos());
663}
664
665bool Range::intersects (const Range &r) const
666{
667 return _range.intersects (r.range());
668}
669
670bool Range::isValid() const
671{
672 return ( _range.left() >= 0 ) &&
673 ( _range.right() >= 0 ) &&
674 ( _sheet != 0 || _sheetName.isEmpty() ) &&
675 ( _range.isValid() ) ;
676}
677
678TQRect Range::range() const
679{
680 return _range;
681}
682
683void Range::setLeftFixed(bool fixed)
684{
685 _leftFixed=fixed;
686}
687bool Range::leftFixed() const
688{
689 return _leftFixed;
690}
691void Range::setRightFixed(bool fixed)
692{
693 _rightFixed=fixed;
694}
695bool Range::rightFixed() const
696{
697 return _rightFixed;
698}
699void Range::setTopFixed(bool fixed)
700{
701 _topFixed=fixed;
702}
703bool Range::topFixed() const
704{
705 return _topFixed;
706}
707void Range::setBottomFixed(bool fixed)
708{
709 _bottomFixed=fixed;
710}
711bool Range::bottomFixed() const
712{
713 return _bottomFixed;
714}
715void Range::setSheet(Sheet* sheet)
716{
717 _sheet=sheet;
718}
719KSpread::Sheet* Range::sheet() const
720{
721 return _sheet;
722}
723void Range::setSheetName(TQString sheetName)
724{
725 _sheetName=sheetName;
726}
727TQString Range::sheetName() const
728{
729 return _sheetName;
730}
731TQString Range::namedArea() const
732{
733 return _namedArea;
734}
735
736
737bool KSpread::util_isAllSelected(const TQRect &selection)
738{
739 return ( selection.top() == 1 && selection.bottom() == KS_rowMax
740 && selection.left() == 1 && selection.right() == KS_colMax);
741}
742
743bool KSpread::util_isColumnSelected(const TQRect &selection)
744{
745 return ( (selection.top() == 1) && (selection.bottom() == KS_rowMax) );
746}
747
748bool KSpread::util_isRowSelected(const TQRect &selection)
749{
750 return ( (selection.left() == 1) && (selection.right() == KS_colMax) );
751}
752
753bool KSpread::util_isRowOrColumnSelected(const TQRect &selection)
754{
755 return ( (selection.left() == 1) && (selection.right() == KS_colMax)
756 || (selection.top() == 1) && (selection.bottom() == KS_rowMax) );
757}
758
759//used in View::slotRename
760bool KSpread::util_validateSheetName(const TQString &name)
761{
762 if (name[0] == ' ')
763 {
764 return false;
765 }
766 for (unsigned int i = 0; i < name.length(); i++)
767 {
768 if ( !(name[i].isLetterOrNumber() ||
769 name[i] == ' ' || name[i] == '.' ||
770 name[i] == '_'))
771 {
772 return false;
773 }
774 }
775 return true;
776}
777
778
779RangeIterator::RangeIterator(TQRect _range, Sheet* _sheet)
780{
781 range = _range;
782 sheet = _sheet;
783 current = TQPoint(0,0);
784}
785
786RangeIterator::~RangeIterator()
787{
788}
789
791{
792 current.setY(range.top());
793
794 /* OK, because even if this equals zero, the 'getNextCellRight' won't
795 try to access it*/
796 current.setX(range.left() - 1);
797 return next();
798}
799
801{
802 if (current.x() == 0 && current.y() == 0)
803 {
804 return first();
805 }
806
807 Cell* cell = NULL;
808 bool done = false;
809
810 while (cell == NULL && !done)
811 {
812 cell = sheet->getNextCellRight(current.x(), current.y());
813 if (cell != NULL && cell->column() > range.right())
814 {
815 cell = NULL;
816 }
817
818 if (cell == NULL)
819 {
820 current.setX(range.left() - 1);
821 current.setY(current.y() + 1);
822 done = (current.y() > range.bottom());
823 }
824 }
825 return cell;
826}
827
828//not used anywhere
829int KSpread::util_penCompare( TQPen const & pen1, TQPen const & pen2 )
830{
831 if ( pen1.style() == TQt::NoPen && pen2.style() == TQt::NoPen )
832 return 0;
833
834 if ( pen1.style() == TQt::NoPen )
835 return -1;
836
837 if ( pen2.style() == TQt::NoPen )
838 return 1;
839
840 if ( pen1.width() < pen2.width() )
841 return -1;
842
843 if ( pen1.width() > pen2.width() )
844 return 1;
845
846 if ( pen1.style() < pen2.style() )
847 return -1;
848
849 if ( pen1.style() > pen2.style() )
850 return 1;
851
852 if ( pen1.color().name() < pen2.color().name() )
853 return -1;
854
855 if ( pen1.color().name() > pen2.color().name() )
856 return 1;
857
858 return 0;
859}
860
861
862TQString KSpread::convertRefToBase( const TQString & sheet, const TQRect & rect )
863{
864 TQPoint bottomRight( rect.bottomRight() );
865
866 TQString s( "$" );
867 s += sheet;
868 s += ".$";
869 s += Cell::columnName( bottomRight.x() );
870 s += '$';
871 s += TQString::number( bottomRight.y() );
872
873 return s;
874}
875
876TQString KSpread::convertRefToRange( const TQString & sheet, const TQRect & rect )
877{
878 TQPoint topLeft( rect.topLeft() );
879 TQPoint bottomRight( rect.bottomRight() );
880
881 if ( topLeft == bottomRight )
882 return convertRefToBase( sheet, rect );
883
884 TQString s( "$" );
885 s += sheet;
886 s += ".$";
887 s += /*util_encodeColumnLabelText*/Cell::columnName( topLeft.x() );
888 s += '$';
889 s += TQString::number( topLeft.y() );
890 s += ":.$";
891 s += /*util_encodeColumnLabelText*/Cell::columnName( bottomRight.x() );
892 s += '$';
893 s += TQString::number( bottomRight.y() );
894
895 return s;
896}
897
898//used in Cell::convertFormulaToOasisFormat
899void KSpread::insertBracket( TQString & s )
900{
901 TQChar c;
902 int i = (int) s.length() - 1;
903
904 while ( i >= 0 )
905 {
906 c = s[i];
907 if ( c == ' ' )
908 s[i] = '_';
909 if ( !(c.isLetterOrNumber() || c == ' ' || c == '.'
910 || c == '_') )
911 {
912 s.insert( i + 1, '[' );
913 return;
914 }
915 --i;
916 }
917}
918
919 // e.g.: Sheet4.A1:Sheet4.E28
920 //used in Sheet::saveOasis
921TQString KSpread::convertRangeToRef( const TQString & sheetName, const TQRect & _area )
922{
923 return sheetName + "." + Cell::name( _area.left(), _area.top() ) + ":" + sheetName + "."+ Cell::name( _area.right(), _area.bottom() );
924}
925
926TQString KSpread::convertOasisPenToString( const TQPen & pen )
927{
928// kdDebug()<<"convertOasisPenToString( const TQPen & pen ) :"<<pen<<endl;
929 // NOTE Stefan: TQPen api docs:
930 // For horizontal and vertical lines a line width of 0 is
931 // the same as a line width of 1.
932 // A line width of 0 will produce a 1 pixel wide line using
933 // a fast algorithm for diagonals. A line width of 1 will
934 // also produce a 1 pixel wide line, but uses a slower more
935 // accurate algorithm for diagonals.
936 TQString s = TQString( "%1pt " ).arg( (pen.width() == 0) ? 1 : pen.width() );
937 switch( pen.style() )
938 {
939 case TQt::NoPen:
940 return "none";
941 case TQt::SolidLine:
942 s+="solid";
943 break;
944 case TQt::DashLine:
945 s+="dashed";
946 break;
947 case TQt::DotLine:
948 s+="dotted";
949 break;
950 case TQt::DashDotLine:
951 s+="dot-dash";
952 break;
953 case TQt::DashDotDotLine:
954 s+="dot-dot-dash";
955 break;
956 default: break;
957 }
958 kdDebug()<<" convertOasisPenToString :"<<s<<endl;
959 if ( pen.color().isValid() )
960 {
961 s+=' ';
962 s+=Style::colorName(pen.color());
963 }
964 return s;
965}
966
967TQPen KSpread::convertOasisStringToPen( const TQString &border )
968{
969 TQPen pen;
970 //string like "0.088cm solid #800000"
971 if (border.isEmpty() || border=="none" || border=="hidden") // in fact no border
972 {
973 pen.setStyle( TQt::NoPen );
974 return pen;
975 }
976 //code from koborder, for the moment kspread doesn't use koborder
977 // ## isn't it faster to use TQStringList::split than parse it 3 times?
978 TQString _width = border.section(' ', 0, 0);
979 TQCString _style = border.section(' ', 1, 1).latin1();
980 TQString _color = border.section(' ', 2, 2);
981
982 pen.setWidth( ( int )( KoUnit::parseValue( _width, 1.0 ) ) );
983
984 if ( _style =="none" )
985 pen.setStyle( TQt::NoPen );
986 else if ( _style =="solid" )
987 pen.setStyle( TQt::SolidLine );
988 else if ( _style =="dashed" )
989 pen.setStyle( TQt::DashLine );
990 else if ( _style =="dotted" )
991 pen.setStyle( TQt::DotLine );
992 else if ( _style =="dot-dash" )
993 pen.setStyle( TQt::DashDotLine );
994 else if ( _style =="dot-dot-dash" )
995 pen.setStyle( TQt::DashDotDotLine );
996 else
997 kdDebug()<<" style undefined : "<<_style<<endl;
998
999 if ( _color.isEmpty() )
1000 pen.setColor( TQColor() );
1001 else
1002 pen.setColor( TQColor( _color ) );
1003
1004 return pen;
1005}
1006
1007//Return true when it's a reference to cell from sheet.
1008bool KSpread::localReferenceAnchor( const TQString &_ref )
1009{
1010 bool isLocalRef = (_ref.find("http://") != 0 &&
1011 _ref.find("mailto:") != 0 &&
1012 _ref.find("ftp://") != 0 &&
1013 _ref.find("file:") != 0 );
1014 return isLocalRef;
1015}
1016
1017
1018TQString KSpread::Oasis::decodeFormula(const TQString& expr, const TDELocale* locale)
1019{
1020 // parsing state
1021 enum { Start, InNumber, InString, InIdentifier, InReference, InSheetName } state;
1022
1023 // use locale settings
1024 TQString decimal = locale ? locale->decimalSymbol() : ".";
1025
1026 // initialize variables
1027 state = Start;
1028 unsigned int i = 0;
1029 const TQString ex = expr;
1030 TQString result;
1031
1032 if (ex[0] == '=')
1033 {
1034 result="=";
1035 ++i;
1036 }
1037
1038 // main loop
1039 while( i < ex.length() )
1040 {
1041 TQChar ch = ex[i];
1042
1043 switch( state )
1044 {
1045 case Start:
1046 {
1047 // check for number
1048 if( ch.isDigit() )
1049 {
1050 state = InNumber;
1051 }
1052
1053 // a string?
1054 else if ( ch == '"' )
1055 {
1056 state = InString;
1057 result.append( ex[i++] );
1058 }
1059
1060 // beginning with alphanumeric ?
1061 // could be identifier, cell, range, or function...
1062 else if( isIdentifier( ch ) )
1063 {
1064 state = InIdentifier;
1065 }
1066
1067 // [ marks sheet name for 3-d cell, e.g ['Sales Q3'.A4]
1068 else if ( ch.unicode() == '[' )
1069 {
1070 ++i;
1071 state = InReference;
1072 // NOTE Stefan: As long as KSpread does not support fixed sheets eat the dollar sign.
1073 if ( ex[i] == '$' ) ++i;
1074 }
1075
1076 // decimal dot ?
1077 else if ( ch == '.' )
1078 {
1079 if ( ex[i+1].isDigit() )
1080 state = InNumber;
1081 else
1082 state = InReference;
1083 }
1084
1085 // look for operator match
1086 else
1087 {
1088 int op;
1089 TQString s;
1090
1091 // check for two-chars operator, such as '<=', '>=', etc
1092 s.append( ch ).append( ex[i+1] );
1093 op = matchOperator( s );
1094
1095 // check for one-char operator, such as '+', ';', etc
1096 if( op == Token::InvalidOp )
1097 {
1098 s = TQString( ch );
1099 op = matchOperator( s );
1100 }
1101
1102 // any matched operator ?
1103 if ( op == Token::Equal )
1104 {
1105 result.append( "==" );
1106 }
1107 else
1108 {
1109 result.append( s );
1110 }
1111 if( op != Token::InvalidOp )
1112 {
1113 int len = s.length();
1114 i += len;
1115 }
1116 else
1117 {
1118 ++i;
1119 state = Start;
1120 }
1121 }
1122 break;
1123 }
1124 case InReference:
1125 {
1126 // consume as long as alpha, dollar sign, underscore, or digit, or colon
1127 if( isIdentifier( ch ) || ch.isDigit() || ch == ':' )
1128 result.append( ex[i] );
1129 else if ( ch == '.' && i > 0 && ex[i-1] != '[' && ex[i-1] != ':' )
1130 result.append( '!' );
1131 else if( ch == ']' )
1132 state = Start;
1133 else if ( ch == '\'' )
1134 {
1135 result.append( ex[i] );
1136 state = InSheetName;
1137 // NOTE Stefan: As long as KSpread does not support fixed sheets eat the dollar sign.
1138 if ( ex[i] == '$' ) ++i;
1139 }
1140 else if ( ch != '.' )
1141 {
1142 state = Start;
1143 break;
1144 }
1145 ++i;
1146 break;
1147 }
1148 case InSheetName:
1149 {
1150 if ( ch == '\'' )
1151 state = InReference;
1152 result.append( ex[i] );
1153 ++i;
1154 break;
1155 }
1156 case InNumber:
1157 {
1158 // consume as long as it's digit
1159 if( ch.isDigit() )
1160 result.append( ex[i++] );
1161 // convert '.' to decimal separator
1162 else if ( ch == '.' )
1163 {
1164 result.append( decimal );
1165 ++i;
1166 }
1167 // exponent ?
1168 else if( ch.upper() == 'E' )
1169 {
1170 result.append( 'E' );
1171 ++i;
1172 }
1173 // we're done with integer number
1174 else
1175 state = Start;
1176 break;
1177 }
1178 case InString:
1179 {
1180 // consume until "
1181 if( ch != '"' )
1182 {
1183 result.append( ex[i++] );
1184 }
1185 // we're done
1186 else
1187 {
1188 result.append( ch );
1189 ++i;
1190 state = Start;
1191 }
1192 break;
1193 }
1194 case InIdentifier:
1195 {
1196 // consume as long as alpha, dollar sign, underscore, or digit
1197 if( isIdentifier( ch ) || ch.isDigit() )
1198 result.append( ex[i++] );
1199 // we're done
1200 else
1201 state = Start;
1202 break;
1203 }
1204 default:
1205 break;
1206 }
1207 }
1208 return result;
1209}
1210
1211/*TQString KSpread::Oasis::encodeFormula(const TQString& expr, const TDELocale* locale)
1212{
1213 // TODO move Cell::convertFormulaToOasisFormat to this point
1214 //expr = "not here yet";
1215 //Q_UNUSED(locale);
1216 kdDebug() << k_funcinfo << " not implemented"
1217 tqFatal(0);
1218}*/
For every cell in the spread sheet there is a Cell object.
TQString name() const
Returns the name of the cell.
TQString columnName() const
Returns the column name of the cell.
A map is a simple container for all sheets.
Definition kspread_map.h:59
Sheet * findSheet(const TQString &_name)
searches for a sheet named _name
Represents the position of a single cell in a workbook.
void setSheet(Sheet *sheet)
Sets the sheet which this point lies on.
void setColumnFixed(bool colFixed)
Sets whether or not the column (x coordinate) of this point is fixed (ie.
void setPos(TQPoint pos)
Sets the position of this point (in rows and columns).
void setSheetName(TQString name)
Sets the name of the sheet which this point lies on.
void setRowFixed(bool rowFixed)
Sets whether or not the row (y coordinate) of this point is fixed (ie.
RangeIterator(TQRect _range, Sheet *_sheet)
Contstruct the iterator with the rectangular cell area and which sheet the area is on.
Represents a region within a workbook.
void setRightFixed(bool fixed)
Sets whether or not the right column is fixed.
bool isValid() const
Returns true if this Range represents a valid region of a spreadsheet.
void setSheet(Sheet *sheet)
Sets the Sheet object associated with this range.
bool operator==(const Range &range) const
Returns true if the other range occupies the same area on the same sheet as this range.
void setBottomFixed(bool fixed)
Sets whether or not the bottom row is fixed.
bool intersects(const Range &r) const
Returns true if this range intersects Range r (ie.
void getStartPoint(Point *pt)
Fills a Point with info (row,column,sheet) about the first point in the range.
void setSheetName(TQString sheetName)
Sets the name of the sheet associated with this range.
void setLeftFixed(bool fixed)
Sets whether or not the left column is fixed .
bool contains(const Point &cell) const
Returns true if this range includes the specified cell.
TQString namedArea() const
Returns the named area represented by this range or an empty string otherwise.
void setTopFixed(bool fixed)
Sets whether or not the top row is fixed.
void getEndPoint(Point *pt)
Fills a Point with info (row,column,sheet) about the last point the range.
TQString toString() const
Returns a string representation of this range as it would appear in a formula.
TQRect range() const
Returns the area on the spreadsheet occupied by this range.
static TQString colorName(const TQColor &color)
Returns the name of a colour.
@ InvalidOp
invalid operator
Definition formula.h:61
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.
bool isIdentifier(TQChar ch)
helper function: return true for valid identifier character
Definition formula.cpp:356
Token::Op matchOperator(const TQString &text)
helper function: return operator of given token text e.g.
Definition formula.cpp:130