kspread

kspread_sheet.cc

00001 /* This file is part of the KDE project
00002    Copyright (C) 1998,  1999 Torben Weis <weis@kde.org>
00003    Copyright (C) 1999 - 2005 The KSpread Team
00004                              www.koffice.org/kspread
00005 
00006    This library is free software; you can redistribute it and/or
00007    modify it under the terms of the GNU Library General Public
00008    License as published by the Free Software Foundation; either
00009    version 2 of the License, or (at your option) any later version.
00010 
00011    This library is distributed in the hope that it will be useful,
00012    but WITHOUT ANY WARRANTY; without even the implied warranty of
00013    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00014    Library General Public License for more details.
00015 
00016    You should have received a copy of the GNU Library General Public License
00017    along with this library; see the file COPYING.LIB.  If not, write to
00018    the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
00019  * Boston, MA 02110-1301, USA.
00020 */
00021 
00022 #include <assert.h>
00023 #include <ctype.h>
00024 #include <float.h>
00025 #include <math.h>
00026 #include <pwd.h>
00027 #include <stdlib.h>
00028 #include <unistd.h>
00029 
00030 #include <qapplication.h>
00031 #include <qcheckbox.h>
00032 #include <qclipboard.h>
00033 #include <qlabel.h>
00034 #include <qlayout.h>
00035 #include <qlineedit.h>
00036 #include <qpicture.h>
00037 #include <qregexp.h>
00038 #include <qvbox.h>
00039 #include <qmap.h>
00040 
00041 #include <kdebug.h>
00042 #include <kmdcodec.h>
00043 #include <kfind.h>
00044 #include <kfinddialog.h>
00045 #include <kmessagebox.h>
00046 #include <kreplace.h>
00047 #include <kreplacedialog.h>
00048 #include <kprinter.h>
00049 #include <kurl.h>
00050 
00051 #include <koChart.h>
00052 #include <KoDom.h>
00053 #include <KoDocumentInfo.h>
00054 #include <KoOasisLoadingContext.h>
00055 #include <KoOasisSettings.h>
00056 #include <KoOasisStyles.h>
00057 #include <KoQueryTrader.h>
00058 #include <KoStyleStack.h>
00059 #include <KoUnit.h>
00060 #include <KoXmlNS.h>
00061 #include <KoXmlWriter.h>
00062 
00063 #include "commands.h"
00064 #include "dependencies.h"
00065 #include "selection.h"
00066 #include "ksploadinginfo.h"
00067 #include "ksprsavinginfo.h"
00068 #include "kspread_canvas.h"
00069 #include "kspread_cluster.h"
00070 #include "kspread_condition.h"
00071 #include "kspread_doc.h"
00072 #include "kspread_global.h"
00073 #include "kspread_locale.h"
00074 #include "kspread_map.h"
00075 #include "kspread_object.h"
00076 #include "kspread_sheetprint.h"
00077 #include "kspread_style.h"
00078 #include "kspread_style_manager.h"
00079 #include "kspread_undo.h"
00080 #include "kspread_util.h"
00081 #include "kspread_view.h"
00082 #include "manipulator.h"
00083 #include "manipulator_data.h"
00084 #include "KSpreadTableIface.h"
00085 
00086 #include "kspread_sheet.h"
00087 #include "kspread_sheet.moc"
00088 
00089 #define NO_MODIFICATION_POSSIBLE \
00090 do { \
00091   KMessageBox::error( 0, i18n ( "You cannot change a protected sheet" ) ); return; \
00092 } while(0)
00093 
00094 namespace KSpread {
00095 
00096 /*****************************************************************************
00097  *
00098  * CellBinding
00099  *
00100  *****************************************************************************/
00101 
00102 CellBinding::CellBinding( Sheet *_sheet, const QRect& _area )
00103 {
00104   m_rctDataArea = _area;
00105 
00106   m_pSheet = _sheet;
00107   m_pSheet->addCellBinding( this );
00108 
00109   m_bIgnoreChanges = false;
00110 }
00111 
00112 CellBinding::~CellBinding()
00113 {
00114   m_pSheet->removeCellBinding( this );
00115 }
00116 
00117 void CellBinding::cellChanged( Cell *_cell )
00118 {
00119   if ( m_bIgnoreChanges )
00120     return;
00121 
00122   emit changed( _cell );
00123 }
00124 
00125 bool CellBinding::contains( int _x, int _y )
00126 {
00127   return m_rctDataArea.contains( QPoint( _x, _y ) );
00128 }
00129 
00130 /*****************************************************************************
00131  *
00132  * ChartBinding
00133  *
00134  *****************************************************************************/
00135 
00136 ChartBinding::ChartBinding( Sheet *_sheet, const QRect& _area, EmbeddedChart *_child )
00137     : CellBinding( _sheet, _area )
00138 {
00139   m_child = _child;
00140 }
00141 
00142 ChartBinding::~ChartBinding()
00143 {
00144 }
00145 
00146 void ChartBinding::cellChanged( Cell* /*changedCell*/ )
00147 {
00148     if ( m_bIgnoreChanges )
00149         return;
00150 
00151     //Ensure display gets updated by marking all cells underneath the chart as
00152     //dirty
00153 
00154     const QRect chartGeometry = m_child->geometry().toQRect();
00155 
00156     double tmp;
00157     int left = sheet()->leftColumn( chartGeometry.left() , tmp );
00158     int top = sheet()->topRow( chartGeometry.top() , tmp );
00159     int right = sheet()->rightColumn( chartGeometry.right() );
00160     int bottom = sheet()->bottomRow( chartGeometry.bottom() );
00161 
00162     sheet()->setRegionPaintDirty( QRect(left,top,right-left,bottom-top) );
00163 
00164     //kdDebug(36001) << m_rctDataArea << endl;
00165 
00166     // Get the chart and resize its data if necessary.
00167     //
00168     // FIXME: Only do this if he data actually changed size.
00169     KoChart::Part  *chart = m_child->chart();
00170     chart->resizeData( m_rctDataArea.height(), m_rctDataArea.width() );
00171 
00172     // Reset all the data, i.e. retransfer them to the chart.
00173     // This is definitely not the most efficient way to do this.
00174     //
00175     // FIXME: Find a way to do it with just the data that changed.
00176     Cell* cell;
00177     for ( int row = 0; row < m_rctDataArea.height(); row++ ) {
00178         for ( int col = 0; col < m_rctDataArea.width(); col++ ) {
00179             cell = m_pSheet->cellAt( m_rctDataArea.left() + col,
00180                      m_rctDataArea.top() + row );
00181             if ( cell && cell->value().isNumber() )
00182         chart->setCellData( row, col, cell->value().asFloat() );
00183             else if ( cell )
00184             chart->setCellData( row, col, cell->value().asString() );
00185             else
00186             chart->setCellData( row, col, KoChart::Value() );
00187         }
00188     }
00189     chart->analyzeHeaders( );
00190 
00191     // ######### Kalle may be interested in that, too
00192 #if 0
00193     Chart::Range range;
00194     range.top = m_rctDataArea.top();
00195     range.left = m_rctDataArea.left();
00196     range.right = m_rctDataArea.right();
00197     range.bottom = m_rctDataArea.bottom();
00198     range.sheet = m_pSheet->name(); */
00199 
00200     //m_child->chart()->setData( matrix );
00201 
00202     // Force a redraw of the chart on all views
00203 
00205 #endif
00206     //    sheet()->emit_polygonInvalidated( m_child->framePointArray() );
00207 }
00208 
00209 
00210 /******************************************************************/
00211 /* Class: TextDrag                                               */
00212 /******************************************************************/
00213 
00214 
00215 TextDrag::TextDrag( QWidget * dragSource, const char * name )
00216     : QTextDrag( dragSource, name )
00217 {
00218 }
00219 
00220 TextDrag::~TextDrag()
00221 {
00222 }
00223 
00224 
00225 QByteArray TextDrag::encodedData( const char * mime ) const
00226 {
00227   if ( strcmp( selectionMimeType(), mime ) == 0)
00228     return m_kspread;
00229   else
00230     return QTextDrag::encodedData( mime );
00231 }
00232 
00233 bool TextDrag::canDecode( QMimeSource* e )
00234 {
00235   if ( e->provides( selectionMimeType() ) )
00236     return true;
00237   return QTextDrag::canDecode(e);
00238 }
00239 
00240 const char * TextDrag::format( int i ) const
00241 {
00242   if ( i < 4 ) // HACK, but how to do otherwise ??
00243     return QTextDrag::format(i);
00244   else if ( i == 4 )
00245     return selectionMimeType();
00246   else return 0;
00247 }
00248 
00249 const char * TextDrag::selectionMimeType()
00250 {
00251   return "application/x-kspread-snippet";
00252 }
00253 
00254 /*****************************************************************************
00255  *
00256  * Sheet
00257  *
00258  *****************************************************************************/
00259 
00260 class Sheet::Private
00261 {
00262 public:
00263 
00264   Map* workbook;
00265 
00266   DCOPObject* dcop;
00267 
00268   QString name;
00269   int id;
00270 
00271   Sheet::LayoutDirection layoutDirection;
00272 
00273   // true if sheet is hidden
00274   bool hide;
00275 
00276   // password of protected sheet
00277   QCString password;
00278 
00279 
00280   bool showGrid;
00281   bool showFormula;
00282   bool showFormulaIndicator;
00283   bool showCommentIndicator;
00284   bool autoCalc;
00285   bool lcMode;
00286   bool showColumnNumber;
00287   bool hideZero;
00288   bool firstLetterUpper;
00289 
00290   // clusters to hold objects
00291   Cluster cells;
00292   RowCluster rows;
00293   ColumnCluster columns;
00294 
00295   // default objects
00296   Cell* defaultCell;
00297   Format* defaultFormat;
00298   RowFormat* defaultRowFormat;
00299   ColumnFormat* defaultColumnFormat;
00300 
00301   // hold the print object
00302   SheetPrint* print;
00303 
00304   // cells that need painting
00305   Region paintDirtyList;
00306 
00307   // to get font metrics
00308   QPainter *painter;
00309   QWidget *widget;
00310 
00311   // List of all cell bindings. For example charts use bindings to get
00312   // informed about changing cell contents.
00313   QPtrList<CellBinding> cellBindings;
00314 
00315   // Indicates whether the sheet should paint the page breaks.
00316   // Doing so costs some time, so by default it should be turned off.
00317   bool showPageBorders;
00318 
00319   // List of all embedded objects. FIXME unused ??
00320   // QPtrList<Child> m_lstChildren;
00321 
00322   // The highest row and column ever accessed by the user.
00323   int maxRow;
00324   int maxColumn;
00325 
00326   // Max range of canvas in x and ye direction.
00327   //  Depends on KS_colMax/KS_rowMax and the width/height of all columns/rows
00328   double sizeMaxX;
00329   double sizeMaxY;
00330 
00331 
00332   bool scrollBarUpdates;
00333 
00334   QPen emptyPen;
00335   QBrush emptyBrush;
00336   QColor emptyColor;
00337 
00338   int scrollPosX;
00339   int scrollPosY;
00340 
00341   KSpread::DependencyManager *dependencies;
00342 };
00343 
00344 int Sheet::s_id = 0L;
00345 QIntDict<Sheet>* Sheet::s_mapSheets;
00346 
00347 Sheet* Sheet::find( int _id )
00348 {
00349   if ( !s_mapSheets )
00350     return 0L;
00351 
00352   return (*s_mapSheets)[ _id ];
00353 }
00354 
00355 Sheet::Sheet (Map* map,
00356     const QString &sheetName, const char *_name )
00357   : QObject( map, _name )
00358 {
00359   if ( s_mapSheets == 0L )
00360     s_mapSheets = new QIntDict<Sheet>;
00361   d = new Private;
00362 
00363   d->workbook = map;
00364 
00365   d->id = s_id++;
00366   s_mapSheets->insert( d->id, this );
00367 
00368   d->layoutDirection = LeftToRight;
00369 
00370   d->defaultFormat = new Format (this, d->workbook->doc()->styleManager()->defaultStyle());
00371   d->emptyPen.setStyle( Qt::NoPen );
00372   d->dcop = 0;
00373   d->name = sheetName;
00374 
00375   dcopObject();
00376   d->cellBindings.setAutoDelete( false );
00377 
00378   // m_lstChildren.setAutoDelete( true );
00379 
00380   d->cells.setAutoDelete( true );
00381   d->rows.setAutoDelete( true );
00382   d->columns.setAutoDelete( true );
00383 
00384   d->defaultCell = new Cell( this, d->workbook->doc()->styleManager()->defaultStyle(), 0, 0);
00385   d->defaultRowFormat = new RowFormat( this, 0 );
00386   d->defaultRowFormat->setDefault();
00387   d->defaultColumnFormat = new ColumnFormat( this, 0 );
00388   d->defaultColumnFormat->setDefault();
00389 
00390   d->widget = new QWidget();
00391   d->painter = new QPainter;
00392   d->painter->begin( d->widget );
00393 
00394   d->maxColumn = 256;
00395   d->maxRow = 256;
00396   d->sizeMaxX = KS_colMax * d->defaultColumnFormat->dblWidth(); // default is max cols * default width
00397   d->sizeMaxY = KS_rowMax * d->defaultRowFormat->dblHeight(); // default is max rows * default height
00398 
00399   d->scrollBarUpdates = true;
00400 
00401   setHidden( false );
00402   d->showGrid=true;
00403   d->showFormula=false;
00404   d->showFormulaIndicator=true;
00405   d->showCommentIndicator=true;
00406   d->showPageBorders = false;
00407 
00408   d->lcMode=false;
00409   d->showColumnNumber=false;
00410   d->hideZero=false;
00411   d->firstLetterUpper=false;
00412   d->autoCalc=true;
00413   // Get a unique name so that we can offer scripting
00414   if ( !_name )
00415   {
00416       QCString s;
00417       s.sprintf("Sheet%i", s_id );
00418       QObject::setName( s.data() );
00419   }
00420   d->print = new SheetPrint( this );
00421 
00422   // initialize dependencies
00423   d->dependencies = new KSpread::DependencyManager (this);
00424 
00425   // connect to named area slots
00426   QObject::connect( doc(), SIGNAL( sig_addAreaName( const QString & ) ),
00427     this, SLOT( slotAreaModified( const QString & ) ) );
00428 
00429   QObject::connect( doc(), SIGNAL( sig_removeAreaName( const QString & ) ),
00430     this, SLOT( slotAreaModified( const QString & ) ) );
00431 
00432 
00433 }
00434 
00435 QString Sheet::sheetName() const
00436 {
00437   return d->name;
00438 }
00439 
00440 Map* Sheet::workbook() const
00441 {
00442   return d->workbook;
00443 }
00444 
00445 Doc* Sheet::doc() const
00446 {
00447   return d->workbook->doc();
00448 }
00449 
00450 int Sheet::id() const
00451 {
00452   return d->id;
00453 }
00454 
00455 Sheet::LayoutDirection Sheet::layoutDirection() const
00456 {
00457   return d->layoutDirection;
00458 }
00459 
00460 void Sheet::setLayoutDirection( LayoutDirection dir )
00461 {
00462   d->layoutDirection = dir;
00463 }
00464 
00465 bool Sheet::isRightToLeft() const
00466 {
00467   return d->layoutDirection == RightToLeft;
00468 }
00469 
00470 bool Sheet::isHidden() const
00471 {
00472   return d->hide;
00473 }
00474 
00475 void Sheet::setHidden( bool hidden )
00476 {
00477   d->hide = hidden;
00478 }
00479 
00480 bool Sheet::getShowGrid() const
00481 {
00482     return d->showGrid;
00483 }
00484 
00485 void Sheet::setShowGrid( bool _showGrid )
00486 {
00487     d->showGrid=_showGrid;
00488 }
00489 
00490 bool Sheet::getShowFormula() const
00491 {
00492     return d->showFormula;
00493 }
00494 
00495 void Sheet::setShowFormula( bool _showFormula )
00496 {
00497     d->showFormula=_showFormula;
00498 }
00499 
00500 bool Sheet::getShowFormulaIndicator() const
00501 {
00502     return d->showFormulaIndicator;
00503 }
00504 
00505 void Sheet::setShowFormulaIndicator( bool _showFormulaIndicator )
00506 {
00507     d->showFormulaIndicator=_showFormulaIndicator;
00508 }
00509 
00510 bool Sheet::getShowCommentIndicator() const
00511 {
00512     return d->showCommentIndicator;
00513 }
00514 
00515 void Sheet::setShowCommentIndicator(bool _indic)
00516 {
00517     d->showCommentIndicator=_indic;
00518 }
00519 
00520 bool Sheet::getLcMode() const
00521 {
00522     return d->lcMode;
00523 }
00524 
00525 void Sheet::setLcMode( bool _lcMode )
00526 {
00527     d->lcMode=_lcMode;
00528 }
00529 
00530 bool Sheet::getAutoCalc() const
00531 {
00532     return d->autoCalc;
00533 }
00534 
00535 void Sheet::setAutoCalc( bool _AutoCalc )
00536 {
00537     //Avoid possible recalculation of dependancies if the auto calc setting hasn't changed
00538     if (d->autoCalc == _AutoCalc)
00539         return;
00540 
00541     //If enabling automatic calculation, make sure that the dependencies are up-to-date
00542     if (_AutoCalc == true)
00543     {
00544         updateAllDependencies();
00545         recalc();
00546     }
00547 
00548     d->autoCalc=_AutoCalc;
00549 
00550 
00551 }
00552 
00553 bool Sheet::getShowColumnNumber() const
00554 {
00555     return d->showColumnNumber;
00556 }
00557 
00558 void Sheet::setShowColumnNumber( bool _showColumnNumber )
00559 {
00560     d->showColumnNumber=_showColumnNumber;
00561 }
00562 
00563 bool Sheet::getHideZero() const
00564 {
00565     return d->hideZero;
00566 }
00567 
00568 void Sheet::setHideZero( bool _hideZero )
00569 {
00570     d->hideZero=_hideZero;
00571 }
00572 
00573 bool Sheet::getFirstLetterUpper() const
00574 {
00575     return d->firstLetterUpper;
00576 }
00577 
00578 void Sheet::setFirstLetterUpper( bool _firstUpper )
00579 {
00580     d->firstLetterUpper=_firstUpper;
00581 }
00582 
00583 bool Sheet::isShowPageBorders() const
00584 {
00585     return d->showPageBorders;
00586 }
00587 
00588 bool Sheet::isEmpty( unsigned long int x, unsigned long int y ) const
00589 {
00590   const Cell* c = cellAt( x, y );
00591   if ( !c || c->isEmpty() )
00592     return true;
00593 
00594   return false;
00595 }
00596 
00597 Cell* Sheet::defaultCell() const
00598 {
00599     return d->defaultCell;
00600 }
00601 
00602 Format* Sheet::defaultFormat()
00603 {
00604     return d->defaultFormat;
00605 }
00606 
00607 const Format* Sheet::defaultFormat() const
00608 {
00609     return d->defaultFormat;
00610 }
00611 
00612 const ColumnFormat* Sheet::columnFormat( int _column ) const
00613 {
00614     const ColumnFormat *p = d->columns.lookup( _column );
00615     if ( p != 0L )
00616         return p;
00617 
00618     return d->defaultColumnFormat;
00619 }
00620 
00621 ColumnFormat* Sheet::columnFormat( int _column )
00622 {
00623     ColumnFormat *p = d->columns.lookup( _column );
00624     if ( p != 0L )
00625         return p;
00626 
00627     return d->defaultColumnFormat;
00628 }
00629 
00630 const RowFormat* Sheet::rowFormat( int _row ) const
00631 {
00632     const RowFormat *p = d->rows.lookup( _row );
00633     if ( p != 0L )
00634         return p;
00635 
00636     return d->defaultRowFormat;
00637 }
00638 
00639 RowFormat* Sheet::rowFormat( int _row )
00640 {
00641     RowFormat *p = d->rows.lookup( _row );
00642     if ( p != 0L )
00643         return p;
00644 
00645     return d->defaultRowFormat;
00646 }
00647 
00648 Value Sheet::value (int col, int row) const
00649 {
00650   Cell *cell = d->cells.lookup (col, row);
00651   if (cell)
00652     return cell->value ();
00653   Value empty;
00654   return empty;
00655 }
00656 
00657 Value Sheet::valueRange (int col1, int row1,
00658     int col2, int row2) const
00659 {
00660   return d->cells.valueRange (col1, row1, col2, row2);
00661 }
00662 
00663 void Sheet::password( QCString & passwd ) const
00664 {
00665     passwd = d->password;
00666 }
00667 
00668 bool Sheet::isProtected() const
00669 {
00670     return !d->password.isNull();
00671 }
00672 
00673 void Sheet::setProtected( QCString const & passwd )
00674 {
00675   d->password = passwd;
00676 }
00677 
00678 bool Sheet::checkPassword( QCString const & passwd ) const
00679 {
00680     return ( passwd == d->password );
00681 }
00682 
00683 SheetPrint* Sheet::print() const
00684 {
00685     return d->print;
00686 }
00687 
00688 QPainter& Sheet::painter()
00689 {
00690     return *d->painter;
00691 }
00692 
00693 QWidget* Sheet::widget()const
00694 {
00695     return d->widget;
00696 }
00697 
00698 CellBinding* Sheet::firstCellBinding()
00699 {
00700     return d->cellBindings.first();
00701 }
00702 
00703 CellBinding* Sheet::nextCellBinding()
00704 {
00705     return d->cellBindings.next();
00706 }
00707 
00708 void Sheet::setDefaultHeight( double height )
00709 {
00710   if ( isProtected() )
00711     NO_MODIFICATION_POSSIBLE;
00712 
00713   d->defaultRowFormat->setDblHeight( height );
00714 }
00715 
00716 void Sheet::setDefaultWidth( double width )
00717 {
00718   if ( isProtected() )
00719     NO_MODIFICATION_POSSIBLE;
00720 
00721   d->defaultColumnFormat->setDblWidth( width );
00722 }
00723 
00724 double Sheet::sizeMaxX() const
00725 {
00726   return d->sizeMaxX;
00727 }
00728 
00729 double Sheet::sizeMaxY() const
00730 {
00731   return d->sizeMaxY;
00732 }
00733 
00734 int Sheet::maxColumn() const
00735 {
00736   return d->maxColumn;
00737 }
00738 
00739 int Sheet::maxRow() const
00740 {
00741   return d->maxRow;
00742 }
00743 
00744 const QPen& Sheet::emptyPen() const
00745 {
00746   return d->emptyPen;
00747 }
00748 
00749 const QBrush& Sheet::emptyBrush() const
00750 {
00751   return d->emptyBrush;
00752 }
00753 
00754 const QColor& Sheet::emptyColor() const
00755 {
00756   return d->emptyColor;
00757 }
00758 
00759 KSpread::DependencyManager *Sheet::dependencies ()
00760 {
00761   return d->dependencies;
00762 }
00763 
00764 int Sheet::numSelected() const
00765 {
00766     int num = 0;
00767 
00768     QPtrListIterator<EmbeddedObject> it(  d->workbook->doc()->embeddedObjects() );
00769     for ( ; it.current() ; ++it )
00770     {
00771         if( it.current()->sheet() == this && it.current()->isSelected() )
00772             num++;
00773     }
00774 
00775     return num;
00776 }
00777 
00778 int Sheet::leftColumn( double _xpos, double &_left,
00779                               const Canvas *_canvas ) const
00780 {
00781     if ( _canvas )
00782     {
00783         _xpos += _canvas->xOffset();
00784         _left = -_canvas->xOffset();
00785     }
00786     else
00787         _left = 0.0;
00788 
00789     int col = 1;
00790     double x = columnFormat( col )->dblWidth( _canvas );
00791     while ( x < _xpos )
00792     {
00793         // Should never happen
00794         if ( col >= KS_colMax )
00795   {
00796       kdDebug(36001) << "Sheet:leftColumn: invalid column (col: " << col + 1 << ")" << endl;
00797       return KS_colMax + 1; //Return out of range value, so other code can react on this
00798   }
00799         _left += columnFormat( col )->dblWidth( _canvas );
00800         col++;
00801         x += columnFormat( col )->dblWidth( _canvas );
00802     }
00803 
00804     return col;
00805 }
00806 
00807 int Sheet::rightColumn( double _xpos, const Canvas *_canvas ) const
00808 {
00809     if ( _canvas )
00810         _xpos += _canvas->xOffset();
00811 
00812     int col = 1;
00813     double x = 0.0;
00814     while ( x < _xpos )
00815     {
00816         // Should never happen
00817         if ( col > KS_colMax )
00818   {
00819       kdDebug(36001) << "Sheet:rightColumn: invalid column (col: " << col << ")" << endl;
00820             return KS_colMax + 1; //Return out of range value, so other code can react on this
00821   }
00822         x += columnFormat( col )->dblWidth( _canvas );
00823         col++;
00824     }
00825 
00826     return col - 1;
00827 }
00828 
00829 QRect Sheet::visibleRect( Canvas const * const _canvas ) const
00830 {
00831   int top    = 0;
00832   int left   = 0;
00833 
00834   double x      = 0;
00835   double y      = 0;
00836   double width  = 0;
00837   double height = 0;
00838 
00839   if ( _canvas )
00840   {
00841     y     += _canvas->yOffset() * _canvas->zoom();
00842     x     += _canvas->xOffset() * _canvas->zoom();
00843     width  = _canvas->width();
00844     height = _canvas->height();
00845   }
00846 
00847   double yn = rowFormat( top )->dblHeight( _canvas );
00848   while ( yn < y )
00849   {
00850     if ( top >= KS_rowMax ) // Should never happen
00851       break;
00852 
00853     ++top;
00854     yn += rowFormat( top )->dblHeight( _canvas );
00855   }
00856 
00857   int bottom = top + 1;
00858 
00859   y += height;
00860   while ( yn < y )
00861   {
00862     if ( bottom > KS_rowMax ) // Should never happen
00863       break;
00864 
00865     ++bottom;
00866     yn += rowFormat( bottom )->dblHeight( _canvas );
00867   }
00868 
00869   double xn = columnFormat( left )->dblWidth( _canvas );
00870   while ( xn < x )
00871   {
00872     if ( left >= KS_colMax )    // Should never happen
00873       break;
00874 
00875     ++left;
00876     xn += columnFormat( left )->dblWidth( _canvas );
00877   }
00878   x += width;
00879 
00880   int right = left + 1;
00881 
00882   while ( xn < x )
00883   {
00884     if ( right > KS_colMax )    // Should never happen
00885       break;
00886 
00887     ++right;
00888     xn += columnFormat( right )->dblWidth( _canvas );
00889   }
00890   x += width;
00891 
00892   return QRect( left, top, right - left + 1, bottom - top + 1 );
00893 }
00894 
00895 int Sheet::topRow( double _ypos, double & _top,
00896                           const Canvas *_canvas ) const
00897 {
00898     if ( _canvas )
00899     {
00900         _ypos += _canvas->yOffset();
00901         _top = -_canvas->yOffset();
00902     }
00903     else
00904         _top = 0.0;
00905 
00906     int row = 1;
00907     double y = rowFormat( row )->dblHeight( _canvas );
00908     while ( y < _ypos )
00909     {
00910         // Should never happen
00911         if ( row >= KS_rowMax )
00912         {
00913             kdDebug(36001) << "Sheet:topRow: invalid row (row: " << row + 1 << ")" << endl;
00914             return KS_rowMax + 1; //Return out of range value, so other code can react on this
00915         }
00916         _top += rowFormat( row )->dblHeight( _canvas );
00917         row++;
00918         y += rowFormat( row )->dblHeight( _canvas );
00919     }
00920 
00921     return row;
00922 }
00923 
00924 int Sheet::bottomRow( double _ypos, const Canvas *_canvas ) const
00925 {
00926     if ( _canvas )
00927         _ypos += _canvas->yOffset();
00928 
00929     int row = 1;
00930     double y = 0.0;
00931     while ( y < _ypos )
00932     {
00933         // Should never happen
00934         if ( row > KS_rowMax )
00935   {
00936       kdDebug(36001) << "Sheet:bottomRow: invalid row (row: " << row << ")" << endl;
00937             return KS_rowMax + 1; //Return out of range value, so other code can react on this
00938   }
00939         y += rowFormat( row )->dblHeight( _canvas );
00940         row++;
00941     }
00942 
00943     return row - 1;
00944 }
00945 
00946 double Sheet::dblColumnPos( int _col, const Canvas *_canvas ) const
00947 {
00948     double x = 0.0;
00949     if ( _canvas )
00950       x -= _canvas->xOffset();
00951     for ( int col = 1; col < _col; col++ )
00952     {
00953         // Should never happen
00954         if ( col > KS_colMax )
00955   {
00956       kdDebug(36001) << "Sheet:columnPos: invalid column (col: " << col << ")" << endl;
00957             return x;
00958   }
00959 
00960         x += columnFormat( col )->dblWidth( _canvas );
00961     }
00962 
00963     return x;
00964 }
00965 
00966 int Sheet::columnPos( int _col, const Canvas *_canvas ) const
00967 {
00968     return (int)dblColumnPos( _col, _canvas );
00969 }
00970 
00971 
00972 double Sheet::dblRowPos( int _row, const Canvas *_canvas ) const
00973 {
00974     double y = 0.0;
00975     if ( _canvas )
00976       y -= _canvas->yOffset();
00977 
00978     for ( int row = 1 ; row < _row ; row++ )
00979     {
00980         // Should never happen
00981         if ( row > KS_rowMax )
00982   {
00983       kdDebug(36001) << "Sheet:rowPos: invalid row (row: " << row << ")" << endl;
00984             return y;
00985   }
00986 
00987         y += rowFormat( row )->dblHeight( _canvas );
00988     }
00989 
00990     return y;
00991 }
00992 
00993 int Sheet::rowPos( int _row, const Canvas *_canvas ) const
00994 {
00995     return (int)dblRowPos( _row, _canvas );
00996 }
00997 
00998 
00999 void Sheet::adjustSizeMaxX ( double _x )
01000 {
01001     d->sizeMaxX += _x;
01002 }
01003 
01004 void Sheet::adjustSizeMaxY ( double _y )
01005 {
01006     d->sizeMaxY += _y;
01007 }
01008 
01009 Cell* Sheet::visibleCellAt( int _column, int _row, bool _scrollbar_update )
01010 {
01011   Cell* cell = cellAt( _column, _row, _scrollbar_update );
01012   if ( cell->obscuringCells().isEmpty() )
01013       return cell;
01014   else
01015       return cell->obscuringCells().last();
01016 }
01017 
01018 Cell* Sheet::firstCell() const
01019 {
01020     return d->cells.firstCell();
01021 }
01022 
01023 RowFormat* Sheet::firstRow() const
01024 {
01025     return d->rows.first();
01026 }
01027 
01028 ColumnFormat* Sheet::firstCol() const
01029 {
01030     return d->columns.first();
01031 }
01032 
01033 Cell* Sheet::cellAt( int _column, int _row ) const
01034 {
01035     Cell *p = d->cells.lookup( _column, _row );
01036     if ( p != 0L )
01037         return p;
01038 
01039     return d->defaultCell;
01040 }
01041 
01042 Cell* Sheet::cellAt( int _column, int _row, bool _scrollbar_update )
01043 {
01044   if ( _column > KS_colMax ) {
01045     _column = KS_colMax;
01046     kdDebug (36001) << "Sheet::cellAt: column range: (col: " << _column << ")" << endl;
01047   }
01048   if ( _row > KS_rowMax) {
01049     kdDebug (36001) << "Sheet::cellAt: row out of range: (row: " << _row << ")" << endl;
01050     _row = KS_rowMax;
01051   }
01052 
01053   if ( _scrollbar_update && d->scrollBarUpdates )
01054   {
01055     checkRangeHBorder( _column );
01056     checkRangeVBorder( _row );
01057   }
01058 
01059   Cell *p = d->cells.lookup( _column, _row );
01060   if ( p != 0L )
01061     return p;
01062 
01063   return d->defaultCell;
01064 }
01065 
01066 ColumnFormat* Sheet::nonDefaultColumnFormat( int _column, bool force_creation )
01067 {
01068     ColumnFormat *p = d->columns.lookup( _column );
01069     if ( p != 0L || !force_creation )
01070         return p;
01071 
01072     p = new ColumnFormat( this, _column );
01073     // TODO: copy the default ColumnFormat here!!
01074     p->setDblWidth( d->defaultColumnFormat->dblWidth() );
01075 
01076     d->columns.insertElement( p, _column );
01077 
01078     return p;
01079 }
01080 
01081 RowFormat* Sheet::nonDefaultRowFormat( int _row, bool force_creation )
01082 {
01083     RowFormat *p = d->rows.lookup( _row );
01084     if ( p != 0L || !force_creation )
01085         return p;
01086 
01087     p = new RowFormat( this, _row );
01088     // TODO: copy the default RowLFormat here!!
01089     p->setDblHeight( d->defaultRowFormat->dblHeight() );
01090 
01091     d->rows.insertElement( p, _row );
01092 
01093     return p;
01094 }
01095 
01096 Cell* Sheet::nonDefaultCell( int _column, int _row,
01097                                            bool _scrollbar_update, Style * _style )
01098 {
01099   // NOTE Stefan: _scrollbar_update defaults to false and this function
01100   //              is never called with it being true. So, this here is
01101   //              actually never processed. I'll leave this in here for the
01102   //              case I'm mistaken, but will remove it for 2.0.
01103   if ( _scrollbar_update && d->scrollBarUpdates )
01104   {
01105     checkRangeHBorder( _column );
01106     checkRangeVBorder( _row );
01107   }
01108 
01109   Cell * p = d->cells.lookup( _column, _row );
01110   if ( p != 0L )
01111     return p;
01112 
01113   Cell * cell = 0;
01114 
01115   if ( _style )
01116     cell = new Cell( this, _style, _column, _row );
01117   else
01118     cell = new Cell( this, _column, _row );
01119 
01120   insertCell( cell );
01121 
01122   return cell;
01123 }
01124 
01125 void Sheet::setText( int _row, int _column, const QString& _text, bool asString )
01126 {
01127   ProtectedCheck prot;
01128   prot.setSheet (this);
01129   prot.add (QPoint (_column, _row));
01130   if (prot.check())
01131     NO_MODIFICATION_POSSIBLE;
01132 
01133   DataManipulator *dm = new DataManipulator ();
01134   dm->setSheet (this);
01135   dm->setValue (_text);
01136   dm->setParsing (!asString);
01137   dm->add (QPoint (_column, _row));
01138   dm->execute ();
01139 
01140   /* PRE-MANIPULATOR CODE looked like this:
01141   TODO remove this after the new code works
01142   if ( !doc()->undoLocked() )
01143   {
01144       UndoSetText *undo = new UndoSetText( doc(), this, cell->text(), _column, _row,cell->formatType() );
01145       doc()->addCommand( undo );
01146   }
01147 
01148   // The cell will force a display refresh itself, so we dont have to care here.
01149   cell->setCellText( _text, asString );
01150   */
01151 
01152   //refresh anchor
01153   if(_text.at(0)=='!')
01154     emit sig_updateView( this, Region(_column,_row,_column,_row) );
01155 }
01156 
01157 void Sheet::setArrayFormula (Selection *selectionInfo, const QString &_text)
01158 {
01159   // check protection
01160   ProtectedCheck prot;
01161   prot.setSheet (this);
01162   prot.add (*selectionInfo);
01163   if (prot.check())
01164     NO_MODIFICATION_POSSIBLE;
01165 
01166   // create and call the manipulator
01167   ArrayFormulaManipulator *afm = new ArrayFormulaManipulator;
01168   afm->setSheet (this);
01169   afm->setText (_text);
01170   afm->add (*selectionInfo);
01171   afm->execute ();
01172 
01173   /* PRE-MANIPULATOR CODE LOOKED LIKE THIS
01174   TODO remove this when the above code works
01175   // add undo
01176   if ( !doc()->undoLocked() )
01177   {
01178     UndoChangeAreaTextCell *undo =
01179         new UndoChangeAreaTextCell (doc(), this,
01180         QRect (_column, _row, cols, rows));
01181     doc()->addCommand( undo );
01182   }
01183 
01184   // fill in the cells ... top-left one gets the formula, the rest gets =INDEX
01185   // TODO: also fill in information about cells being a part of a range
01186   Cell *cell = nonDefaultCell (_column, _row);
01187   cell->setCellText (_text, false);
01188   QString cellRef = cell->name();
01189   for (int row = 0; row < rows; ++row)
01190     for (int col = 0; col < cols; col++)
01191       if (col || row)
01192       {
01193         Cell *cell = nonDefaultCell (_column + col, _row + row);
01194         cell->setCellText ("=INDEX(" + cellRef + ";" + QString::number (row+1)
01195             + ";" + QString::number (col+1) + ")", false);
01196       }
01197   */
01198 }
01199 
01200 void Sheet::setLayoutDirtyFlag()
01201 {
01202     Cell * c = d->cells.firstCell();
01203     for( ; c; c = c->nextCell() )
01204         c->setLayoutDirtyFlag();
01205 }
01206 
01207 void Sheet::setCalcDirtyFlag()
01208 {
01209     Cell* c = d->cells.firstCell();
01210     for( ; c; c = c->nextCell() )
01211     {
01212         if ( !(c->isObscured() && c->isPartOfMerged()) )
01213             c->setCalcDirtyFlag();
01214     }
01215 }
01216 
01217 void Sheet::updateAllDependencies()
01218 {
01219         for (Cell* cell = d->cells.firstCell() ; cell ; cell = cell->nextCell())
01220         {
01221             Point cellLocation;
01222             cellLocation.setSheet(cell->sheet());
01223             cellLocation.setRow(cell->row());
01224             cellLocation.setColumn(cell->column());
01225             d->dependencies->cellChanged(cellLocation);
01226         }
01227 }
01228 
01229 void Sheet::recalc()
01230 {
01231     recalc(false);
01232 }
01233 
01234 void Sheet::recalc( bool force )
01235 {
01236   ElapsedTime et( "Recalculating " + d->name, ElapsedTime::PrintOnlyTime );
01237   //  emitBeginOperation(true);
01238   //  setRegionPaintDirty(QRect(QPoint(1,1), QPoint(KS_colMax, KS_rowMax)));
01239   setCalcDirtyFlag();
01240 
01241   //If automatic calculation is disabled, don't recalculate unless the force flag has been
01242   //set.
01243   if ( !getAutoCalc() && !force )
01244         return;
01245 
01246   //If automatic calculation is disabled, the dependencies won't be up to date, so they need
01247   //to be recalculated.
01248   //FIXME:  Tomas, is there a more efficient way to do this?
01249   if ( !getAutoCalc() )
01250     updateAllDependencies();
01251 
01252 
01253   // (Tomas): actually recalc each cell
01254   // this is FAR from being perfect, dependencies will cause some to be
01255   // recalculated a LOT, but it's still better than otherwise, where
01256   // we get no recalc if the result stored in a file differs from the
01257   // current one - then we only obtain the correct result AFTER we scroll
01258   // to the cell ... recalc should actually ... recalc :)
01259   Cell* c;
01260 
01261   int count = 0;
01262   c = d->cells.firstCell();
01263   for( ; c; c = c->nextCell() )
01264     ++count;
01265 
01266   int cur = 0;
01267   int percent = -1;
01268   c = d->cells.firstCell();
01269   for( ; c; c = c->nextCell() )
01270   {
01271     c->calc (false);
01272     cur++;
01273     // some debug output to get some idea how damn slow this is ...
01274     if (cur*100/count != percent) {
01275       percent = cur*100/count;
01276 //       kdDebug() << "Recalc: " << percent << "%" << endl;
01277     }
01278   }
01279 
01280   //  emitEndOperation();
01281   emit sig_updateView( this );
01282 }
01283 
01284 void Sheet::valueChanged (Cell *cell)
01285 {
01286 
01287   //TODO: call cell updating, when cell damaging implemented
01288 
01289   //prepare the Point structure
01290   Point c;
01291   c.setRow (cell->row());
01292   c.setColumn (cell->column());
01293   c.setSheet( this );
01294 
01295   //update dependencies
01296   if ( getAutoCalc() )
01297         d->dependencies->cellChanged (c);
01298 
01299   //REMOVED - modification change - this was causing modified flag to be set inappropriately.
01300   //nobody else seems to be setting the modified flag, so we do it here
01301 //  doc()->setModified (true);
01302 }
01303 
01304 /*
01305  Methods working on selections:
01306 
01307  TYPE A:
01308  { columns selected:
01309    for all rows with properties X,X':
01310      if default-cell create new cell
01311  }
01312  post undo object (always a UndoCellLayout; difference in title only)
01313  { rows selected:
01314    if condition Y clear properties X,X' of cells;
01315    set properties X,X' of rowformats
01316    emit complete update;
01317  }
01318  { columns selected:
01319    if condition Y clear properties X,X' of cells;
01320    set properties X,X' of columnformats;
01321    for all rows with properties X,X':
01322      create cells if necessary and set properties X,X'
01323    emit complete update;
01324  }
01325  { cells selected:
01326    for all cells with condition Y:
01327      create if necessary and set properties X,X' and do Z;
01328    emit update on selected region;
01329  }
01330 
01331  USED in:
01332  setSelectionFont
01333  setSelectionSize
01334  setSelectionAngle
01335  setSelectionTextColor
01336  setSelectionBgColor
01337  setSelectionPercent
01338  borderAll
01339  borderRemove (exceptions: ### creates cells (why?), ### changes default cell if cell-regions selected?)
01340  setSelectionAlign
01341  setSelectionAlignY
01342  setSelectionMoneyFormat
01343  increaseIndent
01344  decreaseIndent
01345 
01346  TYPE B:
01347  post undo object
01348  { rows selected:
01349    if condition Y do X with cells;
01350    emit update on selection;
01351  }
01352  { columns selected:
01353    if condition Y do X with cells;
01354    emit update on selection;
01355  }
01356  { cells selected:
01357    if condition Y do X with cells; create cell if non-default;
01358    emit update on selection;
01359  }
01360 
01361  USED in:
01362  setSelectionUpperLower (exceptions: no undo; no create-if-default; ### modifies default-cell?)
01363  setSelectionFirstLetterUpper (exceptions: no undo; no create-if-default; ### modifies default-cell?)
01364  setSelectionVerticalText
01365  setSelectionComment
01366  setSelectionRemoveComment (exeception: no create-if-default and work only on non-default-cells for cell regions)
01367  setSelectionBorderColor (exeception: no create-if-default and work only on non-default-cells for cell regions)
01368  setSelectionMultiRow
01369  setSelectionPrecision
01370  clearTextSelection (exception: all only if !areaIsEmpty())
01371  clearValiditySelection (exception: all only if !areaIsEmpty())
01372  clearConditionalSelection (exception: all only if !areaIsEmpty())
01373  setConditional (exception: conditional after create-if-default for cell regions)
01374  setValidity (exception: conditional after create-if-default for cell regions)
01375 
01376  OTHERS:
01377  borderBottom
01378  borderRight
01379  borderLeft
01380  borderTop
01381  borderOutline
01382  => these work only on some cells (at the border); undo only if cells affected; rest is similar to type A
01383  --> better not use CellWorker/workOnCells()
01384 
01385  defaultSelection
01386  => similar to TYPE B, but works on columns/rows if complete columns/rows selected
01387  --> use emit_signal=false and return value of workOnCells to finish
01388 
01389  getWordSpelling
01390  => returns text, no signal emitted, no cell-create, similar to TYPE B
01391  --> use emit_signal=false, create_if_default=false and type B
01392 
01393  setWordSpelling
01394  => no signal emitted, no cell-create, similar to type B
01395  --> use emit_signal=false, create_if_default=false and type B
01396  */
01397 
01398 class UndoAction* Sheet::CellWorkerTypeA::createUndoAction( Doc* doc, Sheet* sheet, const KSpread::Region& region )
01399 {
01400     QString title = getUndoTitle();
01401     return new UndoCellFormat( doc, sheet, region, title );
01402 }
01403 
01404 /*
01405 Sheet::SelectionType Sheet::workOnCells( const QPoint& _marker, CellWorker& worker )
01406 {
01407     // see what is selected; if nothing, take marker position
01408     bool selected = ( m_rctSelection.left() != 0 );
01409     QRect r( m_rctSelection );
01410     if ( !selected )
01411   r.setCoords( _marker.x(), _marker.y(), _marker.x(), _marker.y() );
01412 
01413     // create cells in rows if complete columns selected
01414     Cell *cell;
01415     if ( !worker.type_B && selected && isColumnSelected() )
01416     {
01417   for ( RowFormat* rw =d->rows.first(); rw; rw = rw->next() )
01418   {
01419       if ( !rw->isDefault() && worker.testCondition( rw ) )
01420       {
01421     for ( int i=m_rctSelection.left(); i<=m_rctSelection.right(); i++ )
01422     {
01423         cell = cellAt( i, rw->row() );
01424         if ( cell == d->defaultCell )
01425       // '&& worker.create_if_default' unnecessary as never used in type A
01426         {
01427       cell = new Cell( this, i, rw->row() );
01428       insertCell( cell );
01429         }
01430     }
01431       }
01432   }
01433     }
01434 
01435     // create an undo action
01436     if ( !doc()->undoLocked() )
01437     {
01438   UndoAction *undo = worker.createUndoAction( doc(), this, r );
01439         // test if the worker has an undo action
01440         if ( undo != 0L )
01441       doc()->addCommand( undo );
01442     }
01443 
01444     // complete rows selected ?
01445     if ( selected && isRowSelected() )
01446     {
01447   int row;
01448   for ( Cell* cell = d->cells.firstCell(); cell; cell = cell->nextCell() )
01449   {
01450       row = cell->row();
01451       if ( m_rctSelection.top() <= row && m_rctSelection.bottom() >= row
01452      && worker.testCondition( cell ) )
01453     if ( worker.type_B )
01454         worker.doWork( cell, false, cell->column(), row );
01455     else
01456         worker.prepareCell( cell );
01457   }
01458 
01459   if ( worker.type_B ) {
01460             // for type B there's nothing left to do
01461       if ( worker.emit_signal )
01462     emit sig_updateView( this, r );
01463   } else {
01464             // for type A now work on row formats
01465       for ( int i=m_rctSelection.top(); i<=m_rctSelection.bottom(); i++ )
01466       {
01467     RowFormat *rw=nonDefaultRowFormat(i);
01468     worker.doWork( rw );
01469       }
01470       if ( worker.emit_signal )
01471     emit sig_updateView( this );
01472   }
01473   return CompleteRows;
01474     }
01475     // complete columns selected ?
01476     else if ( selected && isColumnSelected() )
01477     {
01478   int col;
01479   for ( Cell* cell = d->cells.firstCell(); cell; cell = cell->nextCell() )
01480   {
01481       col = cell->column();
01482       if ( m_rctSelection.left() <= col && m_rctSelection.right() >= col
01483      && worker.testCondition( cell ) )
01484     if ( worker.type_B )
01485         worker.doWork( cell, false, col, cell->row() );
01486     else
01487         worker.prepareCell( cell );
01488   }
01489 
01490   if ( worker.type_B ) {
01491       if ( worker.emit_signal )
01492     emit sig_updateView( this, r );
01493   } else {
01494       // for type A now work on column formats
01495       for ( int i=m_rctSelection.left(); i<=m_rctSelection.right(); i++ )
01496       {
01497     ColumnFormat *cl=nonDefaultColumnFormat(i);
01498     worker.doWork( cl );
01499       }
01500       Cell *cell;
01501       for ( RowFormat* rw =d->rows.first(); rw; rw = rw->next() )
01502       {
01503     if ( !rw->isDefault() && worker.testCondition( rw ) )
01504     {
01505         for ( int i=m_rctSelection.left(); i<=m_rctSelection.right(); i++ )
01506         {
01507       cell = cellAt( i, rw->row() );
01508       // ### this if should be not necessary; cells are created
01509       //     before the undo object is created, aren't they?
01510       if ( cell == d->defaultCell )
01511       {
01512           cell = new Cell( this, i, rw->row() );
01513           insertCell( cell );
01514       }
01515       worker.doWork( cell, false, i, rw->row() );
01516         }
01517     }
01518       }
01519             if ( worker.emit_signal )
01520     emit sig_updateView( this );
01521   }
01522   return CompleteColumns;
01523     }
01524     // cell region selected
01525     else
01526     {
01527   Cell *cell;
01528   for ( int x = r.left(); x <= r.right(); x++ )
01529       for ( int y = r.top(); y <= r.bottom(); y++ )
01530       {
01531     cell = cellAt( x, y );
01532                 if ( worker.testCondition( cell ) )
01533     {
01534         if ( worker.create_if_default && cell == d->defaultCell )
01535         {
01536       cell = new Cell( this, x, y );
01537       insertCell( cell );
01538         }
01539                     if ( cell != d->defaultCell )
01540       worker.doWork( cell, true, x, y );
01541     }
01542       }
01543         if ( worker.emit_signal )
01544       emit sig_updateView( this, r );
01545         return CellRegion;
01546     }
01547 }
01548 
01549 */
01550 
01551 Sheet::SelectionType Sheet::workOnCells( Selection* selectionInfo, CellWorker & worker )
01552 {
01553   Sheet::SelectionType result;
01554 
01555   doc()->emitBeginOperation();
01556 
01557   // see what is selected; if nothing, take marker position
01558   bool selected = !(selectionInfo->isSingular());
01559 
01560   // create an undo action
01561   if ( !doc()->undoLocked() )
01562   {
01563     UndoAction* undo = worker.createUndoAction(doc(), this, *selectionInfo);
01564     // test if the worker has an undo action
01565     if ( undo != 0 )
01566     {
01567       doc()->addCommand( undo );
01568     }
01569   }
01570 
01571   Region::ConstIterator endOfList(selectionInfo->constEnd());
01572   for (Region::ConstIterator it = selectionInfo->constBegin(); it != endOfList; ++it)
01573   {
01574     // see what is selected; if nothing, take marker position
01575     QRect range = (*it)->rect().normalize();
01576 
01577   int top = range.top();
01578   int left = range.left();
01579   int bottom = range.bottom();
01580   int right  = range.right();
01581 
01582   // create cells in rows if complete columns selected
01583   Cell * cell;
01584   Style * s = doc()->styleManager()->defaultStyle();
01585 
01586   if ( !worker.type_B && selected && util_isColumnSelected(range) )
01587   {
01588     for ( RowFormat * rw = d->rows.first(); rw; rw = rw->next() )
01589     {
01590       if ( worker.testCondition( rw ) )
01591       {
01592         for ( int col = left; col <= right; ++col )
01593         {
01594           cell = nonDefaultCell( col, rw->row(), false, s );
01595         }
01596       }
01597     }
01598   }
01599 
01600   // complete rows selected ?
01601   if ( selected && util_isRowSelected(range) )
01602   {
01603     for ( int row = top; row <= bottom; ++row )
01604     {
01605       cell = getFirstCellRow( row );
01606       while ( cell )
01607       {
01608         if ( worker.testCondition( cell ) )
01609         {
01610           if ( worker.type_B )
01611             worker.doWork( cell, false, cell->column(), row );
01612           else
01613             worker.prepareCell( cell );
01614         }
01615         cell = getNextCellRight( cell->column(), row );
01616       }
01617     }
01618 
01619     if ( worker.type_B )
01620     {
01621       // for type B there's nothing left to do
01622       ;
01623     }
01624     else
01625     {
01626       // for type A now work on row formats
01627       for ( int i = top; i <= bottom; ++i )
01628       {
01629         RowFormat * rw = nonDefaultRowFormat(i);
01630         worker.doWork( rw );
01631       }
01632 
01633       for ( int row = top; row <= bottom; ++row )
01634       {
01635         cell = getFirstCellRow( row );
01636         while ( cell )
01637         {
01638           if ( worker.testCondition( cell ) )
01639           {
01640             worker.doWork( cell, false, cell->column(), row );
01641           }
01642         cell = getNextCellRight( cell->column(), row );
01643         }
01644       }
01645 
01646     }
01647     result = CompleteRows;
01648   }
01649   // complete columns selected ?
01650   else if ( selected && util_isColumnSelected(range) )
01651   {
01652     for ( int col = range.left(); col <= right; ++col )
01653     {
01654       cell = getFirstCellColumn( col );
01655       while ( cell )
01656       {
01657   if ( worker.testCondition( cell ) )
01658         {
01659           if ( worker.type_B )
01660             worker.doWork( cell, false, col, cell->row() );
01661           else
01662             worker.prepareCell( cell );
01663         }
01664 
01665         cell = getNextCellDown( col, cell->row() );
01666       }
01667     }
01668 
01669     if ( worker.type_B )
01670     {
01671       ;
01672     }
01673     else
01674     {
01675       // for type A now work on column formats
01676       for ( int i = left; i <= right; ++i )
01677       {
01678         ColumnFormat * cl = nonDefaultColumnFormat( i );
01679         worker.doWork( cl );
01680       }
01681 
01682       for ( RowFormat * rw = d->rows.first(); rw; rw = rw->next() )
01683       {
01684         if ( worker.testCondition( rw ) )
01685         {
01686           for ( int i = left; i <= right; ++i )
01687           {
01688             cell = nonDefaultCell( i, rw->row(), false, s );
01689             worker.doWork( cell, false, i, rw->row() );
01690           }
01691         }
01692       }
01693     }
01694     result = CompleteColumns;
01695   }
01696   // cell region selected
01697   else
01698   {
01699     for ( int x = left; x <= right; ++x )
01700     {
01701       enableScrollBarUpdates(false);
01702       for ( int y = top; y <= bottom; ++y )
01703       {
01704         cell = cellAt( x, y );
01705         if ( worker.testCondition( cell ) )
01706         {
01707           if ( cell == d->defaultCell && worker.create_if_default )
01708           {
01709             cell = new Cell( this, s, x, y );
01710             insertCell( cell );
01711           }
01712           if ( cell != d->defaultCell )
01713           {
01714             // kdDebug() << "not default" << endl;
01715             worker.doWork( cell, true, x, y );
01716           }
01717         }
01718       }
01719       enableScrollBarUpdates(true);
01720       checkRangeVBorder(bottom);
01721     }
01722     checkRangeHBorder(right);
01723     result = CellRegion;
01724   }
01725 
01726   } // for Region::Elements
01727 
01728   // emitEndOperation();
01729   emit sig_updateView( this );
01730 
01731   if (worker.emit_signal)
01732   {
01733     emit sig_updateView( this, *selectionInfo );
01734   }
01735 
01736   return result;
01737 }
01738 
01739 void Sheet::setSelectionFont( Selection* selectionInfo,
01740                               const char *_font, int _size,
01741                               signed char _bold, signed char _italic,
01742                               signed char _underline, signed char _strike)
01743 {
01744   FontManipulator* manipulator = new FontManipulator();
01745   manipulator->setSheet(this);
01746   manipulator->setProperty(Format::PFont);
01747   manipulator->setFontFamily(_font);
01748   manipulator->setFontSize(_size);
01749   manipulator->setFontBold(_bold);
01750   manipulator->setFontItalic(_italic);
01751   manipulator->setFontStrike(_strike);
01752   manipulator->setFontUnderline(_underline);
01753   manipulator->add(*selectionInfo);
01754   manipulator->execute();
01755 }
01756 
01757 void Sheet::setSelectionSize(Selection* selectionInfo,
01758                               int _size)
01759 {
01760   // TODO Stefan: Increase/Decrease font size still used?
01761   int size;
01762   Cell* c;
01763   QPoint marker(selectionInfo->marker());
01764   c = cellAt(marker);
01765   size = c->format()->textFontSize(marker.x(), marker.y());
01766 
01767   FontManipulator* manipulator = new FontManipulator();
01768   manipulator->setSheet(this);
01769   manipulator->setProperty(Format::PFont);
01770   manipulator->setFontSize(_size+size);
01771   manipulator->add(*selectionInfo);
01772   manipulator->execute();
01773 }
01774 
01775 
01776 struct SetSelectionUpperLowerWorker : public Sheet::CellWorker {
01777     int _type;
01778     Sheet   * _s;
01779     SetSelectionUpperLowerWorker( int type, Sheet * s )
01780       : Sheet::CellWorker( false ), _type( type ),  _s( s ) { }
01781 
01782     class UndoAction* createUndoAction( Doc* doc, Sheet* sheet, const KSpread::Region& region )
01783     {
01784       return new UndoChangeAreaTextCell( doc, sheet, region );
01785     }
01786     bool testCondition( Cell* c ) {
01787   return ( !c->value().isNumber() && !c->value().isBoolean() &&!c->isFormula() && !c->isDefault()
01788      && !c->text().isEmpty() && c->text()[0] != '*' && c->text()[0] != '!'
01789      && !c->isPartOfMerged() );
01790     }
01791     void doWork( Cell* cell, bool, int, int )
01792     {
01793   cell->setDisplayDirtyFlag();
01794   if ( _type == -1 )
01795       cell->setCellText( (cell->text().lower()));
01796   else if ( _type == 1 )
01797       cell->setCellText( (cell->text().upper()));
01798   cell->clearDisplayDirtyFlag();
01799     }
01800 };
01801 
01802 void Sheet::setSelectionUpperLower( Selection* selectionInfo,
01803                                            int _type )
01804 {
01805   SetSelectionUpperLowerWorker w( _type, this );
01806   workOnCells( selectionInfo, w );
01807 }
01808 
01809 
01810 struct SetSelectionFirstLetterUpperWorker : public Sheet::CellWorker
01811 {
01812     Changes * _c;
01813     Sheet   * _s;
01814     SetSelectionFirstLetterUpperWorker( Sheet * s )
01815       : Sheet::CellWorker( false ),  _s( s ) { }
01816 
01817     class UndoAction* createUndoAction( Doc* doc, Sheet* sheet, const KSpread::Region& region ) {
01818   return   new UndoChangeAreaTextCell( doc, sheet, region );
01819     }
01820     bool testCondition( Cell* c ) {
01821   return ( !c->value().isNumber() && !c->value().isBoolean() &&!c->isFormula() && !c->isDefault()
01822      && !c->text().isEmpty() && c->text()[0] != '*' && c->text()[0] != '!'
01823      && !c->isPartOfMerged() );
01824     }
01825     void doWork( Cell* cell, bool, int, int )
01826     {
01827 
01828   cell->setDisplayDirtyFlag();
01829   QString tmp = cell->text();
01830   int len = tmp.length();
01831   cell->setCellText( (tmp.at(0).upper()+tmp.right(len-1)) );
01832   cell->clearDisplayDirtyFlag();
01833     }
01834 };
01835 
01836 void Sheet::setSelectionfirstLetterUpper( Selection* selectionInfo)
01837 {
01838   SetSelectionFirstLetterUpperWorker w(  this );
01839   workOnCells( selectionInfo, w );
01840 }
01841 
01842 
01843 struct SetSelectionVerticalTextWorker : public Sheet::CellWorker {
01844     bool _b;
01845     SetSelectionVerticalTextWorker( bool b ) : Sheet::CellWorker( ), _b( b ) { }
01846 
01847     class UndoAction* createUndoAction( Doc* doc, Sheet* sheet, const KSpread::Region& region ) {
01848         QString title=i18n("Vertical Text");
01849         return new UndoCellFormat( doc, sheet, region, title );
01850     }
01851     bool testCondition( Cell* cell ) {
01852   return ( !cell->isPartOfMerged() );
01853     }
01854     void doWork( Cell* cell, bool, int, int ) {
01855   cell->setDisplayDirtyFlag();
01856   cell->format()->setVerticalText( _b );
01857   cell->format()->setMultiRow( false );
01858   cell->format()->setAngle( 0 );
01859   cell->clearDisplayDirtyFlag();
01860     }
01861 };
01862 
01863 void Sheet::setSelectionVerticalText( Selection* selectionInfo,
01864                                              bool _b )
01865 {
01866     SetSelectionVerticalTextWorker w( _b );
01867     workOnCells( selectionInfo, w );
01868 }
01869 
01870 
01871 struct SetSelectionCommentWorker : public Sheet::CellWorker {
01872     QString _comment;
01873     SetSelectionCommentWorker( QString comment ) : Sheet::CellWorker( ), _comment( comment ) { }
01874 
01875     class UndoAction* createUndoAction( Doc* doc, Sheet* sheet, const KSpread::Region& region ) {
01876         QString title=i18n("Add Comment");
01877   return new UndoCellFormat( doc, sheet, region, title );
01878     }
01879     bool testCondition( Cell* cell ) {
01880   return ( !cell->isPartOfMerged() );
01881     }
01882     void doWork( Cell* cell, bool, int, int ) {
01883   cell->setDisplayDirtyFlag();
01884   cell->format()->setComment( _comment );
01885   cell->clearDisplayDirtyFlag();
01886     }
01887 };
01888 
01889 void Sheet::setSelectionComment( Selection* selectionInfo,
01890                                         const QString &_comment)
01891 {
01892     SetSelectionCommentWorker w( _comment );
01893     workOnCells( selectionInfo, w );
01894 }
01895 
01896 
01897 void Sheet::setSelectionAngle( Selection* selectionInfo,
01898                                int _value )
01899 {
01900   AngleManipulator* manipulator = new AngleManipulator();
01901   manipulator->setSheet(this);
01902   manipulator->setProperty(Format::PAngle);
01903   manipulator->setAngle(_value);
01904   manipulator->add(*selectionInfo);
01905   manipulator->execute();
01906 }
01907 
01908 struct SetSelectionRemoveCommentWorker : public Sheet::CellWorker {
01909     SetSelectionRemoveCommentWorker( ) : Sheet::CellWorker( false ) { }
01910 
01911     class UndoAction* createUndoAction( Doc* doc, Sheet* sheet, const KSpread::Region& region ) {
01912         QString title=i18n("Remove Comment");
01913   return new UndoCellFormat( doc, sheet, region, title );
01914     }
01915     bool testCondition( Cell* cell ) {
01916   return ( !cell->isPartOfMerged() );
01917     }
01918     void doWork( Cell* cell, bool, int, int ) {
01919   cell->setDisplayDirtyFlag();
01920   cell->format()->setComment( "" );
01921   cell->clearDisplayDirtyFlag();
01922     }
01923 };
01924 
01925 void Sheet::setSelectionRemoveComment( Selection* selectionInfo )
01926 {
01927   if (areaIsEmpty(*selectionInfo, Comment))
01928     return;
01929 
01930   SetSelectionRemoveCommentWorker w;
01931   workOnCells( selectionInfo, w );
01932 }
01933 
01934 
01935 void Sheet::setSelectionTextColor( Selection* selectionInfo,
01936                                    const QColor &tb_Color )
01937 {
01938   FontColorManipulator* manipulator = new FontColorManipulator();
01939   manipulator->setSheet(this);
01940   manipulator->setProperty(Format::PTextPen);
01941   manipulator->setTextColor(tb_Color);
01942   manipulator->add(*selectionInfo);
01943   manipulator->execute();
01944 }
01945 
01946 void Sheet::setSelectionbgColor( Selection* selectionInfo,
01947                                  const QColor &bg_Color )
01948 {
01949   BackgroundColorManipulator* manipulator = new BackgroundColorManipulator();
01950   manipulator->setSheet(this);
01951   manipulator->setProperty(Format::PBackgroundColor);
01952   manipulator->setBackgroundColor(bg_Color);
01953   manipulator->add(*selectionInfo);
01954   manipulator->execute();
01955 }
01956 
01957 
01958 struct SetSelectionBorderColorWorker : public Sheet::CellWorker {
01959     const QColor& bd_Color;
01960     SetSelectionBorderColorWorker( const QColor& _bd_Color ) : Sheet::CellWorker( false ), bd_Color( _bd_Color ) { }
01961 
01962     class UndoAction* createUndoAction( Doc* doc, Sheet* sheet, const KSpread::Region& region ) {
01963         QString title=i18n("Change Border Color");
01964   return new UndoCellFormat( doc, sheet, region, title );
01965     }
01966     bool testCondition( Cell* cell ) {
01967   return ( !cell->isPartOfMerged() );
01968     }
01969     void doWork( Cell* cell, bool, int, int ) {
01970   cell->setDisplayDirtyFlag();
01971   int it_Row = cell->row();
01972   int it_Col = cell->column();
01973   if ( cell->format()->topBorderStyle( it_Row, it_Col )!=Qt::NoPen )
01974     cell->format()->setTopBorderColor( bd_Color );
01975   if ( cell->format()->leftBorderStyle( it_Row, it_Col )!=Qt::NoPen )
01976     cell->format()->setLeftBorderColor( bd_Color );
01977   if ( cell->format()->fallDiagonalStyle( it_Row, it_Col )!=Qt::NoPen )
01978     cell->format()->setFallDiagonalColor( bd_Color );
01979   if ( cell->format()->goUpDiagonalStyle( it_Row, it_Col )!=Qt::NoPen )
01980     cell->format()->setGoUpDiagonalColor( bd_Color );
01981   if ( cell->format()->bottomBorderStyle( it_Row, it_Col )!=Qt::NoPen )
01982     cell->format()->setBottomBorderColor( bd_Color );
01983   if ( cell->format()->rightBorderStyle( it_Row, it_Col )!=Qt::NoPen )
01984     cell->format()->setRightBorderColor( bd_Color );
01985   cell->clearDisplayDirtyFlag();
01986     }
01987 };
01988 
01989 void Sheet::setSelectionBorderColor( Selection* selectionInfo,
01990                                             const QColor &bd_Color )
01991 {
01992     SetSelectionBorderColorWorker w( bd_Color );
01993     workOnCells( selectionInfo, w );
01994 }
01995 
01996 
01997 void Sheet::setSeries( const QPoint &_marker, double start, double end, double step, Series mode, Series type)
01998 {
01999   doc()->emitBeginOperation();
02000 
02001   QString cellText;
02002 
02003   int x,y; /* just some loop counters */
02004 
02005   /* the actual number of columns or rows that the series will span.
02006      i.e. this will count 3 cells for a single cell that spans three rows
02007   */
02008   int numberOfCells;
02009   if (end > start)
02010     numberOfCells = (int) ((end - start) / step + 1); /*initialize for linear*/
02011   else if ( end <start )
02012     numberOfCells = (int) ((start - end) / step + 1); /*initialize for linear*/
02013   else //equal ! => one cell fix infini loop
02014       numberOfCells = 1;
02015   if (type == Geometric)
02016   {
02017     /* basically, A(n) = start ^ n
02018      * so when does end = start ^ n ??
02019      * when n = ln(end) / ln(start)
02020      */
02021     numberOfCells = (int)( (log((double)end) / log((double)start)) +
02022            DBL_EPSILON) + 1;
02023   }
02024 
02025   Cell * cell = NULL;
02026 
02027   /* markers for the top-left corner of the undo region.  It'll probably
02028    * be the top left corner of where the series is, but if something in front
02029    * is obscuring the cell, then it needs to be part of the undo region */
02030   QRect undoRegion;
02031 
02032   undoRegion.setLeft(_marker.x());
02033   undoRegion.setTop(_marker.y());
02034 
02035   /* this whole block is used to find the correct size for the undo region.
02036      We're checking for two different things (in these examples,
02037        mode==column):
02038 
02039        1.  cells are vertically merged.  This means that one value in the
02040        series will span multiple cells.
02041 
02042        2.  a cell in the column is merged to a cell to its left.  In this case
02043        the cell value will be stored in the left most cell so we need to
02044        extend the undo range to include that column.
02045   */
02046   if ( mode == Column )
02047   {
02048     for ( y = _marker.y(); y <= (_marker.y() + numberOfCells - 1); y++ )
02049     {
02050       cell = cellAt( _marker.x(), y );
02051 
02052       if ( cell->isPartOfMerged() )
02053       {
02054         /* case 2. */
02055         cell = cell->obscuringCells().first();
02056         undoRegion.setLeft(QMIN(undoRegion.left(), cell->column()));
02057       }
02058       /* case 1.  Add the extra space to numberOfCells and then skip
02059        over the region.  Note that because of the above if block 'cell'
02060        points to the correct cell in the case where both case 1 and 2
02061        are true
02062       */
02063       numberOfCells += cell->extraYCells();
02064       y += cell->extraYCells();
02065     }
02066     undoRegion.setRight( _marker.x() );
02067     undoRegion.setBottom( y - 1 );
02068 
02069     checkRangeVBorder( undoRegion.bottom() );
02070   }
02071   else if(mode == Row)
02072   {
02073     for ( x = _marker.x(); x <=(_marker.x() + numberOfCells - 1); x++ )
02074     {
02075       /* see the code above for a column series for a description of
02076          what is going on here. */
02077       cell = cellAt( x,_marker.y(), false );
02078 
02079       if ( cell->isPartOfMerged() )
02080       {
02081         cell = cell->obscuringCells().first();
02082         undoRegion.setTop(QMIN(undoRegion.top(), cell->row()));
02083       }
02084       numberOfCells += cell->extraXCells();
02085       x += cell->extraXCells();
02086     }
02087     undoRegion.setBottom( _marker.y() );
02088     undoRegion.setRight( x - 1 );
02089 
02090     checkRangeHBorder( undoRegion.right() );
02091   }
02092 
02093   kdDebug() << "Saving undo information" << endl;
02094 
02095   if ( !doc()->undoLocked() )
02096   {
02097     UndoChangeAreaTextCell *undo = new
02098       UndoChangeAreaTextCell( doc(), this, undoRegion );
02099     doc()->addCommand( undo );
02100   }
02101 
02102   kdDebug() << "Saving undo information done" << endl;
02103 
02104   setRegionPaintDirty( undoRegion );
02105 
02106   x = _marker.x();
02107   y = _marker.y();
02108 
02109   /* now we're going to actually loop through and set the values */
02110   double incr;
02111   Style * s = doc()->styleManager()->defaultStyle();
02112   if (step >= 0 && start < end)
02113   {
02114     for ( incr = start; incr <= end; )
02115     {
02116       cell = nonDefaultCell( x, y, false, s );
02117 
02118       if ( cell->isPartOfMerged() )
02119       {
02120         cell = cell->obscuringCells().first();
02121       }
02122 
02123       //      cell->setCellText(cellText.setNum( incr ));
02124 
02125       cell->setNumber( incr );
02126       if (mode == Column)
02127       {
02128         ++y;
02129         if (cell->doesMergeCells())
02130         {
02131           y += cell->extraYCells();
02132         }
02133       }
02134       else if (mode == Row)
02135       {
02136         ++x;
02137         if (cell->doesMergeCells())
02138         {
02139           x += cell->extraXCells();
02140         }
02141       }
02142       else
02143       {
02144         kdDebug(36001) << "Error in Series::mode" << endl;
02145         return;
02146       }
02147 
02148       if (type == Linear)
02149         incr = incr + step;
02150       else if (type == Geometric)
02151         incr = incr * step;
02152       else
02153       {
02154         kdDebug(36001) << "Error in Series::type" << endl;
02155         return;
02156       }
02157     }
02158   }
02159   else
02160   if (step >= 0 && start > end)
02161   {
02162     for ( incr = start; incr >= end; )
02163     {
02164       cell = nonDefaultCell( x, y, false, s );
02165 
02166       if (cell->isPartOfMerged())
02167       {
02168         cell = cell->obscuringCells().first();
02169       }
02170 
02171       //      cell->setCellText(cellText.setNum( incr ));
02172       cell->setNumber( incr );
02173       if (mode == Column)
02174       {
02175         ++y;
02176         if (cell->doesMergeCells())
02177         {
02178           y += cell->extraYCells();
02179         }
02180       }
02181       else if (mode == Row)
02182       {
02183         ++x;
02184         if (cell->doesMergeCells())
02185         {
02186           x += cell->extraXCells();
02187         }
02188       }
02189       else
02190       {
02191         kdDebug(36001) << "Error in Series::mode" << endl;
02192         return;
02193       }
02194 
02195       if (type == Linear)
02196         incr = incr + step;
02197       else if (type == Geometric)
02198         incr = incr * step;
02199       else
02200       {
02201         kdDebug(36001) << "Error in Series::type" << endl;
02202         return;
02203       }
02204     }
02205   }
02206   else
02207   {
02208     for ( incr = start; incr <= end; )
02209     {
02210       cell = nonDefaultCell( x, y, false, s );
02211 
02212       if (cell->isPartOfMerged())
02213       {
02214         cell = cell->obscuringCells().first();
02215       }
02216 
02217       //cell->setCellText(cellText.setNum( incr ));
02218       cell->setNumber( incr );
02219       if (mode == Column)
02220       {
02221         ++y;
02222         if (cell->doesMergeCells())
02223         {
02224           y += cell->extraYCells();
02225         }
02226       }
02227       else if (mode == Row)
02228       {
02229         ++x;
02230         if (cell->doesMergeCells())
02231         {
02232           x += cell->extraXCells();
02233         }
02234       }
02235       else
02236       {
02237         kdDebug(36001) << "Error in Series::mode" << endl;
02238         return;
02239       }
02240 
02241       if (type == Linear)
02242         incr = incr + step;
02243       else if (type == Geometric)
02244       {
02245 
02246         incr = incr * step;
02247         //a step = 1 into geometric serie is not good
02248         //we don't increase value => infini loop
02249         if (step == 1)
02250             return;
02251       }
02252       else
02253       {
02254         kdDebug(36001) << "Error in Series::type" << endl;
02255         return;
02256       }
02257     }
02258   }
02259 
02260 
02261   //  doc()->emitEndOperation();
02262   emit sig_updateView( this );
02263 }
02264 
02265 
02266 struct SetSelectionPercentWorker : public Sheet::CellWorkerTypeA
02267 {
02268     bool b;
02269     SetSelectionPercentWorker( bool _b ) : b( _b ) { }
02270 
02271     QString getUndoTitle() { return i18n("Format Percent"); }
02272     bool testCondition( RowFormat* ) {
02273         //TODO: no idea what to put here, now that factor's gone :(
02274         return ( true );
02275     }
02276     void doWork( RowFormat* rw ) {
02277   //rw->setPrecision( 0 );
02278   rw->setFormatType( b ? Percentage_format : Generic_format);
02279     }
02280     void doWork( ColumnFormat* cl ) {
02281   cl->setFormatType( b ? Percentage_format : Generic_format);
02282     }
02283     void prepareCell( Cell* cell ) {
02284   cell->format()->clearProperty(Format::PFormatType);
02285   cell->format()->clearNoFallBackProperties( Format::PFormatType );
02286     }
02287     bool testCondition( Cell* cell ) {
02288   return ( !cell->isPartOfMerged() );
02289     }
02290     void doWork( Cell* cell, bool cellRegion, int, int ) {
02291   if ( cellRegion )
02292       cell->setDisplayDirtyFlag();
02293   cell->format()->setFormatType( b ? Percentage_format : Generic_format);
02294   if ( cellRegion )
02295       cell->clearDisplayDirtyFlag();
02296     }
02297 };
02298 
02299 void Sheet::setSelectionPercent( Selection* selectionInfo, bool b )
02300 {
02301     SetSelectionPercentWorker w( b );
02302     workOnCells( selectionInfo, w );
02303 }
02304 
02305 void Sheet::slotAreaModified (const QString &name)
02306 {
02307   d->dependencies->areaModified (name);
02308 }
02309 
02310 
02311 void Sheet::refreshRemoveAreaName(const QString & _areaName)
02312 {
02313   Cell * c = d->cells.firstCell();
02314   QString tmp = "'" + _areaName + "'";
02315   for( ;c ; c = c->nextCell() )
02316   {
02317     if ( c->isFormula() )
02318     {
02319       if (c->text().find(tmp) != -1)
02320       {
02321         if ( !c->makeFormula() )
02322           kdError(36001) << "ERROR: Syntax ERROR" << endl;
02323       }
02324     }
02325   }
02326 }
02327 
02328 void Sheet::refreshChangeAreaName(const QString & _areaName)
02329 {
02330   Cell * c = d->cells.firstCell();
02331   QString tmp = "'" + _areaName + "'";
02332   for( ;c ; c = c->nextCell() )
02333   {
02334     if ( c->isFormula() )
02335     {
02336       if (c->text().find(tmp) != -1)
02337       {
02338         if ( !c->makeFormula() )
02339           kdError(36001) << "ERROR: Syntax ERROR" << endl;
02340         else
02341         {
02342           /* setting a cell calc dirty also sets it paint dirty */
02343           c->setCalcDirtyFlag();
02344         }
02345       }
02346     }
02347   }
02348 }
02349 
02350 void Sheet::changeCellTabName( QString const & old_name, QString const & new_name )
02351 {
02352     Cell* c = d->cells.firstCell();
02353     for( ;c; c = c->nextCell() )
02354     {
02355         if( c->isFormula() )
02356         {
02357             if(c->text().find(old_name)!=-1)
02358             {
02359                 int nb = c->text().contains(old_name+"!");
02360                 QString tmp=old_name+"!";
02361                 int len = tmp.length();
02362                 tmp=c->text();
02363 
02364                 for( int i=0; i<nb; i++ )
02365                 {
02366                     int pos = tmp.find( old_name+"!" );
02367                     tmp.replace( pos, len, new_name+"!" );
02368                 }
02369                 c->setCellText(tmp);
02370             }
02371         }
02372     }
02373 }
02374 
02375 bool Sheet::shiftRow( const QRect &rect,bool makeUndo )
02376 {
02377     UndoInsertCellRow * undo = 0;
02378     if ( !doc()->undoLocked()  &&makeUndo)
02379     {
02380         undo = new UndoInsertCellRow( doc(), this, rect );
02381         doc()->addCommand( undo );
02382     }
02383 
02384     bool res=true;
02385     bool result;
02386     for( int i=rect.top(); i<=rect.bottom(); i++ )
02387     {
02388         for( int j=0; j<=(rect.right()-rect.left()); j++ )
02389         {
02390             result = d->cells.shiftRow( QPoint(rect.left(),i) );
02391             if( !result )
02392                 res=false;
02393         }
02394     }
02395     QPtrListIterator<Sheet> it( workbook()->sheetList() );
02396     for( ; it.current(); ++it )
02397     {
02398         for(int i = rect.top(); i <= rect.bottom(); i++ )
02399             it.current()->changeNameCellRef( QPoint( rect.left(), i ), false,
02400                                              Sheet::ColumnInsert, name(),
02401                                              ( rect.right() - rect.left() + 1),
02402                                              undo);
02403     }
02404     refreshChart(QPoint(rect.left(),rect.top()), false, Sheet::ColumnInsert);
02405     refreshMergedCell();
02406     recalc();
02407     emit sig_updateView( this );
02408 
02409     return res;
02410 }
02411 
02412 bool Sheet::shiftColumn( const QRect& rect,bool makeUndo )
02413 {
02414     UndoInsertCellCol * undo = 0;
02415     if ( !doc()->undoLocked()  &&makeUndo)
02416     {
02417         undo = new UndoInsertCellCol( doc(), this,rect);
02418         doc()->addCommand( undo );
02419     }
02420 
02421     bool res=true;
02422     bool result;
02423     for( int i =rect.left(); i<=rect.right(); i++ )
02424     {
02425         for( int j=0; j<=(rect.bottom()-rect.top()); j++ )
02426         {
02427             result = d->cells.shiftColumn( QPoint(i,rect.top()) );
02428             if(!result)
02429                 res=false;
02430         }
02431     }
02432 
02433     QPtrListIterator<Sheet> it( workbook()->sheetList() );
02434     for( ; it.current(); ++it )
02435     {
02436         for(int i=rect.left();i<=rect.right();i++)
02437             it.current()->changeNameCellRef( QPoint( i, rect.top() ), false,
02438                                              Sheet::RowInsert, name(),
02439                                              ( rect.bottom() - rect.top() + 1 ),
02440                                              undo );
02441     }
02442     refreshChart(/*marker*/QPoint(rect.left(),rect.top()), false, Sheet::RowInsert);
02443     refreshMergedCell();
02444     recalc();
02445     emit sig_updateView( this );
02446 
02447     return res;
02448 }
02449 
02450 void Sheet::unshiftColumn( const QRect & rect,bool makeUndo )
02451 {
02452     UndoRemoveCellCol * undo = 0;
02453     if ( !doc()->undoLocked() && makeUndo )
02454     {
02455         undo = new UndoRemoveCellCol( doc(), this, rect );
02456         doc()->addCommand( undo );
02457     }
02458 
02459     for(int i =rect.top();i<=rect.bottom();i++)
02460         for(int j=rect.left();j<=rect.right();j++)
02461                d->cells.remove(j,i);
02462 
02463     for(int i =rect.left();i<=rect.right();i++)
02464         for(int j=0;j<=(rect.bottom()-rect.top());j++)
02465                 d->cells.unshiftColumn( QPoint(i,rect.top()) );
02466 
02467     QPtrListIterator<Sheet> it( workbook()->sheetList() );
02468     for( ; it.current(); ++it )
02469         for(int i=rect.left();i<=rect.right();i++)
02470                 it.current()->changeNameCellRef( QPoint( i, rect.top() ), false,
02471                                                  Sheet::RowRemove, name(),
02472                                                  ( rect.bottom() - rect.top() + 1 ),
02473                                                  undo );
02474 
02475     refreshChart( QPoint(rect.left(),rect.top()), false, Sheet::RowRemove );
02476     refreshMergedCell();
02477     recalc();
02478     emit sig_updateView( this );
02479 }
02480 
02481 void Sheet::unshiftRow( const QRect & rect,bool makeUndo )
02482 {
02483     UndoRemoveCellRow * undo = 0;
02484     if ( !doc()->undoLocked() && makeUndo )
02485     {
02486         undo = new UndoRemoveCellRow( doc(), this, rect );
02487         doc()->addCommand( undo );
02488     }
02489     for(int i =rect.top();i<=rect.bottom();i++)
02490         for(int j=rect.left();j<=rect.right();j++)
02491                 d->cells.remove(j,i);
02492 
02493     for(int i =rect.top();i<=rect.bottom();i++)
02494         for(int j=0;j<=(rect.right()-rect.left());j++)
02495                 d->cells.unshiftRow( QPoint(rect.left(),i) );
02496 
02497     QPtrListIterator<Sheet> it( workbook()->sheetList() );
02498     for( ; it.current(); ++it )
02499         for(int i=rect.top();i<=rect.bottom();i++)
02500                 it.current()->changeNameCellRef( QPoint( rect.left(), i ), false,
02501                                                  Sheet::ColumnRemove, name(),
02502                                                  ( rect.right() - rect.left() + 1 ),
02503                                                  undo);
02504 
02505     refreshChart(QPoint(rect.left(),rect.top()), false, Sheet::ColumnRemove );
02506     refreshMergedCell();
02507     recalc();
02508     emit sig_updateView( this );
02509 }
02510 
02511 bool Sheet::insertColumn( int col, int nbCol, bool makeUndo )
02512 {
02513     UndoInsertColumn * undo = 0;
02514     if ( !doc()->undoLocked() && makeUndo)
02515     {
02516         undo = new UndoInsertColumn( doc(), this, col, nbCol );
02517         doc()->addCommand( undo );
02518     }
02519 
02520     bool res=true;
02521     bool result;
02522     for( int i=0; i<=nbCol; i++ )
02523     {
02524         // Recalculate range max (minus size of last column)
02525         d->sizeMaxX -= columnFormat( KS_colMax )->dblWidth();
02526 
02527         result = d->cells.insertColumn( col );
02528         d->columns.insertColumn( col );
02529         if(!result)
02530             res = false;
02531 
02532         //Recalculate range max (plus size of new column)
02533         d->sizeMaxX += columnFormat( col+i )->dblWidth();
02534     }
02535 
02536     QPtrListIterator<Sheet> it( workbook()->sheetList() );
02537     for( ; it.current(); ++it )
02538         it.current()->changeNameCellRef( QPoint( col, 1 ), true,
02539                                          Sheet::ColumnInsert, name(),
02540                                          nbCol + 1, undo );
02541 
02542     //update print settings
02543     d->print->insertColumn( col, nbCol );
02544 
02545     refreshChart( QPoint( col, 1 ), true, Sheet::ColumnInsert );
02546     refreshMergedCell();
02547     recalc();
02548     emit sig_updateHBorder( this );
02549     emit sig_updateView( this );
02550 
02551     return res;
02552 }
02553 
02554 bool Sheet::insertRow( int row, int nbRow, bool makeUndo )
02555 {
02556     UndoInsertRow *undo = 0;
02557     if ( !doc()->undoLocked() && makeUndo)
02558     {
02559         undo = new UndoInsertRow( doc(), this, row, nbRow );
02560         doc()->addCommand( undo );
02561     }
02562 
02563     bool res=true;
02564     bool result;
02565     for( int i=0; i<=nbRow; i++ )
02566     {
02567         // Recalculate range max (minus size of last row)
02568         d->sizeMaxY -= rowFormat( KS_rowMax )->dblHeight();
02569 
02570         result = d->cells.insertRow( row );
02571         d->rows.insertRow( row );
02572         if( !result )
02573             res = false;
02574 
02575         //Recalculate range max (plus size of new row)
02576         d->sizeMaxY += rowFormat( row )->dblHeight();
02577     }
02578 
02579     QPtrListIterator<Sheet> it( workbook()->sheetList() );
02580     for( ; it.current(); ++it )
02581         it.current()->changeNameCellRef( QPoint( 1, row ), true,
02582                                          Sheet::RowInsert, name(),
02583                                          nbRow + 1, undo );
02584 
02585     //update print settings
02586     d->print->insertRow( row, nbRow );
02587 
02588     refreshChart( QPoint( 1, row ), true, Sheet::RowInsert );
02589     refreshMergedCell();
02590     recalc();
02591     emit sig_updateVBorder( this );
02592     emit sig_updateView( this );
02593 
02594     return res;
02595 }
02596 
02597 void Sheet::removeColumn( int col, int nbCol, bool makeUndo )
02598 {
02599     UndoRemoveColumn *undo = 0;
02600     if ( !doc()->undoLocked() && makeUndo)
02601     {
02602         undo = new UndoRemoveColumn( doc(), this, col, nbCol );
02603         doc()->addCommand( undo );
02604     }
02605 
02606     for ( int i = 0; i <= nbCol; ++i )
02607     {
02608         // Recalculate range max (minus size of removed column)
02609         d->sizeMaxX -= columnFormat( col )->dblWidth();
02610 
02611         d->cells.removeColumn( col );
02612         d->columns.removeColumn( col );
02613 
02614         //Recalculate range max (plus size of new column)
02615         d->sizeMaxX += columnFormat( KS_colMax )->dblWidth();
02616     }
02617 
02618     QPtrListIterator<Sheet> it( workbook()->sheetList() );
02619     for( ; it.current(); ++it )
02620         it.current()->changeNameCellRef( QPoint( col, 1 ), true,
02621                                          Sheet::ColumnRemove, name(),
02622                                          nbCol + 1, undo );
02623 
02624     //update print settings
02625     d->print->removeColumn( col, nbCol );
02626 
02627     refreshChart( QPoint( col, 1 ), true, Sheet::ColumnRemove );
02628     refreshMergedCell();
02629     recalc();
02630     emit sig_updateHBorder( this );
02631     emit sig_updateView( this );
02632 }
02633 
02634 void Sheet::removeRow( int row, int nbRow, bool makeUndo )
02635 {
02636     UndoRemoveRow *undo = 0;
02637     if ( !doc()->undoLocked() && makeUndo )
02638     {
02639         undo = new UndoRemoveRow( doc(), this, row, nbRow );
02640         doc()->addCommand( undo );
02641     }
02642 
02643     for( int i=0; i<=nbRow; i++ )
02644     {
02645         // Recalculate range max (minus size of removed row)
02646         d->sizeMaxY -= rowFormat( row )->dblHeight();
02647 
02648         d->cells.removeRow( row );
02649         d->rows.removeRow( row );
02650 
02651         //Recalculate range max (plus size of new row)
02652         d->sizeMaxY += rowFormat( KS_rowMax )->dblHeight();
02653     }
02654 
02655     QPtrListIterator<Sheet> it( workbook()->sheetList() );
02656     for( ; it.current(); ++it )
02657         it.current()->changeNameCellRef( QPoint( 1, row ), true,
02658                                          Sheet::RowRemove, name(),
02659                                          nbRow + 1, undo );
02660 
02661     //update print settings
02662     d->print->removeRow( row, nbRow );
02663 
02664     refreshChart( QPoint( 1, row ), true, Sheet::RowRemove );
02665     refreshMergedCell();
02666     recalc();
02667     emit sig_updateVBorder( this );
02668     emit sig_updateView( this );
02669 }
02670 
02671 void Sheet::hideRow(const Region& region)
02672 {
02673   HideShowManipulator* manipulator = new HideShowManipulator();
02674   manipulator->setSheet(this);
02675   manipulator->setManipulateRows(true);
02676   manipulator->add(region);
02677   manipulator->execute();
02678 }
02679 
02680 void Sheet::emitHideRow()
02681 {
02682     emit sig_updateVBorder( this );
02683     emit sig_updateView( this );
02684 }
02685 
02686 void Sheet::showRow(const Region& region)
02687 {
02688   HideShowManipulator* manipulator = new HideShowManipulator();
02689   manipulator->setSheet(this);
02690   manipulator->setManipulateRows(true);
02691   manipulator->setReverse(true);
02692   manipulator->add(region);
02693   manipulator->execute();
02694 }
02695 
02696 
02697 void Sheet::hideColumn(const Region& region)
02698 {
02699   HideShowManipulator* manipulator = new HideShowManipulator();
02700   manipulator->setSheet(this);
02701   manipulator->setManipulateColumns(true);
02702   manipulator->add(region);
02703   manipulator->execute();
02704 }
02705 
02706 void Sheet::emitHideColumn()
02707 {
02708     emit sig_updateHBorder( this );
02709     emit sig_updateView( this );
02710 }
02711 
02712 void Sheet::showColumn(const Region& region)
02713 {
02714   HideShowManipulator* manipulator = new HideShowManipulator();
02715   manipulator->setSheet(this);
02716   manipulator->setManipulateColumns(true);
02717   manipulator->setReverse(true);
02718   manipulator->add(region);
02719   manipulator->execute();
02720 }
02721 
02722 
02723 void Sheet::refreshChart(const QPoint & pos, bool fullRowOrColumn, ChangeRef ref)
02724 {
02725   Cell * c = d->cells.firstCell();
02726   for( ;c; c = c->nextCell() )
02727   {
02728     if ( (ref == ColumnInsert || ref == ColumnRemove) && fullRowOrColumn
02729         && c->column() >= (pos.x() - 1))
02730     {
02731       if (c->updateChart())
02732         return;
02733     }
02734     else if ( (ref == ColumnInsert || ref == ColumnRemove )&& !fullRowOrColumn
02735               && c->column() >= (pos.x() - 1) && c->row() == pos.y() )
02736     {
02737       if (c->updateChart())
02738         return;
02739     }
02740     else if ((ref == RowInsert || ref == RowRemove) && fullRowOrColumn
02741              && c->row() >= (pos.y() - 1))
02742     {
02743       if (c->updateChart())
02744         return;
02745     }
02746     else if ( (ref == RowInsert || ref == RowRemove) && !fullRowOrColumn
02747         && c->column() == pos.x() && c->row() >= (pos.y() - 1) )
02748     {
02749       if (c->updateChart())
02750         return;
02751     }
02752   }
02753 
02754   //refresh chart when there is a chart and you remove
02755   //all cells
02756   if (c == 0L)
02757   {
02758      CellBinding * bind;
02759      for ( bind = firstCellBinding(); bind != 0L; bind = nextCellBinding() )
02760      {
02761        bind->cellChanged( 0 );
02762      }
02763      //    CellBinding * bind = firstCellBinding();
02764      //    if ( bind != 0L )
02765      //      bind->cellChanged( 0 );
02766   }
02767 
02768 }
02769 
02770 void Sheet::refreshMergedCell()
02771 {
02772   Cell* c = d->cells.firstCell();
02773   for( ;c; c = c->nextCell() )
02774   {
02775     if(c->doesMergeCells())
02776       c->mergeCells( c->column(), c->row(), c->extraXCells(), c->extraYCells() );
02777   }
02778 }
02779 
02780 
02781 void Sheet::changeNameCellRef( const QPoint & pos, bool fullRowOrColumn,
02782                                       ChangeRef ref, QString tabname, int nbCol,
02783                                       UndoInsertRemoveAction * undo )
02784 {
02785   bool correctDefaultSheetName = (tabname == name()); // for cells without sheet ref (eg "A1")
02786   Cell* c = d->cells.firstCell();
02787   for( ;c; c = c->nextCell() )
02788   {
02789     if( c->isFormula() )
02790     {
02791       QString origText = c->text();
02792       unsigned int i = 0;
02793       bool error = false;
02794       QString newText;
02795 
02796       bool correctSheetName = correctDefaultSheetName;
02797       //bool previousCorrectSheetName = false;
02798       QChar origCh;
02799       for ( ; i < origText.length(); ++i )
02800       {
02801         origCh = origText[i];
02802         if ( origCh != ':' && origCh != '$' && !origCh.isLetter() )
02803         {
02804           newText += origCh;
02805           // Reset the "correct table indicator"
02806           correctSheetName = correctDefaultSheetName;
02807         }
02808         else // Letter or dollar : maybe start of cell name/range
02809           // (or even ':', like in a range - note that correctSheet is kept in this case)
02810         {
02811           // Collect everything that forms a name (cell name or sheet name)
02812           QString str;
02813           bool sheetNameFound = false; //Sheet names need spaces
02814           for( ; ( i < origText.length() ) &&  // until the end
02815                  (  ( origText[i].isLetter() || origText[i].isDigit() || origText[i] == '$' ) ||  // all text and numbers are welcome
02816                     ( sheetNameFound && origText[i].isSpace() ) ) //in case of a sheet name, we include spaces too
02817                ; ++i )
02818           {
02819             str += origText[i];
02820             if ( origText[i] == '!' )
02821               sheetNameFound = true;
02822           }
02823           // Was it a sheet name ?
02824           if ( origText[i] == '!' )
02825           {
02826             newText += str + '!'; // Copy it (and the '!')
02827             // Look for the sheet name right before that '!'
02828             correctSheetName = ( newText.right( tabname.length()+1 ) == tabname+"!" );
02829           }
02830           else // It must be a cell identifier
02831           {
02832             // Parse it
02833             Point point( str );
02834             if ( point.isValid() )
02835             {
02836               int col = point.pos().x();
02837               int row = point.pos().y();
02838               QString newPoint;
02839 
02840               // Update column
02841               if ( point.columnFixed() )
02842                 newPoint = '$';
02843 
02844               if( ref == ColumnInsert
02845                   && correctSheetName
02846                   && col + nbCol <= KS_colMax
02847                   && col >= pos.x()     // Column after the new one : +1
02848                   && ( fullRowOrColumn || row == pos.y() ) ) // All rows or just one
02849               {
02850                 newPoint += Cell::columnName( col + nbCol );
02851               }
02852               else if( ref == ColumnRemove
02853                        && correctSheetName
02854                        && col > pos.x() // Column after the deleted one : -1
02855                        && ( fullRowOrColumn || row == pos.y() ) ) // All rows or just one
02856               {
02857                 newPoint += Cell::columnName( col - nbCol );
02858               }
02859               else
02860                 newPoint += Cell::columnName( col );
02861 
02862               // Update row
02863               if ( point.rowFixed() )
02864                 newPoint += '$';
02865 
02866               if( ref == RowInsert
02867                   && correctSheetName
02868                   && row + nbCol <= KS_rowMax
02869                   && row >= pos.y() // Row after the new one : +1
02870                   && ( fullRowOrColumn || col == pos.x() ) ) // All columns or just one
02871               {
02872                 newPoint += QString::number( row + nbCol );
02873               }
02874               else if( ref == RowRemove
02875                        && correctSheetName
02876                        && row > pos.y() // Row after the deleted one : -1
02877                        && ( fullRowOrColumn || col == pos.x() ) ) // All columns or just one
02878               {
02879                 newPoint += QString::number( row - nbCol );
02880               }
02881               else
02882                 newPoint += QString::number( row );
02883 
02884               if( correctSheetName &&
02885                   ( ( ref == ColumnRemove
02886                       && col == pos.x() // Column is the deleted one : error
02887                       && ( fullRowOrColumn || row == pos.y() ) ) ||
02888                     ( ref == RowRemove
02889                       && row == pos.y() // Row is the deleted one : error
02890                       && ( fullRowOrColumn || col == pos.x() ) ) ||
02891                     ( ref == ColumnInsert
02892                       && col + nbCol > KS_colMax
02893                       && col >= pos.x()     // Column after the new one : +1
02894                       && ( fullRowOrColumn || row == pos.y() ) ) ||
02895                     ( ref == RowInsert
02896                       && row + nbCol > KS_rowMax
02897                       && row >= pos.y() // Row after the new one : +1
02898                       && ( fullRowOrColumn || col == pos.x() ) ) ) )
02899               {
02900                 newPoint = "#" + i18n("Dependency") + "!";
02901                 error = true;
02902               }
02903 
02904               newText += newPoint;
02905             }
02906             else // Not a cell ref
02907             {
02908               kdDebug(36001) << "Copying (unchanged) : '" << str << "'" << endl;
02909               newText += str;
02910             }
02911             // Copy the char that got us to stop
02912             if ( i < origText.length() ) {
02913               newText += origText[i];
02914               if( origText[i] != ':' )
02915                 correctSheetName = correctDefaultSheetName;
02916             }
02917           }
02918         }
02919       }
02920 
02921       if ( error && undo != 0 ) //Save the original formula, as we cannot calculate the undo of broken formulas
02922       {
02923           QString formulaText = c->text();
02924           int origCol = c->column();
02925           int origRow = c->row();
02926 
02927           if ( ref == ColumnInsert && origCol >= pos.x() )
02928               origCol -= nbCol;
02929           if ( ref == RowInsert && origRow >= pos.y() )
02930               origRow -= nbCol;
02931 
02932           if ( ref == ColumnRemove && origCol >= pos.x() )
02933               origCol += nbCol;
02934           if ( ref == RowRemove && origRow >= pos.y() )
02935               origRow += nbCol;
02936 
02937           undo->saveFormulaReference( this, origCol, origRow, formulaText );
02938       }
02939 
02940       c->setCellText( newText );
02941     }
02942   }
02943 }
02944 
02945 #if 0
02946 void Sheet::replace( const QString &_find, const QString &_replace, long options,
02947                             Canvas *canvas )
02948 {
02949   Selection* selectionInfo = canvas->view()->selectionInfo();
02950 
02951     // Identify the region of interest.
02952     QRect region( selectionInfo->selection() );
02953     QPoint marker( selectionInfo->marker() );
02954 
02955     if (options & KReplaceDialog::SelectedText)
02956     {
02957 
02958         // Complete rows selected ?
02959         if ( util_isRowSelected(region) )
02960         {
02961         }
02962         // Complete columns selected ?
02963         else if ( util_isColumnSelected(region) )
02964         {
02965         }
02966     }
02967     else
02968     {
02969         // All cells.
02970         region.setCoords( 1, 1, d->maxRow, d->maxColumn );
02971     }
02972 
02973     // Create the class that handles all the actual replace stuff, and connect it to its
02974     // local slots.
02975     KReplace dialog( _find, _replace, options );
02976     QObject::connect(
02977         &dialog, SIGNAL( highlight( const QString &, int, int, const QRect & ) ),
02978         canvas, SLOT( highlight( const QString &, int, int, const QRect & ) ) );
02979     QObject::connect(
02980         &dialog, SIGNAL( replace( const QString &, int, int,int, const QRect & ) ),
02981         canvas, SLOT( replace( const QString &, int, int,int, const QRect & ) ) );
02982 
02983     // Now do the replacing...
02984     if ( !doc()->undoLocked() )
02985     {
02986         UndoChangeAreaTextCell *undo = new UndoChangeAreaTextCell( doc(), this, region );
02987         doc()->addCommand( undo );
02988     }
02989 
02990     QRect cellRegion( 0, 0, 0, 0 );
02991     bool bck = options & KFindDialog::FindBackwards;
02992 
02993     int colStart = !bck ? region.left() : region.right();
02994     int colEnd = !bck ? region.right() : region.left();
02995     int rowStart = !bck ? region.top() :region.bottom();
02996     int rowEnd = !bck ? region.bottom() : region.top();
02997     if ( options & KFindDialog::FromCursor ) {
02998         colStart = marker.x();
02999         rowStart =  marker.y();
03000     }
03001     Cell *cell;
03002     for (int row = rowStart ; !bck ? row < rowEnd : row > rowEnd ; !bck ? ++row : --row )
03003     {
03004         for(int col = colStart ; !bck ? col < colEnd : col > colEnd ; !bck ? ++col : --col )
03005         {
03006             cell = cellAt( col, row );
03007             if ( !cell->isDefault() && !cell->isObscured() && !cell->isFormula() )
03008             {
03009                 QString text = cell->text();
03010                 cellRegion.setTop( row );
03011                 cellRegion.setLeft( col );
03012                 if (!dialog.replace( text, cellRegion ))
03013                     return;
03014             }
03015         }
03016     }
03017 }
03018 #endif
03019 
03020 void Sheet::borderBottom( Selection* selectionInfo, const QColor &_color )
03021 {
03022   BorderManipulator* manipulator = new BorderManipulator();
03023   manipulator->setSheet(this);
03024   manipulator->setBottomBorderPen(QPen(_color, 1, Qt::SolidLine));
03025   manipulator->add(*selectionInfo);
03026   manipulator->execute();
03027 }
03028 
03029 void Sheet::borderRight( Selection* selectionInfo, const QColor &_color )
03030 {
03031   BorderManipulator* manipulator = new BorderManipulator();
03032   manipulator->setSheet(this);
03033   manipulator->setRightBorderPen(QPen(_color, 1, Qt::SolidLine));
03034   manipulator->add(*selectionInfo);
03035   manipulator->execute();
03036 }
03037 
03038 void Sheet::borderLeft( Selection* selectionInfo, const QColor &_color )
03039 {
03040   BorderManipulator* manipulator = new BorderManipulator();
03041   manipulator->setSheet(this);
03042   manipulator->setLeftBorderPen(QPen(_color, 1, Qt::SolidLine));
03043   manipulator->add(*selectionInfo);
03044   manipulator->execute();
03045 }
03046 
03047 void Sheet::borderTop( Selection* selectionInfo, const QColor &_color )
03048 {
03049   BorderManipulator* manipulator = new BorderManipulator();
03050   manipulator->setSheet(this);
03051   manipulator->setTopBorderPen(QPen(_color, 1, Qt::SolidLine));
03052   manipulator->add(*selectionInfo);
03053   manipulator->execute();
03054 }
03055 
03056 void Sheet::borderOutline( Selection* selectionInfo, const QColor &_color )
03057 {
03058   BorderManipulator* manipulator = new BorderManipulator();
03059   manipulator->setSheet(this);
03060   manipulator->setTopBorderPen(QPen(_color, 1, Qt::SolidLine));
03061   manipulator->setBottomBorderPen(QPen(_color, 1, Qt::SolidLine));
03062   manipulator->setLeftBorderPen(QPen(_color, 1, Qt::SolidLine));
03063   manipulator->setRightBorderPen(QPen(_color, 1, Qt::SolidLine));
03064   manipulator->add(*selectionInfo);
03065   manipulator->execute();
03066 }
03067 
03068 void Sheet::borderAll( Selection * selectionInfo,
03069                        const QColor & _color )
03070 {
03071   BorderManipulator* manipulator = new BorderManipulator();
03072   manipulator->setSheet(this);
03073   manipulator->setTopBorderPen(QPen(_color, 1, Qt::SolidLine));
03074   manipulator->setBottomBorderPen(QPen(_color, 1, Qt::SolidLine));
03075   manipulator->setLeftBorderPen(QPen(_color, 1, Qt::SolidLine));
03076   manipulator->setRightBorderPen(QPen(_color, 1, Qt::SolidLine));
03077   manipulator->setHorizontalPen(QPen(_color, 1, Qt::SolidLine));
03078   manipulator->setVerticalPen(QPen(_color, 1, Qt::SolidLine));
03079   manipulator->add(*selectionInfo);
03080   manipulator->execute();
03081 }
03082 
03083 void Sheet::borderRemove( Selection* selectionInfo )
03084 {
03085   BorderManipulator* manipulator = new BorderManipulator();
03086   manipulator->setSheet(this);
03087   manipulator->setTopBorderPen(QPen(Qt::NoPen));
03088   manipulator->setBottomBorderPen(QPen(Qt::NoPen));
03089   manipulator->setLeftBorderPen(QPen(Qt::NoPen));
03090   manipulator->setRightBorderPen(QPen(Qt::NoPen));
03091   manipulator->setHorizontalPen(QPen(Qt::NoPen));
03092   manipulator->setVerticalPen(QPen(Qt::NoPen));
03093   manipulator->add(*selectionInfo);
03094   manipulator->execute();
03095 }
03096 
03097 
03098 void Sheet::sortByRow( const QRect &area, int ref_row, SortingOrder mode )
03099 {
03100   Point point;
03101   point.setSheet(this);
03102   point.setSheetName (d->name);
03103   point.setPos(area.topLeft());
03104   point.setColumnFixed(false);
03105   point.setRowFixed(false);
03106 
03107   sortByRow( area, ref_row, 0, 0, mode, mode, mode, 0, false, false, point,true );
03108 }
03109 
03110 void Sheet::sortByColumn( const QRect &area, int ref_column, SortingOrder mode )
03111 {
03112   Point point;
03113   point.setSheet(this);
03114   point.setSheetName(d->name);
03115   point.setPos(area.topLeft());
03116   point.setColumnFixed(false);
03117   point.setRowFixed(false);
03118 
03119   sortByColumn( area, ref_column, 0, 0, mode, mode, mode, 0, false, false,
03120                 point,true );
03121 }
03122 
03123 void Sheet::checkCellContent(Cell * cell1, Cell * cell2, int & ret)
03124 {
03125   if ( cell1->isEmpty() )
03126   {
03127     ret = 1;
03128     return;
03129   }
03130   else if ( cell1->isObscured() && cell1->isPartOfMerged() )
03131   {
03132     ret = 1;
03133     return;
03134   }
03135   else if ( cell2->isEmpty() )
03136   {
03137     ret = 2;
03138     return;
03139   }
03140   ret = 0;
03141 }
03142 
03143 void Sheet::sortByRow( const QRect &area, int key1, int key2, int key3,
03144                               SortingOrder order1, SortingOrder order2,
03145                               SortingOrder order3,
03146                               QStringList const * firstKey, bool copyFormat,
03147                               bool headerRow, Point const & outputPoint, bool respectCase )
03148 {
03149   QRect r( area );
03150   Map::respectCase = respectCase;
03151   Q_ASSERT( order1 == Increase || order1 == Decrease );
03152 
03153   // It may not happen that entire columns are selected.
03154   Q_ASSERT( util_isColumnSelected(r) == false );
03155 
03156   // Are entire rows selected ?
03157   if ( util_isRowSelected(r) )
03158   {
03159     r.setLeft( KS_colMax );
03160     r.setRight( 0 );
03161 
03162     // Determine a correct left and right.
03163     // Iterate over all cells to find out which cells are
03164     // located in the selected rows.
03165     for ( int row = r.top(); row <= r.bottom(); ++row )
03166     {
03167       Cell * c = getFirstCellRow( row );
03168       int col;
03169       while ( c )
03170       {
03171         col = c->column();
03172         if ( !c->isEmpty() )
03173         {
03174           if ( col > r.right() )
03175             r.rRight() = col;
03176           if ( col < r.left() )
03177             r.rLeft() = col;
03178         }
03179         c = getNextCellRight( col, row );
03180       }
03181     }
03182 
03183     // Any cells to sort here ?
03184     if ( r.right() < r.left() )
03185     {
03186         Map::respectCase = true;
03187         return;
03188     }
03189   }
03190 
03191   QRect target( outputPoint.pos().x(), outputPoint.pos().y(), r.width(), r.height() );
03192 
03193   doc()->emitBeginOperation();
03194 
03195   if ( !doc()->undoLocked() )
03196   {
03197     UndoSort *undo = new UndoSort( doc(), this, target );
03198     doc()->addCommand( undo );
03199   }
03200 
03201   if (target.topLeft() != r.topLeft())
03202   {
03203     int targetLeft = target.left();
03204     int targetTop  = target.top();
03205     int sourceTop  = r.top();
03206     int sourceLeft = r.left();
03207 
03208     key1 = key1 - sourceTop + targetTop;
03209     key2 = key2 - sourceTop + targetTop;
03210     key3 = key3 - sourceTop + targetTop;
03211 
03212     for ( int x = 0; x < r.width(); ++x)
03213     {
03214       for ( int y = 0; y < r.height(); ++y )
03215       {
03216         // from - to
03217         copyCells( sourceLeft + x, sourceTop + y,
03218                    targetLeft + x, targetTop + y, copyFormat );
03219       }
03220     }
03221   }
03222 
03223   // Sorting algorithm: David's :). Well, I guess it's called minmax or so.
03224   // For each column, we look for all cells right hand of it and we find the one to swap with it.
03225   // Much faster than the awful bubbleSort...
03226   Cell * cell;
03227   Cell * cell1;
03228   Cell * cell2;
03229   Cell * bestCell;
03230   int status = 0;
03231 
03232   for ( int d = target.left();  d <= target.right(); ++d )
03233   {
03234     cell1 = cellAt( d, key1 );
03235     if ( cell1->isObscured() && cell1->isPartOfMerged() )
03236     {
03237       Cell* obscuring = cell1->obscuringCells().first();
03238       cell = cellAt( obscuring->column(), key1 );
03239       cell1 = cellAt( obscuring->column() + cell->extraXCells() + 1,
03240                       obscuring->column());
03241       d = obscuring->column() + cell->extraXCells() + 1;
03242     }
03243 
03244     // Look for which column we want to swap with the one number d
03245     bestCell = cell1;
03246     int bestX = d;
03247     for ( int x = d + 1 ; x <= target.right(); x++ )
03248     {
03249       cell2 = cellAt( x, key1 );
03250 
03251       checkCellContent(cell2, bestCell, status);
03252       if (status == 1)
03253         continue;
03254       else if (status == 2)
03255       {
03256         // empty cells are always shifted to the end
03257         bestCell = cell2;
03258         bestX = x;
03259         continue;
03260       }
03261 
03262       if ( firstKey )
03263       {
03264         int i1 = firstKey->findIndex( cell2->text() );
03265         int i2 = firstKey->findIndex( bestCell->text() );
03266 
03267         if ( i1 != -1 && i2 != -1 )
03268         {
03269           if ( (order1 == Increase && i1 < i2 )
03270                || (order1 == Decrease && i1 > i2) )
03271           {
03272             bestCell = cell2;
03273             bestX = x;
03274             continue;
03275           }
03276 
03277           if ( i1 == i2 )
03278           {
03279             // check 2nd key
03280             if (key2 <= 0)
03281               continue;
03282 
03283             Cell * cell22 = cellAt( x, key2 );
03284             Cell * bestCell2 = cellAt( bestX, key2 );
03285 
03286             if ( cell22->isEmpty() )
03287             {
03288               /* No need to swap */
03289               continue;
03290             }
03291             else if ( cell22->isObscured() && cell22->isPartOfMerged() )
03292             {
03293               /* No need to swap */
03294               continue;
03295             }
03296             else if ( bestCell2->isEmpty() )
03297             {
03298               // empty cells are always shifted to the end
03299               bestCell = cell2;
03300               bestX = x;
03301               continue;
03302             }
03303 
03304             if ( (order2 == Increase && *cell22 < *bestCell2)
03305                  || (order2 == Decrease && *cell22 > *bestCell2) )
03306             {
03307               bestCell = cell2;
03308               bestX = x;
03309               continue;
03310             }
03311             else if ( (order2 == Increase && *cell22 > *bestCell2)
03312                       || (order2 == Decrease && *cell22 < *bestCell2) )
03313             {
03314               // already in right order
03315               continue;
03316             }
03317             else
03318             {
03319               // they are equal, check 3rd key
03320               if (key3 <= 0)
03321                 continue;
03322 
03323               Cell * cell23 = cellAt( x, key3 );
03324               Cell * bestCell3 = cellAt( bestX, key3 );
03325 
03326               if ( cell23->isEmpty() )
03327               {
03328                 /* No need to swap */
03329                 continue;
03330               }
03331               else if ( cell23->isObscured() && cell23->isPartOfMerged() )
03332               {
03333                 /* No need to swap */
03334                 continue;
03335               }
03336               else if ( bestCell3->isEmpty() )
03337               {
03338                 // empty cells are always shifted to the end
03339                 bestCell = cell2;
03340                 bestX = x;
03341                 continue;
03342               }
03343               if ( (order3 == Increase && *cell23 < *bestCell3)
03344                    || (order3 == Decrease && *cell23 > *bestCell3) )
03345               {
03346                 // they are really equal or in the right order
03347                 // no swap necessary
03348                 continue;
03349               }
03350               else
03351               {
03352                 bestCell = cell2;
03353                 bestX = x;
03354                 continue;
03355               }
03356             }
03357           }
03358           continue;
03359         }
03360         else if ( i1 != -1 && i2 == -1 )
03361         {
03362           // if not in the key list, the cell is shifted to the end - always
03363           bestCell = cell2;
03364           bestX = x;
03365           continue;
03366        }
03367         else if ( i2 != -1 && i1 == -1 )
03368         {
03369           // only text of cell2 is in the list so it is smaller than bestCell
03370           /* No need to swap */
03371           continue;
03372         }
03373 
03374         // if i1 and i2 are equals -1 go on:
03375       } // end if (firstKey)
03376 
03377       // Here we use the operators < and > for cells, which do it all.
03378       if ( (order1 == Increase && *cell2 < *bestCell)
03379            || (order1 == Decrease && *cell2 > *bestCell) )
03380       {
03381         bestCell = cell2;
03382         bestX = x;
03383         continue;
03384       }
03385       else if ( (order1 == Increase && *cell2 > *bestCell)
03386                 || (order1 == Decrease && *cell2 < *bestCell) )
03387       {
03388         // no change necessary
03389         continue;
03390       }
03391       else
03392       {
03393         // *cell2 equals *bestCell
03394         // check 2nd key
03395         if (key2 <= 0)
03396           continue;
03397         Cell * cell22 = cellAt( d, key2 );
03398         Cell * bestCell2 = cellAt( x, key2 );
03399 
03400         checkCellContent(cell2, bestCell, status);
03401         if (status == 1)
03402           continue;
03403         else if (status == 2)
03404         {
03405           // empty cells are always shifted to the end
03406           bestCell = cell2;
03407           bestX = x;
03408           continue;
03409         }
03410 
03411         if ( (order2 == Increase && *cell22 > *bestCell2)
03412              || (order2 == Decrease && *cell22 < *bestCell2) )
03413         {
03414           bestCell = cell2;
03415           bestX = x;
03416           continue;
03417         }
03418         else
03419         if ( (order2 == Increase && *cell22 > *bestCell2)
03420              || (order2 == Decrease && *cell22 < *bestCell2) )
03421         {
03422           // already in right order
03423           continue;
03424         }
03425         else
03426         {
03427           // they are equal, check 3rd key
03428           if (key3 == 0)
03429             continue;
03430           Cell * cell23 = cellAt( d, key3 );
03431           Cell * bestCell3 = cellAt( x, key3 );
03432 
03433           checkCellContent(cell2, bestCell, status);
03434           if (status == 1)
03435             continue;
03436           else if (status == 2)
03437           {
03438             // empty cells are always shifted to the end
03439             bestCell = cell2;
03440             bestX = x;
03441             continue;
03442           }
03443           if ( (order3 == Increase && *cell23 > *bestCell3)
03444                || (order3 == Decrease && *cell23 < *bestCell3) )
03445           {
03446             bestCell = cell2;
03447             bestX = x;
03448             continue;
03449           }
03450           else
03451           {
03452             // they are really equal
03453             // no swap necessary
03454             continue;
03455           }
03456         }
03457       }
03458     }
03459 
03460     // Swap columns cell1 and bestCell (i.e. d and bestX)
03461     if ( d != bestX )
03462     {
03463       int top = target.top();
03464       if (headerRow)
03465         ++top;
03466 
03467       for( int y = target.bottom(); y >= top; --y )
03468       {
03469         if ( y != key1 && y != key2 && y != key3 )
03470           swapCells( d, y, bestX, y, copyFormat );
03471       }
03472       if (key3 > 0)
03473         swapCells( d, key3, bestX, key3, copyFormat );
03474       if (key2 > 0)
03475         swapCells( d, key2, bestX, key2, copyFormat );
03476       swapCells( d, key1, bestX, key1, copyFormat );
03477     }
03478   } // for (d = ...; ...; ++d)
03479   Map::respectCase = true;
03480   //  doc()->emitEndOperation();
03481   emit sig_updateView( this );
03482 }
03483 
03484 void Sheet::sortByColumn( const QRect &area, int key1, int key2, int key3,
03485                                  SortingOrder order1, SortingOrder order2,
03486                                  SortingOrder order3,
03487                                  QStringList const * firstKey, bool copyFormat,
03488                                  bool headerRow,
03489                                  Point const & outputPoint, bool respectCase )
03490 {
03491   QRect r( area );
03492   Map::respectCase = respectCase;
03493 
03494   Q_ASSERT( order1 == Increase || order1 == Decrease );
03495 
03496   // It may not happen that entire rows are selected.
03497   Q_ASSERT( util_isRowSelected(r) == false );
03498 
03499   // Are entire columns selected ?
03500   if ( util_isColumnSelected(r) )
03501   {
03502     r.setTop( KS_rowMax );
03503     r.setBottom( 0 );
03504 
03505     // Determine a correct top and bottom.
03506     // Iterate over all cells to find out which cells are
03507     // located in the selected columns.
03508     for ( int col = r.left(); col <= r.right(); ++col )
03509     {
03510       Cell * c = getFirstCellColumn( col );
03511       int row;
03512       while ( c )
03513       {
03514         row = c->row();
03515         if ( !c->isEmpty() )
03516         {
03517           if ( row > r.bottom() )
03518             r.rBottom() = row;
03519           if ( row < r.top() )
03520             r.rTop() = row;
03521         }
03522         c = getNextCellDown( col, row );
03523       }
03524     }
03525 
03526     // Any cells to sort here ?
03527     if ( r.bottom() < r.top() )
03528     {
03529         Map::respectCase = true;
03530       return;
03531     }
03532   }
03533   QRect target( outputPoint.pos().x(), outputPoint.pos().y(), r.width(), r.height() );
03534 
03535   if ( !doc()->undoLocked() )
03536   {
03537     UndoSort *undo = new UndoSort( doc(), this, target );
03538     doc()->addCommand( undo );
03539   }
03540 
03541   doc()->emitBeginOperation();
03542 
03543   if (target.topLeft() != r.topLeft())
03544   {
03545     int targetLeft = target.left();
03546     int targetTop  = target.top();
03547     int sourceTop  = r.top();
03548     int sourceLeft = r.left();
03549 
03550     key1 = key1 - sourceLeft + targetLeft;
03551     key2 = key2 - sourceLeft + targetLeft;
03552     key3 = key3 - sourceLeft + targetLeft;
03553 
03554     for ( int x = 0; x < r.width(); ++x)
03555     {
03556       for ( int y = 0; y < r.height(); ++y )
03557       {
03558         // from - to
03559         copyCells( sourceLeft + x, sourceTop + y,
03560                    targetLeft + x, targetTop + y, copyFormat );
03561       }
03562     }
03563   }
03564 
03565   // Sorting algorithm: David's :). Well, I guess it's called minmax or so.
03566   // For each row, we look for all rows under it and we find the one to swap with it.
03567   // Much faster than the awful bubbleSort...
03568   // Torben: Asymptotically it is alltogether O(n^2) :-)
03569 
03570   Cell * cell;
03571   Cell * cell1;
03572   Cell * cell2;
03573   Cell * bestCell;
03574   int status = 0;
03575 
03576   int d = target.top();
03577 
03578   if (headerRow)
03579     ++d;
03580 
03581   for ( ; d <= target.bottom(); ++d )
03582   {
03583     // Look for which row we want to swap with the one number d
03584     cell1 = cellAt( key1, d );
03585     if ( cell1->isObscured() && cell1->isPartOfMerged() )
03586     {
03587       Cell* obscuring = cell1->obscuringCells().first();
03588       cell  = cellAt( key1, obscuring->row() );
03589       cell1 = cellAt( key1, obscuring->row() + cell->extraYCells() + 1 );
03590       d     = obscuring->row() + cell->extraYCells() + 1;
03591     }
03592 
03593     bestCell  = cell1;
03594     int bestY = d;
03595 
03596     for ( int y = d + 1 ; y <= target.bottom(); ++y )
03597     {
03598       cell2 = cellAt( key1, y );
03599 
03600       if ( cell2->isEmpty() )
03601       {
03602         /* No need to swap */
03603         continue;
03604       }
03605       else if ( cell2->isObscured() && cell2->isPartOfMerged() )
03606       {
03607         /* No need to swap */
03608         continue;
03609       }
03610       else if ( bestCell->isEmpty() )
03611       {
03612         // empty cells are always shifted to the end
03613         bestCell = cell2;
03614         bestY = y;
03615         continue;
03616       }
03617 
03618       if ( firstKey )
03619       {
03620         int i1 = firstKey->findIndex( cell2->text() );
03621         int i2 = firstKey->findIndex( bestCell->text() );
03622 
03623         if ( i1 != -1 && i2 != -1 )
03624         {
03625           if ( (order1 == Increase && i1 < i2 )
03626                || (order1 == Decrease && i1 > i2) )
03627           {
03628             bestCell = cell2;
03629             bestY = y;
03630             continue;
03631           }
03632 
03633           if ( i1 == i2 )
03634           {
03635             // check 2nd key
03636             if (key2 <= 0)
03637               continue;
03638             Cell * cell22 = cellAt( key2, d );
03639             Cell * bestCell2 = cellAt( key2, y );
03640 
03641             if ( cell22->isEmpty() )
03642             {
03643               /* No need to swap */
03644               continue;
03645             }
03646             else if ( cell22->isObscured() && cell22->isPartOfMerged() )
03647             {
03648               /* No need to swap */
03649               continue;
03650             }
03651             else if ( bestCell2->isEmpty() )
03652             {
03653               // empty cells are always shifted to the end
03654               bestCell = cell2;
03655               bestY = y;
03656               continue;
03657             }
03658 
03659             if ( (order2 == Increase && *cell22 > *bestCell2)
03660                  || (order2 == Decrease && *cell22 < *bestCell2) )
03661             {
03662               bestCell = cell2;
03663               bestY = y;
03664               continue;
03665             }
03666             else if ( (order2 == Increase && *cell22 < *bestCell2)
03667                       || (order2 == Decrease && *cell22 > *bestCell2) )
03668             {
03669               // already in right order
03670               continue;
03671             }
03672             else
03673             {
03674               // they are equal, check 3rd key
03675               if (key3 <= 0)
03676                 continue;
03677               Cell * cell23 = cellAt( key3, d );
03678               Cell * bestCell3 = cellAt( key3, y );
03679 
03680               checkCellContent(cell2, bestCell, status);
03681               if (status == 1)
03682                 continue;
03683               else if (status == 2)
03684               {
03685                 // empty cells are always shifted to the end
03686                 bestCell = cell2;
03687                 bestY = y;
03688                 continue;
03689               }
03690 
03691               if ( (order3 == Increase && *cell23 < *bestCell3)
03692                    || (order3 == Decrease && *cell23 > *bestCell3) )
03693               {
03694                 bestCell = cell2;
03695                 bestY = y;
03696                 continue;
03697               }
03698               else
03699               {
03700                 // they are really equal or in the correct order
03701                 // no swap necessary
03702                 continue;
03703               }
03704             }
03705           }
03706           continue;
03707         }
03708         else if ( i1 != -1 && i2 == -1 )
03709         {
03710           // if not in the key list, the cell is shifted to the end - always
03711           bestCell = cell2;
03712           bestY = y;
03713           continue;
03714         }
03715         else if ( i2 != -1 && i1 == -1 )
03716         {
03717           // only text of cell2 is in the list so it is smaller than bestCell
03718           /* No need to swap */
03719           continue;
03720         }
03721 
03722         // if i1 and i2 are equals -1 go on:
03723       } // if (firstKey)
03724 
03725 
03726         // Here we use the operators < and > for cells, which do it all.
03727       if ( (order1 == Increase && *cell2 < *bestCell)
03728            || (order1 == Decrease && *cell2 > *bestCell) )
03729       {
03730         bestCell = cell2;
03731         bestY = y;
03732       }
03733       else if ( (order1 == Increase && *cell2 > *bestCell)
03734                 || (order1 == Decrease && *cell2 < *bestCell) )
03735       {
03736         // no change necessary
03737         continue;
03738       }
03739       else
03740       {
03741         // *cell2 equals *bestCell
03742         // check 2nd key
03743         if (key2 == 0)
03744           continue;
03745         Cell * cell22 = cellAt( key2, y );
03746         Cell * bestCell2 = cellAt( key2, bestY );
03747 
03748         if ( cell22->isEmpty() )
03749         {
03750           /* No need to swap */
03751           continue;
03752         }
03753         else if ( cell22->isObscured() && cell22->isPartOfMerged() )
03754         {
03755           /* No need to swap */
03756           continue;
03757         }
03758         else if ( bestCell2->isEmpty() )
03759         {
03760           // empty cells are always shifted to the end
03761           bestCell = cell2;
03762           bestY = y;
03763           continue;
03764         }
03765 
03766         if ( (order2 == Increase && *cell22 < *bestCell2)
03767              || (order2 == Decrease && *cell22 > *bestCell2) )
03768         {
03769           bestCell = cell2;
03770           bestY = y;
03771           continue;
03772         }
03773         else if ( (order2 == Increase && *cell22 > *bestCell2)
03774                   || (order2 == Decrease && *cell22 < *bestCell2) )
03775         {
03776           continue;
03777         }
03778         else
03779         {
03780           // they are equal, check 3rd key
03781           if (key3 == 0)
03782             continue;
03783           Cell * cell23 = cellAt( key3, y );
03784           Cell * bestCell3 = cellAt( key3, bestY );
03785 
03786           if ( cell23->isEmpty() )
03787           {
03788             /* No need to swap */
03789             continue;
03790           }
03791           else if ( cell23->isObscured() && cell23->isPartOfMerged() )
03792           {
03793             /* No need to swap */
03794             continue;
03795           }
03796           else if ( bestCell3->isEmpty() )
03797           {
03798             // empty cells are always shifted to the end
03799             bestCell = cell2;
03800             bestY = y;
03801             continue;
03802           }
03803 
03804           if ( (order3 == Increase && *cell23 < *bestCell3)
03805                || (order3 == Decrease && *cell23 > *bestCell3) )
03806           {
03807             bestCell = cell2;
03808             bestY = y;
03809             continue;
03810           }
03811           else
03812           {
03813             // they are really equal or already in the correct order
03814             // no swap necessary
03815             continue;
03816           }
03817         }
03818       }
03819     }
03820 
03821     // Swap rows cell1 and bestCell (i.e. d and bestY)
03822     if ( d != bestY )
03823     {
03824       for (int x = target.left(); x <= target.right(); ++x)
03825       {
03826         if ( x != key1 && x != key2 && x != key3)
03827           swapCells( x, d, x, bestY, copyFormat );
03828       }
03829       if (key3 > 0)
03830         swapCells( key3, d, key3, bestY, copyFormat );
03831       if (key2 > 0)
03832         swapCells( key2, d, key2, bestY, copyFormat );
03833       swapCells( key1, d, key1, bestY, copyFormat );
03834     }
03835   } // for (d = ...; ...; ++d)
03836   // doc()->emitEndOperation();
03837   Map::respectCase = true;
03838   emit sig_updateView( this );
03839 }
03840 
03841 // from - to - copyFormat
03842 void Sheet::copyCells( int x1, int y1, int x2, int y2, bool cpFormat )
03843 {
03844   Cell * sourceCell = cellAt( x1, y1 );
03845   Cell * targetCell = cellAt( x2, y2 );
03846 
03847   if ( sourceCell->isDefault() && targetCell->isDefault())
03848   {
03849     // if the source and target is default there is nothing to copy
03850     return;
03851   }
03852 
03853   targetCell = nonDefaultCell(x2, y2);
03854 
03855   // TODO: check if this enough
03856   targetCell->copyContent( sourceCell );
03857 
03858   /*
03859     if ( !sourceCell->isFormula() )
03860     {
03861     targetCell->copyContent( sourceCell );
03862     }
03863     else
03864     {
03865     targetCell->setCellText( targetCell->decodeFormula( sourceCell->encodeFormula() ) );
03866     targetCell->setCalcDirtyFlag();
03867     targetCell->calc(false);
03868   }
03869   */
03870 
03871   if (cpFormat)
03872   {
03873     targetCell->copyFormat( sourceCell );
03874     /*
03875     targetCell->setAlign( sourceCell->format()->align( x1, y1 ) );
03876     targetCell->setAlignY( sourceCell->format()->alignY( x1, y1 ) );
03877     targetCell->setTextFont( sourceCell->format()->textFont( x1, y1 ) );
03878     targetCell->setTextColor( sourceCell->textColor( x1, y1 ) );
03879     targetCell->setBgColor( sourceCell->bgColor( x1, y1 ) );
03880     targetCell->setLeftBorderPen( sourceCell->leftBorderPen( x1, y1 ) );
03881     targetCell->setTopBorderPen( sourceCell->topBorderPen( x1, y1 ) );
03882     targetCell->setBottomBorderPen( sourceCell->bottomBorderPen( x1, y1 ) );
03883     targetCell->setRightBorderPen( sourceCell->rightBorderPen( x1, y1 ) );
03884     targetCell->setFallDiagonalPen( sourceCell->fallDiagonalPen( x1, y1 ) );
03885     targetCell->setGoUpDiagonalPen( sourceCell->goUpDiagonalPen( x1, y1 ) );
03886     targetCell->setBackGroundBrush( sourceCell->backGroundBrush( x1, y1 ) );
03887     targetCell->setPrecision( sourceCell->precision( x1, y1 ) );
03888     targetCell->format()->setPrefix( sourceCell->prefix( x1, y1 ) );
03889     targetCell->format()->setPostfix( sourceCell->postfix( x1, y1 ) );
03890     targetCell->setFloatFormat( sourceCell->floatFormat( x1, y1 ) );
03891     targetCell->setFloatColor( sourceCell->floatColor( x1, y1 ) );
03892     targetCell->setMultiRow( sourceCell->multiRow( x1, y1 ) );
03893     targetCell->setVerticalText( sourceCell->verticalText( x1, y1 ) );
03894     targetCell->setStyle( sourceCell->style() );
03895     targetCell->setDontPrintText( sourceCell->getDontprintText( x1, y1 ) );
03896     targetCell->setIndent( sourceCell->getIndent( x1, y1 ) );
03897     targetCell->SetConditionList(sourceCell->GetConditionList());
03898     targetCell->setComment( sourceCell->comment( x1, y1 ) );
03899     targetCell->setAngle( sourceCell->getAngle( x1, y1 ) );
03900     targetCell->setFormatType( sourceCell->getFormatType( x1, y1 ) );
03901     */
03902   }
03903 }
03904 
03905 void Sheet::swapCells( int x1, int y1, int x2, int y2, bool cpFormat )
03906 {
03907   Cell * ref1 = cellAt( x1, y1 );
03908   Cell * ref2 = cellAt( x2, y2 );
03909 
03910   if ( ref1->isDefault() )
03911   {
03912     if ( !ref2->isDefault() )
03913     {
03914       ref1 = nonDefaultCell( x1, y1 );
03915       // TODO : make ref2 default instead of copying a default cell into it
03916     }
03917     else
03918       return; // nothing to do
03919   }
03920   else
03921     if ( ref2->isDefault() )
03922     {
03923       ref2 = nonDefaultCell( x2, y2 );
03924       // TODO : make ref1 default instead of copying a default cell into it
03925     }
03926 
03927   // Dummy cell used for swapping cells.
03928   // In fact we copy only content and no layout
03929   // information. Imagine sorting in a sheet. Swapping
03930   // the format while sorting is not what you would expect
03931   // as a user.
03932   if (!ref1->isFormula() && !ref2->isFormula())
03933   {
03934     Cell *tmp = new Cell( this, -1, -1 );
03935 
03936     tmp->copyContent( ref1 );
03937     ref1->copyContent( ref2 );
03938     ref2->copyContent( tmp );
03939 
03940     delete tmp;
03941   }
03942   else
03943     if ( ref1->isFormula() && ref2->isFormula() )
03944     {
03945       QString d = ref1->encodeFormula();
03946       ref1->setCellText( ref1->decodeFormula( ref2->encodeFormula( ) ) );
03947       ref1->setCalcDirtyFlag();
03948       ref1->calc(false);
03949       ref2->setCellText( ref2->decodeFormula( d ) );
03950       ref2->setCalcDirtyFlag();
03951       ref2->calc(false);
03952     }
03953     else
03954       if (ref1->isFormula() && !ref2->isFormula() )
03955       {
03956         QString d = ref1->encodeFormula();
03957         ref1->setCellText(ref2->text());
03958         ref2->setCellText(ref2->decodeFormula(d));
03959         ref2->setCalcDirtyFlag();
03960         ref2->calc(false);
03961       }
03962       else
03963         if (!ref1->isFormula() && ref2->isFormula() )
03964         {
03965           QString d = ref2->encodeFormula();
03966           ref2->setCellText(ref1->text());
03967           ref1->setCellText(ref1->decodeFormula(d));
03968           ref1->setCalcDirtyFlag();
03969           ref1->calc(false);
03970         }
03971 
03972   if (cpFormat)
03973   {
03974     Format::Align a = ref1->format()->align( ref1->column(), ref1->row() );
03975     ref1->format()->setAlign( ref2->format()->align( ref2->column(), ref2->row() ) );
03976     ref2->format()->setAlign(a);
03977 
03978     Format::AlignY ay = ref1->format()->alignY( ref1->column(), ref1->row() );
03979     ref1->format()->setAlignY( ref2->format()->alignY( ref2->column(), ref2->row() ) );
03980     ref2->format()->setAlignY(ay);
03981 
03982     QFont textFont = ref1->format()->textFont( ref1->column(), ref1->row() );
03983     ref1->format()->setTextFont( ref2->format()->textFont( ref2->column(), ref2->row() ) );
03984     ref2->format()->setTextFont(textFont);
03985 
03986     QColor textColor = ref1->format()->textColor( ref1->column(), ref1->row() );
03987     ref1->format()->setTextColor( ref2->format()->textColor( ref2->column(), ref2->row() ) );
03988     ref2->format()->setTextColor(textColor);
03989 
03990     QColor bgColor = ref1->bgColor( ref1->column(), ref1->row() );
03991     ref1->format()->setBgColor( ref2->bgColor( ref2->column(), ref2->row() ) );
03992     ref2->format()->setBgColor(bgColor);
03993 
03994     QPen lbp = ref1->leftBorderPen( ref1->column(), ref1->row() );
03995     ref1->setLeftBorderPen( ref2->leftBorderPen( ref2->column(), ref2->row() ) );
03996     ref2->setLeftBorderPen(lbp);
03997 
03998     QPen tbp = ref1->topBorderPen( ref1->column(), ref1->row() );
03999     ref1->setTopBorderPen( ref2->topBorderPen( ref2->column(), ref2->row() ) );
04000     ref2->setTopBorderPen(tbp);
04001 
04002     QPen bbp = ref1->bottomBorderPen( ref1->column(), ref1->row() );
04003     ref1->setBottomBorderPen( ref2->bottomBorderPen( ref2->column(), ref2->row() ) );
04004     ref2->setBottomBorderPen(bbp);
04005 
04006     QPen rbp = ref1->rightBorderPen( ref1->column(), ref1->row() );
04007     ref1->setRightBorderPen( ref2->rightBorderPen( ref2->column(), ref2->row() ) );
04008     ref2->setRightBorderPen(rbp);
04009 
04010     QPen fdp = ref1->format()->fallDiagonalPen( ref1->column(), ref1->row() );
04011     ref1->format()->setFallDiagonalPen( ref2->format()->fallDiagonalPen( ref2->column(), ref2->row() ) );
04012     ref2->format()->setFallDiagonalPen(fdp);
04013 
04014     QPen udp = ref1->format()->goUpDiagonalPen( ref1->column(), ref1->row() );
04015     ref1->format()->setGoUpDiagonalPen( ref2->format()->goUpDiagonalPen( ref2->column(), ref2->row() ) );
04016     ref2->format()->setGoUpDiagonalPen(udp);
04017 
04018     QBrush bgBrush = ref1->backGroundBrush( ref1->column(), ref1->row() );
04019     ref1->format()->setBackGroundBrush( ref2->backGroundBrush( ref2->column(), ref2->row() ) );
04020     ref2->format()->setBackGroundBrush(bgBrush);
04021 
04022     int pre = ref1->format()->precision( ref1->column(), ref1->row() );
04023     ref1->format()->setPrecision( ref2->format()->precision( ref2->column(), ref2->row() ) );
04024     ref2->format()->setPrecision(pre);
04025 
04026     QString prefix = ref1->format()->prefix( ref1->column(), ref1->row() );
04027     ref1->format()->setPrefix( ref2->format()->prefix( ref2->column(), ref2->row() ) );
04028     ref2->format()->setPrefix(prefix);
04029 
04030     QString postfix = ref1->format()->postfix( ref1->column(), ref1->row() );
04031     ref1->format()->setPostfix( ref2->format()->postfix( ref2->column(), ref2->row() ) );
04032     ref2->format()->setPostfix(postfix);
04033 
04034     Format::FloatFormat f = ref1->format()->floatFormat( ref1->column(), ref1->row() );
04035     ref1->format()->setFloatFormat( ref2->format()->floatFormat( ref2->column(), ref2->row() ) );
04036     ref2->format()->setFloatFormat(f);
04037 
04038     Format::FloatColor c = ref1->format()->floatColor( ref1->column(), ref1->row() );
04039     ref1->format()->setFloatColor( ref2->format()->floatColor( ref2->column(), ref2->row() ) );
04040     ref2->format()->setFloatColor(c);
04041 
04042     bool multi = ref1->format()->multiRow( ref1->column(), ref1->row() );
04043     ref1->format()->setMultiRow( ref2->format()->multiRow( ref2->column(), ref2->row() ) );
04044     ref2->format()->setMultiRow(multi);
04045 
04046     bool vert = ref1->format()->verticalText( ref1->column(), ref1->row() );
04047     ref1->format()->setVerticalText( ref2->format()->verticalText( ref2->column(), ref2->row() ) );
04048     ref2->format()->setVerticalText(vert);
04049 
04050     bool print = ref1->format()->getDontprintText( ref1->column(), ref1->row() );
04051     ref1->format()->setDontPrintText( ref2->format()->getDontprintText( ref2->column(), ref2->row() ) );
04052     ref2->format()->setDontPrintText(print);
04053 
04054     double ind = ref1->format()->getIndent( ref1->column(), ref1->row() );
04055     ref1->format()->setIndent( ref2->format()->getIndent( ref2->column(), ref2->row() ) );
04056     ref2->format()->setIndent( ind );
04057 
04058     QValueList<Conditional> conditionList = ref1->conditionList();
04059     ref1->setConditionList(ref2->conditionList());
04060     ref2->setConditionList(conditionList);
04061 
04062     QString com = ref1->format()->comment( ref1->column(), ref1->row() );
04063     ref1->format()->setComment( ref2->format()->comment( ref2->column(), ref2->row() ) );
04064     ref2->format()->setComment(com);
04065 
04066     int angle = ref1->format()->getAngle( ref1->column(), ref1->row() );
04067     ref1->format()->setAngle( ref2->format()->getAngle( ref2->column(), ref2->row() ) );
04068     ref2->format()->setAngle(angle);
04069 
04070     FormatType form = ref1->format()->getFormatType( ref1->column(), ref1->row() );
04071     ref1->format()->setFormatType( ref2->format()->getFormatType( ref2->column(), ref2->row() ) );
04072     ref2->format()->setFormatType(form);
04073   }
04074 }
04075 
04076 void Sheet::refreshPreference()
04077 {
04078   if ( getAutoCalc() )
04079     recalc();
04080 
04081   emit sig_updateHBorder( this );
04082   emit sig_updateView( this );
04083 }
04084 
04085 
04086 bool Sheet::areaIsEmpty(const Region& region, TestType _type)
04087 {
04088   Region::ConstIterator endOfList = region.constEnd();
04089   for (Region::ConstIterator it = region.constBegin(); it != endOfList; ++it)
04090   {
04091     QRect range = (*it)->rect().normalize();
04092     // Complete rows selected ?
04093     if ((*it)->isRow())
04094     {
04095         for ( int row = range.top(); row <= range.bottom(); ++row )
04096         {
04097             Cell * c = getFirstCellRow( row );
04098             while ( c )
04099             {
04100                 if ( !c->isPartOfMerged())
04101                 {
04102                     switch( _type )
04103                     {
04104                     case Text :
04105                         if ( !c->text().isEmpty())
04106                             return false;
04107                         break;
04108                     case Validity:
04109                         if ( c->getValidity(0))
04110                             return false;
04111                         break;
04112                     case Comment:
04113                         if ( !c->format()->comment(c->column(), row).isEmpty())
04114                             return false;
04115                         break;
04116                     case ConditionalCellAttribute:
04117                         if ( c->conditionList().count()> 0)
04118                             return false;
04119                         break;
04120                     }
04121                 }
04122 
04123                 c = getNextCellRight( c->column(), row );
04124             }
04125         }
04126     }
04127     // Complete columns selected ?
04128     else if ((*it)->isColumn())
04129     {
04130         for ( int col = range.left(); col <= range.right(); ++col )
04131         {
04132             Cell * c = getFirstCellColumn( col );
04133             while ( c )
04134             {
04135                 if ( !c->isPartOfMerged() )
04136                 {
04137                     switch( _type )
04138                     {
04139                     case Text :
04140                         if ( !c->text().isEmpty())
04141                             return false;
04142                         break;
04143                     case Validity:
04144                         if ( c->getValidity(0))
04145                             return false;
04146                         break;
04147                     case Comment:
04148                         if ( !c->format()->comment(col, c->row()).isEmpty())
04149                             return false;
04150                         break;
04151                     case ConditionalCellAttribute:
04152                         if ( c->conditionList().count()> 0)
04153                             return false;
04154                         break;
04155                     }
04156                 }
04157 
04158                 c = getNextCellDown( col, c->row() );
04159             }
04160         }
04161     }
04162     else
04163     {
04164         Cell * cell;
04165 
04166         int right  = range.right();
04167         int bottom = range.bottom();
04168         for ( int x = range.left(); x <= right; ++x )
04169             for ( int y = range.top(); y <= bottom; ++y )
04170             {
04171                 cell = cellAt( x, y );
04172                 if (!cell->isPartOfMerged() )
04173                 {
04174                     switch( _type )
04175                     {
04176                     case Text :
04177                         if ( !cell->text().isEmpty())
04178                             return false;
04179                         break;
04180                     case Validity:
04181                         if ( cell->getValidity(0))
04182                             return false;
04183                         break;
04184                     case Comment:
04185                         if ( !cell->format()->comment(x, y).isEmpty())
04186                             return false;
04187                         break;
04188                     case ConditionalCellAttribute:
04189                         if ( cell->conditionList().count()> 0)
04190                             return false;
04191                         break;
04192                     }
04193                 }
04194             }
04195     }
04196   }
04197   return true;
04198 }
04199 
04200 struct SetSelectionMultiRowWorker : public Sheet::CellWorker
04201 {
04202   bool enable;
04203   SetSelectionMultiRowWorker( bool _enable )
04204     : Sheet::CellWorker( ), enable( _enable ) { }
04205 
04206   class UndoAction* createUndoAction( Doc * doc, Sheet * sheet, const KSpread::Region& region )
04207   {
04208     QString title = i18n("Multirow");
04209     return new UndoCellFormat( doc, sheet, region, title );
04210   }
04211 
04212   bool testCondition( Cell * cell )
04213   {
04214     return ( !cell->isPartOfMerged() );
04215   }
04216 
04217   void doWork( Cell * cell, bool, int, int )
04218   {
04219     cell->setDisplayDirtyFlag();
04220     cell->format()->setMultiRow( enable );
04221     cell->format()->setVerticalText( false );
04222     cell->format()->setAngle( 0 );
04223     cell->clearDisplayDirtyFlag();
04224   }
04225 };
04226 
04227 void Sheet::setSelectionMultiRow( Selection* selectionInfo,
04228                                          bool enable )
04229 {
04230     SetSelectionMultiRowWorker w( enable );
04231     workOnCells( selectionInfo, w );
04232 }
04233 
04234 QString Sheet::guessColumnTitle(QRect& area, int col)
04235 {
04236   //Verify range
04237   Range rg;
04238   rg.setRange(area);
04239   rg.setSheet(this);
04240 
04241   if ( (!rg.isValid()) || (col < area.left()) || (col > area.right()))
04242     return QString();
04243 
04244   //The current guess logic is fairly simple - if the top row of the given area
04245   //appears to contain headers (ie. there is text in each column) the text in the column at
04246   //the top row of the area is returned.
04247 
04248 /*  for (int i=area.left();i<=area.right();i++)
04249   {
04250     Value cellValue=value(i,area.top());
04251 
04252     if (!cellValue.isString())
04253       return QString();
04254   }*/
04255 
04256   Value cellValue=value(col,area.top());
04257   return cellValue.asString();
04258 }
04259 
04260 QString Sheet::guessRowTitle(QRect& area, int row)
04261 {
04262   //Verify range
04263   Range rg;
04264   rg.setRange(area);
04265   rg.setSheet(this);
04266 
04267   if ( (!rg.isValid()) || (row < area.top()) || (row > area.bottom()) )
04268     return QString();
04269 
04270   //The current guess logic is fairly simple - if the leftmost column of the given area
04271   //appears to contain headers (ie. there is text in each row) the text in the row at
04272   //the leftmost column of the area is returned.
04273   /*for (int i=area.top();i<=area.bottom();i++)
04274   {
04275     Value cellValue=value(area.left(),i);
04276 
04277     if (!cellValue.isString())
04278       return QString();
04279   }*/
04280 
04281   Value cellValue=value(area.left(),row);
04282   return cellValue.asString();
04283 }
04284 
04285 void Sheet::setSelectionAlign( Selection* selectionInfo,
04286                                Format::Align _align )
04287 {
04288   HorAlignManipulator* manipulator = new HorAlignManipulator();
04289   manipulator->setSheet(this);
04290   manipulator->setProperty(Format::PAlign);
04291   manipulator->setHorizontalAlignment(_align);
04292   manipulator->add(*selectionInfo);
04293   manipulator->execute();
04294 }
04295 
04296 void Sheet::setSelectionAlignY( Selection* selectionInfo,
04297                                 Format::AlignY _alignY )
04298 {
04299   VerAlignManipulator* manipulator = new VerAlignManipulator();
04300   manipulator->setSheet(this);
04301   manipulator->setProperty(Format::PAlignY);
04302   manipulator->setVerticalAlignment(_alignY);
04303   manipulator->add(*selectionInfo);
04304   manipulator->execute();
04305 }
04306 
04307 
04308 struct SetSelectionPrecisionWorker : public Sheet::CellWorker {
04309     int _delta;
04310     SetSelectionPrecisionWorker( int delta ) : Sheet::CellWorker( ), _delta( delta ) { }
04311 
04312     class UndoAction* createUndoAction( Doc* doc, Sheet* sheet, const KSpread::Region& region ) {
04313         QString title=i18n("Change Precision");
04314   return new UndoCellFormat( doc, sheet, region, title );
04315     }
04316     bool testCondition( Cell* cell ) {
04317   return ( !cell->isPartOfMerged() );
04318     }
04319     void doWork( Cell* cell, bool, int, int ) {
04320   cell->setDisplayDirtyFlag();
04321   if ( _delta == 1 )
04322       cell->incPrecision();
04323   else
04324       cell->decPrecision();
04325   cell->clearDisplayDirtyFlag();
04326     }
04327 };
04328 
04329 void Sheet::setSelectionPrecision( Selection* selectionInfo,
04330                                           int _delta )
04331 {
04332     SetSelectionPrecisionWorker w( _delta );
04333     workOnCells( selectionInfo, w );
04334 }
04335 
04336 struct SetSelectionStyleWorker : public Sheet::CellWorkerTypeA
04337 {
04338   Style * m_style;
04339   SetSelectionStyleWorker( Style * style )
04340     : m_style( style )
04341   {
04342   }
04343 
04344   QString getUndoTitle()
04345   {
04346     return i18n("Apply Style");
04347   }
04348 
04349   void doWork( RowFormat* rw )
04350   {
04351     rw->setStyle( m_style );
04352   }
04353 
04354   void doWork( ColumnFormat* cl )
04355   {
04356     cl->setStyle( m_style );
04357   }
04358 
04359   bool testCondition( Cell* cell )
04360   {
04361     return ( !cell->isPartOfMerged() && cell->format()->style() != m_style );
04362   }
04363 
04364   void doWork( Cell* cell, bool cellRegion, int, int )
04365   {
04366     if ( cellRegion )
04367       cell->setDisplayDirtyFlag();
04368 
04369     cell->format()->setStyle( m_style );
04370 
04371     if ( cellRegion )
04372       cell->clearDisplayDirtyFlag();
04373   }
04374 };
04375 
04376 
04377 void Sheet::setSelectionStyle( Selection * selectionInfo, Style * style )
04378 {
04379     SetSelectionStyleWorker w( style );
04380     workOnCells( selectionInfo, w );
04381 }
04382 
04383 struct SetSelectionMoneyFormatWorker : public Sheet::CellWorkerTypeA
04384 {
04385     bool b;
04386     Doc *m_pDoc;
04387     SetSelectionMoneyFormatWorker( bool _b,Doc* _doc ) : b( _b ), m_pDoc(_doc) { }
04388     QString getUndoTitle() { return i18n("Format Money"); }
04389     bool testCondition( RowFormat* rw ) {
04390   return ( rw->hasProperty( Format::PFormatType )
04391      || rw->hasProperty( Format::PPrecision ) );
04392     }
04393     void doWork( RowFormat* rw ) {
04394   rw->setFormatType( b ? Money_format : Generic_format );
04395   rw->setPrecision( b ? m_pDoc->locale()->fracDigits() : 0 );
04396     }
04397     void doWork( ColumnFormat* cl ) {
04398   cl->setFormatType( b ? Money_format : Generic_format );
04399   cl->setPrecision( b ? m_pDoc->locale()->fracDigits() : 0 );
04400     }
04401     void prepareCell( Cell* c ) {
04402   c->format()->clearProperty( Format::PPrecision );
04403   c->format()->clearNoFallBackProperties( Format::PPrecision );
04404   c->format()->clearProperty( Format::PFormatType );
04405   c->format()->clearNoFallBackProperties( Format::PFormatType );
04406     }
04407     bool testCondition( Cell* cell ) {
04408   return ( !cell->isPartOfMerged() );
04409     }
04410     void doWork( Cell* cell, bool cellRegion, int, int ) {
04411   if ( cellRegion )
04412       cell->setDisplayDirtyFlag();
04413   cell->format()->setFormatType( b ? Money_format : Generic_format );
04414   cell->format()->setPrecision( b ?  m_pDoc->locale()->fracDigits() : 0 );
04415   if ( cellRegion )
04416       cell->clearDisplayDirtyFlag();
04417     }
04418 };
04419 
04420 
04421 void Sheet::setSelectionMoneyFormat( Selection* selectionInfo,
04422                                             bool b )
04423 {
04424     SetSelectionMoneyFormatWorker w( b,doc() );
04425     workOnCells( selectionInfo, w );
04426 }
04427 
04428 
04429 struct IncreaseIndentWorker : public Sheet::CellWorkerTypeA {
04430     double   tmpIndent;
04431     double   valIndent;
04432 
04433     IncreaseIndentWorker( double _tmpIndent, double _valIndent )
04434   : tmpIndent( _tmpIndent ), valIndent( _valIndent ) { }
04435 
04436     QString  getUndoTitle() { return i18n("Increase Indent"); }
04437     bool     testCondition( RowFormat* rw ) {
04438   return ( rw->hasProperty( Format::PIndent ) );
04439     }
04440 
04441     void doWork( RowFormat* rw ) {
04442   rw->setIndent( tmpIndent+valIndent );
04443   //rw->setAlign( Format::Left );
04444     }
04445     void doWork( ColumnFormat* cl ) {
04446   cl->setIndent( tmpIndent+valIndent );
04447   //cl->setAlign( Format::Left );
04448     }
04449     void prepareCell( Cell* c ) {
04450   c->format()->clearProperty( Format::PIndent );
04451   c->format()->clearNoFallBackProperties( Format::PIndent );
04452   //c->format()->clearProperty( Format::PAlign );
04453   //c->format()->clearNoFallBackProperties( Format::PAlign );
04454     }
04455     bool testCondition( Cell* cell ) {
04456   return ( !cell->isPartOfMerged() );
04457     }
04458     void doWork( Cell* cell, bool cellRegion, int x, int y ) {
04459   if ( cellRegion ) {
04460       if(cell->format()->align(x,y)!=Format::Left)
04461       {
04462     //cell->setAlign(Format::Left);
04463     //cell->format()->setIndent( 0.0 );
04464       }
04465       cell->setDisplayDirtyFlag();
04466       cell->format()->setIndent( /* ### ??? --> */ cell->format()->getIndent(x,y) /* <-- */ +valIndent );
04467       cell->clearDisplayDirtyFlag();
04468   } else {
04469       cell->format()->setIndent( tmpIndent+valIndent);
04470       //cell->setAlign( Format::Left);
04471   }
04472     }
04473 };
04474 
04475 
04476 void Sheet::increaseIndent(Selection* selectionInfo)
04477 {
04478     QPoint       marker(selectionInfo->marker());
04479     double       valIndent = doc()->getIndentValue();
04480     Cell *c         = cellAt( marker );
04481     double       tmpIndent = c->format()->getIndent( marker.x(), marker.y() );
04482 
04483     IncreaseIndentWorker  w( tmpIndent, valIndent );
04484     workOnCells( selectionInfo, w );
04485 }
04486 
04487 
04488 struct DecreaseIndentWorker : public Sheet::CellWorkerTypeA {
04489     double tmpIndent, valIndent;
04490     DecreaseIndentWorker( double _tmpIndent, double _valIndent ) : tmpIndent( _tmpIndent ), valIndent( _valIndent ) { }
04491     QString getUndoTitle() { return i18n("Decrease Indent"); }
04492     bool testCondition( RowFormat* rw ) {
04493   return ( rw->hasProperty( Format::PIndent ) );
04494     }
04495     void doWork( RowFormat* rw ) {
04496         rw->setIndent( QMAX( 0.0, tmpIndent - valIndent ) );
04497     }
04498     void doWork( ColumnFormat* cl ) {
04499         cl->setIndent( QMAX( 0.0, tmpIndent - valIndent ) );
04500     }
04501     void prepareCell( Cell* c ) {
04502   c->format()->clearProperty( Format::PIndent );
04503   c->format()->clearNoFallBackProperties( Format::PIndent );
04504     }
04505     bool testCondition( Cell* cell ) {
04506   return ( !cell->isPartOfMerged() );
04507     }
04508     void doWork( Cell* cell, bool cellRegion, int x, int y ) {
04509   if ( cellRegion ) {
04510       cell->setDisplayDirtyFlag();
04511       cell->format()->setIndent( QMAX( 0.0, cell->format()->getIndent( x, y ) - valIndent ) );
04512       cell->clearDisplayDirtyFlag();
04513   } else {
04514       cell->format()->setIndent( QMAX( 0.0, tmpIndent - valIndent ) );
04515   }
04516     }
04517 };
04518 
04519 
04520 void Sheet::decreaseIndent( Selection* selectionInfo )
04521 {
04522     double valIndent = doc()->getIndentValue();
04523     QPoint marker(selectionInfo->marker());
04524     Cell* c = cellAt( marker );
04525     double tmpIndent = c->format()->getIndent( marker.x(), marker.y() );
04526 
04527     DecreaseIndentWorker w( tmpIndent, valIndent );
04528     workOnCells( selectionInfo, w );
04529 }
04530 
04531 
04532 int Sheet::adjustColumnHelper( Cell * c, int _col, int _row )
04533 {
04534     double long_max = 0.0;
04535     c->calculateTextParameters( painter(), _col, _row );
04536     if ( c->textWidth() > long_max )
04537     {
04538         double indent = 0.0;
04539         int a = c->format()->align( c->column(), c->row() );
04540         if ( a == Format::Undefined )
04541         {
04542             if ( c->value().isNumber() || c->isDate() || c->isTime())
04543                 a = Format::Right;
04544             else
04545                 a = Format::Left;
04546         }
04547 
04548         if ( a == Format::Left )
04549             indent = c->format()->getIndent( c->column(), c->row() );
04550         long_max = indent + c->textWidth()
04551             + c->format()->leftBorderWidth( c->column(), c->row() )
04552             + c->format()->rightBorderWidth( c->column(), c->row() );
04553     }
04554     return (int)long_max;
04555 }
04556 
04557 void Sheet::adjustArea(const Region& region)
04558 {
04559   AdjustColumnRowManipulator* manipulator = new AdjustColumnRowManipulator();
04560   manipulator->setSheet(this);
04561   manipulator->setAdjustColumn(true);
04562   manipulator->setAdjustRow(true);
04563   manipulator->add(region);
04564   manipulator->execute();
04565 }
04566 
04567 void Sheet::adjustColumn(const Region& region)
04568 {
04569   AdjustColumnRowManipulator* manipulator = new AdjustColumnRowManipulator();
04570   manipulator->setSheet(this);
04571   manipulator->setAdjustColumn(true);
04572   manipulator->add(region);
04573   manipulator->execute();
04574 }
04575 
04576 void Sheet::adjustRow(const Region& region)
04577 {
04578   AdjustColumnRowManipulator* manipulator = new AdjustColumnRowManipulator();
04579   manipulator->setSheet(this);
04580   manipulator->setAdjustRow(true);
04581   manipulator->add(region);
04582   manipulator->execute();
04583 }
04584 
04585 struct ClearTextSelectionWorker : public Sheet::CellWorker {
04586     Sheet   * _s;
04587 
04588     ClearTextSelectionWorker(  Sheet * s )
04589       : Sheet::CellWorker( ),  _s( s ) { }
04590 
04591     class UndoAction* createUndoAction( Doc* doc, Sheet* sheet, const KSpread::Region& region ) {
04592   return new UndoChangeAreaTextCell( doc, sheet, region );
04593     }
04594     bool testCondition( Cell* cell ) {
04595   return ( !cell->isObscured() );
04596     }
04597     void doWork( Cell* cell, bool, int, int )
04598     {
04599       cell->setCellText( "" );
04600     }
04601 };
04602 
04603 void Sheet::clearTextSelection( Selection* selectionInfo )
04604 {
04605   if (areaIsEmpty(*selectionInfo))
04606     return;
04607 
04608   ClearTextSelectionWorker w( this );
04609   workOnCells( selectionInfo, w );
04610 }
04611 
04612 
04613 struct ClearValiditySelectionWorker : public Sheet::CellWorker {
04614     ClearValiditySelectionWorker( ) : Sheet::CellWorker( ) { }
04615 
04616     class UndoAction* createUndoAction( Doc* doc, Sheet* sheet, const KSpread::Region& region ) {
04617   return new UndoConditional( doc, sheet, region );
04618     }
04619     bool testCondition( Cell* cell ) {
04620   return ( !cell->isObscured() );
04621     }
04622     void doWork( Cell* cell, bool, int, int ) {
04623   cell->removeValidity();
04624     }
04625 };
04626 
04627 void Sheet::clearValiditySelection( Selection* selectionInfo )
04628 {
04629   if (areaIsEmpty(*selectionInfo, Validity))
04630     return;
04631 
04632   ClearValiditySelectionWorker w;
04633   workOnCells( selectionInfo, w );
04634 }
04635 
04636 
04637 struct ClearConditionalSelectionWorker : public Sheet::CellWorker
04638 {
04639   ClearConditionalSelectionWorker( ) : Sheet::CellWorker( ) { }
04640 
04641   class UndoAction* createUndoAction( Doc* doc,
04642                Sheet* sheet,
04643                const KSpread::Region& region )
04644   {
04645     return new UndoConditional( doc, sheet, region );
04646   }
04647   bool testCondition( Cell* cell )
04648   {
04649     return ( !cell->isObscured() );
04650   }
04651   void doWork( Cell* cell, bool, int, int )
04652   {
04653     QValueList<Conditional> emptyList;
04654     cell->setConditionList(emptyList);
04655   }
04656 };
04657 
04658 void Sheet::clearConditionalSelection( Selection* selectionInfo )
04659 {
04660   ClearConditionalSelectionWorker w;
04661   workOnCells( selectionInfo, w );
04662 }
04663 
04664 void Sheet::fillSelection( Selection * selectionInfo, int direction )
04665 {
04666   QRect rct( selectionInfo->selection() );
04667   int right  = rct.right();
04668   int bottom = rct.bottom();
04669   int left   = rct.left();
04670   int top    = rct.top();
04671   int width  = rct.width();
04672   int height = rct.height();
04673 
04674   QDomDocument undoDoc = saveCellRegion( rct );
04675   loadSelectionUndo( undoDoc, rct, left - 1, top - 1, false, 0 );
04676 
04677   QDomDocument doc;
04678 
04679   switch( direction )
04680   {
04681    case Right:
04682     doc = saveCellRegion( QRect( left, top, 1, height ) );
04683     break;
04684 
04685    case Up:
04686     doc = saveCellRegion( QRect( left, bottom, width, 1 ) );
04687     break;
04688 
04689    case Left:
04690     doc = saveCellRegion( QRect( right, top, 1, height ) );
04691     break;
04692 
04693    case Down:
04694     doc = saveCellRegion( QRect( left, top, width, 1 ) );
04695     break;
04696   };
04697 
04698   // Save to buffer
04699   QBuffer buffer;
04700   buffer.open( IO_WriteOnly );
04701   QTextStream str( &buffer );
04702   str.setEncoding( QTextStream::UnicodeUTF8 );
04703   str << doc;
04704   buffer.close();
04705 
04706   int i;
04707   switch( direction )
04708   {
04709    case Right:
04710     for ( i = left + 1; i <= right; ++i )
04711     {
04712       paste( buffer.buffer(), QRect( i, top, 1, 1 ), false );
04713     }
04714     break;
04715 
04716    case Up:
04717     for ( i = bottom + 1; i >= top; --i )
04718     {
04719       paste( buffer.buffer(), QRect( left, i, 1, 1 ), false );
04720     }
04721     break;
04722 
04723    case Left:
04724     for ( i = right - 1; i >= left; --i )
04725     {
04726       paste( buffer.buffer(), QRect( i, top, 1, 1 ), false );
04727     }
04728     break;
04729 
04730    case Down:
04731     for ( i = top + 1; i <= bottom; ++i )
04732     {
04733       paste( buffer.buffer(), QRect( left, i, 1, 1 ), false );
04734     }
04735     break;
04736   }
04737 
04738   this->doc()->setModified( true );
04739 }
04740 
04741 
04742 struct DefaultSelectionWorker : public Sheet::CellWorker {
04743     DefaultSelectionWorker( ) : Sheet::CellWorker( true, false, true ) { }
04744 
04745     class UndoAction* createUndoAction( Doc* doc, Sheet* sheet, const KSpread::Region& region ) {
04746         QString title=i18n("Default Parameters");
04747   return new UndoCellFormat( doc, sheet, region, title );
04748     }
04749     bool testCondition( Cell* ) {
04750   return true;
04751     }
04752     void doWork( Cell* cell, bool, int, int ) {
04753   cell->defaultStyle();
04754     }
04755 };
04756 
04757 void Sheet::defaultSelection( Selection* selectionInfo )
04758 {
04759   QRect selection(selectionInfo->selection());
04760   DefaultSelectionWorker w;
04761   SelectionType st = workOnCells( selectionInfo, w );
04762   switch ( st ) {
04763   case CompleteRows:
04764     RowFormat *rw;
04765     for ( int i = selection.top(); i <= selection.bottom(); i++ ) {
04766       rw = nonDefaultRowFormat( i );
04767       rw->defaultStyleFormat();
04768     }
04769     emit sig_updateView( this, *selectionInfo );
04770     return;
04771   case CompleteColumns:
04772     ColumnFormat *cl;
04773     for ( int i = selection.left(); i <= selection.right(); i++ ) {
04774       cl=nonDefaultColumnFormat( i );
04775       cl->defaultStyleFormat();
04776     }
04777     emit sig_updateView( this, *selectionInfo );
04778     return;
04779   case CellRegion:
04780     emit sig_updateView( this, *selectionInfo );
04781       return;
04782   }
04783 }
04784 
04785 
04786 struct SetConditionalWorker : public Sheet::CellWorker
04787 {
04788   QValueList<Conditional> conditionList;
04789   SetConditionalWorker( QValueList<Conditional> _tmp ) :
04790     Sheet::CellWorker( ), conditionList( _tmp ) { }
04791 
04792   class UndoAction* createUndoAction( Doc* doc,
04793                                       Sheet* sheet, const KSpread::Region& region )
04794   {
04795     return new UndoConditional( doc, sheet, region );
04796   }
04797 
04798   bool testCondition( Cell* )
04799   {
04800     return true;
04801   }
04802 
04803   void doWork( Cell* cell, bool, int, int )
04804   {
04805     if ( !cell->isObscured() ) // TODO: isPartOfMerged()???
04806     {
04807       cell->setConditionList(conditionList);
04808       cell->setDisplayDirtyFlag();
04809     }
04810   }
04811 };
04812 
04813 void Sheet::setConditional( Selection* selectionInfo,
04814                                    QValueList<Conditional> const & newConditions)
04815 {
04816   if ( !doc()->undoLocked() )
04817   {
04818     UndoConditional * undo = new UndoConditional(doc(), this, *selectionInfo);
04819     doc()->addCommand( undo );
04820   }
04821 
04822   Region::ConstIterator endOfList = selectionInfo->constEnd();
04823   for (Region::ConstIterator it = selectionInfo->constBegin(); it != endOfList; ++it)
04824   {
04825     QRect range = (*it)->rect().normalize();
04826 
04827     int l = range.left();
04828     int r = range.right();
04829     int t = range.top();
04830     int b = range.bottom();
04831 
04832     Cell * cell;
04833     Style * s = doc()->styleManager()->defaultStyle();
04834     for (int x = l; x <= r; ++x)
04835     {
04836       for (int y = t; y <= b; ++y)
04837       {
04838         cell = nonDefaultCell( x, y, false, s );
04839         cell->setConditionList( newConditions );
04840         cell->setDisplayDirtyFlag();
04841       }
04842     }
04843   }
04844 
04845   emit sig_updateView( this, *selectionInfo );
04846 }
04847 
04848 
04849 struct SetValidityWorker : public Sheet::CellWorker {
04850     Validity tmp;
04851     SetValidityWorker( Validity _tmp ) : Sheet::CellWorker( ), tmp( _tmp ) { }
04852 
04853     class UndoAction* createUndoAction( Doc* doc, Sheet* sheet, const KSpread::Region& region ) {
04854   return new UndoConditional( doc, sheet, region );
04855     }
04856     bool testCondition( Cell* ) {
04857         return true;
04858     }
04859     void doWork( Cell* cell, bool, int, int ) {
04860   if ( !cell->isObscured() ) {
04861       cell->setDisplayDirtyFlag();
04862       if ( tmp.m_restriction==Restriction::None )
04863     cell->removeValidity();
04864       else
04865       {
04866     Validity *tmpValidity = cell->getValidity();
04867     tmpValidity->message=tmp.message;
04868     tmpValidity->title=tmp.title;
04869     tmpValidity->valMin=tmp.valMin;
04870     tmpValidity->valMax=tmp.valMax;
04871     tmpValidity->m_cond=tmp.m_cond;
04872     tmpValidity->m_action=tmp.m_action;
04873     tmpValidity->m_restriction=tmp.m_restriction;
04874     tmpValidity->timeMin=tmp.timeMin;
04875     tmpValidity->timeMax=tmp.timeMax;
04876     tmpValidity->dateMin=tmp.dateMin;
04877     tmpValidity->dateMax=tmp.dateMax;
04878                 tmpValidity->displayMessage=tmp.displayMessage;
04879                 tmpValidity->allowEmptyCell=tmp.allowEmptyCell;
04880                 tmpValidity->displayValidationInformation=tmp.displayValidationInformation;
04881                 tmpValidity->titleInfo=tmp.titleInfo;
04882                 tmpValidity->messageInfo=tmp.messageInfo;
04883                 tmpValidity->listValidity=tmp.listValidity;
04884       }
04885       cell->clearDisplayDirtyFlag();
04886   }
04887     }
04888 };
04889 
04890 void Sheet::setValidity(Selection* selectionInfo,
04891                         KSpread::Validity tmp )
04892 {
04893     SetValidityWorker w( tmp );
04894     workOnCells( selectionInfo, w );
04895 }
04896 
04897 
04898 struct GetWordSpellingWorker : public Sheet::CellWorker {
04899     QString& listWord;
04900     GetWordSpellingWorker( QString& _listWord ) : Sheet::CellWorker( false, false, true ), listWord( _listWord ) { }
04901 
04902     class UndoAction* createUndoAction( Doc*, Sheet*, const KSpread::Region& ) {
04903   return 0;
04904     }
04905     bool testCondition( Cell* ) {
04906         return true;
04907     }
04908     void doWork( Cell* c, bool cellRegion, int, int ) {
04909   if ( !c->isObscured() || cellRegion /* ### ??? */ ) {
04910       if ( !c->isFormula() && !c->value().isNumber() && !c->value().asString().isEmpty() && !c->isTime()
04911      && !c->isDate()
04912      && !c->text().isEmpty())
04913       {
04914     listWord+=c->text()+'\n';
04915       }
04916   }
04917     }
04918 };
04919 
04920 QString Sheet::getWordSpelling(Selection* selectionInfo )
04921 {
04922     QString listWord;
04923     GetWordSpellingWorker w( listWord );
04924     workOnCells( selectionInfo, w );
04925     return listWord;
04926 }
04927 
04928 
04929 struct SetWordSpellingWorker : public Sheet::CellWorker {
04930     QStringList& list;
04931     int pos;
04932     Sheet   * sheet;
04933     SetWordSpellingWorker( QStringList & _list,Sheet * s )
04934       : Sheet::CellWorker( false, false, true ), list( _list ), pos( 0 ),  sheet( s ) { }
04935 
04936     class UndoAction* createUndoAction( Doc* doc, Sheet* sheet, const KSpread::Region& region ) {
04937   return new UndoChangeAreaTextCell( doc, sheet, region );
04938     }
04939     bool testCondition( Cell* ) {
04940         return true;
04941     }
04942     void doWork( Cell* c, bool cellRegion, int, int )
04943     {
04944   if ( !c->isObscured() || cellRegion /* ### ??? */ ) {
04945       if ( !c->isFormula() && !c->value().isNumber() && !c->value().asString().isEmpty() && !c->isTime()
04946      && !c->isDate()
04947      && !c->text().isEmpty())
04948       {
04949 
04950 
04951     c->setCellText( list[pos] );
04952     pos++;
04953       }
04954   }
04955     }
04956 };
04957 
04958 void Sheet::setWordSpelling(Selection* selectionInfo,
04959                                    const QString _listWord )
04960 {
04961     QStringList list = QStringList::split ( '\n', _listWord );
04962     SetWordSpellingWorker w( list,  this );
04963     workOnCells( selectionInfo, w );
04964 }
04965 
04966 static QString cellAsText( Cell* cell, unsigned int max )
04967 {
04968   QString result;
04969   if( !cell->isDefault() )
04970   {
04971     int l = max - cell->strOutText().length();
04972     if (cell->defineAlignX() == Format::Right )
04973     {
04974         for ( int i = 0; i < l; ++i )
04975           result += " ";
04976         result += cell->strOutText();
04977     }
04978     else if (cell->defineAlignX() == Format::Left )
04979       {
04980           result += " ";
04981           result += cell->strOutText();
04982           // start with "1" because we already set one space
04983           for ( int i = 1; i < l; ++i )
04984             result += " ";
04985        }
04986          else // centered
04987          {
04988            int i;
04989            int s = (int) l / 2;
04990            for ( i = 0; i < s; ++i )
04991              result += " ";
04992            result += cell->strOutText();
04993            for ( i = s; i < l; ++i )
04994              result += " ";
04995           }
04996   }
04997   else
04998   {
04999     for ( unsigned int i = 0; i < max; ++i )
05000       result += " ";
05001   }
05002 
05003   return result;
05004 }
05005 
05006 QString Sheet::copyAsText( Selection* selectionInfo )
05007 {
05008     // Only one cell selected? => copy active cell
05009     if ( selectionInfo->isSingular() )
05010     {
05011         Cell * cell = cellAt( selectionInfo->marker() );
05012         if( !cell->isDefault() )
05013           return cell->strOutText();
05014         return "";
05015     }
05016 
05017     QRect selection(selectionInfo->selection());
05018 
05019     // Find area
05020     unsigned top = selection.bottom();
05021     unsigned bottom = selection.top();
05022     unsigned left = selection.right();
05023     unsigned right = selection.left();
05024 
05025     unsigned max = 1;
05026     for( Cell *c = d->cells.firstCell();c; c = c->nextCell() )
05027     {
05028       if ( !c->isDefault() )
05029       {
05030         QPoint p( c->column(), c->row() );
05031         if ( selection.contains( p ) )
05032         {
05033           top = QMIN( top, (unsigned) c->row() );
05034           left = QMIN( left, (unsigned) c->column() );
05035           bottom = QMAX( bottom, (unsigned) c->row() );
05036           right = QMAX( right, (unsigned) c->column() );
05037 
05038           if ( c->strOutText().length() > max )
05039                  max = c->strOutText().length();
05040         }
05041       }
05042     }
05043 
05044     ++max;
05045 
05046     QString result;
05047     for ( unsigned y = top; y <= bottom; ++y)
05048     {
05049       for ( unsigned x = left; x <= right; ++x)
05050       {
05051         Cell *cell = cellAt( x, y );
05052         result += cellAsText( cell, max );
05053       }
05054       result += "\n";
05055     }
05056 
05057     return result;
05058 }
05059 
05060 void Sheet::copySelection( Selection* selectionInfo )
05061 {
05062     QDomDocument doc = saveCellRegion( *selectionInfo, true );
05063 
05064     // Save to buffer
05065     QBuffer buffer;
05066     buffer.open( IO_WriteOnly );
05067     QTextStream str( &buffer );
05068     str.setEncoding( QTextStream::UnicodeUTF8 );
05069     str << doc;
05070     buffer.close();
05071 
05072     TextDrag * kd = new TextDrag( 0L );
05073     kd->setPlain( copyAsText(selectionInfo) );
05074     kd->setKSpread( buffer.buffer() );
05075 
05076     QApplication::clipboard()->setData( kd );
05077 }
05078 
05079 void Sheet::cutSelection( Selection* selectionInfo )
05080 {
05081     QDomDocument doc = saveCellRegion(*selectionInfo, true, true);
05082 
05083     // Save to buffer
05084     QBuffer buffer;
05085     buffer.open( IO_WriteOnly );
05086     QTextStream str( &buffer );
05087     str.setEncoding( QTextStream::UnicodeUTF8 );
05088     str << doc;
05089     buffer.close();
05090 
05091     TextDrag * kd = new TextDrag( 0L );
05092     kd->setPlain( copyAsText(selectionInfo) );
05093     kd->setKSpread( buffer.buffer() );
05094 
05095     QApplication::clipboard()->setData( kd );
05096 
05097     deleteSelection( selectionInfo, true );
05098 }
05099 
05100 void Sheet::paste( const QRect& pasteArea, bool makeUndo,
05101                    Paste::Mode mode, Paste::Operation operation,
05102                    bool insert, int insertTo, bool pasteFC,
05103                    QClipboard::Mode clipboardMode )
05104 {
05105     QMimeSource * mime = QApplication::clipboard()->data( clipboardMode );
05106     if ( !mime )
05107         return;
05108 
05109     QByteArray b;
05110 
05111     if ( mime->provides( TextDrag::selectionMimeType() ) )
05112     {
05113         b = mime->encodedData( TextDrag::selectionMimeType() );
05114     }
05115     else if( mime->provides( "text/plain" ) )
05116     {
05117         // Note: QClipboard::text() seems to do a better job than encodedData( "text/plain" )
05118         // In particular it handles charsets (in the mimetype). Copied from KPresenter ;-)
05119         QString _text = QApplication::clipboard()->text( clipboardMode );
05120         doc()->emitBeginOperation();
05121         pasteTextPlain( _text, pasteArea );
05122         emit sig_updateView( this );
05123         // doc()->emitEndOperation();
05124         return;
05125     }
05126     else
05127         return;
05128 
05129     // Do the actual pasting.
05130     doc()->emitBeginOperation();
05131     paste( b, pasteArea, makeUndo, mode, operation, insert, insertTo, pasteFC );
05132     emit sig_updateView( this );
05133     // doc()->emitEndOperation();
05134 }
05135 
05136 
05137 void Sheet::pasteTextPlain( QString &_text, QRect pasteArea)
05138 {
05139 //  QString tmp;
05140 //  tmp= QString::fromLocal8Bit(_mime->encodedData( "text/plain" ));
05141   if( _text.isEmpty() )
05142     return;
05143 
05144   QString tmp = _text;
05145   int i;
05146   int mx   = pasteArea.left();
05147   int my   = pasteArea.top();
05148   int rows = 1;
05149   int len  = tmp.length();
05150 
05151   //count the numbers of lines in text
05152   for ( i = 0; i < len; ++i )
05153   {
05154     if ( tmp[i] == '\n' )
05155       ++rows;
05156   }
05157 
05158   Cell * cell = nonDefaultCell( mx, my );
05159   if ( rows == 1 )
05160   {
05161     if ( !doc()->undoLocked() )
05162     {
05163       UndoSetText * undo = new UndoSetText( doc(), this , cell->text(), mx, my, cell->formatType() );
05164       doc()->addCommand( undo );
05165     }
05166   }
05167   else
05168   {
05169       QRect rect(mx, my, mx, my + rows - 1);
05170       UndoChangeAreaTextCell * undo = new UndoChangeAreaTextCell( doc(), this , rect );
05171       doc()->addCommand( undo );
05172   }
05173 
05174   i = 0;
05175   QString rowtext;
05176 
05177   while ( i < rows )
05178   {
05179     int p = 0;
05180 
05181     p = tmp.find('\n');
05182 
05183     if (p < 0)
05184       p = tmp.length();
05185 
05186     rowtext = tmp.left(p);
05187 
05188     if ( !isProtected() || cell->format()->notProtected( mx, my + i ) )
05189     {
05190       cell->setCellText( rowtext );
05191       cell->updateChart();
05192     }
05193 
05194     // next cell
05195     ++i;
05196     cell = nonDefaultCell( mx, my + i );
05197 
05198     if (!cell || p == (int) tmp.length())
05199       break;
05200 
05201     // exclude the left part and '\n'
05202     tmp = tmp.right(tmp.length() - p - 1);
05203   }
05204 
05205   if (!isLoading())
05206     refreshMergedCell();
05207 
05208   emit sig_updateView( this );
05209   emit sig_updateHBorder( this );
05210   emit sig_updateVBorder( this );
05211 }
05212 
05213 void Sheet::paste( const QByteArray& b, const QRect& pasteArea, bool makeUndo,
05214                    Paste::Mode mode, Paste::Operation operation,
05215                    bool insert, int insertTo, bool pasteFC )
05216 {
05217     kdDebug(36001) << "Parsing " << b.size() << " bytes" << endl;
05218 
05219     QBuffer buffer( b );
05220     buffer.open( IO_ReadOnly );
05221     QDomDocument doc;
05222     doc.setContent( &buffer );
05223     buffer.close();
05224 
05225     // ##### TODO: Test for parsing errors
05226 
05227     int mx = pasteArea.left();
05228     int my = pasteArea.top();
05229 
05230     loadSelection( doc, pasteArea, mx - 1, my - 1, makeUndo,
05231                    mode, operation, insert, insertTo, pasteFC );
05232 }
05233 
05234 bool Sheet::loadSelection(const QDomDocument& doc, const QRect& pasteArea,
05235                           int _xshift, int _yshift, bool makeUndo,
05236                           Paste::Mode mode, Paste::Operation operation, bool insert,
05237                           int insertTo, bool pasteFC)
05238 {
05239   //kdDebug(36001) << "loadSelection called. pasteArea=" << pasteArea << endl;
05240 
05241   if (!isLoading() && makeUndo)
05242   {
05243     loadSelectionUndo( doc, pasteArea, _xshift, _yshift, insert, insertTo );
05244   }
05245 
05246   QDomElement root = doc.documentElement(); // "spreadsheet-snippet"
05247 
05248   int rowsInClpbrd    =  root.attribute( "rows" ).toInt();
05249   int columnsInClpbrd =  root.attribute( "columns" ).toInt();
05250 
05251   // find size of rectangle that we want to paste to (either clipboard size or current selection)
05252   const int pasteWidth = ( pasteArea.width() >= columnsInClpbrd
05253                             && util_isRowSelected(pasteArea) == false
05254                             && root.namedItem( "rows" ).toElement().isNull() )
05255     ? pasteArea.width() : columnsInClpbrd;
05256   const int pasteHeight = ( pasteArea.height() >= rowsInClpbrd
05257                             && util_isColumnSelected(pasteArea) == false
05258                             && root.namedItem( "columns" ).toElement().isNull())
05259     ? pasteArea.height() : rowsInClpbrd;
05260 
05261 //   kdDebug() << "loadSelection: paste area has size "
05262 //             << pasteHeight << " rows * "
05263 //             << pasteWidth << " columns " << endl;
05264 //   kdDebug() << "loadSelection: " << rowsInClpbrd << " rows and "
05265 //             << columnsInClpbrd << " columns in clipboard." << endl;
05266 //   kdDebug() << "xshift: " << _xshift << " _yshift: " << _yshift << endl;
05267 
05268   QDomElement e = root.firstChild().toElement(); // "columns", "rows" or "cell"
05269   for (; !e.isNull(); e = e.nextSibling().toElement())
05270   {
05271     // entire columns given
05272     if (e.tagName() == "columns" && !isProtected())
05273     {
05274         _yshift = 0;
05275 
05276         // Clear the existing columns
05277         int col = e.attribute("column").toInt();
05278         int width = e.attribute("count").toInt();
05279         if (!insert)
05280         {
05281             for ( int i = col; i < col + width; ++i )
05282             {
05283                 d->cells.clearColumn( _xshift + i );
05284                 d->columns.removeElement( _xshift + i );
05285             }
05286         }
05287 
05288         // Insert column formats
05289         QDomElement c = e.firstChild().toElement();
05290         for ( ; !c.isNull(); c = c.nextSibling().toElement() )
05291         {
05292             if ( c.tagName() == "column" )
05293             {
05294                 ColumnFormat *cl = new ColumnFormat( this, 0 );
05295                 if ( cl->load( c, _xshift, mode, pasteFC ) )
05296                     insertColumnFormat( cl );
05297                 else
05298                     delete cl;
05299             }
05300         }
05301     }
05302 
05303     // entire rows given
05304     if (e.tagName() == "rows" && !isProtected())
05305     {
05306         _xshift = 0;
05307 
05308         // Clear the existing rows
05309         int row = e.attribute("row").toInt();
05310         int height = e.attribute("count").toInt();
05311         if ( !insert )
05312         {
05313           for( int i = row; i < row + height; ++i )
05314           {
05315             d->cells.clearRow( _yshift + i );
05316             d->rows.removeElement( _yshift + i );
05317           }
05318         }
05319 
05320         // Insert row formats
05321         QDomElement c = e.firstChild().toElement();
05322         for( ; !c.isNull(); c = c.nextSibling().toElement() )
05323         {
05324             if ( c.tagName() == "row" )
05325             {
05326                 RowFormat *cl = new RowFormat( this, 0 );
05327                 if ( cl->load( c, _yshift, mode, pasteFC ) )
05328                     insertRowFormat( cl );
05329                 else
05330                     delete cl;
05331             }
05332         }
05333     }
05334 
05335     Cell* refreshCell = 0;
05336     Cell *cell;
05337     Cell *cellBackup = NULL;
05338     if (e.tagName() == "cell")
05339     {
05340       int row = e.attribute( "row" ).toInt() + _yshift;
05341       int col = e.attribute( "column" ).toInt() + _xshift;
05342 
05343       // tile the selection with the clipboard contents
05344       for (int roff = 0; row + roff - _yshift <= pasteHeight; roff += rowsInClpbrd)
05345       {
05346         for (int coff = 0; col + coff - _xshift <= pasteWidth; coff += columnsInClpbrd)
05347         {
05348 //           kdDebug() << "loadSelection: cell at " << (col+coff) << "," << (row+roff)
05349 //                     << " with roff,coff= " << roff << "," << coff
05350 //                     << ", _xshift: " << _xshift << ", _yshift: " << _yshift << endl;
05351 
05352           cell = nonDefaultCell( col + coff, row + roff );
05353           if (isProtected() && !cell->format()->notProtected(col + coff, row + roff))
05354           {
05355             continue;
05356           }
05357 
05358           cellBackup = new Cell(this, cell->column(), cell->row());
05359           cellBackup->copyAll(cell);
05360 
05361           if (!cell->load(e, _xshift + coff, _yshift + roff, mode, operation, pasteFC))
05362           {
05363             cell->copyAll(cellBackup);
05364           }
05365           else
05366           {
05367             if (cell->isFormula())
05368             {
05369                 cell->setCalcDirtyFlag();
05370             }
05371           }
05372 
05373           delete cellBackup;
05374 
05375 
05376 
05377           cell = cellAt( col + coff, row + roff );
05378           if( !refreshCell && cell->updateChart( false ) )
05379           {
05380             refreshCell = cell;
05381           }
05382         }
05383       }
05384     }
05385 
05386     //refresh chart after that you paste all cells
05387 
05388     /* I don't think this is gonna work....doesn't this only update
05389        one chart -- the one which had a dependant cell update first? - John
05390 
05391        I don't have time to check on this now....
05392     */
05393     if ( refreshCell )
05394         refreshCell->updateChart();
05395   }
05396     this->doc()->setModified( true );
05397 
05398     if (!isLoading())
05399         refreshMergedCell();
05400 
05401     emit sig_updateView( this );
05402     emit sig_updateHBorder( this );
05403     emit sig_updateVBorder( this );
05404 
05405     return true;
05406 }
05407 
05408 void Sheet::loadSelectionUndo(const QDomDocument& d, const QRect& loadArea,
05409                               int _xshift, int _yshift,
05410                               bool insert, int insertTo)
05411 {
05412   QDomElement root = d.documentElement(); // "spreadsheet-snippet"
05413 
05414   int rowsInClpbrd    = root.attribute( "rows" ).toInt();
05415   int columnsInClpbrd = root.attribute( "columns" ).toInt();
05416 
05417   // find rect that we paste to
05418   const int pasteWidth = (loadArea.width() >= columnsInClpbrd &&
05419                           util_isRowSelected(loadArea) == false &&
05420                           root.namedItem( "rows" ).toElement().isNull())
05421       ? loadArea.width() : columnsInClpbrd;
05422   const int pasteHeight = (loadArea.height() >= rowsInClpbrd &&
05423                            util_isColumnSelected(loadArea) == false &&
05424                            root.namedItem( "columns" ).toElement().isNull())
05425       ? loadArea.height() : rowsInClpbrd;
05426 
05427   uint numCols = 0;
05428   uint numRows = 0;
05429 
05430   Region region;
05431   for (QDomNode n = root.firstChild(); !n.isNull(); n = n.nextSibling())
05432   {
05433     QDomElement e = n.toElement(); // "columns", "rows" or "cell"
05434     if (e.tagName() == "columns")
05435     {
05436       _yshift = 0;
05437       int col = e.attribute("column").toInt();
05438       int width = e.attribute("count").toInt();
05439       for (int coff = 0; col + coff <= pasteWidth; coff += columnsInClpbrd)
05440       {
05441         uint overlap = QMAX(0, (col - 1 + coff + width) - pasteWidth);
05442         uint effWidth = width - overlap;
05443         region.add(QRect(_xshift + col + coff, 1, effWidth, KS_rowMax));
05444         numCols += effWidth;
05445       }
05446     }
05447     else if (e.tagName() == "rows")
05448     {
05449       _xshift = 0;
05450       int row = e.attribute("row").toInt();
05451       int height = e.attribute("count").toInt();
05452       for (int roff = 0; row + roff <= pasteHeight; roff += rowsInClpbrd)
05453       {
05454         uint overlap = QMAX(0, (row - 1 + roff + height) - pasteHeight);
05455         uint effHeight = height - overlap;
05456         region.add(QRect(1, _yshift + row + roff, KS_colMax, effHeight));
05457         numRows += effHeight;
05458       }
05459     }
05460     else if (!e.isNull())
05461     {
05462       // store the cols/rows for the insertion
05463       int col = e.attribute("column").toInt();
05464       int row = e.attribute("row").toInt();
05465       for (int coff = 0; col + coff <= pasteWidth; coff += columnsInClpbrd)
05466       {
05467         for (int roff = 0; row + roff <= pasteHeight; roff += rowsInClpbrd)
05468         {
05469           region.add(QPoint(_xshift + col + coff, _yshift + row + roff));
05470         }
05471       }
05472     }
05473   }
05474 
05475   if (!doc()->undoLocked())
05476   {
05477     UndoCellPaste *undo = new UndoCellPaste( doc(), this, _xshift, _yshift, region, insert, insertTo );
05478     doc()->addCommand( undo );
05479   }
05480 
05481   if (insert)
05482   {
05483     QRect rect = region.boundingRect();
05484     // shift cells to the right
05485     if (insertTo == -1 && numCols == 0 && numRows == 0)
05486     {
05487       rect.setWidth(rect.width());
05488       shiftRow(rect, false);
05489     }
05490     // shift cells to the bottom
05491     else if (insertTo == 1 && numCols == 0 && numRows == 0)
05492     {
05493       rect.setHeight(rect.height());
05494       shiftColumn( rect, false );
05495     }
05496     // insert columns
05497     else if (insertTo == 0 && numCols == 0 && numRows > 0)
05498     {
05499       insertRow(rect.top(), rect.height() - 1, false);
05500     }
05501     // insert rows
05502     else if (insertTo == 0 && numCols > 0 && numRows == 0)
05503     {
05504       insertColumn(rect.left(), rect.width() - 1, false);
05505     }
05506   }
05507 }
05508 
05509 bool Sheet::testAreaPasteInsert()const
05510 {
05511     QMimeSource* mime = QApplication::clipboard()->data( QClipboard::Clipboard );
05512     if ( !mime )
05513         return false;
05514 
05515     QByteArray b;
05516 
05517     if ( mime->provides( "application/x-kspread-snippet" ) )
05518         b = mime->encodedData( "application/x-kspread-snippet" );
05519     else
05520         return false;
05521 
05522     QBuffer buffer( b );
05523     buffer.open( IO_ReadOnly );
05524     QDomDocument d;
05525     d.setContent( &buffer );
05526     buffer.close();
05527 
05528     QDomElement e = d.documentElement();
05529     if ( !e.namedItem( "columns" ).toElement().isNull() )
05530         return false;
05531 
05532     if ( !e.namedItem( "rows" ).toElement().isNull() )
05533         return false;
05534 
05535     QDomElement c = e.firstChild().toElement();
05536     for( ; !c.isNull(); c = c.nextSibling().toElement() )
05537     {
05538         if ( c.tagName() == "cell" )
05539                 return true;
05540     }
05541     return false;
05542 }
05543 
05544 void Sheet::deleteCells(const Region& region)
05545 {
05546     // A list of all cells we want to delete.
05547     QPtrStack<Cell> cellStack;
05548 
05549   Region::ConstIterator endOfList = region.constEnd();
05550   for (Region::ConstIterator it = region.constBegin(); it != endOfList; ++it)
05551   {
05552     QRect range = (*it)->rect().normalize();
05553 
05554     int right  = range.right();
05555     int left   = range.left();
05556     int bottom = range.bottom();
05557     int col;
05558     for ( int row = range.top(); row <= bottom; ++row )
05559     {
05560       Cell * c = getFirstCellRow( row );
05561       while ( c )
05562       {
05563         col = c->column();
05564         if ( col < left )
05565         {
05566           c = getNextCellRight( left - 1, row );
05567           continue;
05568         }
05569         if ( col > right )
05570           break;
05571 
05572         if ( !c->isDefault() )
05573           cellStack.push( c );
05574 
05575         c = getNextCellRight( col, row );
05576       }
05577     }
05578   }
05579 
05580     d->cells.setAutoDelete( false );
05581 
05582     // Remove the cells from the sheet
05583     while ( !cellStack.isEmpty() )
05584     {
05585       Cell * cell = cellStack.pop();
05586 
05587       d->cells.remove( cell->column(), cell->row() );
05588       cell->setCalcDirtyFlag();
05589       setRegionPaintDirty(cell->cellRect());
05590 
05591       delete cell;
05592     }
05593 
05594     d->cells.setAutoDelete( true );
05595 
05596     setLayoutDirtyFlag();
05597 
05598     // TODO: don't go through all cells here!
05599     // Since obscured cells might have been deleted we
05600     // have to reenforce it.
05601     Cell * c = d->cells.firstCell();
05602     for( ;c; c = c->nextCell() )
05603     {
05604       if ( c->doesMergeCells() && !c->isDefault() )
05605         c->mergeCells( c->column(), c->row(),
05606            c->extraXCells(), c->extraYCells() );
05607     }
05608     doc()->setModified( true );
05609 }
05610 
05611 void Sheet::deleteSelection( Selection* selectionInfo, bool undo )
05612 {
05613     if ( undo && !doc()->undoLocked() )
05614     {
05615         UndoDelete *undo = new UndoDelete( doc(), this, *selectionInfo );
05616         doc()->addCommand( undo );
05617     }
05618 
05619   Region::ConstIterator endOfList = selectionInfo->constEnd();
05620   for (Region::ConstIterator it = selectionInfo->constBegin(); it != endOfList; ++it)
05621   {
05622     QRect range = (*it)->rect().normalize();
05623 
05624     // Entire rows selected ?
05625     if ( util_isRowSelected(range) )
05626     {
05627         for( int i = range.top(); i <= range.bottom(); ++i )
05628         {
05629             d->cells.clearRow( i );
05630             d->rows.removeElement( i );
05631         }
05632 
05633         emit sig_updateVBorder( this );
05634     }
05635     // Entire columns selected ?
05636     else if ( util_isColumnSelected(range) )
05637     {
05638         for( int i = range.left(); i <= range.right(); ++i )
05639         {
05640             d->cells.clearColumn( i );
05641             d->columns.removeElement( i );
05642         }
05643 
05644         emit sig_updateHBorder( this );
05645     }
05646     else
05647     {
05648     setRegionPaintDirty( range );
05649         deleteCells( range );
05650     }
05651   }
05652     refreshMergedCell();
05653     emit sig_updateView( this );
05654 }
05655 
05656 void Sheet::updateView()
05657 {
05658   emit sig_updateView( this );
05659 }
05660 
05661 void Sheet::updateView( QRect const & rect )
05662 {
05663   emit sig_updateView( this, rect );
05664 }
05665 
05666 void Sheet::updateView(Region* region)
05667 {
05668   emit sig_updateView( this, *region );
05669 }
05670 
05671 void Sheet::refreshView( const Region& region )
05672 {
05673   Region tmpRegion;
05674   Region::ConstIterator endOfList = region.constEnd();
05675   for (Region::ConstIterator it = region.constBegin(); it != endOfList; ++it)
05676   {
05677     QRect range = (*it)->rect().normalize();
05678     // TODO: don't go through all cells when refreshing!
05679     QRect tmp(range);
05680     Cell * c = d->cells.firstCell();
05681     for( ;c; c = c->nextCell() )
05682     {
05683       if ( !c->isDefault() &&
05684             c->row() >= range.top() && c->row() <= range.bottom() &&
05685             c->column() >= range.left() && c->column() <= range.right() )
05686       {
05687         if (c->doesMergeCells())
05688         {
05689               int right=QMAX(tmp.right(),c->column()+c->extraXCells());
05690               int bottom=QMAX(tmp.bottom(),c->row()+c->extraYCells());
05691 
05692               tmp.setRight(right);
05693               tmp.setBottom(bottom);
05694         }
05695       }
05696     }
05697     deleteCells( range );
05698     tmpRegion.add(tmp);
05699   }
05700   emit sig_updateView( this, tmpRegion );
05701 }
05702 
05703 
05704 void Sheet::mergeCells(const Region& region, bool hor, bool ver)
05705 {
05706   // sanity check
05707   if( isProtected() )
05708     return;
05709   if( workbook()->isProtected() )
05710     return;
05711 
05712   MergeManipulator* manipulator = new MergeManipulator();
05713   manipulator->setSheet(this);
05714   manipulator->setHorizontalMerge(hor);
05715   manipulator->setVerticalMerge(ver);
05716   manipulator->add(region);
05717   manipulator->execute();
05718 }
05719 
05720 void Sheet::dissociateCells(const Region& region)
05721 {
05722   // sanity check
05723   if( isProtected() )
05724     return;
05725   if( workbook()->isProtected() )
05726     return;
05727 
05728   Manipulator* manipulator = new MergeManipulator();
05729   manipulator->setSheet(this);
05730   manipulator->setReverse(true);
05731   manipulator->add(region);
05732   manipulator->execute();
05733 }
05734 
05735 bool Sheet::testListChoose(Selection* selectionInfo)
05736 {
05737    QRect selection( selectionInfo->selection() );
05738    QPoint marker( selectionInfo->marker() );
05739 
05740    Cell *cell = cellAt( marker.x(), marker.y() );
05741    QString tmp=cell->text();
05742 
05743    Cell* c = firstCell();
05744    bool different=false;
05745    int col;
05746    for( ;c; c = c->nextCell() )
05747      {
05748        col = c->column();
05749        if ( selection.left() <= col && selection.right() >= col &&
05750             !c->isPartOfMerged() &&
05751             !(col==marker.x() && c->row()==marker.y()))
05752    {
05753      if(!c->isFormula() && !c->value().isNumber() && !c->value().asString().isEmpty()
05754         && !c->isTime() &&!c->isDate() )
05755        {
05756                  if(c->text()!=tmp)
05757                      different=true;
05758        }
05759 
05760    }
05761      }
05762    return different;
05763 }
05764 
05765 
05766 
05767 QDomDocument Sheet::saveCellRegion(const Region& region, bool copy, bool era)
05768 {
05769   QDomDocument dd( "spreadsheet-snippet" );
05770   dd.appendChild( dd.createProcessingInstruction( "xml", "version=\"1.0\" encoding=\"UTF-8\"" ) );
05771   QDomElement root = dd.createElement( "spreadsheet-snippet" );
05772   dd.appendChild(root);
05773 
05774   // find the upper left corner of the selection
05775   QRect boundingRect = region.boundingRect();
05776   int left = boundingRect.left();
05777   int top = boundingRect.top();
05778 
05779   // for tiling the clipboard content in the selection
05780   root.setAttribute( "rows", boundingRect.height() );
05781   root.setAttribute( "columns", boundingRect.width() );
05782 
05783   Region::ConstIterator endOfList = region.constEnd();
05784   for (Region::ConstIterator it = region.constBegin(); it != endOfList; ++it)
05785   {
05786     QRect range = (*it)->rect().normalize();
05787 
05788     //
05789     // Entire rows selected?
05790     //
05791     if ((*it)->isRow())
05792     {
05793       QDomElement rows = dd.createElement("rows");
05794       rows.setAttribute( "count", range.height() );
05795       rows.setAttribute( "row", range.top() - top + 1 );
05796       root.appendChild( rows );
05797 
05798       // Save all cells.
05799       for (Cell* cell = d->cells.firstCell(); cell; cell = cell->nextCell())
05800       {
05801         if (!cell->isDefault() && !cell->isPartOfMerged())
05802         {
05803           QPoint point(cell->column(), cell->row());
05804           if (range.contains(point))
05805           {
05806             root.appendChild(cell->save( dd, 0, top - 1, copy, copy, era));
05807           }
05808         }
05809       }
05810 
05811       // ##### Inefficient
05812       // Save the row formats if there are any
05813       RowFormat* format;
05814       for (int row = range.top(); row <= range.bottom(); ++row)
05815       {
05816         format = rowFormat( row );
05817         if (format && !format->isDefault())
05818         {
05819           QDomElement e = format->save(dd, top - 1, copy);
05820           if (!e.isNull())
05821           {
05822             rows.appendChild( e );
05823           }
05824         }
05825       }
05826       continue;
05827     }
05828 
05829     //
05830     // Entire columns selected?
05831     //
05832     if ((*it)->isColumn())
05833     {
05834       QDomElement columns = dd.createElement("columns");
05835       columns.setAttribute( "count", range.width() );
05836       columns.setAttribute( "column", range.left() - left + 1 );
05837       root.appendChild( columns );
05838 
05839       // Save all cells.
05840       for (Cell* cell = d->cells.firstCell();cell; cell = cell->nextCell())
05841       {
05842         if (!cell->isDefault() && !cell->isPartOfMerged())
05843         {
05844           QPoint point(cell->column(), cell->row());
05845           if (range.contains(point))
05846           {
05847             root.appendChild(cell->save( dd, left - 1, 0, copy, copy, era));
05848           }
05849         }
05850       }
05851 
05852       // ##### Inefficient
05853       // Save the column formats if there are any
05854       ColumnFormat* format;
05855       for (int col = range.left(); col <= range.right(); ++col)
05856       {
05857         format = columnFormat(col);
05858         if (format && !format->isDefault())
05859         {
05860           QDomElement e = format->save(dd, left - 1, copy);
05861           if (!e.isNull())
05862           {
05863             columns.appendChild(e);
05864           }
05865         }
05866       }
05867       continue;
05868     }
05869 
05870     // Save all cells.
05871     //store all cell
05872     //when they don't exist we created them
05873     //because it's necessary when there is a  format on a column/row
05874     //but I remove cell which is inserted.
05875     Cell* cell;
05876     bool insert;
05877     enableScrollBarUpdates(false);
05878     for (int col = range.left(); col <= range.right(); ++col)
05879     {
05880       for (int row = range.top(); row <= range.bottom(); ++row)
05881       {
05882         insert = false;
05883         cell = cellAt(col, row);
05884         if (cell == d->defaultCell)
05885         {
05886           cell = new Cell(this, col, row);
05887           insertCell(cell);
05888           insert = true;
05889         }
05890         root.appendChild(cell->save(dd, left - 1, top - 1, true, copy, era));
05891         if (insert)
05892         {
05893           d->cells.remove(col, row);
05894         }
05895       }
05896     }
05897     enableScrollBarUpdates(true);
05898   }
05899   return dd;
05900 }
05901 
05902 QDomElement Sheet::saveXML( QDomDocument& dd )
05903 {
05904     QDomElement sheet = dd.createElement( "table" );
05905     sheet.setAttribute( "name", d->name );
05906 
05907 
05908     //Laurent: for oasis format I think that we must use style:direction...
05909     sheet.setAttribute( "layoutDirection", (d->layoutDirection == RightToLeft) ? "rtl" : "ltr" );
05910     sheet.setAttribute( "columnnumber", (int)d->showColumnNumber);
05911     sheet.setAttribute( "borders", (int)d->showPageBorders);
05912     sheet.setAttribute( "hide", (int)d->hide);
05913     sheet.setAttribute( "hidezero", (int)d->hideZero);
05914     sheet.setAttribute( "firstletterupper", (int)d->firstLetterUpper);
05915     sheet.setAttribute( "grid", (int)d->showGrid );
05916     sheet.setAttribute( "printGrid", (int)d->print->printGrid() );
05917     sheet.setAttribute( "printCommentIndicator", (int)d->print->printCommentIndicator() );
05918     sheet.setAttribute( "printFormulaIndicator", (int)d->print->printFormulaIndicator() );
05919     sheet.setAttribute( "showFormula", (int)d->showFormula);
05920     sheet.setAttribute( "showFormulaIndicator", (int)d->showFormulaIndicator);
05921     sheet.setAttribute( "showCommentIndicator", (int)d->showCommentIndicator);
05922     sheet.setAttribute( "lcmode", (int)d->lcMode);
05923     sheet.setAttribute( "autoCalc", (int)d->autoCalc);
05924     sheet.setAttribute( "borders1.2", 1);
05925     if ( !d->password.isNull() )
05926     {
05927       if ( d->password.size() > 0 )
05928       {
05929         QCString str = KCodecs::base64Encode( d->password );
05930         sheet.setAttribute( "protected", QString( str.data() ) );
05931       }
05932       else
05933         sheet.setAttribute( "protected", "" );
05934     }
05935 
05936     // paper parameters
05937     QDomElement paper = dd.createElement( "paper" );
05938     paper.setAttribute( "format", d->print->paperFormatString() );
05939     paper.setAttribute( "orientation", d->print->orientationString() );
05940     sheet.appendChild( paper );
05941 
05942     QDomElement borders = dd.createElement( "borders" );
05943     borders.setAttribute( "left", d->print->leftBorder() );
05944     borders.setAttribute( "top", d->print->topBorder() );
05945     borders.setAttribute( "right", d->print->rightBorder() );
05946     borders.setAttribute( "bottom", d->print->bottomBorder() );
05947     paper.appendChild( borders );
05948 
05949     QDomElement head = dd.createElement( "head" );
05950     paper.appendChild( head );
05951     if ( !d->print->headLeft().isEmpty() )
05952     {
05953       QDomElement left = dd.createElement( "left" );
05954       head.appendChild( left );
05955       left.appendChild( dd.createTextNode( d->print->headLeft() ) );
05956     }
05957     if ( !d->print->headMid().isEmpty() )
05958     {
05959       QDomElement center = dd.createElement( "center" );
05960       head.appendChild( center );
05961       center.appendChild( dd.createTextNode( d->print->headMid() ) );
05962     }
05963     if ( !d->print->headRight().isEmpty() )
05964     {
05965       QDomElement right = dd.createElement( "right" );
05966       head.appendChild( right );
05967       right.appendChild( dd.createTextNode( d->print->headRight() ) );
05968     }
05969     QDomElement foot = dd.createElement( "foot" );
05970     paper.appendChild( foot );
05971     if ( !d->print->footLeft().isEmpty() )
05972     {
05973       QDomElement left = dd.createElement( "left" );
05974       foot.appendChild( left );
05975       left.appendChild( dd.createTextNode( d->print->footLeft() ) );
05976     }
05977     if ( !d->print->footMid().isEmpty() )
05978     {
05979       QDomElement center = dd.createElement( "center" );
05980       foot.appendChild( center );
05981       center.appendChild( dd.createTextNode( d->print->footMid() ) );
05982     }
05983     if ( !d->print->footRight().isEmpty() )
05984     {
05985       QDomElement right = dd.createElement( "right" );
05986       foot.appendChild( right );
05987       right.appendChild( dd.createTextNode( d->print->footRight() ) );
05988     }
05989 
05990     // print range
05991     QDomElement printrange = dd.createElement( "printrange-rect" );
05992     QRect _printRange = d->print->printRange();
05993     int left = _printRange.left();
05994     int right = _printRange.right();
05995     int top = _printRange.top();
05996     int bottom = _printRange.bottom();
05997     //If whole rows are selected, then we store zeros, as KS_colMax may change in future
05998     if ( left == 1 && right == KS_colMax )
05999     {
06000       left = 0;
06001       right = 0;
06002     }
06003     //If whole columns are selected, then we store zeros, as KS_rowMax may change in future
06004     if ( top == 1 && bottom == KS_rowMax )
06005     {
06006       top = 0;
06007       bottom = 0;
06008     }
06009     printrange.setAttribute( "left-rect", left );
06010     printrange.setAttribute( "right-rect", right );
06011     printrange.setAttribute( "bottom-rect", bottom );
06012     printrange.setAttribute( "top-rect", top );
06013     sheet.appendChild( printrange );
06014 
06015     // Print repeat columns
06016     QDomElement printRepeatColumns = dd.createElement( "printrepeatcolumns" );
06017     printRepeatColumns.setAttribute( "left", d->print->printRepeatColumns().first );
06018     printRepeatColumns.setAttribute( "right", d->print->printRepeatColumns().second );
06019     sheet.appendChild( printRepeatColumns );
06020 
06021     // Print repeat rows
06022     QDomElement printRepeatRows = dd.createElement( "printrepeatrows" );
06023     printRepeatRows.setAttribute( "top", d->print->printRepeatRows().first );
06024     printRepeatRows.setAttribute( "bottom", d->print->printRepeatRows().second );
06025     sheet.appendChild( printRepeatRows );
06026 
06027     //Save print zoom
06028     sheet.setAttribute( "printZoom", d->print->zoom() );
06029 
06030     //Save page limits
06031     sheet.setAttribute( "printPageLimitX", d->print->pageLimitX() );
06032     sheet.setAttribute( "printPageLimitY", d->print->pageLimitY() );
06033 
06034     // Save all cells.
06035     Cell* c = d->cells.firstCell();
06036     for( ;c; c = c->nextCell() )
06037     {
06038         if ( !c->isDefault() )
06039         {
06040             QDomElement e = c->save( dd );
06041             if ( !e.isNull() )
06042                 sheet.appendChild( e );
06043         }
06044     }
06045 
06046     // Save all RowFormat objects.
06047     RowFormat* rl = d->rows.first();
06048     for( ; rl; rl = rl->next() )
06049     {
06050         if ( !rl->isDefault() )
06051         {
06052             QDomElement e = rl->save( dd );
06053             if ( e.isNull() )
06054                 return QDomElement();
06055             sheet.appendChild( e );
06056         }
06057     }
06058 
06059     // Save all ColumnFormat objects.
06060     ColumnFormat* cl = d->columns.first();
06061     for( ; cl; cl = cl->next() )
06062     {
06063         if ( !cl->isDefault() )
06064         {
06065             QDomElement e = cl->save( dd );
06066             if ( e.isNull() )
06067                 return QDomElement();
06068             sheet.appendChild( e );
06069         }
06070     }
06071 
06072     QPtrListIterator<EmbeddedObject>  chl = doc()->embeddedObjects();
06073     for( ; chl.current(); ++chl )
06074     {
06075        if ( chl.current()->sheet() == this )
06076        {
06077          QDomElement e = chl.current()->save( dd );
06078 
06079          if ( e.isNull() )
06080            return QDomElement();
06081          sheet.appendChild( e );
06082        }
06083     }
06084     return sheet;
06085 }
06086 
06087 bool Sheet::isLoading()
06088 {
06089     return doc()->isLoading();
06090 }
06091 
06092 
06093 QPtrList<EmbeddedObject> Sheet::getSelectedObjects()
06094 {
06095     QPtrList<EmbeddedObject> objects;
06096     QPtrListIterator<EmbeddedObject> it = doc()->embeddedObjects();
06097     for ( ; it.current() ; ++it )
06098     {
06099         if( it.current()->isSelected()
06100             && it.current()->sheet() == this )
06101         {
06102             objects.append( it.current() );
06103         }
06104     }
06105      return objects;
06106 }
06107 
06108 KoRect Sheet::getRealRect( bool all )
06109 {
06110     KoRect rect;
06111 
06112     QPtrListIterator<EmbeddedObject> it( doc()->embeddedObjects() );
06113     for ( ; it.current() ; ++it )
06114     {
06115 
06116         if ( all || ( it.current()->isSelected() && ! it.current()->isProtect() ) )
06117             rect |= it.current()->geometry();
06118     }
06119 
06120     return rect;
06121 }
06122 
06123 // move object for releasemouseevent
06124 KCommand *Sheet::moveObject(View *_view, double diffx, double diffy)
06125 {
06126     bool createCommand=false;
06127     MoveObjectByCmd *moveByCmd=0L;
06128     Canvas * canvas = _view->canvasWidget();
06129     QPtrList<EmbeddedObject> _objects;
06130     _objects.setAutoDelete( false );
06131     QPtrListIterator<EmbeddedObject> it( doc()->embeddedObjects()/*m_objectList*/ );
06132     for ( ; it.current() ; ++it )
06133     {
06134         if ( it.current()->isSelected() && !it.current()->isProtect())
06135         {
06136             _objects.append( it.current() );
06137             KoRect geometry = it.current()->geometry();
06138             geometry.moveBy( -canvas->xOffset(), -canvas->yOffset() );
06139             QRect br = doc()->zoomRect( geometry/*it.current()->geometry()*/ );
06140             br.moveBy( doc()->zoomItX( diffx ), doc()->zoomItY( diffy ) );
06141             br.moveBy( doc()->zoomItX( -canvas->xOffset() ), doc()->zoomItY( -canvas->yOffset() ) );
06142             canvas->repaint( br ); // Previous position
06143             canvas->repaintObject( it.current() ); // New position
06144             createCommand=true;
06145         }
06146     }
06147     if(createCommand) {
06148         moveByCmd = new MoveObjectByCmd( i18n( "Move Objects" ), KoPoint( diffx, diffy ),
06149                                    _objects, doc(), this );
06150 
06151 //         m_doc->updateSideBarItem( this );
06152     }
06153     return moveByCmd;
06154 }
06155 
06156 KCommand *Sheet::moveObject(View *_view,const KoPoint &_move,bool key)
06157 {
06158     QPtrList<EmbeddedObject> _objects;
06159     _objects.setAutoDelete( false );
06160     MoveObjectByCmd *moveByCmd=0L;
06161     Canvas * canvas = _view->canvasWidget();
06162     QPtrListIterator<EmbeddedObject> it( doc()->embeddedObjects()/*m_objectList*/ );
06163     for ( ; it.current() ; ++it )
06164     {
06165         if ( it.current()->isSelected() && !it.current()->isProtect()) {
06166 
06167             KoRect geometry = it.current()->geometry();
06168             geometry.moveBy( -canvas->xOffset(), -canvas->yOffset() );
06169             QRect oldBoundingRect = doc()->zoomRect( geometry );
06170 
06171 
06172             KoRect r = it.current()->geometry();
06173             r.moveBy( _move.x(), _move.y() );
06174 
06175             it.current()->setGeometry( r );
06176             _objects.append( it.current() );
06177 
06178             canvas->repaint( oldBoundingRect );
06179             canvas->repaintObject( it.current() );
06180         }
06181     }
06182 
06183     if ( key && !_objects.isEmpty())
06184         moveByCmd = new MoveObjectByCmd( i18n( "Move Objects" ),
06185                                    KoPoint( _move ),
06186                                    _objects, doc() ,this );
06187 
06188     return moveByCmd;
06189 }
06190 
06191 /*
06192  * Check if object name already exists.
06193  */
06194 bool Sheet::objectNameExists( EmbeddedObject *object, QPtrList<EmbeddedObject> &list ) {
06195     QPtrListIterator<EmbeddedObject> it( list );
06196 
06197     for ( it.toFirst(); it.current(); ++it ) {
06198         // object name can exist in current object.
06199         if ( it.current()->getObjectName() == object->getObjectName() &&
06200              it.current() != object ) {
06201             return true;
06202         }
06203     }
06204     return false;
06205 }
06206 
06207 void Sheet::unifyObjectName( EmbeddedObject *object ) {
06208     if ( object->getObjectName().isEmpty() ) {
06209         object->setObjectName( object->getTypeString() );
06210     }
06211     QString objectName( object->getObjectName() );
06212 
06213     QPtrList<EmbeddedObject> list( doc()->embeddedObjects() );
06214 
06215     int count = 1;
06216 
06217     while ( objectNameExists( object, list ) ) {
06218         count++;
06219         QRegExp rx( " \\(\\d{1,3}\\)$" );
06220         if ( rx.search( objectName ) != -1 ) {
06221             objectName.remove( rx );
06222         }
06223         objectName += QString(" (%1)").arg( count );
06224         object->setObjectName( objectName );
06225     }
06226 }
06227 
06228 
06229 void Sheet::checkContentDirection( QString const & name )
06230 {
06231   /* set sheet's direction to RTL if sheet name is an RTL string */
06232   if ( (name.isRightToLeft()) )
06233     setLayoutDirection( RightToLeft );
06234   else
06235     setLayoutDirection( LeftToRight );
06236 
06237   emit sig_refreshView();
06238 }
06239 
06240 bool Sheet::loadSheetStyleFormat( QDomElement *style )
06241 {
06242     QString hleft, hmiddle, hright;
06243     QString fleft, fmiddle, fright;
06244     QDomNode header = KoDom::namedItemNS( *style, KoXmlNS::style, "header" );
06245 
06246     if ( !header.isNull() )
06247     {
06248         kdDebug() << "Header exists" << endl;
06249         QDomNode part = KoDom::namedItemNS( header, KoXmlNS::style, "region-left" );
06250         if ( !part.isNull() )
06251         {
06252             hleft = getPart( part );
06253             kdDebug() << "Header left: " << hleft << endl;
06254         }
06255         else
06256             kdDebug() << "Style:region:left doesn't exist!" << endl;
06257         part = KoDom::namedItemNS( header, KoXmlNS::style, "region-center" );
06258         if ( !part.isNull() )
06259         {
06260             hmiddle = getPart( part );
06261             kdDebug() << "Header middle: " << hmiddle << endl;
06262         }
06263         part = KoDom::namedItemNS( header, KoXmlNS::style, "region-right" );
06264         if ( !part.isNull() )
06265         {
06266             hright = getPart( part );
06267             kdDebug() << "Header right: " << hright << endl;
06268         }
06269     }
06270     //TODO implement it under kspread
06271     QDomNode headerleft = KoDom::namedItemNS( *style, KoXmlNS::style, "header-left" );
06272     if ( !headerleft.isNull() )
06273     {
06274         QDomElement e = headerleft.toElement();
06275         if ( e.hasAttributeNS( KoXmlNS::style, "display" ) )
06276             kdDebug()<<"header.hasAttribute( style:display ) :"<<e.hasAttributeNS( KoXmlNS::style, "display" )<<endl;
06277         else
06278             kdDebug()<<"header left doesn't has attribute  style:display  \n";
06279     }
06280     //TODO implement it under kspread
06281     QDomNode footerleft = KoDom::namedItemNS( *style, KoXmlNS::style, "footer-left" );
06282     if ( !footerleft.isNull() )
06283     {
06284         QDomElement e = footerleft.toElement();
06285         if ( e.hasAttributeNS( KoXmlNS::style, "display" ) )
06286             kdDebug()<<"footer.hasAttribute( style:display ) :"<<e.hasAttributeNS( KoXmlNS::style, "display" )<<endl;
06287         else
06288             kdDebug()<<"footer left doesn't has attribute  style:display  \n";
06289     }
06290 
06291     QDomNode footer = KoDom::namedItemNS( *style, KoXmlNS::style, "footer" );
06292 
06293     if ( !footer.isNull() )
06294     {
06295         QDomNode part = KoDom::namedItemNS( footer, KoXmlNS::style, "region-left" );
06296         if ( !part.isNull() )
06297         {
06298             fleft = getPart( part );
06299             kdDebug() << "Footer left: " << fleft << endl;
06300         }
06301         part = KoDom::namedItemNS( footer, KoXmlNS::style, "region-center" );
06302         if ( !part.isNull() )
06303         {
06304             fmiddle = getPart( part );
06305             kdDebug() << "Footer middle: " << fmiddle << endl;
06306         }
06307         part = KoDom::namedItemNS( footer, KoXmlNS::style, "region-right" );
06308         if ( !part.isNull() )
06309         {
06310             fright = getPart( part );
06311             kdDebug() << "Footer right: " << fright << endl;
06312         }
06313     }
06314 
06315     print()->setHeadFootLine( hleft, hmiddle, hright,
06316                               fleft, fmiddle, fright );
06317     return true;
06318 }
06319 
06320 void Sheet::replaceMacro( QString & text, const QString & old, const QString & newS )
06321 {
06322   int n = text.find( old );
06323   if ( n != -1 )
06324     text = text.replace( n, old.length(), newS );
06325 }
06326 
06327 
06328 QString Sheet::getPart( const QDomNode & part )
06329 {
06330   QString result;
06331   QDomElement e = KoDom::namedItemNS( part, KoXmlNS::text, "p" );
06332   while ( !e.isNull() )
06333   {
06334     QString text = e.text();
06335     kdDebug() << "PART: " << text << endl;
06336 
06337     QDomElement macro = KoDom::namedItemNS( e, KoXmlNS::text, "time" );
06338     if ( !macro.isNull() )
06339       replaceMacro( text, macro.text(), "<time>" );
06340 
06341     macro = KoDom::namedItemNS( e, KoXmlNS::text, "date" );
06342     if ( !macro.isNull() )
06343       replaceMacro( text, macro.text(), "<date>" );
06344 
06345     macro = KoDom::namedItemNS( e, KoXmlNS::text, "page-number" );
06346     if ( !macro.isNull() )
06347       replaceMacro( text, macro.text(), "<page>" );
06348 
06349     macro = KoDom::namedItemNS( e, KoXmlNS::text, "page-count" );
06350     if ( !macro.isNull() )
06351       replaceMacro( text, macro.text(), "<pages>" );
06352 
06353     macro = KoDom::namedItemNS( e, KoXmlNS::text, "sheet-name" );
06354     if ( !macro.isNull() )
06355       replaceMacro( text, macro.text(), "<sheet>" );
06356 
06357     macro = KoDom::namedItemNS( e, KoXmlNS::text, "title" );
06358     if ( !macro.isNull() )
06359       replaceMacro( text, macro.text(), "<name>" );
06360 
06361     macro = KoDom::namedItemNS( e, KoXmlNS::text, "file-name" );
06362     if ( !macro.isNull() )
06363       replaceMacro( text, macro.text(), "<file>" );
06364 
06365     //add support for multi line into kspread
06366     if ( !result.isEmpty() )
06367       result += '\n';
06368     result += text;
06369     e = e.nextSibling().toElement();
06370   }
06371 
06372   return result;
06373 }
06374 
06375 
06376 bool Sheet::loadOasis( const QDomElement& sheetElement, KoOasisLoadingContext& oasisContext, QDict<Style>& styleMap )
06377 {
06378     d->layoutDirection = LeftToRight;
06379     if ( sheetElement.hasAttributeNS( KoXmlNS::table, "style-name" ) )
06380     {
06381         QString stylename = sheetElement.attributeNS( KoXmlNS::table, "style-name", QString::null );
06382         kdDebug()<<" style of table :"<<stylename<<endl;
06383         const QDomElement *style = oasisContext.oasisStyles().findStyle( stylename, "table" );
06384         Q_ASSERT( style );
06385         kdDebug()<<" style :"<<style<<endl;
06386         if ( style )
06387         {
06388             QDomElement properties( KoDom::namedItemNS( *style, KoXmlNS::style, "table-properties" ) );
06389             if ( !properties.isNull() )
06390             {
06391                 if ( properties.hasAttributeNS( KoXmlNS::table, "display" ) )
06392                 {
06393                     bool visible = (properties.attributeNS( KoXmlNS::table, "display", QString::null ) == "true" ? true : false );
06394                     d->hide = !visible;
06395                 }
06396             }
06397             if ( style->hasAttributeNS( KoXmlNS::style, "master-page-name" ) )
06398             {
06399                 QString masterPageStyleName = style->attributeNS( KoXmlNS::style, "master-page-name", QString::null );
06400                 kdDebug()<<"style->attribute( style:master-page-name ) :"<<masterPageStyleName <<endl;
06401                 QDomElement *masterStyle = oasisContext.oasisStyles().masterPages()[masterPageStyleName];
06402                 kdDebug()<<"oasisStyles.styles()[masterPageStyleName] :"<<masterStyle<<endl;
06403                 if ( masterStyle )
06404                 {
06405                     loadSheetStyleFormat( masterStyle );
06406                     if ( masterStyle->hasAttributeNS( KoXmlNS::style, "page-layout-name" ) )
06407                     {
06408                         QString masterPageLayoutStyleName = masterStyle->attributeNS( KoXmlNS::style, "page-layout-name", QString::null );
06409                         kdDebug()<<"masterPageLayoutStyleName :"<<masterPageLayoutStyleName<<endl;
06410                         const QDomElement *masterLayoutStyle = oasisContext.oasisStyles().findStyle( masterPageLayoutStyleName );
06411                       if ( masterLayoutStyle )
06412                       {
06413                         kdDebug()<<"masterLayoutStyle :"<<masterLayoutStyle<<endl;
06414                         KoStyleStack styleStack;
06415                         styleStack.setTypeProperties( "page-layout" );
06416                         styleStack.push( *masterLayoutStyle );
06417                         loadOasisMasterLayoutPage( styleStack );
06418                       }
06419                     }
06420                 }
06421             }
06422         }
06423     }
06424 
06425     //Maps from a column index to the name of the default cell style for that column
06426     QMap<int,QString> defaultColumnCellStyles;
06427 
06428     int rowIndex = 1;
06429     int indexCol = 1;
06430     QDomNode rowNode = sheetElement.firstChild();
06431     // Some spreadsheet programs may support more rows than
06432     // KSpread so limit the number of repeated rows.
06433     // FIXME POSSIBLE DATA LOSS!
06434     while( !rowNode.isNull() && rowIndex <= KS_rowMax )
06435     {
06436         kdDebug()<<" rowIndex :"<<rowIndex<<" indexCol :"<<indexCol<<endl;
06437         QDomElement rowElement = rowNode.toElement();
06438         if( !rowElement.isNull() )
06439         {
06440             kdDebug()<<" Sheet::loadOasis rowElement.tagName() :"<<rowElement.localName()<<endl;
06441             if ( rowElement.namespaceURI() == KoXmlNS::table )
06442             {
06443                 if ( rowElement.localName()=="table-column" && indexCol <= KS_colMax )
06444                 {
06445                     kdDebug ()<<" table-column found : index column before "<< indexCol<<endl;
06446                     loadColumnFormat( rowElement, oasisContext.oasisStyles(), indexCol , styleMap);
06447                     kdDebug ()<<" table-column found : index column after "<< indexCol<<endl;
06448                 }
06449                 else if ( rowElement.localName() == "table-header-rows" )
06450                 {
06451                   QDomNode headerRowNode = rowElement.firstChild();
06452                   while ( !headerRowNode.isNull() )
06453                   {
06454                     // NOTE Handle header rows as ordinary ones
06455                     //      as long as they're not supported.
06456                     loadRowFormat( headerRowNode.toElement(), rowIndex,
06457                                    oasisContext, /*rowNode.isNull() ,*/ styleMap );
06458                     headerRowNode = headerRowNode.nextSibling();
06459                   }
06460                 }
06461                 else if( rowElement.localName() == "table-row" )
06462                 {
06463                     kdDebug()<<" table-row found :index row before "<<rowIndex<<endl;
06464                     loadRowFormat( rowElement, rowIndex, oasisContext, /*rowNode.isNull() ,*/ styleMap );
06465                     kdDebug()<<" table-row found :index row after "<<rowIndex<<endl;
06466                 }
06467                 else if ( rowElement.localName() == "shapes" )
06468                     loadOasisObjects( rowElement, oasisContext );
06469             }
06470         }
06471         rowNode = rowNode.nextSibling();
06472     }
06473 
06474     if ( sheetElement.hasAttributeNS( KoXmlNS::table, "print-ranges" ) )
06475     {
06476         // e.g.: Sheet4.A1:Sheet4.E28
06477         QString range = sheetElement.attributeNS( KoXmlNS::table, "print-ranges", QString::null );
06478         range = Oasis::decodeFormula( range );
06479         Range p( range );
06480         if ( sheetName() == p.sheetName() )
06481             d->print->setPrintRange( p.range() );
06482     }
06483 
06484 
06485     if ( sheetElement.attributeNS( KoXmlNS::table, "protected", QString::null ) == "true" )
06486     {
06487         QCString passwd( "" );
06488         if ( sheetElement.hasAttributeNS( KoXmlNS::table, "protection-key" ) )
06489         {
06490             QString p = sheetElement.attributeNS( KoXmlNS::table, "protection-key", QString::null );
06491             QCString str( p.latin1() );
06492             kdDebug(30518) << "Decoding password: " << str << endl;
06493             passwd = KCodecs::base64Decode( str );
06494         }
06495         kdDebug(30518) << "Password hash: '" << passwd << "'" << endl;
06496         d->password = passwd;
06497     }
06498     return true;
06499 }
06500 
06501 
06502 void Sheet::loadOasisObjects( const QDomElement &parent, KoOasisLoadingContext& oasisContext )
06503 {
06504     QDomElement e;
06505     QDomNode n = parent.firstChild();
06506     while( !n.isNull() )
06507     {
06508         e = n.toElement();
06509         if ( e.localName() == "frame" && e.namespaceURI() == KoXmlNS::draw )
06510         {
06511           EmbeddedObject *obj = 0;
06512           QDomNode object = KoDom::namedItemNS( e, KoXmlNS::draw, "object" );
06513           if ( !object.isNull() )
06514           {
06515             if ( !object.toElement().attributeNS( KoXmlNS::draw, "notify-on-update-of-ranges", QString::null).isNull() )
06516                 obj = new EmbeddedChart( doc(), this );
06517             else
06518                 obj = new EmbeddedKOfficeObject( doc(), this );
06519           }
06520           else
06521           {
06522             QDomNode image = KoDom::namedItemNS( e, KoXmlNS::draw, "image" );
06523             if ( !image.isNull() )
06524               obj = new EmbeddedPictureObject( this, doc()->pictureCollection() );
06525             else
06526               kdDebug() << "Object type wasn't loaded!" << endl;
06527           }
06528 
06529           if ( obj )
06530           {
06531             obj->loadOasis( e, oasisContext );
06532             insertObject( obj );
06533           }
06534         }
06535         n = n.nextSibling();
06536     }
06537 }
06538 
06539 
06540 void Sheet::loadOasisMasterLayoutPage( KoStyleStack &styleStack )
06541 {
06542     // use A4 as default page size
06543     float left = 20.0;
06544     float right = 20.0;
06545     float top = 20.0;
06546     float bottom = 20.0;
06547     float width = 210.0;
06548     float height = 297.0;
06549     QString orientation = "Portrait";
06550     QString format;
06551 
06552     // Laurent : Why we stored layout information as Millimeter ?!!!!!
06553     // kspread used point for all other attribute
06554     // I don't understand :(
06555     if ( styleStack.hasAttributeNS( KoXmlNS::fo, "page-width" ) )
06556     {
06557         width = KoUnit::toMM(KoUnit::parseValue( styleStack.attributeNS( KoXmlNS::fo, "page-width" ) ) );
06558     }
06559     if ( styleStack.hasAttributeNS( KoXmlNS::fo, "page-height" ) )
06560     {
06561         height = KoUnit::toMM( KoUnit::parseValue( styleStack.attributeNS( KoXmlNS::fo, "page-height" ) ) );
06562     }
06563     if ( styleStack.hasAttributeNS( KoXmlNS::fo, "margin-top" ) )
06564     {
06565         top = KoUnit::toMM(KoUnit::parseValue( styleStack.attributeNS( KoXmlNS::fo, "margin-top" ) ) );
06566     }
06567     if ( styleStack.hasAttributeNS( KoXmlNS::fo, "margin-bottom" ) )
06568     {
06569         bottom = KoUnit::toMM(KoUnit::parseValue( styleStack.attributeNS( KoXmlNS::fo, "margin-bottom" ) ) );
06570     }
06571     if ( styleStack.hasAttributeNS( KoXmlNS::fo, "margin-left" ) )
06572     {
06573         left = KoUnit::toMM(KoUnit::parseValue( styleStack.attributeNS( KoXmlNS::fo, "margin-left" ) ) );
06574     }
06575     if ( styleStack.hasAttributeNS( KoXmlNS::fo, "margin-right" ) )
06576     {
06577         right = KoUnit::toMM(KoUnit::parseValue( styleStack.attributeNS( KoXmlNS::fo, "margin-right" ) ) );
06578     }
06579     if ( styleStack.hasAttributeNS( KoXmlNS::style, "writing-mode" ) )
06580     {
06581         kdDebug()<<"styleStack.hasAttribute( style:writing-mode ) :"<<styleStack.hasAttributeNS( KoXmlNS::style, "writing-mode" )<<endl;
06582         d->layoutDirection = ( styleStack.attributeNS( KoXmlNS::style, "writing-mode" )=="lr-tb" ) ? LeftToRight : RightToLeft;
06583         //TODO
06584         //<value>lr-tb</value>
06585         //<value>rl-tb</value>
06586         //<value>tb-rl</value>
06587         //<value>tb-lr</value>
06588         //<value>lr</value>
06589         //<value>rl</value>
06590         //<value>tb</value>
06591         //<value>page</value>
06592 
06593     }
06594     if ( styleStack.hasAttributeNS( KoXmlNS::style, "print-orientation" ) )
06595     {
06596         orientation = ( styleStack.attributeNS( KoXmlNS::style, "print-orientation" )=="landscape" ) ? "Landscape" : "Portrait" ;
06597     }
06598     if ( styleStack.hasAttributeNS( KoXmlNS::style, "num-format" ) )
06599     {
06600         //not implemented into kspread
06601         //These attributes specify the numbering style to use.
06602         //If a numbering style is not specified, the numbering style is inherited from
06603         //the page style. See section 6.7.8 for information on these attributes
06604         kdDebug()<<" num-format :"<<styleStack.attributeNS( KoXmlNS::style, "num-format" )<<endl;
06605 
06606     }
06607     if ( styleStack.hasAttributeNS( KoXmlNS::fo, "background-color" ) )
06608     {
06609         //TODO
06610         kdDebug()<<" fo:background-color :"<<styleStack.attributeNS( KoXmlNS::fo, "background-color" )<<endl;
06611     }
06612     if ( styleStack.hasAttributeNS( KoXmlNS::style, "print" ) )
06613     {
06614         //todo parsing
06615         QString str = styleStack.attributeNS( KoXmlNS::style, "print" );
06616         kdDebug()<<" style:print :"<<str<<endl;
06617 
06618         if (str.contains( "headers" ) )
06619         {
06620             //TODO implement it into kspread
06621         }
06622         if ( str.contains( "grid" ) )
06623         {
06624             d->print->setPrintGrid( true );
06625         }
06626         if ( str.contains( "annotations" ) )
06627         {
06628             //TODO it's not implemented
06629         }
06630         if ( str.contains( "objects" ) )
06631         {
06632             //TODO it's not implemented
06633         }
06634         if ( str.contains( "charts" ) )
06635         {
06636             //TODO it's not implemented
06637         }
06638         if ( str.contains( "drawings" ) )
06639         {
06640             //TODO it's not implemented
06641         }
06642         if ( str.contains( "formulas" ) )
06643         {
06644             d->showFormula = true;
06645         }
06646         if ( str.contains( "zero-values" ) )
06647         {
06648             //TODO it's not implemented
06649         }
06650     }
06651     if ( styleStack.hasAttributeNS( KoXmlNS::style, "table-centering" ) )
06652     {
06653         QString str = styleStack.attributeNS( KoXmlNS::style, "table-centering" );
06654         //TODO not implemented into kspread
06655         kdDebug()<<" styleStack.attribute( style:table-centering ) :"<<str<<endl;
06656 #if 0
06657         if ( str == "horizontal" )
06658         {
06659         }
06660         else if ( str == "vertical" )
06661         {
06662         }
06663         else if ( str == "both" )
06664         {
06665         }
06666         else if ( str == "none" )
06667         {
06668         }
06669         else
06670             kdDebug()<<" table-centering unknown :"<<str<<endl;
06671 #endif
06672     }
06673     format = QString( "%1x%2" ).arg( width ).arg( height );
06674     kdDebug()<<" format : "<<format<<endl;
06675     d->print->setPaperLayout( left, top, right, bottom, format, orientation );
06676 
06677     kdDebug()<<" left margin :"<<left<<" right :"<<right<<" top :"<<top<<" bottom :"<<bottom<<endl;
06678 //<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"/>
06679 //          QString format = paper.attribute( "format" );
06680 //      QString orientation = paper.attribute( "orientation" );
06681 //        d->print->setPaperLayout( left, top, right, bottom, format, orientation );
06682 //      }
06683 }
06684 
06685 
06686 bool Sheet::loadColumnFormat(const QDomElement& column, const KoOasisStyles& oasisStyles, int & indexCol, const QDict<Style>& styleMap)
06687 {
06688     kdDebug()<<"bool Sheet::loadColumnFormat(const QDomElement& column, const KoOasisStyles& oasisStyles, unsigned int & indexCol ) index Col :"<<indexCol<<endl;
06689 
06690     bool collapsed = ( column.attributeNS( KoXmlNS::table, "visibility", QString::null ) == "collapse" );
06691     Format layout( this , doc()->styleManager()->defaultStyle() );
06692     int number = 1;
06693     double width   = 10;//POINT_TO_MM( colWidth ); FIXME
06694     if ( column.hasAttributeNS( KoXmlNS::table, "number-columns-repeated" ) )
06695     {
06696         bool ok = true;
06697         int n = column.attributeNS( KoXmlNS::table, "number-columns-repeated", QString::null ).toInt( &ok );
06698         if ( ok )
06699           // Some spreadsheet programs may support more rows than KSpread so
06700           // limit the number of repeated rows.
06701           // FIXME POSSIBLE DATA LOSS!
06702           number = QMIN( n, KS_colMax - indexCol + 1 );
06703         kdDebug() << "Repeated: " << number << endl;
06704     }
06705 
06706     KoStyleStack styleStack;
06707     styleStack.setTypeProperties("table-column"); //style for column
06708     if ( column.hasAttributeNS( KoXmlNS::table, "default-cell-style-name" ) )
06709     {
06710         QString str = column.attributeNS( KoXmlNS::table, "default-cell-style-name", QString::null );
06711         kdDebug()<<" default-cell-style-name:"<<str<<" for column " << indexCol <<endl;
06712 
06713     //TODO - Code to look up the style in styleMap and store a reference to it in some map
06714     // between column indicies and Style instances.  This can then be used when rendering cells
06715 
06716     }
06717 
06718     styleStack.setTypeProperties("table-column");
06719     if ( column.hasAttributeNS( KoXmlNS::table, "style-name" ) )
06720     {
06721         QString str = column.attributeNS( KoXmlNS::table, "style-name", QString::null );
06722         const QDomElement *style = oasisStyles.findStyle( str, "table-column" );
06723         if (style)
06724     {
06725         styleStack.push( *style );
06726     /*  FIX_BEFORE_COMMIT
06727         layout.loadOasisStyleProperties( styleStack , oasisStyles );
06728         styleStack.pop();*/
06729     }
06730 
06731         kdDebug()<<" style column:"<<style<<"style name : "<<str<<endl;
06732     }
06733 
06734     if ( styleStack.hasAttributeNS( KoXmlNS::style, "column-width" ) )
06735     {
06736         width = KoUnit::parseValue( styleStack.attributeNS( KoXmlNS::style, "column-width" ) , -1 );
06737         kdDebug()<<" style:column-width : width :"<<width<<endl;
06738     }
06739 
06740     bool insertPageBreak = false;
06741     if ( styleStack.hasAttributeNS( KoXmlNS::fo, "break-before" ) )
06742     {
06743         QString str = styleStack.attributeNS( KoXmlNS::fo, "break-before" );
06744         if ( str == "page" )
06745         {
06746             insertPageBreak = true;
06747         }
06748         else
06749             kdDebug()<<" str :"<<str<<endl;
06750     }
06751 
06752 
06753   //  if ( number>30 )
06754   //      number = 30; //TODO fixme !
06755 
06756     for ( int i = 0; i < number; ++i )
06757     {
06758         kdDebug()<<" insert new column: pos :"<<indexCol<<" width :"<<width<<" hidden ? "<<collapsed<<endl;
06759         ColumnFormat * col = new ColumnFormat( this, indexCol );
06760         col->copy( layout );
06761         if ( width != -1 ) //safe
06762             col->setWidth( (int) width );
06763 
06764         // if ( insertPageBreak )
06765         //   col->setPageBreak( true )
06766 
06767         if ( collapsed )
06768             col->setHide( true );
06769 
06770         insertColumnFormat( col );
06771         ++indexCol;
06772     }
06773     kdDebug()<<" after index column !!!!!!!!!!!!!!!!!! :"<<indexCol<<endl;
06774     return true;
06775 }
06776 
06777 
06778 bool Sheet::loadRowFormat( const QDomElement& row, int &rowIndex, KoOasisLoadingContext& oasisContext,  QDict<Style>& styleMap )
06779 {
06780 //    kdDebug()<<"Sheet::loadRowFormat( const QDomElement& row, int &rowIndex,const KoOasisStyles& oasisStyles, bool isLast )***********\n";
06781     double height = -1.0;
06782     Format layout( this , doc()->styleManager()->defaultStyle() );
06783     KoStyleStack styleStack;
06784     styleStack.setTypeProperties( "table-row" );
06785     int backupRow = rowIndex;
06786 
06787     if ( row.hasAttributeNS( KoXmlNS::table, "style-name" ) )
06788     {
06789         QString str = row.attributeNS( KoXmlNS::table, "style-name", QString::null );
06790         const QDomElement *style = oasisContext.oasisStyles().findStyle( str, "table-row" );
06791       if ( style )
06792         styleStack.push( *style );
06793   //      kdDebug()<<" style column:"<<style<<"style name : "<<str<<endl;
06794     }
06795 
06796     if ( row.hasAttributeNS( KoXmlNS::table,"default-cell-style-name" ) )
06797     {
06798         QString str = row.attributeNS( KoXmlNS::table, "default-cell-style-name", QString::null );
06799 
06800         //TODO - Code to look up this style name in the style map and store it in a map somewhere between
06801         //row indicies and Style instances for use when rendering cells later on.
06802         //      defaultRowCellStyle = styleMap[str];
06803     }
06804 
06805     layout.loadOasisStyleProperties( styleStack, oasisContext.oasisStyles() );
06806     styleStack.setTypeProperties( "table-row" );
06807     if ( styleStack.hasAttributeNS( KoXmlNS::style, "row-height" ) )
06808     {
06809         height = KoUnit::parseValue( styleStack.attributeNS( KoXmlNS::style, "row-height" ) , -1 );
06810     //    kdDebug()<<" properties style:row-height : height :"<<height<<endl;
06811     }
06812 
06813     int number = 1;
06814     if ( row.hasAttributeNS( KoXmlNS::table, "number-rows-repeated" ) )
06815     {
06816         bool ok = true;
06817         int n = row.attributeNS( KoXmlNS::table, "number-rows-repeated", QString::null ).toInt( &ok );
06818         if ( ok )
06819             // Some spreadsheet programs may support more rows than KSpread so
06820             // limit the number of repeated rows.
06821             // FIXME POSSIBLE DATA LOSS!
06822             number = QMIN( n, KS_rowMax - rowIndex + 1 );
06823     }
06824     bool collapse = false;
06825     if ( row.hasAttributeNS( KoXmlNS::table, "visibility" ) )
06826     {
06827         QString visible = row.attributeNS( KoXmlNS::table, "visibility", QString::null );
06828     //    kdDebug()<<" row.attribute( table:visibility ) "<<visible<<endl;
06829         if ( visible == "collapse" )
06830             collapse=true;
06831         else
06832             kdDebug()<<" visible row not implemented/supported : "<<visible<<endl;
06833 
06834     }
06835 
06836     bool insertPageBreak = false;
06837     if ( styleStack.hasAttributeNS( KoXmlNS::fo, "break-before" ) )
06838     {
06839         QString str = styleStack.attributeNS( KoXmlNS::fo, "break-before" );
06840         if ( str == "page" )
06841         {
06842             insertPageBreak = true;
06843         }
06844       //  else
06845       //      kdDebug()<<" str :"<<str<<endl;
06846     }
06847 
06848     //number == number of row to be copy. But we must copy cell too.
06849     for ( int i = 0; i < number; ++i )
06850     {
06851        // kdDebug()<<" create non defaultrow format :"<<rowIndex<<" repeate : "<<number<<" height :"<<height<<endl;
06852         RowFormat * rowL = nonDefaultRowFormat( rowIndex );
06853         rowL->copy( layout );
06854         if ( height != -1 )
06855         {
06856          //   kdDebug() << "Setting row height to " << height << endl;
06857             rowL->setHeight( (int) height );
06858         }
06859         if ( collapse )
06860             rowL->setHide( true );
06861 
06862         ++rowIndex;
06863     }
06864 
06865     int columnIndex = 0;
06866     QDomNode cellNode = row.firstChild();
06867     int endRow = min(backupRow+number,KS_rowMax);
06868 
06869 
06870     while( !cellNode.isNull() )
06871     {
06872         QDomElement cellElement = cellNode.toElement();
06873         if( !cellElement.isNull() )
06874         {
06875             columnIndex++;
06876             QString localName = cellElement.localName();
06877 
06878         if( ((localName == "table-cell") || (localName == "covered-table-cell")) && cellElement.namespaceURI() == KoXmlNS::table)
06879             {
06880     //  kdDebug() << "Loading cell #" << cellCount << endl;
06881 
06882                 Cell* cell = nonDefaultCell( columnIndex, backupRow );
06883         bool cellHasStyle = cellElement.hasAttributeNS( KoXmlNS::table, "style-name" );
06884 
06885         Style* style = 0;
06886 
06887         if ( cellHasStyle )
06888         {
06889             style = styleMap[ cellElement.attributeNS( KoXmlNS::table , "style-name" , QString::null ) ];
06890         }
06891 
06892                 cell->loadOasis( cellElement, oasisContext, style );
06893 
06894 
06895         bool haveStyle = cellHasStyle;
06896                 int cols = 1;
06897 
06898         //Copy this cell across & down if it has repeated rows or columns, but only
06899         //if the cell has some content or a style associated with it.
06900                 if( (number > 1) || cellElement.hasAttributeNS( KoXmlNS::table, "number-columns-repeated" ) )
06901                 {
06902                     bool ok = false;
06903                     int n = cellElement.attributeNS( KoXmlNS::table, "number-columns-repeated", QString::null ).toInt( &ok );
06904 
06905                     if (ok)
06906                       // Some spreadsheet programs may support more rows than
06907                       // KSpread so limit the number of repeated rows.
06908                       // FIXME POSSIBLE DATA LOSS!
06909                       cols = QMIN( n, KS_colMax - columnIndex + 1 );
06910 
06911             if ( !haveStyle && ( cell->isEmpty() && cell->format()->comment( columnIndex, backupRow ).isEmpty() ) )
06912                     {
06913                         //just increment it
06914                         columnIndex +=cols - 1;
06915                     }
06916                     else
06917                     {
06918                             for(int k = cols ; k ; --k )
06919                             {
06920                 if (k != cols)
06921                     columnIndex++;
06922 
06923                 Style* targetStyle = style;
06924 
06925                 for ( int newRow = backupRow; newRow < endRow;++newRow )
06926                 {
06927                                         Cell* target = nonDefaultCell( columnIndex, newRow );
06928 
06929                     if (cell != target)
06930                         target->copyAll( cell );
06931                                 }
06932                 }
06933                     }
06934                 }
06935             }
06936         }
06937         cellNode = cellNode.nextSibling();
06938     }
06939 
06940     return true;
06941 }
06942 
06943 void Sheet::maxRowCols( int & maxCols, int & maxRows )
06944 {
06945   const Cell * cell = firstCell();
06946   while ( cell )
06947   {
06948     if ( cell->column() > maxCols )
06949       maxCols = cell->column();
06950 
06951     if ( cell->row() > maxRows )
06952       maxRows = cell->row();
06953 
06954     cell = cell->nextCell();
06955   }
06956 
06957   const RowFormat * row = firstRow();
06958   while ( row )
06959   {
06960     if ( row->row() > maxRows )
06961       maxRows = row->row();
06962 
06963     row = row->next();
06964   }
06965   const ColumnFormat* col = firstCol();
06966   while ( col )
06967   {
06968     if ( col->column() > maxCols )
06969       maxCols = col->column();
06970 
06971     col = col->next();
06972   }
06973 }
06974 
06975 
06976 void Sheet::saveOasisHeaderFooter( KoXmlWriter &xmlWriter ) const
06977 {
06978     QString headerLeft = print()->headLeft();
06979     QString headerCenter= print()->headMid();
06980     QString headerRight = print()->headRight();
06981 
06982     QString footerLeft = print()->footLeft();
06983     QString footerCenter= print()->footMid();
06984     QString footerRight = print()->footRight();
06985 
06986     xmlWriter.startElement( "style:header");
06987     if ( ( !headerLeft.isEmpty() )
06988          || ( !headerCenter.isEmpty() )
06989          || ( !headerRight.isEmpty() ) )
06990     {
06991         xmlWriter.startElement( "style:region-left" );
06992         xmlWriter.startElement( "text:p" );
06993         convertPart( headerLeft, xmlWriter );
06994         xmlWriter.endElement();
06995         xmlWriter.endElement();
06996 
06997         xmlWriter.startElement( "style:region-center" );
06998         xmlWriter.startElement( "text:p" );
06999         convertPart( headerCenter, xmlWriter );
07000         xmlWriter.endElement();
07001         xmlWriter.endElement();
07002 
07003         xmlWriter.startElement( "style:region-right" );
07004         xmlWriter.startElement( "text:p" );
07005         convertPart( headerRight, xmlWriter );
07006         xmlWriter.endElement();
07007         xmlWriter.endElement();
07008     }
07009     else
07010     {
07011        xmlWriter.startElement( "text:p" );
07012 
07013        xmlWriter.startElement( "text:sheet-name" );
07014        xmlWriter.addTextNode( "???" );
07015        xmlWriter.endElement();
07016 
07017        xmlWriter.endElement();
07018     }
07019     xmlWriter.endElement();
07020 
07021 
07022     xmlWriter.startElement( "style:footer");
07023     if ( ( !footerLeft.isEmpty() )
07024          || ( !footerCenter.isEmpty() )
07025          || ( !footerRight.isEmpty() ) )
07026     {
07027         xmlWriter.startElement( "style:region-left" );
07028         xmlWriter.startElement( "text:p" );
07029         convertPart( footerLeft, xmlWriter );
07030         xmlWriter.endElement();
07031         xmlWriter.endElement(); //style:region-left
07032 
07033         xmlWriter.startElement( "style:region-center" );
07034         xmlWriter.startElement( "text:p" );
07035         convertPart( footerCenter, xmlWriter );
07036         xmlWriter.endElement();
07037         xmlWriter.endElement();
07038 
07039         xmlWriter.startElement( "style:region-right" );
07040         xmlWriter.startElement( "text:p" );
07041         convertPart( footerRight, xmlWriter );
07042         xmlWriter.endElement();
07043         xmlWriter.endElement();
07044     }
07045     else
07046     {
07047 
07048        xmlWriter.startElement( "text:p" );
07049 
07050        xmlWriter.startElement( "text:sheet-name" );
07051        xmlWriter.addTextNode( "Page " ); // ???
07052 
07053        xmlWriter.startElement( "text:page-number" );
07054        xmlWriter.addTextNode( "1" ); // ???
07055        xmlWriter.endElement();
07056 
07057 
07058        xmlWriter.endElement();
07059 
07060        xmlWriter.endElement();
07061     }
07062     xmlWriter.endElement();
07063 
07064 
07065 }
07066 
07067 void Sheet::addText( const QString & text, KoXmlWriter & writer ) const
07068 {
07069     if ( !text.isEmpty() )
07070         writer.addTextNode( text );
07071 }
07072 
07073 void Sheet::convertPart( const QString & part, KoXmlWriter & xmlWriter ) const
07074 {
07075     QString text;
07076     QString var;
07077 
07078     bool inVar = false;
07079     uint i = 0;
07080     uint l = part.length();
07081     while ( i < l )
07082     {
07083         if ( inVar || part[i] == '<' )
07084         {
07085             inVar = true;
07086             var += part[i];
07087             if ( part[i] == '>' )
07088             {
07089                 inVar = false;
07090                 if ( var == "<page>" )
07091                 {
07092                     addText( text, xmlWriter );
07093                     xmlWriter.startElement( "text:page-number" );
07094                     xmlWriter.addTextNode( "1" );
07095                     xmlWriter.endElement();
07096                 }
07097                 else if ( var == "<pages>" )
07098                 {
07099                     addText( text, xmlWriter );
07100                     xmlWriter.startElement( "text:page-count" );
07101                     xmlWriter.addTextNode( "99" ); //TODO I think that it can be different from 99
07102                     xmlWriter.endElement();
07103                 }
07104                 else if ( var == "<date>" )
07105                 {
07106                     addText( text, xmlWriter );
07107                     //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
07108 #if 0 //FIXME
07109                     QDomElement t = dd.createElement( "text:date" );
07110                     t.setAttribute( "text:date-value", "0-00-00" );
07111                     // todo: "style:data-style-name", "N2"
07112                     t.appendChild( dd.createTextNode( QDate::currentDate().toString() ) );
07113                     parent.appendChild( t );
07114 #endif
07115                 }
07116                 else if ( var == "<time>" )
07117                 {
07118                     addText( text, xmlWriter );
07119 
07120                     xmlWriter.startElement( "text:time" );
07121                     xmlWriter.addTextNode( QTime::currentTime().toString() );
07122                     xmlWriter.endElement();
07123                 }
07124                 else if ( var == "<file>" ) // filepath + name
07125                 {
07126                     addText( text, xmlWriter );
07127                     xmlWriter.startElement( "text:file-name" );
07128                     xmlWriter.addAttribute( "text:display", "full" );
07129                     xmlWriter.addTextNode( "???" );
07130                     xmlWriter.endElement();
07131                 }
07132                 else if ( var == "<name>" ) // filename
07133                 {
07134                     addText( text, xmlWriter );
07135 
07136                     xmlWriter.startElement( "text:title" );
07137                     xmlWriter.addTextNode( "???" );
07138                     xmlWriter.endElement();
07139                 }
07140                 else if ( var == "<author>" )
07141                 {
07142                     Doc* sdoc = d->workbook->doc();
07143                     KoDocumentInfo       * docInfo    = sdoc->documentInfo();
07144                     KoDocumentInfoAuthor * authorPage = static_cast<KoDocumentInfoAuthor*>( docInfo->page( "author" ) );
07145 
07146                     text += authorPage->fullName();
07147 
07148                     addText( text, xmlWriter );
07149                 }
07150                 else if ( var == "<email>" )
07151                 {
07152                     Doc* sdoc = d->workbook->doc();
07153                     KoDocumentInfo       * docInfo    = sdoc->documentInfo();
07154                     KoDocumentInfoAuthor * authorPage = static_cast<KoDocumentInfoAuthor*>( docInfo->page( "author" ) );
07155 
07156                     text += authorPage->email();
07157                     addText( text, xmlWriter );
07158 
07159                 }
07160                 else if ( var == "<org>" )
07161                 {
07162                     Doc* sdoc = d->workbook->doc();
07163                     KoDocumentInfo       * docInfo    = sdoc->documentInfo();
07164                     KoDocumentInfoAuthor * authorPage = static_cast<KoDocumentInfoAuthor*>( docInfo->page( "author" ) );
07165 
07166                     text += authorPage->company();
07167                     addText( text, xmlWriter );
07168 
07169                 }
07170                 else if ( var == "<sheet>" )
07171                 {
07172                     addText( text, xmlWriter );
07173 
07174                     xmlWriter.startElement( "text:sheet-name" );
07175                     xmlWriter.addTextNode( "???" );
07176                     xmlWriter.endElement();
07177                 }
07178                 else
07179                 {
07180                     // no known variable:
07181                     text += var;
07182                     addText( text, xmlWriter );
07183                 }
07184 
07185                 text = "";
07186                 var  = "";
07187             }
07188         }
07189         else
07190         {
07191             text += part[i];
07192         }
07193         ++i;
07194     }
07195     if ( !text.isEmpty() || !var.isEmpty() )
07196     {
07197         //we don't have var at the end =>store it
07198         addText( text+var, xmlWriter );
07199     }
07200     kdDebug()<<" text end :"<<text<<" var :"<<var<<endl;
07201 }
07202 
07203 
07204 void Sheet::loadOasisSettings( const KoOasisSettings::NamedMap &settings )
07205 {
07206     // Find the entry in the map that applies to this sheet (by name)
07207     KoOasisSettings::Items items = settings.entry( d->name );
07208     if ( items.isNull() )
07209         return;
07210     d->hideZero = items.parseConfigItemBool( "ShowZeroValues" );
07211     d->showGrid = items.parseConfigItemBool( "ShowGrid" );
07212     d->firstLetterUpper = items.parseConfigItemBool( "FirstLetterUpper" );
07213 
07214     int cursorX = items.parseConfigItemInt( "CursorPositionX" );
07215     int cursorY = items.parseConfigItemInt( "CursorPositionY" );
07216     doc()->loadingInfo()->setCursorPosition( this, QPoint( cursorX, cursorY ) );
07217 
07218     double offsetX = items.parseConfigItemDouble( "xOffset" );
07219     double offsetY = items.parseConfigItemDouble( "yOffset" );
07220     doc()->loadingInfo()->setScrollingOffset( this, KoPoint( offsetX, offsetY ) );
07221 
07222     d->showFormulaIndicator = items.parseConfigItemBool( "ShowFormulaIndicator" );
07223     d->showCommentIndicator = items.parseConfigItemBool( "ShowCommentIndicator" );
07224     d->showPageBorders = items.parseConfigItemBool( "ShowPageBorders" );
07225     d->lcMode = items.parseConfigItemBool( "lcmode" );
07226     d->autoCalc = items.parseConfigItemBool( "autoCalc" );
07227     d->showColumnNumber = items.parseConfigItemBool( "ShowColumnNumber" );
07228 }
07229 
07230 void Sheet::saveOasisSettings( KoXmlWriter &settingsWriter ) const
07231 {
07232     //not into each page into oo spec
07233     settingsWriter.addConfigItem( "ShowZeroValues", d->hideZero );
07234     settingsWriter.addConfigItem( "ShowGrid", d->showGrid );
07235     //not define into oo spec
07236     settingsWriter.addConfigItem( "FirstLetterUpper", d->firstLetterUpper);
07237     settingsWriter.addConfigItem( "ShowFormulaIndicator", d->showFormulaIndicator );
07238     settingsWriter.addConfigItem( "ShowCommentIndicator", d->showCommentIndicator );
07239     settingsWriter.addConfigItem( "ShowPageBorders",d->showPageBorders );
07240     settingsWriter.addConfigItem( "lcmode", d->lcMode );
07241     settingsWriter.addConfigItem( "autoCalc", d->autoCalc );
07242     settingsWriter.addConfigItem( "ShowColumnNumber", d->showColumnNumber );
07243 }
07244 
07245 bool Sheet::saveOasis( KoXmlWriter & xmlWriter, KoGenStyles &mainStyles, GenValidationStyles &valStyle, KoStore *store, KoXmlWriter* /*manifestWriter*/, int &indexObj, int &partIndexObj )
07246 {
07247     int maxCols= 1;
07248     int maxRows= 1;
07249     xmlWriter.startElement( "table:table" );
07250     xmlWriter.addAttribute( "table:name", d->name );
07251     xmlWriter.addAttribute( "table:style-name", saveOasisSheetStyleName(mainStyles )  );
07252     if ( !d->password.isEmpty() )
07253     {
07254         xmlWriter.addAttribute("table:protected", "true" );
07255         QCString str = KCodecs::base64Encode( d->password );
07256         xmlWriter.addAttribute("table:protection-key", QString( str.data() ) );/* FIXME !!!!*/
07257     }
07258     QRect _printRange = d->print->printRange();
07259     if ( _printRange != ( QRect( QPoint( 1, 1 ), QPoint( KS_colMax, KS_rowMax ) ) ) )
07260     {
07261         QString range= convertRangeToRef( d->name, _printRange );
07262         kdDebug()<<" range : "<<range<<endl;
07263         xmlWriter.addAttribute( "table:print-ranges", range );
07264     }
07265 
07266     saveOasisObjects( store, xmlWriter, mainStyles, indexObj, partIndexObj );
07267     maxRowCols( maxCols, maxRows );
07268     saveOasisColRowCell( xmlWriter, mainStyles, maxCols, maxRows, valStyle );
07269     xmlWriter.endElement();
07270     return true;
07271 }
07272 
07273 void Sheet::saveOasisPrintStyleLayout( KoGenStyle &style ) const
07274 {
07275     QString printParameter;
07276     if ( d->print->printGrid() )
07277         printParameter="grid ";
07278     if ( d->print->printObjects() )
07279       printParameter+="objects ";
07280     if ( d->print->printCharts() )
07281       printParameter+="charts ";
07282     if ( d->showFormula )
07283         printParameter+="formulas ";
07284     if ( !printParameter.isEmpty() )
07285     {
07286         printParameter+="drawings zero-values"; //default print style attributes in OO
07287         style.addProperty( "style:print", printParameter );
07288     }
07289 }
07290 
07291 QString Sheet::saveOasisSheetStyleName( KoGenStyles &mainStyles )
07292 {
07293     KoGenStyle pageStyle( Doc::STYLE_PAGE, "table"/*FIXME I don't know if name is sheet*/ );
07294 
07295     KoGenStyle pageMaster( Doc::STYLE_PAGEMASTER );
07296     pageMaster.addAttribute( "style:page-layout-name", d->print->saveOasisSheetStyleLayout( mainStyles ) );
07297 
07298     QBuffer buffer;
07299     buffer.open( IO_WriteOnly );
07300     KoXmlWriter elementWriter( &buffer );  // TODO pass indentation level
07301     saveOasisHeaderFooter(elementWriter);
07302 
07303     QString elementContents = QString::fromUtf8( buffer.buffer(), buffer.buffer().size() );
07304     pageMaster.addChildElement( "headerfooter", elementContents );
07305     pageStyle.addAttribute( "style:master-page-name", mainStyles.lookup( pageMaster, "Standard" ) );
07306 
07307     pageStyle.addProperty( "table:display", !d->hide );
07308     return mainStyles.lookup( pageStyle, "ta" );
07309 }
07310 
07311 
07312 void Sheet::saveOasisColRowCell( KoXmlWriter& xmlWriter, KoGenStyles &mainStyles, int maxCols, int maxRows, GenValidationStyles &valStyle )
07313 {
07314     int i = 1;
07315     while ( i <= maxCols )
07316     {
07317         ColumnFormat* column = columnFormat( i );
07318         KoGenStyle currentColumnStyle( Doc::STYLE_COLUMN, "table-column" );
07319         currentColumnStyle.addPropertyPt( "style:column-width", column->dblWidth() );/*FIXME pt and not mm */
07320         currentColumnStyle.addProperty( "fo:break-before", "auto" );/*FIXME auto or not ?*/
07321 
07322         //style default layout for column
07323         KoGenStyle currentDefaultCellStyle; // the type is determined in saveOasisCellStyle
07324         QString currentDefaultCellStyleName = column->saveOasisCellStyle( currentDefaultCellStyle, mainStyles );
07325 
07326 
07327         bool hide = column->isHide();
07328         int j = i + 1;
07329         int repeated = 1;
07330         while ( j <= maxCols )
07331         {
07332             ColumnFormat* nextColumn = columnFormat( j );
07333             KoGenStyle nextColumnStyle( Doc::STYLE_COLUMN, "table-column" );
07334             nextColumnStyle.addPropertyPt( "style:column-width", nextColumn->dblWidth() );/*FIXME pt and not mm */
07335             nextColumnStyle.addProperty( "fo:break-before", "auto" );/*FIXME auto or not ?*/
07336 
07337             KoGenStyle nextDefaultCellStyle; // the type is determined in saveOasisCellStyle
07338             QString nextDefaultCellStyleName = nextColumn->saveOasisCellStyle( nextDefaultCellStyle, mainStyles );
07339 
07340             //FIXME all the time repeate == 2
07341             if ( ( nextColumnStyle == currentColumnStyle ) && ( hide == nextColumn->isHide() ) && ( nextDefaultCellStyleName == currentDefaultCellStyleName ) )
07342                 ++repeated;
07343             else
07344                 break;
07345             ++j;
07346         }
07347         xmlWriter.startElement( "table:table-column" );
07348         xmlWriter.addAttribute( "table:style-name", mainStyles.lookup( currentColumnStyle, "co" ) );
07349         //FIXME doesn't create format if it's default format
07350 
07351         // skip 'table:default-cell-style-name' attribute for the default style
07352         if ( !currentDefaultCellStyle.isDefaultStyle() )
07353             xmlWriter.addAttribute( "table:default-cell-style-name", currentDefaultCellStyleName );
07354 
07355         if ( hide )
07356             xmlWriter.addAttribute( "table:visibility", "collapse" );
07357 
07358         if ( repeated > 1 )
07359             xmlWriter.addAttribute( "table:number-columns-repeated", repeated  );
07360         xmlWriter.endElement();
07361         i += repeated;
07362     }
07363 
07364     for ( i = 1; i <= maxRows; ++i )
07365     {
07366         const RowFormat* row = rowFormat( i );
07367         KoGenStyle currentRowStyle( Doc::STYLE_ROW, "table-row" );
07368         currentRowStyle.addPropertyPt( "style:row-height", row->dblHeight());/*FIXME pt and not mm */
07369         currentRowStyle.addProperty( "fo:break-before", "auto" );/*FIXME auto or not ?*/
07370 
07371         xmlWriter.startElement( "table:table-row" );
07372         xmlWriter.addAttribute( "table:style-name", mainStyles.lookup( currentRowStyle, "ro" ) );
07373         int repeated = 1;
07374         if ( !rowAsCell( i, maxCols ) )
07375         {
07376             bool hide = row->isHide();
07377             int j = i + 1;
07378             while ( j <= maxRows )
07379             {
07380                 const RowFormat *nextRow = rowFormat( j );
07381                 KoGenStyle nextRowStyle( Doc::STYLE_ROW, "table-row" );
07382                 nextRowStyle.addPropertyPt( "style:row-height", nextRow->dblHeight() );/*FIXME pt and not mm */
07383                 nextRowStyle.addProperty( "fo:break-before", "auto" );/*FIXME auto or not ?*/
07384 
07385                 //FIXME all the time repeate == 2
07386                 if ( ( nextRowStyle==currentRowStyle ) && ( hide == nextRow->isHide() ) &&!rowAsCell( j, maxCols ) )
07387                     ++repeated;
07388                 else
07389                     break;
07390                 ++j;
07391             }
07392             i += repeated-1; /*it's double incremented into loop for*/
07393             if ( row->isHide() )
07394                 xmlWriter.addAttribute( "table:visibility", "collapse" );
07395             if (  repeated > 1 )
07396                 xmlWriter.addAttribute( "table:number-rows-repeated", repeated  );
07397         }
07398         else
07399         {
07400             if ( row->isHide() )
07401                 xmlWriter.addAttribute( "table:visibility", "collapse" );
07402             saveOasisCells( xmlWriter, mainStyles, i, maxCols, valStyle );
07403         }
07404         xmlWriter.endElement();
07405     }
07406 }
07407 
07408 bool Sheet::rowAsCell( int row, int maxCols )
07409 {
07410     int i = 1;
07411     while ( i <= maxCols )
07412     {
07413         Cell* cell = cellAt( i, row );
07414         if ( !cell->isDefault() )
07415             return true;
07416         i++;
07417     }
07418     return false;
07419 }
07420 
07421 void Sheet::saveOasisCells( KoXmlWriter& xmlWriter, KoGenStyles &mainStyles, int row, int maxCols, GenValidationStyles &valStyle )
07422 {
07423     int i = 1;
07424     while ( i <= maxCols )
07425     {
07426         int repeated = 1;
07427         Cell* cell = cellAt( i, row );
07428         cell->saveOasis( xmlWriter, mainStyles, row, i, maxCols, repeated, valStyle );
07429         i += repeated;
07430     }
07431 }
07432 
07433 bool Sheet::loadXML( const QDomElement& sheet )
07434 {
07435     bool ok = false;
07436     if ( !doc()->loadingInfo() ||  !doc()->loadingInfo()->loadTemplate() )
07437     {
07438         d->name = sheet.attribute( "name" );
07439         if ( d->name.isEmpty() )
07440         {
07441             doc()->setErrorMessage( i18n("Invalid document. Sheet name is empty.") );
07442             return false;
07443         }
07444     }
07445 
07446     bool detectDirection = true;
07447     d->layoutDirection = LeftToRight;
07448     QString layoutDir = sheet.attribute( "layoutDirection" );
07449     if( !layoutDir.isEmpty() )
07450     {
07451         if( layoutDir == "rtl" )
07452         {
07453            detectDirection = false;
07454            d->layoutDirection = RightToLeft;
07455         }
07456         else if( layoutDir == "ltr" )
07457         {
07458            detectDirection = false;
07459            d->layoutDirection = LeftToRight;
07460         }
07461         else
07462             kdDebug()<<" Direction not implemented : "<<layoutDir<<endl;
07463     }
07464     if( detectDirection )
07465        checkContentDirection( d->name );
07466 
07467     /* older versions of KSpread allowed all sorts of characters that
07468        the parser won't actually understand.  Replace these with '_'
07469        Also, the initial character cannot be a space.
07470     */
07471     if (d->name[0] == ' ')
07472     {
07473       d->name.remove(0,1);
07474     }
07475     for (unsigned int i=0; i < d->name.length(); i++)
07476     {
07477       if ( !(d->name[i].isLetterOrNumber() ||
07478              d->name[i] == ' ' || d->name[i] == '.' ||
07479              d->name[i] == '_'))
07480         {
07481         d->name[i] = '_';
07482       }
07483     }
07484 
07485     /* make sure there are no name collisions with the altered name */
07486     QString testName;
07487     QString baseName;
07488     int nameSuffix = 0;
07489 
07490     testName = d->name;
07491     baseName = d->name;
07492 
07493     /* so we don't panic over finding ourself in the follwing test*/
07494     d->name = "";
07495     while (workbook()->findSheet(testName) != NULL)
07496     {
07497       nameSuffix++;
07498       testName = baseName + '_' + QString::number(nameSuffix);
07499     }
07500     d->name = testName;
07501 
07502     kdDebug(36001)<<"Sheet::loadXML: table name="<<d->name<<endl;
07503     setName(d->name.utf8());
07504     (dynamic_cast<SheetIface*>(dcopObject()))->sheetNameHasChanged();
07505 
07506     if( sheet.hasAttribute( "grid" ) )
07507     {
07508         d->showGrid = (int)sheet.attribute("grid").toInt( &ok );
07509         // we just ignore 'ok' - if it didn't work, go on
07510     }
07511     if( sheet.hasAttribute( "printGrid" ) )
07512     {
07513         d->print->setPrintGrid( (bool)sheet.attribute("printGrid").toInt( &ok ) );
07514         // we just ignore 'ok' - if it didn't work, go on
07515     }
07516     if( sheet.hasAttribute( "printCommentIndicator" ) )
07517     {
07518         d->print->setPrintCommentIndicator( (bool)sheet.attribute("printCommentIndicator").toInt( &ok ) );
07519         // we just ignore 'ok' - if it didn't work, go on
07520     }
07521     if( sheet.hasAttribute( "printFormulaIndicator" ) )
07522     {
07523         d->print->setPrintFormulaIndicator( (bool)sheet.attribute("printFormulaIndicator").toInt( &ok ) );
07524         // we just ignore 'ok' - if it didn't work, go on
07525     }
07526     if( sheet.hasAttribute( "hide" ) )
07527     {
07528         d->hide = (bool)sheet.attribute("hide").toInt( &ok );
07529         // we just ignore 'ok' - if it didn't work, go on
07530     }
07531     if( sheet.hasAttribute( "showFormula" ) )
07532     {
07533         d->showFormula = (bool)sheet.attribute("showFormula").toInt( &ok );
07534         // we just ignore 'ok' - if it didn't work, go on
07535     }
07536     //Compatibility with KSpread 1.1.x
07537     if( sheet.hasAttribute( "formular" ) )
07538     {
07539         d->showFormula = (bool)sheet.attribute("formular").toInt( &ok );
07540         // we just ignore 'ok' - if it didn't work, go on
07541     }
07542     if( sheet.hasAttribute( "showFormulaIndicator" ) )
07543     {
07544         d->showFormulaIndicator = (bool)sheet.attribute("showFormulaIndicator").toInt( &ok );
07545         // we just ignore 'ok' - if it didn't work, go on
07546     }
07547     if( sheet.hasAttribute( "showCommentIndicator" ) )
07548     {
07549         d->showCommentIndicator = (bool)sheet.attribute("showCommentIndicator").toInt( &ok );
07550         // we just ignore 'ok' - if it didn't work, go on
07551     }
07552     if( sheet.hasAttribute( "borders" ) )
07553     {
07554         d->showPageBorders = (bool)sheet.attribute("borders").toInt( &ok );
07555         // we just ignore 'ok' - if it didn't work, go on
07556     }
07557     if( sheet.hasAttribute( "lcmode" ) )
07558     {
07559         d->lcMode = (bool)sheet.attribute("lcmode").toInt( &ok );
07560         // we just ignore 'ok' - if it didn't work, go on
07561     }
07562     if ( sheet.hasAttribute( "autoCalc" ) )
07563     {
07564         d->autoCalc = ( bool )sheet.attribute( "autoCalc" ).toInt( &ok );
07565         // we just ignore 'ok' - if it didn't work, go on
07566     }
07567     if( sheet.hasAttribute( "columnnumber" ) )
07568     {
07569         d->showColumnNumber = (bool)sheet.attribute("columnnumber").toInt( &ok );
07570         // we just ignore 'ok' - if it didn't work, go on
07571     }
07572     if( sheet.hasAttribute( "hidezero" ) )
07573     {
07574         d->hideZero = (bool)sheet.attribute("hidezero").toInt( &ok );
07575         // we just ignore 'ok' - if it didn't work, go on
07576     }
07577     if( sheet.hasAttribute( "firstletterupper" ) )
07578     {
07579         d->firstLetterUpper = (bool)sheet.attribute("firstletterupper").toInt( &ok );
07580         // we just ignore 'ok' - if it didn't work, go on
07581     }
07582 
07583     // Load the paper layout
07584     QDomElement paper = sheet.namedItem( "paper" ).toElement();
07585     if ( !paper.isNull() )
07586     {
07587       QString format = paper.attribute( "format" );
07588       QString orientation = paper.attribute( "orientation" );
07589 
07590       // <borders>
07591       QDomElement borders = paper.namedItem( "borders" ).toElement();
07592       if ( !borders.isNull() )
07593       {
07594         float left = borders.attribute( "left" ).toFloat();
07595         float right = borders.attribute( "right" ).toFloat();
07596         float top = borders.attribute( "top" ).toFloat();
07597         float bottom = borders.attribute( "bottom" ).toFloat();
07598         d->print->setPaperLayout( left, top, right, bottom, format, orientation );
07599       }
07600       QString hleft, hright, hcenter;
07601       QString fleft, fright, fcenter;
07602       // <head>
07603       QDomElement head = paper.namedItem( "head" ).toElement();
07604       if ( !head.isNull() )
07605       {
07606         QDomElement left = head.namedItem( "left" ).toElement();
07607         if ( !left.isNull() )
07608           hleft = left.text();
07609         QDomElement center = head.namedItem( "center" ).toElement();
07610         if ( !center.isNull() )
07611         hcenter = center.text();
07612         QDomElement right = head.namedItem( "right" ).toElement();
07613         if ( !right.isNull() )
07614           hright = right.text();
07615       }
07616       // <foot>
07617       QDomElement foot = paper.namedItem( "foot" ).toElement();
07618       if ( !foot.isNull() )
07619       {
07620         QDomElement left = foot.namedItem( "left" ).toElement();
07621         if ( !left.isNull() )
07622           fleft = left.text();
07623         QDomElement center = foot.namedItem( "center" ).toElement();
07624         if ( !center.isNull() )
07625           fcenter = center.text();
07626         QDomElement right = foot.namedItem( "right" ).toElement();
07627         if ( !right.isNull() )
07628           fright = right.text();
07629       }
07630       d->print->setHeadFootLine( hleft, hcenter, hright, fleft, fcenter, fright);
07631     }
07632 
07633       // load print range
07634       QDomElement printrange = sheet.namedItem( "printrange-rect" ).toElement();
07635       if ( !printrange.isNull() )
07636       {
07637         int left = printrange.attribute( "left-rect" ).toInt();
07638         int right = printrange.attribute( "right-rect" ).toInt();
07639         int bottom = printrange.attribute( "bottom-rect" ).toInt();
07640         int top = printrange.attribute( "top-rect" ).toInt();
07641         if ( left == 0 ) //whole row(s) selected
07642         {
07643           left = 1;
07644           right = KS_colMax;
07645         }
07646         if ( top == 0 ) //whole column(s) selected
07647         {
07648           top = 1;
07649           bottom = KS_rowMax;
07650         }
07651         d->print->setPrintRange( QRect( QPoint( left, top ), QPoint( right, bottom ) ) );
07652       }
07653 
07654       // load print zoom
07655       if( sheet.hasAttribute( "printZoom" ) )
07656       {
07657         double zoom = sheet.attribute( "printZoom" ).toDouble( &ok );
07658         if ( ok )
07659         {
07660           d->print->setZoom( zoom );
07661         }
07662       }
07663 
07664       // load page limits
07665       if( sheet.hasAttribute( "printPageLimitX" ) )
07666       {
07667         int pageLimit = sheet.attribute( "printPageLimitX" ).toInt( &ok );
07668         if ( ok )
07669         {
07670           d->print->setPageLimitX( pageLimit );
07671         }
07672       }
07673 
07674       // load page limits
07675       if( sheet.hasAttribute( "printPageLimitY" ) )
07676       {
07677         int pageLimit = sheet.attribute( "printPageLimitY" ).toInt( &ok );
07678         if ( ok )
07679         {
07680           d->print->setPageLimitY( pageLimit );
07681         }
07682       }
07683 
07684     // Load the cells
07685     QDomNode n = sheet.firstChild();
07686     while( !n.isNull() )
07687     {
07688         QDomElement e = n.toElement();
07689         if ( !e.isNull() )
07690         {
07691             QString tagName=e.tagName();
07692             if ( tagName == "cell" )
07693         {
07694             Cell *cell = new Cell( this, 0, 0 );
07695             if ( cell->load( e, 0, 0 ) )
07696                 insertCell( cell );
07697             else
07698                 delete cell; // Allow error handling: just skip invalid cells
07699         }
07700             else if ( tagName == "row" )
07701         {
07702             RowFormat *rl = new RowFormat( this, 0 );
07703             if ( rl->load( e ) )
07704                 insertRowFormat( rl );
07705             else
07706                 delete rl;
07707         }
07708             else if ( tagName == "column" )
07709         {
07710             ColumnFormat *cl = new ColumnFormat( this, 0 );
07711             if ( cl->load( e ) )
07712                 insertColumnFormat( cl );
07713             else
07714                 delete cl;
07715         }
07716             else if ( tagName == "object" )
07717         {
07718             EmbeddedKOfficeObject *ch = new EmbeddedKOfficeObject( doc(), this );
07719             if ( ch->load( e ) )
07720                 insertObject( ch );
07721             else
07722             {
07723                 ch->embeddedObject()->setDeleted(true);
07724                 delete ch;
07725             }
07726         }
07727             else if ( tagName == "chart" )
07728         {
07729           EmbeddedChart *ch = new EmbeddedChart( doc(), this );
07730           if ( ch->load( e ) )
07731                 insertObject( ch );
07732           else
07733           {
07734             ch->embeddedObject()->setDeleted(true);
07735             delete ch;
07736           }
07737         }
07738         }
07739 
07740         n = n.nextSibling();
07741     }
07742 
07743 
07744     // load print repeat columns
07745     QDomElement printrepeatcolumns = sheet.namedItem( "printrepeatcolumns" ).toElement();
07746     if ( !printrepeatcolumns.isNull() )
07747     {
07748         int left = printrepeatcolumns.attribute( "left" ).toInt();
07749         int right = printrepeatcolumns.attribute( "right" ).toInt();
07750         d->print->setPrintRepeatColumns( qMakePair( left, right ) );
07751     }
07752 
07753     // load print repeat rows
07754     QDomElement printrepeatrows = sheet.namedItem( "printrepeatrows" ).toElement();
07755     if ( !printrepeatrows.isNull() )
07756     {
07757         int top = printrepeatrows.attribute( "top" ).toInt();
07758         int bottom = printrepeatrows.attribute( "bottom" ).toInt();
07759         d->print->setPrintRepeatRows( qMakePair( top, bottom ) );
07760     }
07761 
07762     if( !sheet.hasAttribute( "borders1.2" ) )
07763     {
07764       convertObscuringBorders();
07765     }
07766 
07767     if ( sheet.hasAttribute( "protected" ) )
07768     {
07769       QString passwd = sheet.attribute( "protected" );
07770 
07771       if ( passwd.length() > 0 )
07772       {
07773         QCString str( passwd.latin1() );
07774         d->password = KCodecs::base64Decode( str );
07775       }
07776       else
07777         d->password = QCString( "" );
07778     }
07779 
07780     return true;
07781 }
07782 
07783 
07784 bool Sheet::loadChildren( KoStore* _store )
07785 {
07786     QPtrListIterator<EmbeddedObject> it( doc()->embeddedObjects() );
07787     for( ; it.current(); ++it )
07788     {
07789         if ( it.current()->sheet() == this && ( it.current()->getType() == OBJECT_KOFFICE_PART || it.current()->getType() == OBJECT_CHART ) )
07790         {
07791             kdDebug() << "KSpreadSheet::loadChildren" << endl;
07792             if ( !dynamic_cast<EmbeddedKOfficeObject*>( it.current() )->embeddedObject()->loadDocument( _store ) )
07793                 return false;
07794         }
07795     }
07796 
07797     return true;
07798 }
07799 
07800 
07801 void Sheet::setShowPageBorders( bool b )
07802 {
07803     if ( b == d->showPageBorders )
07804         return;
07805 
07806     d->showPageBorders = b;
07807     emit sig_updateView( this );
07808 }
07809 
07810 void Sheet::addCellBinding( CellBinding *_bind )
07811 {
07812   d->cellBindings.append( _bind );
07813 
07814   doc()->setModified( true );
07815 }
07816 
07817 void Sheet::removeCellBinding( CellBinding *_bind )
07818 {
07819   d->cellBindings.removeRef( _bind );
07820 
07821   doc()->setModified( true );
07822 }
07823 
07824 Sheet* Sheet::findSheet( const QString & _name )
07825 {
07826   if ( !workbook() )
07827     return 0L;
07828 
07829   return workbook()->findSheet( _name );
07830 }
07831 
07832 // ###### Torben: Use this one instead of d->cells.insert()
07833 void Sheet::insertCell( Cell *_cell )
07834 {
07835 
07836   d->cells.insert( _cell, _cell->column(), _cell->row() );
07837 
07838   // Adjust the scrollbar range, if the max. dimension has changed.
07839   if ( d->scrollBarUpdates )
07840   {
07841     checkRangeHBorder ( _cell->column() );
07842     checkRangeVBorder ( _cell->row() );
07843   }
07844 }
07845 
07846 void Sheet::insertColumnFormat( ColumnFormat *l )
07847 {
07848   d->columns.insertElement( l, l->column() );
07849 }
07850 
07851 void Sheet::insertRowFormat( RowFormat *l )
07852 {
07853   d->rows.insertElement( l, l->row() );
07854 }
07855 
07856 void Sheet::update()
07857 {
07858   Cell* c = d->cells.firstCell();
07859   for( ;c; c = c->nextCell() )
07860   {
07861     updateCell(c, c->column(), c->row());
07862   }
07863 }
07864 
07865 void Sheet::updateCellArea(const Region& cellArea)
07866 {
07867   if ( doc()->isLoading() || doc()->delayCalculation() || (!getAutoCalc()))
07868     return;
07869 
07870   setRegionPaintDirty( cellArea );
07871 }
07872 
07873 void Sheet::updateCell( Cell */*cell*/, int _column, int _row )
07874 {
07875   QRect cellArea(QPoint(_column, _row), QPoint(_column, _row));
07876 
07877   updateCellArea(cellArea);
07878 }
07879 
07880 void Sheet::emit_updateRow( RowFormat *_format, int _row, bool repaint )
07881 {
07882     if ( doc()->isLoading() )
07883         return;
07884 
07885     Cell* c = d->cells.firstCell();
07886     for( ;c; c = c->nextCell() )
07887       if ( c->row() == _row )
07888           c->setLayoutDirtyFlag( true );
07889 
07890     if ( repaint )
07891     {
07892         //All the cells in this row, or below this row will need to be repainted
07893         //So add that region of the sheet to the paint dirty list.
07894         setRegionPaintDirty( QRect( 0 , _row , KS_colMax , KS_rowMax) );
07895 
07896       emit sig_updateVBorder( this );
07897       emit sig_updateView( this );
07898     }
07899     emit sig_maxRow(maxRow());
07900     _format->clearDisplayDirtyFlag();
07901 }
07902 
07903 void Sheet::emit_updateColumn( ColumnFormat *_format, int _column )
07904 {
07905     if ( doc()->isLoading() )
07906         return;
07907 
07908     Cell* c = d->cells.firstCell();
07909     for( ;c; c = c->nextCell() )
07910         if ( c->column() == _column )
07911             c->setLayoutDirtyFlag( true );
07912 
07913     //All the cells in this column or to the right of it will need to be repainted if the column
07914     //has been resized or hidden, so add that region of the sheet to the paint dirty list.
07915     setRegionPaintDirty( QRect( _column , 0 , KS_colMax , KS_rowMax) );
07916 
07917     emit sig_updateHBorder( this );
07918     emit sig_updateView( this );
07919     emit sig_maxColumn( maxColumn() );
07920 
07921 
07922 
07923     _format->clearDisplayDirtyFlag();
07924 }
07925 
07926 bool Sheet::insertChart( const KoRect& _rect, KoDocumentEntry& _e, const QRect& _data )
07927 {
07928     kdDebug(36001) << "Creating document" << endl;
07929     KoDocument* dd = _e.createDoc();
07930     kdDebug(36001) << "Created" << endl;
07931     if ( !dd )
07932         // Error message is already displayed, so just return
07933         return false;
07934 
07935     kdDebug(36001) << "NOW FETCHING INTERFACE" << endl;
07936 
07937     if ( !dd->initDoc(KoDocument::InitDocEmbedded) )
07938         return false;
07939 
07940     EmbeddedChart * ch = new EmbeddedChart( doc(), this, dd, _rect );
07941     ch->setDataArea( _data );
07942     ch->update();
07943     ch->chart()->setCanChangeValue( false  );
07944 
07945     KoChart::WizardExtension * wiz = ch->chart()->wizardExtension();
07946 
07947     Range dataRange;
07948     dataRange.setRange( _data );
07949     dataRange.setSheet( this );
07950 
07951     QString rangeString=dataRange.toString();
07952 
07953     if ( wiz )
07954         wiz->show( rangeString );
07955 
07956     insertObject( ch );
07957 
07958     return true;
07959 }
07960 
07961 bool Sheet::insertChild( const KoRect& _rect, KoDocumentEntry& _e )
07962 {
07963     KoDocument* d = _e.createDoc( doc() );
07964     if ( !d )
07965     {
07966         kdDebug() << "Error inserting child!" << endl;
07967         return false;
07968     }
07969     if ( !d->initDoc(KoDocument::InitDocEmbedded) )
07970         return false;
07971 
07972     EmbeddedKOfficeObject* ch = new EmbeddedKOfficeObject( doc(), this, d, _rect );
07973     insertObject( ch );
07974     return true;
07975 }
07976 
07977 bool Sheet::insertPicture( const KoPoint& point , const KURL& url )
07978 {
07979     KoPicture picture = doc()->pictureCollection()->downloadPicture( url , 0 );
07980 
07981     return insertPicture(point,picture);
07982 }
07983 
07984 bool Sheet::insertPicture( const KoPoint& point ,  KoPicture& picture )
07985 {
07986 
07987     if (picture.isNull())
07988         return false;
07989 
07990     KoPictureKey key = picture.getKey();
07991 
07992     KoRect destinationRect;
07993     destinationRect.setLeft( point.x()  );
07994     destinationRect.setTop( point.y()  );
07995 
07996     //Generate correct pixel size - this is a bit tricky.
07997     //This ensures that when we load the image it appears
07998     //the same size on screen on a 100%-zoom KSpread spreadsheet as it would in an
07999     //image viewer or another spreadsheet program such as OpenOffice.
08000     //
08001     //KoUnit assumes 72DPI, whereas the user's display resolution will probably be
08002     //different (eg. 96*96).  So, we convert the actual size in pixels into inches
08003     //using the screen display resolution and then use KoUnit to convert back into
08004     //the appropriate pixel size KSpread.
08005 
08006     KoSize destinationSize;
08007 
08008     double inchWidth = (double)picture.getOriginalSize().width() / KoGlobal::dpiX();
08009     double inchHeight = (double)picture.getOriginalSize().height() / KoGlobal::dpiY();
08010 
08011     destinationSize.setWidth( KoUnit::fromUserValue(inchWidth,KoUnit::U_INCH) );
08012     destinationSize.setHeight( KoUnit::fromUserValue(inchHeight,KoUnit::U_INCH) );
08013 
08014     destinationRect.setSize( destinationSize);
08015 
08016     EmbeddedPictureObject* object = new EmbeddedPictureObject( this, destinationRect, doc()->pictureCollection(),key);
08017    // ch->setPicture(key);
08018 
08019     insertObject( object );
08020     return true;
08021 }
08022 
08023 bool Sheet::insertPicture( const KoPoint& point, const QPixmap& pixmap  )
08024 {
08025     QByteArray data;
08026     QBuffer buffer(data);
08027 
08028     buffer.open( IO_ReadWrite );
08029     pixmap.save( &buffer , "PNG" );
08030 
08031     //Reset the buffer so that KoPicture reads the whole file from the beginning
08032     //(at the moment the read/write position is at the end)
08033     buffer.reset();
08034 
08035     KoPicture picture;
08036     picture.load( &buffer , "PNG" );
08037 
08038     doc()->pictureCollection()->insertPicture(picture);
08039 
08040     return insertPicture( point , picture );
08041 }
08042 
08043 void Sheet::insertObject( EmbeddedObject *_obj )
08044 {
08045     doc()->insertObject( _obj );
08046     emit sig_updateView( _obj );
08047 }
08048 
08049 void Sheet::changeChildGeometry( EmbeddedKOfficeObject *_child, const KoRect& _rect )
08050 {
08051     _child->setGeometry( _rect );
08052 
08053     emit sig_updateChildGeometry( _child );
08054 }
08055 
08056 bool Sheet::saveChildren( KoStore* _store, const QString &_path )
08057 {
08058     int i = 0;
08059 
08060     QPtrListIterator<EmbeddedObject> it( doc()->embeddedObjects() );
08061     for( ; it.current(); ++it )
08062     {
08063         if ( it.current()->sheet() == this && ( it.current()->getType() == OBJECT_KOFFICE_PART || it.current()->getType() == OBJECT_CHART ) )
08064         {
08065             QString path = QString( "%1/%2" ).arg( _path ).arg( i++ );
08066             if ( !dynamic_cast<EmbeddedKOfficeObject*>( it.current() )->embeddedObject()->document()->saveToStore( _store, path ) )
08067                 return false;
08068         }
08069     }
08070     return true;
08071 }
08072 
08073 bool Sheet::saveOasisObjects( KoStore */*store*/, KoXmlWriter &xmlWriter, KoGenStyles& mainStyles, int & indexObj, int &partIndexObj )
08074 {
08075   //int i = 0;
08076   if ( doc()->embeddedObjects().isEmpty() )
08077     return true;
08078 
08079   bool objectFound = false; // object on this sheet?
08080   EmbeddedObject::KSpreadOasisSaveContext sc( xmlWriter, mainStyles, indexObj, partIndexObj );
08081   QPtrListIterator<EmbeddedObject> it( doc()->embeddedObjects() );
08082   for( ; it.current(); ++it )
08083   {
08084     if ( it.current()->sheet() == this && ( doc()->savingWholeDocument() || it.current()->isSelected() ) )
08085     {
08086       if ( !objectFound )
08087       {
08088         xmlWriter.startElement( "table:shapes" );
08089         objectFound = true;
08090       }
08091       if ( !it.current()->saveOasisObject(sc)  )
08092       {
08093         xmlWriter.endElement();
08094         return false;
08095       }
08096       ++indexObj;
08097     }
08098   }
08099   if ( objectFound )
08100   {
08101     xmlWriter.endElement();
08102   }
08103   return true;
08104 }
08105 
08106 Sheet::~Sheet()
08107 {
08108     //Disable automatic recalculation of dependancies on this sheet to prevent crashes
08109     //in certain situations:
08110     //
08111     //For example, suppose a cell in SheetB depends upon a cell in SheetA.  If the cell in SheetB is emptied
08112     //after SheetA has already been deleted, the program would try to remove dependancies from the cell in SheetA
08113     //causing a crash.
08114     setAutoCalc(false);
08115 
08116     s_mapSheets->remove( d->id );
08117 
08118     //when you remove all sheet (close file)
08119     //you must reinit s_id otherwise there is not
08120     //the good name between map and sheet
08121     if( s_mapSheets->count()==0)
08122       s_id=0L;
08123 
08124     Cell* c = d->cells.firstCell();
08125     for( ; c; c = c->nextCell() )
08126         c->sheetDies();
08127 
08128     d->cells.clear(); // cells destructor needs sheet to still exist
08129 
08130     d->painter->end();
08131     delete d->painter;
08132     delete d->widget;
08133 
08134     delete d->defaultFormat;
08135     delete d->defaultCell;
08136     delete d->defaultRowFormat;
08137     delete d->defaultColumnFormat;
08138     delete d->print;
08139     delete d->dcop;
08140 
08141     delete d->dependencies;
08142 
08143     delete d;
08144 
08145     //this is for debugging a crash
08146     d=0;
08147 }
08148 
08149 void Sheet::checkRangeHBorder ( int _column )
08150 {
08151     if ( d->scrollBarUpdates && _column > d->maxColumn )
08152     {
08153       d->maxColumn = _column;
08154       emit sig_maxColumn( _column );
08155     }
08156 }
08157 
08158 void Sheet::checkRangeVBorder ( int _row )
08159 {
08160     if ( d->scrollBarUpdates && _row > d->maxRow )
08161     {
08162       d->maxRow = _row;
08163       emit sig_maxRow( _row );
08164     }
08165 }
08166 
08167 
08168 void Sheet::enableScrollBarUpdates( bool _enable )
08169 {
08170   d->scrollBarUpdates = _enable;
08171 }
08172 
08173 DCOPObject* Sheet::dcopObject()
08174 {
08175     if ( !d->dcop )
08176         d->dcop = new SheetIface( this );
08177 
08178     return d->dcop;
08179 }
08180 
08181 void Sheet::hideSheet(bool _hide)
08182 {
08183     setHidden(_hide);
08184     if(_hide)
08185         emit sig_SheetHidden(this);
08186     else
08187         emit sig_SheetShown(this);
08188 }
08189 
08190 void Sheet::removeSheet()
08191 {
08192     emit sig_SheetRemoved(this);
08193 }
08194 
08195 bool Sheet::setSheetName( const QString& name, bool init, bool /*makeUndo*/ )
08196 {
08197     if ( workbook()->findSheet( name ) )
08198         return false;
08199 
08200     if ( isProtected() )
08201       return false;
08202 
08203     if ( d->name == name )
08204         return true;
08205 
08206     QString old_name = d->name;
08207     d->name = name;
08208 
08209     if ( init )
08210         return true;
08211 
08212     QPtrListIterator<Sheet> it( workbook()->sheetList() );
08213     for ( ; it.current(); ++it )
08214         it.current()->changeCellTabName( old_name, name );
08215 
08216     doc()->changeAreaSheetName( old_name, name );
08217     emit sig_nameChanged( this, old_name );
08218 
08219     setName(name.utf8());
08220     (dynamic_cast<SheetIface*>(dcopObject()))->sheetNameHasChanged();
08221 
08222     return true;
08223 }
08224 
08225 
08226 void Sheet::updateLocale()
08227 {
08228   doc()->emitBeginOperation(true);
08229   setRegionPaintDirty(QRect(QPoint(1,1), QPoint(KS_colMax, KS_rowMax)));
08230 
08231   Cell* c = d->cells.firstCell();
08232   for( ;c; c = c->nextCell() )
08233   {
08234     QString _text = c->text();
08235     c->setCellText( _text );
08236   }
08237   emit sig_updateView( this );
08238   //  doc()->emitEndOperation();
08239 }
08240 
08241 Cell* Sheet::getFirstCellColumn(int col) const
08242 { return d->cells.getFirstCellColumn(col); }
08243 
08244 Cell* Sheet::getLastCellColumn(int col) const
08245 { return d->cells.getLastCellColumn(col); }
08246 
08247 Cell* Sheet::getFirstCellRow(int row) const
08248 { return d->cells.getFirstCellRow(row); }
08249 
08250 Cell* Sheet::getLastCellRow(int row) const
08251 { return d->cells.getLastCellRow(row); }
08252 
08253 Cell* Sheet::getNextCellUp(int col, int row) const
08254 { return d->cells.getNextCellUp(col, row); }
08255 
08256 Cell* Sheet::getNextCellDown(int col, int row) const
08257 { return d->cells.getNextCellDown(col, row); }
08258 
08259 Cell* Sheet::getNextCellLeft(int col, int row) const
08260 { return d->cells.getNextCellLeft(col, row); }
08261 
08262 Cell* Sheet::getNextCellRight(int col, int row) const
08263 { return d->cells.getNextCellRight(col, row); }
08264 
08265 void Sheet::convertObscuringBorders()
08266 {
08267   /* a word of explanation here:
08268      beginning with KSpread 1.2 (actually, cvs of Mar 28, 2002), border information
08269      is stored differently.  Previously, for a cell obscuring a region, the entire
08270      region's border's data would be stored in the obscuring cell.  This caused
08271      some data loss in certain situations.  After that date, each cell stores
08272      its own border data, and prints it even if it is an obscured cell (as long
08273      as that border isn't across an obscuring border).
08274      Anyway, this function is used when loading a file that was stored with the
08275      old way of borders.  All new files have the sheet attribute "borders1.2" so
08276      if that isn't in the file, all the border data will be converted here.
08277      It's a bit of a hack but I can't think of a better way and it's not *that*
08278      bad of a hack.:-)
08279   */
08280   Cell* c = d->cells.firstCell();
08281   QPen topPen, bottomPen, leftPen, rightPen;
08282   for( ;c; c = c->nextCell() )
08283   {
08284     if (c->extraXCells() > 0 || c->extraYCells() > 0)
08285     {
08286       topPen = c->topBorderPen(c->column(), c->row());
08287       leftPen = c->leftBorderPen(c->column(), c->row());
08288       rightPen = c->rightBorderPen(c->column(), c->row());
08289       bottomPen = c->bottomBorderPen(c->column(), c->row());
08290 
08291       c->format()->setTopBorderStyle(Qt::NoPen);
08292       c->format()->setLeftBorderStyle(Qt::NoPen);
08293       c->format()->setRightBorderStyle(Qt::NoPen);
08294       c->format()->setBottomBorderStyle(Qt::NoPen);
08295 
08296       for (int x = c->column(); x < c->column() + c->extraXCells(); x++)
08297       {
08298         nonDefaultCell( x, c->row() )->setTopBorderPen(topPen);
08299         nonDefaultCell( x, c->row() + c->extraYCells() )->
08300           setBottomBorderPen(bottomPen);
08301       }
08302       for (int y = c->row(); y < c->row() + c->extraYCells(); y++)
08303       {
08304         nonDefaultCell( c->column(), y )->setLeftBorderPen(leftPen);
08305         nonDefaultCell( c->column() + c->extraXCells(), y )->
08306           setRightBorderPen(rightPen);
08307       }
08308     }
08309   }
08310 }
08311 
08312 /**********************
08313  * Printout Functions *
08314  **********************/
08315 
08316 // TODO Stefan: these belong to View, even better Canvas
08317 void Sheet::setRegionPaintDirty( Region const & region )
08318 {
08319   DilationManipulator manipulator;
08320   manipulator.setSheet(this);
08321   manipulator.add(region);
08322   manipulator.execute();
08323   // don't put it in the undo list! ;-)
08324   d->paintDirtyList.add(manipulator);
08325   //kdDebug() << "setRegionPaintDirty "<< static_cast<Region*>(&manipulator)->name(this) << endl;
08326 }
08327 
08328 void Sheet::setRegionPaintDirty( QRect const & range )
08329 {
08330   DilationManipulator manipulator;
08331   manipulator.setSheet(this);
08332   manipulator.add(range);
08333   manipulator.execute();
08334   // don't put it in the undo list! ;-)
08335   d->paintDirtyList.add(manipulator);
08336   //kdDebug() << "setRegionPaintDirty "<< static_cast<Region*>(&manipulator)->name(this) << endl;
08337 }
08338 
08339 void Sheet::clearPaintDirtyData()
08340 {
08341   d->paintDirtyList.clear();
08342 }
08343 
08344 bool Sheet::cellIsPaintDirty( QPoint const & cell ) const
08345 {
08346   return d->paintDirtyList.contains(cell);
08347 }
08348 
08349 #ifndef NDEBUG
08350 void Sheet::printDebug()
08351 {
08352     int iMaxColumn = maxColumn();
08353     int iMaxRow = maxRow();
08354 
08355     kdDebug(36001) << "Cell | Content  | DataT | Text" << endl;
08356     Cell *cell;
08357     for ( int currentrow = 1 ; currentrow < iMaxRow ; ++currentrow )
08358     {
08359         for ( int currentcolumn = 1 ; currentcolumn < iMaxColumn ; currentcolumn++ )
08360         {
08361             cell = cellAt( currentcolumn, currentrow );
08362             if ( !cell->isDefault() && !cell->isEmpty() )
08363             {
08364                 QString cellDescr = Cell::name( currentcolumn, currentrow );
08365                 cellDescr = cellDescr.rightJustify( 4,' ' );
08366                 //QString cellDescr = "Cell ";
08367                 //cellDescr += QString::number(currentrow).rightJustify(3,'0') + ',';
08368                 //cellDescr += QString::number(currentcolumn).rightJustify(3,'0') + ' ';
08369                 cellDescr += " | ";
08370                 cellDescr += cell->value().type();
08371                 cellDescr += " | ";
08372                 cellDescr += cell->text();
08373                 if ( cell->isFormula() )
08374                     cellDescr += QString("  [result: %1]").arg( cell->value().asString() );
08375                 kdDebug(36001) << cellDescr << endl;
08376             }
08377         }
08378     }
08379 }
08380 #endif
08381 
08382 } // namespace KSpread
08383 
KDE Home | KDE Accessibility Home | Description of Access Keys