/* This file is part of the KDE project
   Copyright (C) 2006 Stefan Nikolaus <stefan.nikolaus@kdemail.net>
   Copyright (C) 1998, 1999 Torben Weis <weis@kde.org>

   This library is free software; you can redistribute it and/or
   modify it under the terms of the GNU Library General Public
   License as published by the Free Software Foundation; either
   version 2 of the License, or (at your option) any later version.

   This library is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
   Library General Public License for more details.

   You should have received a copy of the GNU Library General Public License
   along with this library; see the file COPYING.LIB.  If not, write to
   the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
 * Boston, MA 02110-1301, USA.
*/

#include <ctype.h>

#include <tqregexp.h>

#include <kdebug.h>

#include "formula.h"
#include "kspread_doc.h"
#include "kspread_locale.h"
#include "kspread_map.h"
#include "kspread_sheet.h"
#include "kspread_style.h"
#include "kspread_util.h"

using namespace KSpread;

//helper functions for the formatting
bool KSpread::formatIsDate (FormatType fmt)
{
  return ((fmt == ShortDate_format) || (fmt == TextDate_format) ||
      (((int) fmt >= 200) && ((int) fmt < 300)));
}

bool KSpread::formatIsTime (FormatType fmt)
{
  return (((int) fmt >= 50) && ((int) fmt < 70));
}

bool KSpread::formatIsFraction (FormatType fmt)
{
  return (((int) fmt >= 70) && ((int) fmt < 80));
}


//used in Point::init, Cell::encodeFormula and
//  dialogs/kspread_dlg_paperlayout.cpp
int KSpread::util_decodeColumnLabelText( const TQString &_col )
{
    int col = 0;
    int offset='a'-'A';
    int counterColumn = 0;
    for ( uint i=0; i < _col.length(); i++ )
    {
        counterColumn = (int) pow(26.0 , static_cast<int>(_col.length() - i - 1));
        if( (int)(_col[i]) >= 'A' && (int)(_col[i]) <= 'Z' )
            col += counterColumn * ( _col[i].latin1() - 'A' + 1);  // okay here (Werner)
        else if( (int)(_col[i]) >= 'a' && (int)(_col[i]) <= 'z' )
            col += counterColumn * ( _col[i].latin1() - 'A' - offset + 1 );
        else
            kdDebug(36001) << "util_decodeColumnLabelText: Wrong characters in label text for col:'" << _col << "'" << endl;
    }
    return col;
}

//used in dialogs/kspread_dlg_paperlayout.cpp
TQString KSpread::util_rangeColumnName( const TQRect &_area)
{
    return TQString("%1:%2")
        .arg( Cell::columnName( _area.left()))
        .arg( Cell::columnName(_area.right()));
}

//used in dialogs/kspread_dlg_paperlayout.cpp
TQString KSpread::util_rangeRowName( const TQRect &_area)
{
    return TQString("%1:%2")
        .arg( _area.top())
        .arg(_area.bottom());
}

TQString KSpread::util_rangeName(const TQRect &_area)
{
    return Cell::name( _area.left(), _area.top() ) + ":" +
  Cell::name( _area.right(), _area.bottom() );
}

TQString KSpread::util_rangeName(Sheet * _sheet, const TQRect &_area)
{
    return _sheet->sheetName() + "!" + util_rangeName(_area);
}

TQDomElement KSpread::util_createElement( const TQString & tagName, const TQFont & font, TQDomDocument & doc )
{
  TQDomElement e( doc.createElement( tagName ) );

  e.setAttribute( "family", font.family() );
  e.setAttribute( "size", font.pointSize() );
  e.setAttribute( "weight", font.weight() );
  if ( font.bold() )
    e.setAttribute( "bold", "yes" );
  if ( font.italic() )
    e.setAttribute( "italic", "yes" );
  if ( font.underline() )
    e.setAttribute( "underline", "yes" );
  if ( font.strikeOut() )
    e.setAttribute( "strikeout", "yes" );
  //e.setAttribute( "charset", TDEGlobal::charsets()->name( font ) );

  return e;
}

TQDomElement KSpread::util_createElement( const TQString & tagname, const TQPen & pen, TQDomDocument & doc )
{
  TQDomElement e( doc.createElement( tagname ) );
  e.setAttribute( "color", pen.color().name() );
  e.setAttribute( "style", (int)pen.style() );
  e.setAttribute( "width", (int)pen.width() );
  return e;
}

TQFont KSpread::util_toFont( TQDomElement & element )
{
  TQFont f;
  f.setFamily( element.attribute( "family" ) );

  bool ok;
  f.setPointSize( element.attribute("size").toInt( &ok ) );
  if ( !ok )
    return TQFont();

  f.setWeight( element.attribute("weight").toInt( &ok ) );
  if ( !ok )
    return TQFont();

  if ( element.hasAttribute( "italic" ) && element.attribute("italic") == "yes" )
    f.setItalic( true );

  if ( element.hasAttribute( "bold" ) && element.attribute("bold") == "yes" )
    f.setBold( true );

  if ( element.hasAttribute( "underline" ) && element.attribute("underline") == "yes" )
    f.setUnderline( true );

  if ( element.hasAttribute( "strikeout" ) && element.attribute("strikeout") == "yes" )
    f.setStrikeOut( true );

  /* Uncomment when charset is added to kspread_dlg_layout
     + save a document-global charset
     if ( element.hasAttribute( "charset" ) )
       TDEGlobal::charsets()->setTQFont( f, element.attribute("charset") );
      else
  */
  // ######## Not needed anymore in 3.0?
  //TDEGlobal::charsets()->setTQFont( f, TDEGlobal::locale()->charset() );

  return f;
}

TQPen KSpread::util_toPen( TQDomElement & element )
{
  bool ok;
  TQPen p;

  p.setStyle( (TQt::PenStyle)element.attribute("style").toInt( &ok ) );
  if ( !ok )
    return TQPen();

  p.setWidth( element.attribute("width").toInt( &ok ) );
  if ( !ok )
    return TQPen();

  p.setColor( TQColor( element.attribute("color") ) );

  return p;
}

Point::Point(const TQString & _str)
{
    _sheet = 0;
    init(_str);
}

void Point::setPos(TQPoint pos)
{
    _pos=pos;
}
TQPoint Point::pos() const
{
    return _pos;
}
void Point::setSheet(Sheet* sheet)
{
    _sheet=sheet;
}
KSpread::Sheet* Point::sheet() const
{
    return _sheet;
}
void Point::setSheetName(TQString name)
{
    _sheetName=name;
}
TQString Point::sheetName() const
{
    return _sheetName;
}
void Point::setColumnFixed(bool colFixed)
{
    _columnFixed=colFixed;
}
bool Point::columnFixed() const
{
    return _columnFixed;
}
void Point::setRowFixed(bool rowFixed)
{
    _rowFixed=rowFixed;
}
bool Point::rowFixed() const
{
    return _rowFixed;
}


void Point::init(const TQString & _str)
{
    _columnFixed=false;
    _rowFixed=false;

//    kdDebug(36001) <<"Point::init ("<<_str<<")"<<endl;
    _pos.setX(-1);

    uint len = _str.length();
    if ( !len )
    {
  kdDebug(36001) << "Point::init: len = 0" << endl;
  return;
    }

    TQString str( _str );
    int n = _str.find( '!' );
    if ( n != -1 )
    {
      _sheetName = _str.left( n );
      str = _str.right( len - n - 1 ); // remove the '!'
      len = str.length();
    }

    uint p = 0;

    // Fixed ?
    if ( str[0] == '$' )
    {
  _columnFixed = true;
  p++;
    }
    else
  _columnFixed = false;

    // Malformed ?
    if ( p == len )
    {
  kdDebug(36001) << "Point::init: no point after '$' (str: '" << str.mid( p ) << "'" << endl;
  return;
    }
    if ( str[p] < 'A' || str[p] > 'Z' )
    {
  if ( str[p] < 'a' || str[p] > 'z' )
  {
      kdDebug(36001) << "Point::init: wrong first character in point (str: '" << str.mid( p ) << "'" << endl;
      return;
  }
    }
    //default is error
    int x = -1;
    //search for the first character != text
    int result = str.find( TQRegExp("[^A-Za-z]+"), p );

    //get the colomn number for the character between actual position and the first non text charakter
    if ( result != -1 )
  x = util_decodeColumnLabelText( str.mid( p, result - p ) ); // x is defined now
    else  // If there isn't any, then this is not a point -> return
    {
  kdDebug(36001) << "Point::init: no number in string (str: '" << str.mid( p, result ) << "'" << endl;
  return;
    }
    p = result;

    //limit is KS_colMax
    if ( x > KS_colMax )
    {
  kdDebug(36001) << "Point::init: column value too high (col: " << x << ")" << endl;
  return;
    }

    // Malformed ?
    if (p == len)
    {
  kdDebug(36001) << "Point::init: p==len after cols" << endl;
  return;
    }

    if (str[p] == '$')
    {
  _rowFixed = true;
  p++;
  // Malformed ?
  if ( p == len )
  {
      kdDebug(36001) << "Point::init: p==len after $ of row" << endl;
      return;
  }
    }
    else
  _rowFixed = false;

    uint p2 = p;
    while ( p < len )
    {
  if ( !isdigit( TQChar(str[p++]) ) )
  {
      kdDebug(36001) << "Point::init: no number" << endl;
      return;
  }
    }

    bool ok;
    int y = str.mid( p2, p-p2 ).toInt( &ok );
    if ( !ok )
    {
  kdDebug(36001) << "Point::init: Invalid number (str: '" << str.mid( p2, p-p2 ) << "'" << endl;
  return;
    }
    if ( y > KS_rowMax )
    {
  kdDebug(36001) << "Point::init: row value too high (row: " << y << ")" << endl;
  return;
    }
    if ( y <= 0 )
    {
  kdDebug(36001) << "Point::init: y <= 0" << endl;
  return;
    }
    _pos = TQPoint( x, y );
}

bool util_isPointValid( TQPoint point )
{
    if (    point.x() >= 1
        &&  point.y() >= 1
        &&  point.x() <= KS_colMax
        &&  point.y() <= KS_rowMax
       )
        return true;
    else
        return false;
}

bool util_isRectValid( TQRect rect )
{
    if (    util_isPointValid( rect.topLeft() )
        &&  util_isPointValid( rect.bottomRight() )
       )
        return true;
    else
        return false;
}

Point::Point( const TQString & str, Map * map,
                            Sheet * sheet )
{

    uint p = 0;
    int p2 = str.find( '!' );
    if ( p2 != -1 )
    {
        _sheetName = str.left( p2++ );
        while ( true )
        {
            _sheet = map->findSheet( _sheetName );
            if ( !sheet && _sheetName[0] == ' ' )
            {
                _sheetName = _sheetName.right( _sheetName.length() - 1 );
                continue;
            }
            break;
        }
        p = p2;

        //If the loop didn't return a sheet, better keep a string for isValid
        if ( _sheetName.isEmpty() )
        {
            kdDebug(36001) << "Point: tableName is unknown" << endl;
            _sheetName = "unknown";
        }
    }
    else
    {
        if ( sheet != 0 )
        {
            _sheet = sheet;
            _sheetName = sheet->sheetName();
        }
        else
            _sheet = 0;
    }

    init( str.mid( p ) );
}

Cell *Point::cell() const
{
    return _sheet->cellAt(_pos);
}

bool Point::operator== (const Point &cell) const
{
  //sheet info ignored
  return (_pos == cell.pos());
}

bool Point::operator< (const Point &cell) const
{
  //sheet info ignored
  return (pos().y() < cell.pos().y()) ? true :
      ((pos().y() == cell.pos().y()) && (pos().x() < cell.pos().x()));
}

bool Range::operator ==(const Range& otherRange) const
{
    if (    _range == otherRange._range
        &&  _leftFixed == otherRange._leftFixed
        &&  _rightFixed == otherRange._rightFixed
        &&  _bottomFixed == otherRange._bottomFixed
        &&  _topFixed == otherRange._topFixed
        &&  _sheet == otherRange._sheet )
            return true;
    else
            return false;
}

Range::Range()
{
    _sheet = 0;
    _range.setLeft( -1 );

    _leftFixed=false;
    _rightFixed=false;
    _topFixed=false;
    _bottomFixed=false;
}
Range::Range(const TQString & _str)
{
    _range.setLeft(-1);
    _sheet = 0;

    int p = _str.find(':');
 //   if (p == -1)
 // return;

    Point ul;
    Point lr; ;

    if ( p != -1)
    {
        ul = Point(_str.left(p));
        lr = Point(_str.mid(p + 1));
    }
    else
    {
        ul = Point(_str);
        lr = ul;
    }

    _range = TQRect(ul.pos(), lr.pos());
    _sheetName = ul.sheetName();

    _leftFixed = ul.columnFixed();
    _rightFixed = lr.columnFixed();
    _topFixed = ul.rowFixed();
    _bottomFixed = lr.rowFixed();
}

 Range::Range( const Range& r )
 {
    _sheet = r.sheet();
    _sheetName = r.sheetName();
    _range = r.range();
    _namedArea = r.namedArea();

    _leftFixed=r._leftFixed;
    _rightFixed=r._rightFixed;
    _topFixed=r._topFixed;
    _bottomFixed=r._bottomFixed;
  }

 Range::Range( const Point& ul, const Point& lr )
  {
    _range = TQRect( ul.pos(), lr.pos() );
    if ( ul.sheetName() != lr.sheetName() )
    {
      _range.setLeft( -1 );
      return;
    }
    _sheetName = ul.sheetName();
    _sheet = ul.sheet();
    _leftFixed = ul.columnFixed();
    _rightFixed = lr.columnFixed();
    _topFixed = ul.rowFixed();
    _bottomFixed = lr.rowFixed();
  }

Range::Range(const TQString & str, Map * map,
         Sheet * sheet)
{
  _range.setLeft(-1);
  _sheet = 0;

  //try to parse as named area
  bool gotNamed = false;
  TQString tmp = str.lower();
  TQValueList < Reference >::Iterator it;
  TQValueList < Reference > area = map->doc()->listArea();
  for (it = area.begin(); it != area.end(); ++it) {
    if ((*it).ref_name.lower() == tmp) {
      // success - such named area exists
      _range = (*it).rect;
      _sheet = map->findSheet((*it).sheet_name);
      gotNamed = true;
      _namedArea = tmp;
      break;
    }
  }
  if (gotNamed) {
    // we have a named area - no need to proceed further
    _leftFixed = false;
    _rightFixed = false;
    _topFixed = false;
    _bottomFixed = false;
    return;
  }

    _range.setLeft(-1);
    _sheet = 0;

    int p = 0;
    int p2 = str.find('!');
    if (p2 != -1)
    {
      _sheetName = str.left(p2++);
      while ( true )
      {
        _sheet = map->findSheet(_sheetName);

        if ( !_sheet && _sheetName[0] == ' ' )
        {
          _sheetName = _sheetName.right( _sheetName.length() - 1 );
          continue;
        }
        break;
      }
      p = p2;
    } else
      _sheet = sheet;


    int p3 = str.find(':', p);
    if (p3 == -1)
  return;

    Point ul(str.mid(p, p3 - p));
    Point lr(str.mid(p3 + 1));
    _range = TQRect(ul.pos(), lr.pos());

    _leftFixed = ul.columnFixed();
    _rightFixed = lr.columnFixed();
    _topFixed = ul.rowFixed();
    _bottomFixed = lr.rowFixed();
}

TQString Range::toString() const
{
  TQString result;

  if (_sheet)
  {
    result=util_rangeName(_sheet,_range);
  }
  else
  {
    result=util_rangeName(_range);
  }

  //Insert $ characters to show fixed parts of range

  int pos=result.find("!")+1;
  Q_ASSERT(pos != -1);

  if (_leftFixed)
  {
    result.insert(pos,'$');
    pos++; //Takes account of extra character added in
  }
  if (_topFixed)
  {
    result.insert(pos+Cell::columnName(_range.left()).length(),'$');
  }

  pos=result.find(":")+1;
  Q_ASSERT(pos != -1);

  if (_rightFixed)
  {
    result.insert(pos,'$');
    pos++; //Takes account of extra character added in
  }
  if (_bottomFixed)
  {
    result.insert(pos+Cell::columnName(_range.right()).length(),'$');
  }


  return result;
}

void Range::getStartPoint(Point* pt)
{
  if (!isValid()) return;

  pt->setRow(startRow());
  pt->setColumn(startCol());
  pt->setColumnFixed(_leftFixed);
  pt->setRowFixed(_topFixed);
  pt->setSheet(_sheet);
  pt->setSheetName(_sheetName);
}

void Range::getEndPoint(Point* pt)
{
  if (!isValid()) return;

  pt->setRow(endRow());
  pt->setColumn(endCol());
  pt->setColumnFixed(_rightFixed);
  pt->setRowFixed(_bottomFixed);
  pt->setSheet(_sheet);
  pt->setSheetName(_sheetName);
}

bool Range::contains (const Point &cell) const
{
  return _range.contains (cell.pos());
}

bool Range::intersects (const Range &r) const
{
  return _range.intersects (r.range());
}

bool Range::isValid() const
{
  return  ( _range.left() >= 0 ) &&
    ( _range.right() >= 0 ) &&
    ( _sheet != 0 || _sheetName.isEmpty() ) &&
    ( _range.isValid() ) ;
}

TQRect Range::range() const
{
    return _range;
}

void Range::setLeftFixed(bool fixed)
{
    _leftFixed=fixed;
}
bool Range::leftFixed() const
{
    return _leftFixed;
}
void Range::setRightFixed(bool fixed)
{
    _rightFixed=fixed;
}
bool Range::rightFixed() const
{
    return _rightFixed;
}
void Range::setTopFixed(bool fixed)
{
    _topFixed=fixed;
}
bool Range::topFixed() const
{
    return _topFixed;
}
void Range::setBottomFixed(bool fixed)
{
    _bottomFixed=fixed;
}
bool Range::bottomFixed() const
{
    return _bottomFixed;
}
void Range::setSheet(Sheet* sheet)
{
    _sheet=sheet;
}
KSpread::Sheet* Range::sheet() const
{
    return _sheet;
}
void Range::setSheetName(TQString sheetName)
{
    _sheetName=sheetName;
}
TQString Range::sheetName() const
{
    return _sheetName;
}
TQString Range::namedArea() const
{
    return _namedArea;
}


bool KSpread::util_isAllSelected(const TQRect &selection)
{
  return ( selection.top() == 1 && selection.bottom() == KS_rowMax
     && selection.left() == 1 && selection.right() == KS_colMax);
}

bool KSpread::util_isColumnSelected(const TQRect &selection)
{
  return ( (selection.top() == 1) && (selection.bottom() == KS_rowMax) );
}

bool KSpread::util_isRowSelected(const TQRect &selection)
{
  return ( (selection.left() == 1) && (selection.right() == KS_colMax) );
}

bool KSpread::util_isRowOrColumnSelected(const TQRect &selection)
{
    return ( (selection.left() == 1) && (selection.right() == KS_colMax)
             || (selection.top() == 1) && (selection.bottom() == KS_rowMax) );
}

//used in View::slotRename
bool KSpread::util_validateSheetName(const TQString &name)
{
  if (name[0] == ' ')
  {
    return false;
  }
  for (unsigned int i = 0; i < name.length(); i++)
  {
    if ( !(name[i].isLetterOrNumber() ||
           name[i] == ' ' || name[i] == '.' ||
           name[i] == '_'))
    {
      return false;
    }
  }
  return true;
}


RangeIterator::RangeIterator(TQRect _range, Sheet* _sheet)
{
  range = _range;
  sheet = _sheet;
  current = TQPoint(0,0);
}

RangeIterator::~RangeIterator()
{
}

Cell* RangeIterator::first()
{
  current.setY(range.top());

  /* OK, because even if this equals zero, the 'getNextCellRight' won't
     try to access it*/
  current.setX(range.left() - 1);
  return next();
}

Cell* RangeIterator::next()
{
  if (current.x() == 0 && current.y() == 0)
  {
    return first();
  }

  Cell* cell = NULL;
  bool done = false;

  while (cell == NULL && !done)
  {
    cell = sheet->getNextCellRight(current.x(), current.y());
    if (cell != NULL && cell->column() > range.right())
    {
      cell = NULL;
    }

    if (cell == NULL)
    {
      current.setX(range.left() - 1);
      current.setY(current.y() + 1);
      done = (current.y() > range.bottom());
    }
  }
  return cell;
}

//not used anywhere
int KSpread::util_penCompare( TQPen const & pen1, TQPen const & pen2 )
{
  if ( pen1.style() == TQt::NoPen && pen2.style() == TQt::NoPen )
    return 0;

  if ( pen1.style() == TQt::NoPen )
    return -1;

  if ( pen2.style() == TQt::NoPen )
    return 1;

  if ( pen1.width() < pen2.width() )
    return -1;

  if ( pen1.width() > pen2.width() )
    return 1;

  if ( pen1.style() < pen2.style() )
    return -1;

  if ( pen1.style() > pen2.style() )
    return 1;

  if ( pen1.color().name() < pen2.color().name() )
    return -1;

  if ( pen1.color().name() > pen2.color().name() )
    return 1;

  return 0;
}


TQString KSpread::convertRefToBase( const TQString & sheet, const TQRect & rect )
{
  TQPoint bottomRight( rect.bottomRight() );

  TQString s( "$" );
  s += sheet;
  s += ".$";
  s += Cell::columnName( bottomRight.x() );
  s += '$';
  s += TQString::number( bottomRight.y() );

  return s;
}

TQString KSpread::convertRefToRange( const TQString & sheet, const TQRect & rect )
{
  TQPoint topLeft( rect.topLeft() );
  TQPoint bottomRight( rect.bottomRight() );

  if ( topLeft == bottomRight )
    return convertRefToBase( sheet, rect );

  TQString s( "$" );
  s += sheet;
  s += ".$";
  s += /*util_encodeColumnLabelText*/Cell::columnName( topLeft.x() );
  s += '$';
  s += TQString::number( topLeft.y() );
  s += ":.$";
  s += /*util_encodeColumnLabelText*/Cell::columnName( bottomRight.x() );
  s += '$';
  s += TQString::number( bottomRight.y() );

  return s;
}

//used in Cell::convertFormulaToOasisFormat
void KSpread::insertBracket( TQString & s )
{
  TQChar c;
  int i = (int) s.length() - 1;

  while ( i >= 0 )
  {
    c = s[i];
    if ( c == ' ' )
      s[i] = '_';
    if ( !(c.isLetterOrNumber() || c == ' ' || c == '.'
           || c == '_') )
    {
      s.insert( i + 1, '[' );
      return;
    }
    --i;
  }
}

 // e.g.: Sheet4.A1:Sheet4.E28
 //used in Sheet::saveOasis
TQString KSpread::convertRangeToRef( const TQString & sheetName, const TQRect & _area )
{
    return sheetName + "." + Cell::name( _area.left(), _area.top() ) + ":" + sheetName + "."+ Cell::name( _area.right(), _area.bottom() );
}

TQString KSpread::convertOasisPenToString( const TQPen & pen )
{
//     kdDebug()<<"convertOasisPenToString( const TQPen & pen ) :"<<pen<<endl;
    // NOTE Stefan: TQPen api docs:
    //              For horizontal and vertical lines a line width of 0 is
    //              the same as a line width of 1.
    //              A line width of 0 will produce a 1 pixel wide line using
    //              a fast algorithm for diagonals. A line width of 1 will
    //              also produce a 1 pixel wide line, but uses a slower more
    //              accurate algorithm for diagonals.
    TQString s = TQString( "%1pt " ).arg( (pen.width() == 0) ? 1 : pen.width() );
    switch( pen.style() )
    {
    case TQt::NoPen:
        return "none";
    case TQt::SolidLine:
        s+="solid";
        break;
    case TQt::DashLine:
        s+="dashed";
        break;
    case TQt::DotLine:
        s+="dotted";
        break;
    case TQt::DashDotLine:
        s+="dot-dash";
        break;
    case TQt::DashDotDotLine:
        s+="dot-dot-dash";
        break;
    default: break;
    }
    kdDebug()<<" convertOasisPenToString :"<<s<<endl;
    if ( pen.color().isValid() )
    {
        s+=' ';
        s+=Style::colorName(pen.color());
    }
    return s;
}

TQPen KSpread::convertOasisStringToPen( const TQString &border )
{
    TQPen pen;
    //string like "0.088cm solid #800000"
    if (border.isEmpty() || border=="none" || border=="hidden") // in fact no border
    {
        pen.setStyle( TQt::NoPen );
        return pen;
    }
    //code from koborder, for the moment kspread doesn't use koborder
    // ## isn't it faster to use TQStringList::split than parse it 3 times?
    TQString _width = border.section(' ', 0, 0);
    TQCString _style = border.section(' ', 1, 1).latin1();
    TQString _color = border.section(' ', 2, 2);

    pen.setWidth( ( int )( KoUnit::parseValue( _width, 1.0 ) ) );

    if ( _style =="none" )
        pen.setStyle( TQt::NoPen );
    else if ( _style =="solid" )
        pen.setStyle( TQt::SolidLine );
    else if ( _style =="dashed" )
        pen.setStyle( TQt::DashLine );
    else if ( _style =="dotted" )
        pen.setStyle( TQt::DotLine );
    else if ( _style =="dot-dash" )
        pen.setStyle( TQt::DashDotLine );
    else if ( _style =="dot-dot-dash" )
        pen.setStyle( TQt::DashDotDotLine );
    else
        kdDebug()<<" style undefined : "<<_style<<endl;

    if ( _color.isEmpty() )
        pen.setColor( TQColor() );
    else
        pen.setColor(  TQColor( _color ) );

    return pen;
}

//Return true when it's a reference to cell from sheet.
bool KSpread::localReferenceAnchor( const TQString &_ref )
{
    bool isLocalRef = (_ref.find("http://") != 0 &&
                       _ref.find("mailto:") != 0 &&
                       _ref.find("ftp://") != 0  &&
                       _ref.find("file:") != 0 );
    return isLocalRef;
}


TQString KSpread::Oasis::decodeFormula(const TQString& expr, const TDELocale* locale)
{
  // parsing state
  enum { Start, InNumber, InString, InIdentifier, InReference, InSheetName } state;

  // use locale settings
  TQString decimal = locale ? locale->decimalSymbol() : ".";

  // initialize variables
  state = Start;
  unsigned int i = 0;
  const TQString ex = expr;
  TQString result;

  if (ex[0] == '=')
  {
	result="=";
	++i;
  }

  // main loop
  while( i < ex.length() )
  {
    TQChar ch = ex[i];

    switch( state )
    {
    case Start:
    {
       // check for number
       if( ch.isDigit() )
       {
         state = InNumber;
       }

       // a string?
       else if ( ch == '"' )
       {
         state = InString;
         result.append( ex[i++] );
       }

       // beginning with alphanumeric ?
       // could be identifier, cell, range, or function...
       else if( isIdentifier( ch ) )
       {
         state = InIdentifier;
       }

       // [ marks sheet name for 3-d cell, e.g ['Sales Q3'.A4]
       else if ( ch.unicode() == '[' )
       {
         ++i;
         state = InReference;
         // NOTE Stefan: As long as KSpread does not support fixed sheets eat the dollar sign.
         if ( ex[i] == '$' ) ++i;
       }

       // decimal dot ?
       else if ( ch == '.' )
       {
           if ( ex[i+1].isDigit() )
               state = InNumber;
           else
               state = InReference;
       }

       // look for operator match
       else
       {
         int op;
         TQString s;

         // check for two-chars operator, such as '<=', '>=', etc
         s.append( ch ).append( ex[i+1] );
         op = matchOperator( s );

         // check for one-char operator, such as '+', ';', etc
         if( op == Token::InvalidOp )
         {
           s = TQString( ch );
           op = matchOperator( s );
         }

         // any matched operator ?
         if (  op == Token::Equal )
         {
           result.append( "==" );
         }
         else
         {
           result.append( s );
         }
         if( op != Token::InvalidOp )
         {
           int len = s.length();
           i += len;
         }
         else
         {
           ++i;
           state = Start;
         }
        }
       break;
    }
    case InReference:
    {
       // consume as long as alpha, dollar sign, underscore, or digit, or colon
       if( isIdentifier( ch )  || ch.isDigit() || ch == ':' )
         result.append( ex[i] );
       else if ( ch == '.' && i > 0 && ex[i-1] != '[' && ex[i-1] != ':' )
         result.append( '!' );
       else if( ch == ']' )
         state = Start;
       else if ( ch == '\'' )
       {
         result.append( ex[i] );
         state = InSheetName;
         // NOTE Stefan: As long as KSpread does not support fixed sheets eat the dollar sign.
         if ( ex[i] == '$' ) ++i;
       }
       else if ( ch != '.' )
       {
           state = Start;
           break;
       }
       ++i;
       break;
    }
    case InSheetName:
    {
      if ( ch == '\'' )
        state = InReference;
      result.append( ex[i] );
      ++i;
      break;
    }
    case InNumber:
    {
       // consume as long as it's digit
       if( ch.isDigit() )
         result.append( ex[i++] );
       // convert '.' to decimal separator
       else if ( ch == '.' )
       {
         result.append( decimal );
         ++i;
       }
       // exponent ?
       else if( ch.upper() == 'E' )
       {
         result.append( 'E' );
         ++i;
       }
       // we're done with integer number
       else
         state = Start;
       break;
    }
    case InString:
    {
       // consume until "
       if( ch != '"' )
       {
         result.append( ex[i++] );
       }
       // we're done
       else
       {
         result.append( ch );
         ++i;
         state = Start;
       }
       break;
    }
    case InIdentifier:
    {
       // consume as long as alpha, dollar sign, underscore, or digit
      if( isIdentifier( ch )  || ch.isDigit() )
        result.append( ex[i++] );
       // we're done
      else
        state = Start;
      break;
    }
    default:
       break;
    }
  }
  return result;
}

/*TQString KSpread::Oasis::encodeFormula(const TQString& expr, const TDELocale* locale)
{
  // TODO move Cell::convertFormulaToOasisFormat to this point
  //expr = "not here yet";
  //Q_UNUSED(locale);
  kdDebug() << k_funcinfo << " not implemented"
  tqFatal(0);
}*/
