kspread

kspread_sheet.cc

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