/* This file is part of the KDE project Copyright 1998, 1999 Torben Weis Copyright 1999- 2006 The KSpread Team www.koffice.org/kspread This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "commands.h" #include "dependencies.h" #include "selection.h" #include "ksploadinginfo.h" #include "ksprsavinginfo.h" #include "kspread_canvas.h" #include "kspread_cluster.h" #include "kspread_condition.h" #include "kspread_doc.h" #include "kspread_global.h" #include "kspread_locale.h" #include "kspread_map.h" #include "kspread_object.h" #include "kspread_sheetprint.h" #include "kspread_style.h" #include "kspread_style_manager.h" #include "kspread_undo.h" #include "kspread_util.h" #include "kspread_view.h" #include "manipulator.h" #include "manipulator_data.h" #include "KSpreadTableIface.h" #include "kspread_sheet.h" #include "kspread_sheet.moc" #define NO_MODIFICATION_POSSIBLE \ do { \ KMessageBox::error( 0, i18n ( "You cannot change a protected sheet" ) ); return; \ } while(0) namespace KSpread { /***************************************************************************** * * CellBinding * *****************************************************************************/ CellBinding::CellBinding( Sheet *_sheet, const TQRect& _area ) { m_rctDataArea = _area; m_pSheet = _sheet; m_pSheet->addCellBinding( this ); m_bIgnoreChanges = false; } CellBinding::~CellBinding() { m_pSheet->removeCellBinding( this ); } void CellBinding::cellChanged( Cell *_cell ) { if ( m_bIgnoreChanges ) return; emit changed( _cell ); } bool CellBinding::contains( int _x, int _y ) { return m_rctDataArea.contains( TQPoint( _x, _y ) ); } /***************************************************************************** * * ChartBinding * *****************************************************************************/ ChartBinding::ChartBinding( Sheet *_sheet, const TQRect& _area, EmbeddedChart *_child ) : CellBinding( _sheet, _area ) { m_child = _child; } ChartBinding::~ChartBinding() { } void ChartBinding::cellChanged( Cell* /*changedCell*/ ) { if ( m_bIgnoreChanges ) return; //Ensure display gets updated by marking all cells underneath the chart as //dirty const TQRect chartGeometry = m_child->geometry().toTQRect(); double tmp; int left = sheet()->leftColumn( chartGeometry.left() , tmp ); int top = sheet()->topRow( chartGeometry.top() , tmp ); int right = sheet()->rightColumn( chartGeometry.right() ); int bottom = sheet()->bottomRow( chartGeometry.bottom() ); sheet()->setRegionPaintDirty( TQRect(left,top,right-left,bottom-top) ); //kdDebug(36001) << m_rctDataArea << endl; // Get the chart and resize its data if necessary. // // FIXME: Only do this if he data actually changed size. KoChart::Part *chart = m_child->chart(); chart->resizeData( m_rctDataArea.height(), m_rctDataArea.width() ); // Reset all the data, i.e. retransfer them to the chart. // This is definitely not the most efficient way to do this. // // FIXME: Find a way to do it with just the data that changed. Cell* cell; for ( int row = 0; row < m_rctDataArea.height(); row++ ) { for ( int col = 0; col < m_rctDataArea.width(); col++ ) { cell = m_pSheet->cellAt( m_rctDataArea.left() + col, m_rctDataArea.top() + row ); if ( cell && cell->value().isNumber() ) chart->setCellData( row, col, cell->value().asFloat() ); else if ( cell ) chart->setCellData( row, col, cell->value().asString() ); else chart->setCellData( row, col, KoChart::Value() ); } } chart->analyzeHeaders( ); // ######### Kalle may be interested in that, too #if 0 Chart::Range range; range.top = m_rctDataArea.top(); range.left = m_rctDataArea.left(); range.right = m_rctDataArea.right(); range.bottom = m_rctDataArea.bottom(); range.sheet = m_pSheet->name(); */ //m_child->chart()->setData( matrix ); // Force a redraw of the chart on all views /** TODO - replace the call below with something that will repaint this chart */ #endif // sheet()->emit_polygonInvalidated( m_child->framePointArray() ); } /******************************************************************/ /* Class: TextDrag */ /******************************************************************/ TextDrag::TextDrag( TQWidget * dragSource, const char * name ) : TQTextDrag( dragSource, name ) { } TextDrag::~TextDrag() { } TQByteArray TextDrag::encodedData( const char * mime ) const { if ( strcmp( selectionMimeType(), mime ) == 0) return m_kspread; else return TQTextDrag::encodedData( mime ); } bool TextDrag::canDecode( TQMimeSource* e ) { if ( e->provides( selectionMimeType() ) ) return true; return TQTextDrag::canDecode(e); } const char * TextDrag::format( int i ) const { if ( i < 4 ) // HACK, but how to do otherwise ?? return TQTextDrag::format(i); else if ( i == 4 ) return selectionMimeType(); else return 0; } const char * TextDrag::selectionMimeType() { return "application/x-kspread-snippet"; } /***************************************************************************** * * Sheet * *****************************************************************************/ class Sheet::Private { public: Map* workbook; DCOPObject* dcop; TQString name; int id; Sheet::LayoutDirection layoutDirection; // true if sheet is hidden bool hide; // password of protected sheet TQCString password; bool showGrid; bool showFormula; bool showFormulaIndicator; bool showCommentIndicator; bool autoCalc; bool lcMode; bool showColumnNumber; bool hideZero; bool firstLetterUpper; // clusters to hold objects Cluster cells; RowCluster rows; ColumnCluster columns; // default objects Cell* defaultCell; Format* defaultFormat; RowFormat* defaultRowFormat; ColumnFormat* defaultColumnFormat; // hold the print object SheetPrint* print; // cells that need painting Region paintDirtyList; // to get font metrics TQPainter *painter; TQWidget *widget; // List of all cell bindings. For example charts use bindings to get // informed about changing cell contents. TQPtrList cellBindings; // Indicates whether the sheet should paint the page breaks. // Doing so costs some time, so by default it should be turned off. bool showPageBorders; // List of all embedded objects. FIXME unused ?? // TQPtrList m_lstChildren; // The highest row and column ever accessed by the user. int maxRow; int maxColumn; // Max range of canvas in x and ye direction. // Depends on KS_colMax/KS_rowMax and the width/height of all columns/rows double sizeMaxX; double sizeMaxY; bool scrollBarUpdates; TQPen emptyPen; TQBrush emptyBrush; TQColor emptyColor; int scrollPosX; int scrollPosY; KSpread::DependencyManager *dependencies; }; int Sheet::s_id = 0L; TQIntDict* Sheet::s_mapSheets; Sheet* Sheet::find( int _id ) { if ( !s_mapSheets ) return 0L; return (*s_mapSheets)[ _id ]; } Sheet::Sheet (Map* map, const TQString &sheetName, const char *_name ) : TQObject( map, _name ) { if ( s_mapSheets == 0L ) s_mapSheets = new TQIntDict; d = new Private; d->workbook = map; d->id = s_id++; s_mapSheets->insert( d->id, this ); d->layoutDirection = LeftToRight; d->defaultFormat = new Format (this, d->workbook->doc()->styleManager()->defaultStyle()); d->emptyPen.setStyle( TQt::NoPen ); d->dcop = 0; d->name = sheetName; dcopObject(); d->cellBindings.setAutoDelete( false ); // m_lstChildren.setAutoDelete( true ); d->cells.setAutoDelete( true ); d->rows.setAutoDelete( true ); d->columns.setAutoDelete( true ); d->defaultCell = new Cell( this, d->workbook->doc()->styleManager()->defaultStyle(), 0, 0); d->defaultRowFormat = new RowFormat( this, 0 ); d->defaultRowFormat->setDefault(); d->defaultColumnFormat = new ColumnFormat( this, 0 ); d->defaultColumnFormat->setDefault(); d->widget = new TQWidget(); d->painter = new TQPainter; d->painter->begin( d->widget ); d->maxColumn = 256; d->maxRow = 256; d->sizeMaxX = KS_colMax * d->defaultColumnFormat->dblWidth(); // default is max cols * default width d->sizeMaxY = KS_rowMax * d->defaultRowFormat->dblHeight(); // default is max rows * default height d->scrollBarUpdates = true; setHidden( false ); d->showGrid=true; d->showFormula=false; d->showFormulaIndicator=true; d->showCommentIndicator=true; d->showPageBorders = false; d->lcMode=false; d->showColumnNumber=false; d->hideZero=false; d->firstLetterUpper=false; d->autoCalc=true; // Get a unique name so that we can offer scripting if ( !_name ) { TQCString s; s.sprintf("Sheet%i", s_id ); TQObject::setName( s.data() ); } d->print = new SheetPrint( this ); // initialize dependencies d->dependencies = new KSpread::DependencyManager (this); // connect to named area slots TQObject::connect( doc(), TQ_SIGNAL( sig_addAreaName( const TQString & ) ), this, TQ_SLOT( slotAreaModified( const TQString & ) ) ); TQObject::connect( doc(), TQ_SIGNAL( sig_removeAreaName( const TQString & ) ), this, TQ_SLOT( slotAreaModified( const TQString & ) ) ); } TQString Sheet::sheetName() const { return d->name; } Map* Sheet::workbook() const { return d->workbook; } Doc* Sheet::doc() const { return d->workbook->doc(); } int Sheet::id() const { return d->id; } Sheet::LayoutDirection Sheet::layoutDirection() const { return d->layoutDirection; } void Sheet::setLayoutDirection( LayoutDirection dir ) { d->layoutDirection = dir; } bool Sheet::isRightToLeft() const { return d->layoutDirection == RightToLeft; } bool Sheet::isHidden() const { return d->hide; } void Sheet::setHidden( bool hidden ) { d->hide = hidden; } bool Sheet::getShowGrid() const { return d->showGrid; } void Sheet::setShowGrid( bool _showGrid ) { d->showGrid=_showGrid; } bool Sheet::getShowFormula() const { return d->showFormula; } void Sheet::setShowFormula( bool _showFormula ) { d->showFormula=_showFormula; } bool Sheet::getShowFormulaIndicator() const { return d->showFormulaIndicator; } void Sheet::setShowFormulaIndicator( bool _showFormulaIndicator ) { d->showFormulaIndicator=_showFormulaIndicator; } bool Sheet::getShowCommentIndicator() const { return d->showCommentIndicator; } void Sheet::setShowCommentIndicator(bool _indic) { d->showCommentIndicator=_indic; } bool Sheet::getLcMode() const { return d->lcMode; } void Sheet::setLcMode( bool _lcMode ) { d->lcMode=_lcMode; } bool Sheet::getAutoCalc() const { return d->autoCalc; } void Sheet::setAutoCalc( bool _AutoCalc ) { //Avoid possible recalculation of dependancies if the auto calc setting hasn't changed if (d->autoCalc == _AutoCalc) return; //If enabling automatic calculation, make sure that the dependencies are up-to-date if (_AutoCalc == true) { updateAllDependencies(); recalc(); } d->autoCalc=_AutoCalc; } bool Sheet::getShowColumnNumber() const { return d->showColumnNumber; } void Sheet::setShowColumnNumber( bool _showColumnNumber ) { d->showColumnNumber=_showColumnNumber; } bool Sheet::getHideZero() const { return d->hideZero; } void Sheet::setHideZero( bool _hideZero ) { d->hideZero=_hideZero; } bool Sheet::getFirstLetterUpper() const { return d->firstLetterUpper; } void Sheet::setFirstLetterUpper( bool _firstUpper ) { d->firstLetterUpper=_firstUpper; } bool Sheet::isShowPageBorders() const { return d->showPageBorders; } bool Sheet::isEmpty( unsigned long int x, unsigned long int y ) const { const Cell* c = cellAt( x, y ); if ( !c || c->isEmpty() ) return true; return false; } Cell* Sheet::defaultCell() const { return d->defaultCell; } Format* Sheet::defaultFormat() { return d->defaultFormat; } const Format* Sheet::defaultFormat() const { return d->defaultFormat; } const ColumnFormat* Sheet::columnFormat( int _column ) const { const ColumnFormat *p = d->columns.lookup( _column ); if ( p != 0L ) return p; return d->defaultColumnFormat; } ColumnFormat* Sheet::columnFormat( int _column ) { ColumnFormat *p = d->columns.lookup( _column ); if ( p != 0L ) return p; return d->defaultColumnFormat; } const RowFormat* Sheet::rowFormat( int _row ) const { const RowFormat *p = d->rows.lookup( _row ); if ( p != 0L ) return p; return d->defaultRowFormat; } RowFormat* Sheet::rowFormat( int _row ) { RowFormat *p = d->rows.lookup( _row ); if ( p != 0L ) return p; return d->defaultRowFormat; } Value Sheet::value (int col, int row) const { Cell *cell = d->cells.lookup (col, row); if (cell) return cell->value (); Value empty; return empty; } Value Sheet::valueRange (int col1, int row1, int col2, int row2) const { return d->cells.valueRange (col1, row1, col2, row2); } void Sheet::password( TQCString & passwd ) const { passwd = d->password; } bool Sheet::isProtected() const { return !d->password.isNull(); } void Sheet::setProtected( TQCString const & passwd ) { d->password = passwd; } bool Sheet::checkPassword( TQCString const & passwd ) const { return ( passwd == d->password ); } SheetPrint* Sheet::print() const { return d->print; } TQPainter& Sheet::painter() { return *d->painter; } TQWidget* Sheet::widget()const { return d->widget; } CellBinding* Sheet::firstCellBinding() { return d->cellBindings.first(); } CellBinding* Sheet::nextCellBinding() { return d->cellBindings.next(); } void Sheet::setDefaultHeight( double height ) { if ( isProtected() ) NO_MODIFICATION_POSSIBLE; d->defaultRowFormat->setDblHeight( height ); } void Sheet::setDefaultWidth( double width ) { if ( isProtected() ) NO_MODIFICATION_POSSIBLE; d->defaultColumnFormat->setDblWidth( width ); } double Sheet::sizeMaxX() const { return d->sizeMaxX; } double Sheet::sizeMaxY() const { return d->sizeMaxY; } int Sheet::maxColumn() const { return d->maxColumn; } int Sheet::maxRow() const { return d->maxRow; } const TQPen& Sheet::emptyPen() const { return d->emptyPen; } const TQBrush& Sheet::emptyBrush() const { return d->emptyBrush; } const TQColor& Sheet::emptyColor() const { return d->emptyColor; } KSpread::DependencyManager *Sheet::dependencies () { return d->dependencies; } int Sheet::numSelected() const { int num = 0; TQPtrListIterator it( d->workbook->doc()->embeddedObjects() ); for ( ; it.current() ; ++it ) { if( it.current()->sheet() == this && it.current()->isSelected() ) num++; } return num; } int Sheet::leftColumn( double _xpos, double &_left, const Canvas *_canvas ) const { if ( _canvas ) { _xpos += _canvas->xOffset(); _left = -_canvas->xOffset(); } else _left = 0.0; int col = 1; double x = columnFormat( col )->dblWidth( _canvas ); while ( x < _xpos ) { // Should never happen if ( col >= KS_colMax ) { kdDebug(36001) << "Sheet:leftColumn: invalid column (col: " << col + 1 << ")" << endl; return KS_colMax + 1; //Return out of range value, so other code can react on this } _left += columnFormat( col )->dblWidth( _canvas ); col++; x += columnFormat( col )->dblWidth( _canvas ); } return col; } int Sheet::rightColumn( double _xpos, const Canvas *_canvas ) const { if ( _canvas ) _xpos += _canvas->xOffset(); int col = 1; double x = 0.0; while ( x < _xpos ) { // Should never happen if ( col > KS_colMax ) { kdDebug(36001) << "Sheet:rightColumn: invalid column (col: " << col << ")" << endl; return KS_colMax + 1; //Return out of range value, so other code can react on this } x += columnFormat( col )->dblWidth( _canvas ); col++; } return col - 1; } TQRect Sheet::visibleRect( Canvas const * const _canvas ) const { int top = 0; int left = 0; double x = 0; double y = 0; double width = 0; double height = 0; if ( _canvas ) { y += _canvas->yOffset() * _canvas->zoom(); x += _canvas->xOffset() * _canvas->zoom(); width = _canvas->width(); height = _canvas->height(); } double yn = rowFormat( top )->dblHeight( _canvas ); while ( yn < y ) { if ( top >= KS_rowMax ) // Should never happen break; ++top; yn += rowFormat( top )->dblHeight( _canvas ); } int bottom = top + 1; y += height; while ( yn < y ) { if ( bottom > KS_rowMax ) // Should never happen break; ++bottom; yn += rowFormat( bottom )->dblHeight( _canvas ); } double xn = columnFormat( left )->dblWidth( _canvas ); while ( xn < x ) { if ( left >= KS_colMax ) // Should never happen break; ++left; xn += columnFormat( left )->dblWidth( _canvas ); } x += width; int right = left + 1; while ( xn < x ) { if ( right > KS_colMax ) // Should never happen break; ++right; xn += columnFormat( right )->dblWidth( _canvas ); } x += width; return TQRect( left, top, right - left + 1, bottom - top + 1 ); } int Sheet::topRow( double _ypos, double & _top, const Canvas *_canvas ) const { if ( _canvas ) { _ypos += _canvas->yOffset(); _top = -_canvas->yOffset(); } else _top = 0.0; int row = 1; double y = rowFormat( row )->dblHeight( _canvas ); while ( y < _ypos ) { // Should never happen if ( row >= KS_rowMax ) { kdDebug(36001) << "Sheet:topRow: invalid row (row: " << row + 1 << ")" << endl; return KS_rowMax + 1; //Return out of range value, so other code can react on this } _top += rowFormat( row )->dblHeight( _canvas ); row++; y += rowFormat( row )->dblHeight( _canvas ); } return row; } int Sheet::bottomRow( double _ypos, const Canvas *_canvas ) const { if ( _canvas ) _ypos += _canvas->yOffset(); int row = 1; double y = 0.0; while ( y < _ypos ) { // Should never happen if ( row > KS_rowMax ) { kdDebug(36001) << "Sheet:bottomRow: invalid row (row: " << row << ")" << endl; return KS_rowMax + 1; //Return out of range value, so other code can react on this } y += rowFormat( row )->dblHeight( _canvas ); row++; } return row - 1; } double Sheet::dblColumnPos( int _col, const Canvas *_canvas ) const { double x = 0.0; if ( _canvas ) x -= _canvas->xOffset(); for ( int col = 1; col < _col; col++ ) { // Should never happen if ( col > KS_colMax ) { kdDebug(36001) << "Sheet:columnPos: invalid column (col: " << col << ")" << endl; return x; } x += columnFormat( col )->dblWidth( _canvas ); } return x; } int Sheet::columnPos( int _col, const Canvas *_canvas ) const { return (int)dblColumnPos( _col, _canvas ); } double Sheet::dblRowPos( int _row, const Canvas *_canvas ) const { double y = 0.0; if ( _canvas ) y -= _canvas->yOffset(); for ( int row = 1 ; row < _row ; row++ ) { // Should never happen if ( row > KS_rowMax ) { kdDebug(36001) << "Sheet:rowPos: invalid row (row: " << row << ")" << endl; return y; } y += rowFormat( row )->dblHeight( _canvas ); } return y; } int Sheet::rowPos( int _row, const Canvas *_canvas ) const { return (int)dblRowPos( _row, _canvas ); } void Sheet::adjustSizeMaxX ( double _x ) { d->sizeMaxX += _x; } void Sheet::adjustSizeMaxY ( double _y ) { d->sizeMaxY += _y; } Cell* Sheet::visibleCellAt( int _column, int _row, bool _scrollbar_update ) { Cell* cell = cellAt( _column, _row, _scrollbar_update ); if ( cell->obscuringCells().isEmpty() ) return cell; else return cell->obscuringCells().last(); } Cell* Sheet::firstCell() const { return d->cells.firstCell(); } RowFormat* Sheet::firstRow() const { return d->rows.first(); } ColumnFormat* Sheet::firstCol() const { return d->columns.first(); } Cell* Sheet::cellAt( int _column, int _row ) const { Cell *p = d->cells.lookup( _column, _row ); if ( p != 0L ) return p; return d->defaultCell; } Cell* Sheet::cellAt( int _column, int _row, bool _scrollbar_update ) { if ( _scrollbar_update && d->scrollBarUpdates ) { checkRangeHBorder( _column ); checkRangeVBorder( _row ); } Cell *p = d->cells.lookup( _column, _row ); if ( p != 0L ) return p; return d->defaultCell; } ColumnFormat* Sheet::nonDefaultColumnFormat( int _column, bool force_creation ) { ColumnFormat *p = d->columns.lookup( _column ); if ( p != 0L || !force_creation ) return p; p = new ColumnFormat( this, _column ); // TODO: copy the default ColumnFormat here!! p->setDblWidth( d->defaultColumnFormat->dblWidth() ); d->columns.insertElement( p, _column ); return p; } RowFormat* Sheet::nonDefaultRowFormat( int _row, bool force_creation ) { RowFormat *p = d->rows.lookup( _row ); if ( p != 0L || !force_creation ) return p; p = new RowFormat( this, _row ); // TODO: copy the default RowLFormat here!! p->setDblHeight( d->defaultRowFormat->dblHeight() ); d->rows.insertElement( p, _row ); return p; } Cell* Sheet::nonDefaultCell( int _column, int _row, bool _scrollbar_update, Style * _style ) { // NOTE Stefan: _scrollbar_update defaults to false and this function // is never called with it being true. So, this here is // actually never processed. I'll leave this in here for the // case I'm mistaken, but will remove it for 2.0. if ( _scrollbar_update && d->scrollBarUpdates ) { checkRangeHBorder( _column ); checkRangeVBorder( _row ); } Cell * p = d->cells.lookup( _column, _row ); if ( p != 0L ) return p; Cell * cell = 0; if ( _style ) cell = new Cell( this, _style, _column, _row ); else cell = new Cell( this, _column, _row ); insertCell( cell ); return cell; } void Sheet::setText( int _row, int _column, const TQString& _text, bool asString ) { ProtectedCheck prot; prot.setSheet (this); prot.add (TQPoint (_column, _row)); if (prot.check()) NO_MODIFICATION_POSSIBLE; DataManipulator *dm = new DataManipulator (); dm->setSheet (this); dm->setValue (_text); dm->setParsing (!asString); dm->add (TQPoint (_column, _row)); dm->execute (); /* PRE-MANIPULATOR CODE looked like this: TODO remove this after the new code works if ( !doc()->undoLocked() ) { UndoSetText *undo = new UndoSetText( doc(), this, cell->text(), _column, _row,cell->formatType() ); doc()->addCommand( undo ); } // The cell will force a display refresh itself, so we dont have to care here. cell->setCellText( _text, asString ); */ //refresh anchor if(_text.at(0)=='!') emit sig_updateView( this, Region(_column,_row,_column,_row) ); } void Sheet::setArrayFormula (Selection *selectionInfo, const TQString &_text) { // check protection ProtectedCheck prot; prot.setSheet (this); prot.add (*selectionInfo); if (prot.check()) NO_MODIFICATION_POSSIBLE; // create and call the manipulator ArrayFormulaManipulator *afm = new ArrayFormulaManipulator; afm->setSheet (this); afm->setText (_text); afm->add (*selectionInfo); afm->execute (); /* PRE-MANIPULATOR CODE LOOKED LIKE THIS TODO remove this when the above code works // add undo if ( !doc()->undoLocked() ) { UndoChangeAreaTextCell *undo = new UndoChangeAreaTextCell (doc(), this, TQRect (_column, _row, cols, rows)); doc()->addCommand( undo ); } // fill in the cells ... top-left one gets the formula, the rest gets =INDEX // TODO: also fill in information about cells being a part of a range Cell *cell = nonDefaultCell (_column, _row); cell->setCellText (_text, false); TQString cellRef = cell->name(); for (int row = 0; row < rows; ++row) for (int col = 0; col < cols; col++) if (col || row) { Cell *cell = nonDefaultCell (_column + col, _row + row); cell->setCellText ("=INDEX(" + cellRef + ";" + TQString::number (row+1) + ";" + TQString::number (col+1) + ")", false); } */ } void Sheet::setLayoutDirtyFlag() { Cell * c = d->cells.firstCell(); for( ; c; c = c->nextCell() ) c->setLayoutDirtyFlag(); } void Sheet::setCalcDirtyFlag() { Cell* c = d->cells.firstCell(); for( ; c; c = c->nextCell() ) { if ( !(c->isObscured() && c->isPartOfMerged()) ) c->setCalcDirtyFlag(); } } void Sheet::updateAllDependencies() { for (Cell* cell = d->cells.firstCell() ; cell ; cell = cell->nextCell()) { Point cellLocation; cellLocation.setSheet(cell->sheet()); cellLocation.setRow(cell->row()); cellLocation.setColumn(cell->column()); d->dependencies->cellChanged(cellLocation); } } void Sheet::recalc() { recalc(false); } void Sheet::recalc( bool force ) { ElapsedTime et( "Recalculating " + d->name, ElapsedTime::PrintOnlyTime ); // emitBeginOperation(true); // setRegionPaintDirty(TQRect(TQPoint(1,1), TQPoint(KS_colMax, KS_rowMax))); setCalcDirtyFlag(); //If automatic calculation is disabled, don't recalculate unless the force flag has been //set. if ( !getAutoCalc() && !force ) return; //If automatic calculation is disabled, the dependencies won't be up to date, so they need //to be recalculated. //FIXME: Tomas, is there a more efficient way to do this? if ( !getAutoCalc() ) updateAllDependencies(); // (Tomas): actually recalc each cell // this is FAR from being perfect, dependencies will cause some to be // recalculated a LOT, but it's still better than otherwise, where // we get no recalc if the result stored in a file differs from the // current one - then we only obtain the correct result AFTER we scroll // to the cell ... recalc should actually ... recalc :) Cell* c; int count = 0; c = d->cells.firstCell(); for( ; c; c = c->nextCell() ) ++count; int cur = 0; int percent = -1; c = d->cells.firstCell(); for( ; c; c = c->nextCell() ) { c->calc (false); cur++; // some debug output to get some idea how damn slow this is ... if (cur*100/count != percent) { percent = cur*100/count; // kdDebug() << "Recalc: " << percent << "%" << endl; } } // emitEndOperation(); emit sig_updateView( this ); } void Sheet::valueChanged (Cell *cell) { //TODO: call cell updating, when cell damaging implemented //prepare the Point structure Point c; c.setRow (cell->row()); c.setColumn (cell->column()); c.setSheet( this ); //update dependencies if ( getAutoCalc() ) d->dependencies->cellChanged (c); //REMOVED - modification change - this was causing modified flag to be set inappropriately. //nobody else seems to be setting the modified flag, so we do it here // doc()->setModified (true); } /* Methods working on selections: TYPE A: { columns selected: for all rows with properties X,X': if default-cell create new cell } post undo object (always a UndoCellLayout; difference in title only) { rows selected: if condition Y clear properties X,X' of cells; set properties X,X' of rowformats emit complete update; } { columns selected: if condition Y clear properties X,X' of cells; set properties X,X' of columnformats; for all rows with properties X,X': create cells if necessary and set properties X,X' emit complete update; } { cells selected: for all cells with condition Y: create if necessary and set properties X,X' and do Z; emit update on selected region; } USED in: setSelectionFont setSelectionSize setSelectionAngle setSelectionTextColor setSelectionBgColor setSelectionPercent borderAll borderRemove (exceptions: ### creates cells (why?), ### changes default cell if cell-regions selected?) setSelectionAlign setSelectionAlignY setSelectionMoneyFormat increaseIndent decreaseIndent TYPE B: post undo object { rows selected: if condition Y do X with cells; emit update on selection; } { columns selected: if condition Y do X with cells; emit update on selection; } { cells selected: if condition Y do X with cells; create cell if non-default; emit update on selection; } USED in: setSelectionUpperLower (exceptions: no undo; no create-if-default; ### modifies default-cell?) setSelectionFirstLetterUpper (exceptions: no undo; no create-if-default; ### modifies default-cell?) setSelectionVerticalText setSelectionComment setSelectionRemoveComment (exeception: no create-if-default and work only on non-default-cells for cell regions) setSelectionBorderColor (exeception: no create-if-default and work only on non-default-cells for cell regions) setSelectionMultiRow setSelectionPrecision clearTextSelection (exception: all only if !areaIsEmpty()) clearValiditySelection (exception: all only if !areaIsEmpty()) clearConditionalSelection (exception: all only if !areaIsEmpty()) setConditional (exception: conditional after create-if-default for cell regions) setValidity (exception: conditional after create-if-default for cell regions) OTHERS: borderBottom borderRight borderLeft borderTop borderOutline => these work only on some cells (at the border); undo only if cells affected; rest is similar to type A --> better not use CellWorker/workOnCells() defaultSelection => similar to TYPE B, but works on columns/rows if complete columns/rows selected --> use emit_signal=false and return value of workOnCells to finish getWordSpelling => returns text, no signal emitted, no cell-create, similar to TYPE B --> use emit_signal=false, create_if_default=false and type B setWordSpelling => no signal emitted, no cell-create, similar to type B --> use emit_signal=false, create_if_default=false and type B */ class UndoAction* Sheet::CellWorkerTypeA::createUndoAction( Doc* doc, Sheet* sheet, const KSpread::Region& region ) { TQString title = getUndoTitle(); return new UndoCellFormat( doc, sheet, region, title ); } /* Sheet::SelectionType Sheet::workOnCells( const TQPoint& _marker, CellWorker& worker ) { // see what is selected; if nothing, take marker position bool selected = ( m_rctSelection.left() != 0 ); TQRect r( m_rctSelection ); if ( !selected ) r.setCoords( _marker.x(), _marker.y(), _marker.x(), _marker.y() ); // create cells in rows if complete columns selected Cell *cell; if ( !worker.type_B && selected && isColumnSelected() ) { for ( RowFormat* rw =d->rows.first(); rw; rw = rw->next() ) { if ( !rw->isDefault() && worker.testCondition( rw ) ) { for ( int i=m_rctSelection.left(); i<=m_rctSelection.right(); i++ ) { cell = cellAt( i, rw->row() ); if ( cell == d->defaultCell ) // '&& worker.create_if_default' unnecessary as never used in type A { cell = new Cell( this, i, rw->row() ); insertCell( cell ); } } } } } // create an undo action if ( !doc()->undoLocked() ) { UndoAction *undo = worker.createUndoAction( doc(), this, r ); // test if the worker has an undo action if ( undo != 0L ) doc()->addCommand( undo ); } // complete rows selected ? if ( selected && isRowSelected() ) { int row; for ( Cell* cell = d->cells.firstCell(); cell; cell = cell->nextCell() ) { row = cell->row(); if ( m_rctSelection.top() <= row && m_rctSelection.bottom() >= row && worker.testCondition( cell ) ) if ( worker.type_B ) worker.doWork( cell, false, cell->column(), row ); else worker.prepareCell( cell ); } if ( worker.type_B ) { // for type B there's nothing left to do if ( worker.emit_signal ) emit sig_updateView( this, r ); } else { // for type A now work on row formats for ( int i=m_rctSelection.top(); i<=m_rctSelection.bottom(); i++ ) { RowFormat *rw=nonDefaultRowFormat(i); worker.doWork( rw ); } if ( worker.emit_signal ) emit sig_updateView( this ); } return CompleteRows; } // complete columns selected ? else if ( selected && isColumnSelected() ) { int col; for ( Cell* cell = d->cells.firstCell(); cell; cell = cell->nextCell() ) { col = cell->column(); if ( m_rctSelection.left() <= col && m_rctSelection.right() >= col && worker.testCondition( cell ) ) if ( worker.type_B ) worker.doWork( cell, false, col, cell->row() ); else worker.prepareCell( cell ); } if ( worker.type_B ) { if ( worker.emit_signal ) emit sig_updateView( this, r ); } else { // for type A now work on column formats for ( int i=m_rctSelection.left(); i<=m_rctSelection.right(); i++ ) { ColumnFormat *cl=nonDefaultColumnFormat(i); worker.doWork( cl ); } Cell *cell; for ( RowFormat* rw =d->rows.first(); rw; rw = rw->next() ) { if ( !rw->isDefault() && worker.testCondition( rw ) ) { for ( int i=m_rctSelection.left(); i<=m_rctSelection.right(); i++ ) { cell = cellAt( i, rw->row() ); // ### this if should be not necessary; cells are created // before the undo object is created, aren't they? if ( cell == d->defaultCell ) { cell = new Cell( this, i, rw->row() ); insertCell( cell ); } worker.doWork( cell, false, i, rw->row() ); } } } if ( worker.emit_signal ) emit sig_updateView( this ); } return CompleteColumns; } // cell region selected else { Cell *cell; for ( int x = r.left(); x <= r.right(); x++ ) for ( int y = r.top(); y <= r.bottom(); y++ ) { cell = cellAt( x, y ); if ( worker.testCondition( cell ) ) { if ( worker.create_if_default && cell == d->defaultCell ) { cell = new Cell( this, x, y ); insertCell( cell ); } if ( cell != d->defaultCell ) worker.doWork( cell, true, x, y ); } } if ( worker.emit_signal ) emit sig_updateView( this, r ); return CellRegion; } } */ Sheet::SelectionType Sheet::workOnCells( Selection* selectionInfo, CellWorker & worker ) { Sheet::SelectionType result; doc()->emitBeginOperation(); // see what is selected; if nothing, take marker position bool selected = !(selectionInfo->isSingular()); // create an undo action if ( !doc()->undoLocked() ) { UndoAction* undo = worker.createUndoAction(doc(), this, *selectionInfo); // test if the worker has an undo action if ( undo != 0 ) { doc()->addCommand( undo ); } } Region::ConstIterator endOfList(selectionInfo->constEnd()); for (Region::ConstIterator it = selectionInfo->constBegin(); it != endOfList; ++it) { // see what is selected; if nothing, take marker position TQRect range = (*it)->rect().normalize(); int top = range.top(); int left = range.left(); int bottom = range.bottom(); int right = range.right(); // create cells in rows if complete columns selected Cell * cell; Style * s = doc()->styleManager()->defaultStyle(); if ( !worker.type_B && selected && util_isColumnSelected(range) ) { for ( RowFormat * rw = d->rows.first(); rw; rw = rw->next() ) { if ( worker.testCondition( rw ) ) { for ( int col = left; col <= right; ++col ) { cell = nonDefaultCell( col, rw->row(), false, s ); } } } } // complete rows selected ? if ( selected && util_isRowSelected(range) ) { for ( int row = top; row <= bottom; ++row ) { cell = getFirstCellRow( row ); while ( cell ) { if ( worker.testCondition( cell ) ) { if ( worker.type_B ) worker.doWork( cell, false, cell->column(), row ); else worker.prepareCell( cell ); } cell = getNextCellRight( cell->column(), row ); } } if ( worker.type_B ) { // for type B there's nothing left to do ; } else { // for type A now work on row formats for ( int i = top; i <= bottom; ++i ) { RowFormat * rw = nonDefaultRowFormat(i); worker.doWork( rw ); } for ( int row = top; row <= bottom; ++row ) { cell = getFirstCellRow( row ); while ( cell ) { if ( worker.testCondition( cell ) ) { worker.doWork( cell, false, cell->column(), row ); } cell = getNextCellRight( cell->column(), row ); } } } result = CompleteRows; } // complete columns selected ? else if ( selected && util_isColumnSelected(range) ) { for ( int col = range.left(); col <= right; ++col ) { cell = getFirstCellColumn( col ); while ( cell ) { if ( worker.testCondition( cell ) ) { if ( worker.type_B ) worker.doWork( cell, false, col, cell->row() ); else worker.prepareCell( cell ); } cell = getNextCellDown( col, cell->row() ); } } if ( worker.type_B ) { ; } else { // for type A now work on column formats for ( int i = left; i <= right; ++i ) { ColumnFormat * cl = nonDefaultColumnFormat( i ); worker.doWork( cl ); } for ( RowFormat * rw = d->rows.first(); rw; rw = rw->next() ) { if ( worker.testCondition( rw ) ) { for ( int i = left; i <= right; ++i ) { cell = nonDefaultCell( i, rw->row(), false, s ); worker.doWork( cell, false, i, rw->row() ); } } } } result = CompleteColumns; } // cell region selected else { for ( int x = left; x <= right; ++x ) { enableScrollBarUpdates(false); for ( int y = top; y <= bottom; ++y ) { cell = cellAt( x, y ); if ( worker.testCondition( cell ) ) { if ( cell == d->defaultCell && worker.create_if_default ) { cell = new Cell( this, s, x, y ); insertCell( cell ); } if ( cell != d->defaultCell ) { // kdDebug() << "not default" << endl; worker.doWork( cell, true, x, y ); } } } enableScrollBarUpdates(true); checkRangeVBorder(bottom); } checkRangeHBorder(right); result = CellRegion; } } // for Region::Elements // emitEndOperation(); emit sig_updateView( this ); if (worker.emit_signal) { emit sig_updateView( this, *selectionInfo ); } return result; } void Sheet::setSelectionFont( Selection* selectionInfo, const char *_font, int _size, signed char _bold, signed char _italic, signed char _underline, signed char _strike) { FontManipulator* manipulator = new FontManipulator(); manipulator->setSheet(this); manipulator->setProperty(Format::PFont); manipulator->setFontFamily(_font); manipulator->setFontSize(_size); manipulator->setFontBold(_bold); manipulator->setFontItalic(_italic); manipulator->setFontStrike(_strike); manipulator->setFontUnderline(_underline); manipulator->add(*selectionInfo); manipulator->execute(); } void Sheet::setSelectionSize(Selection* selectionInfo, int _size) { // TODO Stefan: Increase/Decrease font size still used? int size; Cell* c; TQPoint marker(selectionInfo->marker()); c = cellAt(marker); size = c->format()->textFontSize(marker.x(), marker.y()); FontManipulator* manipulator = new FontManipulator(); manipulator->setSheet(this); manipulator->setProperty(Format::PFont); manipulator->setFontSize(_size+size); manipulator->add(*selectionInfo); manipulator->execute(); } struct SetSelectionUpperLowerWorker : public Sheet::CellWorker { int _type; Sheet * _s; SetSelectionUpperLowerWorker( int type, Sheet * s ) : Sheet::CellWorker( false ), _type( type ), _s( s ) { } class UndoAction* createUndoAction( Doc* doc, Sheet* sheet, const KSpread::Region& region ) { return new UndoChangeAreaTextCell( doc, sheet, region ); } bool testCondition( Cell* c ) { return ( !c->value().isNumber() && !c->value().isBoolean() &&!c->isFormula() && !c->isDefault() && !c->text().isEmpty() && c->text()[0] != '*' && c->text()[0] != '!' && !c->isPartOfMerged() ); } void doWork( Cell* cell, bool, int, int ) { cell->setDisplayDirtyFlag(); if ( _type == -1 ) cell->setCellText( (cell->text().lower())); else if ( _type == 1 ) cell->setCellText( (cell->text().upper())); cell->clearDisplayDirtyFlag(); } }; void Sheet::setSelectionUpperLower( Selection* selectionInfo, int _type ) { SetSelectionUpperLowerWorker w( _type, this ); workOnCells( selectionInfo, w ); } struct SetSelectionFirstLetterUpperWorker : public Sheet::CellWorker { Changes * _c; Sheet * _s; SetSelectionFirstLetterUpperWorker( Sheet * s ) : Sheet::CellWorker( false ), _s( s ) { } class UndoAction* createUndoAction( Doc* doc, Sheet* sheet, const KSpread::Region& region ) { return new UndoChangeAreaTextCell( doc, sheet, region ); } bool testCondition( Cell* c ) { return ( !c->value().isNumber() && !c->value().isBoolean() &&!c->isFormula() && !c->isDefault() && !c->text().isEmpty() && c->text()[0] != '*' && c->text()[0] != '!' && !c->isPartOfMerged() ); } void doWork( Cell* cell, bool, int, int ) { cell->setDisplayDirtyFlag(); TQString tmp = cell->text(); int len = tmp.length(); cell->setCellText( (tmp.at(0).upper()+tmp.right(len-1)) ); cell->clearDisplayDirtyFlag(); } }; void Sheet::setSelectionfirstLetterUpper( Selection* selectionInfo) { SetSelectionFirstLetterUpperWorker w( this ); workOnCells( selectionInfo, w ); } struct SetSelectionVerticalTextWorker : public Sheet::CellWorker { bool _b; SetSelectionVerticalTextWorker( bool b ) : Sheet::CellWorker( ), _b( b ) { } class UndoAction* createUndoAction( Doc* doc, Sheet* sheet, const KSpread::Region& region ) { TQString title=i18n("Vertical Text"); return new UndoCellFormat( doc, sheet, region, title ); } bool testCondition( Cell* cell ) { return ( !cell->isPartOfMerged() ); } void doWork( Cell* cell, bool, int, int ) { cell->setDisplayDirtyFlag(); cell->format()->setVerticalText( _b ); cell->format()->setMultiRow( false ); cell->format()->setAngle( 0 ); cell->clearDisplayDirtyFlag(); } }; void Sheet::setSelectionVerticalText( Selection* selectionInfo, bool _b ) { SetSelectionVerticalTextWorker w( _b ); workOnCells( selectionInfo, w ); } struct SetSelectionCommentWorker : public Sheet::CellWorker { TQString _comment; SetSelectionCommentWorker( TQString comment ) : Sheet::CellWorker( ), _comment( comment ) { } class UndoAction* createUndoAction( Doc* doc, Sheet* sheet, const KSpread::Region& region ) { TQString title=i18n("Add Comment"); return new UndoCellFormat( doc, sheet, region, title ); } bool testCondition( Cell* cell ) { return ( !cell->isPartOfMerged() ); } void doWork( Cell* cell, bool, int, int ) { cell->setDisplayDirtyFlag(); cell->format()->setComment( _comment ); cell->clearDisplayDirtyFlag(); } }; void Sheet::setSelectionComment( Selection* selectionInfo, const TQString &_comment) { SetSelectionCommentWorker w( _comment ); workOnCells( selectionInfo, w ); } void Sheet::setSelectionAngle( Selection* selectionInfo, int _value ) { AngleManipulator* manipulator = new AngleManipulator(); manipulator->setSheet(this); manipulator->setProperty(Format::PAngle); manipulator->setAngle(_value); manipulator->add(*selectionInfo); manipulator->execute(); } struct SetSelectionRemoveCommentWorker : public Sheet::CellWorker { SetSelectionRemoveCommentWorker( ) : Sheet::CellWorker( false ) { } class UndoAction* createUndoAction( Doc* doc, Sheet* sheet, const KSpread::Region& region ) { TQString title=i18n("Remove Comment"); return new UndoCellFormat( doc, sheet, region, title ); } bool testCondition( Cell* cell ) { return ( !cell->isPartOfMerged() ); } void doWork( Cell* cell, bool, int, int ) { cell->setDisplayDirtyFlag(); cell->format()->setComment( "" ); cell->clearDisplayDirtyFlag(); } }; void Sheet::setSelectionRemoveComment( Selection* selectionInfo ) { if (areaIsEmpty(*selectionInfo, Comment)) return; SetSelectionRemoveCommentWorker w; workOnCells( selectionInfo, w ); } void Sheet::setSelectionTextColor( Selection* selectionInfo, const TQColor &tb_Color ) { FontColorManipulator* manipulator = new FontColorManipulator(); manipulator->setSheet(this); manipulator->setProperty(Format::PTextPen); manipulator->setTextColor(tb_Color); manipulator->add(*selectionInfo); manipulator->execute(); } void Sheet::setSelectionbgColor( Selection* selectionInfo, const TQColor &bg_Color ) { BackgroundColorManipulator* manipulator = new BackgroundColorManipulator(); manipulator->setSheet(this); manipulator->setProperty(Format::PBackgroundColor); manipulator->setBackgroundColor(bg_Color); manipulator->add(*selectionInfo); manipulator->execute(); } struct SetSelectionBorderColorWorker : public Sheet::CellWorker { const TQColor& bd_Color; SetSelectionBorderColorWorker( const TQColor& _bd_Color ) : Sheet::CellWorker( false ), bd_Color( _bd_Color ) { } class UndoAction* createUndoAction( Doc* doc, Sheet* sheet, const KSpread::Region& region ) { TQString title=i18n("Change Border Color"); return new UndoCellFormat( doc, sheet, region, title ); } bool testCondition( Cell* cell ) { return ( !cell->isPartOfMerged() ); } void doWork( Cell* cell, bool, int, int ) { cell->setDisplayDirtyFlag(); int it_Row = cell->row(); int it_Col = cell->column(); if ( cell->format()->topBorderStyle( it_Row, it_Col )!=TQt::NoPen ) cell->format()->setTopBorderColor( bd_Color ); if ( cell->format()->leftBorderStyle( it_Row, it_Col )!=TQt::NoPen ) cell->format()->setLeftBorderColor( bd_Color ); if ( cell->format()->fallDiagonalStyle( it_Row, it_Col )!=TQt::NoPen ) cell->format()->setFallDiagonalColor( bd_Color ); if ( cell->format()->goUpDiagonalStyle( it_Row, it_Col )!=TQt::NoPen ) cell->format()->setGoUpDiagonalColor( bd_Color ); if ( cell->format()->bottomBorderStyle( it_Row, it_Col )!=TQt::NoPen ) cell->format()->setBottomBorderColor( bd_Color ); if ( cell->format()->rightBorderStyle( it_Row, it_Col )!=TQt::NoPen ) cell->format()->setRightBorderColor( bd_Color ); cell->clearDisplayDirtyFlag(); } }; void Sheet::setSelectionBorderColor( Selection* selectionInfo, const TQColor &bd_Color ) { SetSelectionBorderColorWorker w( bd_Color ); workOnCells( selectionInfo, w ); } void Sheet::setSeries( const TQPoint &_marker, double start, double end, double step, Series mode, Series type) { doc()->emitBeginOperation(); TQString cellText; int x,y; /* just some loop counters */ /* the actual number of columns or rows that the series will span. i.e. this will count 3 cells for a single cell that spans three rows */ int numberOfCells; if (end > start) numberOfCells = (int) ((end - start) / step + 1); /*initialize for linear*/ else if ( end one cell fix infini loop numberOfCells = 1; if (type == Geometric) { /* basically, A(n) = start ^ n * so when does end = start ^ n ?? * when n = ln(end) / ln(start) */ numberOfCells = (int)( (log((double)end) / log((double)start)) + DBL_EPSILON) + 1; } Cell * cell = NULL; /* markers for the top-left corner of the undo region. It'll probably * be the top left corner of where the series is, but if something in front * is obscuring the cell, then it needs to be part of the undo region */ TQRect undoRegion; undoRegion.setLeft(_marker.x()); undoRegion.setTop(_marker.y()); /* this whole block is used to find the correct size for the undo region. We're checking for two different things (in these examples, mode==column): 1. cells are vertically merged. This means that one value in the series will span multiple cells. 2. a cell in the column is merged to a cell to its left. In this case the cell value will be stored in the left most cell so we need to extend the undo range to include that column. */ if ( mode == Column ) { for ( y = _marker.y(); y <= (_marker.y() + numberOfCells - 1) && y <= KS_rowMax; y++ ) { cell = cellAt( _marker.x(), y ); if ( cell->isPartOfMerged() ) { /* case 2. */ cell = cell->obscuringCells().first(); undoRegion.setLeft(TQMIN(undoRegion.left(), cell->column())); } /* case 1. Add the extra space to numberOfCells and then skip over the region. Note that because of the above if block 'cell' points to the correct cell in the case where both case 1 and 2 are true */ numberOfCells += cell->extraYCells(); y += cell->extraYCells(); } undoRegion.setRight( _marker.x() ); undoRegion.setBottom( y - 1 ); checkRangeVBorder( undoRegion.bottom() ); } else if(mode == Row) { for ( x = _marker.x(); x <=(_marker.x() + numberOfCells - 1) && x <= KS_colMax; x++ ) { /* see the code above for a column series for a description of what is going on here. */ cell = cellAt( x,_marker.y(), false ); if ( cell->isPartOfMerged() ) { cell = cell->obscuringCells().first(); undoRegion.setTop(TQMIN(undoRegion.top(), cell->row())); } numberOfCells += cell->extraXCells(); x += cell->extraXCells(); } undoRegion.setBottom( _marker.y() ); undoRegion.setRight( x - 1 ); checkRangeHBorder( undoRegion.right() ); } kdDebug() << "Saving undo information" << endl; if ( !doc()->undoLocked() ) { UndoChangeAreaTextCell *undo = new UndoChangeAreaTextCell( doc(), this, undoRegion ); doc()->addCommand( undo ); } kdDebug() << "Saving undo information done" << endl; setRegionPaintDirty( undoRegion ); x = _marker.x(); y = _marker.y(); /* now we're going to actually loop through and set the values */ double incr; Style * s = doc()->styleManager()->defaultStyle(); if (step >= 0 && start < end) { for ( incr = start; incr <= end; ) { cell = nonDefaultCell( x, y, false, s ); if ( cell->isPartOfMerged() ) { cell = cell->obscuringCells().first(); } // cell->setCellText(cellText.setNum( incr )); cell->setNumber( incr ); if (mode == Column) { ++y; if (cell->doesMergeCells()) { y += cell->extraYCells(); } if ( y > KS_rowMax ) { break; } } else if (mode == Row) { ++x; if (cell->doesMergeCells()) { x += cell->extraXCells(); } if ( x > KS_colMax ) { break; } } else { kdDebug(36001) << "Error in Series::mode" << endl; return; } if (type == Linear) incr = incr + step; else if (type == Geometric) incr = incr * step; else { kdDebug(36001) << "Error in Series::type" << endl; return; } } } else if (step >= 0 && start > end) { for ( incr = start; incr >= end; ) { cell = nonDefaultCell( x, y, false, s ); if (cell->isPartOfMerged()) { cell = cell->obscuringCells().first(); } // cell->setCellText(cellText.setNum( incr )); cell->setNumber( incr ); if (mode == Column) { ++y; if (cell->doesMergeCells()) { y += cell->extraYCells(); } if ( y > KS_rowMax ) { break; } } else if (mode == Row) { ++x; if (cell->doesMergeCells()) { x += cell->extraXCells(); } if ( x > KS_colMax ) { break; } } else { kdDebug(36001) << "Error in Series::mode" << endl; return; } if (type == Linear) incr = incr + step; else if (type == Geometric) incr = incr * step; else { kdDebug(36001) << "Error in Series::type" << endl; return; } } } else { for ( incr = start; incr <= end; ) { cell = nonDefaultCell( x, y, false, s ); if (cell->isPartOfMerged()) { cell = cell->obscuringCells().first(); } //cell->setCellText(cellText.setNum( incr )); cell->setNumber( incr ); if (mode == Column) { ++y; if (cell->doesMergeCells()) { y += cell->extraYCells(); } if ( y > KS_rowMax ) { break; } } else if (mode == Row) { ++x; if (cell->doesMergeCells()) { x += cell->extraXCells(); } if ( x > KS_colMax ) { break; } } else { kdDebug(36001) << "Error in Series::mode" << endl; return; } if (type == Linear) incr = incr + step; else if (type == Geometric) { incr = incr * step; //a step = 1 into geometric serie is not good //we don't increase value => infini loop if (step == 1) return; } else { kdDebug(36001) << "Error in Series::type" << endl; return; } } } // doc()->emitEndOperation(); emit sig_updateView( this ); } struct SetSelectionPercentWorker : public Sheet::CellWorkerTypeA { bool b; SetSelectionPercentWorker( bool _b ) : b( _b ) { } TQString getUndoTitle() { return i18n("Format Percent"); } bool testCondition( RowFormat* ) { //TODO: no idea what to put here, now that factor's gone :( return ( true ); } void doWork( RowFormat* rw ) { //rw->setPrecision( 0 ); rw->setFormatType( b ? Percentage_format : Generic_format); } void doWork( ColumnFormat* cl ) { cl->setFormatType( b ? Percentage_format : Generic_format); } void prepareCell( Cell* cell ) { cell->format()->clearProperty(Format::PFormatType); cell->format()->clearNoFallBackProperties( Format::PFormatType ); } bool testCondition( Cell* cell ) { return ( !cell->isPartOfMerged() ); } void doWork( Cell* cell, bool cellRegion, int, int ) { if ( cellRegion ) cell->setDisplayDirtyFlag(); cell->format()->setFormatType( b ? Percentage_format : Generic_format); if ( cellRegion ) cell->clearDisplayDirtyFlag(); } }; void Sheet::setSelectionPercent( Selection* selectionInfo, bool b ) { SetSelectionPercentWorker w( b ); workOnCells( selectionInfo, w ); } void Sheet::slotAreaModified (const TQString &name) { d->dependencies->areaModified (name); } void Sheet::refreshRemoveAreaName(const TQString & _areaName) { Cell * c = d->cells.firstCell(); TQString tmp = "'" + _areaName + "'"; for( ;c ; c = c->nextCell() ) { if ( c->isFormula() ) { if (c->text().find(tmp) != -1) { if ( !c->makeFormula() ) kdError(36001) << "ERROR: Syntax ERROR" << endl; } } } } void Sheet::refreshChangeAreaName(const TQString & _areaName) { Cell * c = d->cells.firstCell(); TQString tmp = "'" + _areaName + "'"; for( ;c ; c = c->nextCell() ) { if ( c->isFormula() ) { if (c->text().find(tmp) != -1) { if ( !c->makeFormula() ) kdError(36001) << "ERROR: Syntax ERROR" << endl; else { /* setting a cell calc dirty also sets it paint dirty */ c->setCalcDirtyFlag(); } } } } } void Sheet::changeCellTabName( TQString const & old_name, TQString const & new_name ) { Cell* c = d->cells.firstCell(); for( ;c; c = c->nextCell() ) { if( c->isFormula() ) { if(c->text().find(old_name)!=-1) { int nb = c->text().contains(old_name+"!"); TQString tmp=old_name+"!"; int len = tmp.length(); tmp=c->text(); for( int i=0; isetCellText(tmp); } } } } bool Sheet::shiftRow( const TQRect &rect,bool makeUndo ) { UndoInsertCellRow * undo = 0; if ( !doc()->undoLocked() &&makeUndo) { undo = new UndoInsertCellRow( doc(), this, rect ); doc()->addCommand( undo ); } bool res=true; bool result; for( int i=rect.top(); i<=rect.bottom(); i++ ) { for( int j=0; j<=(rect.right()-rect.left()); j++ ) { result = d->cells.shiftRow( TQPoint(rect.left(),i) ); if( !result ) res=false; } } TQPtrListIterator it( workbook()->sheetList() ); for( ; it.current(); ++it ) { for(int i = rect.top(); i <= rect.bottom(); i++ ) it.current()->changeNameCellRef( TQPoint( rect.left(), i ), false, Sheet::ColumnInsert, name(), ( rect.right() - rect.left() + 1), undo); } refreshChart(TQPoint(rect.left(),rect.top()), false, Sheet::ColumnInsert); refreshMergedCell(); recalc(); emit sig_updateView( this ); return res; } bool Sheet::shiftColumn( const TQRect& rect,bool makeUndo ) { UndoInsertCellCol * undo = 0; if ( !doc()->undoLocked() &&makeUndo) { undo = new UndoInsertCellCol( doc(), this,rect); doc()->addCommand( undo ); } bool res=true; bool result; for( int i =rect.left(); i<=rect.right(); i++ ) { for( int j=0; j<=(rect.bottom()-rect.top()); j++ ) { result = d->cells.shiftColumn( TQPoint(i,rect.top()) ); if(!result) res=false; } } TQPtrListIterator it( workbook()->sheetList() ); for( ; it.current(); ++it ) { for(int i=rect.left();i<=rect.right();i++) it.current()->changeNameCellRef( TQPoint( i, rect.top() ), false, Sheet::RowInsert, name(), ( rect.bottom() - rect.top() + 1 ), undo ); } refreshChart(/*marker*/TQPoint(rect.left(),rect.top()), false, Sheet::RowInsert); refreshMergedCell(); recalc(); emit sig_updateView( this ); return res; } void Sheet::unshiftColumn( const TQRect & rect,bool makeUndo ) { UndoRemoveCellCol * undo = 0; if ( !doc()->undoLocked() && makeUndo ) { undo = new UndoRemoveCellCol( doc(), this, rect ); doc()->addCommand( undo ); } for(int i =rect.top();i<=rect.bottom();i++) for(int j=rect.left();j<=rect.right();j++) d->cells.remove(j,i); for(int i =rect.left();i<=rect.right();i++) for(int j=0;j<=(rect.bottom()-rect.top());j++) d->cells.unshiftColumn( TQPoint(i,rect.top()) ); TQPtrListIterator it( workbook()->sheetList() ); for( ; it.current(); ++it ) for(int i=rect.left();i<=rect.right();i++) it.current()->changeNameCellRef( TQPoint( i, rect.top() ), false, Sheet::RowRemove, name(), ( rect.bottom() - rect.top() + 1 ), undo ); refreshChart( TQPoint(rect.left(),rect.top()), false, Sheet::RowRemove ); refreshMergedCell(); recalc(); emit sig_updateView( this ); } void Sheet::unshiftRow( const TQRect & rect,bool makeUndo ) { UndoRemoveCellRow * undo = 0; if ( !doc()->undoLocked() && makeUndo ) { undo = new UndoRemoveCellRow( doc(), this, rect ); doc()->addCommand( undo ); } for(int i =rect.top();i<=rect.bottom();i++) for(int j=rect.left();j<=rect.right();j++) d->cells.remove(j,i); for(int i =rect.top();i<=rect.bottom();i++) for(int j=0;j<=(rect.right()-rect.left());j++) d->cells.unshiftRow( TQPoint(rect.left(),i) ); TQPtrListIterator it( workbook()->sheetList() ); for( ; it.current(); ++it ) for(int i=rect.top();i<=rect.bottom();i++) it.current()->changeNameCellRef( TQPoint( rect.left(), i ), false, Sheet::ColumnRemove, name(), ( rect.right() - rect.left() + 1 ), undo); refreshChart(TQPoint(rect.left(),rect.top()), false, Sheet::ColumnRemove ); refreshMergedCell(); recalc(); emit sig_updateView( this ); } bool Sheet::insertColumn( int col, int nbCol, bool makeUndo ) { UndoInsertColumn * undo = 0; if ( !doc()->undoLocked() && makeUndo) { undo = new UndoInsertColumn( doc(), this, col, nbCol ); doc()->addCommand( undo ); } bool res=true; bool result; for( int i=0; i<=nbCol; i++ ) { // Recalculate range max (minus size of last column) d->sizeMaxX -= columnFormat( KS_colMax )->dblWidth(); result = d->cells.insertColumn( col ); d->columns.insertColumn( col ); if(!result) res = false; //Recalculate range max (plus size of new column) d->sizeMaxX += columnFormat( col+i )->dblWidth(); } TQPtrListIterator it( workbook()->sheetList() ); for( ; it.current(); ++it ) it.current()->changeNameCellRef( TQPoint( col, 1 ), true, Sheet::ColumnInsert, name(), nbCol + 1, undo ); //update print settings d->print->insertColumn( col, nbCol ); refreshChart( TQPoint( col, 1 ), true, Sheet::ColumnInsert ); refreshMergedCell(); recalc(); emit sig_updateHBorder( this ); emit sig_updateView( this ); return res; } bool Sheet::insertRow( int row, int nbRow, bool makeUndo ) { UndoInsertRow *undo = 0; if ( !doc()->undoLocked() && makeUndo) { undo = new UndoInsertRow( doc(), this, row, nbRow ); doc()->addCommand( undo ); } bool res=true; bool result; for( int i=0; i<=nbRow; i++ ) { // Recalculate range max (minus size of last row) d->sizeMaxY -= rowFormat( KS_rowMax )->dblHeight(); result = d->cells.insertRow( row ); d->rows.insertRow( row ); if( !result ) res = false; //Recalculate range max (plus size of new row) d->sizeMaxY += rowFormat( row )->dblHeight(); } TQPtrListIterator it( workbook()->sheetList() ); for( ; it.current(); ++it ) it.current()->changeNameCellRef( TQPoint( 1, row ), true, Sheet::RowInsert, name(), nbRow + 1, undo ); //update print settings d->print->insertRow( row, nbRow ); refreshChart( TQPoint( 1, row ), true, Sheet::RowInsert ); refreshMergedCell(); recalc(); emit sig_updateVBorder( this ); emit sig_updateView( this ); return res; } void Sheet::removeColumn( int col, int nbCol, bool makeUndo ) { UndoRemoveColumn *undo = 0; if ( !doc()->undoLocked() && makeUndo) { undo = new UndoRemoveColumn( doc(), this, col, nbCol ); doc()->addCommand( undo ); } for ( int i = 0; i <= nbCol; ++i ) { // Recalculate range max (minus size of removed column) d->sizeMaxX -= columnFormat( col )->dblWidth(); d->cells.removeColumn( col ); d->columns.removeColumn( col ); //Recalculate range max (plus size of new column) d->sizeMaxX += columnFormat( KS_colMax )->dblWidth(); } TQPtrListIterator it( workbook()->sheetList() ); for( ; it.current(); ++it ) it.current()->changeNameCellRef( TQPoint( col, 1 ), true, Sheet::ColumnRemove, name(), nbCol + 1, undo ); //update print settings d->print->removeColumn( col, nbCol ); refreshChart( TQPoint( col, 1 ), true, Sheet::ColumnRemove ); refreshMergedCell(); recalc(); emit sig_updateHBorder( this ); emit sig_updateView( this ); } void Sheet::removeRow( int row, int nbRow, bool makeUndo ) { UndoRemoveRow *undo = 0; if ( !doc()->undoLocked() && makeUndo ) { undo = new UndoRemoveRow( doc(), this, row, nbRow ); doc()->addCommand( undo ); } for( int i=0; i<=nbRow; i++ ) { // Recalculate range max (minus size of removed row) d->sizeMaxY -= rowFormat( row )->dblHeight(); d->cells.removeRow( row ); d->rows.removeRow( row ); //Recalculate range max (plus size of new row) d->sizeMaxY += rowFormat( KS_rowMax )->dblHeight(); } TQPtrListIterator it( workbook()->sheetList() ); for( ; it.current(); ++it ) it.current()->changeNameCellRef( TQPoint( 1, row ), true, Sheet::RowRemove, name(), nbRow + 1, undo ); //update print settings d->print->removeRow( row, nbRow ); refreshChart( TQPoint( 1, row ), true, Sheet::RowRemove ); refreshMergedCell(); recalc(); emit sig_updateVBorder( this ); emit sig_updateView( this ); } void Sheet::hideRow(const Region& region) { HideShowManipulator* manipulator = new HideShowManipulator(); manipulator->setSheet(this); manipulator->setManipulateRows(true); manipulator->add(region); manipulator->execute(); } void Sheet::emitHideRow() { emit sig_updateVBorder( this ); emit sig_updateView( this ); } void Sheet::showRow(const Region& region) { HideShowManipulator* manipulator = new HideShowManipulator(); manipulator->setSheet(this); manipulator->setManipulateRows(true); manipulator->setReverse(true); manipulator->add(region); manipulator->execute(); } void Sheet::hideColumn(const Region& region) { HideShowManipulator* manipulator = new HideShowManipulator(); manipulator->setSheet(this); manipulator->setManipulateColumns(true); manipulator->add(region); manipulator->execute(); } void Sheet::emitHideColumn() { emit sig_updateHBorder( this ); emit sig_updateView( this ); } void Sheet::showColumn(const Region& region) { HideShowManipulator* manipulator = new HideShowManipulator(); manipulator->setSheet(this); manipulator->setManipulateColumns(true); manipulator->setReverse(true); manipulator->add(region); manipulator->execute(); } void Sheet::refreshChart(const TQPoint & pos, bool fullRowOrColumn, ChangeRef ref) { Cell * c = d->cells.firstCell(); for( ;c; c = c->nextCell() ) { if ( (ref == ColumnInsert || ref == ColumnRemove) && fullRowOrColumn && c->column() >= (pos.x() - 1)) { if (c->updateChart()) return; } else if ( (ref == ColumnInsert || ref == ColumnRemove )&& !fullRowOrColumn && c->column() >= (pos.x() - 1) && c->row() == pos.y() ) { if (c->updateChart()) return; } else if ((ref == RowInsert || ref == RowRemove) && fullRowOrColumn && c->row() >= (pos.y() - 1)) { if (c->updateChart()) return; } else if ( (ref == RowInsert || ref == RowRemove) && !fullRowOrColumn && c->column() == pos.x() && c->row() >= (pos.y() - 1) ) { if (c->updateChart()) return; } } //refresh chart when there is a chart and you remove //all cells if (c == 0L) { CellBinding * bind; for ( bind = firstCellBinding(); bind != 0L; bind = nextCellBinding() ) { bind->cellChanged( 0 ); } // CellBinding * bind = firstCellBinding(); // if ( bind != 0L ) // bind->cellChanged( 0 ); } } void Sheet::refreshMergedCell() { Cell* c = d->cells.firstCell(); for( ;c; c = c->nextCell() ) { if(c->doesMergeCells()) c->mergeCells( c->column(), c->row(), c->extraXCells(), c->extraYCells() ); } } void Sheet::changeNameCellRef( const TQPoint & pos, bool fullRowOrColumn, ChangeRef ref, TQString tabname, int nbCol, UndoInsertRemoveAction * undo ) { bool correctDefaultSheetName = (tabname == name()); // for cells without sheet ref (eg "A1") Cell* c = d->cells.firstCell(); for( ;c; c = c->nextCell() ) { if( c->isFormula() ) { TQString origText = c->text(); unsigned int i = 0; bool error = false; TQString newText; bool correctSheetName = correctDefaultSheetName; //bool previousCorrectSheetName = false; TQChar origCh; for ( ; i < origText.length(); ++i ) { origCh = origText[i]; if ( origCh != ':' && origCh != '$' && !origCh.isLetter() ) { newText += origCh; // Reset the "correct table indicator" correctSheetName = correctDefaultSheetName; } else // Letter or dollar : maybe start of cell name/range // (or even ':', like in a range - note that correctSheet is kept in this case) { // Collect everything that forms a name (cell name or sheet name) TQString str; bool sheetNameFound = false; //Sheet names need spaces for( ; ( i < origText.length() ) && // until the end ( ( origText[i].isLetter() || origText[i].isDigit() || origText[i] == '$' ) || // all text and numbers are welcome ( sheetNameFound && origText[i].isSpace() ) ) //in case of a sheet name, we include spaces too ; ++i ) { str += origText[i]; if ( origText[i] == '!' ) sheetNameFound = true; } // Was it a sheet name ? if ( origText[i] == '!' ) { newText += str + '!'; // Copy it (and the '!') // Look for the sheet name right before that '!' correctSheetName = ( newText.right( tabname.length()+1 ) == tabname+"!" ); } else // It must be a cell identifier { // Parse it Point point( str ); if ( point.isValid() ) { int col = point.pos().x(); int row = point.pos().y(); TQString newPoint; // Update column if ( point.columnFixed() ) newPoint = '$'; if( ref == ColumnInsert && correctSheetName && col + nbCol <= KS_colMax && col >= pos.x() // Column after the new one : +1 && ( fullRowOrColumn || row == pos.y() ) ) // All rows or just one { newPoint += Cell::columnName( col + nbCol ); } else if( ref == ColumnRemove && correctSheetName && col > pos.x() // Column after the deleted one : -1 && ( fullRowOrColumn || row == pos.y() ) ) // All rows or just one { newPoint += Cell::columnName( col - nbCol ); } else newPoint += Cell::columnName( col ); // Update row if ( point.rowFixed() ) newPoint += '$'; if( ref == RowInsert && correctSheetName && row + nbCol <= KS_rowMax && row >= pos.y() // Row after the new one : +1 && ( fullRowOrColumn || col == pos.x() ) ) // All columns or just one { newPoint += TQString::number( row + nbCol ); } else if( ref == RowRemove && correctSheetName && row > pos.y() // Row after the deleted one : -1 && ( fullRowOrColumn || col == pos.x() ) ) // All columns or just one { newPoint += TQString::number( row - nbCol ); } else newPoint += TQString::number( row ); if( correctSheetName && ( ( ref == ColumnRemove && col == pos.x() // Column is the deleted one : error && ( fullRowOrColumn || row == pos.y() ) ) || ( ref == RowRemove && row == pos.y() // Row is the deleted one : error && ( fullRowOrColumn || col == pos.x() ) ) || ( ref == ColumnInsert && col + nbCol > KS_colMax && col >= pos.x() // Column after the new one : +1 && ( fullRowOrColumn || row == pos.y() ) ) || ( ref == RowInsert && row + nbCol > KS_rowMax && row >= pos.y() // Row after the new one : +1 && ( fullRowOrColumn || col == pos.x() ) ) ) ) { newPoint = "#" + i18n("Dependency") + "!"; error = true; } newText += newPoint; } else // Not a cell ref { kdDebug(36001) << "Copying (unchanged) : '" << str << "'" << endl; newText += str; } // Copy the char that got us to stop if ( i < origText.length() ) { newText += origText[i]; if( origText[i] != ':' ) correctSheetName = correctDefaultSheetName; } } } } if ( error && undo != 0 ) //Save the original formula, as we cannot calculate the undo of broken formulas { TQString formulaText = c->text(); int origCol = c->column(); int origRow = c->row(); if ( ref == ColumnInsert && origCol >= pos.x() ) origCol -= nbCol; if ( ref == RowInsert && origRow >= pos.y() ) origRow -= nbCol; if ( ref == ColumnRemove && origCol >= pos.x() ) origCol += nbCol; if ( ref == RowRemove && origRow >= pos.y() ) origRow += nbCol; undo->saveFormulaReference( this, origCol, origRow, formulaText ); } c->setCellText( newText ); } } } #if 0 void Sheet::replace( const TQString &_find, const TQString &_replace, long options, Canvas *canvas ) { Selection* selectionInfo = canvas->view()->selectionInfo(); // Identify the region of interest. TQRect region( selectionInfo->selection() ); TQPoint marker( selectionInfo->marker() ); if (options & KReplaceDialog::SelectedText) { // Complete rows selected ? if ( util_isRowSelected(region) ) { } // Complete columns selected ? else if ( util_isColumnSelected(region) ) { } } else { // All cells. region.setCoords( 1, 1, d->maxRow, d->maxColumn ); } // Create the class that handles all the actual replace stuff, and connect it to its // local slots. KReplace dialog( _find, _replace, options ); TQObject::connect( &dialog, TQ_SIGNAL( highlight( const TQString &, int, int, const TQRect & ) ), canvas, TQ_SLOT( highlight( const TQString &, int, int, const TQRect & ) ) ); TQObject::connect( &dialog, TQ_SIGNAL( replace( const TQString &, int, int,int, const TQRect & ) ), canvas, TQ_SLOT( replace( const TQString &, int, int,int, const TQRect & ) ) ); // Now do the replacing... if ( !doc()->undoLocked() ) { UndoChangeAreaTextCell *undo = new UndoChangeAreaTextCell( doc(), this, region ); doc()->addCommand( undo ); } TQRect cellRegion( 0, 0, 0, 0 ); bool bck = options & KFindDialog::FindBackwards; int colStart = !bck ? region.left() : region.right(); int colEnd = !bck ? region.right() : region.left(); int rowStart = !bck ? region.top() :region.bottom(); int rowEnd = !bck ? region.bottom() : region.top(); if ( options & KFindDialog::FromCursor ) { colStart = marker.x(); rowStart = marker.y(); } Cell *cell; for (int row = rowStart ; !bck ? row < rowEnd : row > rowEnd ; !bck ? ++row : --row ) { for(int col = colStart ; !bck ? col < colEnd : col > colEnd ; !bck ? ++col : --col ) { cell = cellAt( col, row ); if ( !cell->isDefault() && !cell->isObscured() && !cell->isFormula() ) { TQString text = cell->text(); cellRegion.setTop( row ); cellRegion.setLeft( col ); if (!dialog.replace( text, cellRegion )) return; } } } } #endif void Sheet::borderBottom( Selection* selectionInfo, const TQColor &_color ) { BorderManipulator* manipulator = new BorderManipulator(); manipulator->setSheet(this); manipulator->setBottomBorderPen(TQPen(_color, 1, TQt::SolidLine)); manipulator->add(*selectionInfo); manipulator->execute(); } void Sheet::borderRight( Selection* selectionInfo, const TQColor &_color ) { BorderManipulator* manipulator = new BorderManipulator(); manipulator->setSheet(this); manipulator->setRightBorderPen(TQPen(_color, 1, TQt::SolidLine)); manipulator->add(*selectionInfo); manipulator->execute(); } void Sheet::borderLeft( Selection* selectionInfo, const TQColor &_color ) { BorderManipulator* manipulator = new BorderManipulator(); manipulator->setSheet(this); manipulator->setLeftBorderPen(TQPen(_color, 1, TQt::SolidLine)); manipulator->add(*selectionInfo); manipulator->execute(); } void Sheet::borderTop( Selection* selectionInfo, const TQColor &_color ) { BorderManipulator* manipulator = new BorderManipulator(); manipulator->setSheet(this); manipulator->setTopBorderPen(TQPen(_color, 1, TQt::SolidLine)); manipulator->add(*selectionInfo); manipulator->execute(); } void Sheet::borderOutline( Selection* selectionInfo, const TQColor &_color ) { BorderManipulator* manipulator = new BorderManipulator(); manipulator->setSheet(this); manipulator->setTopBorderPen(TQPen(_color, 1, TQt::SolidLine)); manipulator->setBottomBorderPen(TQPen(_color, 1, TQt::SolidLine)); manipulator->setLeftBorderPen(TQPen(_color, 1, TQt::SolidLine)); manipulator->setRightBorderPen(TQPen(_color, 1, TQt::SolidLine)); manipulator->add(*selectionInfo); manipulator->execute(); } void Sheet::borderAll( Selection * selectionInfo, const TQColor & _color ) { BorderManipulator* manipulator = new BorderManipulator(); manipulator->setSheet(this); manipulator->setTopBorderPen(TQPen(_color, 1, TQt::SolidLine)); manipulator->setBottomBorderPen(TQPen(_color, 1, TQt::SolidLine)); manipulator->setLeftBorderPen(TQPen(_color, 1, TQt::SolidLine)); manipulator->setRightBorderPen(TQPen(_color, 1, TQt::SolidLine)); manipulator->setHorizontalPen(TQPen(_color, 1, TQt::SolidLine)); manipulator->setVerticalPen(TQPen(_color, 1, TQt::SolidLine)); manipulator->add(*selectionInfo); manipulator->execute(); } void Sheet::borderRemove( Selection* selectionInfo ) { BorderManipulator* manipulator = new BorderManipulator(); manipulator->setSheet(this); manipulator->setTopBorderPen(TQPen(TQt::NoPen)); manipulator->setBottomBorderPen(TQPen(TQt::NoPen)); manipulator->setLeftBorderPen(TQPen(TQt::NoPen)); manipulator->setRightBorderPen(TQPen(TQt::NoPen)); manipulator->setHorizontalPen(TQPen(TQt::NoPen)); manipulator->setVerticalPen(TQPen(TQt::NoPen)); manipulator->add(*selectionInfo); manipulator->execute(); } void Sheet::sortByRow( const TQRect &area, int ref_row, SortingOrder mode ) { Point point; point.setSheet(this); point.setSheetName (d->name); point.setPos(area.topLeft()); point.setColumnFixed(false); point.setRowFixed(false); sortByRow( area, ref_row, 0, 0, mode, mode, mode, 0, false, false, point,true ); } void Sheet::sortByColumn( const TQRect &area, int ref_column, SortingOrder mode ) { Point point; point.setSheet(this); point.setSheetName(d->name); point.setPos(area.topLeft()); point.setColumnFixed(false); point.setRowFixed(false); sortByColumn( area, ref_column, 0, 0, mode, mode, mode, 0, false, false, point,true ); } void Sheet::checkCellContent(Cell * cell1, Cell * cell2, int & ret) { if ( cell1->isEmpty() ) { ret = 1; return; } else if ( cell1->isObscured() && cell1->isPartOfMerged() ) { ret = 1; return; } else if ( cell2->isEmpty() ) { ret = 2; return; } ret = 0; } void Sheet::sortByRow( const TQRect &area, int key1, int key2, int key3, SortingOrder order1, SortingOrder order2, SortingOrder order3, TQStringList const * firstKey, bool copyFormat, bool headerRow, Point const & outputPoint, bool respectCase ) { TQRect r( area ); Map::respectCase = respectCase; Q_ASSERT( order1 == Increase || order1 == Decrease ); // It may not happen that entire columns are selected. Q_ASSERT( util_isColumnSelected(r) == false ); // Are entire rows selected ? if ( util_isRowSelected(r) ) { r.setLeft( KS_colMax ); r.setRight( 0 ); // Determine a correct left and right. // Iterate over all cells to find out which cells are // located in the selected rows. for ( int row = r.top(); row <= r.bottom(); ++row ) { Cell * c = getFirstCellRow( row ); int col; while ( c ) { col = c->column(); if ( !c->isEmpty() ) { if ( col > r.right() ) r.rRight() = col; if ( col < r.left() ) r.rLeft() = col; } c = getNextCellRight( col, row ); } } // Any cells to sort here ? if ( r.right() < r.left() ) { Map::respectCase = true; return; } } TQRect target( outputPoint.pos().x(), outputPoint.pos().y(), r.width(), r.height() ); doc()->emitBeginOperation(); if ( !doc()->undoLocked() ) { UndoSort *undo = new UndoSort( doc(), this, target ); doc()->addCommand( undo ); } if (target.topLeft() != r.topLeft()) { int targetLeft = target.left(); int targetTop = target.top(); int sourceTop = r.top(); int sourceLeft = r.left(); key1 = key1 - sourceTop + targetTop; key2 = key2 - sourceTop + targetTop; key3 = key3 - sourceTop + targetTop; for ( int x = 0; x < r.width(); ++x) { for ( int y = 0; y < r.height(); ++y ) { // from - to copyCells( sourceLeft + x, sourceTop + y, targetLeft + x, targetTop + y, copyFormat ); } } } // Sorting algorithm: David's :). Well, I guess it's called minmax or so. // For each column, we look for all cells right hand of it and we find the one to swap with it. // Much faster than the awful bubbleSort... Cell * cell; Cell * cell1; Cell * cell2; Cell * bestCell; int status = 0; for ( int d = target.left(); d <= target.right(); ++d ) { cell1 = cellAt( d, key1 ); if ( cell1->isObscured() && cell1->isPartOfMerged() ) { Cell* obscuring = cell1->obscuringCells().first(); cell = cellAt( obscuring->column(), key1 ); cell1 = cellAt( obscuring->column() + cell->extraXCells() + 1, obscuring->column()); d = obscuring->column() + cell->extraXCells() + 1; } // Look for which column we want to swap with the one number d bestCell = cell1; int bestX = d; for ( int x = d + 1 ; x <= target.right(); x++ ) { cell2 = cellAt( x, key1 ); checkCellContent(cell2, bestCell, status); if (status == 1) continue; else if (status == 2) { // empty cells are always shifted to the end bestCell = cell2; bestX = x; continue; } if ( firstKey ) { int i1 = firstKey->findIndex( cell2->text() ); int i2 = firstKey->findIndex( bestCell->text() ); if ( i1 != -1 && i2 != -1 ) { if ( (order1 == Increase && i1 < i2 ) || (order1 == Decrease && i1 > i2) ) { bestCell = cell2; bestX = x; continue; } if ( i1 == i2 ) { // check 2nd key if (key2 <= 0) continue; Cell * cell22 = cellAt( x, key2 ); Cell * bestCell2 = cellAt( bestX, key2 ); if ( cell22->isEmpty() ) { /* No need to swap */ continue; } else if ( cell22->isObscured() && cell22->isPartOfMerged() ) { /* No need to swap */ continue; } else if ( bestCell2->isEmpty() ) { // empty cells are always shifted to the end bestCell = cell2; bestX = x; continue; } if ( (order2 == Increase && *cell22 < *bestCell2) || (order2 == Decrease && *cell22 > *bestCell2) ) { bestCell = cell2; bestX = x; continue; } else if ( (order2 == Increase && *cell22 > *bestCell2) || (order2 == Decrease && *cell22 < *bestCell2) ) { // already in right order continue; } else { // they are equal, check 3rd key if (key3 <= 0) continue; Cell * cell23 = cellAt( x, key3 ); Cell * bestCell3 = cellAt( bestX, key3 ); if ( cell23->isEmpty() ) { /* No need to swap */ continue; } else if ( cell23->isObscured() && cell23->isPartOfMerged() ) { /* No need to swap */ continue; } else if ( bestCell3->isEmpty() ) { // empty cells are always shifted to the end bestCell = cell2; bestX = x; continue; } if ( (order3 == Increase && *cell23 < *bestCell3) || (order3 == Decrease && *cell23 > *bestCell3) ) { // they are really equal or in the right order // no swap necessary continue; } else { bestCell = cell2; bestX = x; continue; } } } continue; } else if ( i1 != -1 && i2 == -1 ) { // if not in the key list, the cell is shifted to the end - always bestCell = cell2; bestX = x; continue; } else if ( i2 != -1 && i1 == -1 ) { // only text of cell2 is in the list so it is smaller than bestCell /* No need to swap */ continue; } // if i1 and i2 are equals -1 go on: } // end if (firstKey) // Here we use the operators < and > for cells, which do it all. if ( (order1 == Increase && *cell2 < *bestCell) || (order1 == Decrease && *cell2 > *bestCell) ) { bestCell = cell2; bestX = x; continue; } else if ( (order1 == Increase && *cell2 > *bestCell) || (order1 == Decrease && *cell2 < *bestCell) ) { // no change necessary continue; } else { // *cell2 equals *bestCell // check 2nd key if (key2 <= 0) continue; Cell * cell22 = cellAt( d, key2 ); Cell * bestCell2 = cellAt( x, key2 ); checkCellContent(cell2, bestCell, status); if (status == 1) continue; else if (status == 2) { // empty cells are always shifted to the end bestCell = cell2; bestX = x; continue; } if ( (order2 == Increase && *cell22 > *bestCell2) || (order2 == Decrease && *cell22 < *bestCell2) ) { bestCell = cell2; bestX = x; continue; } else if ( (order2 == Increase && *cell22 > *bestCell2) || (order2 == Decrease && *cell22 < *bestCell2) ) { // already in right order continue; } else { // they are equal, check 3rd key if (key3 == 0) continue; Cell * cell23 = cellAt( d, key3 ); Cell * bestCell3 = cellAt( x, key3 ); checkCellContent(cell2, bestCell, status); if (status == 1) continue; else if (status == 2) { // empty cells are always shifted to the end bestCell = cell2; bestX = x; continue; } if ( (order3 == Increase && *cell23 > *bestCell3) || (order3 == Decrease && *cell23 < *bestCell3) ) { bestCell = cell2; bestX = x; continue; } else { // they are really equal // no swap necessary continue; } } } } // Swap columns cell1 and bestCell (i.e. d and bestX) if ( d != bestX ) { int top = target.top(); if (headerRow) ++top; for( int y = target.bottom(); y >= top; --y ) { if ( y != key1 && y != key2 && y != key3 ) swapCells( d, y, bestX, y, copyFormat ); } if (key3 > 0) swapCells( d, key3, bestX, key3, copyFormat ); if (key2 > 0) swapCells( d, key2, bestX, key2, copyFormat ); swapCells( d, key1, bestX, key1, copyFormat ); } } // for (d = ...; ...; ++d) Map::respectCase = true; // doc()->emitEndOperation(); emit sig_updateView( this ); } void Sheet::sortByColumn( const TQRect &area, int key1, int key2, int key3, SortingOrder order1, SortingOrder order2, SortingOrder order3, TQStringList const * firstKey, bool copyFormat, bool headerRow, Point const & outputPoint, bool respectCase ) { TQRect r( area ); Map::respectCase = respectCase; Q_ASSERT( order1 == Increase || order1 == Decrease ); // It may not happen that entire rows are selected. Q_ASSERT( util_isRowSelected(r) == false ); // Are entire columns selected ? if ( util_isColumnSelected(r) ) { r.setTop( KS_rowMax ); r.setBottom( 0 ); // Determine a correct top and bottom. // Iterate over all cells to find out which cells are // located in the selected columns. for ( int col = r.left(); col <= r.right(); ++col ) { Cell * c = getFirstCellColumn( col ); int row; while ( c ) { row = c->row(); if ( !c->isEmpty() ) { if ( row > r.bottom() ) r.rBottom() = row; if ( row < r.top() ) r.rTop() = row; } c = getNextCellDown( col, row ); } } // Any cells to sort here ? if ( r.bottom() < r.top() ) { Map::respectCase = true; return; } } TQRect target( outputPoint.pos().x(), outputPoint.pos().y(), r.width(), r.height() ); if ( !doc()->undoLocked() ) { UndoSort *undo = new UndoSort( doc(), this, target ); doc()->addCommand( undo ); } doc()->emitBeginOperation(); if (target.topLeft() != r.topLeft()) { int targetLeft = target.left(); int targetTop = target.top(); int sourceTop = r.top(); int sourceLeft = r.left(); key1 = key1 - sourceLeft + targetLeft; key2 = key2 - sourceLeft + targetLeft; key3 = key3 - sourceLeft + targetLeft; for ( int x = 0; x < r.width(); ++x) { for ( int y = 0; y < r.height(); ++y ) { // from - to copyCells( sourceLeft + x, sourceTop + y, targetLeft + x, targetTop + y, copyFormat ); } } } // Sorting algorithm: David's :). Well, I guess it's called minmax or so. // For each row, we look for all rows under it and we find the one to swap with it. // Much faster than the awful bubbleSort... // Torben: Asymptotically it is alltogether O(n^2) :-) Cell * cell; Cell * cell1; Cell * cell2; Cell * bestCell; int status = 0; int d = target.top(); if (headerRow) ++d; for ( ; d <= target.bottom(); ++d ) { // Look for which row we want to swap with the one number d cell1 = cellAt( key1, d ); if ( cell1->isObscured() && cell1->isPartOfMerged() ) { Cell* obscuring = cell1->obscuringCells().first(); cell = cellAt( key1, obscuring->row() ); cell1 = cellAt( key1, obscuring->row() + cell->extraYCells() + 1 ); d = obscuring->row() + cell->extraYCells() + 1; } bestCell = cell1; int bestY = d; for ( int y = d + 1 ; y <= target.bottom(); ++y ) { cell2 = cellAt( key1, y ); if ( cell2->isEmpty() ) { /* No need to swap */ continue; } else if ( cell2->isObscured() && cell2->isPartOfMerged() ) { /* No need to swap */ continue; } else if ( bestCell->isEmpty() ) { // empty cells are always shifted to the end bestCell = cell2; bestY = y; continue; } if ( firstKey ) { int i1 = firstKey->findIndex( cell2->text() ); int i2 = firstKey->findIndex( bestCell->text() ); if ( i1 != -1 && i2 != -1 ) { if ( (order1 == Increase && i1 < i2 ) || (order1 == Decrease && i1 > i2) ) { bestCell = cell2; bestY = y; continue; } if ( i1 == i2 ) { // check 2nd key if (key2 <= 0) continue; Cell * cell22 = cellAt( key2, d ); Cell * bestCell2 = cellAt( key2, y ); if ( cell22->isEmpty() ) { /* No need to swap */ continue; } else if ( cell22->isObscured() && cell22->isPartOfMerged() ) { /* No need to swap */ continue; } else if ( bestCell2->isEmpty() ) { // empty cells are always shifted to the end bestCell = cell2; bestY = y; continue; } if ( (order2 == Increase && *cell22 > *bestCell2) || (order2 == Decrease && *cell22 < *bestCell2) ) { bestCell = cell2; bestY = y; continue; } else if ( (order2 == Increase && *cell22 < *bestCell2) || (order2 == Decrease && *cell22 > *bestCell2) ) { // already in right order continue; } else { // they are equal, check 3rd key if (key3 <= 0) continue; Cell * cell23 = cellAt( key3, d ); Cell * bestCell3 = cellAt( key3, y ); checkCellContent(cell2, bestCell, status); if (status == 1) continue; else if (status == 2) { // empty cells are always shifted to the end bestCell = cell2; bestY = y; continue; } if ( (order3 == Increase && *cell23 < *bestCell3) || (order3 == Decrease && *cell23 > *bestCell3) ) { bestCell = cell2; bestY = y; continue; } else { // they are really equal or in the correct order // no swap necessary continue; } } } continue; } else if ( i1 != -1 && i2 == -1 ) { // if not in the key list, the cell is shifted to the end - always bestCell = cell2; bestY = y; continue; } else if ( i2 != -1 && i1 == -1 ) { // only text of cell2 is in the list so it is smaller than bestCell /* No need to swap */ continue; } // if i1 and i2 are equals -1 go on: } // if (firstKey) // Here we use the operators < and > for cells, which do it all. if ( (order1 == Increase && *cell2 < *bestCell) || (order1 == Decrease && *cell2 > *bestCell) ) { bestCell = cell2; bestY = y; } else if ( (order1 == Increase && *cell2 > *bestCell) || (order1 == Decrease && *cell2 < *bestCell) ) { // no change necessary continue; } else { // *cell2 equals *bestCell // check 2nd key if (key2 == 0) continue; Cell * cell22 = cellAt( key2, y ); Cell * bestCell2 = cellAt( key2, bestY ); if ( cell22->isEmpty() ) { /* No need to swap */ continue; } else if ( cell22->isObscured() && cell22->isPartOfMerged() ) { /* No need to swap */ continue; } else if ( bestCell2->isEmpty() ) { // empty cells are always shifted to the end bestCell = cell2; bestY = y; continue; } if ( (order2 == Increase && *cell22 < *bestCell2) || (order2 == Decrease && *cell22 > *bestCell2) ) { bestCell = cell2; bestY = y; continue; } else if ( (order2 == Increase && *cell22 > *bestCell2) || (order2 == Decrease && *cell22 < *bestCell2) ) { continue; } else { // they are equal, check 3rd key if (key3 == 0) continue; Cell * cell23 = cellAt( key3, y ); Cell * bestCell3 = cellAt( key3, bestY ); if ( cell23->isEmpty() ) { /* No need to swap */ continue; } else if ( cell23->isObscured() && cell23->isPartOfMerged() ) { /* No need to swap */ continue; } else if ( bestCell3->isEmpty() ) { // empty cells are always shifted to the end bestCell = cell2; bestY = y; continue; } if ( (order3 == Increase && *cell23 < *bestCell3) || (order3 == Decrease && *cell23 > *bestCell3) ) { bestCell = cell2; bestY = y; continue; } else { // they are really equal or already in the correct order // no swap necessary continue; } } } } // Swap rows cell1 and bestCell (i.e. d and bestY) if ( d != bestY ) { for (int x = target.left(); x <= target.right(); ++x) { if ( x != key1 && x != key2 && x != key3) swapCells( x, d, x, bestY, copyFormat ); } if (key3 > 0) swapCells( key3, d, key3, bestY, copyFormat ); if (key2 > 0) swapCells( key2, d, key2, bestY, copyFormat ); swapCells( key1, d, key1, bestY, copyFormat ); } } // for (d = ...; ...; ++d) // doc()->emitEndOperation(); Map::respectCase = true; emit sig_updateView( this ); } // from - to - copyFormat void Sheet::copyCells( int x1, int y1, int x2, int y2, bool cpFormat ) { Cell * sourceCell = cellAt( x1, y1 ); Cell * targetCell = cellAt( x2, y2 ); if ( sourceCell->isDefault() && targetCell->isDefault()) { // if the source and target is default there is nothing to copy return; } targetCell = nonDefaultCell(x2, y2); // TODO: check if this enough targetCell->copyContent( sourceCell ); /* if ( !sourceCell->isFormula() ) { targetCell->copyContent( sourceCell ); } else { targetCell->setCellText( targetCell->decodeFormula( sourceCell->encodeFormula() ) ); targetCell->setCalcDirtyFlag(); targetCell->calc(false); } */ if (cpFormat) { targetCell->copyFormat( sourceCell ); /* targetCell->setAlign( sourceCell->format()->align( x1, y1 ) ); targetCell->setAlignY( sourceCell->format()->alignY( x1, y1 ) ); targetCell->setTextFont( sourceCell->format()->textFont( x1, y1 ) ); targetCell->setTextColor( sourceCell->textColor( x1, y1 ) ); targetCell->setBgColor( sourceCell->bgColor( x1, y1 ) ); targetCell->setLeftBorderPen( sourceCell->leftBorderPen( x1, y1 ) ); targetCell->setTopBorderPen( sourceCell->topBorderPen( x1, y1 ) ); targetCell->setBottomBorderPen( sourceCell->bottomBorderPen( x1, y1 ) ); targetCell->setRightBorderPen( sourceCell->rightBorderPen( x1, y1 ) ); targetCell->setFallDiagonalPen( sourceCell->fallDiagonalPen( x1, y1 ) ); targetCell->setGoUpDiagonalPen( sourceCell->goUpDiagonalPen( x1, y1 ) ); targetCell->setBackGroundBrush( sourceCell->backGroundBrush( x1, y1 ) ); targetCell->setPrecision( sourceCell->precision( x1, y1 ) ); targetCell->format()->setPrefix( sourceCell->prefix( x1, y1 ) ); targetCell->format()->setPostfix( sourceCell->postfix( x1, y1 ) ); targetCell->setFloatFormat( sourceCell->floatFormat( x1, y1 ) ); targetCell->setFloatColor( sourceCell->floatColor( x1, y1 ) ); targetCell->setMultiRow( sourceCell->multiRow( x1, y1 ) ); targetCell->setVerticalText( sourceCell->verticalText( x1, y1 ) ); targetCell->setStyle( sourceCell->style() ); targetCell->setDontPrintText( sourceCell->getDontprintText( x1, y1 ) ); targetCell->setIndent( sourceCell->getIndent( x1, y1 ) ); targetCell->SetConditionList(sourceCell->GetConditionList()); targetCell->setComment( sourceCell->comment( x1, y1 ) ); targetCell->setAngle( sourceCell->getAngle( x1, y1 ) ); targetCell->setFormatType( sourceCell->getFormatType( x1, y1 ) ); */ } } void Sheet::swapCells( int x1, int y1, int x2, int y2, bool cpFormat ) { Cell * ref1 = cellAt( x1, y1 ); Cell * ref2 = cellAt( x2, y2 ); if ( ref1->isDefault() ) { if ( !ref2->isDefault() ) { ref1 = nonDefaultCell( x1, y1 ); // TODO : make ref2 default instead of copying a default cell into it } else return; // nothing to do } else if ( ref2->isDefault() ) { ref2 = nonDefaultCell( x2, y2 ); // TODO : make ref1 default instead of copying a default cell into it } // Dummy cell used for swapping cells. // In fact we copy only content and no layout // information. Imagine sorting in a sheet. Swapping // the format while sorting is not what you would expect // as a user. if (!ref1->isFormula() && !ref2->isFormula()) { Cell *tmp = new Cell( this, -1, -1 ); tmp->copyContent( ref1 ); ref1->copyContent( ref2 ); ref2->copyContent( tmp ); delete tmp; } else if ( ref1->isFormula() && ref2->isFormula() ) { TQString d = ref1->encodeFormula(); ref1->setCellText( ref1->decodeFormula( ref2->encodeFormula( ) ) ); ref1->setCalcDirtyFlag(); ref1->calc(false); ref2->setCellText( ref2->decodeFormula( d ) ); ref2->setCalcDirtyFlag(); ref2->calc(false); } else if (ref1->isFormula() && !ref2->isFormula() ) { TQString d = ref1->encodeFormula(); ref1->setCellText(ref2->text()); ref2->setCellText(ref2->decodeFormula(d)); ref2->setCalcDirtyFlag(); ref2->calc(false); } else if (!ref1->isFormula() && ref2->isFormula() ) { TQString d = ref2->encodeFormula(); ref2->setCellText(ref1->text()); ref1->setCellText(ref1->decodeFormula(d)); ref1->setCalcDirtyFlag(); ref1->calc(false); } if (cpFormat) { Format::Align a = ref1->format()->align( ref1->column(), ref1->row() ); ref1->format()->setAlign( ref2->format()->align( ref2->column(), ref2->row() ) ); ref2->format()->setAlign(a); Format::AlignY ay = ref1->format()->alignY( ref1->column(), ref1->row() ); ref1->format()->setAlignY( ref2->format()->alignY( ref2->column(), ref2->row() ) ); ref2->format()->setAlignY(ay); TQFont textFont = ref1->format()->textFont( ref1->column(), ref1->row() ); ref1->format()->setTextFont( ref2->format()->textFont( ref2->column(), ref2->row() ) ); ref2->format()->setTextFont(textFont); TQColor textColor = ref1->format()->textColor( ref1->column(), ref1->row() ); ref1->format()->setTextColor( ref2->format()->textColor( ref2->column(), ref2->row() ) ); ref2->format()->setTextColor(textColor); TQColor bgColor = ref1->bgColor( ref1->column(), ref1->row() ); ref1->format()->setBgColor( ref2->bgColor( ref2->column(), ref2->row() ) ); ref2->format()->setBgColor(bgColor); TQPen lbp = ref1->leftBorderPen( ref1->column(), ref1->row() ); ref1->setLeftBorderPen( ref2->leftBorderPen( ref2->column(), ref2->row() ) ); ref2->setLeftBorderPen(lbp); TQPen tbp = ref1->topBorderPen( ref1->column(), ref1->row() ); ref1->setTopBorderPen( ref2->topBorderPen( ref2->column(), ref2->row() ) ); ref2->setTopBorderPen(tbp); TQPen bbp = ref1->bottomBorderPen( ref1->column(), ref1->row() ); ref1->setBottomBorderPen( ref2->bottomBorderPen( ref2->column(), ref2->row() ) ); ref2->setBottomBorderPen(bbp); TQPen rbp = ref1->rightBorderPen( ref1->column(), ref1->row() ); ref1->setRightBorderPen( ref2->rightBorderPen( ref2->column(), ref2->row() ) ); ref2->setRightBorderPen(rbp); TQPen fdp = ref1->format()->fallDiagonalPen( ref1->column(), ref1->row() ); ref1->format()->setFallDiagonalPen( ref2->format()->fallDiagonalPen( ref2->column(), ref2->row() ) ); ref2->format()->setFallDiagonalPen(fdp); TQPen udp = ref1->format()->goUpDiagonalPen( ref1->column(), ref1->row() ); ref1->format()->setGoUpDiagonalPen( ref2->format()->goUpDiagonalPen( ref2->column(), ref2->row() ) ); ref2->format()->setGoUpDiagonalPen(udp); TQBrush bgBrush = ref1->backGroundBrush( ref1->column(), ref1->row() ); ref1->format()->setBackGroundBrush( ref2->backGroundBrush( ref2->column(), ref2->row() ) ); ref2->format()->setBackGroundBrush(bgBrush); int pre = ref1->format()->precision( ref1->column(), ref1->row() ); ref1->format()->setPrecision( ref2->format()->precision( ref2->column(), ref2->row() ) ); ref2->format()->setPrecision(pre); TQString prefix = ref1->format()->prefix( ref1->column(), ref1->row() ); ref1->format()->setPrefix( ref2->format()->prefix( ref2->column(), ref2->row() ) ); ref2->format()->setPrefix(prefix); TQString postfix = ref1->format()->postfix( ref1->column(), ref1->row() ); ref1->format()->setPostfix( ref2->format()->postfix( ref2->column(), ref2->row() ) ); ref2->format()->setPostfix(postfix); Format::FloatFormat f = ref1->format()->floatFormat( ref1->column(), ref1->row() ); ref1->format()->setFloatFormat( ref2->format()->floatFormat( ref2->column(), ref2->row() ) ); ref2->format()->setFloatFormat(f); Format::FloatColor c = ref1->format()->floatColor( ref1->column(), ref1->row() ); ref1->format()->setFloatColor( ref2->format()->floatColor( ref2->column(), ref2->row() ) ); ref2->format()->setFloatColor(c); bool multi = ref1->format()->multiRow( ref1->column(), ref1->row() ); ref1->format()->setMultiRow( ref2->format()->multiRow( ref2->column(), ref2->row() ) ); ref2->format()->setMultiRow(multi); bool vert = ref1->format()->verticalText( ref1->column(), ref1->row() ); ref1->format()->setVerticalText( ref2->format()->verticalText( ref2->column(), ref2->row() ) ); ref2->format()->setVerticalText(vert); bool print = ref1->format()->getDontprintText( ref1->column(), ref1->row() ); ref1->format()->setDontPrintText( ref2->format()->getDontprintText( ref2->column(), ref2->row() ) ); ref2->format()->setDontPrintText(print); double ind = ref1->format()->getIndent( ref1->column(), ref1->row() ); ref1->format()->setIndent( ref2->format()->getIndent( ref2->column(), ref2->row() ) ); ref2->format()->setIndent( ind ); TQValueList conditionList = ref1->conditionList(); ref1->setConditionList(ref2->conditionList()); ref2->setConditionList(conditionList); TQString com = ref1->format()->comment( ref1->column(), ref1->row() ); ref1->format()->setComment( ref2->format()->comment( ref2->column(), ref2->row() ) ); ref2->format()->setComment(com); int angle = ref1->format()->getAngle( ref1->column(), ref1->row() ); ref1->format()->setAngle( ref2->format()->getAngle( ref2->column(), ref2->row() ) ); ref2->format()->setAngle(angle); FormatType form = ref1->format()->getFormatType( ref1->column(), ref1->row() ); ref1->format()->setFormatType( ref2->format()->getFormatType( ref2->column(), ref2->row() ) ); ref2->format()->setFormatType(form); } } void Sheet::refreshPreference() { if ( getAutoCalc() ) recalc(); emit sig_updateHBorder( this ); emit sig_updateView( this ); } bool Sheet::areaIsEmpty(const Region& region, TestType _type) { Region::ConstIterator endOfList = region.constEnd(); for (Region::ConstIterator it = region.constBegin(); it != endOfList; ++it) { TQRect range = (*it)->rect().normalize(); // Complete rows selected ? if ((*it)->isRow()) { for ( int row = range.top(); row <= range.bottom(); ++row ) { Cell * c = getFirstCellRow( row ); while ( c ) { if ( !c->isPartOfMerged()) { switch( _type ) { case Text : if ( !c->text().isEmpty()) return false; break; case Validity: if ( c->getValidity(0)) return false; break; case Comment: if ( !c->format()->comment(c->column(), row).isEmpty()) return false; break; case ConditionalCellAttribute: if ( c->conditionList().count()> 0) return false; break; } } c = getNextCellRight( c->column(), row ); } } } // Complete columns selected ? else if ((*it)->isColumn()) { for ( int col = range.left(); col <= range.right(); ++col ) { Cell * c = getFirstCellColumn( col ); while ( c ) { if ( !c->isPartOfMerged() ) { switch( _type ) { case Text : if ( !c->text().isEmpty()) return false; break; case Validity: if ( c->getValidity(0)) return false; break; case Comment: if ( !c->format()->comment(col, c->row()).isEmpty()) return false; break; case ConditionalCellAttribute: if ( c->conditionList().count()> 0) return false; break; } } c = getNextCellDown( col, c->row() ); } } } else { Cell * cell; int right = range.right(); int bottom = range.bottom(); for ( int x = range.left(); x <= right; ++x ) for ( int y = range.top(); y <= bottom; ++y ) { cell = cellAt( x, y ); if (!cell->isPartOfMerged() ) { switch( _type ) { case Text : if ( !cell->text().isEmpty()) return false; break; case Validity: if ( cell->getValidity(0)) return false; break; case Comment: if ( !cell->format()->comment(x, y).isEmpty()) return false; break; case ConditionalCellAttribute: if ( cell->conditionList().count()> 0) return false; break; } } } } } return true; } struct SetSelectionMultiRowWorker : public Sheet::CellWorker { bool enable; SetSelectionMultiRowWorker( bool _enable ) : Sheet::CellWorker( ), enable( _enable ) { } class UndoAction* createUndoAction( Doc * doc, Sheet * sheet, const KSpread::Region& region ) { TQString title = i18n("Multirow"); return new UndoCellFormat( doc, sheet, region, title ); } bool testCondition( Cell * cell ) { return ( !cell->isPartOfMerged() ); } void doWork( Cell * cell, bool, int, int ) { cell->setDisplayDirtyFlag(); cell->format()->setMultiRow( enable ); cell->format()->setVerticalText( false ); cell->format()->setAngle( 0 ); cell->clearDisplayDirtyFlag(); } }; void Sheet::setSelectionMultiRow( Selection* selectionInfo, bool enable ) { SetSelectionMultiRowWorker w( enable ); workOnCells( selectionInfo, w ); } TQString Sheet::guessColumnTitle(TQRect& area, int col) { //Verify range Range rg; rg.setRange(area); rg.setSheet(this); if ( (!rg.isValid()) || (col < area.left()) || (col > area.right())) return TQString(); //The current guess logic is fairly simple - if the top row of the given area //appears to contain headers (ie. there is text in each column) the text in the column at //the top row of the area is returned. /* for (int i=area.left();i<=area.right();i++) { Value cellValue=value(i,area.top()); if (!cellValue.isString()) return TQString(); }*/ Value cellValue=value(col,area.top()); return cellValue.asString(); } TQString Sheet::guessRowTitle(TQRect& area, int row) { //Verify range Range rg; rg.setRange(area); rg.setSheet(this); if ( (!rg.isValid()) || (row < area.top()) || (row > area.bottom()) ) return TQString(); //The current guess logic is fairly simple - if the leftmost column of the given area //appears to contain headers (ie. there is text in each row) the text in the row at //the leftmost column of the area is returned. /*for (int i=area.top();i<=area.bottom();i++) { Value cellValue=value(area.left(),i); if (!cellValue.isString()) return TQString(); }*/ Value cellValue=value(area.left(),row); return cellValue.asString(); } void Sheet::setSelectionAlign( Selection* selectionInfo, Format::Align _align ) { HorAlignManipulator* manipulator = new HorAlignManipulator(); manipulator->setSheet(this); manipulator->setProperty(Format::PAlign); manipulator->setHorizontalAlignment(_align); manipulator->add(*selectionInfo); manipulator->execute(); } void Sheet::setSelectionAlignY( Selection* selectionInfo, Format::AlignY _alignY ) { VerAlignManipulator* manipulator = new VerAlignManipulator(); manipulator->setSheet(this); manipulator->setProperty(Format::PAlignY); manipulator->setVerticalAlignment(_alignY); manipulator->add(*selectionInfo); manipulator->execute(); } struct SetSelectionPrecisionWorker : public Sheet::CellWorker { int _delta; SetSelectionPrecisionWorker( int delta ) : Sheet::CellWorker( ), _delta( delta ) { } class UndoAction* createUndoAction( Doc* doc, Sheet* sheet, const KSpread::Region& region ) { TQString title=i18n("Change Precision"); return new UndoCellFormat( doc, sheet, region, title ); } bool testCondition( Cell* cell ) { return ( !cell->isPartOfMerged() ); } void doWork( Cell* cell, bool, int, int ) { cell->setDisplayDirtyFlag(); if ( _delta == 1 ) cell->incPrecision(); else cell->decPrecision(); cell->clearDisplayDirtyFlag(); } }; void Sheet::setSelectionPrecision( Selection* selectionInfo, int _delta ) { SetSelectionPrecisionWorker w( _delta ); workOnCells( selectionInfo, w ); } struct SetSelectionStyleWorker : public Sheet::CellWorkerTypeA { Style * m_style; SetSelectionStyleWorker( Style * style ) : m_style( style ) { } TQString getUndoTitle() { return i18n("Apply Style"); } void doWork( RowFormat* rw ) { rw->setStyle( m_style ); } void doWork( ColumnFormat* cl ) { cl->setStyle( m_style ); } bool testCondition( Cell* cell ) { return ( !cell->isPartOfMerged() && cell->format()->style() != m_style ); } void doWork( Cell* cell, bool cellRegion, int, int ) { if ( cellRegion ) cell->setDisplayDirtyFlag(); cell->format()->setStyle( m_style ); if ( cellRegion ) cell->clearDisplayDirtyFlag(); } }; void Sheet::setSelectionStyle( Selection * selectionInfo, Style * style ) { SetSelectionStyleWorker w( style ); workOnCells( selectionInfo, w ); } struct SetSelectionMoneyFormatWorker : public Sheet::CellWorkerTypeA { bool b; Doc *m_pDoc; SetSelectionMoneyFormatWorker( bool _b,Doc* _doc ) : b( _b ), m_pDoc(_doc) { } TQString getUndoTitle() { return i18n("Format Money"); } bool testCondition( RowFormat* rw ) { return ( rw->hasProperty( Format::PFormatType ) || rw->hasProperty( Format::PPrecision ) ); } void doWork( RowFormat* rw ) { rw->setFormatType( b ? Money_format : Generic_format ); rw->setPrecision( b ? m_pDoc->locale()->fracDigits() : 0 ); } void doWork( ColumnFormat* cl ) { cl->setFormatType( b ? Money_format : Generic_format ); cl->setPrecision( b ? m_pDoc->locale()->fracDigits() : 0 ); } void prepareCell( Cell* c ) { c->format()->clearProperty( Format::PPrecision ); c->format()->clearNoFallBackProperties( Format::PPrecision ); c->format()->clearProperty( Format::PFormatType ); c->format()->clearNoFallBackProperties( Format::PFormatType ); } bool testCondition( Cell* cell ) { return ( !cell->isPartOfMerged() ); } void doWork( Cell* cell, bool cellRegion, int, int ) { if ( cellRegion ) cell->setDisplayDirtyFlag(); cell->format()->setFormatType( b ? Money_format : Generic_format ); cell->format()->setPrecision( b ? m_pDoc->locale()->fracDigits() : 0 ); if ( cellRegion ) cell->clearDisplayDirtyFlag(); } }; void Sheet::setSelectionMoneyFormat( Selection* selectionInfo, bool b ) { SetSelectionMoneyFormatWorker w( b,doc() ); workOnCells( selectionInfo, w ); } struct IncreaseIndentWorker : public Sheet::CellWorkerTypeA { double tmpIndent; double valIndent; IncreaseIndentWorker( double _tmpIndent, double _valIndent ) : tmpIndent( _tmpIndent ), valIndent( _valIndent ) { } TQString getUndoTitle() { return i18n("Increase Indent"); } bool testCondition( RowFormat* rw ) { return ( rw->hasProperty( Format::PIndent ) ); } void doWork( RowFormat* rw ) { rw->setIndent( tmpIndent+valIndent ); //rw->setAlign( Format::Left ); } void doWork( ColumnFormat* cl ) { cl->setIndent( tmpIndent+valIndent ); //cl->setAlign( Format::Left ); } void prepareCell( Cell* c ) { c->format()->clearProperty( Format::PIndent ); c->format()->clearNoFallBackProperties( Format::PIndent ); //c->format()->clearProperty( Format::PAlign ); //c->format()->clearNoFallBackProperties( Format::PAlign ); } bool testCondition( Cell* cell ) { return ( !cell->isPartOfMerged() ); } void doWork( Cell* cell, bool cellRegion, int x, int y ) { if ( cellRegion ) { if(cell->format()->align(x,y)!=Format::Left) { //cell->setAlign(Format::Left); //cell->format()->setIndent( 0.0 ); } cell->setDisplayDirtyFlag(); cell->format()->setIndent( /* ### ??? --> */ cell->format()->getIndent(x,y) /* <-- */ +valIndent ); cell->clearDisplayDirtyFlag(); } else { cell->format()->setIndent( tmpIndent+valIndent); //cell->setAlign( Format::Left); } } }; void Sheet::increaseIndent(Selection* selectionInfo) { TQPoint marker(selectionInfo->marker()); double valIndent = doc()->getIndentValue(); Cell *c = cellAt( marker ); double tmpIndent = c->format()->getIndent( marker.x(), marker.y() ); IncreaseIndentWorker w( tmpIndent, valIndent ); workOnCells( selectionInfo, w ); } struct DecreaseIndentWorker : public Sheet::CellWorkerTypeA { double tmpIndent, valIndent; DecreaseIndentWorker( double _tmpIndent, double _valIndent ) : tmpIndent( _tmpIndent ), valIndent( _valIndent ) { } TQString getUndoTitle() { return i18n("Decrease Indent"); } bool testCondition( RowFormat* rw ) { return ( rw->hasProperty( Format::PIndent ) ); } void doWork( RowFormat* rw ) { rw->setIndent( TQMAX( 0.0, tmpIndent - valIndent ) ); } void doWork( ColumnFormat* cl ) { cl->setIndent( TQMAX( 0.0, tmpIndent - valIndent ) ); } void prepareCell( Cell* c ) { c->format()->clearProperty( Format::PIndent ); c->format()->clearNoFallBackProperties( Format::PIndent ); } bool testCondition( Cell* cell ) { return ( !cell->isPartOfMerged() ); } void doWork( Cell* cell, bool cellRegion, int x, int y ) { if ( cellRegion ) { cell->setDisplayDirtyFlag(); cell->format()->setIndent( TQMAX( 0.0, cell->format()->getIndent( x, y ) - valIndent ) ); cell->clearDisplayDirtyFlag(); } else { cell->format()->setIndent( TQMAX( 0.0, tmpIndent - valIndent ) ); } } }; void Sheet::decreaseIndent( Selection* selectionInfo ) { double valIndent = doc()->getIndentValue(); TQPoint marker(selectionInfo->marker()); Cell* c = cellAt( marker ); double tmpIndent = c->format()->getIndent( marker.x(), marker.y() ); DecreaseIndentWorker w( tmpIndent, valIndent ); workOnCells( selectionInfo, w ); } int Sheet::adjustColumnHelper( Cell * c, int _col, int _row ) { double long_max = 0.0; c->calculateTextParameters( painter(), _col, _row ); if ( c->textWidth() > long_max ) { double indent = 0.0; int a = c->format()->align( c->column(), c->row() ); if ( a == Format::Undefined ) { if ( c->value().isNumber() || c->isDate() || c->isTime()) a = Format::Right; else a = Format::Left; } if ( a == Format::Left ) indent = c->format()->getIndent( c->column(), c->row() ); long_max = indent + c->textWidth() + c->format()->leftBorderWidth( c->column(), c->row() ) + c->format()->rightBorderWidth( c->column(), c->row() ); } return (int)long_max; } void Sheet::adjustArea(const Region& region) { AdjustColumnRowManipulator* manipulator = new AdjustColumnRowManipulator(); manipulator->setSheet(this); manipulator->setAdjustColumn(true); manipulator->setAdjustRow(true); manipulator->add(region); manipulator->execute(); } void Sheet::adjustColumn(const Region& region) { AdjustColumnRowManipulator* manipulator = new AdjustColumnRowManipulator(); manipulator->setSheet(this); manipulator->setAdjustColumn(true); manipulator->add(region); manipulator->execute(); } void Sheet::adjustRow(const Region& region) { AdjustColumnRowManipulator* manipulator = new AdjustColumnRowManipulator(); manipulator->setSheet(this); manipulator->setAdjustRow(true); manipulator->add(region); manipulator->execute(); } struct ClearTextSelectionWorker : public Sheet::CellWorker { Sheet * _s; ClearTextSelectionWorker( Sheet * s ) : Sheet::CellWorker( ), _s( s ) { } class UndoAction* createUndoAction( Doc* doc, Sheet* sheet, const KSpread::Region& region ) { return new UndoChangeAreaTextCell( doc, sheet, region ); } bool testCondition( Cell* cell ) { return ( !cell->isObscured() ); } void doWork( Cell* cell, bool, int, int ) { cell->setCellText( "" ); } }; void Sheet::clearTextSelection( Selection* selectionInfo ) { if (areaIsEmpty(*selectionInfo)) return; ClearTextSelectionWorker w( this ); workOnCells( selectionInfo, w ); } struct ClearValiditySelectionWorker : public Sheet::CellWorker { ClearValiditySelectionWorker( ) : Sheet::CellWorker( ) { } class UndoAction* createUndoAction( Doc* doc, Sheet* sheet, const KSpread::Region& region ) { return new UndoConditional( doc, sheet, region ); } bool testCondition( Cell* cell ) { return ( !cell->isObscured() ); } void doWork( Cell* cell, bool, int, int ) { cell->removeValidity(); } }; void Sheet::clearValiditySelection( Selection* selectionInfo ) { if (areaIsEmpty(*selectionInfo, Validity)) return; ClearValiditySelectionWorker w; workOnCells( selectionInfo, w ); } struct ClearConditionalSelectionWorker : public Sheet::CellWorker { ClearConditionalSelectionWorker( ) : Sheet::CellWorker( ) { } class UndoAction* createUndoAction( Doc* doc, Sheet* sheet, const KSpread::Region& region ) { return new UndoConditional( doc, sheet, region ); } bool testCondition( Cell* cell ) { return ( !cell->isObscured() ); } void doWork( Cell* cell, bool, int, int ) { TQValueList emptyList; cell->setConditionList(emptyList); } }; void Sheet::clearConditionalSelection( Selection* selectionInfo ) { ClearConditionalSelectionWorker w; workOnCells( selectionInfo, w ); } void Sheet::fillSelection( Selection * selectionInfo, int direction ) { TQRect rct( selectionInfo->selection() ); int right = rct.right(); int bottom = rct.bottom(); int left = rct.left(); int top = rct.top(); int width = rct.width(); int height = rct.height(); TQDomDocument undoDoc = saveCellRegion( rct ); loadSelectionUndo( undoDoc, rct, left - 1, top - 1, false, 0 ); TQDomDocument doc; switch( direction ) { case Right: doc = saveCellRegion( TQRect( left, top, 1, height ) ); break; case Up: doc = saveCellRegion( TQRect( left, bottom, width, 1 ) ); break; case Left: doc = saveCellRegion( TQRect( right, top, 1, height ) ); break; case Down: doc = saveCellRegion( TQRect( left, top, width, 1 ) ); break; }; // Save to buffer TQBuffer buffer; buffer.open( IO_WriteOnly ); TQTextStream str( &buffer ); str.setEncoding( TQTextStream::UnicodeUTF8 ); str << doc; buffer.close(); int i; switch( direction ) { case Right: for ( i = left + 1; i <= right; ++i ) { paste( buffer.buffer(), TQRect( i, top, 1, 1 ), false ); } break; case Up: for ( i = bottom + 1; i >= top; --i ) { paste( buffer.buffer(), TQRect( left, i, 1, 1 ), false ); } break; case Left: for ( i = right - 1; i >= left; --i ) { paste( buffer.buffer(), TQRect( i, top, 1, 1 ), false ); } break; case Down: for ( i = top + 1; i <= bottom; ++i ) { paste( buffer.buffer(), TQRect( left, i, 1, 1 ), false ); } break; } this->doc()->setModified( true ); } struct DefaultSelectionWorker : public Sheet::CellWorker { DefaultSelectionWorker( ) : Sheet::CellWorker( true, false, true ) { } class UndoAction* createUndoAction( Doc* doc, Sheet* sheet, const KSpread::Region& region ) { TQString title=i18n("Default Parameters"); return new UndoCellFormat( doc, sheet, region, title ); } bool testCondition( Cell* ) { return true; } void doWork( Cell* cell, bool, int, int ) { cell->defaultStyle(); } }; void Sheet::defaultSelection( Selection* selectionInfo ) { TQRect selection(selectionInfo->selection()); DefaultSelectionWorker w; SelectionType st = workOnCells( selectionInfo, w ); switch ( st ) { case CompleteRows: RowFormat *rw; for ( int i = selection.top(); i <= selection.bottom(); i++ ) { rw = nonDefaultRowFormat( i ); rw->defaultStyleFormat(); } emit sig_updateView( this, *selectionInfo ); return; case CompleteColumns: ColumnFormat *cl; for ( int i = selection.left(); i <= selection.right(); i++ ) { cl=nonDefaultColumnFormat( i ); cl->defaultStyleFormat(); } emit sig_updateView( this, *selectionInfo ); return; case CellRegion: emit sig_updateView( this, *selectionInfo ); return; } } struct SetConditionalWorker : public Sheet::CellWorker { TQValueList conditionList; SetConditionalWorker( TQValueList _tmp ) : Sheet::CellWorker( ), conditionList( _tmp ) { } class UndoAction* createUndoAction( Doc* doc, Sheet* sheet, const KSpread::Region& region ) { return new UndoConditional( doc, sheet, region ); } bool testCondition( Cell* ) { return true; } void doWork( Cell* cell, bool, int, int ) { if ( !cell->isObscured() ) // TODO: isPartOfMerged()??? { cell->setConditionList(conditionList); cell->setDisplayDirtyFlag(); } } }; void Sheet::setConditional( Selection* selectionInfo, TQValueList const & newConditions) { if ( !doc()->undoLocked() ) { UndoConditional * undo = new UndoConditional(doc(), this, *selectionInfo); doc()->addCommand( undo ); } Region::ConstIterator endOfList = selectionInfo->constEnd(); for (Region::ConstIterator it = selectionInfo->constBegin(); it != endOfList; ++it) { TQRect range = (*it)->rect().normalize(); int l = range.left(); int r = range.right(); int t = range.top(); int b = range.bottom(); Cell * cell; Style * s = doc()->styleManager()->defaultStyle(); for (int x = l; x <= r; ++x) { for (int y = t; y <= b; ++y) { cell = nonDefaultCell( x, y, false, s ); cell->setConditionList( newConditions ); cell->setDisplayDirtyFlag(); } } } emit sig_updateView( this, *selectionInfo ); } struct SetValidityWorker : public Sheet::CellWorker { Validity tmp; SetValidityWorker( Validity _tmp ) : Sheet::CellWorker( ), tmp( _tmp ) { } class UndoAction* createUndoAction( Doc* doc, Sheet* sheet, const KSpread::Region& region ) { return new UndoConditional( doc, sheet, region ); } bool testCondition( Cell* ) { return true; } void doWork( Cell* cell, bool, int, int ) { if ( !cell->isObscured() ) { cell->setDisplayDirtyFlag(); if ( tmp.m_restriction==Restriction::None ) cell->removeValidity(); else { Validity *tmpValidity = cell->getValidity(); tmpValidity->message=tmp.message; tmpValidity->title=tmp.title; tmpValidity->valMin=tmp.valMin; tmpValidity->valMax=tmp.valMax; tmpValidity->m_cond=tmp.m_cond; tmpValidity->m_action=tmp.m_action; tmpValidity->m_restriction=tmp.m_restriction; tmpValidity->timeMin=tmp.timeMin; tmpValidity->timeMax=tmp.timeMax; tmpValidity->dateMin=tmp.dateMin; tmpValidity->dateMax=tmp.dateMax; tmpValidity->displayMessage=tmp.displayMessage; tmpValidity->allowEmptyCell=tmp.allowEmptyCell; tmpValidity->displayValidationInformation=tmp.displayValidationInformation; tmpValidity->titleInfo=tmp.titleInfo; tmpValidity->messageInfo=tmp.messageInfo; tmpValidity->listValidity=tmp.listValidity; } cell->clearDisplayDirtyFlag(); } } }; void Sheet::setValidity(Selection* selectionInfo, KSpread::Validity tmp ) { SetValidityWorker w( tmp ); workOnCells( selectionInfo, w ); } struct GetWordSpellingWorker : public Sheet::CellWorker { TQString& listWord; GetWordSpellingWorker( TQString& _listWord ) : Sheet::CellWorker( false, false, true ), listWord( _listWord ) { } class UndoAction* createUndoAction( Doc*, Sheet*, const KSpread::Region& ) { return 0; } bool testCondition( Cell* ) { return true; } void doWork( Cell* c, bool cellRegion, int, int ) { if ( !c->isObscured() || cellRegion /* ### ??? */ ) { if ( !c->isFormula() && !c->value().isNumber() && !c->value().asString().isEmpty() && !c->isTime() && !c->isDate() && !c->text().isEmpty()) { listWord+=c->text()+'\n'; } } } }; TQString Sheet::getWordSpelling(Selection* selectionInfo ) { TQString listWord; GetWordSpellingWorker w( listWord ); workOnCells( selectionInfo, w ); return listWord; } struct SetWordSpellingWorker : public Sheet::CellWorker { TQStringList& list; int pos; Sheet * sheet; SetWordSpellingWorker( TQStringList & _list,Sheet * s ) : Sheet::CellWorker( false, false, true ), list( _list ), pos( 0 ), sheet( s ) { } class UndoAction* createUndoAction( Doc* doc, Sheet* sheet, const KSpread::Region& region ) { return new UndoChangeAreaTextCell( doc, sheet, region ); } bool testCondition( Cell* ) { return true; } void doWork( Cell* c, bool cellRegion, int, int ) { if ( !c->isObscured() || cellRegion /* ### ??? */ ) { if ( !c->isFormula() && !c->value().isNumber() && !c->value().asString().isEmpty() && !c->isTime() && !c->isDate() && !c->text().isEmpty()) { c->setCellText( list[pos] ); pos++; } } } }; void Sheet::setWordSpelling(Selection* selectionInfo, const TQString _listWord ) { TQStringList list = TQStringList::split ( '\n', _listWord ); SetWordSpellingWorker w( list, this ); workOnCells( selectionInfo, w ); } static TQString cellAsText( Cell* cell, unsigned int max ) { TQString result; if( !cell->isDefault() ) { int l = max - cell->strOutText().length(); if (cell->defineAlignX() == Format::Right ) { for ( int i = 0; i < l; ++i ) result += " "; result += cell->strOutText(); } else if (cell->defineAlignX() == Format::Left ) { result += " "; result += cell->strOutText(); // start with "1" because we already set one space for ( int i = 1; i < l; ++i ) result += " "; } else // centered { int i; int s = (int) l / 2; for ( i = 0; i < s; ++i ) result += " "; result += cell->strOutText(); for ( i = s; i < l; ++i ) result += " "; } } else { for ( unsigned int i = 0; i < max; ++i ) result += " "; } return result; } TQString Sheet::copyAsText( Selection* selectionInfo ) { // Only one cell selected? => copy active cell if ( selectionInfo->isSingular() ) { Cell * cell = cellAt( selectionInfo->marker() ); if( !cell->isDefault() ) return cell->strOutText(); return ""; } TQRect selection(selectionInfo->selection()); // Find area unsigned top = selection.bottom(); unsigned bottom = selection.top(); unsigned left = selection.right(); unsigned right = selection.left(); unsigned max = 1; for( Cell *c = d->cells.firstCell();c; c = c->nextCell() ) { if ( !c->isDefault() ) { TQPoint p( c->column(), c->row() ); if ( selection.contains( p ) ) { top = TQMIN( top, (unsigned) c->row() ); left = TQMIN( left, (unsigned) c->column() ); bottom = TQMAX( bottom, (unsigned) c->row() ); right = TQMAX( right, (unsigned) c->column() ); if ( c->strOutText().length() > max ) max = c->strOutText().length(); } } } ++max; TQString result; for ( unsigned y = top; y <= bottom; ++y) { for ( unsigned x = left; x <= right; ++x) { Cell *cell = cellAt( x, y ); result += cellAsText( cell, max ); } result += "\n"; } return result; } void Sheet::copySelection( Selection* selectionInfo ) { TQDomDocument doc = saveCellRegion( *selectionInfo, true ); // Save to buffer TQBuffer buffer; buffer.open( IO_WriteOnly ); TQTextStream str( &buffer ); str.setEncoding( TQTextStream::UnicodeUTF8 ); str << doc; buffer.close(); TextDrag * kd = new TextDrag( 0L ); kd->setPlain( copyAsText(selectionInfo) ); kd->setKSpread( buffer.buffer() ); TQApplication::clipboard()->setData( kd ); } void Sheet::cutSelection( Selection* selectionInfo ) { TQDomDocument doc = saveCellRegion(*selectionInfo, true, true); // Save to buffer TQBuffer buffer; buffer.open( IO_WriteOnly ); TQTextStream str( &buffer ); str.setEncoding( TQTextStream::UnicodeUTF8 ); str << doc; buffer.close(); TextDrag * kd = new TextDrag( 0L ); kd->setPlain( copyAsText(selectionInfo) ); kd->setKSpread( buffer.buffer() ); TQApplication::clipboard()->setData( kd ); deleteSelection( selectionInfo, true ); } void Sheet::paste( const TQRect& pasteArea, bool makeUndo, Paste::Mode mode, Paste::Operation operation, bool insert, int insertTo, bool pasteFC, TQClipboard::Mode clipboardMode ) { TQMimeSource * mime = TQApplication::clipboard()->data( clipboardMode ); if ( !mime ) return; TQByteArray b; if ( mime->provides( TextDrag::selectionMimeType() ) ) { b = mime->encodedData( TextDrag::selectionMimeType() ); } else if( mime->provides( "text/plain" ) ) { // Note: TQClipboard::text() seems to do a better job than encodedData( "text/plain" ) // In particular it handles charsets (in the mimetype). Copied from KPresenter ;-) TQString _text = TQApplication::clipboard()->text( clipboardMode ); doc()->emitBeginOperation(); pasteTextPlain( _text, pasteArea ); emit sig_updateView( this ); // doc()->emitEndOperation(); return; } else return; // Do the actual pasting. doc()->emitBeginOperation(); paste( b, pasteArea, makeUndo, mode, operation, insert, insertTo, pasteFC ); emit sig_updateView( this ); // doc()->emitEndOperation(); } void Sheet::pasteTextPlain( TQString &_text, TQRect pasteArea) { // TQString tmp; // tmp= TQString::fromLocal8Bit(_mime->encodedData( "text/plain" )); if( _text.isEmpty() ) return; TQString tmp = _text; int i; int mx = pasteArea.left(); int my = pasteArea.top(); int rows = 1; int len = tmp.length(); //count the numbers of lines in text for ( i = 0; i < len; ++i ) { if ( tmp[i] == '\n' ) ++rows; } Cell * cell = nonDefaultCell( mx, my ); if ( rows == 1 ) { if ( !doc()->undoLocked() ) { UndoSetText * undo = new UndoSetText( doc(), this , cell->text(), mx, my, cell->formatType() ); doc()->addCommand( undo ); } } else { TQRect rect(mx, my, mx, my + rows - 1); UndoChangeAreaTextCell * undo = new UndoChangeAreaTextCell( doc(), this , rect ); doc()->addCommand( undo ); } i = 0; TQString rowtext; while ( i < rows ) { int p = 0; p = tmp.find('\n'); if (p < 0) p = tmp.length(); rowtext = tmp.left(p); if ( !isProtected() || cell->format()->notProtected( mx, my + i ) ) { cell->setCellText( rowtext ); cell->updateChart(); } // next cell ++i; cell = nonDefaultCell( mx, my + i ); if (!cell || p == (int) tmp.length()) break; // exclude the left part and '\n' tmp = tmp.right(tmp.length() - p - 1); } if (!isLoading()) refreshMergedCell(); emit sig_updateView( this ); emit sig_updateHBorder( this ); emit sig_updateVBorder( this ); } void Sheet::paste( const TQByteArray& b, const TQRect& pasteArea, bool makeUndo, Paste::Mode mode, Paste::Operation operation, bool insert, int insertTo, bool pasteFC ) { kdDebug(36001) << "Parsing " << b.size() << " bytes" << endl; TQBuffer buffer( b ); buffer.open( IO_ReadOnly ); TQDomDocument doc; doc.setContent( &buffer ); buffer.close(); // ##### TODO: Test for parsing errors int mx = pasteArea.left(); int my = pasteArea.top(); loadSelection( doc, pasteArea, mx - 1, my - 1, makeUndo, mode, operation, insert, insertTo, pasteFC ); } bool Sheet::loadSelection(const TQDomDocument& doc, const TQRect& pasteArea, int _xshift, int _yshift, bool makeUndo, Paste::Mode mode, Paste::Operation operation, bool insert, int insertTo, bool pasteFC) { //kdDebug(36001) << "loadSelection called. pasteArea=" << pasteArea << endl; if (!isLoading() && makeUndo) { loadSelectionUndo( doc, pasteArea, _xshift, _yshift, insert, insertTo ); } TQDomElement root = doc.documentElement(); // "spreadsheet-snippet" int rowsInClpbrd = root.attribute( "rows" ).toInt(); int columnsInClpbrd = root.attribute( "columns" ).toInt(); // find size of rectangle that we want to paste to (either clipboard size or current selection) const int pasteWidth = ( pasteArea.width() >= columnsInClpbrd && util_isRowSelected(pasteArea) == false && root.namedItem( "rows" ).toElement().isNull() ) ? pasteArea.width() : columnsInClpbrd; const int pasteHeight = ( pasteArea.height() >= rowsInClpbrd && util_isColumnSelected(pasteArea) == false && root.namedItem( "columns" ).toElement().isNull()) ? pasteArea.height() : rowsInClpbrd; // kdDebug() << "loadSelection: paste area has size " // << pasteHeight << " rows * " // << pasteWidth << " columns " << endl; // kdDebug() << "loadSelection: " << rowsInClpbrd << " rows and " // << columnsInClpbrd << " columns in clipboard." << endl; // kdDebug() << "xshift: " << _xshift << " _yshift: " << _yshift << endl; TQDomElement e = root.firstChild().toElement(); // "columns", "rows" or "cell" for (; !e.isNull(); e = e.nextSibling().toElement()) { // entire columns given if (e.tagName() == "columns" && !isProtected()) { _yshift = 0; // Clear the existing columns int col = e.attribute("column").toInt(); int width = e.attribute("count").toInt(); if (!insert) { for ( int i = col; i < col + width; ++i ) { d->cells.clearColumn( _xshift + i ); d->columns.removeElement( _xshift + i ); } } // Insert column formats TQDomElement c = e.firstChild().toElement(); for ( ; !c.isNull(); c = c.nextSibling().toElement() ) { if ( c.tagName() == "column" ) { ColumnFormat *cl = new ColumnFormat( this, 0 ); if ( cl->load( c, _xshift, mode, pasteFC ) ) insertColumnFormat( cl ); else delete cl; } } } // entire rows given if (e.tagName() == "rows" && !isProtected()) { _xshift = 0; // Clear the existing rows int row = e.attribute("row").toInt(); int height = e.attribute("count").toInt(); if ( !insert ) { for( int i = row; i < row + height; ++i ) { d->cells.clearRow( _yshift + i ); d->rows.removeElement( _yshift + i ); } } // Insert row formats TQDomElement c = e.firstChild().toElement(); for( ; !c.isNull(); c = c.nextSibling().toElement() ) { if ( c.tagName() == "row" ) { RowFormat *cl = new RowFormat( this, 0 ); if ( cl->load( c, _yshift, mode, pasteFC ) ) insertRowFormat( cl ); else delete cl; } } } Cell* refreshCell = 0; Cell *cell; Cell *cellBackup = NULL; if (e.tagName() == "cell") { int row = e.attribute( "row" ).toInt() + _yshift; int col = e.attribute( "column" ).toInt() + _xshift; // tile the selection with the clipboard contents for (int roff = 0; row + roff - _yshift <= pasteHeight; roff += rowsInClpbrd) { for (int coff = 0; col + coff - _xshift <= pasteWidth; coff += columnsInClpbrd) { // kdDebug() << "loadSelection: cell at " << (col+coff) << "," << (row+roff) // << " with roff,coff= " << roff << "," << coff // << ", _xshift: " << _xshift << ", _yshift: " << _yshift << endl; cell = nonDefaultCell( col + coff, row + roff ); if (isProtected() && !cell->format()->notProtected(col + coff, row + roff)) { continue; } cellBackup = new Cell(this, cell->column(), cell->row()); cellBackup->copyAll(cell); if (!cell->load(e, _xshift + coff, _yshift + roff, mode, operation, pasteFC)) { cell->copyAll(cellBackup); } else { if (cell->isFormula()) { cell->setCalcDirtyFlag(); } } delete cellBackup; cell = cellAt( col + coff, row + roff ); if( !refreshCell && cell->updateChart( false ) ) { refreshCell = cell; } } } } //refresh chart after that you paste all cells /* I don't think this is gonna work....doesn't this only update one chart -- the one which had a dependant cell update first? - John I don't have time to check on this now.... */ if ( refreshCell ) refreshCell->updateChart(); } this->doc()->setModified( true ); if (!isLoading()) refreshMergedCell(); emit sig_updateView( this ); emit sig_updateHBorder( this ); emit sig_updateVBorder( this ); return true; } void Sheet::loadSelectionUndo(const TQDomDocument& d, const TQRect& loadArea, int _xshift, int _yshift, bool insert, int insertTo) { TQDomElement root = d.documentElement(); // "spreadsheet-snippet" int rowsInClpbrd = root.attribute( "rows" ).toInt(); int columnsInClpbrd = root.attribute( "columns" ).toInt(); // find rect that we paste to const int pasteWidth = (loadArea.width() >= columnsInClpbrd && util_isRowSelected(loadArea) == false && root.namedItem( "rows" ).toElement().isNull()) ? loadArea.width() : columnsInClpbrd; const int pasteHeight = (loadArea.height() >= rowsInClpbrd && util_isColumnSelected(loadArea) == false && root.namedItem( "columns" ).toElement().isNull()) ? loadArea.height() : rowsInClpbrd; uint numCols = 0; uint numRows = 0; Region region; for (TQDomNode n = root.firstChild(); !n.isNull(); n = n.nextSibling()) { TQDomElement e = n.toElement(); // "columns", "rows" or "cell" if (e.tagName() == "columns") { _yshift = 0; int col = e.attribute("column").toInt(); int width = e.attribute("count").toInt(); for (int coff = 0; col + coff <= pasteWidth; coff += columnsInClpbrd) { uint overlap = TQMAX(0, (col - 1 + coff + width) - pasteWidth); uint effWidth = width - overlap; region.add(TQRect(_xshift + col + coff, 1, effWidth, KS_rowMax)); numCols += effWidth; } } else if (e.tagName() == "rows") { _xshift = 0; int row = e.attribute("row").toInt(); int height = e.attribute("count").toInt(); for (int roff = 0; row + roff <= pasteHeight; roff += rowsInClpbrd) { uint overlap = TQMAX(0, (row - 1 + roff + height) - pasteHeight); uint effHeight = height - overlap; region.add(TQRect(1, _yshift + row + roff, KS_colMax, effHeight)); numRows += effHeight; } } else if (!e.isNull()) { // store the cols/rows for the insertion int col = e.attribute("column").toInt(); int row = e.attribute("row").toInt(); for (int coff = 0; col + coff <= pasteWidth; coff += columnsInClpbrd) { for (int roff = 0; row + roff <= pasteHeight; roff += rowsInClpbrd) { region.add(TQPoint(_xshift + col + coff, _yshift + row + roff)); } } } } if (!doc()->undoLocked()) { UndoCellPaste *undo = new UndoCellPaste( doc(), this, _xshift, _yshift, region, insert, insertTo ); doc()->addCommand( undo ); } if (insert) { TQRect rect = region.boundingRect(); // shift cells to the right if (insertTo == -1 && numCols == 0 && numRows == 0) { rect.setWidth(rect.width()); shiftRow(rect, false); } // shift cells to the bottom else if (insertTo == 1 && numCols == 0 && numRows == 0) { rect.setHeight(rect.height()); shiftColumn( rect, false ); } // insert columns else if (insertTo == 0 && numCols == 0 && numRows > 0) { insertRow(rect.top(), rect.height() - 1, false); } // insert rows else if (insertTo == 0 && numCols > 0 && numRows == 0) { insertColumn(rect.left(), rect.width() - 1, false); } } } bool Sheet::testAreaPasteInsert()const { TQMimeSource* mime = TQApplication::clipboard()->data( TQClipboard::Clipboard ); if ( !mime ) return false; TQByteArray b; if ( mime->provides( "application/x-kspread-snippet" ) ) b = mime->encodedData( "application/x-kspread-snippet" ); else return false; TQBuffer buffer( b ); buffer.open( IO_ReadOnly ); TQDomDocument d; d.setContent( &buffer ); buffer.close(); TQDomElement e = d.documentElement(); if ( !e.namedItem( "columns" ).toElement().isNull() ) return false; if ( !e.namedItem( "rows" ).toElement().isNull() ) return false; TQDomElement c = e.firstChild().toElement(); for( ; !c.isNull(); c = c.nextSibling().toElement() ) { if ( c.tagName() == "cell" ) return true; } return false; } void Sheet::deleteCells(const Region& region) { // A list of all cells we want to delete. TQPtrStack cellStack; Region::ConstIterator endOfList = region.constEnd(); for (Region::ConstIterator it = region.constBegin(); it != endOfList; ++it) { TQRect range = (*it)->rect().normalize(); int right = range.right(); int left = range.left(); int bottom = range.bottom(); int col; for ( int row = range.top(); row <= bottom; ++row ) { Cell * c = getFirstCellRow( row ); while ( c ) { col = c->column(); if ( col < left ) { c = getNextCellRight( left - 1, row ); continue; } if ( col > right ) break; if ( !c->isDefault() ) cellStack.push( c ); c = getNextCellRight( col, row ); } } } d->cells.setAutoDelete( false ); // Remove the cells from the sheet while ( !cellStack.isEmpty() ) { Cell * cell = cellStack.pop(); d->cells.remove( cell->column(), cell->row() ); cell->setCalcDirtyFlag(); setRegionPaintDirty(cell->cellRect()); delete cell; } d->cells.setAutoDelete( true ); setLayoutDirtyFlag(); // TODO: don't go through all cells here! // Since obscured cells might have been deleted we // have to reenforce it. Cell * c = d->cells.firstCell(); for( ;c; c = c->nextCell() ) { if ( c->doesMergeCells() && !c->isDefault() ) c->mergeCells( c->column(), c->row(), c->extraXCells(), c->extraYCells() ); } doc()->setModified( true ); } void Sheet::deleteSelection( Selection* selectionInfo, bool undo ) { if ( undo && !doc()->undoLocked() ) { UndoDelete *undo = new UndoDelete( doc(), this, *selectionInfo ); doc()->addCommand( undo ); } Region::ConstIterator endOfList = selectionInfo->constEnd(); for (Region::ConstIterator it = selectionInfo->constBegin(); it != endOfList; ++it) { TQRect range = (*it)->rect().normalize(); // Entire rows selected ? if ( util_isRowSelected(range) ) { for( int i = range.top(); i <= range.bottom(); ++i ) { d->cells.clearRow( i ); d->rows.removeElement( i ); } emit sig_updateVBorder( this ); } // Entire columns selected ? else if ( util_isColumnSelected(range) ) { for( int i = range.left(); i <= range.right(); ++i ) { d->cells.clearColumn( i ); d->columns.removeElement( i ); } emit sig_updateHBorder( this ); } else { setRegionPaintDirty( range ); deleteCells( range ); } } refreshMergedCell(); emit sig_updateView( this ); } void Sheet::updateView() { emit sig_updateView( this ); } void Sheet::updateView( TQRect const & rect ) { emit sig_updateView( this, rect ); } void Sheet::updateView(Region* region) { emit sig_updateView( this, *region ); } void Sheet::refreshView( const Region& region ) { Region tmpRegion; Region::ConstIterator endOfList = region.constEnd(); for (Region::ConstIterator it = region.constBegin(); it != endOfList; ++it) { TQRect range = (*it)->rect().normalize(); // TODO: don't go through all cells when refreshing! TQRect tmp(range); Cell * c = d->cells.firstCell(); for( ;c; c = c->nextCell() ) { if ( !c->isDefault() && c->row() >= range.top() && c->row() <= range.bottom() && c->column() >= range.left() && c->column() <= range.right() ) { if (c->doesMergeCells()) { int right=TQMAX(tmp.right(),c->column()+c->extraXCells()); int bottom=TQMAX(tmp.bottom(),c->row()+c->extraYCells()); tmp.setRight(right); tmp.setBottom(bottom); } } } deleteCells( range ); tmpRegion.add(tmp); } emit sig_updateView( this, tmpRegion ); } void Sheet::mergeCells(const Region& region, bool hor, bool ver) { // sanity check if( isProtected() ) return; if( workbook()->isProtected() ) return; MergeManipulator* manipulator = new MergeManipulator(); manipulator->setSheet(this); manipulator->setHorizontalMerge(hor); manipulator->setVerticalMerge(ver); manipulator->add(region); manipulator->execute(); } void Sheet::dissociateCells(const Region& region) { // sanity check if( isProtected() ) return; if( workbook()->isProtected() ) return; Manipulator* manipulator = new MergeManipulator(); manipulator->setSheet(this); manipulator->setReverse(true); manipulator->add(region); manipulator->execute(); } bool Sheet::testListChoose(Selection* selectionInfo) { TQRect selection( selectionInfo->selection() ); TQPoint marker( selectionInfo->marker() ); Cell *cell = cellAt( marker.x(), marker.y() ); TQString tmp=cell->text(); Cell* c = firstCell(); bool different=false; int col; for( ;c; c = c->nextCell() ) { col = c->column(); if ( selection.left() <= col && selection.right() >= col && !c->isPartOfMerged() && !(col==marker.x() && c->row()==marker.y())) { if(!c->isFormula() && !c->value().isNumber() && !c->value().asString().isEmpty() && !c->isTime() &&!c->isDate() ) { if(c->text()!=tmp) different=true; } } } return different; } TQDomDocument Sheet::saveCellRegion(const Region& region, bool copy, bool era) { TQDomDocument dd( "spreadsheet-snippet" ); dd.appendChild( dd.createProcessingInstruction( "xml", "version=\"1.0\" encoding=\"UTF-8\"" ) ); TQDomElement root = dd.createElement( "spreadsheet-snippet" ); dd.appendChild(root); // find the upper left corner of the selection TQRect boundingRect = region.boundingRect(); int left = boundingRect.left(); int top = boundingRect.top(); // for tiling the clipboard content in the selection root.setAttribute( "rows", boundingRect.height() ); root.setAttribute( "columns", boundingRect.width() ); Region::ConstIterator endOfList = region.constEnd(); for (Region::ConstIterator it = region.constBegin(); it != endOfList; ++it) { TQRect range = (*it)->rect().normalize(); // // Entire rows selected? // if ((*it)->isRow()) { TQDomElement rows = dd.createElement("rows"); rows.setAttribute( "count", range.height() ); rows.setAttribute( "row", range.top() - top + 1 ); root.appendChild( rows ); // Save all cells. for (Cell* cell = d->cells.firstCell(); cell; cell = cell->nextCell()) { if (!cell->isDefault() && !cell->isPartOfMerged()) { TQPoint point(cell->column(), cell->row()); if (range.contains(point)) { root.appendChild(cell->save( dd, 0, top - 1, copy, copy, era)); } } } // ##### Inefficient // Save the row formats if there are any RowFormat* format; for (int row = range.top(); row <= range.bottom(); ++row) { format = rowFormat( row ); if (format && !format->isDefault()) { TQDomElement e = format->save(dd, top - 1, copy); if (!e.isNull()) { rows.appendChild( e ); } } } continue; } // // Entire columns selected? // if ((*it)->isColumn()) { TQDomElement columns = dd.createElement("columns"); columns.setAttribute( "count", range.width() ); columns.setAttribute( "column", range.left() - left + 1 ); root.appendChild( columns ); // Save all cells. for (Cell* cell = d->cells.firstCell();cell; cell = cell->nextCell()) { if (!cell->isDefault() && !cell->isPartOfMerged()) { TQPoint point(cell->column(), cell->row()); if (range.contains(point)) { root.appendChild(cell->save( dd, left - 1, 0, copy, copy, era)); } } } // ##### Inefficient // Save the column formats if there are any ColumnFormat* format; for (int col = range.left(); col <= range.right(); ++col) { format = columnFormat(col); if (format && !format->isDefault()) { TQDomElement e = format->save(dd, left - 1, copy); if (!e.isNull()) { columns.appendChild(e); } } } continue; } // Save all cells. //store all cell //when they don't exist we created them //because it's necessary when there is a format on a column/row //but I remove cell which is inserted. Cell* cell; bool insert; enableScrollBarUpdates(false); for (int col = range.left(); col <= range.right(); ++col) { for (int row = range.top(); row <= range.bottom(); ++row) { insert = false; cell = cellAt(col, row); if (cell == d->defaultCell) { cell = new Cell(this, col, row); insertCell(cell); insert = true; } root.appendChild(cell->save(dd, left - 1, top - 1, true, copy, era)); if (insert) { d->cells.remove(col, row); } } } enableScrollBarUpdates(true); } return dd; } TQDomElement Sheet::saveXML( TQDomDocument& dd ) { TQDomElement sheet = dd.createElement( "table" ); sheet.setAttribute( "name", d->name ); //Laurent: for oasis format I think that we must use style:direction... sheet.setAttribute( "layoutDirection", (d->layoutDirection == RightToLeft) ? "rtl" : "ltr" ); sheet.setAttribute( "columnnumber", (int)d->showColumnNumber); sheet.setAttribute( "borders", (int)d->showPageBorders); sheet.setAttribute( "hide", (int)d->hide); sheet.setAttribute( "hidezero", (int)d->hideZero); sheet.setAttribute( "firstletterupper", (int)d->firstLetterUpper); sheet.setAttribute( "grid", (int)d->showGrid ); sheet.setAttribute( "printGrid", (int)d->print->printGrid() ); sheet.setAttribute( "printCommentIndicator", (int)d->print->printCommentIndicator() ); sheet.setAttribute( "printFormulaIndicator", (int)d->print->printFormulaIndicator() ); sheet.setAttribute( "showFormula", (int)d->showFormula); sheet.setAttribute( "showFormulaIndicator", (int)d->showFormulaIndicator); sheet.setAttribute( "showCommentIndicator", (int)d->showCommentIndicator); sheet.setAttribute( "lcmode", (int)d->lcMode); sheet.setAttribute( "autoCalc", (int)d->autoCalc); sheet.setAttribute( "borders1.2", 1); if ( !d->password.isNull() ) { if ( d->password.size() > 0 ) { TQCString str = KCodecs::base64Encode( d->password ); sheet.setAttribute( "protected", TQString( str.data() ) ); } else sheet.setAttribute( "protected", "" ); } // paper parameters TQDomElement paper = dd.createElement( "paper" ); paper.setAttribute( "format", d->print->paperFormatString() ); paper.setAttribute( "orientation", d->print->orientationString() ); sheet.appendChild( paper ); TQDomElement borders = dd.createElement( "borders" ); borders.setAttribute( "left", d->print->leftBorder() ); borders.setAttribute( "top", d->print->topBorder() ); borders.setAttribute( "right", d->print->rightBorder() ); borders.setAttribute( "bottom", d->print->bottomBorder() ); paper.appendChild( borders ); TQDomElement head = dd.createElement( "head" ); paper.appendChild( head ); if ( !d->print->headLeft().isEmpty() ) { TQDomElement left = dd.createElement( "left" ); head.appendChild( left ); left.appendChild( dd.createTextNode( d->print->headLeft() ) ); } if ( !d->print->headMid().isEmpty() ) { TQDomElement center = dd.createElement( "center" ); head.appendChild( center ); center.appendChild( dd.createTextNode( d->print->headMid() ) ); } if ( !d->print->headRight().isEmpty() ) { TQDomElement right = dd.createElement( "right" ); head.appendChild( right ); right.appendChild( dd.createTextNode( d->print->headRight() ) ); } TQDomElement foot = dd.createElement( "foot" ); paper.appendChild( foot ); if ( !d->print->footLeft().isEmpty() ) { TQDomElement left = dd.createElement( "left" ); foot.appendChild( left ); left.appendChild( dd.createTextNode( d->print->footLeft() ) ); } if ( !d->print->footMid().isEmpty() ) { TQDomElement center = dd.createElement( "center" ); foot.appendChild( center ); center.appendChild( dd.createTextNode( d->print->footMid() ) ); } if ( !d->print->footRight().isEmpty() ) { TQDomElement right = dd.createElement( "right" ); foot.appendChild( right ); right.appendChild( dd.createTextNode( d->print->footRight() ) ); } // print range TQDomElement printrange = dd.createElement( "printrange-rect" ); TQRect _printRange = d->print->printRange(); int left = _printRange.left(); int right = _printRange.right(); int top = _printRange.top(); int bottom = _printRange.bottom(); //If whole rows are selected, then we store zeros, as KS_colMax may change in future if ( left == 1 && right == KS_colMax ) { left = 0; right = 0; } //If whole columns are selected, then we store zeros, as KS_rowMax may change in future if ( top == 1 && bottom == KS_rowMax ) { top = 0; bottom = 0; } printrange.setAttribute( "left-rect", left ); printrange.setAttribute( "right-rect", right ); printrange.setAttribute( "bottom-rect", bottom ); printrange.setAttribute( "top-rect", top ); sheet.appendChild( printrange ); // Print repeat columns TQDomElement printRepeatColumns = dd.createElement( "printrepeatcolumns" ); printRepeatColumns.setAttribute( "left", d->print->printRepeatColumns().first ); printRepeatColumns.setAttribute( "right", d->print->printRepeatColumns().second ); sheet.appendChild( printRepeatColumns ); // Print repeat rows TQDomElement printRepeatRows = dd.createElement( "printrepeatrows" ); printRepeatRows.setAttribute( "top", d->print->printRepeatRows().first ); printRepeatRows.setAttribute( "bottom", d->print->printRepeatRows().second ); sheet.appendChild( printRepeatRows ); //Save print zoom sheet.setAttribute( "printZoom", d->print->zoom() ); //Save page limits sheet.setAttribute( "printPageLimitX", d->print->pageLimitX() ); sheet.setAttribute( "printPageLimitY", d->print->pageLimitY() ); // Save all cells. Cell* c = d->cells.firstCell(); for( ;c; c = c->nextCell() ) { if ( !c->isDefault() ) { TQDomElement e = c->save( dd ); if ( !e.isNull() ) sheet.appendChild( e ); } } // Save all RowFormat objects. RowFormat* rl = d->rows.first(); for( ; rl; rl = rl->next() ) { if ( !rl->isDefault() ) { TQDomElement e = rl->save( dd ); if ( e.isNull() ) return TQDomElement(); sheet.appendChild( e ); } } // Save all ColumnFormat objects. ColumnFormat* cl = d->columns.first(); for( ; cl; cl = cl->next() ) { if ( !cl->isDefault() ) { TQDomElement e = cl->save( dd ); if ( e.isNull() ) return TQDomElement(); sheet.appendChild( e ); } } TQPtrListIterator chl = doc()->embeddedObjects(); for( ; chl.current(); ++chl ) { if ( chl.current()->sheet() == this ) { TQDomElement e = chl.current()->save( dd ); if ( e.isNull() ) return TQDomElement(); sheet.appendChild( e ); } } return sheet; } bool Sheet::isLoading() { return doc()->isLoading(); } TQPtrList Sheet::getSelectedObjects() { TQPtrList objects; TQPtrListIterator it = doc()->embeddedObjects(); for ( ; it.current() ; ++it ) { if( it.current()->isSelected() && it.current()->sheet() == this ) { objects.append( it.current() ); } } return objects; } KoRect Sheet::getRealRect( bool all ) { KoRect rect; TQPtrListIterator it( doc()->embeddedObjects() ); for ( ; it.current() ; ++it ) { if ( all || ( it.current()->isSelected() && ! it.current()->isProtect() ) ) rect |= it.current()->geometry(); } return rect; } // move object for releasemouseevent KCommand *Sheet::moveObject(View *_view, double diffx, double diffy) { bool createCommand=false; MoveObjectByCmd *moveByCmd=0L; Canvas * canvas = _view->canvasWidget(); TQPtrList _objects; _objects.setAutoDelete( false ); TQPtrListIterator it( doc()->embeddedObjects()/*m_objectList*/ ); for ( ; it.current() ; ++it ) { if ( it.current()->isSelected() && !it.current()->isProtect()) { _objects.append( it.current() ); KoRect geometry = it.current()->geometry(); geometry.moveBy( -canvas->xOffset(), -canvas->yOffset() ); TQRect br = doc()->zoomRect( geometry/*it.current()->geometry()*/ ); br.moveBy( doc()->zoomItX( diffx ), doc()->zoomItY( diffy ) ); br.moveBy( doc()->zoomItX( -canvas->xOffset() ), doc()->zoomItY( -canvas->yOffset() ) ); canvas->repaint( br ); // Previous position canvas->repaintObject( it.current() ); // New position createCommand=true; } } if(createCommand) { moveByCmd = new MoveObjectByCmd( i18n( "Move Objects" ), KoPoint( diffx, diffy ), _objects, doc(), this ); // m_doc->updateSideBarItem( this ); } return moveByCmd; } KCommand *Sheet::moveObject(View *_view,const KoPoint &_move,bool key) { TQPtrList _objects; _objects.setAutoDelete( false ); MoveObjectByCmd *moveByCmd=0L; Canvas * canvas = _view->canvasWidget(); TQPtrListIterator it( doc()->embeddedObjects()/*m_objectList*/ ); for ( ; it.current() ; ++it ) { if ( it.current()->isSelected() && !it.current()->isProtect()) { KoRect geometry = it.current()->geometry(); geometry.moveBy( -canvas->xOffset(), -canvas->yOffset() ); TQRect oldBoundingRect = doc()->zoomRect( geometry ); KoRect r = it.current()->geometry(); r.moveBy( _move.x(), _move.y() ); it.current()->setGeometry( r ); _objects.append( it.current() ); canvas->repaint( oldBoundingRect ); canvas->repaintObject( it.current() ); } } if ( key && !_objects.isEmpty()) moveByCmd = new MoveObjectByCmd( i18n( "Move Objects" ), KoPoint( _move ), _objects, doc() ,this ); return moveByCmd; } /* * Check if object name already exists. */ bool Sheet::objectNameExists( EmbeddedObject *object, TQPtrList &list ) { TQPtrListIterator it( list ); for ( it.toFirst(); it.current(); ++it ) { // object name can exist in current object. if ( it.current()->getObjectName() == object->getObjectName() && it.current() != object ) { return true; } } return false; } void Sheet::unifyObjectName( EmbeddedObject *object ) { if ( object->getObjectName().isEmpty() ) { object->setObjectName( object->getTypeString() ); } TQString objectName( object->getObjectName() ); TQPtrList list( doc()->embeddedObjects() ); int count = 1; while ( objectNameExists( object, list ) ) { count++; TQRegExp rx( " \\(\\d{1,3}\\)$" ); if ( rx.search( objectName ) != -1 ) { objectName.remove( rx ); } objectName += TQString(" (%1)").arg( count ); object->setObjectName( objectName ); } } void Sheet::checkContentDirection( TQString const & name ) { /* set sheet's direction to RTL if sheet name is an RTL string */ if ( (name.isRightToLeft()) ) setLayoutDirection( RightToLeft ); else setLayoutDirection( LeftToRight ); emit sig_refreshView(); } bool Sheet::loadSheetStyleFormat( TQDomElement *style ) { TQString hleft, hmiddle, hright; TQString fleft, fmiddle, fright; TQDomNode header = KoDom::namedItemNS( *style, KoXmlNS::style, "header" ); if ( !header.isNull() ) { kdDebug() << "Header exists" << endl; TQDomNode part = KoDom::namedItemNS( header, KoXmlNS::style, "region-left" ); if ( !part.isNull() ) { hleft = getPart( part ); kdDebug() << "Header left: " << hleft << endl; } else kdDebug() << "Style:region:left doesn't exist!" << endl; part = KoDom::namedItemNS( header, KoXmlNS::style, "region-center" ); if ( !part.isNull() ) { hmiddle = getPart( part ); kdDebug() << "Header middle: " << hmiddle << endl; } part = KoDom::namedItemNS( header, KoXmlNS::style, "region-right" ); if ( !part.isNull() ) { hright = getPart( part ); kdDebug() << "Header right: " << hright << endl; } } //TODO implement it under kspread TQDomNode headerleft = KoDom::namedItemNS( *style, KoXmlNS::style, "header-left" ); if ( !headerleft.isNull() ) { TQDomElement e = headerleft.toElement(); if ( e.hasAttributeNS( KoXmlNS::style, "display" ) ) kdDebug()<<"header.hasAttribute( style:display ) :"<setHeadFootLine( hleft, hmiddle, hright, fleft, fmiddle, fright ); return true; } void Sheet::replaceMacro( TQString & text, const TQString & old, const TQString & newS ) { int n = text.find( old ); if ( n != -1 ) text = text.replace( n, old.length(), newS ); } TQString Sheet::getPart( const TQDomNode & part ) { TQString result; TQDomElement e = KoDom::namedItemNS( part, KoXmlNS::text, "p" ); while ( !e.isNull() ) { TQString text = e.text(); kdDebug() << "PART: " << text << endl; TQDomElement macro = KoDom::namedItemNS( e, KoXmlNS::text, "time" ); if ( !macro.isNull() ) replaceMacro( text, macro.text(), "