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