/* This file is part of the KDE project
   Copyright 1998, 1999 Torben Weis <weis@kde.org>
   Copyright 1999- 2006 The KSpread Team
                        www.koffice.org/kspread

   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 <assert.h>
#include <ctype.h>
#include <float.h>
#include <math.h>
#include <pwd.h>
#include <stdlib.h>
#include <unistd.h>

#include <tqapplication.h>
#include <tqcheckbox.h>
#include <tqclipboard.h>
#include <tqlabel.h>
#include <tqlayout.h>
#include <tqlineedit.h>
#include <tqpicture.h>
#include <tqregexp.h>
#include <tqvbox.h>
#include <tqmap.h>

#include <kdebug.h>
#include <kmdcodec.h>
#include <kfind.h>
#include <kfinddialog.h>
#include <tdemessagebox.h>
#include <kreplace.h>
#include <kreplacedialog.h>
#include <kprinter.h>
#include <kurl.h>

#include <koChart.h>
#include <KoDom.h>
#include <KoDocumentInfo.h>
#include <KoOasisLoadingContext.h>
#include <KoOasisSettings.h>
#include <KoOasisStyles.h>
#include <KoQueryTrader.h>
#include <KoStyleStack.h>
#include <KoUnit.h>
#include <KoXmlNS.h>
#include <KoXmlWriter.h>

#include "commands.h"
#include "dependencies.h"
#include "selection.h"
#include "ksploadinginfo.h"
#include "ksprsavinginfo.h"
#include "kspread_canvas.h"
#include "kspread_cluster.h"
#include "kspread_condition.h"
#include "kspread_doc.h"
#include "kspread_global.h"
#include "kspread_locale.h"
#include "kspread_map.h"
#include "kspread_object.h"
#include "kspread_sheetprint.h"
#include "kspread_style.h"
#include "kspread_style_manager.h"
#include "kspread_undo.h"
#include "kspread_util.h"
#include "kspread_view.h"
#include "manipulator.h"
#include "manipulator_data.h"
#include "KSpreadTableIface.h"

#include "kspread_sheet.h"
#include "kspread_sheet.moc"

#define NO_MODIFICATION_POSSIBLE \
do { \
  KMessageBox::error( 0, i18n ( "You cannot change a protected sheet" ) ); return; \
} while(0)

namespace KSpread {

/*****************************************************************************
 *
 * CellBinding
 *
 *****************************************************************************/

CellBinding::CellBinding( Sheet *_sheet, const TQRect& _area )
{
  m_rctDataArea = _area;

  m_pSheet = _sheet;
  m_pSheet->addCellBinding( this );

  m_bIgnoreChanges = false;
}

CellBinding::~CellBinding()
{
  m_pSheet->removeCellBinding( this );
}

void CellBinding::cellChanged( Cell *_cell )
{
  if ( m_bIgnoreChanges )
    return;

  emit changed( _cell );
}

bool CellBinding::contains( int _x, int _y )
{
  return m_rctDataArea.contains( TQPoint( _x, _y ) );
}

/*****************************************************************************
 *
 * ChartBinding
 *
 *****************************************************************************/

ChartBinding::ChartBinding( Sheet *_sheet, const TQRect& _area, EmbeddedChart *_child )
    : CellBinding( _sheet, _area )
{
  m_child = _child;
}

ChartBinding::~ChartBinding()
{
}

void ChartBinding::cellChanged( Cell* /*changedCell*/ )
{
    if ( m_bIgnoreChanges )
        return;

	//Ensure display gets updated by marking all cells underneath the chart as
	//dirty

	const TQRect chartGeometry = m_child->geometry().toTQRect();

	double tmp;
  	int left = sheet()->leftColumn( chartGeometry.left() , tmp );
  	int top = sheet()->topRow( chartGeometry.top() , tmp );
	int right = sheet()->rightColumn( chartGeometry.right() );
	int bottom = sheet()->bottomRow( chartGeometry.bottom() );

	sheet()->setRegionPaintDirty( TQRect(left,top,right-left,bottom-top) );

    //kdDebug(36001) << m_rctDataArea << endl;

    // Get the chart and resize its data if necessary.
    //
    // FIXME: Only do this if he data actually changed size.
    KoChart::Part  *chart = m_child->chart();
    chart->resizeData( m_rctDataArea.height(), m_rctDataArea.width() );

    // Reset all the data, i.e. retransfer them to the chart.
    // This is definitely not the most efficient way to do this.
    //
    // FIXME: Find a way to do it with just the data that changed.
    Cell* cell;
    for ( int row = 0; row < m_rctDataArea.height(); row++ ) {
        for ( int col = 0; col < m_rctDataArea.width(); col++ ) {
            cell = m_pSheet->cellAt( m_rctDataArea.left() + col,
				     m_rctDataArea.top() + row );
            if ( cell && cell->value().isNumber() )
		chart->setCellData( row, col, cell->value().asFloat() );
            else if ( cell )
	        chart->setCellData( row, col, cell->value().asString() );
            else
	        chart->setCellData( row, col, KoChart::Value() );
        }
    }
    chart->analyzeHeaders( );

    // ######### Kalle may be interested in that, too
#if 0
    Chart::Range range;
    range.top = m_rctDataArea.top();
    range.left = m_rctDataArea.left();
    range.right = m_rctDataArea.right();
    range.bottom = m_rctDataArea.bottom();
    range.sheet = m_pSheet->name(); */

    //m_child->chart()->setData( matrix );

    // Force a redraw of the chart on all views

    /** TODO - replace the call below with something that will repaint this chart */
#endif
    //    sheet()->emit_polygonInvalidated( m_child->framePointArray() );
}


/******************************************************************/
/* Class: TextDrag                                               */
/******************************************************************/


TextDrag::TextDrag( TQWidget * dragSource, const char * name )
    : TQTextDrag( dragSource, name )
{
}

TextDrag::~TextDrag()
{
}


TQByteArray TextDrag::encodedData( const char * mime ) const
{
  if ( strcmp( selectionMimeType(), mime ) == 0)
    return m_kspread;
  else
    return TQTextDrag::encodedData( mime );
}

bool TextDrag::canDecode( TQMimeSource* e )
{
  if ( e->provides( selectionMimeType() ) )
    return true;
  return TQTextDrag::canDecode(e);
}

const char * TextDrag::format( int i ) const
{
  if ( i < 4 ) // HACK, but how to do otherwise ??
    return TQTextDrag::format(i);
  else if ( i == 4 )
    return selectionMimeType();
  else return 0;
}

const char * TextDrag::selectionMimeType()
{
  return "application/x-kspread-snippet";
}

/*****************************************************************************
 *
 * Sheet
 *
 *****************************************************************************/

class Sheet::Private
{
public:

  Map* workbook;

  DCOPObject* dcop;

  TQString name;
  int id;

  Sheet::LayoutDirection layoutDirection;

  // true if sheet is hidden
  bool hide;

  // password of protected sheet
  TQCString password;


  bool showGrid;
  bool showFormula;
  bool showFormulaIndicator;
  bool showCommentIndicator;
  bool autoCalc;
  bool lcMode;
  bool showColumnNumber;
  bool hideZero;
  bool firstLetterUpper;

  // clusters to hold objects
  Cluster cells;
  RowCluster rows;
  ColumnCluster columns;

  // default objects
  Cell* defaultCell;
  Format* defaultFormat;
  RowFormat* defaultRowFormat;
  ColumnFormat* defaultColumnFormat;

  // hold the print object
  SheetPrint* print;

  // cells that need painting
  Region paintDirtyList;

  // to get font metrics
  TQPainter *painter;
  TQWidget *widget;

  // List of all cell bindings. For example charts use bindings to get
  // informed about changing cell contents.
  TQPtrList<CellBinding> cellBindings;

  // Indicates whether the sheet should paint the page breaks.
  // Doing so costs some time, so by default it should be turned off.
  bool showPageBorders;

  // List of all embedded objects. FIXME unused ??
  // TQPtrList<Child> m_lstChildren;

  // The highest row and column ever accessed by the user.
  int maxRow;
  int maxColumn;

  // Max range of canvas in x and ye direction.
  //  Depends on KS_colMax/KS_rowMax and the width/height of all columns/rows
  double sizeMaxX;
  double sizeMaxY;


  bool scrollBarUpdates;

  TQPen emptyPen;
  TQBrush emptyBrush;
  TQColor emptyColor;

  int scrollPosX;
  int scrollPosY;

  KSpread::DependencyManager *dependencies;
};

int Sheet::s_id = 0L;
TQIntDict<Sheet>* Sheet::s_mapSheets;

Sheet* Sheet::find( int _id )
{
  if ( !s_mapSheets )
    return 0L;

  return (*s_mapSheets)[ _id ];
}

Sheet::Sheet (Map* map,
    const TQString &sheetName, const char *_name )
  : TQObject( map, _name )
{
  if ( s_mapSheets == 0L )
    s_mapSheets = new TQIntDict<Sheet>;
  d = new Private;

  d->workbook = map;

  d->id = s_id++;
  s_mapSheets->insert( d->id, this );

  d->layoutDirection = LeftToRight;

  d->defaultFormat = new Format (this, d->workbook->doc()->styleManager()->defaultStyle());
  d->emptyPen.setStyle( TQt::NoPen );
  d->dcop = 0;
  d->name = sheetName;

  dcopObject();
  d->cellBindings.setAutoDelete( false );

  // m_lstChildren.setAutoDelete( true );

  d->cells.setAutoDelete( true );
  d->rows.setAutoDelete( true );
  d->columns.setAutoDelete( true );

  d->defaultCell = new Cell( this, d->workbook->doc()->styleManager()->defaultStyle(), 0, 0);
  d->defaultRowFormat = new RowFormat( this, 0 );
  d->defaultRowFormat->setDefault();
  d->defaultColumnFormat = new ColumnFormat( this, 0 );
  d->defaultColumnFormat->setDefault();

  d->widget = new TQWidget();
  d->painter = new TQPainter;
  d->painter->begin( d->widget );

  d->maxColumn = 256;
  d->maxRow = 256;
  d->sizeMaxX = KS_colMax * d->defaultColumnFormat->dblWidth(); // default is max cols * default width
  d->sizeMaxY = KS_rowMax * d->defaultRowFormat->dblHeight(); // default is max rows * default height

  d->scrollBarUpdates = true;

  setHidden( false );
  d->showGrid=true;
  d->showFormula=false;
  d->showFormulaIndicator=true;
  d->showCommentIndicator=true;
  d->showPageBorders = false;

  d->lcMode=false;
  d->showColumnNumber=false;
  d->hideZero=false;
  d->firstLetterUpper=false;
  d->autoCalc=true;
  // Get a unique name so that we can offer scripting
  if ( !_name )
  {
      TQCString s;
      s.sprintf("Sheet%i", s_id );
      TQObject::setName( s.data() );
  }
  d->print = new SheetPrint( this );

  // initialize dependencies
  d->dependencies = new KSpread::DependencyManager (this);

  // connect to named area slots
  TQObject::connect( doc(), TQ_SIGNAL( sig_addAreaName( const TQString & ) ),
    this, TQ_SLOT( slotAreaModified( const TQString & ) ) );

  TQObject::connect( doc(), TQ_SIGNAL( sig_removeAreaName( const TQString & ) ),
    this, TQ_SLOT( slotAreaModified( const TQString & ) ) );


}

TQString Sheet::sheetName() const
{
  return d->name;
}

Map* Sheet::workbook() const
{
  return d->workbook;
}

Doc* Sheet::doc() const
{
  return d->workbook->doc();
}

int Sheet::id() const
{
  return d->id;
}

Sheet::LayoutDirection Sheet::layoutDirection() const
{
  return d->layoutDirection;
}

void Sheet::setLayoutDirection( LayoutDirection dir )
{
  d->layoutDirection = dir;
}

bool Sheet::isRightToLeft() const
{
  return d->layoutDirection == RightToLeft;
}

bool Sheet::isHidden() const
{
  return d->hide;
}

void Sheet::setHidden( bool hidden )
{
  d->hide = hidden;
}

bool Sheet::getShowGrid() const
{
    return d->showGrid;
}

void Sheet::setShowGrid( bool _showGrid )
{
    d->showGrid=_showGrid;
}

bool Sheet::getShowFormula() const
{
    return d->showFormula;
}

void Sheet::setShowFormula( bool _showFormula )
{
    d->showFormula=_showFormula;
}

bool Sheet::getShowFormulaIndicator() const
{
    return d->showFormulaIndicator;
}

void Sheet::setShowFormulaIndicator( bool _showFormulaIndicator )
{
    d->showFormulaIndicator=_showFormulaIndicator;
}

bool Sheet::getShowCommentIndicator() const
{
    return d->showCommentIndicator;
}

void Sheet::setShowCommentIndicator(bool _indic)
{
    d->showCommentIndicator=_indic;
}

bool Sheet::getLcMode() const
{
    return d->lcMode;
}

void Sheet::setLcMode( bool _lcMode )
{
    d->lcMode=_lcMode;
}

bool Sheet::getAutoCalc() const
{
    return d->autoCalc;
}

void Sheet::setAutoCalc( bool _AutoCalc )
{
    //Avoid possible recalculation of dependancies if the auto calc setting hasn't changed
    if (d->autoCalc == _AutoCalc)
        return;

    //If enabling automatic calculation, make sure that the dependencies are up-to-date
    if (_AutoCalc == true)
    {
        updateAllDependencies();
        recalc();
    }

    d->autoCalc=_AutoCalc;


}

bool Sheet::getShowColumnNumber() const
{
    return d->showColumnNumber;
}

void Sheet::setShowColumnNumber( bool _showColumnNumber )
{
    d->showColumnNumber=_showColumnNumber;
}

bool Sheet::getHideZero() const
{
    return d->hideZero;
}

void Sheet::setHideZero( bool _hideZero )
{
    d->hideZero=_hideZero;
}

bool Sheet::getFirstLetterUpper() const
{
    return d->firstLetterUpper;
}

void Sheet::setFirstLetterUpper( bool _firstUpper )
{
    d->firstLetterUpper=_firstUpper;
}

bool Sheet::isShowPageBorders() const
{
    return d->showPageBorders;
}

bool Sheet::isEmpty( unsigned long int x, unsigned long int y ) const
{
  const Cell* c = cellAt( x, y );
  if ( !c || c->isEmpty() )
    return true;

  return false;
}

Cell* Sheet::defaultCell() const
{
    return d->defaultCell;
}

Format* Sheet::defaultFormat()
{
    return d->defaultFormat;
}

const Format* Sheet::defaultFormat() const
{
    return d->defaultFormat;
}

const ColumnFormat* Sheet::columnFormat( int _column ) const
{
    const ColumnFormat *p = d->columns.lookup( _column );
    if ( p != 0L )
        return p;

    return d->defaultColumnFormat;
}

ColumnFormat* Sheet::columnFormat( int _column )
{
    ColumnFormat *p = d->columns.lookup( _column );
    if ( p != 0L )
        return p;

    return d->defaultColumnFormat;
}

const RowFormat* Sheet::rowFormat( int _row ) const
{
    const RowFormat *p = d->rows.lookup( _row );
    if ( p != 0L )
        return p;

    return d->defaultRowFormat;
}

RowFormat* Sheet::rowFormat( int _row )
{
    RowFormat *p = d->rows.lookup( _row );
    if ( p != 0L )
        return p;

    return d->defaultRowFormat;
}

Value Sheet::value (int col, int row) const
{
  Cell *cell = d->cells.lookup (col, row);
  if (cell)
    return cell->value ();
  Value empty;
  return empty;
}

Value Sheet::valueRange (int col1, int row1,
    int col2, int row2) const
{
  return d->cells.valueRange (col1, row1, col2, row2);
}

void Sheet::password( TQCString & passwd ) const
{
    passwd = d->password;
}

bool Sheet::isProtected() const
{
    return !d->password.isNull();
}

void Sheet::setProtected( TQCString const & passwd )
{
  d->password = passwd;
}

bool Sheet::checkPassword( TQCString const & passwd ) const
{
    return ( passwd == d->password );
}

SheetPrint* Sheet::print() const
{
    return d->print;
}

TQPainter& Sheet::painter()
{
    return *d->painter;
}

TQWidget* Sheet::widget()const
{
    return d->widget;
}

CellBinding* Sheet::firstCellBinding()
{
    return d->cellBindings.first();
}

CellBinding* Sheet::nextCellBinding()
{
    return d->cellBindings.next();
}

void Sheet::setDefaultHeight( double height )
{
  if ( isProtected() )
    NO_MODIFICATION_POSSIBLE;

  d->defaultRowFormat->setDblHeight( height );
}

void Sheet::setDefaultWidth( double width )
{
  if ( isProtected() )
    NO_MODIFICATION_POSSIBLE;

  d->defaultColumnFormat->setDblWidth( width );
}

double Sheet::sizeMaxX() const
{
  return d->sizeMaxX;
}

double Sheet::sizeMaxY() const
{
  return d->sizeMaxY;
}

int Sheet::maxColumn() const
{
  return d->maxColumn;
}

int Sheet::maxRow() const
{
  return d->maxRow;
}

const TQPen& Sheet::emptyPen() const
{
  return d->emptyPen;
}

const TQBrush& Sheet::emptyBrush() const
{
  return d->emptyBrush;
}

const TQColor& Sheet::emptyColor() const
{
  return d->emptyColor;
}

KSpread::DependencyManager *Sheet::dependencies ()
{
  return d->dependencies;
}

int Sheet::numSelected() const
{
    int num = 0;

    TQPtrListIterator<EmbeddedObject> it(  d->workbook->doc()->embeddedObjects() );
    for ( ; it.current() ; ++it )
    {
        if( it.current()->sheet() == this && it.current()->isSelected() )
            num++;
    }

    return num;
}

int Sheet::leftColumn( double _xpos, double &_left,
                              const Canvas *_canvas ) const
{
    if ( _canvas )
    {
        _xpos += _canvas->xOffset();
        _left = -_canvas->xOffset();
    }
    else
        _left = 0.0;

    int col = 1;
    double x = columnFormat( col )->dblWidth( _canvas );
    while ( x < _xpos )
    {
        // Should never happen
        if ( col >= KS_colMax )
  {
      kdDebug(36001) << "Sheet:leftColumn: invalid column (col: " << col + 1 << ")" << endl;
      return KS_colMax + 1; //Return out of range value, so other code can react on this
  }
        _left += columnFormat( col )->dblWidth( _canvas );
        col++;
        x += columnFormat( col )->dblWidth( _canvas );
    }

    return col;
}

int Sheet::rightColumn( double _xpos, const Canvas *_canvas ) const
{
    if ( _canvas )
        _xpos += _canvas->xOffset();

    int col = 1;
    double x = 0.0;
    while ( x < _xpos )
    {
        // Should never happen
        if ( col > KS_colMax )
  {
      kdDebug(36001) << "Sheet:rightColumn: invalid column (col: " << col << ")" << endl;
            return KS_colMax + 1; //Return out of range value, so other code can react on this
  }
        x += columnFormat( col )->dblWidth( _canvas );
        col++;
    }

    return col - 1;
}

TQRect Sheet::visibleRect( Canvas const * const _canvas ) const
{
  int top    = 0;
  int left   = 0;

  double x      = 0;
  double y      = 0;
  double width  = 0;
  double height = 0;

  if ( _canvas )
  {
    y     += _canvas->yOffset() * _canvas->zoom();
    x     += _canvas->xOffset() * _canvas->zoom();
    width  = _canvas->width();
    height = _canvas->height();
  }

  double yn = rowFormat( top )->dblHeight( _canvas );
  while ( yn < y )
  {
    if ( top >= KS_rowMax ) // Should never happen
      break;

    ++top;
    yn += rowFormat( top )->dblHeight( _canvas );
  }

  int bottom = top + 1;

  y += height;
  while ( yn < y )
  {
    if ( bottom > KS_rowMax ) // Should never happen
      break;

    ++bottom;
    yn += rowFormat( bottom )->dblHeight( _canvas );
  }

  double xn = columnFormat( left )->dblWidth( _canvas );
  while ( xn < x )
  {
    if ( left >= KS_colMax )    // Should never happen
      break;

    ++left;
    xn += columnFormat( left )->dblWidth( _canvas );
  }
  x += width;

  int right = left + 1;

  while ( xn < x )
  {
    if ( right > KS_colMax )    // Should never happen
      break;

    ++right;
    xn += columnFormat( right )->dblWidth( _canvas );
  }
  x += width;

  return TQRect( left, top, right - left + 1, bottom - top + 1 );
}

int Sheet::topRow( double _ypos, double & _top,
                          const Canvas *_canvas ) const
{
    if ( _canvas )
    {
        _ypos += _canvas->yOffset();
        _top = -_canvas->yOffset();
    }
    else
        _top = 0.0;

    int row = 1;
    double y = rowFormat( row )->dblHeight( _canvas );
    while ( y < _ypos )
    {
        // Should never happen
        if ( row >= KS_rowMax )
        {
            kdDebug(36001) << "Sheet:topRow: invalid row (row: " << row + 1 << ")" << endl;
            return KS_rowMax + 1; //Return out of range value, so other code can react on this
        }
        _top += rowFormat( row )->dblHeight( _canvas );
        row++;
        y += rowFormat( row )->dblHeight( _canvas );
    }

    return row;
}

int Sheet::bottomRow( double _ypos, const Canvas *_canvas ) const
{
    if ( _canvas )
        _ypos += _canvas->yOffset();

    int row = 1;
    double y = 0.0;
    while ( y < _ypos )
    {
        // Should never happen
        if ( row > KS_rowMax )
  {
      kdDebug(36001) << "Sheet:bottomRow: invalid row (row: " << row << ")" << endl;
            return KS_rowMax + 1; //Return out of range value, so other code can react on this
  }
        y += rowFormat( row )->dblHeight( _canvas );
        row++;
    }

    return row - 1;
}

double Sheet::dblColumnPos( int _col, const Canvas *_canvas ) const
{
    double x = 0.0;
    if ( _canvas )
      x -= _canvas->xOffset();
    for ( int col = 1; col < _col; col++ )
    {
        // Should never happen
        if ( col > KS_colMax )
  {
      kdDebug(36001) << "Sheet:columnPos: invalid column (col: " << col << ")" << endl;
            return x;
  }

        x += columnFormat( col )->dblWidth( _canvas );
    }

    return x;
}

int Sheet::columnPos( int _col, const Canvas *_canvas ) const
{
    return (int)dblColumnPos( _col, _canvas );
}


double Sheet::dblRowPos( int _row, const Canvas *_canvas ) const
{
    double y = 0.0;
    if ( _canvas )
      y -= _canvas->yOffset();

    for ( int row = 1 ; row < _row ; row++ )
    {
        // Should never happen
        if ( row > KS_rowMax )
  {
      kdDebug(36001) << "Sheet:rowPos: invalid row (row: " << row << ")" << endl;
            return y;
  }

        y += rowFormat( row )->dblHeight( _canvas );
    }

    return y;
}

int Sheet::rowPos( int _row, const Canvas *_canvas ) const
{
    return (int)dblRowPos( _row, _canvas );
}


void Sheet::adjustSizeMaxX ( double _x )
{
    d->sizeMaxX += _x;
}

void Sheet::adjustSizeMaxY ( double _y )
{
    d->sizeMaxY += _y;
}

Cell* Sheet::visibleCellAt( int _column, int _row, bool _scrollbar_update )
{
  Cell* cell = cellAt( _column, _row, _scrollbar_update );
  if ( cell->obscuringCells().isEmpty() )
      return cell;
  else
      return cell->obscuringCells().last();
}

Cell* Sheet::firstCell() const
{
    return d->cells.firstCell();
}

RowFormat* Sheet::firstRow() const
{
    return d->rows.first();
}

ColumnFormat* Sheet::firstCol() const
{
    return d->columns.first();
}

Cell* Sheet::cellAt( int _column, int _row ) const
{
    Cell *p = d->cells.lookup( _column, _row );
    if ( p != 0L )
        return p;

    return d->defaultCell;
}

Cell* Sheet::cellAt( int _column, int _row, bool _scrollbar_update )
{
  if ( _scrollbar_update && d->scrollBarUpdates )
  {
    checkRangeHBorder( _column );
    checkRangeVBorder( _row );
  }

  Cell *p = d->cells.lookup( _column, _row );
  if ( p != 0L )
    return p;

  return d->defaultCell;
}

ColumnFormat* Sheet::nonDefaultColumnFormat( int _column, bool force_creation )
{
    ColumnFormat *p = d->columns.lookup( _column );
    if ( p != 0L || !force_creation )
        return p;

    p = new ColumnFormat( this, _column );
    // TODO: copy the default ColumnFormat here!!
    p->setDblWidth( d->defaultColumnFormat->dblWidth() );

    d->columns.insertElement( p, _column );

    return p;
}

RowFormat* Sheet::nonDefaultRowFormat( int _row, bool force_creation )
{
    RowFormat *p = d->rows.lookup( _row );
    if ( p != 0L || !force_creation )
        return p;

    p = new RowFormat( this, _row );
    // TODO: copy the default RowLFormat here!!
    p->setDblHeight( d->defaultRowFormat->dblHeight() );

    d->rows.insertElement( p, _row );

    return p;
}

Cell* Sheet::nonDefaultCell( int _column, int _row,
                                           bool _scrollbar_update, Style * _style )
{
  // NOTE Stefan: _scrollbar_update defaults to false and this function
  //              is never called with it being true. So, this here is
  //              actually never processed. I'll leave this in here for the
  //              case I'm mistaken, but will remove it for 2.0.
  if ( _scrollbar_update && d->scrollBarUpdates )
  {
    checkRangeHBorder( _column );
    checkRangeVBorder( _row );
  }

  Cell * p = d->cells.lookup( _column, _row );
  if ( p != 0L )
    return p;

  Cell * cell = 0;

  if ( _style )
    cell = new Cell( this, _style, _column, _row );
  else
    cell = new Cell( this, _column, _row );

  insertCell( cell );

  return cell;
}

void Sheet::setText( int _row, int _column, const TQString& _text, bool asString )
{
  ProtectedCheck prot;
  prot.setSheet (this);
  prot.add (TQPoint (_column, _row));
  if (prot.check())
    NO_MODIFICATION_POSSIBLE;

  DataManipulator *dm = new DataManipulator ();
  dm->setSheet (this);
  dm->setValue (_text);
  dm->setParsing (!asString);
  dm->add (TQPoint (_column, _row));
  dm->execute ();

  /* PRE-MANIPULATOR CODE looked like this:
  TODO remove this after the new code works
  if ( !doc()->undoLocked() )
  {
      UndoSetText *undo = new UndoSetText( doc(), this, cell->text(), _column, _row,cell->formatType() );
      doc()->addCommand( undo );
  }

  // The cell will force a display refresh itself, so we dont have to care here.
  cell->setCellText( _text, asString );
  */

  //refresh anchor
  if(_text.at(0)=='!')
    emit sig_updateView( this, Region(_column,_row,_column,_row) );
}

void Sheet::setArrayFormula (Selection *selectionInfo, const TQString &_text)
{
  // check protection
  ProtectedCheck prot;
  prot.setSheet (this);
  prot.add (*selectionInfo);
  if (prot.check())
    NO_MODIFICATION_POSSIBLE;

  // create and call the manipulator
  ArrayFormulaManipulator *afm = new ArrayFormulaManipulator;
  afm->setSheet (this);
  afm->setText (_text);
  afm->add (*selectionInfo);
  afm->execute ();

  /* PRE-MANIPULATOR CODE LOOKED LIKE THIS
  TODO remove this when the above code works
  // add undo
  if ( !doc()->undoLocked() )
  {
    UndoChangeAreaTextCell *undo =
        new UndoChangeAreaTextCell (doc(), this,
        TQRect (_column, _row, cols, rows));
    doc()->addCommand( undo );
  }

  // fill in the cells ... top-left one gets the formula, the rest gets =INDEX
  // TODO: also fill in information about cells being a part of a range
  Cell *cell = nonDefaultCell (_column, _row);
  cell->setCellText (_text, false);
  TQString cellRef = cell->name();
  for (int row = 0; row < rows; ++row)
    for (int col = 0; col < cols; col++)
      if (col || row)
      {
        Cell *cell = nonDefaultCell (_column + col, _row + row);
        cell->setCellText ("=INDEX(" + cellRef + ";" + TQString::number (row+1)
            + ";" + TQString::number (col+1) + ")", false);
      }
  */
}

void Sheet::setLayoutDirtyFlag()
{
    Cell * c = d->cells.firstCell();
    for( ; c; c = c->nextCell() )
        c->setLayoutDirtyFlag();
}

void Sheet::setCalcDirtyFlag()
{
    Cell* c = d->cells.firstCell();
    for( ; c; c = c->nextCell() )
    {
        if ( !(c->isObscured() && c->isPartOfMerged()) )
            c->setCalcDirtyFlag();
    }
}

void Sheet::updateAllDependencies()
{
        for (Cell* cell = d->cells.firstCell() ; cell ; cell = cell->nextCell())
        {
            Point cellLocation;
            cellLocation.setSheet(cell->sheet());
            cellLocation.setRow(cell->row());
            cellLocation.setColumn(cell->column());
            d->dependencies->cellChanged(cellLocation);
        }
}

void Sheet::recalc()
{
    recalc(false);
}

void Sheet::recalc( bool force )
{
  ElapsedTime et( "Recalculating " + d->name, ElapsedTime::PrintOnlyTime );
  //  emitBeginOperation(true);
  //  setRegionPaintDirty(TQRect(TQPoint(1,1), TQPoint(KS_colMax, KS_rowMax)));
  setCalcDirtyFlag();

  //If automatic calculation is disabled, don't recalculate unless the force flag has been
  //set.
  if ( !getAutoCalc() && !force )
        return;

  //If automatic calculation is disabled, the dependencies won't be up to date, so they need
  //to be recalculated.
  //FIXME:  Tomas, is there a more efficient way to do this?
  if ( !getAutoCalc() )
    updateAllDependencies();


  // (Tomas): actually recalc each cell
  // this is FAR from being perfect, dependencies will cause some to be
  // recalculated a LOT, but it's still better than otherwise, where
  // we get no recalc if the result stored in a file differs from the
  // current one - then we only obtain the correct result AFTER we scroll
  // to the cell ... recalc should actually ... recalc :)
  Cell* c;

  int count = 0;
  c = d->cells.firstCell();
  for( ; c; c = c->nextCell() )
    ++count;

  int cur = 0;
  int percent = -1;
  c = d->cells.firstCell();
  for( ; c; c = c->nextCell() )
  {
    c->calc (false);
    cur++;
    // some debug output to get some idea how damn slow this is ...
    if (cur*100/count != percent) {
      percent = cur*100/count;
//       kdDebug() << "Recalc: " << percent << "%" << endl;
    }
  }

  //  emitEndOperation();
  emit sig_updateView( this );
}

void Sheet::valueChanged (Cell *cell)
{

  //TODO: call cell updating, when cell damaging implemented

  //prepare the Point structure
  Point c;
  c.setRow (cell->row());
  c.setColumn (cell->column());
  c.setSheet( this );

  //update dependencies
  if ( getAutoCalc() )
        d->dependencies->cellChanged (c);

  //REMOVED - modification change - this was causing modified flag to be set inappropriately.
  //nobody else seems to be setting the modified flag, so we do it here
//  doc()->setModified (true);
}

/*
 Methods working on selections:

 TYPE A:
 { columns selected:
   for all rows with properties X,X':
     if default-cell create new cell
 }
 post undo object (always a UndoCellLayout; difference in title only)
 { rows selected:
   if condition Y clear properties X,X' of cells;
   set properties X,X' of rowformats
   emit complete update;
 }
 { columns selected:
   if condition Y clear properties X,X' of cells;
   set properties X,X' of columnformats;
   for all rows with properties X,X':
     create cells if necessary and set properties X,X'
   emit complete update;
 }
 { cells selected:
   for all cells with condition Y:
     create if necessary and set properties X,X' and do Z;
   emit update on selected region;
 }

 USED in:
 setSelectionFont
 setSelectionSize
 setSelectionAngle
 setSelectionTextColor
 setSelectionBgColor
 setSelectionPercent
 borderAll
 borderRemove (exceptions: ### creates cells (why?), ### changes default cell if cell-regions selected?)
 setSelectionAlign
 setSelectionAlignY
 setSelectionMoneyFormat
 increaseIndent
 decreaseIndent

 TYPE B:
 post undo object
 { rows selected:
   if condition Y do X with cells;
   emit update on selection;
 }
 { columns selected:
   if condition Y do X with cells;
   emit update on selection;
 }
 { cells selected:
   if condition Y do X with cells; create cell if non-default;
   emit update on selection;
 }

 USED in:
 setSelectionUpperLower (exceptions: no undo; no create-if-default; ### modifies default-cell?)
 setSelectionFirstLetterUpper (exceptions: no undo; no create-if-default; ### modifies default-cell?)
 setSelectionVerticalText
 setSelectionComment
 setSelectionRemoveComment (exeception: no create-if-default and work only on non-default-cells for cell regions)
 setSelectionBorderColor (exeception: no create-if-default and work only on non-default-cells for cell regions)
 setSelectionMultiRow
 setSelectionPrecision
 clearTextSelection (exception: all only if !areaIsEmpty())
 clearValiditySelection (exception: all only if !areaIsEmpty())
 clearConditionalSelection (exception: all only if !areaIsEmpty())
 setConditional (exception: conditional after create-if-default for cell regions)
 setValidity (exception: conditional after create-if-default for cell regions)

 OTHERS:
 borderBottom
 borderRight
 borderLeft
 borderTop
 borderOutline
 => these work only on some cells (at the border); undo only if cells affected; rest is similar to type A
 --> better not use CellWorker/workOnCells()

 defaultSelection
 => similar to TYPE B, but works on columns/rows if complete columns/rows selected
 --> use emit_signal=false and return value of workOnCells to finish

 getWordSpelling
 => returns text, no signal emitted, no cell-create, similar to TYPE B
 --> use emit_signal=false, create_if_default=false and type B

 setWordSpelling
 => no signal emitted, no cell-create, similar to type B
 --> use emit_signal=false, create_if_default=false and type B
 */

class UndoAction* Sheet::CellWorkerTypeA::createUndoAction( Doc* doc, Sheet* sheet, const KSpread::Region& region )
{
    TQString title = getUndoTitle();
    return new UndoCellFormat( doc, sheet, region, title );
}

/*
Sheet::SelectionType Sheet::workOnCells( const TQPoint& _marker, CellWorker& worker )
{
    // see what is selected; if nothing, take marker position
    bool selected = ( m_rctSelection.left() != 0 );
    TQRect r( m_rctSelection );
    if ( !selected )
  r.setCoords( _marker.x(), _marker.y(), _marker.x(), _marker.y() );

    // create cells in rows if complete columns selected
    Cell *cell;
    if ( !worker.type_B && selected && isColumnSelected() )
    {
  for ( RowFormat* rw =d->rows.first(); rw; rw = rw->next() )
  {
      if ( !rw->isDefault() && worker.testCondition( rw ) )
      {
    for ( int i=m_rctSelection.left(); i<=m_rctSelection.right(); i++ )
    {
        cell = cellAt( i, rw->row() );
        if ( cell == d->defaultCell )
      // '&& worker.create_if_default' unnecessary as never used in type A
        {
      cell = new Cell( this, i, rw->row() );
      insertCell( cell );
        }
    }
      }
  }
    }

    // create an undo action
    if ( !doc()->undoLocked() )
    {
  UndoAction *undo = worker.createUndoAction( doc(), this, r );
        // test if the worker has an undo action
        if ( undo != 0L )
      doc()->addCommand( undo );
    }

    // complete rows selected ?
    if ( selected && isRowSelected() )
    {
  int row;
  for ( Cell* cell = d->cells.firstCell(); cell; cell = cell->nextCell() )
  {
      row = cell->row();
      if ( m_rctSelection.top() <= row && m_rctSelection.bottom() >= row
     && worker.testCondition( cell ) )
    if ( worker.type_B )
        worker.doWork( cell, false, cell->column(), row );
    else
        worker.prepareCell( cell );
  }

  if ( worker.type_B ) {
            // for type B there's nothing left to do
      if ( worker.emit_signal )
    emit sig_updateView( this, r );
  } else {
            // for type A now work on row formats
      for ( int i=m_rctSelection.top(); i<=m_rctSelection.bottom(); i++ )
      {
    RowFormat *rw=nonDefaultRowFormat(i);
    worker.doWork( rw );
      }
      if ( worker.emit_signal )
    emit sig_updateView( this );
  }
  return CompleteRows;
    }
    // complete columns selected ?
    else if ( selected && isColumnSelected() )
    {
  int col;
  for ( Cell* cell = d->cells.firstCell(); cell; cell = cell->nextCell() )
  {
      col = cell->column();
      if ( m_rctSelection.left() <= col && m_rctSelection.right() >= col
     && worker.testCondition( cell ) )
    if ( worker.type_B )
        worker.doWork( cell, false, col, cell->row() );
    else
        worker.prepareCell( cell );
  }

  if ( worker.type_B ) {
      if ( worker.emit_signal )
    emit sig_updateView( this, r );
  } else {
      // for type A now work on column formats
      for ( int i=m_rctSelection.left(); i<=m_rctSelection.right(); i++ )
      {
    ColumnFormat *cl=nonDefaultColumnFormat(i);
    worker.doWork( cl );
      }
      Cell *cell;
      for ( RowFormat* rw =d->rows.first(); rw; rw = rw->next() )
      {
    if ( !rw->isDefault() && worker.testCondition( rw ) )
    {
        for ( int i=m_rctSelection.left(); i<=m_rctSelection.right(); i++ )
        {
      cell = cellAt( i, rw->row() );
      // ### this if should be not necessary; cells are created
      //     before the undo object is created, aren't they?
      if ( cell == d->defaultCell )
      {
          cell = new Cell( this, i, rw->row() );
          insertCell( cell );
      }
      worker.doWork( cell, false, i, rw->row() );
        }
    }
      }
            if ( worker.emit_signal )
    emit sig_updateView( this );
  }
  return CompleteColumns;
    }
    // cell region selected
    else
    {
  Cell *cell;
  for ( int x = r.left(); x <= r.right(); x++ )
      for ( int y = r.top(); y <= r.bottom(); y++ )
      {
    cell = cellAt( x, y );
                if ( worker.testCondition( cell ) )
    {
        if ( worker.create_if_default && cell == d->defaultCell )
        {
      cell = new Cell( this, x, y );
      insertCell( cell );
        }
                    if ( cell != d->defaultCell )
      worker.doWork( cell, true, x, y );
    }
      }
        if ( worker.emit_signal )
      emit sig_updateView( this, r );
        return CellRegion;
    }
}

*/

Sheet::SelectionType Sheet::workOnCells( Selection* selectionInfo, CellWorker & worker )
{
  Sheet::SelectionType result;

  doc()->emitBeginOperation();

  // see what is selected; if nothing, take marker position
  bool selected = !(selectionInfo->isSingular());

  // create an undo action
  if ( !doc()->undoLocked() )
  {
    UndoAction* undo = worker.createUndoAction(doc(), this, *selectionInfo);
    // test if the worker has an undo action
    if ( undo != 0 )
    {
      doc()->addCommand( undo );
    }
  }

  Region::ConstIterator endOfList(selectionInfo->constEnd());
  for (Region::ConstIterator it = selectionInfo->constBegin(); it != endOfList; ++it)
  {
    // see what is selected; if nothing, take marker position
    TQRect range = (*it)->rect().normalize();

  int top = range.top();
  int left = range.left();
  int bottom = range.bottom();
  int right  = range.right();

  // create cells in rows if complete columns selected
  Cell * cell;
  Style * s = doc()->styleManager()->defaultStyle();

  if ( !worker.type_B && selected && util_isColumnSelected(range) )
  {
    for ( RowFormat * rw = d->rows.first(); rw; rw = rw->next() )
    {
      if ( worker.testCondition( rw ) )
      {
        for ( int col = left; col <= right; ++col )
        {
          cell = nonDefaultCell( col, rw->row(), false, s );
        }
      }
    }
  }

  // complete rows selected ?
  if ( selected && util_isRowSelected(range) )
  {
    for ( int row = top; row <= bottom; ++row )
    {
      cell = getFirstCellRow( row );
      while ( cell )
      {
        if ( worker.testCondition( cell ) )
        {
          if ( worker.type_B )
            worker.doWork( cell, false, cell->column(), row );
          else
            worker.prepareCell( cell );
        }
        cell = getNextCellRight( cell->column(), row );
      }
    }

    if ( worker.type_B )
    {
      // for type B there's nothing left to do
      ;
    }
    else
    {
      // for type A now work on row formats
      for ( int i = top; i <= bottom; ++i )
      {
        RowFormat * rw = nonDefaultRowFormat(i);
        worker.doWork( rw );
      }

      for ( int row = top; row <= bottom; ++row )
      {
        cell = getFirstCellRow( row );
        while ( cell )
        {
          if ( worker.testCondition( cell ) )
          {
            worker.doWork( cell, false, cell->column(), row );
          }
        cell = getNextCellRight( cell->column(), row );
        }
      }

    }
    result = CompleteRows;
  }
  // complete columns selected ?
  else if ( selected && util_isColumnSelected(range) )
  {
    for ( int col = range.left(); col <= right; ++col )
    {
      cell = getFirstCellColumn( col );
      while ( cell )
      {
  if ( worker.testCondition( cell ) )
        {
          if ( worker.type_B )
            worker.doWork( cell, false, col, cell->row() );
          else
            worker.prepareCell( cell );
        }

        cell = getNextCellDown( col, cell->row() );
      }
    }

    if ( worker.type_B )
    {
      ;
    }
    else
    {
      // for type A now work on column formats
      for ( int i = left; i <= right; ++i )
      {
        ColumnFormat * cl = nonDefaultColumnFormat( i );
        worker.doWork( cl );
      }

      for ( RowFormat * rw = d->rows.first(); rw; rw = rw->next() )
      {
        if ( worker.testCondition( rw ) )
        {
          for ( int i = left; i <= right; ++i )
          {
            cell = nonDefaultCell( i, rw->row(), false, s );
            worker.doWork( cell, false, i, rw->row() );
          }
        }
      }
    }
    result = CompleteColumns;
  }
  // cell region selected
  else
  {
    for ( int x = left; x <= right; ++x )
    {
      enableScrollBarUpdates(false);
      for ( int y = top; y <= bottom; ++y )
      {
        cell = cellAt( x, y );
        if ( worker.testCondition( cell ) )
        {
          if ( cell == d->defaultCell && worker.create_if_default )
          {
            cell = new Cell( this, s, x, y );
            insertCell( cell );
          }
          if ( cell != d->defaultCell )
          {
            // kdDebug() << "not default" << endl;
            worker.doWork( cell, true, x, y );
          }
        }
      }
      enableScrollBarUpdates(true);
      checkRangeVBorder(bottom);
    }
    checkRangeHBorder(right);
    result = CellRegion;
  }

  } // for Region::Elements

  // emitEndOperation();
  emit sig_updateView( this );

  if (worker.emit_signal)
  {
    emit sig_updateView( this, *selectionInfo );
  }

  return result;
}

void Sheet::setSelectionFont( Selection* selectionInfo,
                              const char *_font, int _size,
                              signed char _bold, signed char _italic,
                              signed char _underline, signed char _strike)
{
  FontManipulator* manipulator = new FontManipulator();
  manipulator->setSheet(this);
  manipulator->setProperty(Format::PFont);
  manipulator->setFontFamily(_font);
  manipulator->setFontSize(_size);
  manipulator->setFontBold(_bold);
  manipulator->setFontItalic(_italic);
  manipulator->setFontStrike(_strike);
  manipulator->setFontUnderline(_underline);
  manipulator->add(*selectionInfo);
  manipulator->execute();
}

void Sheet::setSelectionSize(Selection* selectionInfo,
                              int _size)
{
  // TODO Stefan: Increase/Decrease font size still used?
  int size;
  Cell* c;
  TQPoint marker(selectionInfo->marker());
  c = cellAt(marker);
  size = c->format()->textFontSize(marker.x(), marker.y());

  FontManipulator* manipulator = new FontManipulator();
  manipulator->setSheet(this);
  manipulator->setProperty(Format::PFont);
  manipulator->setFontSize(_size+size);
  manipulator->add(*selectionInfo);
  manipulator->execute();
}


struct SetSelectionUpperLowerWorker : public Sheet::CellWorker {
    int _type;
    Sheet   * _s;
    SetSelectionUpperLowerWorker( int type, Sheet * s )
      : Sheet::CellWorker( false ), _type( type ),  _s( s ) { }

    class UndoAction* createUndoAction( Doc* doc, Sheet* sheet, const KSpread::Region& region )
    {
      return new UndoChangeAreaTextCell( doc, sheet, region );
    }
    bool testCondition( Cell* c ) {
  return ( !c->value().isNumber() && !c->value().isBoolean() &&!c->isFormula() && !c->isDefault()
     && !c->text().isEmpty() && c->text()[0] != '*' && c->text()[0] != '!'
     && !c->isPartOfMerged() );
    }
    void doWork( Cell* cell, bool, int, int )
    {
  cell->setDisplayDirtyFlag();
  if ( _type == -1 )
      cell->setCellText( (cell->text().lower()));
  else if ( _type == 1 )
      cell->setCellText( (cell->text().upper()));
  cell->clearDisplayDirtyFlag();
    }
};

void Sheet::setSelectionUpperLower( Selection* selectionInfo,
                                           int _type )
{
  SetSelectionUpperLowerWorker w( _type, this );
  workOnCells( selectionInfo, w );
}


struct SetSelectionFirstLetterUpperWorker : public Sheet::CellWorker
{
    Changes * _c;
    Sheet   * _s;
    SetSelectionFirstLetterUpperWorker( Sheet * s )
      : Sheet::CellWorker( false ),  _s( s ) { }

    class UndoAction* createUndoAction( Doc* doc, Sheet* sheet, const KSpread::Region& region ) {
  return   new UndoChangeAreaTextCell( doc, sheet, region );
    }
    bool testCondition( Cell* c ) {
  return ( !c->value().isNumber() && !c->value().isBoolean() &&!c->isFormula() && !c->isDefault()
     && !c->text().isEmpty() && c->text()[0] != '*' && c->text()[0] != '!'
     && !c->isPartOfMerged() );
    }
    void doWork( Cell* cell, bool, int, int )
    {

  cell->setDisplayDirtyFlag();
  TQString tmp = cell->text();
  int len = tmp.length();
  cell->setCellText( (tmp.at(0).upper()+tmp.right(len-1)) );
  cell->clearDisplayDirtyFlag();
    }
};

void Sheet::setSelectionfirstLetterUpper( Selection* selectionInfo)
{
  SetSelectionFirstLetterUpperWorker w(  this );
  workOnCells( selectionInfo, w );
}


struct SetSelectionVerticalTextWorker : public Sheet::CellWorker {
    bool _b;
    SetSelectionVerticalTextWorker( bool b ) : Sheet::CellWorker( ), _b( b ) { }

    class UndoAction* createUndoAction( Doc* doc, Sheet* sheet, const KSpread::Region& region ) {
        TQString title=i18n("Vertical Text");
        return new UndoCellFormat( doc, sheet, region, title );
    }
    bool testCondition( Cell* cell ) {
  return ( !cell->isPartOfMerged() );
    }
    void doWork( Cell* cell, bool, int, int ) {
  cell->setDisplayDirtyFlag();
  cell->format()->setVerticalText( _b );
  cell->format()->setMultiRow( false );
  cell->format()->setAngle( 0 );
  cell->clearDisplayDirtyFlag();
    }
};

void Sheet::setSelectionVerticalText( Selection* selectionInfo,
                                             bool _b )
{
    SetSelectionVerticalTextWorker w( _b );
    workOnCells( selectionInfo, w );
}


struct SetSelectionCommentWorker : public Sheet::CellWorker {
    TQString _comment;
    SetSelectionCommentWorker( TQString comment ) : Sheet::CellWorker( ), _comment( comment ) { }

    class UndoAction* createUndoAction( Doc* doc, Sheet* sheet, const KSpread::Region& region ) {
        TQString title=i18n("Add Comment");
  return new UndoCellFormat( doc, sheet, region, title );
    }
    bool testCondition( Cell* cell ) {
  return ( !cell->isPartOfMerged() );
    }
    void doWork( Cell* cell, bool, int, int ) {
  cell->setDisplayDirtyFlag();
  cell->format()->setComment( _comment );
  cell->clearDisplayDirtyFlag();
    }
};

void Sheet::setSelectionComment( Selection* selectionInfo,
                                        const TQString &_comment)
{
    SetSelectionCommentWorker w( _comment );
    workOnCells( selectionInfo, w );
}


void Sheet::setSelectionAngle( Selection* selectionInfo,
                               int _value )
{
  AngleManipulator* manipulator = new AngleManipulator();
  manipulator->setSheet(this);
  manipulator->setProperty(Format::PAngle);
  manipulator->setAngle(_value);
  manipulator->add(*selectionInfo);
  manipulator->execute();
}

struct SetSelectionRemoveCommentWorker : public Sheet::CellWorker {
    SetSelectionRemoveCommentWorker( ) : Sheet::CellWorker( false ) { }

    class UndoAction* createUndoAction( Doc* doc, Sheet* sheet, const KSpread::Region& region ) {
        TQString title=i18n("Remove Comment");
  return new UndoCellFormat( doc, sheet, region, title );
    }
    bool testCondition( Cell* cell ) {
  return ( !cell->isPartOfMerged() );
    }
    void doWork( Cell* cell, bool, int, int ) {
  cell->setDisplayDirtyFlag();
  cell->format()->setComment( "" );
  cell->clearDisplayDirtyFlag();
    }
};

void Sheet::setSelectionRemoveComment( Selection* selectionInfo )
{
  if (areaIsEmpty(*selectionInfo, Comment))
    return;

  SetSelectionRemoveCommentWorker w;
  workOnCells( selectionInfo, w );
}


void Sheet::setSelectionTextColor( Selection* selectionInfo,
                                   const TQColor &tb_Color )
{
  FontColorManipulator* manipulator = new FontColorManipulator();
  manipulator->setSheet(this);
  manipulator->setProperty(Format::PTextPen);
  manipulator->setTextColor(tb_Color);
  manipulator->add(*selectionInfo);
  manipulator->execute();
}

void Sheet::setSelectionbgColor( Selection* selectionInfo,
                                 const TQColor &bg_Color )
{
  BackgroundColorManipulator* manipulator = new BackgroundColorManipulator();
  manipulator->setSheet(this);
  manipulator->setProperty(Format::PBackgroundColor);
  manipulator->setBackgroundColor(bg_Color);
  manipulator->add(*selectionInfo);
  manipulator->execute();
}


struct SetSelectionBorderColorWorker : public Sheet::CellWorker {
    const TQColor& bd_Color;
    SetSelectionBorderColorWorker( const TQColor& _bd_Color ) : Sheet::CellWorker( false ), bd_Color( _bd_Color ) { }

    class UndoAction* createUndoAction( Doc* doc, Sheet* sheet, const KSpread::Region& region ) {
        TQString title=i18n("Change Border Color");
  return new UndoCellFormat( doc, sheet, region, title );
    }
    bool testCondition( Cell* cell ) {
  return ( !cell->isPartOfMerged() );
    }
    void doWork( Cell* cell, bool, int, int ) {
  cell->setDisplayDirtyFlag();
  int it_Row = cell->row();
  int it_Col = cell->column();
  if ( cell->format()->topBorderStyle( it_Row, it_Col )!=TQt::NoPen )
    cell->format()->setTopBorderColor( bd_Color );
  if ( cell->format()->leftBorderStyle( it_Row, it_Col )!=TQt::NoPen )
    cell->format()->setLeftBorderColor( bd_Color );
  if ( cell->format()->fallDiagonalStyle( it_Row, it_Col )!=TQt::NoPen )
    cell->format()->setFallDiagonalColor( bd_Color );
  if ( cell->format()->goUpDiagonalStyle( it_Row, it_Col )!=TQt::NoPen )
    cell->format()->setGoUpDiagonalColor( bd_Color );
  if ( cell->format()->bottomBorderStyle( it_Row, it_Col )!=TQt::NoPen )
    cell->format()->setBottomBorderColor( bd_Color );
  if ( cell->format()->rightBorderStyle( it_Row, it_Col )!=TQt::NoPen )
    cell->format()->setRightBorderColor( bd_Color );
  cell->clearDisplayDirtyFlag();
    }
};

void Sheet::setSelectionBorderColor( Selection* selectionInfo,
                                            const TQColor &bd_Color )
{
    SetSelectionBorderColorWorker w( bd_Color );
    workOnCells( selectionInfo, w );
}


void Sheet::setSeries( const TQPoint &_marker, double start, double end, double step, Series mode, Series type)
{
  doc()->emitBeginOperation();

  TQString cellText;

  int x,y; /* just some loop counters */

  /* the actual number of columns or rows that the series will span.
     i.e. this will count 3 cells for a single cell that spans three rows
  */
  int numberOfCells;
  if (end > start)
    numberOfCells = (int) ((end - start) / step + 1); /*initialize for linear*/
  else if ( end <start )
    numberOfCells = (int) ((start - end) / step + 1); /*initialize for linear*/
  else //equal ! => one cell fix infini loop
      numberOfCells = 1;
  if (type == Geometric)
  {
    /* basically, A(n) = start ^ n
     * so when does end = start ^ n ??
     * when n = ln(end) / ln(start)
     */
    numberOfCells = (int)( (log((double)end) / log((double)start)) +
           DBL_EPSILON) + 1;
  }

  Cell * cell = NULL;

  /* markers for the top-left corner of the undo region.  It'll probably
   * be the top left corner of where the series is, but if something in front
   * is obscuring the cell, then it needs to be part of the undo region */
  TQRect undoRegion;

  undoRegion.setLeft(_marker.x());
  undoRegion.setTop(_marker.y());

  /* this whole block is used to find the correct size for the undo region.
     We're checking for two different things (in these examples,
       mode==column):

       1.  cells are vertically merged.  This means that one value in the
       series will span multiple cells.

       2.  a cell in the column is merged to a cell to its left.  In this case
       the cell value will be stored in the left most cell so we need to
       extend the undo range to include that column.
  */
  if ( mode == Column )
  {
    for ( y = _marker.y(); y <= (_marker.y() + numberOfCells - 1) && y <= KS_rowMax; y++ )
    {
      cell = cellAt( _marker.x(), y );

      if ( cell->isPartOfMerged() )
      {
        /* case 2. */
        cell = cell->obscuringCells().first();
        undoRegion.setLeft(TQMIN(undoRegion.left(), cell->column()));
      }
      /* case 1.  Add the extra space to numberOfCells and then skip
       over the region.  Note that because of the above if block 'cell'
       points to the correct cell in the case where both case 1 and 2
       are true
      */
      numberOfCells += cell->extraYCells();
      y += cell->extraYCells();
    }
    undoRegion.setRight( _marker.x() );
    undoRegion.setBottom( y - 1 );

    checkRangeVBorder( undoRegion.bottom() );
  }
  else if(mode == Row)
  {
    for ( x = _marker.x(); x <=(_marker.x() + numberOfCells - 1) && x <= KS_colMax; x++ )
    {
      /* see the code above for a column series for a description of
         what is going on here. */
      cell = cellAt( x,_marker.y(), false );

      if ( cell->isPartOfMerged() )
      {
        cell = cell->obscuringCells().first();
        undoRegion.setTop(TQMIN(undoRegion.top(), cell->row()));
      }
      numberOfCells += cell->extraXCells();
      x += cell->extraXCells();
    }
    undoRegion.setBottom( _marker.y() );
    undoRegion.setRight( x - 1 );

    checkRangeHBorder( undoRegion.right() );
  }

  kdDebug() << "Saving undo information" << endl;

  if ( !doc()->undoLocked() )
  {
    UndoChangeAreaTextCell *undo = new
      UndoChangeAreaTextCell( doc(), this, undoRegion );
    doc()->addCommand( undo );
  }

  kdDebug() << "Saving undo information done" << endl;

  setRegionPaintDirty( undoRegion );

  x = _marker.x();
  y = _marker.y();

  /* now we're going to actually loop through and set the values */
  double incr;
  Style * s = doc()->styleManager()->defaultStyle();
  if (step >= 0 && start < end)
  {
    for ( incr = start; incr <= end; )
    {
      cell = nonDefaultCell( x, y, false, s );

      if ( cell->isPartOfMerged() )
      {
        cell = cell->obscuringCells().first();
      }

      //      cell->setCellText(cellText.setNum( incr ));

      cell->setNumber( incr );
      if (mode == Column)
      {
        ++y;
        if (cell->doesMergeCells())
        {
          y += cell->extraYCells();
        }
        if ( y > KS_rowMax )
        {
          break;
        }
      }
      else if (mode == Row)
      {
        ++x;
        if (cell->doesMergeCells())
        {
          x += cell->extraXCells();
        }
        if ( x > KS_colMax )
        {
          break;
        }
      }
      else
      {
        kdDebug(36001) << "Error in Series::mode" << endl;
        return;
      }

      if (type == Linear)
        incr = incr + step;
      else if (type == Geometric)
        incr = incr * step;
      else
      {
        kdDebug(36001) << "Error in Series::type" << endl;
        return;
      }
    }
  }
  else
  if (step >= 0 && start > end)
  {
    for ( incr = start; incr >= end; )
    {
      cell = nonDefaultCell( x, y, false, s );

      if (cell->isPartOfMerged())
      {
        cell = cell->obscuringCells().first();
      }

      //      cell->setCellText(cellText.setNum( incr ));
      cell->setNumber( incr );
      if (mode == Column)
      {
        ++y;
        if (cell->doesMergeCells())
        {
          y += cell->extraYCells();
        }
        if ( y > KS_rowMax )
        {
          break;
        }
      }
      else if (mode == Row)
      {
        ++x;
        if (cell->doesMergeCells())
        {
          x += cell->extraXCells();
        }
        if ( x > KS_colMax )
        {
          break;
        }
      }
      else
      {
        kdDebug(36001) << "Error in Series::mode" << endl;
        return;
      }

      if (type == Linear)
        incr = incr + step;
      else if (type == Geometric)
        incr = incr * step;
      else
      {
        kdDebug(36001) << "Error in Series::type" << endl;
        return;
      }
    }
  }
  else
  {
    for ( incr = start; incr <= end; )
    {
      cell = nonDefaultCell( x, y, false, s );

      if (cell->isPartOfMerged())
      {
        cell = cell->obscuringCells().first();
      }

      //cell->setCellText(cellText.setNum( incr ));
      cell->setNumber( incr );
      if (mode == Column)
      {
        ++y;
        if (cell->doesMergeCells())
        {
          y += cell->extraYCells();
        }
        if ( y > KS_rowMax )
        {
          break;
        }
      }
      else if (mode == Row)
      {
        ++x;
        if (cell->doesMergeCells())
        {
          x += cell->extraXCells();
        }
        if ( x > KS_colMax )
        {
          break;
        }
      }
      else
      {
        kdDebug(36001) << "Error in Series::mode" << endl;
        return;
      }

      if (type == Linear)
        incr = incr + step;
      else if (type == Geometric)
      {

        incr = incr * step;
        //a step = 1 into geometric serie is not good
        //we don't increase value => infini loop
        if (step == 1)
            return;
      }
      else
      {
        kdDebug(36001) << "Error in Series::type" << endl;
        return;
      }
    }
  }


  //  doc()->emitEndOperation();
  emit sig_updateView( this );
}


struct SetSelectionPercentWorker : public Sheet::CellWorkerTypeA
{
    bool b;
    SetSelectionPercentWorker( bool _b ) : b( _b ) { }

    TQString getUndoTitle() { return i18n("Format Percent"); }
    bool testCondition( RowFormat* ) {
        //TODO: no idea what to put here, now that factor's gone :(
        return ( true );
    }
    void doWork( RowFormat* rw ) {
  //rw->setPrecision( 0 );
  rw->setFormatType( b ? Percentage_format : Generic_format);
    }
    void doWork( ColumnFormat* cl ) {
  cl->setFormatType( b ? Percentage_format : Generic_format);
    }
    void prepareCell( Cell* cell ) {
  cell->format()->clearProperty(Format::PFormatType);
  cell->format()->clearNoFallBackProperties( Format::PFormatType );
    }
    bool testCondition( Cell* cell ) {
  return ( !cell->isPartOfMerged() );
    }
    void doWork( Cell* cell, bool cellRegion, int, int ) {
  if ( cellRegion )
      cell->setDisplayDirtyFlag();
  cell->format()->setFormatType( b ? Percentage_format : Generic_format);
  if ( cellRegion )
      cell->clearDisplayDirtyFlag();
    }
};

void Sheet::setSelectionPercent( Selection* selectionInfo, bool b )
{
    SetSelectionPercentWorker w( b );
    workOnCells( selectionInfo, w );
}

void Sheet::slotAreaModified (const TQString &name)
{
  d->dependencies->areaModified (name);
}


void Sheet::refreshRemoveAreaName(const TQString & _areaName)
{
  Cell * c = d->cells.firstCell();
  TQString tmp = "'" + _areaName + "'";
  for( ;c ; c = c->nextCell() )
  {
    if ( c->isFormula() )
    {
      if (c->text().find(tmp) != -1)
      {
        if ( !c->makeFormula() )
          kdError(36001) << "ERROR: Syntax ERROR" << endl;
      }
    }
  }
}

void Sheet::refreshChangeAreaName(const TQString & _areaName)
{
  Cell * c = d->cells.firstCell();
  TQString tmp = "'" + _areaName + "'";
  for( ;c ; c = c->nextCell() )
  {
    if ( c->isFormula() )
    {
      if (c->text().find(tmp) != -1)
      {
        if ( !c->makeFormula() )
          kdError(36001) << "ERROR: Syntax ERROR" << endl;
        else
        {
          /* setting a cell calc dirty also sets it paint dirty */
          c->setCalcDirtyFlag();
        }
      }
    }
  }
}

void Sheet::changeCellTabName( TQString const & old_name, TQString const & new_name )
{
    Cell* c = d->cells.firstCell();
    for( ;c; c = c->nextCell() )
    {
        if( c->isFormula() )
        {
            if(c->text().find(old_name)!=-1)
            {
                int nb = c->text().contains(old_name+"!");
                TQString tmp=old_name+"!";
                int len = tmp.length();
                tmp=c->text();

                for( int i=0; i<nb; i++ )
                {
                    int pos = tmp.find( old_name+"!" );
                    tmp.replace( pos, len, new_name+"!" );
                }
                c->setCellText(tmp);
            }
        }
    }
}

bool Sheet::shiftRow( const TQRect &rect,bool makeUndo )
{
    UndoInsertCellRow * undo = 0;
    if ( !doc()->undoLocked()  &&makeUndo)
    {
        undo = new UndoInsertCellRow( doc(), this, rect );
        doc()->addCommand( undo );
    }

    bool res=true;
    bool result;
    for( int i=rect.top(); i<=rect.bottom(); i++ )
    {
        for( int j=0; j<=(rect.right()-rect.left()); j++ )
        {
            result = d->cells.shiftRow( TQPoint(rect.left(),i) );
            if( !result )
                res=false;
        }
    }
    TQPtrListIterator<Sheet> it( workbook()->sheetList() );
    for( ; it.current(); ++it )
    {
        for(int i = rect.top(); i <= rect.bottom(); i++ )
            it.current()->changeNameCellRef( TQPoint( rect.left(), i ), false,
                                             Sheet::ColumnInsert, name(),
                                             ( rect.right() - rect.left() + 1),
                                             undo);
    }
    refreshChart(TQPoint(rect.left(),rect.top()), false, Sheet::ColumnInsert);
    refreshMergedCell();
    recalc();
    emit sig_updateView( this );

    return res;
}

bool Sheet::shiftColumn( const TQRect& rect,bool makeUndo )
{
    UndoInsertCellCol * undo = 0;
    if ( !doc()->undoLocked()  &&makeUndo)
    {
        undo = new UndoInsertCellCol( doc(), this,rect);
        doc()->addCommand( undo );
    }

    bool res=true;
    bool result;
    for( int i =rect.left(); i<=rect.right(); i++ )
    {
        for( int j=0; j<=(rect.bottom()-rect.top()); j++ )
        {
            result = d->cells.shiftColumn( TQPoint(i,rect.top()) );
            if(!result)
                res=false;
        }
    }

    TQPtrListIterator<Sheet> it( workbook()->sheetList() );
    for( ; it.current(); ++it )
    {
        for(int i=rect.left();i<=rect.right();i++)
            it.current()->changeNameCellRef( TQPoint( i, rect.top() ), false,
                                             Sheet::RowInsert, name(),
                                             ( rect.bottom() - rect.top() + 1 ),
                                             undo );
    }
    refreshChart(/*marker*/TQPoint(rect.left(),rect.top()), false, Sheet::RowInsert);
    refreshMergedCell();
    recalc();
    emit sig_updateView( this );

    return res;
}

void Sheet::unshiftColumn( const TQRect & rect,bool makeUndo )
{
    UndoRemoveCellCol * undo = 0;
    if ( !doc()->undoLocked() && makeUndo )
    {
        undo = new UndoRemoveCellCol( doc(), this, rect );
        doc()->addCommand( undo );
    }

    for(int i =rect.top();i<=rect.bottom();i++)
        for(int j=rect.left();j<=rect.right();j++)
               d->cells.remove(j,i);

    for(int i =rect.left();i<=rect.right();i++)
        for(int j=0;j<=(rect.bottom()-rect.top());j++)
                d->cells.unshiftColumn( TQPoint(i,rect.top()) );

    TQPtrListIterator<Sheet> it( workbook()->sheetList() );
    for( ; it.current(); ++it )
        for(int i=rect.left();i<=rect.right();i++)
                it.current()->changeNameCellRef( TQPoint( i, rect.top() ), false,
                                                 Sheet::RowRemove, name(),
                                                 ( rect.bottom() - rect.top() + 1 ),
                                                 undo );

    refreshChart( TQPoint(rect.left(),rect.top()), false, Sheet::RowRemove );
    refreshMergedCell();
    recalc();
    emit sig_updateView( this );
}

void Sheet::unshiftRow( const TQRect & rect,bool makeUndo )
{
    UndoRemoveCellRow * undo = 0;
    if ( !doc()->undoLocked() && makeUndo )
    {
        undo = new UndoRemoveCellRow( doc(), this, rect );
        doc()->addCommand( undo );
    }
    for(int i =rect.top();i<=rect.bottom();i++)
        for(int j=rect.left();j<=rect.right();j++)
                d->cells.remove(j,i);

    for(int i =rect.top();i<=rect.bottom();i++)
        for(int j=0;j<=(rect.right()-rect.left());j++)
                d->cells.unshiftRow( TQPoint(rect.left(),i) );

    TQPtrListIterator<Sheet> it( workbook()->sheetList() );
    for( ; it.current(); ++it )
        for(int i=rect.top();i<=rect.bottom();i++)
                it.current()->changeNameCellRef( TQPoint( rect.left(), i ), false,
                                                 Sheet::ColumnRemove, name(),
                                                 ( rect.right() - rect.left() + 1 ),
                                                 undo);

    refreshChart(TQPoint(rect.left(),rect.top()), false, Sheet::ColumnRemove );
    refreshMergedCell();
    recalc();
    emit sig_updateView( this );
}

bool Sheet::insertColumn( int col, int nbCol, bool makeUndo )
{
    UndoInsertColumn * undo = 0;
    if ( !doc()->undoLocked() && makeUndo)
    {
        undo = new UndoInsertColumn( doc(), this, col, nbCol );
        doc()->addCommand( undo );
    }

    bool res=true;
    bool result;
    for( int i=0; i<=nbCol; i++ )
    {
        // Recalculate range max (minus size of last column)
        d->sizeMaxX -= columnFormat( KS_colMax )->dblWidth();

        result = d->cells.insertColumn( col );
        d->columns.insertColumn( col );
        if(!result)
            res = false;

        //Recalculate range max (plus size of new column)
        d->sizeMaxX += columnFormat( col+i )->dblWidth();
    }

    TQPtrListIterator<Sheet> it( workbook()->sheetList() );
    for( ; it.current(); ++it )
        it.current()->changeNameCellRef( TQPoint( col, 1 ), true,
                                         Sheet::ColumnInsert, name(),
                                         nbCol + 1, undo );

    //update print settings
    d->print->insertColumn( col, nbCol );

    refreshChart( TQPoint( col, 1 ), true, Sheet::ColumnInsert );
    refreshMergedCell();
    recalc();
    emit sig_updateHBorder( this );
    emit sig_updateView( this );

    return res;
}

bool Sheet::insertRow( int row, int nbRow, bool makeUndo )
{
    UndoInsertRow *undo = 0;
    if ( !doc()->undoLocked() && makeUndo)
    {
        undo = new UndoInsertRow( doc(), this, row, nbRow );
        doc()->addCommand( undo );
    }

    bool res=true;
    bool result;
    for( int i=0; i<=nbRow; i++ )
    {
        // Recalculate range max (minus size of last row)
        d->sizeMaxY -= rowFormat( KS_rowMax )->dblHeight();

        result = d->cells.insertRow( row );
        d->rows.insertRow( row );
        if( !result )
            res = false;

        //Recalculate range max (plus size of new row)
        d->sizeMaxY += rowFormat( row )->dblHeight();
    }

    TQPtrListIterator<Sheet> it( workbook()->sheetList() );
    for( ; it.current(); ++it )
        it.current()->changeNameCellRef( TQPoint( 1, row ), true,
                                         Sheet::RowInsert, name(),
                                         nbRow + 1, undo );

    //update print settings
    d->print->insertRow( row, nbRow );

    refreshChart( TQPoint( 1, row ), true, Sheet::RowInsert );
    refreshMergedCell();
    recalc();
    emit sig_updateVBorder( this );
    emit sig_updateView( this );

    return res;
}

void Sheet::removeColumn( int col, int nbCol, bool makeUndo )
{
    UndoRemoveColumn *undo = 0;
    if ( !doc()->undoLocked() && makeUndo)
    {
        undo = new UndoRemoveColumn( doc(), this, col, nbCol );
        doc()->addCommand( undo );
    }

    for ( int i = 0; i <= nbCol; ++i )
    {
        // Recalculate range max (minus size of removed column)
        d->sizeMaxX -= columnFormat( col )->dblWidth();

        d->cells.removeColumn( col );
        d->columns.removeColumn( col );

        //Recalculate range max (plus size of new column)
        d->sizeMaxX += columnFormat( KS_colMax )->dblWidth();
    }

    TQPtrListIterator<Sheet> it( workbook()->sheetList() );
    for( ; it.current(); ++it )
        it.current()->changeNameCellRef( TQPoint( col, 1 ), true,
                                         Sheet::ColumnRemove, name(),
                                         nbCol + 1, undo );

    //update print settings
    d->print->removeColumn( col, nbCol );

    refreshChart( TQPoint( col, 1 ), true, Sheet::ColumnRemove );
    refreshMergedCell();
    recalc();
    emit sig_updateHBorder( this );
    emit sig_updateView( this );
}

void Sheet::removeRow( int row, int nbRow, bool makeUndo )
{
    UndoRemoveRow *undo = 0;
    if ( !doc()->undoLocked() && makeUndo )
    {
        undo = new UndoRemoveRow( doc(), this, row, nbRow );
        doc()->addCommand( undo );
    }

    for( int i=0; i<=nbRow; i++ )
    {
        // Recalculate range max (minus size of removed row)
        d->sizeMaxY -= rowFormat( row )->dblHeight();

        d->cells.removeRow( row );
        d->rows.removeRow( row );

        //Recalculate range max (plus size of new row)
        d->sizeMaxY += rowFormat( KS_rowMax )->dblHeight();
    }

    TQPtrListIterator<Sheet> it( workbook()->sheetList() );
    for( ; it.current(); ++it )
        it.current()->changeNameCellRef( TQPoint( 1, row ), true,
                                         Sheet::RowRemove, name(),
                                         nbRow + 1, undo );

    //update print settings
    d->print->removeRow( row, nbRow );

    refreshChart( TQPoint( 1, row ), true, Sheet::RowRemove );
    refreshMergedCell();
    recalc();
    emit sig_updateVBorder( this );
    emit sig_updateView( this );
}

void Sheet::hideRow(const Region& region)
{
  HideShowManipulator* manipulator = new HideShowManipulator();
  manipulator->setSheet(this);
  manipulator->setManipulateRows(true);
  manipulator->add(region);
  manipulator->execute();
}

void Sheet::emitHideRow()
{
    emit sig_updateVBorder( this );
    emit sig_updateView( this );
}

void Sheet::showRow(const Region& region)
{
  HideShowManipulator* manipulator = new HideShowManipulator();
  manipulator->setSheet(this);
  manipulator->setManipulateRows(true);
  manipulator->setReverse(true);
  manipulator->add(region);
  manipulator->execute();
}


void Sheet::hideColumn(const Region& region)
{
  HideShowManipulator* manipulator = new HideShowManipulator();
  manipulator->setSheet(this);
  manipulator->setManipulateColumns(true);
  manipulator->add(region);
  manipulator->execute();
}

void Sheet::emitHideColumn()
{
    emit sig_updateHBorder( this );
    emit sig_updateView( this );
}

void Sheet::showColumn(const Region& region)
{
  HideShowManipulator* manipulator = new HideShowManipulator();
  manipulator->setSheet(this);
  manipulator->setManipulateColumns(true);
  manipulator->setReverse(true);
  manipulator->add(region);
  manipulator->execute();
}


void Sheet::refreshChart(const TQPoint & pos, bool fullRowOrColumn, ChangeRef ref)
{
  Cell * c = d->cells.firstCell();
  for( ;c; c = c->nextCell() )
  {
    if ( (ref == ColumnInsert || ref == ColumnRemove) && fullRowOrColumn
        && c->column() >= (pos.x() - 1))
    {
      if (c->updateChart())
        return;
    }
    else if ( (ref == ColumnInsert || ref == ColumnRemove )&& !fullRowOrColumn
              && c->column() >= (pos.x() - 1) && c->row() == pos.y() )
    {
      if (c->updateChart())
        return;
    }
    else if ((ref == RowInsert || ref == RowRemove) && fullRowOrColumn
             && c->row() >= (pos.y() - 1))
    {
      if (c->updateChart())
        return;
    }
    else if ( (ref == RowInsert || ref == RowRemove) && !fullRowOrColumn
        && c->column() == pos.x() && c->row() >= (pos.y() - 1) )
    {
      if (c->updateChart())
        return;
    }
  }

  //refresh chart when there is a chart and you remove
  //all cells
  if (c == 0L)
  {
     CellBinding * bind;
     for ( bind = firstCellBinding(); bind != 0L; bind = nextCellBinding() )
     {
       bind->cellChanged( 0 );
     }
     //    CellBinding * bind = firstCellBinding();
     //    if ( bind != 0L )
     //      bind->cellChanged( 0 );
  }

}

void Sheet::refreshMergedCell()
{
  Cell* c = d->cells.firstCell();
  for( ;c; c = c->nextCell() )
  {
    if(c->doesMergeCells())
      c->mergeCells( c->column(), c->row(), c->extraXCells(), c->extraYCells() );
  }
}


void Sheet::changeNameCellRef( const TQPoint & pos, bool fullRowOrColumn,
                                      ChangeRef ref, TQString tabname, int nbCol,
                                      UndoInsertRemoveAction * undo )
{
  bool correctDefaultSheetName = (tabname == name()); // for cells without sheet ref (eg "A1")
  Cell* c = d->cells.firstCell();
  for( ;c; c = c->nextCell() )
  {
    if( c->isFormula() )
    {
      TQString origText = c->text();
      unsigned int i = 0;
      bool error = false;
      TQString newText;

      bool correctSheetName = correctDefaultSheetName;
      //bool previousCorrectSheetName = false;
      TQChar origCh;
      for ( ; i < origText.length(); ++i )
      {
        origCh = origText[i];
        if ( origCh != ':' && origCh != '$' && !origCh.isLetter() )
        {
          newText += origCh;
          // Reset the "correct table indicator"
          correctSheetName = correctDefaultSheetName;
        }
        else // Letter or dollar : maybe start of cell name/range
          // (or even ':', like in a range - note that correctSheet is kept in this case)
        {
          // Collect everything that forms a name (cell name or sheet name)
          TQString str;
          bool sheetNameFound = false; //Sheet names need spaces
          for( ; ( i < origText.length() ) &&  // until the end
                 (  ( origText[i].isLetter() || origText[i].isDigit() || origText[i] == '$' ) ||  // all text and numbers are welcome
                    ( sheetNameFound && origText[i].isSpace() ) ) //in case of a sheet name, we include spaces too
               ; ++i )
          {
            str += origText[i];
            if ( origText[i] == '!' )
              sheetNameFound = true;
          }
          // Was it a sheet name ?
          if ( origText[i] == '!' )
          {
            newText += str + '!'; // Copy it (and the '!')
            // Look for the sheet name right before that '!'
            correctSheetName = ( newText.right( tabname.length()+1 ) == tabname+"!" );
          }
          else // It must be a cell identifier
          {
            // Parse it
            Point point( str );
            if ( point.isValid() )
            {
              int col = point.pos().x();
              int row = point.pos().y();
              TQString newPoint;

              // Update column
              if ( point.columnFixed() )
                newPoint = '$';

              if( ref == ColumnInsert
                  && correctSheetName
                  && col + nbCol <= KS_colMax
                  && col >= pos.x()     // Column after the new one : +1
                  && ( fullRowOrColumn || row == pos.y() ) ) // All rows or just one
              {
                newPoint += Cell::columnName( col + nbCol );
              }
              else if( ref == ColumnRemove
                       && correctSheetName
                       && col > pos.x() // Column after the deleted one : -1
                       && ( fullRowOrColumn || row == pos.y() ) ) // All rows or just one
              {
                newPoint += Cell::columnName( col - nbCol );
              }
              else
                newPoint += Cell::columnName( col );

              // Update row
              if ( point.rowFixed() )
                newPoint += '$';

              if( ref == RowInsert
                  && correctSheetName
                  && row + nbCol <= KS_rowMax
                  && row >= pos.y() // Row after the new one : +1
                  && ( fullRowOrColumn || col == pos.x() ) ) // All columns or just one
              {
                newPoint += TQString::number( row + nbCol );
              }
              else if( ref == RowRemove
                       && correctSheetName
                       && row > pos.y() // Row after the deleted one : -1
                       && ( fullRowOrColumn || col == pos.x() ) ) // All columns or just one
              {
                newPoint += TQString::number( row - nbCol );
              }
              else
                newPoint += TQString::number( row );

              if( correctSheetName &&
                  ( ( ref == ColumnRemove
                      && col == pos.x() // Column is the deleted one : error
                      && ( fullRowOrColumn || row == pos.y() ) ) ||
                    ( ref == RowRemove
                      && row == pos.y() // Row is the deleted one : error
                      && ( fullRowOrColumn || col == pos.x() ) ) ||
                    ( ref == ColumnInsert
                      && col + nbCol > KS_colMax
                      && col >= pos.x()     // Column after the new one : +1
                      && ( fullRowOrColumn || row == pos.y() ) ) ||
                    ( ref == RowInsert
                      && row + nbCol > KS_rowMax
                      && row >= pos.y() // Row after the new one : +1
                      && ( fullRowOrColumn || col == pos.x() ) ) ) )
              {
                newPoint = "#" + i18n("Dependency") + "!";
                error = true;
              }

              newText += newPoint;
            }
            else // Not a cell ref
            {
              kdDebug(36001) << "Copying (unchanged) : '" << str << "'" << endl;
              newText += str;
            }
            // Copy the char that got us to stop
            if ( i < origText.length() ) {
              newText += origText[i];
              if( origText[i] != ':' )
                correctSheetName = correctDefaultSheetName;
            }
          }
        }
      }

      if ( error && undo != 0 ) //Save the original formula, as we cannot calculate the undo of broken formulas
      {
          TQString formulaText = c->text();
          int origCol = c->column();
          int origRow = c->row();

          if ( ref == ColumnInsert && origCol >= pos.x() )
              origCol -= nbCol;
          if ( ref == RowInsert && origRow >= pos.y() )
              origRow -= nbCol;

          if ( ref == ColumnRemove && origCol >= pos.x() )
              origCol += nbCol;
          if ( ref == RowRemove && origRow >= pos.y() )
              origRow += nbCol;

          undo->saveFormulaReference( this, origCol, origRow, formulaText );
      }

      c->setCellText( newText );
    }
  }
}

#if 0
void Sheet::replace( const TQString &_find, const TQString &_replace, long options,
                            Canvas *canvas )
{
  Selection* selectionInfo = canvas->view()->selectionInfo();

    // Identify the region of interest.
    TQRect region( selectionInfo->selection() );
    TQPoint marker( selectionInfo->marker() );

    if (options & KReplaceDialog::SelectedText)
    {

        // Complete rows selected ?
        if ( util_isRowSelected(region) )
        {
        }
        // Complete columns selected ?
        else if ( util_isColumnSelected(region) )
        {
        }
    }
    else
    {
        // All cells.
        region.setCoords( 1, 1, d->maxRow, d->maxColumn );
    }

    // Create the class that handles all the actual replace stuff, and connect it to its
    // local slots.
    KReplace dialog( _find, _replace, options );
    TQObject::connect(
        &dialog, TQ_SIGNAL( highlight( const TQString &, int, int, const TQRect & ) ),
        canvas, TQ_SLOT( highlight( const TQString &, int, int, const TQRect & ) ) );
    TQObject::connect(
        &dialog, TQ_SIGNAL( replace( const TQString &, int, int,int, const TQRect & ) ),
        canvas, TQ_SLOT( replace( const TQString &, int, int,int, const TQRect & ) ) );

    // Now do the replacing...
    if ( !doc()->undoLocked() )
    {
        UndoChangeAreaTextCell *undo = new UndoChangeAreaTextCell( doc(), this, region );
        doc()->addCommand( undo );
    }

    TQRect cellRegion( 0, 0, 0, 0 );
    bool bck = options & KFindDialog::FindBackwards;

    int colStart = !bck ? region.left() : region.right();
    int colEnd = !bck ? region.right() : region.left();
    int rowStart = !bck ? region.top() :region.bottom();
    int rowEnd = !bck ? region.bottom() : region.top();
    if ( options & KFindDialog::FromCursor ) {
        colStart = marker.x();
        rowStart =  marker.y();
    }
    Cell *cell;
    for (int row = rowStart ; !bck ? row < rowEnd : row > rowEnd ; !bck ? ++row : --row )
    {
        for(int col = colStart ; !bck ? col < colEnd : col > colEnd ; !bck ? ++col : --col )
        {
            cell = cellAt( col, row );
            if ( !cell->isDefault() && !cell->isObscured() && !cell->isFormula() )
            {
                TQString text = cell->text();
                cellRegion.setTop( row );
                cellRegion.setLeft( col );
                if (!dialog.replace( text, cellRegion ))
                    return;
            }
        }
    }
}
#endif

void Sheet::borderBottom( Selection* selectionInfo, const TQColor &_color )
{
  BorderManipulator* manipulator = new BorderManipulator();
  manipulator->setSheet(this);
  manipulator->setBottomBorderPen(TQPen(_color, 1, TQt::SolidLine));
  manipulator->add(*selectionInfo);
  manipulator->execute();
}

void Sheet::borderRight( Selection* selectionInfo, const TQColor &_color )
{
  BorderManipulator* manipulator = new BorderManipulator();
  manipulator->setSheet(this);
  manipulator->setRightBorderPen(TQPen(_color, 1, TQt::SolidLine));
  manipulator->add(*selectionInfo);
  manipulator->execute();
}

void Sheet::borderLeft( Selection* selectionInfo, const TQColor &_color )
{
  BorderManipulator* manipulator = new BorderManipulator();
  manipulator->setSheet(this);
  manipulator->setLeftBorderPen(TQPen(_color, 1, TQt::SolidLine));
  manipulator->add(*selectionInfo);
  manipulator->execute();
}

void Sheet::borderTop( Selection* selectionInfo, const TQColor &_color )
{
  BorderManipulator* manipulator = new BorderManipulator();
  manipulator->setSheet(this);
  manipulator->setTopBorderPen(TQPen(_color, 1, TQt::SolidLine));
  manipulator->add(*selectionInfo);
  manipulator->execute();
}

void Sheet::borderOutline( Selection* selectionInfo, const TQColor &_color )
{
  BorderManipulator* manipulator = new BorderManipulator();
  manipulator->setSheet(this);
  manipulator->setTopBorderPen(TQPen(_color, 1, TQt::SolidLine));
  manipulator->setBottomBorderPen(TQPen(_color, 1, TQt::SolidLine));
  manipulator->setLeftBorderPen(TQPen(_color, 1, TQt::SolidLine));
  manipulator->setRightBorderPen(TQPen(_color, 1, TQt::SolidLine));
  manipulator->add(*selectionInfo);
  manipulator->execute();
}

void Sheet::borderAll( Selection * selectionInfo,
                       const TQColor & _color )
{
  BorderManipulator* manipulator = new BorderManipulator();
  manipulator->setSheet(this);
  manipulator->setTopBorderPen(TQPen(_color, 1, TQt::SolidLine));
  manipulator->setBottomBorderPen(TQPen(_color, 1, TQt::SolidLine));
  manipulator->setLeftBorderPen(TQPen(_color, 1, TQt::SolidLine));
  manipulator->setRightBorderPen(TQPen(_color, 1, TQt::SolidLine));
  manipulator->setHorizontalPen(TQPen(_color, 1, TQt::SolidLine));
  manipulator->setVerticalPen(TQPen(_color, 1, TQt::SolidLine));
  manipulator->add(*selectionInfo);
  manipulator->execute();
}

void Sheet::borderRemove( Selection* selectionInfo )
{
  BorderManipulator* manipulator = new BorderManipulator();
  manipulator->setSheet(this);
  manipulator->setTopBorderPen(TQPen(TQt::NoPen));
  manipulator->setBottomBorderPen(TQPen(TQt::NoPen));
  manipulator->setLeftBorderPen(TQPen(TQt::NoPen));
  manipulator->setRightBorderPen(TQPen(TQt::NoPen));
  manipulator->setHorizontalPen(TQPen(TQt::NoPen));
  manipulator->setVerticalPen(TQPen(TQt::NoPen));
  manipulator->add(*selectionInfo);
  manipulator->execute();
}


void Sheet::sortByRow( const TQRect &area, int ref_row, SortingOrder mode )
{
  Point point;
  point.setSheet(this);
  point.setSheetName (d->name);
  point.setPos(area.topLeft());
  point.setColumnFixed(false);
  point.setRowFixed(false);

  sortByRow( area, ref_row, 0, 0, mode, mode, mode, 0, false, false, point,true );
}

void Sheet::sortByColumn( const TQRect &area, int ref_column, SortingOrder mode )
{
  Point point;
  point.setSheet(this);
  point.setSheetName(d->name);
  point.setPos(area.topLeft());
  point.setColumnFixed(false);
  point.setRowFixed(false);

  sortByColumn( area, ref_column, 0, 0, mode, mode, mode, 0, false, false,
                point,true );
}

void Sheet::checkCellContent(Cell * cell1, Cell * cell2, int & ret)
{
  if ( cell1->isEmpty() )
  {
    ret = 1;
    return;
  }
  else if ( cell1->isObscured() && cell1->isPartOfMerged() )
  {
    ret = 1;
    return;
  }
  else if ( cell2->isEmpty() )
  {
    ret = 2;
    return;
  }
  ret = 0;
}

void Sheet::sortByRow( const TQRect &area, int key1, int key2, int key3,
                              SortingOrder order1, SortingOrder order2,
                              SortingOrder order3,
                              TQStringList const * firstKey, bool copyFormat,
                              bool headerRow, Point const & outputPoint, bool respectCase )
{
  TQRect r( area );
  Map::respectCase = respectCase;
  Q_ASSERT( order1 == Increase || order1 == Decrease );

  // It may not happen that entire columns are selected.
  Q_ASSERT( util_isColumnSelected(r) == false );

  // Are entire rows selected ?
  if ( util_isRowSelected(r) )
  {
    r.setLeft( KS_colMax );
    r.setRight( 0 );

    // Determine a correct left and right.
    // Iterate over all cells to find out which cells are
    // located in the selected rows.
    for ( int row = r.top(); row <= r.bottom(); ++row )
    {
      Cell * c = getFirstCellRow( row );
      int col;
      while ( c )
      {
        col = c->column();
        if ( !c->isEmpty() )
        {
          if ( col > r.right() )
            r.rRight() = col;
          if ( col < r.left() )
            r.rLeft() = col;
        }
        c = getNextCellRight( col, row );
      }
    }

    // Any cells to sort here ?
    if ( r.right() < r.left() )
    {
        Map::respectCase = true;
        return;
    }
  }

  TQRect target( outputPoint.pos().x(), outputPoint.pos().y(), r.width(), r.height() );

  doc()->emitBeginOperation();

  if ( !doc()->undoLocked() )
  {
    UndoSort *undo = new UndoSort( doc(), this, target );
    doc()->addCommand( undo );
  }

  if (target.topLeft() != r.topLeft())
  {
    int targetLeft = target.left();
    int targetTop  = target.top();
    int sourceTop  = r.top();
    int sourceLeft = r.left();

    key1 = key1 - sourceTop + targetTop;
    key2 = key2 - sourceTop + targetTop;
    key3 = key3 - sourceTop + targetTop;

    for ( int x = 0; x < r.width(); ++x)
    {
      for ( int y = 0; y < r.height(); ++y )
      {
        // from - to
        copyCells( sourceLeft + x, sourceTop + y,
                   targetLeft + x, targetTop + y, copyFormat );
      }
    }
  }

  // Sorting algorithm: David's :). Well, I guess it's called minmax or so.
  // For each column, we look for all cells right hand of it and we find the one to swap with it.
  // Much faster than the awful bubbleSort...
  Cell * cell;
  Cell * cell1;
  Cell * cell2;
  Cell * bestCell;
  int status = 0;

  for ( int d = target.left();  d <= target.right(); ++d )
  {
    cell1 = cellAt( d, key1 );
    if ( cell1->isObscured() && cell1->isPartOfMerged() )
    {
      Cell* obscuring = cell1->obscuringCells().first();
      cell = cellAt( obscuring->column(), key1 );
      cell1 = cellAt( obscuring->column() + cell->extraXCells() + 1,
                      obscuring->column());
      d = obscuring->column() + cell->extraXCells() + 1;
    }

    // Look for which column we want to swap with the one number d
    bestCell = cell1;
    int bestX = d;
    for ( int x = d + 1 ; x <= target.right(); x++ )
    {
      cell2 = cellAt( x, key1 );

      checkCellContent(cell2, bestCell, status);
      if (status == 1)
        continue;
      else if (status == 2)
      {
        // empty cells are always shifted to the end
        bestCell = cell2;
        bestX = x;
        continue;
      }

      if ( firstKey )
      {
        int i1 = firstKey->findIndex( cell2->text() );
        int i2 = firstKey->findIndex( bestCell->text() );

        if ( i1 != -1 && i2 != -1 )
        {
          if ( (order1 == Increase && i1 < i2 )
               || (order1 == Decrease && i1 > i2) )
          {
            bestCell = cell2;
            bestX = x;
            continue;
          }

          if ( i1 == i2 )
          {
            // check 2nd key
            if (key2 <= 0)
              continue;

            Cell * cell22 = cellAt( x, key2 );
            Cell * bestCell2 = cellAt( bestX, key2 );

            if ( cell22->isEmpty() )
            {
              /* No need to swap */
              continue;
            }
            else if ( cell22->isObscured() && cell22->isPartOfMerged() )
            {
              /* No need to swap */
              continue;
            }
            else if ( bestCell2->isEmpty() )
            {
              // empty cells are always shifted to the end
              bestCell = cell2;
              bestX = x;
              continue;
            }

            if ( (order2 == Increase && *cell22 < *bestCell2)
                 || (order2 == Decrease && *cell22 > *bestCell2) )
            {
              bestCell = cell2;
              bestX = x;
              continue;
            }
            else if ( (order2 == Increase && *cell22 > *bestCell2)
                      || (order2 == Decrease && *cell22 < *bestCell2) )
            {
              // already in right order
              continue;
            }
            else
            {
              // they are equal, check 3rd key
              if (key3 <= 0)
                continue;

              Cell * cell23 = cellAt( x, key3 );
              Cell * bestCell3 = cellAt( bestX, key3 );

              if ( cell23->isEmpty() )
              {
                /* No need to swap */
                continue;
              }
              else if ( cell23->isObscured() && cell23->isPartOfMerged() )
              {
                /* No need to swap */
                continue;
              }
              else if ( bestCell3->isEmpty() )
              {
                // empty cells are always shifted to the end
                bestCell = cell2;
                bestX = x;
                continue;
              }
              if ( (order3 == Increase && *cell23 < *bestCell3)
                   || (order3 == Decrease && *cell23 > *bestCell3) )
              {
                // they are really equal or in the right order
                // no swap necessary
                continue;
              }
              else
              {
                bestCell = cell2;
                bestX = x;
                continue;
              }
            }
          }
          continue;
        }
        else if ( i1 != -1 && i2 == -1 )
        {
          // if not in the key list, the cell is shifted to the end - always
          bestCell = cell2;
          bestX = x;
          continue;
       }
        else if ( i2 != -1 && i1 == -1 )
        {
          // only text of cell2 is in the list so it is smaller than bestCell
          /* No need to swap */
          continue;
        }

        // if i1 and i2 are equals -1 go on:
      } // end if (firstKey)

      // Here we use the operators < and > for cells, which do it all.
      if ( (order1 == Increase && *cell2 < *bestCell)
           || (order1 == Decrease && *cell2 > *bestCell) )
      {
        bestCell = cell2;
        bestX = x;
        continue;
      }
      else if ( (order1 == Increase && *cell2 > *bestCell)
                || (order1 == Decrease && *cell2 < *bestCell) )
      {
        // no change necessary
        continue;
      }
      else
      {
        // *cell2 equals *bestCell
        // check 2nd key
        if (key2 <= 0)
          continue;
        Cell * cell22 = cellAt( d, key2 );
        Cell * bestCell2 = cellAt( x, key2 );

        checkCellContent(cell2, bestCell, status);
        if (status == 1)
          continue;
        else if (status == 2)
        {
          // empty cells are always shifted to the end
          bestCell = cell2;
          bestX = x;
          continue;
        }

        if ( (order2 == Increase && *cell22 > *bestCell2)
             || (order2 == Decrease && *cell22 < *bestCell2) )
        {
          bestCell = cell2;
          bestX = x;
          continue;
        }
        else
        if ( (order2 == Increase && *cell22 > *bestCell2)
             || (order2 == Decrease && *cell22 < *bestCell2) )
        {
          // already in right order
          continue;
        }
        else
        {
          // they are equal, check 3rd key
          if (key3 == 0)
            continue;
          Cell * cell23 = cellAt( d, key3 );
          Cell * bestCell3 = cellAt( x, key3 );

          checkCellContent(cell2, bestCell, status);
          if (status == 1)
            continue;
          else if (status == 2)
          {
            // empty cells are always shifted to the end
            bestCell = cell2;
            bestX = x;
            continue;
          }
          if ( (order3 == Increase && *cell23 > *bestCell3)
               || (order3 == Decrease && *cell23 < *bestCell3) )
          {
            bestCell = cell2;
            bestX = x;
            continue;
          }
          else
          {
            // they are really equal
            // no swap necessary
            continue;
          }
        }
      }
    }

    // Swap columns cell1 and bestCell (i.e. d and bestX)
    if ( d != bestX )
    {
      int top = target.top();
      if (headerRow)
        ++top;

      for( int y = target.bottom(); y >= top; --y )
      {
        if ( y != key1 && y != key2 && y != key3 )
          swapCells( d, y, bestX, y, copyFormat );
      }
      if (key3 > 0)
        swapCells( d, key3, bestX, key3, copyFormat );
      if (key2 > 0)
        swapCells( d, key2, bestX, key2, copyFormat );
      swapCells( d, key1, bestX, key1, copyFormat );
    }
  } // for (d = ...; ...; ++d)
  Map::respectCase = true;
  //  doc()->emitEndOperation();
  emit sig_updateView( this );
}

void Sheet::sortByColumn( const TQRect &area, int key1, int key2, int key3,
                                 SortingOrder order1, SortingOrder order2,
                                 SortingOrder order3,
                                 TQStringList const * firstKey, bool copyFormat,
                                 bool headerRow,
                                 Point const & outputPoint, bool respectCase )
{
  TQRect r( area );
  Map::respectCase = respectCase;

  Q_ASSERT( order1 == Increase || order1 == Decrease );

  // It may not happen that entire rows are selected.
  Q_ASSERT( util_isRowSelected(r) == false );

  // Are entire columns selected ?
  if ( util_isColumnSelected(r) )
  {
    r.setTop( KS_rowMax );
    r.setBottom( 0 );

    // Determine a correct top and bottom.
    // Iterate over all cells to find out which cells are
    // located in the selected columns.
    for ( int col = r.left(); col <= r.right(); ++col )
    {
      Cell * c = getFirstCellColumn( col );
      int row;
      while ( c )
      {
        row = c->row();
        if ( !c->isEmpty() )
        {
          if ( row > r.bottom() )
            r.rBottom() = row;
          if ( row < r.top() )
            r.rTop() = row;
        }
        c = getNextCellDown( col, row );
      }
    }

    // Any cells to sort here ?
    if ( r.bottom() < r.top() )
    {
        Map::respectCase = true;
      return;
    }
  }
  TQRect target( outputPoint.pos().x(), outputPoint.pos().y(), r.width(), r.height() );

  if ( !doc()->undoLocked() )
  {
    UndoSort *undo = new UndoSort( doc(), this, target );
    doc()->addCommand( undo );
  }

  doc()->emitBeginOperation();

  if (target.topLeft() != r.topLeft())
  {
    int targetLeft = target.left();
    int targetTop  = target.top();
    int sourceTop  = r.top();
    int sourceLeft = r.left();

    key1 = key1 - sourceLeft + targetLeft;
    key2 = key2 - sourceLeft + targetLeft;
    key3 = key3 - sourceLeft + targetLeft;

    for ( int x = 0; x < r.width(); ++x)
    {
      for ( int y = 0; y < r.height(); ++y )
      {
        // from - to
        copyCells( sourceLeft + x, sourceTop + y,
                   targetLeft + x, targetTop + y, copyFormat );
      }
    }
  }

  // Sorting algorithm: David's :). Well, I guess it's called minmax or so.
  // For each row, we look for all rows under it and we find the one to swap with it.
  // Much faster than the awful bubbleSort...
  // Torben: Asymptotically it is alltogether O(n^2) :-)

  Cell * cell;
  Cell * cell1;
  Cell * cell2;
  Cell * bestCell;
  int status = 0;

  int d = target.top();

  if (headerRow)
    ++d;

  for ( ; d <= target.bottom(); ++d )
  {
    // Look for which row we want to swap with the one number d
    cell1 = cellAt( key1, d );
    if ( cell1->isObscured() && cell1->isPartOfMerged() )
    {
      Cell* obscuring = cell1->obscuringCells().first();
      cell  = cellAt( key1, obscuring->row() );
      cell1 = cellAt( key1, obscuring->row() + cell->extraYCells() + 1 );
      d     = obscuring->row() + cell->extraYCells() + 1;
    }

    bestCell  = cell1;
    int bestY = d;

    for ( int y = d + 1 ; y <= target.bottom(); ++y )
    {
      cell2 = cellAt( key1, y );

      if ( cell2->isEmpty() )
      {
        /* No need to swap */
        continue;
      }
      else if ( cell2->isObscured() && cell2->isPartOfMerged() )
      {
        /* No need to swap */
        continue;
      }
      else if ( bestCell->isEmpty() )
      {
        // empty cells are always shifted to the end
        bestCell = cell2;
        bestY = y;
        continue;
      }

      if ( firstKey )
      {
        int i1 = firstKey->findIndex( cell2->text() );
        int i2 = firstKey->findIndex( bestCell->text() );

        if ( i1 != -1 && i2 != -1 )
        {
          if ( (order1 == Increase && i1 < i2 )
               || (order1 == Decrease && i1 > i2) )
          {
            bestCell = cell2;
            bestY = y;
            continue;
          }

          if ( i1 == i2 )
          {
            // check 2nd key
            if (key2 <= 0)
              continue;
            Cell * cell22 = cellAt( key2, d );
            Cell * bestCell2 = cellAt( key2, y );

            if ( cell22->isEmpty() )
            {
              /* No need to swap */
              continue;
            }
            else if ( cell22->isObscured() && cell22->isPartOfMerged() )
            {
              /* No need to swap */
              continue;
            }
            else if ( bestCell2->isEmpty() )
            {
              // empty cells are always shifted to the end
              bestCell = cell2;
              bestY = y;
              continue;
            }

            if ( (order2 == Increase && *cell22 > *bestCell2)
                 || (order2 == Decrease && *cell22 < *bestCell2) )
            {
              bestCell = cell2;
              bestY = y;
              continue;
            }
            else if ( (order2 == Increase && *cell22 < *bestCell2)
                      || (order2 == Decrease && *cell22 > *bestCell2) )
            {
              // already in right order
              continue;
            }
            else
            {
              // they are equal, check 3rd key
              if (key3 <= 0)
                continue;
              Cell * cell23 = cellAt( key3, d );
              Cell * bestCell3 = cellAt( key3, y );

              checkCellContent(cell2, bestCell, status);
              if (status == 1)
                continue;
              else if (status == 2)
              {
                // empty cells are always shifted to the end
                bestCell = cell2;
                bestY = y;
                continue;
              }

              if ( (order3 == Increase && *cell23 < *bestCell3)
                   || (order3 == Decrease && *cell23 > *bestCell3) )
              {
                bestCell = cell2;
                bestY = y;
                continue;
              }
              else
              {
                // they are really equal or in the correct order
                // no swap necessary
                continue;
              }
            }
          }
          continue;
        }
        else if ( i1 != -1 && i2 == -1 )
        {
          // if not in the key list, the cell is shifted to the end - always
          bestCell = cell2;
          bestY = y;
          continue;
        }
        else if ( i2 != -1 && i1 == -1 )
        {
          // only text of cell2 is in the list so it is smaller than bestCell
          /* No need to swap */
          continue;
        }

        // if i1 and i2 are equals -1 go on:
      } // if (firstKey)


        // Here we use the operators < and > for cells, which do it all.
      if ( (order1 == Increase && *cell2 < *bestCell)
           || (order1 == Decrease && *cell2 > *bestCell) )
      {
        bestCell = cell2;
        bestY = y;
      }
      else if ( (order1 == Increase && *cell2 > *bestCell)
                || (order1 == Decrease && *cell2 < *bestCell) )
      {
        // no change necessary
        continue;
      }
      else
      {
        // *cell2 equals *bestCell
        // check 2nd key
        if (key2 == 0)
          continue;
        Cell * cell22 = cellAt( key2, y );
        Cell * bestCell2 = cellAt( key2, bestY );

        if ( cell22->isEmpty() )
        {
          /* No need to swap */
          continue;
        }
        else if ( cell22->isObscured() && cell22->isPartOfMerged() )
        {
          /* No need to swap */
          continue;
        }
        else if ( bestCell2->isEmpty() )
        {
          // empty cells are always shifted to the end
          bestCell = cell2;
          bestY = y;
          continue;
        }

        if ( (order2 == Increase && *cell22 < *bestCell2)
             || (order2 == Decrease && *cell22 > *bestCell2) )
        {
          bestCell = cell2;
          bestY = y;
          continue;
        }
        else if ( (order2 == Increase && *cell22 > *bestCell2)
                  || (order2 == Decrease && *cell22 < *bestCell2) )
        {
          continue;
        }
        else
        {
          // they are equal, check 3rd key
          if (key3 == 0)
            continue;
          Cell * cell23 = cellAt( key3, y );
          Cell * bestCell3 = cellAt( key3, bestY );

          if ( cell23->isEmpty() )
          {
            /* No need to swap */
            continue;
          }
          else if ( cell23->isObscured() && cell23->isPartOfMerged() )
          {
            /* No need to swap */
            continue;
          }
          else if ( bestCell3->isEmpty() )
          {
            // empty cells are always shifted to the end
            bestCell = cell2;
            bestY = y;
            continue;
          }

          if ( (order3 == Increase && *cell23 < *bestCell3)
               || (order3 == Decrease && *cell23 > *bestCell3) )
          {
            bestCell = cell2;
            bestY = y;
            continue;
          }
          else
          {
            // they are really equal or already in the correct order
            // no swap necessary
            continue;
          }
        }
      }
    }

    // Swap rows cell1 and bestCell (i.e. d and bestY)
    if ( d != bestY )
    {
      for (int x = target.left(); x <= target.right(); ++x)
      {
        if ( x != key1 && x != key2 && x != key3)
          swapCells( x, d, x, bestY, copyFormat );
      }
      if (key3 > 0)
        swapCells( key3, d, key3, bestY, copyFormat );
      if (key2 > 0)
        swapCells( key2, d, key2, bestY, copyFormat );
      swapCells( key1, d, key1, bestY, copyFormat );
    }
  } // for (d = ...; ...; ++d)
  // doc()->emitEndOperation();
  Map::respectCase = true;
  emit sig_updateView( this );
}

// from - to - copyFormat
void Sheet::copyCells( int x1, int y1, int x2, int y2, bool cpFormat )
{
  Cell * sourceCell = cellAt( x1, y1 );
  Cell * targetCell = cellAt( x2, y2 );

  if ( sourceCell->isDefault() && targetCell->isDefault())
  {
    // if the source and target is default there is nothing to copy
    return;
  }

  targetCell = nonDefaultCell(x2, y2);

  // TODO: check if this enough
  targetCell->copyContent( sourceCell );

  /*
    if ( !sourceCell->isFormula() )
    {
    targetCell->copyContent( sourceCell );
    }
    else
    {
    targetCell->setCellText( targetCell->decodeFormula( sourceCell->encodeFormula() ) );
    targetCell->setCalcDirtyFlag();
    targetCell->calc(false);
  }
  */

  if (cpFormat)
  {
    targetCell->copyFormat( sourceCell );
    /*
    targetCell->setAlign( sourceCell->format()->align( x1, y1 ) );
    targetCell->setAlignY( sourceCell->format()->alignY( x1, y1 ) );
    targetCell->setTextFont( sourceCell->format()->textFont( x1, y1 ) );
    targetCell->setTextColor( sourceCell->textColor( x1, y1 ) );
    targetCell->setBgColor( sourceCell->bgColor( x1, y1 ) );
    targetCell->setLeftBorderPen( sourceCell->leftBorderPen( x1, y1 ) );
    targetCell->setTopBorderPen( sourceCell->topBorderPen( x1, y1 ) );
    targetCell->setBottomBorderPen( sourceCell->bottomBorderPen( x1, y1 ) );
    targetCell->setRightBorderPen( sourceCell->rightBorderPen( x1, y1 ) );
    targetCell->setFallDiagonalPen( sourceCell->fallDiagonalPen( x1, y1 ) );
    targetCell->setGoUpDiagonalPen( sourceCell->goUpDiagonalPen( x1, y1 ) );
    targetCell->setBackGroundBrush( sourceCell->backGroundBrush( x1, y1 ) );
    targetCell->setPrecision( sourceCell->precision( x1, y1 ) );
    targetCell->format()->setPrefix( sourceCell->prefix( x1, y1 ) );
    targetCell->format()->setPostfix( sourceCell->postfix( x1, y1 ) );
    targetCell->setFloatFormat( sourceCell->floatFormat( x1, y1 ) );
    targetCell->setFloatColor( sourceCell->floatColor( x1, y1 ) );
    targetCell->setMultiRow( sourceCell->multiRow( x1, y1 ) );
    targetCell->setVerticalText( sourceCell->verticalText( x1, y1 ) );
    targetCell->setStyle( sourceCell->style() );
    targetCell->setDontPrintText( sourceCell->getDontprintText( x1, y1 ) );
    targetCell->setIndent( sourceCell->getIndent( x1, y1 ) );
    targetCell->SetConditionList(sourceCell->GetConditionList());
    targetCell->setComment( sourceCell->comment( x1, y1 ) );
    targetCell->setAngle( sourceCell->getAngle( x1, y1 ) );
    targetCell->setFormatType( sourceCell->getFormatType( x1, y1 ) );
    */
  }
}

void Sheet::swapCells( int x1, int y1, int x2, int y2, bool cpFormat )
{
  Cell * ref1 = cellAt( x1, y1 );
  Cell * ref2 = cellAt( x2, y2 );

  if ( ref1->isDefault() )
  {
    if ( !ref2->isDefault() )
    {
      ref1 = nonDefaultCell( x1, y1 );
      // TODO : make ref2 default instead of copying a default cell into it
    }
    else
      return; // nothing to do
  }
  else
    if ( ref2->isDefault() )
    {
      ref2 = nonDefaultCell( x2, y2 );
      // TODO : make ref1 default instead of copying a default cell into it
    }

  // Dummy cell used for swapping cells.
  // In fact we copy only content and no layout
  // information. Imagine sorting in a sheet. Swapping
  // the format while sorting is not what you would expect
  // as a user.
  if (!ref1->isFormula() && !ref2->isFormula())
  {
    Cell *tmp = new Cell( this, -1, -1 );

    tmp->copyContent( ref1 );
    ref1->copyContent( ref2 );
    ref2->copyContent( tmp );

    delete tmp;
  }
  else
    if ( ref1->isFormula() && ref2->isFormula() )
    {
      TQString d = ref1->encodeFormula();
      ref1->setCellText( ref1->decodeFormula( ref2->encodeFormula( ) ) );
      ref1->setCalcDirtyFlag();
      ref1->calc(false);
      ref2->setCellText( ref2->decodeFormula( d ) );
      ref2->setCalcDirtyFlag();
      ref2->calc(false);
    }
    else
      if (ref1->isFormula() && !ref2->isFormula() )
      {
        TQString d = ref1->encodeFormula();
        ref1->setCellText(ref2->text());
        ref2->setCellText(ref2->decodeFormula(d));
        ref2->setCalcDirtyFlag();
        ref2->calc(false);
      }
      else
        if (!ref1->isFormula() && ref2->isFormula() )
        {
          TQString d = ref2->encodeFormula();
          ref2->setCellText(ref1->text());
          ref1->setCellText(ref1->decodeFormula(d));
          ref1->setCalcDirtyFlag();
          ref1->calc(false);
        }

  if (cpFormat)
  {
    Format::Align a = ref1->format()->align( ref1->column(), ref1->row() );
    ref1->format()->setAlign( ref2->format()->align( ref2->column(), ref2->row() ) );
    ref2->format()->setAlign(a);

    Format::AlignY ay = ref1->format()->alignY( ref1->column(), ref1->row() );
    ref1->format()->setAlignY( ref2->format()->alignY( ref2->column(), ref2->row() ) );
    ref2->format()->setAlignY(ay);

    TQFont textFont = ref1->format()->textFont( ref1->column(), ref1->row() );
    ref1->format()->setTextFont( ref2->format()->textFont( ref2->column(), ref2->row() ) );
    ref2->format()->setTextFont(textFont);

    TQColor textColor = ref1->format()->textColor( ref1->column(), ref1->row() );
    ref1->format()->setTextColor( ref2->format()->textColor( ref2->column(), ref2->row() ) );
    ref2->format()->setTextColor(textColor);

    TQColor bgColor = ref1->bgColor( ref1->column(), ref1->row() );
    ref1->format()->setBgColor( ref2->bgColor( ref2->column(), ref2->row() ) );
    ref2->format()->setBgColor(bgColor);

    TQPen lbp = ref1->leftBorderPen( ref1->column(), ref1->row() );
    ref1->setLeftBorderPen( ref2->leftBorderPen( ref2->column(), ref2->row() ) );
    ref2->setLeftBorderPen(lbp);

    TQPen tbp = ref1->topBorderPen( ref1->column(), ref1->row() );
    ref1->setTopBorderPen( ref2->topBorderPen( ref2->column(), ref2->row() ) );
    ref2->setTopBorderPen(tbp);

    TQPen bbp = ref1->bottomBorderPen( ref1->column(), ref1->row() );
    ref1->setBottomBorderPen( ref2->bottomBorderPen( ref2->column(), ref2->row() ) );
    ref2->setBottomBorderPen(bbp);

    TQPen rbp = ref1->rightBorderPen( ref1->column(), ref1->row() );
    ref1->setRightBorderPen( ref2->rightBorderPen( ref2->column(), ref2->row() ) );
    ref2->setRightBorderPen(rbp);

    TQPen fdp = ref1->format()->fallDiagonalPen( ref1->column(), ref1->row() );
    ref1->format()->setFallDiagonalPen( ref2->format()->fallDiagonalPen( ref2->column(), ref2->row() ) );
    ref2->format()->setFallDiagonalPen(fdp);

    TQPen udp = ref1->format()->goUpDiagonalPen( ref1->column(), ref1->row() );
    ref1->format()->setGoUpDiagonalPen( ref2->format()->goUpDiagonalPen( ref2->column(), ref2->row() ) );
    ref2->format()->setGoUpDiagonalPen(udp);

    TQBrush bgBrush = ref1->backGroundBrush( ref1->column(), ref1->row() );
    ref1->format()->setBackGroundBrush( ref2->backGroundBrush( ref2->column(), ref2->row() ) );
    ref2->format()->setBackGroundBrush(bgBrush);

    int pre = ref1->format()->precision( ref1->column(), ref1->row() );
    ref1->format()->setPrecision( ref2->format()->precision( ref2->column(), ref2->row() ) );
    ref2->format()->setPrecision(pre);

    TQString prefix = ref1->format()->prefix( ref1->column(), ref1->row() );
    ref1->format()->setPrefix( ref2->format()->prefix( ref2->column(), ref2->row() ) );
    ref2->format()->setPrefix(prefix);

    TQString postfix = ref1->format()->postfix( ref1->column(), ref1->row() );
    ref1->format()->setPostfix( ref2->format()->postfix( ref2->column(), ref2->row() ) );
    ref2->format()->setPostfix(postfix);

    Format::FloatFormat f = ref1->format()->floatFormat( ref1->column(), ref1->row() );
    ref1->format()->setFloatFormat( ref2->format()->floatFormat( ref2->column(), ref2->row() ) );
    ref2->format()->setFloatFormat(f);

    Format::FloatColor c = ref1->format()->floatColor( ref1->column(), ref1->row() );
    ref1->format()->setFloatColor( ref2->format()->floatColor( ref2->column(), ref2->row() ) );
    ref2->format()->setFloatColor(c);

    bool multi = ref1->format()->multiRow( ref1->column(), ref1->row() );
    ref1->format()->setMultiRow( ref2->format()->multiRow( ref2->column(), ref2->row() ) );
    ref2->format()->setMultiRow(multi);

    bool vert = ref1->format()->verticalText( ref1->column(), ref1->row() );
    ref1->format()->setVerticalText( ref2->format()->verticalText( ref2->column(), ref2->row() ) );
    ref2->format()->setVerticalText(vert);

    bool print = ref1->format()->getDontprintText( ref1->column(), ref1->row() );
    ref1->format()->setDontPrintText( ref2->format()->getDontprintText( ref2->column(), ref2->row() ) );
    ref2->format()->setDontPrintText(print);

    double ind = ref1->format()->getIndent( ref1->column(), ref1->row() );
    ref1->format()->setIndent( ref2->format()->getIndent( ref2->column(), ref2->row() ) );
    ref2->format()->setIndent( ind );

    TQValueList<Conditional> conditionList = ref1->conditionList();
    ref1->setConditionList(ref2->conditionList());
    ref2->setConditionList(conditionList);

    TQString com = ref1->format()->comment( ref1->column(), ref1->row() );
    ref1->format()->setComment( ref2->format()->comment( ref2->column(), ref2->row() ) );
    ref2->format()->setComment(com);

    int angle = ref1->format()->getAngle( ref1->column(), ref1->row() );
    ref1->format()->setAngle( ref2->format()->getAngle( ref2->column(), ref2->row() ) );
    ref2->format()->setAngle(angle);

    FormatType form = ref1->format()->getFormatType( ref1->column(), ref1->row() );
    ref1->format()->setFormatType( ref2->format()->getFormatType( ref2->column(), ref2->row() ) );
    ref2->format()->setFormatType(form);
  }
}

void Sheet::refreshPreference()
{
  if ( getAutoCalc() )
    recalc();

  emit sig_updateHBorder( this );
  emit sig_updateView( this );
}


bool Sheet::areaIsEmpty(const Region& region, TestType _type)
{
  Region::ConstIterator endOfList = region.constEnd();
  for (Region::ConstIterator it = region.constBegin(); it != endOfList; ++it)
  {
    TQRect range = (*it)->rect().normalize();
    // Complete rows selected ?
    if ((*it)->isRow())
    {
        for ( int row = range.top(); row <= range.bottom(); ++row )
        {
            Cell * c = getFirstCellRow( row );
            while ( c )
            {
                if ( !c->isPartOfMerged())
                {
                    switch( _type )
                    {
                    case Text :
                        if ( !c->text().isEmpty())
                            return false;
                        break;
                    case Validity:
                        if ( c->getValidity(0))
                            return false;
                        break;
                    case Comment:
                        if ( !c->format()->comment(c->column(), row).isEmpty())
                            return false;
                        break;
                    case ConditionalCellAttribute:
                        if ( c->conditionList().count()> 0)
                            return false;
                        break;
                    }
                }

                c = getNextCellRight( c->column(), row );
            }
        }
    }
    // Complete columns selected ?
    else if ((*it)->isColumn())
    {
        for ( int col = range.left(); col <= range.right(); ++col )
        {
            Cell * c = getFirstCellColumn( col );
            while ( c )
            {
                if ( !c->isPartOfMerged() )
                {
                    switch( _type )
                    {
                    case Text :
                        if ( !c->text().isEmpty())
                            return false;
                        break;
                    case Validity:
                        if ( c->getValidity(0))
                            return false;
                        break;
                    case Comment:
                        if ( !c->format()->comment(col, c->row()).isEmpty())
                            return false;
                        break;
                    case ConditionalCellAttribute:
                        if ( c->conditionList().count()> 0)
                            return false;
                        break;
                    }
                }

                c = getNextCellDown( col, c->row() );
            }
        }
    }
    else
    {
        Cell * cell;

        int right  = range.right();
        int bottom = range.bottom();
        for ( int x = range.left(); x <= right; ++x )
            for ( int y = range.top(); y <= bottom; ++y )
            {
                cell = cellAt( x, y );
                if (!cell->isPartOfMerged() )
                {
                    switch( _type )
                    {
                    case Text :
                        if ( !cell->text().isEmpty())
                            return false;
                        break;
                    case Validity:
                        if ( cell->getValidity(0))
                            return false;
                        break;
                    case Comment:
                        if ( !cell->format()->comment(x, y).isEmpty())
                            return false;
                        break;
                    case ConditionalCellAttribute:
                        if ( cell->conditionList().count()> 0)
                            return false;
                        break;
                    }
                }
            }
    }
  }
  return true;
}

struct SetSelectionMultiRowWorker : public Sheet::CellWorker
{
  bool enable;
  SetSelectionMultiRowWorker( bool _enable )
    : Sheet::CellWorker( ), enable( _enable ) { }

  class UndoAction* createUndoAction( Doc * doc, Sheet * sheet, const KSpread::Region& region )
  {
    TQString title = i18n("Multirow");
    return new UndoCellFormat( doc, sheet, region, title );
  }

  bool testCondition( Cell * cell )
  {
    return ( !cell->isPartOfMerged() );
  }

  void doWork( Cell * cell, bool, int, int )
  {
    cell->setDisplayDirtyFlag();
    cell->format()->setMultiRow( enable );
    cell->format()->setVerticalText( false );
    cell->format()->setAngle( 0 );
    cell->clearDisplayDirtyFlag();
  }
};

void Sheet::setSelectionMultiRow( Selection* selectionInfo,
                                         bool enable )
{
    SetSelectionMultiRowWorker w( enable );
    workOnCells( selectionInfo, w );
}

TQString Sheet::guessColumnTitle(TQRect& area, int col)
{
  //Verify range
  Range rg;
  rg.setRange(area);
  rg.setSheet(this);

  if ( (!rg.isValid()) || (col < area.left()) || (col > area.right()))
    return TQString();

  //The current guess logic is fairly simple - if the top row of the given area
  //appears to contain headers (ie. there is text in each column) the text in the column at
  //the top row of the area is returned.

/*  for (int i=area.left();i<=area.right();i++)
  {
    Value cellValue=value(i,area.top());

    if (!cellValue.isString())
      return TQString();
  }*/

  Value cellValue=value(col,area.top());
  return cellValue.asString();
}

TQString Sheet::guessRowTitle(TQRect& area, int row)
{
  //Verify range
  Range rg;
  rg.setRange(area);
  rg.setSheet(this);

  if ( (!rg.isValid()) || (row < area.top()) || (row > area.bottom()) )
    return TQString();

  //The current guess logic is fairly simple - if the leftmost column of the given area
  //appears to contain headers (ie. there is text in each row) the text in the row at
  //the leftmost column of the area is returned.
  /*for (int i=area.top();i<=area.bottom();i++)
  {
    Value cellValue=value(area.left(),i);

    if (!cellValue.isString())
      return TQString();
  }*/

  Value cellValue=value(area.left(),row);
  return cellValue.asString();
}

void Sheet::setSelectionAlign( Selection* selectionInfo,
                               Format::Align _align )
{
  HorAlignManipulator* manipulator = new HorAlignManipulator();
  manipulator->setSheet(this);
  manipulator->setProperty(Format::PAlign);
  manipulator->setHorizontalAlignment(_align);
  manipulator->add(*selectionInfo);
  manipulator->execute();
}

void Sheet::setSelectionAlignY( Selection* selectionInfo,
                                Format::AlignY _alignY )
{
  VerAlignManipulator* manipulator = new VerAlignManipulator();
  manipulator->setSheet(this);
  manipulator->setProperty(Format::PAlignY);
  manipulator->setVerticalAlignment(_alignY);
  manipulator->add(*selectionInfo);
  manipulator->execute();
}


struct SetSelectionPrecisionWorker : public Sheet::CellWorker {
    int _delta;
    SetSelectionPrecisionWorker( int delta ) : Sheet::CellWorker( ), _delta( delta ) { }

    class UndoAction* createUndoAction( Doc* doc, Sheet* sheet, const KSpread::Region& region ) {
        TQString title=i18n("Change Precision");
  return new UndoCellFormat( doc, sheet, region, title );
    }
    bool testCondition( Cell* cell ) {
  return ( !cell->isPartOfMerged() );
    }
    void doWork( Cell* cell, bool, int, int ) {
  cell->setDisplayDirtyFlag();
  if ( _delta == 1 )
      cell->incPrecision();
  else
      cell->decPrecision();
  cell->clearDisplayDirtyFlag();
    }
};

void Sheet::setSelectionPrecision( Selection* selectionInfo,
                                          int _delta )
{
    SetSelectionPrecisionWorker w( _delta );
    workOnCells( selectionInfo, w );
}

struct SetSelectionStyleWorker : public Sheet::CellWorkerTypeA
{
  Style * m_style;
  SetSelectionStyleWorker( Style * style )
    : m_style( style )
  {
  }

  TQString getUndoTitle()
  {
    return i18n("Apply Style");
  }

  void doWork( RowFormat* rw )
  {
    rw->setStyle( m_style );
  }

  void doWork( ColumnFormat* cl )
  {
    cl->setStyle( m_style );
  }

  bool testCondition( Cell* cell )
  {
    return ( !cell->isPartOfMerged() && cell->format()->style() != m_style );
  }

  void doWork( Cell* cell, bool cellRegion, int, int )
  {
    if ( cellRegion )
      cell->setDisplayDirtyFlag();

    cell->format()->setStyle( m_style );

    if ( cellRegion )
      cell->clearDisplayDirtyFlag();
  }
};


void Sheet::setSelectionStyle( Selection * selectionInfo, Style * style )
{
    SetSelectionStyleWorker w( style );
    workOnCells( selectionInfo, w );
}

struct SetSelectionMoneyFormatWorker : public Sheet::CellWorkerTypeA
{
    bool b;
    Doc *m_pDoc;
    SetSelectionMoneyFormatWorker( bool _b,Doc* _doc ) : b( _b ), m_pDoc(_doc) { }
    TQString getUndoTitle() { return i18n("Format Money"); }
    bool testCondition( RowFormat* rw ) {
  return ( rw->hasProperty( Format::PFormatType )
     || rw->hasProperty( Format::PPrecision ) );
    }
    void doWork( RowFormat* rw ) {
  rw->setFormatType( b ? Money_format : Generic_format );
  rw->setPrecision( b ? m_pDoc->locale()->fracDigits() : 0 );
    }
    void doWork( ColumnFormat* cl ) {
  cl->setFormatType( b ? Money_format : Generic_format );
  cl->setPrecision( b ? m_pDoc->locale()->fracDigits() : 0 );
    }
    void prepareCell( Cell* c ) {
  c->format()->clearProperty( Format::PPrecision );
  c->format()->clearNoFallBackProperties( Format::PPrecision );
  c->format()->clearProperty( Format::PFormatType );
  c->format()->clearNoFallBackProperties( Format::PFormatType );
    }
    bool testCondition( Cell* cell ) {
  return ( !cell->isPartOfMerged() );
    }
    void doWork( Cell* cell, bool cellRegion, int, int ) {
  if ( cellRegion )
      cell->setDisplayDirtyFlag();
  cell->format()->setFormatType( b ? Money_format : Generic_format );
  cell->format()->setPrecision( b ?  m_pDoc->locale()->fracDigits() : 0 );
  if ( cellRegion )
      cell->clearDisplayDirtyFlag();
    }
};


void Sheet::setSelectionMoneyFormat( Selection* selectionInfo,
                                            bool b )
{
    SetSelectionMoneyFormatWorker w( b,doc() );
    workOnCells( selectionInfo, w );
}


struct IncreaseIndentWorker : public Sheet::CellWorkerTypeA {
    double   tmpIndent;
    double   valIndent;

    IncreaseIndentWorker( double _tmpIndent, double _valIndent )
  : tmpIndent( _tmpIndent ), valIndent( _valIndent ) { }

    TQString  getUndoTitle() { return i18n("Increase Indent"); }
    bool     testCondition( RowFormat* rw ) {
  return ( rw->hasProperty( Format::PIndent ) );
    }

    void doWork( RowFormat* rw ) {
  rw->setIndent( tmpIndent+valIndent );
  //rw->setAlign( Format::Left );
    }
    void doWork( ColumnFormat* cl ) {
  cl->setIndent( tmpIndent+valIndent );
  //cl->setAlign( Format::Left );
    }
    void prepareCell( Cell* c ) {
  c->format()->clearProperty( Format::PIndent );
  c->format()->clearNoFallBackProperties( Format::PIndent );
  //c->format()->clearProperty( Format::PAlign );
  //c->format()->clearNoFallBackProperties( Format::PAlign );
    }
    bool testCondition( Cell* cell ) {
  return ( !cell->isPartOfMerged() );
    }
    void doWork( Cell* cell, bool cellRegion, int x, int y ) {
  if ( cellRegion ) {
      if(cell->format()->align(x,y)!=Format::Left)
      {
    //cell->setAlign(Format::Left);
    //cell->format()->setIndent( 0.0 );
      }
      cell->setDisplayDirtyFlag();
      cell->format()->setIndent( /* ### ??? --> */ cell->format()->getIndent(x,y) /* <-- */ +valIndent );
      cell->clearDisplayDirtyFlag();
  } else {
      cell->format()->setIndent( tmpIndent+valIndent);
      //cell->setAlign( Format::Left);
  }
    }
};


void Sheet::increaseIndent(Selection* selectionInfo)
{
    TQPoint       marker(selectionInfo->marker());
    double       valIndent = doc()->getIndentValue();
    Cell *c         = cellAt( marker );
    double       tmpIndent = c->format()->getIndent( marker.x(), marker.y() );

    IncreaseIndentWorker  w( tmpIndent, valIndent );
    workOnCells( selectionInfo, w );
}


struct DecreaseIndentWorker : public Sheet::CellWorkerTypeA {
    double tmpIndent, valIndent;
    DecreaseIndentWorker( double _tmpIndent, double _valIndent ) : tmpIndent( _tmpIndent ), valIndent( _valIndent ) { }
    TQString getUndoTitle() { return i18n("Decrease Indent"); }
    bool testCondition( RowFormat* rw ) {
  return ( rw->hasProperty( Format::PIndent ) );
    }
    void doWork( RowFormat* rw ) {
        rw->setIndent( TQMAX( 0.0, tmpIndent - valIndent ) );
    }
    void doWork( ColumnFormat* cl ) {
        cl->setIndent( TQMAX( 0.0, tmpIndent - valIndent ) );
    }
    void prepareCell( Cell* c ) {
  c->format()->clearProperty( Format::PIndent );
  c->format()->clearNoFallBackProperties( Format::PIndent );
    }
    bool testCondition( Cell* cell ) {
  return ( !cell->isPartOfMerged() );
    }
    void doWork( Cell* cell, bool cellRegion, int x, int y ) {
  if ( cellRegion ) {
      cell->setDisplayDirtyFlag();
      cell->format()->setIndent( TQMAX( 0.0, cell->format()->getIndent( x, y ) - valIndent ) );
      cell->clearDisplayDirtyFlag();
  } else {
      cell->format()->setIndent( TQMAX( 0.0, tmpIndent - valIndent ) );
  }
    }
};


void Sheet::decreaseIndent( Selection* selectionInfo )
{
    double valIndent = doc()->getIndentValue();
    TQPoint marker(selectionInfo->marker());
    Cell* c = cellAt( marker );
    double tmpIndent = c->format()->getIndent( marker.x(), marker.y() );

    DecreaseIndentWorker w( tmpIndent, valIndent );
    workOnCells( selectionInfo, w );
}


int Sheet::adjustColumnHelper( Cell * c, int _col, int _row )
{
    double long_max = 0.0;
    c->calculateTextParameters( painter(), _col, _row );
    if ( c->textWidth() > long_max )
    {
        double indent = 0.0;
        int a = c->format()->align( c->column(), c->row() );
        if ( a == Format::Undefined )
        {
            if ( c->value().isNumber() || c->isDate() || c->isTime())
                a = Format::Right;
            else
                a = Format::Left;
        }

        if ( a == Format::Left )
            indent = c->format()->getIndent( c->column(), c->row() );
        long_max = indent + c->textWidth()
            + c->format()->leftBorderWidth( c->column(), c->row() )
            + c->format()->rightBorderWidth( c->column(), c->row() );
    }
    return (int)long_max;
}

void Sheet::adjustArea(const Region& region)
{
  AdjustColumnRowManipulator* manipulator = new AdjustColumnRowManipulator();
  manipulator->setSheet(this);
  manipulator->setAdjustColumn(true);
  manipulator->setAdjustRow(true);
  manipulator->add(region);
  manipulator->execute();
}

void Sheet::adjustColumn(const Region& region)
{
  AdjustColumnRowManipulator* manipulator = new AdjustColumnRowManipulator();
  manipulator->setSheet(this);
  manipulator->setAdjustColumn(true);
  manipulator->add(region);
  manipulator->execute();
}

void Sheet::adjustRow(const Region& region)
{
  AdjustColumnRowManipulator* manipulator = new AdjustColumnRowManipulator();
  manipulator->setSheet(this);
  manipulator->setAdjustRow(true);
  manipulator->add(region);
  manipulator->execute();
}

struct ClearTextSelectionWorker : public Sheet::CellWorker {
    Sheet   * _s;

    ClearTextSelectionWorker(  Sheet * s )
      : Sheet::CellWorker( ),  _s( s ) { }

    class UndoAction* createUndoAction( Doc* doc, Sheet* sheet, const KSpread::Region& region ) {
  return new UndoChangeAreaTextCell( doc, sheet, region );
    }
    bool testCondition( Cell* cell ) {
  return ( !cell->isObscured() );
    }
    void doWork( Cell* cell, bool, int, int )
    {
      cell->setCellText( "" );
    }
};

void Sheet::clearTextSelection( Selection* selectionInfo )
{
  if (areaIsEmpty(*selectionInfo))
    return;

  ClearTextSelectionWorker w( this );
  workOnCells( selectionInfo, w );
}


struct ClearValiditySelectionWorker : public Sheet::CellWorker {
    ClearValiditySelectionWorker( ) : Sheet::CellWorker( ) { }

    class UndoAction* createUndoAction( Doc* doc, Sheet* sheet, const KSpread::Region& region ) {
  return new UndoConditional( doc, sheet, region );
    }
    bool testCondition( Cell* cell ) {
  return ( !cell->isObscured() );
    }
    void doWork( Cell* cell, bool, int, int ) {
  cell->removeValidity();
    }
};

void Sheet::clearValiditySelection( Selection* selectionInfo )
{
  if (areaIsEmpty(*selectionInfo, Validity))
    return;

  ClearValiditySelectionWorker w;
  workOnCells( selectionInfo, w );
}


struct ClearConditionalSelectionWorker : public Sheet::CellWorker
{
  ClearConditionalSelectionWorker( ) : Sheet::CellWorker( ) { }

  class UndoAction* createUndoAction( Doc* doc,
               Sheet* sheet,
               const KSpread::Region& region )
  {
    return new UndoConditional( doc, sheet, region );
  }
  bool testCondition( Cell* cell )
  {
    return ( !cell->isObscured() );
  }
  void doWork( Cell* cell, bool, int, int )
  {
    TQValueList<Conditional> emptyList;
    cell->setConditionList(emptyList);
  }
};

void Sheet::clearConditionalSelection( Selection* selectionInfo )
{
  ClearConditionalSelectionWorker w;
  workOnCells( selectionInfo, w );
}

void Sheet::fillSelection( Selection * selectionInfo, int direction )
{
  TQRect rct( selectionInfo->selection() );
  int right  = rct.right();
  int bottom = rct.bottom();
  int left   = rct.left();
  int top    = rct.top();
  int width  = rct.width();
  int height = rct.height();

  TQDomDocument undoDoc = saveCellRegion( rct );
  loadSelectionUndo( undoDoc, rct, left - 1, top - 1, false, 0 );

  TQDomDocument doc;

  switch( direction )
  {
   case Right:
    doc = saveCellRegion( TQRect( left, top, 1, height ) );
    break;

   case Up:
    doc = saveCellRegion( TQRect( left, bottom, width, 1 ) );
    break;

   case Left:
    doc = saveCellRegion( TQRect( right, top, 1, height ) );
    break;

   case Down:
    doc = saveCellRegion( TQRect( left, top, width, 1 ) );
    break;
  };

  // Save to buffer
  TQBuffer buffer;
  buffer.open( IO_WriteOnly );
  TQTextStream str( &buffer );
  str.setEncoding( TQTextStream::UnicodeUTF8 );
  str << doc;
  buffer.close();

  int i;
  switch( direction )
  {
   case Right:
    for ( i = left + 1; i <= right; ++i )
    {
      paste( buffer.buffer(), TQRect( i, top, 1, 1 ), false );
    }
    break;

   case Up:
    for ( i = bottom + 1; i >= top; --i )
    {
      paste( buffer.buffer(), TQRect( left, i, 1, 1 ), false );
    }
    break;

   case Left:
    for ( i = right - 1; i >= left; --i )
    {
      paste( buffer.buffer(), TQRect( i, top, 1, 1 ), false );
    }
    break;

   case Down:
    for ( i = top + 1; i <= bottom; ++i )
    {
      paste( buffer.buffer(), TQRect( left, i, 1, 1 ), false );
    }
    break;
  }

  this->doc()->setModified( true );
}


struct DefaultSelectionWorker : public Sheet::CellWorker {
    DefaultSelectionWorker( ) : Sheet::CellWorker( true, false, true ) { }

    class UndoAction* createUndoAction( Doc* doc, Sheet* sheet, const KSpread::Region& region ) {
        TQString title=i18n("Default Parameters");
  return new UndoCellFormat( doc, sheet, region, title );
    }
    bool testCondition( Cell* ) {
  return true;
    }
    void doWork( Cell* cell, bool, int, int ) {
  cell->defaultStyle();
    }
};

void Sheet::defaultSelection( Selection* selectionInfo )
{
  TQRect selection(selectionInfo->selection());
  DefaultSelectionWorker w;
  SelectionType st = workOnCells( selectionInfo, w );
  switch ( st ) {
  case CompleteRows:
    RowFormat *rw;
    for ( int i = selection.top(); i <= selection.bottom(); i++ ) {
      rw = nonDefaultRowFormat( i );
      rw->defaultStyleFormat();
    }
    emit sig_updateView( this, *selectionInfo );
    return;
  case CompleteColumns:
    ColumnFormat *cl;
    for ( int i = selection.left(); i <= selection.right(); i++ ) {
      cl=nonDefaultColumnFormat( i );
      cl->defaultStyleFormat();
    }
    emit sig_updateView( this, *selectionInfo );
    return;
  case CellRegion:
    emit sig_updateView( this, *selectionInfo );
      return;
  }
}


struct SetConditionalWorker : public Sheet::CellWorker
{
  TQValueList<Conditional> conditionList;
  SetConditionalWorker( TQValueList<Conditional> _tmp ) :
    Sheet::CellWorker( ), conditionList( _tmp ) { }

  class UndoAction* createUndoAction( Doc* doc,
                                      Sheet* sheet, const KSpread::Region& region )
  {
    return new UndoConditional( doc, sheet, region );
  }

  bool testCondition( Cell* )
  {
    return true;
  }

  void doWork( Cell* cell, bool, int, int )
  {
    if ( !cell->isObscured() ) // TODO: isPartOfMerged()???
    {
      cell->setConditionList(conditionList);
      cell->setDisplayDirtyFlag();
    }
  }
};

void Sheet::setConditional( Selection* selectionInfo,
                                   TQValueList<Conditional> const & newConditions)
{
  if ( !doc()->undoLocked() )
  {
    UndoConditional * undo = new UndoConditional(doc(), this, *selectionInfo);
    doc()->addCommand( undo );
  }

  Region::ConstIterator endOfList = selectionInfo->constEnd();
  for (Region::ConstIterator it = selectionInfo->constBegin(); it != endOfList; ++it)
  {
    TQRect range = (*it)->rect().normalize();

    int l = range.left();
    int r = range.right();
    int t = range.top();
    int b = range.bottom();

    Cell * cell;
    Style * s = doc()->styleManager()->defaultStyle();
    for (int x = l; x <= r; ++x)
    {
      for (int y = t; y <= b; ++y)
      {
        cell = nonDefaultCell( x, y, false, s );
        cell->setConditionList( newConditions );
        cell->setDisplayDirtyFlag();
      }
    }
  }

  emit sig_updateView( this, *selectionInfo );
}


struct SetValidityWorker : public Sheet::CellWorker {
    Validity tmp;
    SetValidityWorker( Validity _tmp ) : Sheet::CellWorker( ), tmp( _tmp ) { }

    class UndoAction* createUndoAction( Doc* doc, Sheet* sheet, const KSpread::Region& region ) {
  return new UndoConditional( doc, sheet, region );
    }
    bool testCondition( Cell* ) {
        return true;
    }
    void doWork( Cell* cell, bool, int, int ) {
  if ( !cell->isObscured() ) {
      cell->setDisplayDirtyFlag();
      if ( tmp.m_restriction==Restriction::None )
    cell->removeValidity();
      else
      {
    Validity *tmpValidity = cell->getValidity();
    tmpValidity->message=tmp.message;
    tmpValidity->title=tmp.title;
    tmpValidity->valMin=tmp.valMin;
    tmpValidity->valMax=tmp.valMax;
    tmpValidity->m_cond=tmp.m_cond;
    tmpValidity->m_action=tmp.m_action;
    tmpValidity->m_restriction=tmp.m_restriction;
    tmpValidity->timeMin=tmp.timeMin;
    tmpValidity->timeMax=tmp.timeMax;
    tmpValidity->dateMin=tmp.dateMin;
    tmpValidity->dateMax=tmp.dateMax;
                tmpValidity->displayMessage=tmp.displayMessage;
                tmpValidity->allowEmptyCell=tmp.allowEmptyCell;
                tmpValidity->displayValidationInformation=tmp.displayValidationInformation;
                tmpValidity->titleInfo=tmp.titleInfo;
                tmpValidity->messageInfo=tmp.messageInfo;
                tmpValidity->listValidity=tmp.listValidity;
      }
      cell->clearDisplayDirtyFlag();
  }
    }
};

void Sheet::setValidity(Selection* selectionInfo,
                        KSpread::Validity tmp )
{
    SetValidityWorker w( tmp );
    workOnCells( selectionInfo, w );
}


struct GetWordSpellingWorker : public Sheet::CellWorker {
    TQString& listWord;
    GetWordSpellingWorker( TQString& _listWord ) : Sheet::CellWorker( false, false, true ), listWord( _listWord ) { }

    class UndoAction* createUndoAction( Doc*, Sheet*, const KSpread::Region& ) {
  return 0;
    }
    bool testCondition( Cell* ) {
        return true;
    }
    void doWork( Cell* c, bool cellRegion, int, int ) {
  if ( !c->isObscured() || cellRegion /* ### ??? */ ) {
      if ( !c->isFormula() && !c->value().isNumber() && !c->value().asString().isEmpty() && !c->isTime()
     && !c->isDate()
     && !c->text().isEmpty())
      {
    listWord+=c->text()+'\n';
      }
  }
    }
};

TQString Sheet::getWordSpelling(Selection* selectionInfo )
{
    TQString listWord;
    GetWordSpellingWorker w( listWord );
    workOnCells( selectionInfo, w );
    return listWord;
}


struct SetWordSpellingWorker : public Sheet::CellWorker {
    TQStringList& list;
    int pos;
    Sheet   * sheet;
    SetWordSpellingWorker( TQStringList & _list,Sheet * s )
      : Sheet::CellWorker( false, false, true ), list( _list ), pos( 0 ),  sheet( s ) { }

    class UndoAction* createUndoAction( Doc* doc, Sheet* sheet, const KSpread::Region& region ) {
  return new UndoChangeAreaTextCell( doc, sheet, region );
    }
    bool testCondition( Cell* ) {
        return true;
    }
    void doWork( Cell* c, bool cellRegion, int, int )
    {
  if ( !c->isObscured() || cellRegion /* ### ??? */ ) {
      if ( !c->isFormula() && !c->value().isNumber() && !c->value().asString().isEmpty() && !c->isTime()
     && !c->isDate()
     && !c->text().isEmpty())
      {


    c->setCellText( list[pos] );
    pos++;
      }
  }
    }
};

void Sheet::setWordSpelling(Selection* selectionInfo,
                                   const TQString _listWord )
{
    TQStringList list = TQStringList::split ( '\n', _listWord );
    SetWordSpellingWorker w( list,  this );
    workOnCells( selectionInfo, w );
}

static TQString cellAsText( Cell* cell, unsigned int max )
{
  TQString result;
  if( !cell->isDefault() )
  {
    int l = max - cell->strOutText().length();
    if (cell->defineAlignX() == Format::Right )
    {
        for ( int i = 0; i < l; ++i )
          result += " ";
        result += cell->strOutText();
    }
    else if (cell->defineAlignX() == Format::Left )
      {
          result += " ";
          result += cell->strOutText();
          // start with "1" because we already set one space
          for ( int i = 1; i < l; ++i )
            result += " ";
       }
         else // centered
         {
           int i;
           int s = (int) l / 2;
           for ( i = 0; i < s; ++i )
             result += " ";
           result += cell->strOutText();
           for ( i = s; i < l; ++i )
             result += " ";
          }
  }
  else
  {
    for ( unsigned int i = 0; i < max; ++i )
      result += " ";
  }

  return result;
}

TQString Sheet::copyAsText( Selection* selectionInfo )
{
    // Only one cell selected? => copy active cell
    if ( selectionInfo->isSingular() )
    {
        Cell * cell = cellAt( selectionInfo->marker() );
        if( !cell->isDefault() )
          return cell->strOutText();
        return "";
    }

    TQRect selection(selectionInfo->selection());

    // Find area
    unsigned top = selection.bottom();
    unsigned bottom = selection.top();
    unsigned left = selection.right();
    unsigned right = selection.left();

    unsigned max = 1;
    for( Cell *c = d->cells.firstCell();c; c = c->nextCell() )
    {
      if ( !c->isDefault() )
      {
        TQPoint p( c->column(), c->row() );
        if ( selection.contains( p ) )
        {
          top = TQMIN( top, (unsigned) c->row() );
          left = TQMIN( left, (unsigned) c->column() );
          bottom = TQMAX( bottom, (unsigned) c->row() );
          right = TQMAX( right, (unsigned) c->column() );

          if ( c->strOutText().length() > max )
                 max = c->strOutText().length();
        }
      }
    }

    ++max;

    TQString result;
    for ( unsigned y = top; y <= bottom; ++y)
    {
      for ( unsigned x = left; x <= right; ++x)
      {
        Cell *cell = cellAt( x, y );
        result += cellAsText( cell, max );
      }
      result += "\n";
    }

    return result;
}

void Sheet::copySelection( Selection* selectionInfo )
{
    TQDomDocument doc = saveCellRegion( *selectionInfo, true );

    // Save to buffer
    TQBuffer buffer;
    buffer.open( IO_WriteOnly );
    TQTextStream str( &buffer );
    str.setEncoding( TQTextStream::UnicodeUTF8 );
    str << doc;
    buffer.close();

    TextDrag * kd = new TextDrag( 0L );
    kd->setPlain( copyAsText(selectionInfo) );
    kd->setKSpread( buffer.buffer() );

    TQApplication::clipboard()->setData( kd );
}

void Sheet::cutSelection( Selection* selectionInfo )
{
    TQDomDocument doc = saveCellRegion(*selectionInfo, true, true);

    // Save to buffer
    TQBuffer buffer;
    buffer.open( IO_WriteOnly );
    TQTextStream str( &buffer );
    str.setEncoding( TQTextStream::UnicodeUTF8 );
    str << doc;
    buffer.close();

    TextDrag * kd = new TextDrag( 0L );
    kd->setPlain( copyAsText(selectionInfo) );
    kd->setKSpread( buffer.buffer() );

    TQApplication::clipboard()->setData( kd );

    deleteSelection( selectionInfo, true );
}

void Sheet::paste( const TQRect& pasteArea, bool makeUndo,
                   Paste::Mode mode, Paste::Operation operation,
                   bool insert, int insertTo, bool pasteFC,
                   TQClipboard::Mode clipboardMode )
{
    TQMimeSource * mime = TQApplication::clipboard()->data( clipboardMode );
    if ( !mime )
        return;

    TQByteArray b;

    if ( mime->provides( TextDrag::selectionMimeType() ) )
    {
        b = mime->encodedData( TextDrag::selectionMimeType() );
    }
    else if( mime->provides( "text/plain" ) )
    {
        // Note: TQClipboard::text() seems to do a better job than encodedData( "text/plain" )
        // In particular it handles charsets (in the mimetype). Copied from KPresenter ;-)
        TQString _text = TQApplication::clipboard()->text( clipboardMode );
        doc()->emitBeginOperation();
        pasteTextPlain( _text, pasteArea );
        emit sig_updateView( this );
        // doc()->emitEndOperation();
        return;
    }
    else
        return;

    // Do the actual pasting.
    doc()->emitBeginOperation();
    paste( b, pasteArea, makeUndo, mode, operation, insert, insertTo, pasteFC );
    emit sig_updateView( this );
    // doc()->emitEndOperation();
}


void Sheet::pasteTextPlain( TQString &_text, TQRect pasteArea)
{
//  TQString tmp;
//  tmp= TQString::fromLocal8Bit(_mime->encodedData( "text/plain" ));
  if( _text.isEmpty() )
    return;

  TQString tmp = _text;
  int i;
  int mx   = pasteArea.left();
  int my   = pasteArea.top();
  int rows = 1;
  int len  = tmp.length();

  //count the numbers of lines in text
  for ( i = 0; i < len; ++i )
  {
    if ( tmp[i] == '\n' )
      ++rows;
  }

  Cell * cell = nonDefaultCell( mx, my );
  if ( rows == 1 )
  {
    if ( !doc()->undoLocked() )
    {
      UndoSetText * undo = new UndoSetText( doc(), this , cell->text(), mx, my, cell->formatType() );
      doc()->addCommand( undo );
    }
  }
  else
  {
      TQRect rect(mx, my, mx, my + rows - 1);
      UndoChangeAreaTextCell * undo = new UndoChangeAreaTextCell( doc(), this , rect );
      doc()->addCommand( undo );
  }

  i = 0;
  TQString rowtext;

  while ( i < rows )
  {
    int p = 0;

    p = tmp.find('\n');

    if (p < 0)
      p = tmp.length();

    rowtext = tmp.left(p);

    if ( !isProtected() || cell->format()->notProtected( mx, my + i ) )
    {
      cell->setCellText( rowtext );
      cell->updateChart();
    }

    // next cell
    ++i;
    cell = nonDefaultCell( mx, my + i );

    if (!cell || p == (int) tmp.length())
      break;

    // exclude the left part and '\n'
    tmp = tmp.right(tmp.length() - p - 1);
  }

  if (!isLoading())
    refreshMergedCell();

  emit sig_updateView( this );
  emit sig_updateHBorder( this );
  emit sig_updateVBorder( this );
}

void Sheet::paste( const TQByteArray& b, const TQRect& pasteArea, bool makeUndo,
                   Paste::Mode mode, Paste::Operation operation,
                   bool insert, int insertTo, bool pasteFC )
{
    kdDebug(36001) << "Parsing " << b.size() << " bytes" << endl;

    TQBuffer buffer( b );
    buffer.open( IO_ReadOnly );
    TQDomDocument doc;
    doc.setContent( &buffer );
    buffer.close();

    // ##### TODO: Test for parsing errors

    int mx = pasteArea.left();
    int my = pasteArea.top();

    loadSelection( doc, pasteArea, mx - 1, my - 1, makeUndo,
                   mode, operation, insert, insertTo, pasteFC );
}

bool Sheet::loadSelection(const TQDomDocument& doc, const TQRect& pasteArea,
                          int _xshift, int _yshift, bool makeUndo,
                          Paste::Mode mode, Paste::Operation operation, bool insert,
                          int insertTo, bool pasteFC)
{
  //kdDebug(36001) << "loadSelection called. pasteArea=" << pasteArea << endl;

  if (!isLoading() && makeUndo)
  {
    loadSelectionUndo( doc, pasteArea, _xshift, _yshift, insert, insertTo );
  }

  TQDomElement root = doc.documentElement(); // "spreadsheet-snippet"

  int rowsInClpbrd    =  root.attribute( "rows" ).toInt();
  int columnsInClpbrd =  root.attribute( "columns" ).toInt();

  // find size of rectangle that we want to paste to (either clipboard size or current selection)
  const int pasteWidth = ( pasteArea.width() >= columnsInClpbrd
                            && util_isRowSelected(pasteArea) == false
                            && root.namedItem( "rows" ).toElement().isNull() )
    ? pasteArea.width() : columnsInClpbrd;
  const int pasteHeight = ( pasteArea.height() >= rowsInClpbrd
                            && util_isColumnSelected(pasteArea) == false
                            && root.namedItem( "columns" ).toElement().isNull())
    ? pasteArea.height() : rowsInClpbrd;

//   kdDebug() << "loadSelection: paste area has size "
//             << pasteHeight << " rows * "
//             << pasteWidth << " columns " << endl;
//   kdDebug() << "loadSelection: " << rowsInClpbrd << " rows and "
//             << columnsInClpbrd << " columns in clipboard." << endl;
//   kdDebug() << "xshift: " << _xshift << " _yshift: " << _yshift << endl;

  TQDomElement e = root.firstChild().toElement(); // "columns", "rows" or "cell"
  for (; !e.isNull(); e = e.nextSibling().toElement())
  {
    // entire columns given
    if (e.tagName() == "columns" && !isProtected())
    {
        _yshift = 0;

        // Clear the existing columns
        int col = e.attribute("column").toInt();
        int width = e.attribute("count").toInt();
        if (!insert)
        {
            for ( int i = col; i < col + width; ++i )
            {
                d->cells.clearColumn( _xshift + i );
                d->columns.removeElement( _xshift + i );
            }
        }

        // Insert column formats
        TQDomElement c = e.firstChild().toElement();
        for ( ; !c.isNull(); c = c.nextSibling().toElement() )
        {
            if ( c.tagName() == "column" )
            {
                ColumnFormat *cl = new ColumnFormat( this, 0 );
                if ( cl->load( c, _xshift, mode, pasteFC ) )
                    insertColumnFormat( cl );
                else
                    delete cl;
            }
        }
    }

    // entire rows given
    if (e.tagName() == "rows" && !isProtected())
    {
        _xshift = 0;

        // Clear the existing rows
        int row = e.attribute("row").toInt();
        int height = e.attribute("count").toInt();
        if ( !insert )
        {
          for( int i = row; i < row + height; ++i )
          {
            d->cells.clearRow( _yshift + i );
            d->rows.removeElement( _yshift + i );
          }
        }

        // Insert row formats
        TQDomElement c = e.firstChild().toElement();
        for( ; !c.isNull(); c = c.nextSibling().toElement() )
        {
            if ( c.tagName() == "row" )
            {
                RowFormat *cl = new RowFormat( this, 0 );
                if ( cl->load( c, _yshift, mode, pasteFC ) )
                    insertRowFormat( cl );
                else
                    delete cl;
            }
        }
    }

    Cell* refreshCell = 0;
    Cell *cell;
    Cell *cellBackup = NULL;
    if (e.tagName() == "cell")
    {
      int row = e.attribute( "row" ).toInt() + _yshift;
      int col = e.attribute( "column" ).toInt() + _xshift;

      // tile the selection with the clipboard contents
      for (int roff = 0; row + roff - _yshift <= pasteHeight; roff += rowsInClpbrd)
      {
        for (int coff = 0; col + coff - _xshift <= pasteWidth; coff += columnsInClpbrd)
        {
//           kdDebug() << "loadSelection: cell at " << (col+coff) << "," << (row+roff)
//                     << " with roff,coff= " << roff << "," << coff
//                     << ", _xshift: " << _xshift << ", _yshift: " << _yshift << endl;

          cell = nonDefaultCell( col + coff, row + roff );
          if (isProtected() && !cell->format()->notProtected(col + coff, row + roff))
          {
            continue;
          }

          cellBackup = new Cell(this, cell->column(), cell->row());
          cellBackup->copyAll(cell);

          if (!cell->load(e, _xshift + coff, _yshift + roff, mode, operation, pasteFC))
          {
            cell->copyAll(cellBackup);
          }
          else
          {
            if (cell->isFormula())
            {
                cell->setCalcDirtyFlag();
            }
          }

          delete cellBackup;



          cell = cellAt( col + coff, row + roff );
          if( !refreshCell && cell->updateChart( false ) )
          {
            refreshCell = cell;
          }
        }
      }
    }

    //refresh chart after that you paste all cells

    /* I don't think this is gonna work....doesn't this only update
       one chart -- the one which had a dependant cell update first? - John

       I don't have time to check on this now....
    */
    if ( refreshCell )
        refreshCell->updateChart();
  }
    this->doc()->setModified( true );

    if (!isLoading())
        refreshMergedCell();

    emit sig_updateView( this );
    emit sig_updateHBorder( this );
    emit sig_updateVBorder( this );

    return true;
}

void Sheet::loadSelectionUndo(const TQDomDocument& d, const TQRect& loadArea,
                              int _xshift, int _yshift,
                              bool insert, int insertTo)
{
  TQDomElement root = d.documentElement(); // "spreadsheet-snippet"

  int rowsInClpbrd    = root.attribute( "rows" ).toInt();
  int columnsInClpbrd = root.attribute( "columns" ).toInt();

  // find rect that we paste to
  const int pasteWidth = (loadArea.width() >= columnsInClpbrd &&
                          util_isRowSelected(loadArea) == false &&
                          root.namedItem( "rows" ).toElement().isNull())
      ? loadArea.width() : columnsInClpbrd;
  const int pasteHeight = (loadArea.height() >= rowsInClpbrd &&
                           util_isColumnSelected(loadArea) == false &&
                           root.namedItem( "columns" ).toElement().isNull())
      ? loadArea.height() : rowsInClpbrd;

  uint numCols = 0;
  uint numRows = 0;

  Region region;
  for (TQDomNode n = root.firstChild(); !n.isNull(); n = n.nextSibling())
  {
    TQDomElement e = n.toElement(); // "columns", "rows" or "cell"
    if (e.tagName() == "columns")
    {
      _yshift = 0;
      int col = e.attribute("column").toInt();
      int width = e.attribute("count").toInt();
      for (int coff = 0; col + coff <= pasteWidth; coff += columnsInClpbrd)
      {
        uint overlap = TQMAX(0, (col - 1 + coff + width) - pasteWidth);
        uint effWidth = width - overlap;
        region.add(TQRect(_xshift + col + coff, 1, effWidth, KS_rowMax));
        numCols += effWidth;
      }
    }
    else if (e.tagName() == "rows")
    {
      _xshift = 0;
      int row = e.attribute("row").toInt();
      int height = e.attribute("count").toInt();
      for (int roff = 0; row + roff <= pasteHeight; roff += rowsInClpbrd)
      {
        uint overlap = TQMAX(0, (row - 1 + roff + height) - pasteHeight);
        uint effHeight = height - overlap;
        region.add(TQRect(1, _yshift + row + roff, KS_colMax, effHeight));
        numRows += effHeight;
      }
    }
    else if (!e.isNull())
    {
      // store the cols/rows for the insertion
      int col = e.attribute("column").toInt();
      int row = e.attribute("row").toInt();
      for (int coff = 0; col + coff <= pasteWidth; coff += columnsInClpbrd)
      {
        for (int roff = 0; row + roff <= pasteHeight; roff += rowsInClpbrd)
        {
          region.add(TQPoint(_xshift + col + coff, _yshift + row + roff));
        }
      }
    }
  }

  if (!doc()->undoLocked())
  {
    UndoCellPaste *undo = new UndoCellPaste( doc(), this, _xshift, _yshift, region, insert, insertTo );
    doc()->addCommand( undo );
  }

  if (insert)
  {
    TQRect rect = region.boundingRect();
    // shift cells to the right
    if (insertTo == -1 && numCols == 0 && numRows == 0)
    {
      rect.setWidth(rect.width());
      shiftRow(rect, false);
    }
    // shift cells to the bottom
    else if (insertTo == 1 && numCols == 0 && numRows == 0)
    {
      rect.setHeight(rect.height());
      shiftColumn( rect, false );
    }
    // insert columns
    else if (insertTo == 0 && numCols == 0 && numRows > 0)
    {
      insertRow(rect.top(), rect.height() - 1, false);
    }
    // insert rows
    else if (insertTo == 0 && numCols > 0 && numRows == 0)
    {
      insertColumn(rect.left(), rect.width() - 1, false);
    }
  }
}

bool Sheet::testAreaPasteInsert()const
{
    TQMimeSource* mime = TQApplication::clipboard()->data( TQClipboard::Clipboard );
    if ( !mime )
        return false;

    TQByteArray b;

    if ( mime->provides( "application/x-kspread-snippet" ) )
        b = mime->encodedData( "application/x-kspread-snippet" );
    else
        return false;

    TQBuffer buffer( b );
    buffer.open( IO_ReadOnly );
    TQDomDocument d;
    d.setContent( &buffer );
    buffer.close();

    TQDomElement e = d.documentElement();
    if ( !e.namedItem( "columns" ).toElement().isNull() )
        return false;

    if ( !e.namedItem( "rows" ).toElement().isNull() )
        return false;

    TQDomElement c = e.firstChild().toElement();
    for( ; !c.isNull(); c = c.nextSibling().toElement() )
    {
        if ( c.tagName() == "cell" )
                return true;
    }
    return false;
}

void Sheet::deleteCells(const Region& region)
{
    // A list of all cells we want to delete.
    TQPtrStack<Cell> cellStack;

  Region::ConstIterator endOfList = region.constEnd();
  for (Region::ConstIterator it = region.constBegin(); it != endOfList; ++it)
  {
    TQRect range = (*it)->rect().normalize();

    int right  = range.right();
    int left   = range.left();
    int bottom = range.bottom();
    int col;
    for ( int row = range.top(); row <= bottom; ++row )
    {
      Cell * c = getFirstCellRow( row );
      while ( c )
      {
        col = c->column();
        if ( col < left )
        {
          c = getNextCellRight( left - 1, row );
          continue;
        }
        if ( col > right )
          break;

        if ( !c->isDefault() )
          cellStack.push( c );

        c = getNextCellRight( col, row );
      }
    }
  }

    d->cells.setAutoDelete( false );

    // Remove the cells from the sheet
    while ( !cellStack.isEmpty() )
    {
      Cell * cell = cellStack.pop();

      d->cells.remove( cell->column(), cell->row() );
      cell->setCalcDirtyFlag();
      setRegionPaintDirty(cell->cellRect());

      delete cell;
    }

    d->cells.setAutoDelete( true );

    setLayoutDirtyFlag();

    // TODO: don't go through all cells here!
    // Since obscured cells might have been deleted we
    // have to reenforce it.
    Cell * c = d->cells.firstCell();
    for( ;c; c = c->nextCell() )
    {
      if ( c->doesMergeCells() && !c->isDefault() )
        c->mergeCells( c->column(), c->row(),
           c->extraXCells(), c->extraYCells() );
    }
    doc()->setModified( true );
}

void Sheet::deleteSelection( Selection* selectionInfo, bool undo )
{
    if ( undo && !doc()->undoLocked() )
    {
        UndoDelete *undo = new UndoDelete( doc(), this, *selectionInfo );
        doc()->addCommand( undo );
    }

  Region::ConstIterator endOfList = selectionInfo->constEnd();
  for (Region::ConstIterator it = selectionInfo->constBegin(); it != endOfList; ++it)
  {
    TQRect range = (*it)->rect().normalize();

    // Entire rows selected ?
    if ( util_isRowSelected(range) )
    {
        for( int i = range.top(); i <= range.bottom(); ++i )
        {
            d->cells.clearRow( i );
            d->rows.removeElement( i );
        }

        emit sig_updateVBorder( this );
    }
    // Entire columns selected ?
    else if ( util_isColumnSelected(range) )
    {
        for( int i = range.left(); i <= range.right(); ++i )
        {
            d->cells.clearColumn( i );
            d->columns.removeElement( i );
        }

        emit sig_updateHBorder( this );
    }
    else
    {
	setRegionPaintDirty( range );
        deleteCells( range );
    }
  }
    refreshMergedCell();
    emit sig_updateView( this );
}

void Sheet::updateView()
{
  emit sig_updateView( this );
}

void Sheet::updateView( TQRect const & rect )
{
  emit sig_updateView( this, rect );
}

void Sheet::updateView(Region* region)
{
  emit sig_updateView( this, *region );
}

void Sheet::refreshView( const Region& region )
{
  Region tmpRegion;
  Region::ConstIterator endOfList = region.constEnd();
  for (Region::ConstIterator it = region.constBegin(); it != endOfList; ++it)
  {
    TQRect range = (*it)->rect().normalize();
    // TODO: don't go through all cells when refreshing!
    TQRect tmp(range);
    Cell * c = d->cells.firstCell();
    for( ;c; c = c->nextCell() )
    {
      if ( !c->isDefault() &&
            c->row() >= range.top() && c->row() <= range.bottom() &&
            c->column() >= range.left() && c->column() <= range.right() )
      {
        if (c->doesMergeCells())
        {
              int right=TQMAX(tmp.right(),c->column()+c->extraXCells());
              int bottom=TQMAX(tmp.bottom(),c->row()+c->extraYCells());

              tmp.setRight(right);
              tmp.setBottom(bottom);
        }
      }
    }
    deleteCells( range );
    tmpRegion.add(tmp);
  }
  emit sig_updateView( this, tmpRegion );
}


void Sheet::mergeCells(const Region& region, bool hor, bool ver)
{
  // sanity check
  if( isProtected() )
    return;
  if( workbook()->isProtected() )
    return;

  MergeManipulator* manipulator = new MergeManipulator();
  manipulator->setSheet(this);
  manipulator->setHorizontalMerge(hor);
  manipulator->setVerticalMerge(ver);
  manipulator->add(region);
  manipulator->execute();
}

void Sheet::dissociateCells(const Region& region)
{
  // sanity check
  if( isProtected() )
    return;
  if( workbook()->isProtected() )
    return;

  Manipulator* manipulator = new MergeManipulator();
  manipulator->setSheet(this);
  manipulator->setReverse(true);
  manipulator->add(region);
  manipulator->execute();
}

bool Sheet::testListChoose(Selection* selectionInfo)
{
   TQRect selection( selectionInfo->selection() );
   TQPoint marker( selectionInfo->marker() );

   Cell *cell = cellAt( marker.x(), marker.y() );
   TQString tmp=cell->text();

   Cell* c = firstCell();
   bool different=false;
   int col;
   for( ;c; c = c->nextCell() )
     {
       col = c->column();
       if ( selection.left() <= col && selection.right() >= col &&
            !c->isPartOfMerged() &&
            !(col==marker.x() && c->row()==marker.y()))
   {
     if(!c->isFormula() && !c->value().isNumber() && !c->value().asString().isEmpty()
        && !c->isTime() &&!c->isDate() )
       {
                 if(c->text()!=tmp)
                     different=true;
       }

   }
     }
   return different;
}



TQDomDocument Sheet::saveCellRegion(const Region& region, bool copy, bool era)
{
  TQDomDocument dd( "spreadsheet-snippet" );
  dd.appendChild( dd.createProcessingInstruction( "xml", "version=\"1.0\" encoding=\"UTF-8\"" ) );
  TQDomElement root = dd.createElement( "spreadsheet-snippet" );
  dd.appendChild(root);

  // find the upper left corner of the selection
  TQRect boundingRect = region.boundingRect();
  int left = boundingRect.left();
  int top = boundingRect.top();

  // for tiling the clipboard content in the selection
  root.setAttribute( "rows", boundingRect.height() );
  root.setAttribute( "columns", boundingRect.width() );

  Region::ConstIterator endOfList = region.constEnd();
  for (Region::ConstIterator it = region.constBegin(); it != endOfList; ++it)
  {
    TQRect range = (*it)->rect().normalize();

    //
    // Entire rows selected?
    //
    if ((*it)->isRow())
    {
      TQDomElement rows = dd.createElement("rows");
      rows.setAttribute( "count", range.height() );
      rows.setAttribute( "row", range.top() - top + 1 );
      root.appendChild( rows );

      // Save all cells.
      for (Cell* cell = d->cells.firstCell(); cell; cell = cell->nextCell())
      {
        if (!cell->isDefault() && !cell->isPartOfMerged())
        {
          TQPoint point(cell->column(), cell->row());
          if (range.contains(point))
          {
            root.appendChild(cell->save( dd, 0, top - 1, copy, copy, era));
          }
        }
      }

      // ##### Inefficient
      // Save the row formats if there are any
      RowFormat* format;
      for (int row = range.top(); row <= range.bottom(); ++row)
      {
        format = rowFormat( row );
        if (format && !format->isDefault())
        {
          TQDomElement e = format->save(dd, top - 1, copy);
          if (!e.isNull())
          {
            rows.appendChild( e );
          }
        }
      }
      continue;
    }

    //
    // Entire columns selected?
    //
    if ((*it)->isColumn())
    {
      TQDomElement columns = dd.createElement("columns");
      columns.setAttribute( "count", range.width() );
      columns.setAttribute( "column", range.left() - left + 1 );
      root.appendChild( columns );

      // Save all cells.
      for (Cell* cell = d->cells.firstCell();cell; cell = cell->nextCell())
      {
        if (!cell->isDefault() && !cell->isPartOfMerged())
        {
          TQPoint point(cell->column(), cell->row());
          if (range.contains(point))
          {
            root.appendChild(cell->save( dd, left - 1, 0, copy, copy, era));
          }
        }
      }

      // ##### Inefficient
      // Save the column formats if there are any
      ColumnFormat* format;
      for (int col = range.left(); col <= range.right(); ++col)
      {
        format = columnFormat(col);
        if (format && !format->isDefault())
        {
          TQDomElement e = format->save(dd, left - 1, copy);
          if (!e.isNull())
          {
            columns.appendChild(e);
          }
        }
      }
      continue;
    }

    // Save all cells.
    //store all cell
    //when they don't exist we created them
    //because it's necessary when there is a  format on a column/row
    //but I remove cell which is inserted.
    Cell* cell;
    bool insert;
    enableScrollBarUpdates(false);
    for (int col = range.left(); col <= range.right(); ++col)
    {
      for (int row = range.top(); row <= range.bottom(); ++row)
      {
        insert = false;
        cell = cellAt(col, row);
        if (cell == d->defaultCell)
        {
          cell = new Cell(this, col, row);
          insertCell(cell);
          insert = true;
        }
        root.appendChild(cell->save(dd, left - 1, top - 1, true, copy, era));
        if (insert)
        {
          d->cells.remove(col, row);
        }
      }
    }
    enableScrollBarUpdates(true);
  }
  return dd;
}

TQDomElement Sheet::saveXML( TQDomDocument& dd )
{
    TQDomElement sheet = dd.createElement( "table" );
    sheet.setAttribute( "name", d->name );


    //Laurent: for oasis format I think that we must use style:direction...
    sheet.setAttribute( "layoutDirection", (d->layoutDirection == RightToLeft) ? "rtl" : "ltr" );
    sheet.setAttribute( "columnnumber", (int)d->showColumnNumber);
    sheet.setAttribute( "borders", (int)d->showPageBorders);
    sheet.setAttribute( "hide", (int)d->hide);
    sheet.setAttribute( "hidezero", (int)d->hideZero);
    sheet.setAttribute( "firstletterupper", (int)d->firstLetterUpper);
    sheet.setAttribute( "grid", (int)d->showGrid );
    sheet.setAttribute( "printGrid", (int)d->print->printGrid() );
    sheet.setAttribute( "printCommentIndicator", (int)d->print->printCommentIndicator() );
    sheet.setAttribute( "printFormulaIndicator", (int)d->print->printFormulaIndicator() );
    sheet.setAttribute( "showFormula", (int)d->showFormula);
    sheet.setAttribute( "showFormulaIndicator", (int)d->showFormulaIndicator);
    sheet.setAttribute( "showCommentIndicator", (int)d->showCommentIndicator);
    sheet.setAttribute( "lcmode", (int)d->lcMode);
    sheet.setAttribute( "autoCalc", (int)d->autoCalc);
    sheet.setAttribute( "borders1.2", 1);
    if ( !d->password.isNull() )
    {
      if ( d->password.size() > 0 )
      {
        TQCString str = KCodecs::base64Encode( d->password );
        sheet.setAttribute( "protected", TQString( str.data() ) );
      }
      else
        sheet.setAttribute( "protected", "" );
    }

    // paper parameters
    TQDomElement paper = dd.createElement( "paper" );
    paper.setAttribute( "format", d->print->paperFormatString() );
    paper.setAttribute( "orientation", d->print->orientationString() );
    sheet.appendChild( paper );

    TQDomElement borders = dd.createElement( "borders" );
    borders.setAttribute( "left", d->print->leftBorder() );
    borders.setAttribute( "top", d->print->topBorder() );
    borders.setAttribute( "right", d->print->rightBorder() );
    borders.setAttribute( "bottom", d->print->bottomBorder() );
    paper.appendChild( borders );

    TQDomElement head = dd.createElement( "head" );
    paper.appendChild( head );
    if ( !d->print->headLeft().isEmpty() )
    {
      TQDomElement left = dd.createElement( "left" );
      head.appendChild( left );
      left.appendChild( dd.createTextNode( d->print->headLeft() ) );
    }
    if ( !d->print->headMid().isEmpty() )
    {
      TQDomElement center = dd.createElement( "center" );
      head.appendChild( center );
      center.appendChild( dd.createTextNode( d->print->headMid() ) );
    }
    if ( !d->print->headRight().isEmpty() )
    {
      TQDomElement right = dd.createElement( "right" );
      head.appendChild( right );
      right.appendChild( dd.createTextNode( d->print->headRight() ) );
    }
    TQDomElement foot = dd.createElement( "foot" );
    paper.appendChild( foot );
    if ( !d->print->footLeft().isEmpty() )
    {
      TQDomElement left = dd.createElement( "left" );
      foot.appendChild( left );
      left.appendChild( dd.createTextNode( d->print->footLeft() ) );
    }
    if ( !d->print->footMid().isEmpty() )
    {
      TQDomElement center = dd.createElement( "center" );
      foot.appendChild( center );
      center.appendChild( dd.createTextNode( d->print->footMid() ) );
    }
    if ( !d->print->footRight().isEmpty() )
    {
      TQDomElement right = dd.createElement( "right" );
      foot.appendChild( right );
      right.appendChild( dd.createTextNode( d->print->footRight() ) );
    }

    // print range
    TQDomElement printrange = dd.createElement( "printrange-rect" );
    TQRect _printRange = d->print->printRange();
    int left = _printRange.left();
    int right = _printRange.right();
    int top = _printRange.top();
    int bottom = _printRange.bottom();
    //If whole rows are selected, then we store zeros, as KS_colMax may change in future
    if ( left == 1 && right == KS_colMax )
    {
      left = 0;
      right = 0;
    }
    //If whole columns are selected, then we store zeros, as KS_rowMax may change in future
    if ( top == 1 && bottom == KS_rowMax )
    {
      top = 0;
      bottom = 0;
    }
    printrange.setAttribute( "left-rect", left );
    printrange.setAttribute( "right-rect", right );
    printrange.setAttribute( "bottom-rect", bottom );
    printrange.setAttribute( "top-rect", top );
    sheet.appendChild( printrange );

    // Print repeat columns
    TQDomElement printRepeatColumns = dd.createElement( "printrepeatcolumns" );
    printRepeatColumns.setAttribute( "left", d->print->printRepeatColumns().first );
    printRepeatColumns.setAttribute( "right", d->print->printRepeatColumns().second );
    sheet.appendChild( printRepeatColumns );

    // Print repeat rows
    TQDomElement printRepeatRows = dd.createElement( "printrepeatrows" );
    printRepeatRows.setAttribute( "top", d->print->printRepeatRows().first );
    printRepeatRows.setAttribute( "bottom", d->print->printRepeatRows().second );
    sheet.appendChild( printRepeatRows );

    //Save print zoom
    sheet.setAttribute( "printZoom", d->print->zoom() );

    //Save page limits
    sheet.setAttribute( "printPageLimitX", d->print->pageLimitX() );
    sheet.setAttribute( "printPageLimitY", d->print->pageLimitY() );

    // Save all cells.
    Cell* c = d->cells.firstCell();
    for( ;c; c = c->nextCell() )
    {
        if ( !c->isDefault() )
        {
            TQDomElement e = c->save( dd );
            if ( !e.isNull() )
                sheet.appendChild( e );
        }
    }

    // Save all RowFormat objects.
    RowFormat* rl = d->rows.first();
    for( ; rl; rl = rl->next() )
    {
        if ( !rl->isDefault() )
        {
            TQDomElement e = rl->save( dd );
            if ( e.isNull() )
                return TQDomElement();
            sheet.appendChild( e );
        }
    }

    // Save all ColumnFormat objects.
    ColumnFormat* cl = d->columns.first();
    for( ; cl; cl = cl->next() )
    {
        if ( !cl->isDefault() )
        {
            TQDomElement e = cl->save( dd );
            if ( e.isNull() )
                return TQDomElement();
            sheet.appendChild( e );
        }
    }

    TQPtrListIterator<EmbeddedObject>  chl = doc()->embeddedObjects();
    for( ; chl.current(); ++chl )
    {
       if ( chl.current()->sheet() == this )
       {
         TQDomElement e = chl.current()->save( dd );

         if ( e.isNull() )
           return TQDomElement();
         sheet.appendChild( e );
       }
    }
    return sheet;
}

bool Sheet::isLoading()
{
    return doc()->isLoading();
}


TQPtrList<EmbeddedObject> Sheet::getSelectedObjects()
{
    TQPtrList<EmbeddedObject> objects;
    TQPtrListIterator<EmbeddedObject> it = doc()->embeddedObjects();
    for ( ; it.current() ; ++it )
    {
        if( it.current()->isSelected()
            && it.current()->sheet() == this )
        {
            objects.append( it.current() );
        }
    }
     return objects;
}

KoRect Sheet::getRealRect( bool all )
{
    KoRect rect;

    TQPtrListIterator<EmbeddedObject> it( doc()->embeddedObjects() );
    for ( ; it.current() ; ++it )
    {

        if ( all || ( it.current()->isSelected() && ! it.current()->isProtect() ) )
            rect |= it.current()->geometry();
    }

    return rect;
}

// move object for releasemouseevent
KCommand *Sheet::moveObject(View *_view, double diffx, double diffy)
{
    bool createCommand=false;
    MoveObjectByCmd *moveByCmd=0L;
    Canvas * canvas = _view->canvasWidget();
    TQPtrList<EmbeddedObject> _objects;
    _objects.setAutoDelete( false );
    TQPtrListIterator<EmbeddedObject> it( doc()->embeddedObjects()/*m_objectList*/ );
    for ( ; it.current() ; ++it )
    {
        if ( it.current()->isSelected() && !it.current()->isProtect())
        {
            _objects.append( it.current() );
            KoRect geometry = it.current()->geometry();
            geometry.moveBy( -canvas->xOffset(), -canvas->yOffset() );
            TQRect br = doc()->zoomRect( geometry/*it.current()->geometry()*/ );
            br.moveBy( doc()->zoomItX( diffx ), doc()->zoomItY( diffy ) );
            br.moveBy( doc()->zoomItX( -canvas->xOffset() ), doc()->zoomItY( -canvas->yOffset() ) );
            canvas->repaint( br ); // Previous position
            canvas->repaintObject( it.current() ); // New position
            createCommand=true;
        }
    }
    if(createCommand) {
        moveByCmd = new MoveObjectByCmd( i18n( "Move Objects" ), KoPoint( diffx, diffy ),
                                   _objects, doc(), this );

//         m_doc->updateSideBarItem( this );
    }
    return moveByCmd;
}

KCommand *Sheet::moveObject(View *_view,const KoPoint &_move,bool key)
{
    TQPtrList<EmbeddedObject> _objects;
    _objects.setAutoDelete( false );
    MoveObjectByCmd *moveByCmd=0L;
    Canvas * canvas = _view->canvasWidget();
    TQPtrListIterator<EmbeddedObject> it( doc()->embeddedObjects()/*m_objectList*/ );
    for ( ; it.current() ; ++it )
    {
        if ( it.current()->isSelected() && !it.current()->isProtect()) {

            KoRect geometry = it.current()->geometry();
            geometry.moveBy( -canvas->xOffset(), -canvas->yOffset() );
            TQRect oldBoundingRect = doc()->zoomRect( geometry );


            KoRect r = it.current()->geometry();
            r.moveBy( _move.x(), _move.y() );

            it.current()->setGeometry( r );
            _objects.append( it.current() );

            canvas->repaint( oldBoundingRect );
            canvas->repaintObject( it.current() );
        }
    }

    if ( key && !_objects.isEmpty())
        moveByCmd = new MoveObjectByCmd( i18n( "Move Objects" ),
                                   KoPoint( _move ),
                                   _objects, doc() ,this );

    return moveByCmd;
}

/*
 * Check if object name already exists.
 */
bool Sheet::objectNameExists( EmbeddedObject *object, TQPtrList<EmbeddedObject> &list ) {
    TQPtrListIterator<EmbeddedObject> it( list );

    for ( it.toFirst(); it.current(); ++it ) {
        // object name can exist in current object.
        if ( it.current()->getObjectName() == object->getObjectName() &&
             it.current() != object ) {
            return true;
        }
    }
    return false;
}

void Sheet::unifyObjectName( EmbeddedObject *object ) {
    if ( object->getObjectName().isEmpty() ) {
        object->setObjectName( object->getTypeString() );
    }
    TQString objectName( object->getObjectName() );

    TQPtrList<EmbeddedObject> list( doc()->embeddedObjects() );

    int count = 1;

    while ( objectNameExists( object, list ) ) {
        count++;
        TQRegExp rx( " \\(\\d{1,3}\\)$" );
        if ( rx.search( objectName ) != -1 ) {
            objectName.remove( rx );
        }
        objectName += TQString(" (%1)").arg( count );
        object->setObjectName( objectName );
    }
}


void Sheet::checkContentDirection( TQString const & name )
{
  /* set sheet's direction to RTL if sheet name is an RTL string */
  if ( (name.isRightToLeft()) )
    setLayoutDirection( RightToLeft );
  else
    setLayoutDirection( LeftToRight );

  emit sig_refreshView();
}

bool Sheet::loadSheetStyleFormat( TQDomElement *style )
{
    TQString hleft, hmiddle, hright;
    TQString fleft, fmiddle, fright;
    TQDomNode header = KoDom::namedItemNS( *style, KoXmlNS::style, "header" );

    if ( !header.isNull() )
    {
        kdDebug() << "Header exists" << endl;
        TQDomNode part = KoDom::namedItemNS( header, KoXmlNS::style, "region-left" );
        if ( !part.isNull() )
        {
            hleft = getPart( part );
            kdDebug() << "Header left: " << hleft << endl;
        }
        else
            kdDebug() << "Style:region:left doesn't exist!" << endl;
        part = KoDom::namedItemNS( header, KoXmlNS::style, "region-center" );
        if ( !part.isNull() )
        {
            hmiddle = getPart( part );
            kdDebug() << "Header middle: " << hmiddle << endl;
        }
        part = KoDom::namedItemNS( header, KoXmlNS::style, "region-right" );
        if ( !part.isNull() )
        {
            hright = getPart( part );
            kdDebug() << "Header right: " << hright << endl;
        }
    }
    //TODO implement it under kspread
    TQDomNode headerleft = KoDom::namedItemNS( *style, KoXmlNS::style, "header-left" );
    if ( !headerleft.isNull() )
    {
        TQDomElement e = headerleft.toElement();
        if ( e.hasAttributeNS( KoXmlNS::style, "display" ) )
            kdDebug()<<"header.hasAttribute( style:display ) :"<<e.hasAttributeNS( KoXmlNS::style, "display" )<<endl;
        else
            kdDebug()<<"header left doesn't has attribute  style:display  \n";
    }
    //TODO implement it under kspread
    TQDomNode footerleft = KoDom::namedItemNS( *style, KoXmlNS::style, "footer-left" );
    if ( !footerleft.isNull() )
    {
        TQDomElement e = footerleft.toElement();
        if ( e.hasAttributeNS( KoXmlNS::style, "display" ) )
            kdDebug()<<"footer.hasAttribute( style:display ) :"<<e.hasAttributeNS( KoXmlNS::style, "display" )<<endl;
        else
            kdDebug()<<"footer left doesn't has attribute  style:display  \n";
    }

    TQDomNode footer = KoDom::namedItemNS( *style, KoXmlNS::style, "footer" );

    if ( !footer.isNull() )
    {
        TQDomNode part = KoDom::namedItemNS( footer, KoXmlNS::style, "region-left" );
        if ( !part.isNull() )
        {
            fleft = getPart( part );
            kdDebug() << "Footer left: " << fleft << endl;
        }
        part = KoDom::namedItemNS( footer, KoXmlNS::style, "region-center" );
        if ( !part.isNull() )
        {
            fmiddle = getPart( part );
            kdDebug() << "Footer middle: " << fmiddle << endl;
        }
        part = KoDom::namedItemNS( footer, KoXmlNS::style, "region-right" );
        if ( !part.isNull() )
        {
            fright = getPart( part );
            kdDebug() << "Footer right: " << fright << endl;
        }
    }

    print()->setHeadFootLine( hleft, hmiddle, hright,
                              fleft, fmiddle, fright );
    return true;
}

void Sheet::replaceMacro( TQString & text, const TQString & old, const TQString & newS )
{
  int n = text.find( old );
  if ( n != -1 )
    text = text.replace( n, old.length(), newS );
}


TQString Sheet::getPart( const TQDomNode & part )
{
  TQString result;
  TQDomElement e = KoDom::namedItemNS( part, KoXmlNS::text, "p" );
  while ( !e.isNull() )
  {
    TQString text = e.text();
    kdDebug() << "PART: " << text << endl;

    TQDomElement macro = KoDom::namedItemNS( e, KoXmlNS::text, "time" );
    if ( !macro.isNull() )
      replaceMacro( text, macro.text(), "<time>" );

    macro = KoDom::namedItemNS( e, KoXmlNS::text, "date" );
    if ( !macro.isNull() )
      replaceMacro( text, macro.text(), "<date>" );

    macro = KoDom::namedItemNS( e, KoXmlNS::text, "page-number" );
    if ( !macro.isNull() )
      replaceMacro( text, macro.text(), "<page>" );

    macro = KoDom::namedItemNS( e, KoXmlNS::text, "page-count" );
    if ( !macro.isNull() )
      replaceMacro( text, macro.text(), "<pages>" );

    macro = KoDom::namedItemNS( e, KoXmlNS::text, "sheet-name" );
    if ( !macro.isNull() )
      replaceMacro( text, macro.text(), "<sheet>" );

    macro = KoDom::namedItemNS( e, KoXmlNS::text, "title" );
    if ( !macro.isNull() )
      replaceMacro( text, macro.text(), "<name>" );

    macro = KoDom::namedItemNS( e, KoXmlNS::text, "file-name" );
    if ( !macro.isNull() )
      replaceMacro( text, macro.text(), "<file>" );

    //add support for multi line into kspread
    if ( !result.isEmpty() )
      result += '\n';
    result += text;
    e = e.nextSibling().toElement();
  }

  return result;
}


bool Sheet::loadOasis( const TQDomElement& sheetElement, KoOasisLoadingContext& oasisContext, TQDict<Style>& styleMap )
{
    d->layoutDirection = LeftToRight;
    if ( sheetElement.hasAttributeNS( KoXmlNS::table, "style-name" ) )
    {
        TQString stylename = sheetElement.attributeNS( KoXmlNS::table, "style-name", TQString() );
        //kdDebug()<<" style of table :"<<stylename<<endl;
        const TQDomElement *style = oasisContext.oasisStyles().findStyle( stylename, "table" );
        Q_ASSERT( style );
        //kdDebug()<<" style :"<<style<<endl;
        if ( style )
        {
            TQDomElement properties( KoDom::namedItemNS( *style, KoXmlNS::style, "table-properties" ) );
            if ( !properties.isNull() )
            {
                if ( properties.hasAttributeNS( KoXmlNS::table, "display" ) )
                {
                    bool visible = (properties.attributeNS( KoXmlNS::table, "display", TQString() ) == "true" ? true : false );
                    d->hide = !visible;
                }
            }
            if ( style->hasAttributeNS( KoXmlNS::style, "master-page-name" ) )
            {
                TQString masterPageStyleName = style->attributeNS( KoXmlNS::style, "master-page-name", TQString() );
                //kdDebug()<<"style->attribute( style:master-page-name ) :"<<masterPageStyleName <<endl;
                TQDomElement *masterStyle = oasisContext.oasisStyles().masterPages()[masterPageStyleName];
                //kdDebug()<<"oasisStyles.styles()[masterPageStyleName] :"<<masterStyle<<endl;
                if ( masterStyle )
                {
                    loadSheetStyleFormat( masterStyle );
                    if ( masterStyle->hasAttributeNS( KoXmlNS::style, "page-layout-name" ) )
                    {
                        TQString masterPageLayoutStyleName = masterStyle->attributeNS( KoXmlNS::style, "page-layout-name", TQString() );
                        //kdDebug()<<"masterPageLayoutStyleName :"<<masterPageLayoutStyleName<<endl;
                        const TQDomElement *masterLayoutStyle = oasisContext.oasisStyles().findStyle( masterPageLayoutStyleName );
                      if ( masterLayoutStyle )
                      {
                        //kdDebug()<<"masterLayoutStyle :"<<masterLayoutStyle<<endl;
                        KoStyleStack styleStack;
                        styleStack.setTypeProperties( "page-layout" );
                        styleStack.push( *masterLayoutStyle );
                        loadOasisMasterLayoutPage( styleStack );
                      }
                    }
                }
            }
        }
    }

    //Maps from a column index to the name of the default cell style for that column
    TQMap<int,TQString> defaultColumnCellStyles;

    int rowIndex = 1;
    int indexCol = 1;
    TQDomNode rowNode = sheetElement.firstChild();
    // Some spreadsheet programs may support more rows than
    // KSpread so limit the number of repeated rows.
    // FIXME POSSIBLE DATA LOSS!
    while( !rowNode.isNull() && rowIndex <= KS_rowMax )
    {
        kdDebug()<<" rowIndex :"<<rowIndex<<" indexCol :"<<indexCol<<endl;
        TQDomElement rowElement = rowNode.toElement();
        if( !rowElement.isNull() )
        {
            kdDebug()<<" Sheet::loadOasis rowElement.tagName() :"<<rowElement.localName()<<endl;
            if ( rowElement.namespaceURI() == KoXmlNS::table )
            {
                if ( rowElement.localName()=="table-column" && indexCol <= KS_colMax )
                {
                    kdDebug ()<<" table-column found : index column before "<< indexCol<<endl;
                    loadColumnFormat( rowElement, oasisContext.oasisStyles(), indexCol , styleMap);
                    kdDebug ()<<" table-column found : index column after "<< indexCol<<endl;
                }
                else if ( rowElement.localName() == "table-header-rows" )
                {
                  TQDomNode headerRowNode = rowElement.firstChild();
                  while ( !headerRowNode.isNull() )
                  {
                    // NOTE Handle header rows as ordinary ones
                    //      as long as they're not supported.
                    loadRowFormat( headerRowNode.toElement(), rowIndex,
                                   oasisContext, /*rowNode.isNull() ,*/ styleMap );
                    headerRowNode = headerRowNode.nextSibling();
                  }
                }
                else if( rowElement.localName() == "table-row" )
                {
                    kdDebug()<<" table-row found :index row before "<<rowIndex<<endl;
                    loadRowFormat( rowElement, rowIndex, oasisContext, /*rowNode.isNull() ,*/ styleMap );
                    kdDebug()<<" table-row found :index row after "<<rowIndex<<endl;
                }
                else if ( rowElement.localName() == "shapes" )
                    loadOasisObjects( rowElement, oasisContext );
            }
        }
        rowNode = rowNode.nextSibling();
    }

    if ( sheetElement.hasAttributeNS( KoXmlNS::table, "print-ranges" ) )
    {
        // e.g.: Sheet4.A1:Sheet4.E28
        TQString range = sheetElement.attributeNS( KoXmlNS::table, "print-ranges", TQString() );
        range = Oasis::decodeFormula( range );
        Range p( range );
        if ( sheetName() == p.sheetName() )
            d->print->setPrintRange( p.range() );
    }


    if ( sheetElement.attributeNS( KoXmlNS::table, "protected", TQString() ) == "true" )
    {
        TQCString passwd( "" );
        if ( sheetElement.hasAttributeNS( KoXmlNS::table, "protection-key" ) )
        {
            TQString p = sheetElement.attributeNS( KoXmlNS::table, "protection-key", TQString() );
            TQCString str( p.latin1() );
            kdDebug(30518) << "Decoding password: " << str << endl;
            passwd = KCodecs::base64Decode( str );
        }
        kdDebug(30518) << "Password hash: '" << passwd << "'" << endl;
        d->password = passwd;
    }
    return true;
}


void Sheet::loadOasisObjects( const TQDomElement &parent, KoOasisLoadingContext& oasisContext )
{
    TQDomElement e;
    TQDomNode n = parent.firstChild();
    while( !n.isNull() )
    {
        e = n.toElement();
        if ( e.localName() == "frame" && e.namespaceURI() == KoXmlNS::draw )
        {
          EmbeddedObject *obj = 0;
          TQDomNode object = KoDom::namedItemNS( e, KoXmlNS::draw, "object" );
          if ( !object.isNull() )
          {
            if ( !object.toElement().attributeNS( KoXmlNS::draw, "notify-on-update-of-ranges", TQString()).isNull() )
                obj = new EmbeddedChart( doc(), this );
            else
                obj = new EmbeddedKOfficeObject( doc(), this );
          }
          else
          {
            TQDomNode image = KoDom::namedItemNS( e, KoXmlNS::draw, "image" );
            if ( !image.isNull() )
              obj = new EmbeddedPictureObject( this, doc()->pictureCollection() );
            else
              kdDebug() << "Object type wasn't loaded!" << endl;
          }

          if ( obj )
          {
            obj->loadOasis( e, oasisContext );
            insertObject( obj );
          }
        }
        n = n.nextSibling();
    }
}


void Sheet::loadOasisMasterLayoutPage( KoStyleStack &styleStack )
{
    // use A4 as default page size
    float left = 20.0;
    float right = 20.0;
    float top = 20.0;
    float bottom = 20.0;
    float width = 210.0;
    float height = 297.0;
    TQString orientation = "Portrait";
    TQString format;

    // Laurent : Why we stored layout information as Millimeter ?!!!!!
    // kspread used point for all other attribute
    // I don't understand :(
    if ( styleStack.hasAttributeNS( KoXmlNS::fo, "page-width" ) )
    {
        width = KoUnit::toMM(KoUnit::parseValue( styleStack.attributeNS( KoXmlNS::fo, "page-width" ) ) );
    }
    if ( styleStack.hasAttributeNS( KoXmlNS::fo, "page-height" ) )
    {
        height = KoUnit::toMM( KoUnit::parseValue( styleStack.attributeNS( KoXmlNS::fo, "page-height" ) ) );
    }
    if ( styleStack.hasAttributeNS( KoXmlNS::fo, "margin-top" ) )
    {
        top = KoUnit::toMM(KoUnit::parseValue( styleStack.attributeNS( KoXmlNS::fo, "margin-top" ) ) );
    }
    if ( styleStack.hasAttributeNS( KoXmlNS::fo, "margin-bottom" ) )
    {
        bottom = KoUnit::toMM(KoUnit::parseValue( styleStack.attributeNS( KoXmlNS::fo, "margin-bottom" ) ) );
    }
    if ( styleStack.hasAttributeNS( KoXmlNS::fo, "margin-left" ) )
    {
        left = KoUnit::toMM(KoUnit::parseValue( styleStack.attributeNS( KoXmlNS::fo, "margin-left" ) ) );
    }
    if ( styleStack.hasAttributeNS( KoXmlNS::fo, "margin-right" ) )
    {
        right = KoUnit::toMM(KoUnit::parseValue( styleStack.attributeNS( KoXmlNS::fo, "margin-right" ) ) );
    }
    if ( styleStack.hasAttributeNS( KoXmlNS::style, "writing-mode" ) )
    {
        kdDebug()<<"styleStack.hasAttribute( style:writing-mode ) :"<<styleStack.hasAttributeNS( KoXmlNS::style, "writing-mode" )<<endl;
        d->layoutDirection = ( styleStack.attributeNS( KoXmlNS::style, "writing-mode" )=="lr-tb" ) ? LeftToRight : RightToLeft;
        //TODO
        //<value>lr-tb</value>
        //<value>rl-tb</value>
        //<value>tb-rl</value>
        //<value>tb-lr</value>
        //<value>lr</value>
        //<value>rl</value>
        //<value>tb</value>
        //<value>page</value>

    }
    if ( styleStack.hasAttributeNS( KoXmlNS::style, "print-orientation" ) )
    {
        orientation = ( styleStack.attributeNS( KoXmlNS::style, "print-orientation" )=="landscape" ) ? "Landscape" : "Portrait" ;
    }
    if ( styleStack.hasAttributeNS( KoXmlNS::style, "num-format" ) )
    {
        //not implemented into kspread
        //These attributes specify the numbering style to use.
        //If a numbering style is not specified, the numbering style is inherited from
        //the page style. See section 6.7.8 for information on these attributes
        kdDebug()<<" num-format :"<<styleStack.attributeNS( KoXmlNS::style, "num-format" )<<endl;

    }
    if ( styleStack.hasAttributeNS( KoXmlNS::fo, "background-color" ) )
    {
        //TODO
        kdDebug()<<" fo:background-color :"<<styleStack.attributeNS( KoXmlNS::fo, "background-color" )<<endl;
    }
    if ( styleStack.hasAttributeNS( KoXmlNS::style, "print" ) )
    {
        //todo parsing
        TQString str = styleStack.attributeNS( KoXmlNS::style, "print" );
        kdDebug()<<" style:print :"<<str<<endl;

        if (str.contains( "headers" ) )
        {
            //TODO implement it into kspread
        }
        if ( str.contains( "grid" ) )
        {
            d->print->setPrintGrid( true );
        }
        if ( str.contains( "annotations" ) )
        {
            //TODO it's not implemented
        }
        if ( str.contains( "objects" ) )
        {
            //TODO it's not implemented
        }
        if ( str.contains( "charts" ) )
        {
            //TODO it's not implemented
        }
        if ( str.contains( "drawings" ) )
        {
            //TODO it's not implemented
        }
        if ( str.contains( "formulas" ) )
        {
            d->showFormula = true;
        }
        if ( str.contains( "zero-values" ) )
        {
            //TODO it's not implemented
        }
    }
    if ( styleStack.hasAttributeNS( KoXmlNS::style, "table-centering" ) )
    {
        TQString str = styleStack.attributeNS( KoXmlNS::style, "table-centering" );
        //TODO not implemented into kspread
        kdDebug()<<" styleStack.attribute( style:table-centering ) :"<<str<<endl;
#if 0
        if ( str == "horizontal" )
        {
        }
        else if ( str == "vertical" )
        {
        }
        else if ( str == "both" )
        {
        }
        else if ( str == "none" )
        {
        }
        else
            kdDebug()<<" table-centering unknown :"<<str<<endl;
#endif
    }
    format = TQString( "%1x%2" ).arg( width ).arg( height );
    kdDebug()<<" format : "<<format<<endl;
    d->print->setPaperLayout( left, top, right, bottom, format, orientation );

    kdDebug()<<" left margin :"<<left<<" right :"<<right<<" top :"<<top<<" bottom :"<<bottom<<endl;
//<style:properties fo:page-width="21.8cm" fo:page-height="28.801cm" fo:margin-top="2cm" fo:margin-bottom="2.799cm" fo:margin-left="1.3cm" fo:margin-right="1.3cm" style:writing-mode="lr-tb"/>
//          TQString format = paper.attribute( "format" );
//      TQString orientation = paper.attribute( "orientation" );
//        d->print->setPaperLayout( left, top, right, bottom, format, orientation );
//      }
}


bool Sheet::loadColumnFormat(const TQDomElement& column, const KoOasisStyles& oasisStyles, int & indexCol, const TQDict<Style>& styleMap)
{
    kdDebug()<<"bool Sheet::loadColumnFormat(const TQDomElement& column, const KoOasisStyles& oasisStyles, unsigned int & indexCol ) index Col :"<<indexCol<<endl;

    bool isNonDefaultColumn = false;

    int number = 1;
    if ( column.hasAttributeNS( KoXmlNS::table, "number-columns-repeated" ) )
    {
        bool ok = true;
        int n = column.attributeNS( KoXmlNS::table, "number-columns-repeated", TQString() ).toInt( &ok );
        if ( ok )
          // Some spreadsheet programs may support more rows than KSpread so
          // limit the number of repeated rows.
          // FIXME POSSIBLE DATA LOSS!
          number = TQMIN( n, KS_colMax - indexCol + 1 );
        kdDebug() << "Repeated: " << number << endl;
    }

    Format layout( this , doc()->styleManager()->defaultStyle() );
    if ( column.hasAttributeNS( KoXmlNS::table, "default-cell-style-name" ) )
    {
      const TQString styleName = column.attributeNS( KoXmlNS::table, "default-cell-style-name", TQString() );
      if ( !styleName.isEmpty() )
      {
        Style* const style = styleMap[ styleName ];
        if ( style )
        {
          layout.setStyle( style );
          isNonDefaultColumn = true;
        }
      }
    }

    bool collapsed = false;
    if ( column.hasAttributeNS( KoXmlNS::table, "visibility" ) )
    {
      const TQString visibility = column.attributeNS( KoXmlNS::table, "visibility", TQString() );
      if ( visibility == "visible" )
        collapsed = false;
      else if ( visibility == "collapse" )
        collapsed = true;
      else if ( visibility == "filter" )
        collapsed = false; // FIXME Stefan: Set to true, if filters are supported.
      isNonDefaultColumn = true;
    }

    KoStyleStack styleStack;
    if ( column.hasAttributeNS( KoXmlNS::table, "style-name" ) )
    {
      TQString str = column.attributeNS( KoXmlNS::table, "style-name", TQString() );
      const TQDomElement *style = oasisStyles.findStyle( str, "table-column" );
      if ( style )
      {
        styleStack.push( *style );
        isNonDefaultColumn = true;
      }
    }
    styleStack.setTypeProperties("table-column"); //style for column

    double width = -1.0;
    if ( styleStack.hasAttributeNS( KoXmlNS::style, "column-width" ) )
    {
        width = KoUnit::parseValue( styleStack.attributeNS( KoXmlNS::style, "column-width" ) , -1.0 );
        kdDebug()<<" style:column-width : width :"<<width<<endl;
        isNonDefaultColumn = true;
    }

    bool insertPageBreak = false;
    if ( styleStack.hasAttributeNS( KoXmlNS::fo, "break-before" ) )
    {
        TQString str = styleStack.attributeNS( KoXmlNS::fo, "break-before" );
        if ( str == "page" )
        {
            insertPageBreak = true;
        }
        else
            kdDebug()<<" str :"<<str<<endl;
        isNonDefaultColumn = true;
    }

    for ( int i = 0; i < number; ++i )
    {
//         kdDebug()<<" insert new column: pos :"<<indexCol<<" width :"<<width<<" hidden ? "<<collapsed<<endl;

      ColumnFormat* columnFormat;
      if ( isNonDefaultColumn )
      {
        columnFormat = nonDefaultColumnFormat( indexCol );

        if ( width != -1.0 ) //safe
            columnFormat->setWidth( (int) width );
        // if ( insertPageBreak )
        //   columnFormat->setPageBreak( true )
        if ( collapsed )
            columnFormat->setHide( true );
      }
      else
      {
        columnFormat = this->columnFormat( indexCol );
      }
      columnFormat->copy( layout );

      ++indexCol;
    }
//     kdDebug()<<" after index column !!!!!!!!!!!!!!!!!! :"<<indexCol<<endl;
    return true;
}


bool Sheet::loadRowFormat( const TQDomElement& row, int &rowIndex, KoOasisLoadingContext& oasisContext,  TQDict<Style>& styleMap )
{
//    kdDebug()<<"Sheet::loadRowFormat( const TQDomElement& row, int &rowIndex,const KoOasisStyles& oasisStyles, bool isLast )***********\n";

    int backupRow = rowIndex;
    bool isNonDefaultRow = false;

    KoStyleStack styleStack;
    if ( row.hasAttributeNS( KoXmlNS::table, "style-name" ) )
    {
      TQString str = row.attributeNS( KoXmlNS::table, "style-name", TQString() );
      const TQDomElement *style = oasisContext.oasisStyles().findStyle( str, "table-row" );
      if ( style )
      {
        styleStack.push( *style );
        isNonDefaultRow = true;
      }
    }
    styleStack.setTypeProperties( "table-row" );

    Format layout( this , doc()->styleManager()->defaultStyle() );
    if ( row.hasAttributeNS( KoXmlNS::table,"default-cell-style-name" ) )
    {
      const TQString styleName = row.attributeNS( KoXmlNS::table, "default-cell-style-name", TQString() );
      if ( !styleName.isEmpty() )
      {
        Style* const style = styleMap[ styleName ];
        if ( style )
        {
          layout.setStyle( style );
          isNonDefaultRow = true;
        }
      }
    }

    double height = -1.0;
    if ( styleStack.hasAttributeNS( KoXmlNS::style, "row-height" ) )
    {
        height = KoUnit::parseValue( styleStack.attributeNS( KoXmlNS::style, "row-height" ) , -1.0 );
    //    kdDebug()<<" properties style:row-height : height :"<<height<<endl;
        isNonDefaultRow = true;
    }

    int number = 1;
    if ( row.hasAttributeNS( KoXmlNS::table, "number-rows-repeated" ) )
    {
        bool ok = true;
        int n = row.attributeNS( KoXmlNS::table, "number-rows-repeated", TQString() ).toInt( &ok );
        if ( ok )
            // Some spreadsheet programs may support more rows than KSpread so
            // limit the number of repeated rows.
            // FIXME POSSIBLE DATA LOSS!
            number = TQMIN( n, KS_rowMax - rowIndex + 1 );
    }

    bool collapse = false;
    if ( row.hasAttributeNS( KoXmlNS::table, "visibility" ) )
    {
      const TQString visibility = row.attributeNS( KoXmlNS::table, "visibility", TQString() );
      if ( visibility == "visible" )
        collapse = false;
      else if ( visibility == "collapse" )
        collapse = true;
      else if ( visibility == "filter" )
        collapse = false; // FIXME Stefan: Set to true, if filters are supported.
      isNonDefaultRow = true;
    }

    bool insertPageBreak = false;
    if ( styleStack.hasAttributeNS( KoXmlNS::fo, "break-before" ) )
    {
        TQString str = styleStack.attributeNS( KoXmlNS::fo, "break-before" );
        if ( str == "page" )
        {
            insertPageBreak = true;
        }
      //  else
      //      kdDebug()<<" str :"<<str<<endl;
        isNonDefaultRow = true;
    }

    //number == number of row to be copy. But we must copy cell too.
    for ( int i = 0; i < number; ++i )
    {
       // kdDebug()<<" create non defaultrow format :"<<rowIndex<<" repeate : "<<number<<" height :"<<height<<endl;

      RowFormat* rowFormat;
      if ( isNonDefaultRow )
      {
        rowFormat = nonDefaultRowFormat( rowIndex );

        if ( height != -1.0 )
            rowFormat->setHeight( (int) height );
        if ( collapse )
            rowFormat->setHide( true );
      }
      else
      {
        rowFormat = this->rowFormat( rowIndex );
      }
      rowFormat->copy( layout );

      ++rowIndex;
    }

    int columnIndex = 0;
    TQDomNode cellNode = row.firstChild();
    int endRow = min(backupRow+number,KS_rowMax);


    while ( !cellNode.isNull() )
    {
        TQDomElement cellElement = cellNode.toElement();
        if ( !cellElement.isNull() )
        {
            columnIndex++;
            TQString localName = cellElement.localName();

            if ( ((localName == "table-cell") || (localName == "covered-table-cell")) && cellElement.namespaceURI() == KoXmlNS::table)
            {
                //kdDebug() << "Loading cell #" << cellCount << endl;

                Style* style = 0;
                const bool cellHasStyle = cellElement.hasAttributeNS( KoXmlNS::table, "style-name" );
                if ( cellHasStyle )
                {
                    style = styleMap[ cellElement.attributeNS( KoXmlNS::table , "style-name" , TQString() ) ];
                }

                Cell* const cell = nonDefaultCell( columnIndex, backupRow ); // FIXME Stefan: if empty, delete afterwards
                cell->loadOasis( cellElement, oasisContext, style );

                int cols = 1;

                // Copy this cell across & down, if it has repeated rows or columns, but only
                // if the cell has some content or a style associated with it.
                if ( (number > 1) || cellElement.hasAttributeNS( KoXmlNS::table, "number-columns-repeated" ) )
                {
                    bool ok = false;
                    int n = cellElement.attributeNS( KoXmlNS::table, "number-columns-repeated", TQString() ).toInt( &ok );

                    if (ok)
                        // Some spreadsheet programs may support more columns than
                        // KSpread so limit the number of repeated columns.
                        // FIXME POSSIBLE DATA LOSS!
                        cols = TQMIN( n, KS_colMax - columnIndex + 1 );

                    if ( !cellHasStyle && ( cell->isEmpty() && cell->format()->comment( columnIndex, backupRow ).isEmpty() ) )
                    {
                        // just increment it
                        columnIndex += cols - 1;
                    }
                    else
                    {
                        for ( int k = cols ; k ; --k )
                        {
                            if ( k != cols )
                                columnIndex++;

                            for ( int newRow = backupRow; newRow < endRow; ++newRow )
                            {
                                Cell* target = nonDefaultCell( columnIndex, newRow );

                                if (cell != target)
                                    target->copyAll( cell );
                            }
                        }
                    }
                }
            }
        }
        cellNode = cellNode.nextSibling();
    }

    return true;
}

void Sheet::maxRowCols( int & maxCols, int & maxRows )
{
  const Cell * cell = firstCell();
  while ( cell )
  {
    if ( cell->column() > maxCols )
      maxCols = cell->column();

    if ( cell->row() > maxRows )
      maxRows = cell->row();

    cell = cell->nextCell();
  }

  const RowFormat * row = firstRow();
  while ( row )
  {
    if ( row->row() > maxRows )
      maxRows = row->row();

    row = row->next();
  }
  const ColumnFormat* col = firstCol();
  while ( col )
  {
    if ( col->column() > maxCols )
      maxCols = col->column();

    col = col->next();
  }
}


bool Sheet::compareRows( int row1, int row2, int& maxCols ) const
{
  if ( *rowFormat( row1 ) != *rowFormat( row2 ) )
  {
//     kdDebug() << "\t Formats of " << row1 << " and " << row2 << " are different" << endl;
    return false;
  }
  // FIXME Stefan: Make use of the cluster functionality.
  for ( int col = 1; col <= maxCols; ++col )
  {
    if ( *cellAt( col, row1 ) != *cellAt( col, row2 ) )
    {
//       kdDebug() << "\t Cell at column " << col << " in row " << row2 << " differs from the one in row " << row1 << endl;
      return false;
    }
  }
  return true;
}


void Sheet::saveOasisHeaderFooter( KoXmlWriter &xmlWriter ) const
{
    TQString headerLeft = print()->headLeft();
    TQString headerCenter= print()->headMid();
    TQString headerRight = print()->headRight();

    TQString footerLeft = print()->footLeft();
    TQString footerCenter= print()->footMid();
    TQString footerRight = print()->footRight();

    xmlWriter.startElement( "style:header");
    if ( ( !headerLeft.isEmpty() )
         || ( !headerCenter.isEmpty() )
         || ( !headerRight.isEmpty() ) )
    {
        xmlWriter.startElement( "style:region-left" );
        xmlWriter.startElement( "text:p" );
        convertPart( headerLeft, xmlWriter );
        xmlWriter.endElement();
        xmlWriter.endElement();

        xmlWriter.startElement( "style:region-center" );
        xmlWriter.startElement( "text:p" );
        convertPart( headerCenter, xmlWriter );
        xmlWriter.endElement();
        xmlWriter.endElement();

        xmlWriter.startElement( "style:region-right" );
        xmlWriter.startElement( "text:p" );
        convertPart( headerRight, xmlWriter );
        xmlWriter.endElement();
        xmlWriter.endElement();
    }
    else
    {
       xmlWriter.startElement( "text:p" );

       xmlWriter.startElement( "text:sheet-name" );
       xmlWriter.addTextNode( "???" );
       xmlWriter.endElement();

       xmlWriter.endElement();
    }
    xmlWriter.endElement();


    xmlWriter.startElement( "style:footer");
    if ( ( !footerLeft.isEmpty() )
         || ( !footerCenter.isEmpty() )
         || ( !footerRight.isEmpty() ) )
    {
        xmlWriter.startElement( "style:region-left" );
        xmlWriter.startElement( "text:p" );
        convertPart( footerLeft, xmlWriter );
        xmlWriter.endElement();
        xmlWriter.endElement(); //style:region-left

        xmlWriter.startElement( "style:region-center" );
        xmlWriter.startElement( "text:p" );
        convertPart( footerCenter, xmlWriter );
        xmlWriter.endElement();
        xmlWriter.endElement();

        xmlWriter.startElement( "style:region-right" );
        xmlWriter.startElement( "text:p" );
        convertPart( footerRight, xmlWriter );
        xmlWriter.endElement();
        xmlWriter.endElement();
    }
    else
    {
       xmlWriter.startElement( "text:p" );

       xmlWriter.startElement( "text:sheet-name" );
       xmlWriter.addTextNode( "Page " ); // ???
       xmlWriter.endElement();

       xmlWriter.startElement( "text:page-number" );
       xmlWriter.addTextNode( "1" ); // ???
       xmlWriter.endElement();

       xmlWriter.endElement();
    }
    xmlWriter.endElement();


}

void Sheet::addText( const TQString & text, KoXmlWriter & writer ) const
{
    if ( !text.isEmpty() )
        writer.addTextNode( text );
}

void Sheet::convertPart( const TQString & part, KoXmlWriter & xmlWriter ) const
{
    TQString text;
    TQString var;

    bool inVar = false;
    uint i = 0;
    uint l = part.length();
    while ( i < l )
    {
        if ( inVar || part[i] == '<' )
        {
            inVar = true;
            var += part[i];
            if ( part[i] == '>' )
            {
                inVar = false;
                if ( var == "<page>" )
                {
                    addText( text, xmlWriter );
                    xmlWriter.startElement( "text:page-number" );
                    xmlWriter.addTextNode( "1" );
                    xmlWriter.endElement();
                }
                else if ( var == "<pages>" )
                {
                    addText( text, xmlWriter );
                    xmlWriter.startElement( "text:page-count" );
                    xmlWriter.addTextNode( "99" ); //TODO I think that it can be different from 99
                    xmlWriter.endElement();
                }
                else if ( var == "<date>" )
                {
                    addText( text, xmlWriter );
                    //text:p><text:date style:data-style-name="N2" text:date-value="2005-10-02">02/10/2005</text:date>, <text:time>10:20:12</text:time></text:p> "add style" => create new style
#if 0 //FIXME
                    TQDomElement t = dd.createElement( "text:date" );
                    t.setAttribute( "text:date-value", "0-00-00" );
                    // todo: "style:data-style-name", "N2"
                    t.appendChild( dd.createTextNode( TQDate::currentDate().toString() ) );
                    parent.appendChild( t );
#endif
                }
                else if ( var == "<time>" )
                {
                    addText( text, xmlWriter );

                    xmlWriter.startElement( "text:time" );
                    xmlWriter.addTextNode( TQTime::currentTime().toString() );
                    xmlWriter.endElement();
                }
                else if ( var == "<file>" ) // filepath + name
                {
                    addText( text, xmlWriter );
                    xmlWriter.startElement( "text:file-name" );
                    xmlWriter.addAttribute( "text:display", "full" );
                    xmlWriter.addTextNode( "???" );
                    xmlWriter.endElement();
                }
                else if ( var == "<name>" ) // filename
                {
                    addText( text, xmlWriter );

                    xmlWriter.startElement( "text:title" );
                    xmlWriter.addTextNode( "???" );
                    xmlWriter.endElement();
                }
                else if ( var == "<author>" )
                {
                    Doc* sdoc = d->workbook->doc();
                    KoDocumentInfo       * docInfo    = sdoc->documentInfo();
                    KoDocumentInfoAuthor * authorPage = static_cast<KoDocumentInfoAuthor*>( docInfo->page( "author" ) );

                    text += authorPage->fullName();

                    addText( text, xmlWriter );
                }
                else if ( var == "<email>" )
                {
                    Doc* sdoc = d->workbook->doc();
                    KoDocumentInfo       * docInfo    = sdoc->documentInfo();
                    KoDocumentInfoAuthor * authorPage = static_cast<KoDocumentInfoAuthor*>( docInfo->page( "author" ) );

                    text += authorPage->email();
                    addText( text, xmlWriter );

                }
                else if ( var == "<org>" )
                {
                    Doc* sdoc = d->workbook->doc();
                    KoDocumentInfo       * docInfo    = sdoc->documentInfo();
                    KoDocumentInfoAuthor * authorPage = static_cast<KoDocumentInfoAuthor*>( docInfo->page( "author" ) );

                    text += authorPage->company();
                    addText( text, xmlWriter );

                }
                else if ( var == "<sheet>" )
                {
                    addText( text, xmlWriter );

                    xmlWriter.startElement( "text:sheet-name" );
                    xmlWriter.addTextNode( "???" );
                    xmlWriter.endElement();
                }
                else
                {
                    // no known variable:
                    text += var;
                    addText( text, xmlWriter );
                }

                text = "";
                var  = "";
            }
        }
        else
        {
            text += part[i];
        }
        ++i;
    }
    if ( !text.isEmpty() || !var.isEmpty() )
    {
        //we don't have var at the end =>store it
        addText( text+var, xmlWriter );
    }
    kdDebug()<<" text end :"<<text<<" var :"<<var<<endl;
}


void Sheet::loadOasisSettings( const KoOasisSettings::NamedMap &settings )
{
    // Find the entry in the map that applies to this sheet (by name)
    KoOasisSettings::Items items = settings.entry( d->name );
    if ( items.isNull() )
        return;
    d->hideZero = items.parseConfigItemBool( "ShowZeroValues" );
    d->showGrid = items.parseConfigItemBool( "ShowGrid" );
    d->firstLetterUpper = items.parseConfigItemBool( "FirstLetterUpper" );

    int cursorX = items.parseConfigItemInt( "CursorPositionX" );
    int cursorY = items.parseConfigItemInt( "CursorPositionY" );
    doc()->loadingInfo()->setCursorPosition( this, TQPoint( cursorX, cursorY ) );

    double offsetX = items.parseConfigItemDouble( "xOffset" );
    double offsetY = items.parseConfigItemDouble( "yOffset" );
    doc()->loadingInfo()->setScrollingOffset( this, KoPoint( offsetX, offsetY ) );

    d->showFormulaIndicator = items.parseConfigItemBool( "ShowFormulaIndicator" );
    d->showCommentIndicator = items.parseConfigItemBool( "ShowCommentIndicator" );
    d->showPageBorders = items.parseConfigItemBool( "ShowPageBorders" );
    d->lcMode = items.parseConfigItemBool( "lcmode" );
    d->autoCalc = items.parseConfigItemBool( "autoCalc" );
    d->showColumnNumber = items.parseConfigItemBool( "ShowColumnNumber" );
}

void Sheet::saveOasisSettings( KoXmlWriter &settingsWriter ) const
{
    //not into each page into oo spec
    settingsWriter.addConfigItem( "ShowZeroValues", d->hideZero );
    settingsWriter.addConfigItem( "ShowGrid", d->showGrid );
    //not define into oo spec
    settingsWriter.addConfigItem( "FirstLetterUpper", d->firstLetterUpper);
    settingsWriter.addConfigItem( "ShowFormulaIndicator", d->showFormulaIndicator );
    settingsWriter.addConfigItem( "ShowCommentIndicator", d->showCommentIndicator );
    settingsWriter.addConfigItem( "ShowPageBorders",d->showPageBorders );
    settingsWriter.addConfigItem( "lcmode", d->lcMode );
    settingsWriter.addConfigItem( "autoCalc", d->autoCalc );
    settingsWriter.addConfigItem( "ShowColumnNumber", d->showColumnNumber );
}

bool Sheet::saveOasis( KoXmlWriter & xmlWriter, KoGenStyles &mainStyles, GenValidationStyles &valStyle, KoStore *store, KoXmlWriter* /*manifestWriter*/, int &indexObj, int &partIndexObj )
{
    int maxCols= 1;
    int maxRows= 1;
    xmlWriter.startElement( "table:table" );
    xmlWriter.addAttribute( "table:name", d->name );
    xmlWriter.addAttribute( "table:style-name", saveOasisSheetStyleName(mainStyles )  );
    if ( !d->password.isEmpty() )
    {
        xmlWriter.addAttribute("table:protected", "true" );
        TQCString str = KCodecs::base64Encode( d->password );
        xmlWriter.addAttribute("table:protection-key", TQString( str.data() ) );/* FIXME !!!!*/
    }
    TQRect _printRange = d->print->printRange();
    if ( _printRange != ( TQRect( TQPoint( 1, 1 ), TQPoint( KS_colMax, KS_rowMax ) ) ) )
    {
        TQString range= convertRangeToRef( d->name, _printRange );
        kdDebug()<<" range : "<<range<<endl;
        xmlWriter.addAttribute( "table:print-ranges", range );
    }

    saveOasisObjects( store, xmlWriter, mainStyles, indexObj, partIndexObj );
    maxRowCols( maxCols, maxRows );
    saveOasisColRowCell( xmlWriter, mainStyles, maxCols, maxRows, valStyle );
    xmlWriter.endElement();
    return true;
}

void Sheet::saveOasisPrintStyleLayout( KoGenStyle &style ) const
{
    TQString printParameter;
    if ( d->print->printGrid() )
        printParameter="grid ";
    if ( d->print->printObjects() )
      printParameter+="objects ";
    if ( d->print->printCharts() )
      printParameter+="charts ";
    if ( d->showFormula )
        printParameter+="formulas ";
    if ( !printParameter.isEmpty() )
    {
        printParameter+="drawings zero-values"; //default print style attributes in OO
        style.addProperty( "style:print", printParameter );
    }
}

TQString Sheet::saveOasisSheetStyleName( KoGenStyles &mainStyles )
{
    KoGenStyle pageStyle( Doc::STYLE_PAGE, "table"/*FIXME I don't know if name is sheet*/ );

    KoGenStyle pageMaster( Doc::STYLE_PAGEMASTER );
    pageMaster.addAttribute( "style:page-layout-name", d->print->saveOasisSheetStyleLayout( mainStyles ) );

    TQBuffer buffer;
    buffer.open( IO_WriteOnly );
    KoXmlWriter elementWriter( &buffer );  // TODO pass indentation level
    saveOasisHeaderFooter(elementWriter);

    TQString elementContents = TQString::fromUtf8( buffer.buffer(), buffer.buffer().size() );
    pageMaster.addChildElement( "headerfooter", elementContents );
    pageStyle.addAttribute( "style:master-page-name", mainStyles.lookup( pageMaster, "Standard" ) );

    pageStyle.addProperty( "table:display", !d->hide );
    return mainStyles.lookup( pageStyle, "ta" );
}


void Sheet::saveOasisColRowCell( KoXmlWriter& xmlWriter, KoGenStyles &mainStyles,
                                 int maxCols, int maxRows, GenValidationStyles &valStyle )
{
    kdDebug() << "Sheet::saveOasisColRowCell: " << d->name << endl;
    kdDebug() << "\t Sheet dimension: " << maxCols << " x " << maxRows << endl;

    // saving the columns
    //
    int i = 1;
    while ( i <= maxCols )
    {
//         kdDebug() << "Sheet::saveOasisColRowCell: first col loop:"
//                   << " i: " << i
//                   << " column: " << column->column() << endl;
        ColumnFormat* column = columnFormat( i );

        KoGenStyle currentColumnStyle( Doc::STYLE_COLUMN_AUTO, "table-column" );
        currentColumnStyle.addPropertyPt( "style:column-width", column->dblWidth() );
        currentColumnStyle.addProperty( "fo:break-before", "auto" );/*FIXME auto or not ?*/

        //style default layout for column
        KoGenStyle currentDefaultCellStyle; // the type is determined in saveOasisCellStyle
        TQString currentDefaultCellStyleName = column->saveOasisCellStyle( currentDefaultCellStyle, mainStyles );


        bool hide = column->isHide();
        bool refColumnIsDefault = column->isDefault();
        int j = i;
        int repeated = 1;

        while ( j <= maxCols )
        {
//           kdDebug() << "Sheet::saveOasisColRowCell: second col loop:"
//                     << " j: " << j
//                     << " column: " << nextColumn->column() << endl;
          ColumnFormat* nextColumn = d->columns.next( j++ );

          // no next or not the adjacent column?
          if ( !nextColumn || nextColumn->column() != j )
          {
            if ( refColumnIsDefault )
            {
              // if the origin column was a default column,
              // we count the default columns
              if ( !nextColumn )
              {
                repeated = maxCols - i + 1;
              }
              else
              {
                repeated = nextColumn->column() - j + 1;
              }
            }
            // otherwise we just stop here to process the adjacent
            // column in the next iteration of the outer loop
            break;
          }
#if 0
          KoGenStyle nextColumnStyle( Doc::STYLE_COLUMN_AUTO, "table-column" );
          nextColumnStyle.addPropertyPt( "style:column-width", nextColumn->dblWidth() );/*FIXME pt and not mm */
          nextColumnStyle.addProperty( "fo:break-before", "auto" );/*FIXME auto or not ?*/

          KoGenStyle nextDefaultCellStyle; // the type is determined in saveOasisCellStyle
          TQString nextDefaultCellStyleName = nextColumn->saveOasisCellStyle( nextDefaultCellStyle, mainStyles );

          if ( hide != nextColumn->isHide() ||
               nextDefaultCellStyleName != currentDefaultCellStyleName ||
               !( nextColumnStyle == currentColumnStyle ) )
          {
            break;
          }
#endif
          if ( *column != *nextColumn )
          {
            break;
          }
          ++repeated;
        }
        xmlWriter.startElement( "table:table-column" );
        if ( !column->isDefault() )
        {
          xmlWriter.addAttribute( "table:style-name", mainStyles.lookup( currentColumnStyle, "co" ) );

          if ( !currentDefaultCellStyle.isDefaultStyle() )
              xmlWriter.addAttribute( "table:default-cell-style-name", currentDefaultCellStyleName );

          if ( hide )
              xmlWriter.addAttribute( "table:visibility", "collapse" );
        }

        if ( repeated > 1 )
            xmlWriter.addAttribute( "table:number-columns-repeated", repeated  );

        xmlWriter.endElement();

        kdDebug() << "Sheet::saveOasisColRowCell: column " << i << " "
                  << "repeated " << repeated << " time(s)" << endl;
        i += repeated;
    }


    // saving the rows and the cells
    // we have to loop through all rows of the used area
    for ( i = 1; i <= maxRows; ++i )
    {
        RowFormat* const row = rowFormat( i );

        KoGenStyle currentRowStyle( Doc::STYLE_ROW_AUTO, "table-row" );
        currentRowStyle.addPropertyPt( "style:row-height", row->dblHeight() );
        currentRowStyle.addProperty( "fo:break-before", "auto" );/*FIXME auto or not ?*/

        // default cell style for row
        KoGenStyle currentDefaultCellStyle; // the type is determined in saveOasisCellStyle
        TQString currentDefaultCellStyleName = row->saveOasisCellStyle( currentDefaultCellStyle, mainStyles );

        xmlWriter.startElement( "table:table-row" );

        if ( !row->isDefault() )
        {
          xmlWriter.addAttribute( "table:style-name", mainStyles.lookup( currentRowStyle, "ro" ) );
        }
        int repeated = 1;
        // empty row?
        if ( !getFirstCellRow( i ) )
        {
//               kDebug() << "Sheet::saveOasisColRowCell: first row loop:"
//                         << " i: " << i
//                         << " row: " << row->row() << endl;
            //bool isHidden = row->isHide();
            bool isDefault = row->isDefault();
            int j = i + 1;

            // search for
            //   next non-empty row
            // or
            //   next row with different Format
            while ( j <= maxRows && !getFirstCellRow( j ) )
            {
              RowFormat* const nextRow = rowFormat( j );
//               kDebug() << "Sheet::saveOasisColRowCell: second row loop:"
//                         << " j: " << j
//                         << " row: " << nextRow->row() << endl;

              // if the reference row has the default row format
              if ( isDefault )
              {
                // if the next is not default, stop here
                if ( !nextRow->isDefault() )
                  break;
                // otherwise, jump to the next
                ++j;
                continue;
              }
#if 0
              // create the Oasis representation of the format for the comparison
              KoGenStyle nextRowStyle( Doc::STYLE_ROW_AUTO, "table-row" );
              nextRowStyle.addPropertyPt( "style:row-height", nextRow->dblHeight() );
              nextRowStyle.addProperty( "fo:break-before", "auto" );/*FIXME auto or not ?*/

              // default cell style name for next row
              KoGenStyle nextDefaultCellStyle; // the type is determined in saveOasisCellStyle
              TQString nextDefaultCellStyleName = nextRow->saveOasisCellStyle( nextDefaultCellStyle, mainStyles );

              // if the formats differ, stop here
              if ( isHidden != nextRow->isHide() ||
                   nextDefaultCellStyleName != currentDefaultCellStyleName ||
                   !(nextRowStyle == currentRowStyle) )
              {
                break;
              }
#endif
              if ( *row != *nextRow )
              {
                break;
              }
              // otherwise, process the next
              ++j;
            }
            repeated = j - i;

            if ( repeated > 1 )
                xmlWriter.addAttribute( "table:number-rows-repeated", repeated  );
            if ( !currentDefaultCellStyle.isDefaultStyle() )
              xmlWriter.addAttribute( "table:default-cell-style-name", currentDefaultCellStyleName );
            if ( row->isHide() ) // never true for the default row
              xmlWriter.addAttribute( "table:visibility", "collapse" );

            // NOTE Stefan: Even if paragraph 8.1 states, that rows may be empty, the
            //              RelaxNG schema does not allow that.
            xmlWriter.startElement( "table:table-cell" );
            xmlWriter.endElement();

            kdDebug() << "Sheet::saveOasisColRowCell: empty row " << i << ' '
                      << "repeated " << repeated << " time(s)" << endl;

            // copy the index for the next row to process
            i = j - 1; /*it's already incremented in the for loop*/
        }
        else // row is not empty
        {
            if ( !currentDefaultCellStyle.isDefaultStyle() )
              xmlWriter.addAttribute( "table:default-cell-style-name", currentDefaultCellStyleName );
            if ( row->isHide() ) // never true for the default row
              xmlWriter.addAttribute( "table:visibility", "collapse" );

            int j = i + 1;
            while ( compareRows( i, j, maxCols ) && j <= maxRows )
            {
              j++;
              repeated++;
            }
            if ( repeated > 1 )
            {
              kdDebug() << "Sheet::saveOasisColRowCell: NON-empty row " << i << ' '
                        << "repeated " << repeated << " times" << endl;

              xmlWriter.addAttribute( "table:number-rows-repeated", repeated  );
            }

            saveOasisCells( xmlWriter, mainStyles, i, maxCols, valStyle );

            // copy the index for the next row to process
            i = j - 1; /*it's already incremented in the for loop*/
        }
        xmlWriter.endElement();
    }
}

void Sheet::saveOasisCells( KoXmlWriter& xmlWriter, KoGenStyles &mainStyles, int row, int maxCols, GenValidationStyles &valStyle )
{
    int i = 1;
    Cell* cell = cellAt( i, row );
    Cell* nextCell = getNextCellRight( i, row );
    // while
    //   the current cell is not a default one
    // or
    //   we have a further cell in this row
    while ( !cell->isDefault() || nextCell )
    {
        kdDebug() << "Sheet::saveOasisCells:"
                  << " i: " << i
                  << " column: " << (cell->isDefault() ? 0 : cell->column()) << endl;
        int repeated = 1;
        cell->saveOasis( xmlWriter, mainStyles, row, i, repeated, valStyle );
        i += repeated;
        // stop if we reached the end column
        if ( i > maxCols )
          break;
        cell = cellAt( i, row );
        nextCell = getNextCellRight( i, row );
    }
}

bool Sheet::loadXML( const TQDomElement& sheet )
{
    bool ok = false;
    if ( !doc()->loadingInfo() ||  !doc()->loadingInfo()->loadTemplate() )
    {
        d->name = sheet.attribute( "name" );
        if ( d->name.isEmpty() )
        {
            doc()->setErrorMessage( i18n("Invalid document. Sheet name is empty.") );
            return false;
        }
    }

    bool detectDirection = true;
    d->layoutDirection = LeftToRight;
    TQString layoutDir = sheet.attribute( "layoutDirection" );
    if( !layoutDir.isEmpty() )
    {
        if( layoutDir == "rtl" )
        {
           detectDirection = false;
           d->layoutDirection = RightToLeft;
        }
        else if( layoutDir == "ltr" )
        {
           detectDirection = false;
           d->layoutDirection = LeftToRight;
        }
        else
            kdDebug()<<" Direction not implemented : "<<layoutDir<<endl;
    }
    if( detectDirection )
       checkContentDirection( d->name );

    /* older versions of KSpread allowed all sorts of characters that
       the parser won't actually understand.  Replace these with '_'
       Also, the initial character cannot be a space.
    */
    if (d->name[0] == ' ')
    {
      d->name.remove(0,1);
    }
    for (unsigned int i=0; i < d->name.length(); i++)
    {
      if ( !(d->name[i].isLetterOrNumber() ||
             d->name[i] == ' ' || d->name[i] == '.' ||
             d->name[i] == '_'))
        {
        d->name[i] = '_';
      }
    }

    /* make sure there are no name collisions with the altered name */
    TQString testName;
    TQString baseName;
    int nameSuffix = 0;

    testName = d->name;
    baseName = d->name;

    /* so we don't panic over finding ourself in the follwing test*/
    d->name = "";
    while (workbook()->findSheet(testName) != NULL)
    {
      nameSuffix++;
      testName = baseName + '_' + TQString::number(nameSuffix);
    }
    d->name = testName;

    kdDebug(36001)<<"Sheet::loadXML: table name="<<d->name<<endl;
    setName(d->name.utf8());
    (dynamic_cast<SheetIface*>(dcopObject()))->sheetNameHasChanged();

    if( sheet.hasAttribute( "grid" ) )
    {
        d->showGrid = (int)sheet.attribute("grid").toInt( &ok );
        // we just ignore 'ok' - if it didn't work, go on
    }
    if( sheet.hasAttribute( "printGrid" ) )
    {
        d->print->setPrintGrid( (bool)sheet.attribute("printGrid").toInt( &ok ) );
        // we just ignore 'ok' - if it didn't work, go on
    }
    if( sheet.hasAttribute( "printCommentIndicator" ) )
    {
        d->print->setPrintCommentIndicator( (bool)sheet.attribute("printCommentIndicator").toInt( &ok ) );
        // we just ignore 'ok' - if it didn't work, go on
    }
    if( sheet.hasAttribute( "printFormulaIndicator" ) )
    {
        d->print->setPrintFormulaIndicator( (bool)sheet.attribute("printFormulaIndicator").toInt( &ok ) );
        // we just ignore 'ok' - if it didn't work, go on
    }
    if( sheet.hasAttribute( "hide" ) )
    {
        d->hide = (bool)sheet.attribute("hide").toInt( &ok );
        // we just ignore 'ok' - if it didn't work, go on
    }
    if( sheet.hasAttribute( "showFormula" ) )
    {
        d->showFormula = (bool)sheet.attribute("showFormula").toInt( &ok );
        // we just ignore 'ok' - if it didn't work, go on
    }
    //Compatibility with KSpread 1.1.x
    if( sheet.hasAttribute( "formular" ) )
    {
        d->showFormula = (bool)sheet.attribute("formular").toInt( &ok );
        // we just ignore 'ok' - if it didn't work, go on
    }
    if( sheet.hasAttribute( "showFormulaIndicator" ) )
    {
        d->showFormulaIndicator = (bool)sheet.attribute("showFormulaIndicator").toInt( &ok );
        // we just ignore 'ok' - if it didn't work, go on
    }
    if( sheet.hasAttribute( "showCommentIndicator" ) )
    {
        d->showCommentIndicator = (bool)sheet.attribute("showCommentIndicator").toInt( &ok );
        // we just ignore 'ok' - if it didn't work, go on
    }
    if( sheet.hasAttribute( "borders" ) )
    {
        d->showPageBorders = (bool)sheet.attribute("borders").toInt( &ok );
        // we just ignore 'ok' - if it didn't work, go on
    }
    if( sheet.hasAttribute( "lcmode" ) )
    {
        d->lcMode = (bool)sheet.attribute("lcmode").toInt( &ok );
        // we just ignore 'ok' - if it didn't work, go on
    }
    if ( sheet.hasAttribute( "autoCalc" ) )
    {
        d->autoCalc = ( bool )sheet.attribute( "autoCalc" ).toInt( &ok );
        // we just ignore 'ok' - if it didn't work, go on
    }
    if( sheet.hasAttribute( "columnnumber" ) )
    {
        d->showColumnNumber = (bool)sheet.attribute("columnnumber").toInt( &ok );
        // we just ignore 'ok' - if it didn't work, go on
    }
    if( sheet.hasAttribute( "hidezero" ) )
    {
        d->hideZero = (bool)sheet.attribute("hidezero").toInt( &ok );
        // we just ignore 'ok' - if it didn't work, go on
    }
    if( sheet.hasAttribute( "firstletterupper" ) )
    {
        d->firstLetterUpper = (bool)sheet.attribute("firstletterupper").toInt( &ok );
        // we just ignore 'ok' - if it didn't work, go on
    }

    // Load the paper layout
    TQDomElement paper = sheet.namedItem( "paper" ).toElement();
    if ( !paper.isNull() )
    {
      TQString format = paper.attribute( "format" );
      TQString orientation = paper.attribute( "orientation" );

      // <borders>
      TQDomElement borders = paper.namedItem( "borders" ).toElement();
      if ( !borders.isNull() )
      {
        float left = borders.attribute( "left" ).toFloat();
        float right = borders.attribute( "right" ).toFloat();
        float top = borders.attribute( "top" ).toFloat();
        float bottom = borders.attribute( "bottom" ).toFloat();
        d->print->setPaperLayout( left, top, right, bottom, format, orientation );
      }
      TQString hleft, hright, hcenter;
      TQString fleft, fright, fcenter;
      // <head>
      TQDomElement head = paper.namedItem( "head" ).toElement();
      if ( !head.isNull() )
      {
        TQDomElement left = head.namedItem( "left" ).toElement();
        if ( !left.isNull() )
          hleft = left.text();
        TQDomElement center = head.namedItem( "center" ).toElement();
        if ( !center.isNull() )
        hcenter = center.text();
        TQDomElement right = head.namedItem( "right" ).toElement();
        if ( !right.isNull() )
          hright = right.text();
      }
      // <foot>
      TQDomElement foot = paper.namedItem( "foot" ).toElement();
      if ( !foot.isNull() )
      {
        TQDomElement left = foot.namedItem( "left" ).toElement();
        if ( !left.isNull() )
          fleft = left.text();
        TQDomElement center = foot.namedItem( "center" ).toElement();
        if ( !center.isNull() )
          fcenter = center.text();
        TQDomElement right = foot.namedItem( "right" ).toElement();
        if ( !right.isNull() )
          fright = right.text();
      }
      d->print->setHeadFootLine( hleft, hcenter, hright, fleft, fcenter, fright);
    }

      // load print range
      TQDomElement printrange = sheet.namedItem( "printrange-rect" ).toElement();
      if ( !printrange.isNull() )
      {
        int left = printrange.attribute( "left-rect" ).toInt();
        int right = printrange.attribute( "right-rect" ).toInt();
        int bottom = printrange.attribute( "bottom-rect" ).toInt();
        int top = printrange.attribute( "top-rect" ).toInt();
        if ( left == 0 ) //whole row(s) selected
        {
          left = 1;
          right = KS_colMax;
        }
        if ( top == 0 ) //whole column(s) selected
        {
          top = 1;
          bottom = KS_rowMax;
        }
        d->print->setPrintRange( TQRect( TQPoint( left, top ), TQPoint( right, bottom ) ) );
      }

      // load print zoom
      if( sheet.hasAttribute( "printZoom" ) )
      {
        double zoom = sheet.attribute( "printZoom" ).toDouble( &ok );
        if ( ok )
        {
          d->print->setZoom( zoom );
        }
      }

      // load page limits
      if( sheet.hasAttribute( "printPageLimitX" ) )
      {
        int pageLimit = sheet.attribute( "printPageLimitX" ).toInt( &ok );
        if ( ok )
        {
          d->print->setPageLimitX( pageLimit );
        }
      }

      // load page limits
      if( sheet.hasAttribute( "printPageLimitY" ) )
      {
        int pageLimit = sheet.attribute( "printPageLimitY" ).toInt( &ok );
        if ( ok )
        {
          d->print->setPageLimitY( pageLimit );
        }
      }

    // Load the cells
    TQDomNode n = sheet.firstChild();
    while( !n.isNull() )
    {
        TQDomElement e = n.toElement();
        if ( !e.isNull() )
        {
            TQString tagName=e.tagName();
            if ( tagName == "cell" )
        {
            Cell *cell = new Cell( this, 0, 0 );
            if ( cell->load( e, 0, 0 ) )
                insertCell( cell );
            else
                delete cell; // Allow error handling: just skip invalid cells
        }
            else if ( tagName == "row" )
        {
            RowFormat *rl = new RowFormat( this, 0 );
            if ( rl->load( e ) )
                insertRowFormat( rl );
            else
                delete rl;
        }
            else if ( tagName == "column" )
        {
            ColumnFormat *cl = new ColumnFormat( this, 0 );
            if ( cl->load( e ) )
                insertColumnFormat( cl );
            else
                delete cl;
        }
            else if ( tagName == "object" )
        {
            EmbeddedKOfficeObject *ch = new EmbeddedKOfficeObject( doc(), this );
            if ( ch->load( e ) )
                insertObject( ch );
            else
            {
                ch->embeddedObject()->setDeleted(true);
                delete ch;
            }
        }
            else if ( tagName == "chart" )
        {
          EmbeddedChart *ch = new EmbeddedChart( doc(), this );
          if ( ch->load( e ) )
                insertObject( ch );
          else
          {
            ch->embeddedObject()->setDeleted(true);
            delete ch;
          }
        }
        }

        n = n.nextSibling();
    }


    // load print repeat columns
    TQDomElement printrepeatcolumns = sheet.namedItem( "printrepeatcolumns" ).toElement();
    if ( !printrepeatcolumns.isNull() )
    {
        int left = printrepeatcolumns.attribute( "left" ).toInt();
        int right = printrepeatcolumns.attribute( "right" ).toInt();
        d->print->setPrintRepeatColumns( qMakePair( left, right ) );
    }

    // load print repeat rows
    TQDomElement printrepeatrows = sheet.namedItem( "printrepeatrows" ).toElement();
    if ( !printrepeatrows.isNull() )
    {
        int top = printrepeatrows.attribute( "top" ).toInt();
        int bottom = printrepeatrows.attribute( "bottom" ).toInt();
        d->print->setPrintRepeatRows( qMakePair( top, bottom ) );
    }

    if( !sheet.hasAttribute( "borders1.2" ) )
    {
      convertObscuringBorders();
    }

    if ( sheet.hasAttribute( "protected" ) )
    {
      TQString passwd = sheet.attribute( "protected" );

      if ( passwd.length() > 0 )
      {
        TQCString str( passwd.latin1() );
        d->password = KCodecs::base64Decode( str );
      }
      else
        d->password = TQCString( "" );
    }

    return true;
}


bool Sheet::loadChildren( KoStore* _store )
{
    TQPtrListIterator<EmbeddedObject> it( doc()->embeddedObjects() );
    for( ; it.current(); ++it )
    {
        if ( it.current()->sheet() == this && ( it.current()->getType() == OBJECT_KOFFICE_PART || it.current()->getType() == OBJECT_CHART ) )
        {
            kdDebug() << "KSpreadSheet::loadChildren" << endl;
            if ( !dynamic_cast<EmbeddedKOfficeObject*>( it.current() )->embeddedObject()->loadDocument( _store ) )
                return false;
        }
    }

    return true;
}


void Sheet::setShowPageBorders( bool b )
{
    if ( b == d->showPageBorders )
        return;

    d->showPageBorders = b;
    emit sig_updateView( this );
}

void Sheet::addCellBinding( CellBinding *_bind )
{
  d->cellBindings.append( _bind );

  doc()->setModified( true );
}

void Sheet::removeCellBinding( CellBinding *_bind )
{
  d->cellBindings.removeRef( _bind );

  doc()->setModified( true );
}

Sheet* Sheet::findSheet( const TQString & _name )
{
  if ( !workbook() )
    return 0L;

  return workbook()->findSheet( _name );
}

// ###### Torben: Use this one instead of d->cells.insert()
void Sheet::insertCell( Cell *_cell )
{

  d->cells.insert( _cell, _cell->column(), _cell->row() );

  // Adjust the scrollbar range, if the max. dimension has changed.
  if ( d->scrollBarUpdates )
  {
    checkRangeHBorder ( _cell->column() );
    checkRangeVBorder ( _cell->row() );
  }
}

void Sheet::insertColumnFormat( ColumnFormat *l )
{
  d->columns.insertElement( l, l->column() );
}

void Sheet::insertRowFormat( RowFormat *l )
{
  d->rows.insertElement( l, l->row() );
}

void Sheet::update()
{
  Cell* c = d->cells.firstCell();
  for( ;c; c = c->nextCell() )
  {
    updateCell(c, c->column(), c->row());
  }
}

void Sheet::updateCellArea(const Region& cellArea)
{
  if ( doc()->isLoading() || doc()->delayCalculation() || (!getAutoCalc()))
    return;

  setRegionPaintDirty( cellArea );
}

void Sheet::updateCell( Cell */*cell*/, int _column, int _row )
{
  TQRect cellArea(TQPoint(_column, _row), TQPoint(_column, _row));

  updateCellArea(cellArea);
}

void Sheet::emit_updateRow( RowFormat *_format, int _row, bool repaint )
{
    if ( doc()->isLoading() )
        return;

    Cell* c = d->cells.firstCell();
    for( ;c; c = c->nextCell() )
      if ( c->row() == _row )
          c->setLayoutDirtyFlag( true );

    if ( repaint )
    {
        //All the cells in this row, or below this row will need to be repainted
        //So add that region of the sheet to the paint dirty list.
        setRegionPaintDirty( TQRect( 0 , _row , KS_colMax , KS_rowMax) );

      emit sig_updateVBorder( this );
      emit sig_updateView( this );
    }
    emit sig_maxRow(maxRow());
    _format->clearDisplayDirtyFlag();
}

void Sheet::emit_updateColumn( ColumnFormat *_format, int _column )
{
    if ( doc()->isLoading() )
        return;

    Cell* c = d->cells.firstCell();
    for( ;c; c = c->nextCell() )
        if ( c->column() == _column )
            c->setLayoutDirtyFlag( true );

    //All the cells in this column or to the right of it will need to be repainted if the column
    //has been resized or hidden, so add that region of the sheet to the paint dirty list.
    setRegionPaintDirty( TQRect( _column , 0 , KS_colMax , KS_rowMax) );

    emit sig_updateHBorder( this );
    emit sig_updateView( this );
    emit sig_maxColumn( maxColumn() );



    _format->clearDisplayDirtyFlag();
}

bool Sheet::insertChart( const KoRect& _rect, KoDocumentEntry& _e, const TQRect& _data )
{
    kdDebug(36001) << "Creating document" << endl;
    KoDocument* dd = _e.createDoc();
    kdDebug(36001) << "Created" << endl;
    if ( !dd )
        // Error message is already displayed, so just return
        return false;

    kdDebug(36001) << "NOW FETCHING INTERFACE" << endl;

    if ( !dd->initDoc(KoDocument::InitDocEmbedded) )
        return false;

    EmbeddedChart * ch = new EmbeddedChart( doc(), this, dd, _rect );
    ch->setDataArea( _data );
    ch->update();
    ch->chart()->setCanChangeValue( false  );

    KoChart::WizardExtension * wiz = ch->chart()->wizardExtension();

    Range dataRange;
    dataRange.setRange( _data );
    dataRange.setSheet( this );

    TQString rangeString=dataRange.toString();

    if ( wiz )
        wiz->show( rangeString );

    insertObject( ch );

    return true;
}

bool Sheet::insertChild( const KoRect& _rect, KoDocumentEntry& _e )
{
    KoDocument* d = _e.createDoc( doc() );
    if ( !d )
    {
        kdDebug() << "Error inserting child!" << endl;
        return false;
    }
    if ( !d->initDoc(KoDocument::InitDocEmbedded) )
        return false;

    EmbeddedKOfficeObject* ch = new EmbeddedKOfficeObject( doc(), this, d, _rect );
    insertObject( ch );
    return true;
}

bool Sheet::insertPicture( const KoPoint& point , const KURL& url )
{
    KoPicture picture = doc()->pictureCollection()->downloadPicture( url , 0 );

    return insertPicture(point,picture);
}

bool Sheet::insertPicture( const KoPoint& point ,  KoPicture& picture )
{

    if (picture.isNull())
	    return false;

    KoPictureKey key = picture.getKey();

    KoRect destinationRect;
    destinationRect.setLeft( point.x()  );
    destinationRect.setTop( point.y()  );

    //Generate correct pixel size - this is a bit tricky.
    //This ensures that when we load the image it appears
    //the same size on screen on a 100%-zoom KSpread spreadsheet as it would in an
    //image viewer or another spreadsheet program such as OpenOffice.
    //
    //KoUnit assumes 72DPI, whereas the user's display resolution will probably be
    //different (eg. 96*96).  So, we convert the actual size in pixels into inches
    //using the screen display resolution and then use KoUnit to convert back into
    //the appropriate pixel size KSpread.

    KoSize destinationSize;

    double inchWidth = (double)picture.getOriginalSize().width() / KoGlobal::dpiX();
    double inchHeight = (double)picture.getOriginalSize().height() / KoGlobal::dpiY();

    destinationSize.setWidth( KoUnit::fromUserValue(inchWidth,KoUnit::U_INCH) );
    destinationSize.setHeight( KoUnit::fromUserValue(inchHeight,KoUnit::U_INCH) );

    destinationRect.setSize( destinationSize);

    EmbeddedPictureObject* object = new EmbeddedPictureObject( this, destinationRect, doc()->pictureCollection(),key);
   // ch->setPicture(key);

    insertObject( object );
    return true;
}

bool Sheet::insertPicture( const KoPoint& point, const TQPixmap& pixmap  )
{
	TQByteArray data;
	TQBuffer buffer(data);

	buffer.open( IO_ReadWrite );
	pixmap.save( &buffer , "PNG" );

	//Reset the buffer so that KoPicture reads the whole file from the beginning
	//(at the moment the read/write position is at the end)
	buffer.reset();

	KoPicture picture;
	picture.load( &buffer , "PNG" );

	doc()->pictureCollection()->insertPicture(picture);

	return insertPicture( point , picture );
}

void Sheet::insertObject( EmbeddedObject *_obj )
{
    doc()->insertObject( _obj );
    emit sig_updateView( _obj );
}

void Sheet::changeChildGeometry( EmbeddedKOfficeObject *_child, const KoRect& _rect )
{
    _child->setGeometry( _rect );

    emit sig_updateChildGeometry( _child );
}

bool Sheet::saveChildren( KoStore* _store, const TQString &_path )
{
    int i = 0;

    TQPtrListIterator<EmbeddedObject> it( doc()->embeddedObjects() );
    for( ; it.current(); ++it )
    {
        if ( it.current()->sheet() == this && ( it.current()->getType() == OBJECT_KOFFICE_PART || it.current()->getType() == OBJECT_CHART ) )
        {
            TQString path = TQString( "%1/%2" ).arg( _path ).arg( i++ );
            if ( !dynamic_cast<EmbeddedKOfficeObject*>( it.current() )->embeddedObject()->document()->saveToStore( _store, path ) )
                return false;
        }
    }
    return true;
}

bool Sheet::saveOasisObjects( KoStore */*store*/, KoXmlWriter &xmlWriter, KoGenStyles& mainStyles, int & indexObj, int &partIndexObj )
{
  //int i = 0;
  if ( doc()->embeddedObjects().isEmpty() )
    return true;

  bool objectFound = false; // object on this sheet?
  EmbeddedObject::KSpreadOasisSaveContext sc( xmlWriter, mainStyles, indexObj, partIndexObj );
  TQPtrListIterator<EmbeddedObject> it( doc()->embeddedObjects() );
  for( ; it.current(); ++it )
  {
    if ( it.current()->sheet() == this && ( doc()->savingWholeDocument() || it.current()->isSelected() ) )
    {
      if ( !objectFound )
      {
        xmlWriter.startElement( "table:shapes" );
        objectFound = true;
      }
      if ( !it.current()->saveOasisObject(sc)  )
      {
        xmlWriter.endElement();
        return false;
      }
      ++indexObj;
    }
  }
  if ( objectFound )
  {
    xmlWriter.endElement();
  }
  return true;
}

Sheet::~Sheet()
{
    //Disable automatic recalculation of dependancies on this sheet to prevent crashes
    //in certain situations:
    //
    //For example, suppose a cell in SheetB depends upon a cell in SheetA.  If the cell in SheetB is emptied
    //after SheetA has already been deleted, the program would try to remove dependancies from the cell in SheetA
    //causing a crash.
    setAutoCalc(false);

    s_mapSheets->remove( d->id );

    //when you remove all sheet (close file)
    //you must reinit s_id otherwise there is not
    //the good name between map and sheet
    if( s_mapSheets->count()==0)
      s_id=0L;

    Cell* c = d->cells.firstCell();
    for( ; c; c = c->nextCell() )
        c->sheetDies();

    d->cells.clear(); // cells destructor needs sheet to still exist

    d->painter->end();
    delete d->painter;
    delete d->widget;

    delete d->defaultFormat;
    delete d->defaultCell;
    delete d->defaultRowFormat;
    delete d->defaultColumnFormat;
    delete d->print;
    delete d->dcop;

    delete d->dependencies;

    delete d;

    //this is for debugging a crash
    d=0;
}

void Sheet::checkRangeHBorder ( int _column )
{
    if ( d->scrollBarUpdates && _column > d->maxColumn )
    {
      d->maxColumn = _column;
      emit sig_maxColumn( _column );
    }
}

void Sheet::checkRangeVBorder ( int _row )
{
    if ( d->scrollBarUpdates && _row > d->maxRow )
    {
      d->maxRow = _row;
      emit sig_maxRow( _row );
    }
}


void Sheet::enableScrollBarUpdates( bool _enable )
{
  d->scrollBarUpdates = _enable;
}

DCOPObject* Sheet::dcopObject()
{
    if ( !d->dcop )
        d->dcop = new SheetIface( this );

    return d->dcop;
}

void Sheet::hideSheet(bool _hide)
{
    setHidden(_hide);
    if(_hide)
        emit sig_SheetHidden(this);
    else
        emit sig_SheetShown(this);
}

void Sheet::removeSheet()
{
    emit sig_SheetRemoved(this);
}

bool Sheet::setSheetName( const TQString& name, bool init, bool /*makeUndo*/ )
{
    if ( workbook()->findSheet( name ) )
        return false;

    if ( isProtected() )
      return false;

    if ( d->name == name )
        return true;

    TQString old_name = d->name;
    d->name = name;

    if ( init )
        return true;

    TQPtrListIterator<Sheet> it( workbook()->sheetList() );
    for ( ; it.current(); ++it )
        it.current()->changeCellTabName( old_name, name );

    doc()->changeAreaSheetName( old_name, name );
    emit sig_nameChanged( this, old_name );

    setName(name.utf8());
    (dynamic_cast<SheetIface*>(dcopObject()))->sheetNameHasChanged();

    return true;
}


void Sheet::updateLocale()
{
  doc()->emitBeginOperation(true);
  setRegionPaintDirty(TQRect(TQPoint(1,1), TQPoint(KS_colMax, KS_rowMax)));

  Cell* c = d->cells.firstCell();
  for( ;c; c = c->nextCell() )
  {
    TQString _text = c->text();
    c->setCellText( _text );
  }
  emit sig_updateView( this );
  //  doc()->emitEndOperation();
}

Cell* Sheet::getFirstCellColumn(int col) const
{ return d->cells.getFirstCellColumn(col); }

Cell* Sheet::getLastCellColumn(int col) const
{ return d->cells.getLastCellColumn(col); }

Cell* Sheet::getFirstCellRow(int row) const
{ return d->cells.getFirstCellRow(row); }

Cell* Sheet::getLastCellRow(int row) const
{ return d->cells.getLastCellRow(row); }

Cell* Sheet::getNextCellUp(int col, int row) const
{ return d->cells.getNextCellUp(col, row); }

Cell* Sheet::getNextCellDown(int col, int row) const
{ return d->cells.getNextCellDown(col, row); }

Cell* Sheet::getNextCellLeft(int col, int row) const
{ return d->cells.getNextCellLeft(col, row); }

Cell* Sheet::getNextCellRight(int col, int row) const
{ return d->cells.getNextCellRight(col, row); }

void Sheet::convertObscuringBorders()
{
  /* a word of explanation here:
     beginning with KSpread 1.2 (actually, cvs of Mar 28, 2002), border information
     is stored differently.  Previously, for a cell obscuring a region, the entire
     region's border's data would be stored in the obscuring cell.  This caused
     some data loss in certain situations.  After that date, each cell stores
     its own border data, and prints it even if it is an obscured cell (as long
     as that border isn't across an obscuring border).
     Anyway, this function is used when loading a file that was stored with the
     old way of borders.  All new files have the sheet attribute "borders1.2" so
     if that isn't in the file, all the border data will be converted here.
     It's a bit of a hack but I can't think of a better way and it's not *that*
     bad of a hack.:-)
  */
  Cell* c = d->cells.firstCell();
  TQPen topPen, bottomPen, leftPen, rightPen;
  for( ;c; c = c->nextCell() )
  {
    if (c->extraXCells() > 0 || c->extraYCells() > 0)
    {
      topPen = c->topBorderPen(c->column(), c->row());
      leftPen = c->leftBorderPen(c->column(), c->row());
      rightPen = c->rightBorderPen(c->column(), c->row());
      bottomPen = c->bottomBorderPen(c->column(), c->row());

      c->format()->setTopBorderStyle(TQt::NoPen);
      c->format()->setLeftBorderStyle(TQt::NoPen);
      c->format()->setRightBorderStyle(TQt::NoPen);
      c->format()->setBottomBorderStyle(TQt::NoPen);

      for (int x = c->column(); x < c->column() + c->extraXCells(); x++)
      {
        nonDefaultCell( x, c->row() )->setTopBorderPen(topPen);
        nonDefaultCell( x, c->row() + c->extraYCells() )->
          setBottomBorderPen(bottomPen);
      }
      for (int y = c->row(); y < c->row() + c->extraYCells(); y++)
      {
        nonDefaultCell( c->column(), y )->setLeftBorderPen(leftPen);
        nonDefaultCell( c->column() + c->extraXCells(), y )->
          setRightBorderPen(rightPen);
      }
    }
  }
}

/**********************
 * Printout Functions *
 **********************/

// TODO Stefan: these belong to View, even better Canvas
void Sheet::setRegionPaintDirty( Region const & region )
{
  DilationManipulator manipulator;
  manipulator.setSheet(this);
  manipulator.add(region);
  manipulator.execute();
  // don't put it in the undo list! ;-)
  d->paintDirtyList.add(manipulator);
  //kdDebug() << "setRegionPaintDirty "<< static_cast<Region*>(&manipulator)->name(this) << endl;
}

void Sheet::setRegionPaintDirty( TQRect const & range )
{
  DilationManipulator manipulator;
  manipulator.setSheet(this);
  manipulator.add(range);
  manipulator.execute();
  // don't put it in the undo list! ;-)
  d->paintDirtyList.add(manipulator);
  //kdDebug() << "setRegionPaintDirty "<< static_cast<Region*>(&manipulator)->name(this) << endl;
}

void Sheet::clearPaintDirtyData()
{
  d->paintDirtyList.clear();
}

bool Sheet::cellIsPaintDirty( TQPoint const & cell ) const
{
  return d->paintDirtyList.contains(cell);
}

#ifndef NDEBUG
void Sheet::printDebug()
{
    int iMaxColumn = maxColumn();
    int iMaxRow = maxRow();

    kdDebug(36001) << "Cell | Content  | DataT | Text" << endl;
    Cell *cell;
    for ( int currentrow = 1 ; currentrow < iMaxRow ; ++currentrow )
    {
        for ( int currentcolumn = 1 ; currentcolumn < iMaxColumn ; currentcolumn++ )
        {
            cell = cellAt( currentcolumn, currentrow );
            if ( !cell->isDefault() && !cell->isEmpty() )
            {
                TQString cellDescr = Cell::name( currentcolumn, currentrow );
                cellDescr = cellDescr.rightJustify( 4,' ' );
                //TQString cellDescr = "Cell ";
                //cellDescr += TQString::number(currentrow).rightJustify(3,'0') + ',';
                //cellDescr += TQString::number(currentcolumn).rightJustify(3,'0') + ' ';
                cellDescr += " | ";
                cellDescr += cell->value().type();
                cellDescr += " | ";
                cellDescr += cell->text();
                if ( cell->isFormula() )
                    cellDescr += TQString("  [result: %1]").arg( cell->value().asString() );
                kdDebug(36001) << cellDescr << endl;
            }
        }
    }
}
#endif

} // namespace KSpread

