kspread

kspread_util.cc

00001 /* This file is part of the KDE project
00002    Copyright (C) 2006 Stefan Nikolaus <stefan.nikolaus@kdemail.net>
00003    Copyright (C) 1998, 1999 Torben Weis <weis@kde.org>
00004 
00005    This library is free software; you can redistribute it and/or
00006    modify it under the terms of the GNU Library General Public
00007    License as published by the Free Software Foundation; either
00008    version 2 of the License, or (at your option) any later version.
00009 
00010    This library is distributed in the hope that it will be useful,
00011    but WITHOUT ANY WARRANTY; without even the implied warranty of
00012    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00013    Library General Public License for more details.
00014 
00015    You should have received a copy of the GNU Library General Public License
00016    along with this library; see the file COPYING.LIB.  If not, write to
00017    the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
00018  * Boston, MA 02110-1301, USA.
00019 */
00020 
00021 #include <ctype.h>
00022 
00023 #include <qregexp.h>
00024 
00025 #include <kdebug.h>
00026 
00027 #include "formula.h"
00028 #include "kspread_doc.h"
00029 #include "kspread_locale.h"
00030 #include "kspread_map.h"
00031 #include "kspread_sheet.h"
00032 #include "kspread_style.h"
00033 #include "kspread_util.h"
00034 
00035 using namespace KSpread;
00036 
00037 //helper functions for the formatting
00038 bool KSpread::formatIsDate (FormatType fmt)
00039 {
00040   return ((fmt == ShortDate_format) || (fmt == TextDate_format) ||
00041       (((int) fmt >= 200) && ((int) fmt < 300)));
00042 }
00043 
00044 bool KSpread::formatIsTime (FormatType fmt)
00045 {
00046   return (((int) fmt >= 50) && ((int) fmt < 70));
00047 }
00048 
00049 bool KSpread::formatIsFraction (FormatType fmt)
00050 {
00051   return (((int) fmt >= 70) && ((int) fmt < 80));
00052 }
00053 
00054 
00055 //used in Point::init, Cell::encodeFormula and
00056 //  dialogs/kspread_dlg_paperlayout.cc
00057 int KSpread::util_decodeColumnLabelText( const QString &_col )
00058 {
00059     int col = 0;
00060     int offset='a'-'A';
00061     int counterColumn = 0;
00062     for ( uint i=0; i < _col.length(); i++ )
00063     {
00064         counterColumn = (int) pow(26.0 , static_cast<int>(_col.length() - i - 1));
00065         if( _col[i] >= 'A' && _col[i] <= 'Z' )
00066             col += counterColumn * ( _col[i].latin1() - 'A' + 1);  // okay here (Werner)
00067         else if( _col[i] >= 'a' && _col[i] <= 'z' )
00068             col += counterColumn * ( _col[i].latin1() - 'A' - offset + 1 );
00069         else
00070             kdDebug(36001) << "util_decodeColumnLabelText: Wrong characters in label text for col:'" << _col << "'" << endl;
00071     }
00072     return col;
00073 }
00074 
00075 //used in dialogs/kspread_dlg_paperlayout.cc
00076 QString KSpread::util_rangeColumnName( const QRect &_area)
00077 {
00078     return QString("%1:%2")
00079         .arg( Cell::columnName( _area.left()))
00080         .arg( Cell::columnName(_area.right()));
00081 }
00082 
00083 //used in dialogs/kspread_dlg_paperlayout.cc
00084 QString KSpread::util_rangeRowName( const QRect &_area)
00085 {
00086     return QString("%1:%2")
00087         .arg( _area.top())
00088         .arg(_area.bottom());
00089 }
00090 
00091 QString KSpread::util_rangeName(const QRect &_area)
00092 {
00093     return Cell::name( _area.left(), _area.top() ) + ":" +
00094   Cell::name( _area.right(), _area.bottom() );
00095 }
00096 
00097 QString KSpread::util_rangeName(Sheet * _sheet, const QRect &_area)
00098 {
00099     return _sheet->sheetName() + "!" + util_rangeName(_area);
00100 }
00101 
00102 QDomElement KSpread::util_createElement( const QString & tagName, const QFont & font, QDomDocument & doc )
00103 {
00104   QDomElement e( doc.createElement( tagName ) );
00105 
00106   e.setAttribute( "family", font.family() );
00107   e.setAttribute( "size", font.pointSize() );
00108   e.setAttribute( "weight", font.weight() );
00109   if ( font.bold() )
00110     e.setAttribute( "bold", "yes" );
00111   if ( font.italic() )
00112     e.setAttribute( "italic", "yes" );
00113   if ( font.underline() )
00114     e.setAttribute( "underline", "yes" );
00115   if ( font.strikeOut() )
00116     e.setAttribute( "strikeout", "yes" );
00117   //e.setAttribute( "charset", KGlobal::charsets()->name( font ) );
00118 
00119   return e;
00120 }
00121 
00122 QDomElement KSpread::util_createElement( const QString & tagname, const QPen & pen, QDomDocument & doc )
00123 {
00124   QDomElement e( doc.createElement( tagname ) );
00125   e.setAttribute( "color", pen.color().name() );
00126   e.setAttribute( "style", (int)pen.style() );
00127   e.setAttribute( "width", (int)pen.width() );
00128   return e;
00129 }
00130 
00131 QFont KSpread::util_toFont( QDomElement & element )
00132 {
00133   QFont f;
00134   f.setFamily( element.attribute( "family" ) );
00135 
00136   bool ok;
00137   f.setPointSize( element.attribute("size").toInt( &ok ) );
00138   if ( !ok )
00139     return QFont();
00140 
00141   f.setWeight( element.attribute("weight").toInt( &ok ) );
00142   if ( !ok )
00143     return QFont();
00144 
00145   if ( element.hasAttribute( "italic" ) && element.attribute("italic") == "yes" )
00146     f.setItalic( true );
00147 
00148   if ( element.hasAttribute( "bold" ) && element.attribute("bold") == "yes" )
00149     f.setBold( true );
00150 
00151   if ( element.hasAttribute( "underline" ) && element.attribute("underline") == "yes" )
00152     f.setUnderline( true );
00153 
00154   if ( element.hasAttribute( "strikeout" ) && element.attribute("strikeout") == "yes" )
00155     f.setStrikeOut( true );
00156 
00157   /* Uncomment when charset is added to kspread_dlg_layout
00158      + save a document-global charset
00159      if ( element.hasAttribute( "charset" ) )
00160        KGlobal::charsets()->setQFont( f, element.attribute("charset") );
00161       else
00162   */
00163   // ######## Not needed anymore in 3.0?
00164   //KGlobal::charsets()->setQFont( f, KGlobal::locale()->charset() );
00165 
00166   return f;
00167 }
00168 
00169 QPen KSpread::util_toPen( QDomElement & element )
00170 {
00171   bool ok;
00172   QPen p;
00173 
00174   p.setStyle( (Qt::PenStyle)element.attribute("style").toInt( &ok ) );
00175   if ( !ok )
00176     return QPen();
00177 
00178   p.setWidth( element.attribute("width").toInt( &ok ) );
00179   if ( !ok )
00180     return QPen();
00181 
00182   p.setColor( QColor( element.attribute("color") ) );
00183 
00184   return p;
00185 }
00186 
00187 Point::Point(const QString & _str)
00188 {
00189     _sheet = 0;
00190     init(_str);
00191 }
00192 
00193 void Point::setPos(QPoint pos)
00194 {
00195     _pos=pos;
00196 }
00197 QPoint Point::pos() const
00198 {
00199     return _pos;
00200 }
00201 void Point::setSheet(Sheet* sheet)
00202 {
00203     _sheet=sheet;
00204 }
00205 KSpread::Sheet* Point::sheet() const
00206 {
00207     return _sheet;
00208 }
00209 void Point::setSheetName(QString name)
00210 {
00211     _sheetName=name;
00212 }
00213 QString Point::sheetName() const
00214 {
00215     return _sheetName;
00216 }
00217 void Point::setColumnFixed(bool colFixed)
00218 {
00219     _columnFixed=colFixed;
00220 }
00221 bool Point::columnFixed() const
00222 {
00223     return _columnFixed;
00224 }
00225 void Point::setRowFixed(bool rowFixed)
00226 {
00227     _rowFixed=rowFixed;
00228 }
00229 bool Point::rowFixed() const
00230 {
00231     return _rowFixed;
00232 }
00233 
00234 
00235 void Point::init(const QString & _str)
00236 {
00237     _columnFixed=false;
00238     _rowFixed=false;
00239 
00240 //    kdDebug(36001) <<"Point::init ("<<_str<<")"<<endl;
00241     _pos.setX(-1);
00242 
00243     uint len = _str.length();
00244     if ( !len )
00245     {
00246   kdDebug(36001) << "Point::init: len = 0" << endl;
00247   return;
00248     }
00249 
00250     QString str( _str );
00251     int n = _str.find( '!' );
00252     if ( n != -1 )
00253     {
00254       _sheetName = _str.left( n );
00255       str = _str.right( len - n - 1 ); // remove the '!'
00256       len = str.length();
00257     }
00258 
00259     uint p = 0;
00260 
00261     // Fixed ?
00262     if ( str[0] == '$' )
00263     {
00264   _columnFixed = true;
00265   p++;
00266     }
00267     else
00268   _columnFixed = false;
00269 
00270     // Malformed ?
00271     if ( p == len )
00272     {
00273   kdDebug(36001) << "Point::init: no point after '$' (str: '" << str.mid( p ) << "'" << endl;
00274   return;
00275     }
00276     if ( str[p] < 'A' || str[p] > 'Z' )
00277     {
00278   if ( str[p] < 'a' || str[p] > 'z' )
00279   {
00280       kdDebug(36001) << "Point::init: wrong first character in point (str: '" << str.mid( p ) << "'" << endl;
00281       return;
00282   }
00283     }
00284     //default is error
00285     int x = -1;
00286     //search for the first character != text
00287     int result = str.find( QRegExp("[^A-Za-z]+"), p );
00288 
00289     //get the colomn number for the character between actual position and the first non text charakter
00290     if ( result != -1 )
00291   x = util_decodeColumnLabelText( str.mid( p, result - p ) ); // x is defined now
00292     else  // If there isn't any, then this is not a point -> return
00293     {
00294   kdDebug(36001) << "Point::init: no number in string (str: '" << str.mid( p, result ) << "'" << endl;
00295   return;
00296     }
00297     p = result;
00298 
00299     //limit is KS_colMax
00300     if ( x > KS_colMax )
00301     {
00302   kdDebug(36001) << "Point::init: column value too high (col: " << x << ")" << endl;
00303   return;
00304     }
00305 
00306     // Malformed ?
00307     if (p == len)
00308     {
00309   kdDebug(36001) << "Point::init: p==len after cols" << endl;
00310   return;
00311     }
00312 
00313     if (str[p] == '$')
00314     {
00315   _rowFixed = true;
00316   p++;
00317   // Malformed ?
00318   if ( p == len )
00319   {
00320       kdDebug(36001) << "Point::init: p==len after $ of row" << endl;
00321       return;
00322   }
00323     }
00324     else
00325   _rowFixed = false;
00326 
00327     uint p2 = p;
00328     while ( p < len )
00329     {
00330   if ( !isdigit( QChar(str[p++]) ) )
00331   {
00332       kdDebug(36001) << "Point::init: no number" << endl;
00333       return;
00334   }
00335     }
00336 
00337     bool ok;
00338     int y = str.mid( p2, p-p2 ).toInt( &ok );
00339     if ( !ok )
00340     {
00341   kdDebug(36001) << "Point::init: Invalid number (str: '" << str.mid( p2, p-p2 ) << "'" << endl;
00342   return;
00343     }
00344     if ( y > KS_rowMax )
00345     {
00346   kdDebug(36001) << "Point::init: row value too high (row: " << y << ")" << endl;
00347   return;
00348     }
00349     if ( y <= 0 )
00350     {
00351   kdDebug(36001) << "Point::init: y <= 0" << endl;
00352   return;
00353     }
00354     _pos = QPoint( x, y );
00355 }
00356 
00357 bool util_isPointValid( QPoint point )
00358 {
00359     if (    point.x() >= 1
00360         &&  point.y() >= 1
00361         &&  point.x() <= KS_colMax
00362         &&  point.y() <= KS_rowMax
00363        )
00364         return true;
00365     else
00366         return false;
00367 }
00368 
00369 bool util_isRectValid( QRect rect )
00370 {
00371     if (    util_isPointValid( rect.topLeft() )
00372         &&  util_isPointValid( rect.bottomRight() )
00373        )
00374         return true;
00375     else
00376         return false;
00377 }
00378 
00379 Point::Point( const QString & str, Map * map,
00380                             Sheet * sheet )
00381 {
00382 
00383     uint p = 0;
00384     int p2 = str.find( '!' );
00385     if ( p2 != -1 )
00386     {
00387         _sheetName = str.left( p2++ );
00388         while ( true )
00389         {
00390             _sheet = map->findSheet( _sheetName );
00391             if ( !sheet && _sheetName[0] == ' ' )
00392             {
00393                 _sheetName = _sheetName.right( _sheetName.length() - 1 );
00394                 continue;
00395             }
00396             break;
00397         }
00398         p = p2;
00399 
00400         //If the loop didn't return a sheet, better keep a string for isValid
00401         if ( _sheetName.isEmpty() )
00402         {
00403             kdDebug(36001) << "Point: tableName is unknown" << endl;
00404             _sheetName = "unknown";
00405         }
00406     }
00407     else
00408     {
00409         if ( sheet != 0 )
00410         {
00411             _sheet = sheet;
00412             _sheetName = sheet->sheetName();
00413         }
00414         else
00415             _sheet = 0;
00416     }
00417 
00418     init( str.mid( p ) );
00419 }
00420 
00421 Cell *Point::cell() const
00422 {
00423     return _sheet->cellAt(_pos);
00424 }
00425 
00426 bool Point::operator== (const Point &cell) const
00427 {
00428   //sheet info ignored
00429   return (_pos == cell.pos());
00430 }
00431 
00432 bool Point::operator< (const Point &cell) const
00433 {
00434   //sheet info ignored
00435   return (pos().y() < cell.pos().y()) ? true :
00436       ((pos().y() == cell.pos().y()) && (pos().x() < cell.pos().x()));
00437 }
00438 
00439 bool Range::operator ==(const Range& otherRange) const
00440 {
00441     if (    _range == otherRange._range
00442         &&  _leftFixed == otherRange._leftFixed
00443         &&  _rightFixed == otherRange._rightFixed
00444         &&  _bottomFixed == otherRange._bottomFixed
00445         &&  _topFixed == otherRange._topFixed
00446         &&  _sheet == otherRange._sheet )
00447             return true;
00448     else
00449             return false;
00450 }
00451 
00452 Range::Range()
00453 {
00454     _sheet = 0;
00455     _range.setLeft( -1 );
00456 
00457     _leftFixed=false;
00458     _rightFixed=false;
00459     _topFixed=false;
00460     _bottomFixed=false;
00461 }
00462 Range::Range(const QString & _str)
00463 {
00464     _range.setLeft(-1);
00465     _sheet = 0;
00466 
00467     int p = _str.find(':');
00468  //   if (p == -1)
00469  // return;
00470 
00471     Point ul;
00472     Point lr; ;
00473 
00474     if ( p != -1)
00475     {
00476         ul = Point(_str.left(p));
00477         lr = Point(_str.mid(p + 1));
00478     }
00479     else
00480     {
00481         ul = Point(_str);
00482         lr = ul;
00483     }
00484 
00485     _range = QRect(ul.pos(), lr.pos());
00486     _sheetName = ul.sheetName();
00487 
00488     _leftFixed = ul.columnFixed();
00489     _rightFixed = lr.columnFixed();
00490     _topFixed = ul.rowFixed();
00491     _bottomFixed = lr.rowFixed();
00492 }
00493 
00494  Range::Range( const Range& r )
00495  {
00496     _sheet = r.sheet();
00497     _sheetName = r.sheetName();
00498     _range = r.range();
00499     _namedArea = r.namedArea();
00500 
00501     _leftFixed=r._leftFixed;
00502     _rightFixed=r._rightFixed;
00503     _topFixed=r._topFixed;
00504     _bottomFixed=r._bottomFixed;
00505   }
00506 
00507  Range::Range( const Point& ul, const Point& lr )
00508   {
00509     _range = QRect( ul.pos(), lr.pos() );
00510     if ( ul.sheetName() != lr.sheetName() )
00511     {
00512       _range.setLeft( -1 );
00513       return;
00514     }
00515     _sheetName = ul.sheetName();
00516     _sheet = ul.sheet();
00517     _leftFixed = ul.columnFixed();
00518     _rightFixed = lr.columnFixed();
00519     _topFixed = ul.rowFixed();
00520     _bottomFixed = lr.rowFixed();
00521   }
00522 
00523 Range::Range(const QString & str, Map * map,
00524          Sheet * sheet)
00525 {
00526   _range.setLeft(-1);
00527   _sheet = 0;
00528 
00529   //try to parse as named area
00530   bool gotNamed = false;
00531   QString tmp = str.lower();
00532   QValueList < Reference >::Iterator it;
00533   QValueList < Reference > area = map->doc()->listArea();
00534   for (it = area.begin(); it != area.end(); ++it) {
00535     if ((*it).ref_name.lower() == tmp) {
00536       // success - such named area exists
00537       _range = (*it).rect;
00538       _sheet = map->findSheet((*it).sheet_name);
00539       gotNamed = true;
00540       _namedArea = tmp;
00541       break;
00542     }
00543   }
00544   if (gotNamed) {
00545     // we have a named area - no need to proceed further
00546     _leftFixed = false;
00547     _rightFixed = false;
00548     _topFixed = false;
00549     _bottomFixed = false;
00550     return;
00551   }
00552 
00553     _range.setLeft(-1);
00554     _sheet = 0;
00555 
00556     int p = 0;
00557     int p2 = str.find('!');
00558     if (p2 != -1)
00559     {
00560       _sheetName = str.left(p2++);
00561       while ( true )
00562       {
00563         _sheet = map->findSheet(_sheetName);
00564 
00565         if ( !_sheet && _sheetName[0] == ' ' )
00566         {
00567           _sheetName = _sheetName.right( _sheetName.length() - 1 );
00568           continue;
00569         }
00570         break;
00571       }
00572       p = p2;
00573     } else
00574       _sheet = sheet;
00575 
00576 
00577     int p3 = str.find(':', p);
00578     if (p3 == -1)
00579   return;
00580 
00581     Point ul(str.mid(p, p3 - p));
00582     Point lr(str.mid(p3 + 1));
00583     _range = QRect(ul.pos(), lr.pos());
00584 
00585     _leftFixed = ul.columnFixed();
00586     _rightFixed = lr.columnFixed();
00587     _topFixed = ul.rowFixed();
00588     _bottomFixed = lr.rowFixed();
00589 }
00590 
00591 QString Range::toString() const
00592 {
00593   QString result;
00594 
00595   if (_sheet)
00596   {
00597     result=util_rangeName(_sheet,_range);
00598   }
00599   else
00600   {
00601     result=util_rangeName(_range);
00602   }
00603 
00604   //Insert $ characters to show fixed parts of range
00605 
00606   int pos=result.find("!")+1;
00607   Q_ASSERT(pos != -1);
00608 
00609   if (_leftFixed)
00610   {
00611     result.insert(pos,'$');
00612     pos++; //Takes account of extra character added in
00613   }
00614   if (_topFixed)
00615   {
00616     result.insert(pos+Cell::columnName(_range.left()).length(),'$');
00617   }
00618 
00619   pos=result.find(":")+1;
00620   Q_ASSERT(pos != -1);
00621 
00622   if (_rightFixed)
00623   {
00624     result.insert(pos,'$');
00625     pos++; //Takes account of extra character added in
00626   }
00627   if (_bottomFixed)
00628   {
00629     result.insert(pos+Cell::columnName(_range.right()).length(),'$');
00630   }
00631 
00632 
00633   return result;
00634 }
00635 
00636 void Range::getStartPoint(Point* pt)
00637 {
00638   if (!isValid()) return;
00639 
00640   pt->setRow(startRow());
00641   pt->setColumn(startCol());
00642   pt->setColumnFixed(_leftFixed);
00643   pt->setRowFixed(_topFixed);
00644   pt->setSheet(_sheet);
00645   pt->setSheetName(_sheetName);
00646 }
00647 
00648 void Range::getEndPoint(Point* pt)
00649 {
00650   if (!isValid()) return;
00651 
00652   pt->setRow(endRow());
00653   pt->setColumn(endCol());
00654   pt->setColumnFixed(_rightFixed);
00655   pt->setRowFixed(_bottomFixed);
00656   pt->setSheet(_sheet);
00657   pt->setSheetName(_sheetName);
00658 }
00659 
00660 bool Range::contains (const Point &cell) const
00661 {
00662   return _range.contains (cell.pos());
00663 }
00664 
00665 bool Range::intersects (const Range &r) const
00666 {
00667   return _range.intersects (r.range());
00668 }
00669 
00670 bool Range::isValid() const
00671 {
00672   return  ( _range.left() >= 0 ) &&
00673     ( _range.right() >= 0 ) &&
00674     ( _sheet != 0 || _sheetName.isEmpty() ) &&
00675     ( _range.isValid() ) ;
00676 }
00677 
00678 QRect Range::range() const
00679 {
00680     return _range;
00681 }
00682 
00683 void Range::setLeftFixed(bool fixed)
00684 {
00685     _leftFixed=fixed;
00686 }
00687 bool Range::leftFixed() const
00688 {
00689     return _leftFixed;
00690 }
00691 void Range::setRightFixed(bool fixed)
00692 {
00693     _rightFixed=fixed;
00694 }
00695 bool Range::rightFixed() const
00696 {
00697     return _rightFixed;
00698 }
00699 void Range::setTopFixed(bool fixed)
00700 {
00701     _topFixed=fixed;
00702 }
00703 bool Range::topFixed() const
00704 {
00705     return _topFixed;
00706 }
00707 void Range::setBottomFixed(bool fixed)
00708 {
00709     _bottomFixed=fixed;
00710 }
00711 bool Range::bottomFixed() const
00712 {
00713     return _bottomFixed;
00714 }
00715 void Range::setSheet(Sheet* sheet)
00716 {
00717     _sheet=sheet;
00718 }
00719 KSpread::Sheet* Range::sheet() const
00720 {
00721     return _sheet;
00722 }
00723 void Range::setSheetName(QString sheetName)
00724 {
00725     _sheetName=sheetName;
00726 }
00727 QString Range::sheetName() const
00728 {
00729     return _sheetName;
00730 }
00731 QString Range::namedArea() const
00732 {
00733     return _namedArea;
00734 }
00735 
00736 
00737 bool KSpread::util_isAllSelected(const QRect &selection)
00738 {
00739   return ( selection.top() == 1 && selection.bottom() == KS_rowMax
00740      && selection.left() == 1 && selection.right() == KS_colMax);
00741 }
00742 
00743 bool KSpread::util_isColumnSelected(const QRect &selection)
00744 {
00745   return ( (selection.top() == 1) && (selection.bottom() == KS_rowMax) );
00746 }
00747 
00748 bool KSpread::util_isRowSelected(const QRect &selection)
00749 {
00750   return ( (selection.left() == 1) && (selection.right() == KS_colMax) );
00751 }
00752 
00753 bool KSpread::util_isRowOrColumnSelected(const QRect &selection)
00754 {
00755     return ( (selection.left() == 1) && (selection.right() == KS_colMax)
00756              || (selection.top() == 1) && (selection.bottom() == KS_rowMax) );
00757 }
00758 
00759 //used in View::slotRename
00760 bool KSpread::util_validateSheetName(const QString &name)
00761 {
00762   if (name[0] == ' ')
00763   {
00764     return false;
00765   }
00766   for (unsigned int i = 0; i < name.length(); i++)
00767   {
00768     if ( !(name[i].isLetterOrNumber() ||
00769            name[i] == ' ' || name[i] == '.' ||
00770            name[i] == '_'))
00771     {
00772       return false;
00773     }
00774   }
00775   return true;
00776 }
00777 
00778 
00779 RangeIterator::RangeIterator(QRect _range, Sheet* _sheet)
00780 {
00781   range = _range;
00782   sheet = _sheet;
00783   current = QPoint(0,0);
00784 }
00785 
00786 RangeIterator::~RangeIterator()
00787 {
00788 }
00789 
00790 Cell* RangeIterator::first()
00791 {
00792   current.setY(range.top());
00793 
00794   /* OK, because even if this equals zero, the 'getNextCellRight' won't
00795      try to access it*/
00796   current.setX(range.left() - 1);
00797   return next();
00798 }
00799 
00800 Cell* RangeIterator::next()
00801 {
00802   if (current.x() == 0 && current.y() == 0)
00803   {
00804     return first();
00805   }
00806 
00807   Cell* cell = NULL;
00808   bool done = false;
00809 
00810   while (cell == NULL && !done)
00811   {
00812     cell = sheet->getNextCellRight(current.x(), current.y());
00813     if (cell != NULL && cell->column() > range.right())
00814     {
00815       cell = NULL;
00816     }
00817 
00818     if (cell == NULL)
00819     {
00820       current.setX(range.left() - 1);
00821       current.setY(current.y() + 1);
00822       done = (current.y() > range.bottom());
00823     }
00824   }
00825   return cell;
00826 }
00827 
00828 //not used anywhere
00829 int KSpread::util_penCompare( QPen const & pen1, QPen const & pen2 )
00830 {
00831   if ( pen1.style() == Qt::NoPen && pen2.style() == Qt::NoPen )
00832     return 0;
00833 
00834   if ( pen1.style() == Qt::NoPen )
00835     return -1;
00836 
00837   if ( pen2.style() == Qt::NoPen )
00838     return 1;
00839 
00840   if ( pen1.width() < pen2.width() )
00841     return -1;
00842 
00843   if ( pen1.width() > pen2.width() )
00844     return 1;
00845 
00846   if ( pen1.style() < pen2.style() )
00847     return -1;
00848 
00849   if ( pen1.style() > pen2.style() )
00850     return 1;
00851 
00852   if ( pen1.color().name() < pen2.color().name() )
00853     return -1;
00854 
00855   if ( pen1.color().name() > pen2.color().name() )
00856     return 1;
00857 
00858   return 0;
00859 }
00860 
00861 
00862 QString KSpread::convertRefToBase( const QString & sheet, const QRect & rect )
00863 {
00864   QPoint bottomRight( rect.bottomRight() );
00865 
00866   QString s( "$" );
00867   s += sheet;
00868   s += ".$";
00869   s += Cell::columnName( bottomRight.x() );
00870   s += '$';
00871   s += QString::number( bottomRight.y() );
00872 
00873   return s;
00874 }
00875 
00876 QString KSpread::convertRefToRange( const QString & sheet, const QRect & rect )
00877 {
00878   QPoint topLeft( rect.topLeft() );
00879   QPoint bottomRight( rect.bottomRight() );
00880 
00881   if ( topLeft == bottomRight )
00882     return convertRefToBase( sheet, rect );
00883 
00884   QString s( "$" );
00885   s += sheet;
00886   s += ".$";
00887   s += /*util_encodeColumnLabelText*/Cell::columnName( topLeft.x() );
00888   s += '$';
00889   s += QString::number( topLeft.y() );
00890   s += ":.$";
00891   s += /*util_encodeColumnLabelText*/Cell::columnName( bottomRight.x() );
00892   s += '$';
00893   s += QString::number( bottomRight.y() );
00894 
00895   return s;
00896 }
00897 
00898 //used in Cell::convertFormulaToOasisFormat
00899 void KSpread::insertBracket( QString & s )
00900 {
00901   QChar c;
00902   int i = (int) s.length() - 1;
00903 
00904   while ( i >= 0 )
00905   {
00906     c = s[i];
00907     if ( c == ' ' )
00908       s[i] = '_';
00909     if ( !(c.isLetterOrNumber() || c == ' ' || c == '.'
00910            || c == '_') )
00911     {
00912       s.insert( i + 1, '[' );
00913       return;
00914     }
00915     --i;
00916   }
00917 }
00918 
00919  // e.g.: Sheet4.A1:Sheet4.E28
00920  //used in Sheet::saveOasis
00921 QString KSpread::convertRangeToRef( const QString & sheetName, const QRect & _area )
00922 {
00923     return sheetName + "." + Cell::name( _area.left(), _area.top() ) + ":" + sheetName + "."+ Cell::name( _area.right(), _area.bottom() );
00924 }
00925 
00926 QString KSpread::convertOasisPenToString( const QPen & pen )
00927 {
00928     kdDebug()<<"convertOasisPenToString( const QPen & pen ) :"<<pen<<endl;
00929     QString s = QString( "%1pt " ).arg( pen.width() );
00930     switch( pen.style() )
00931     {
00932     case Qt::NoPen:
00933         return "none";
00934     case Qt::SolidLine:
00935         s+="solid";
00936         break;
00937     case Qt::DashLine:
00938         s+="dashed";
00939         break;
00940     case Qt::DotLine:
00941         s+="dotted";
00942         break;
00943     case Qt::DashDotLine:
00944         s+="dot-dash";
00945         break;
00946     case Qt::DashDotDotLine:
00947         s+="dot-dot-dash";
00948         break;
00949     default: break;
00950     }
00951     kdDebug()<<" convertOasisPenToString :"<<s<<endl;
00952     if ( pen.color().isValid() )
00953     {
00954         s+=' ';
00955         s+=Style::colorName(pen.color());
00956     }
00957     return s;
00958 }
00959 
00960 QPen KSpread::convertOasisStringToPen( const QString &border )
00961 {
00962     QPen pen;
00963     //string like "0.088cm solid #800000"
00964     if (border.isEmpty() || border=="none" || border=="hidden") // in fact no border
00965     {
00966         pen.setStyle( Qt::NoPen );
00967         return pen;
00968     }
00969     //code from koborder, for the moment kspread doesn't use koborder
00970     // ## isn't it faster to use QStringList::split than parse it 3 times?
00971     QString _width = border.section(' ', 0, 0);
00972     QCString _style = border.section(' ', 1, 1).latin1();
00973     QString _color = border.section(' ', 2, 2);
00974 
00975     pen.setWidth( ( int )( KoUnit::parseValue( _width, 1.0 ) ) );
00976 
00977     if ( _style =="none" )
00978         pen.setStyle( Qt::NoPen );
00979     else if ( _style =="solid" )
00980         pen.setStyle( Qt::SolidLine );
00981     else if ( _style =="dashed" )
00982         pen.setStyle( Qt::DashLine );
00983     else if ( _style =="dotted" )
00984         pen.setStyle( Qt::DotLine );
00985     else if ( _style =="dot-dash" )
00986         pen.setStyle( Qt::DashDotLine );
00987     else if ( _style =="dot-dot-dash" )
00988         pen.setStyle( Qt::DashDotDotLine );
00989     else
00990         kdDebug()<<" style undefined : "<<_style<<endl;
00991 
00992     if ( _color.isEmpty() )
00993         pen.setColor( QColor() );
00994     else
00995         pen.setColor(  QColor( _color ) );
00996 
00997     return pen;
00998 }
00999 
01000 //Return true when it's a reference to cell from sheet.
01001 bool KSpread::localReferenceAnchor( const QString &_ref )
01002 {
01003     bool isLocalRef = (_ref.find("http://") != 0 &&
01004                        _ref.find("mailto:") != 0 &&
01005                        _ref.find("ftp://") != 0  &&
01006                        _ref.find("file:") != 0 );
01007     return isLocalRef;
01008 }
01009 
01010 
01011 QString KSpread::Oasis::decodeFormula(const QString& expr, const KLocale* locale)
01012 {
01013   // parsing state
01014   enum { Start, InNumber, InString, InIdentifier, InReference, InSheetName } state;
01015 
01016   // use locale settings
01017   QString decimal = locale ? locale->decimalSymbol() : ".";
01018 
01019   // initialize variables
01020   state = Start;
01021   unsigned int i = 0;
01022   const QString ex = expr;
01023   QString result;
01024 
01025   if (ex[0] == '=')
01026   {
01027     result="=";
01028     ++i;
01029   }
01030 
01031   // main loop
01032   while( i < ex.length() )
01033   {
01034     QChar ch = ex[i];
01035 
01036     switch( state )
01037     {
01038     case Start:
01039     {
01040        // check for number
01041        if( ch.isDigit() )
01042        {
01043          state = InNumber;
01044        }
01045 
01046        // a string?
01047        else if ( ch == '"' )
01048        {
01049          state = InString;
01050          result.append( ex[i++] );
01051        }
01052 
01053        // beginning with alphanumeric ?
01054        // could be identifier, cell, range, or function...
01055        else if( isIdentifier( ch ) )
01056        {
01057          state = InIdentifier;
01058        }
01059 
01060        // [ marks sheet name for 3-d cell, e.g ['Sales Q3'.A4]
01061        else if ( ch.unicode() == '[' )
01062        {
01063          ++i;
01064          state = InReference;
01065        }
01066 
01067        // decimal dot ?
01068        else if ( ch == '.' )
01069        {
01070          state = InNumber;
01071        }
01072 
01073        // look for operator match
01074        else
01075        {
01076          int op;
01077          QString s;
01078 
01079          // check for two-chars operator, such as '<=', '>=', etc
01080          s.append( ch ).append( ex[i+1] );
01081          op = matchOperator( s );
01082 
01083          // check for one-char operator, such as '+', ';', etc
01084          if( op == Token::InvalidOp )
01085          {
01086            s = QString( ch );
01087            op = matchOperator( s );
01088          }
01089 
01090          // any matched operator ?
01091          if (  op == Token::Equal )
01092          {
01093            result.append( "==" );
01094          }
01095          else
01096          {
01097            result.append( s );
01098          }
01099          if( op != Token::InvalidOp )
01100          {
01101            int len = s.length();
01102            i += len;
01103          }
01104          else
01105          {
01106            ++i;
01107            state = Start;
01108          }
01109         }
01110        break;
01111     }
01112     case InReference:
01113     {
01114        // consume as long as alpha, dollar sign, underscore, or digit, or colon
01115        if( isIdentifier( ch )  || ch.isDigit() || ch == ':' )
01116          result.append( ex[i] );
01117        else if ( ch == '.' && ex[i-1] != '[' && ex[i-1] != ':' )
01118          result.append( '!' );
01119        else if( ch == ']' )
01120          state = Start;
01121        else if ( ch.unicode() == 39 )
01122        {
01123          result.append( ex[i] );
01124          state = InSheetName;
01125        }
01126        ++i;
01127        break;
01128     }
01129     case InSheetName:
01130     {
01131       if ( ch.unicode() == 39 )
01132         state = InReference;
01133       result.append( ex[i] );
01134       ++i;
01135       break;
01136     }
01137     case InNumber:
01138     {
01139        // consume as long as it's digit
01140        if( ch.isDigit() )
01141          result.append( ex[i++] );
01142        // convert '.' to decimal separator
01143        else if ( ch == '.' )
01144        {
01145          result.append( decimal );
01146          ++i;
01147        }
01148        // exponent ?
01149        else if( ch.upper() == 'E' )
01150        {
01151          result.append( 'E' );
01152          ++i;
01153        }
01154        // we're done with integer number
01155        else
01156          state = Start;
01157        break;
01158     }
01159     case InString:
01160     {
01161        // consume until "
01162        if( ch != '"' )
01163        {
01164          result.append( ex[i++] );
01165        }
01166        // we're done
01167        else
01168        {
01169          result.append( ch );
01170          ++i;
01171          state = Start;
01172        }
01173        break;
01174     }
01175     case InIdentifier:
01176     {
01177        // consume as long as alpha, dollar sign, underscore, or digit
01178       if( isIdentifier( ch )  || ch.isDigit() )
01179         result.append( ex[i++] );
01180        // we're done
01181       else
01182         state = Start;
01183       break;
01184     }
01185     default:
01186        break;
01187     }
01188   }
01189   return result;
01190 }
01191 
01192 /*QString KSpread::Oasis::encodeFormula(const QString& expr, const KLocale* locale)
01193 {
01194   // TODO move Cell::convertFormulaToOasisFormat to this point
01195   //expr = "not here yet";
01196   //Q_UNUSED(locale);
01197   kdDebug() << k_funcinfo << " not implemented"
01198   qFatal(0);
01199 }*/
KDE Home | KDE Accessibility Home | Description of Access Keys