diff --git a/kspread/CHANGES b/kspread/CHANGES index 3fb98510d32..9577d14cc94 100644 --- a/kspread/CHANGES +++ b/kspread/CHANGES @@ -1,54 +1,56 @@ Changes from 1.1 to 1.2 ======================= Until beta2: ------------ - bug fixes - perfomance enhancements - added show/hide grid on printout option - Sort enhanced for - sorting by up to three rows or columns - possibility of putting the result of sorting some place else - you can use a custom lists (like days of week) as a primary key - option if you want to copy the layout or not - you can define a row header which gets copied but not sorted - auto continuation support for every direction and all types supported in KSpread and for more complex series like 1,3,4,6,... - just one "copy" instead of "copy" and "copy as text" - insertion of series supports now doubles and decreasing series +- support for inserting data from SQL databases + Until beta1: ------------ - bug fixes :-) - performance enhancements - support up to 2^15 columns and rows, formerly it was 676 columns and 10000 rows - add support for spell-check - many new built-in functions (see detailed list below) - function name is now case-insensitive - "Related Function" in formula editor - move functions in kspread_interpreter into several kspread_functions_*.cc New functions added: conversion: INT2BOOL, CharToAscii, AsciiToChar, BOOL2STRING, NUM2STRING, BOOL2INT math: DIV, LCD, PRODUCT, LCM, TOGGLE, TRIM, ABS text: COMPARE, CLEAN, SLEEK, PROPER, REPLACE date/time: DAYS, WEEKS, MONTHS, YEARS financial: DB, SLN, SYD, EURO logical: XOR statistical: CHIDIST, FDIST, TDIST, CONFIDENCE, BETADIST, GAMMADIST, PHI, GAUSS, MEDIAN, POISSON, GAMMALN, NORMINV, NORMSINV, WEIBULL, EXPONDIST, NORMDIST, NORMSDIST, FISHER, FISHERINV, LOGNORMDIST diff --git a/kspread/kspread_cell.cc b/kspread/kspread_cell.cc index d8af1ab8711..cfcd1e710da 100644 --- a/kspread/kspread_cell.cc +++ b/kspread/kspread_cell.cc @@ -1,4726 +1,4726 @@ /* This file is part of the KDE project Copyright (C) 1998, 1999 Torben Weis 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include #include #include #include #include #include #include #include "kspread_global.h" #include "kspread_canvas.h" #include "kspread_map.h" #include "kspread_doc.h" #include "kspread_util.h" #include #include #define DO_UPDATE m_pTable->updateCell( this, m_iColumn, m_iRow ) QChar KSpreadCell::decimal_point = '\0'; /***************************************************************************** * * KSpreadCell * *****************************************************************************/ KSpreadCell::KSpreadCell( KSpreadTable *_table, int _column, int _row ) : KSpreadLayout( _table ), conditions(this), m_bShrinkToSize(false) { m_nextCell = 0; m_previousCell = 0; m_pCode = 0; m_pPrivate = 0L; m_pQML = NULL; m_lstDepends.setAutoDelete( TRUE ); m_lstDependingOnMe.setAutoDelete( TRUE ); m_content = Text; m_dataType = StringData; // we use this for empty cells m_dValue = 0.0; m_iRow = _row; m_iColumn = _column; m_style = ST_Normal; m_iExtraXCells = 0; m_iExtraYCells = 0; m_iExtraWidth = 0; m_iExtraHeight = 0; m_pObscuringCell = 0; m_iPrecision = -1; m_iOutTextWidth = 0; m_iOutTextHeight = 0; m_nbLines=0; m_Validity=0; } void KSpreadCell::copyLayout( KSpreadCell *_cell ) { copyLayout( _cell->column(), _cell->row() ); } void KSpreadCell::copyLayout( int _column, int _row ) { KSpreadCell * cell = m_pTable->cellAt( _column, _row ); setAlign( cell->align( _column, _row ) ); setAlignY( cell->alignY( _column, _row ) ); setTextFont( cell->textFont( _column, _row ) ); setTextColor( cell->textColor( _column, _row ) ); setBgColor( cell->bgColor( _column, _row) ); setLeftBorderPen(cell->leftBorderPen( _column, _row )); setTopBorderPen(cell->topBorderPen( _column, _row )); setBottomBorderPen(cell->bottomBorderPen( _column, _row )); setRightBorderPen(cell->rightBorderPen( _column, _row )); setFallDiagonalPen(cell->fallDiagonalPen( _column, _row )); setGoUpDiagonalPen(cell->goUpDiagonalPen( _column, _row )); setBackGroundBrush(cell->backGroundBrush( _column, _row)); setPrecision( cell->precision( _column, _row ) ); setPrefix( cell->prefix( _column, _row ) ); setPostfix( cell->postfix( _column, _row ) ); setFloatFormat( cell->floatFormat( _column, _row ) ); setFloatColor( cell->floatColor( _column, _row ) ); setFactor( cell->factor( _column, _row ) ); setMultiRow( cell->multiRow( _column, _row ) ); setVerticalText( cell->verticalText( _column, _row ) ); setStyle( cell->style()); setDontPrintText(cell->getDontprintText(_column, _row ) ); setIndent( cell->getIndent(_column, _row ) ); QValueList conditionList = cell->GetConditionList(); conditions.SetConditionList(conditionList); setComment( cell->comment(_column, _row) ); setAngle( cell->getAngle(_column, _row) ); setFormatType( cell->getFormatType(_column, _row) ); } void KSpreadCell::copyAll( KSpreadCell *cell ) { Q_ASSERT( !isDefault() ); // trouble ahead... copyLayout( cell ); copyContent( cell ); } void KSpreadCell::copyContent( KSpreadCell* cell ) { Q_ASSERT( !isDefault() ); // trouble ahead... if (cell->isFormula() && cell->column() > 0 && cell->row() > 0) { // change all the references, e.g. from A1 to A3 if copying // from e.g. B2 to B4 QString d = cell->encodeFormula(); setCellText( cell->decodeFormula( d ), true ); } else setCellText( cell->text() ); setAction(cell->action() ); if ( m_pPrivate ) delete m_pPrivate; m_pPrivate = 0; if ( cell->m_pPrivate ) m_pPrivate = cell->m_pPrivate->copy( this ); } void KSpreadCell::defaultStyle() { defaultStyleLayout(); QValueList emptyList; conditions.SetConditionList(emptyList); if(m_Validity != NULL) delete m_Validity; m_Validity = NULL; } void KSpreadCell::layoutChanged() { setFlag(Flag_LayoutDirty); } KSpreadLayout* KSpreadCell::fallbackLayout( int, int row ) { return table()->rowLayout( row ); } const KSpreadLayout* KSpreadCell::fallbackLayout( int, int row ) const { return table()->rowLayout( row ); } void KSpreadCell::forceExtraCells( int _col, int _row, int _x, int _y ) { // Unobscure the objects we obscure right now for( int x = _col; x <= _col + m_iExtraXCells; x++ ) for( int y = _row; y <= _row + m_iExtraYCells; y++ ) if ( x != _col || y != _row ) { KSpreadCell *cell = m_pTable->nonDefaultCell( x, y ); cell->unobscure(); } // disable forcing ? if ( _x == 0 && _y == 0 ) { clearFlag(Flag_ForceExtra); m_iExtraXCells = 0; m_iExtraYCells = 0; m_iExtraWidth = 0; m_iExtraHeight = 0; return; } setFlag(Flag_ForceExtra); m_iExtraXCells = _x; m_iExtraYCells = _y; // Obscure the cells for( int x = _col; x <= _col + _x; x++ ) for( int y = _row; y <= _row + _y; y++ ) if ( x != _col || y != _row ) { KSpreadCell *cell = m_pTable->nonDefaultCell( x, y ); cell->obscure( this ); } // Refresh the layout // QPainter painter; // painter.begin( m_pTable->gui()->canvasWidget() ); setFlag(Flag_LayoutDirty); makeLayout( m_pTable->painter(), _col, _row ); } void KSpreadCell::move( int col, int row ) { setLayoutDirtyFlag(); setCalcDirtyFlag(); setDisplayDirtyFlag(); //int ex = extraXCells(); //int ey = extraYCells(); if ( m_pObscuringCell ) m_pObscuringCell = 0; // Unobscure the objects we obscure right now for( int x = m_iColumn; x <= m_iColumn + m_iExtraXCells; x++ ) for( int y = m_iRow; y <= m_iRow + m_iExtraYCells; y++ ) if ( x != m_iColumn || y != m_iRow ) { KSpreadCell *cell = m_pTable->nonDefaultCell( x, y ); cell->unobscure(); } m_iColumn = col; m_iRow = row; // Reobscure cells if we are forced to do so. //if ( m_bForceExtraCells ) // forceExtraCells( col, row, ex, ey ); } void KSpreadCell::setLayoutDirtyFlag() { setFlag(Flag_LayoutDirty); if ( m_pObscuringCell ) m_pObscuringCell->setLayoutDirtyFlag(); } bool KSpreadCell::needsPrinting() const { if ( isDefault() ) return FALSE; if ( !m_strText.isEmpty() ) return TRUE; if ( hasProperty( PTopBorder ) || hasProperty( PLeftBorder ) || hasProperty( PRightBorder ) || hasProperty( PBottomBorder ) || hasProperty( PFallDiagonal ) || hasProperty( PGoUpDiagonal ) || hasProperty( PBackgroundBrush ) || hasProperty( PBackgroundColor ) ) return TRUE; return FALSE; } bool KSpreadCell::isEmpty() const { return isDefault() || m_strText.isEmpty(); } bool KSpreadCell::isObscuringForced() { if ( !m_pObscuringCell ) return FALSE; return m_pObscuringCell->isForceExtraCells(); } void KSpreadCell::obscure( KSpreadCell *_cell ) { m_pObscuringCell = _cell; } void KSpreadCell::unobscure() { m_pObscuringCell = 0L; setFlag(Flag_LayoutDirty); } void KSpreadCell::clicked( KSpreadCanvas *_canvas ) { if ( m_style == KSpreadCell::ST_Normal ) return; else if ( m_style == KSpreadCell::ST_Select ) { // We do only show a menu if the user himself clicked // on the cell. if ( !_canvas ) return; QPopupMenu *popup = new QPopupMenu(_canvas); SelectPrivate *s = (SelectPrivate*)m_pPrivate; int id = 0; QStringList::ConstIterator it = s->m_lstItems.begin(); for( ; it != s->m_lstItems.end(); ++it ) popup->insertItem( *it, id++ ); QObject::connect( popup, SIGNAL( activated( int ) ), s, SLOT( slotItemSelected( int ) ) ); RowLayout *rl = m_pTable->rowLayout( row() ); int tx = m_pTable->columnPos( column(), _canvas ); int ty = m_pTable->rowPos( row(), _canvas ); int h = rl->height( _canvas ); if ( m_iExtraYCells ) h = m_iExtraHeight; ty += h; QPoint p( tx, ty ); QPoint p2 = _canvas->mapToGlobal( p ); popup->popup( p2 ); //delete popup; return; } if ( m_strAction.isEmpty() ) return; KSContext context; QPtrList lst; lst.setAutoDelete( TRUE ); KSParseNode* code = m_pTable->doc()->interpreter()->parse( context, m_pTable, m_strAction, lst ); // Did a syntax error occur ? if ( context.exception() ) { kdDebug(36001) << "Failed action in cell " << util_cellName(m_iColumn, m_iRow) << endl; if (m_pTable->doc()->getShowMessageError()) { QString tmp(i18n("Error in cell %1\n\n")); tmp = tmp.arg( util_cellName( m_pTable, m_iColumn, m_iRow ) ); tmp += context.exception()->toString( context ); KMessageBox::error((QWidget*)0L , tmp); } return; } KSContext& context2 = m_pTable->doc()->context(); if ( !m_pTable->doc()->interpreter()->evaluate( context2, code, m_pTable ) ) // Print out exception if any if ( context2.exception() &&m_pTable->doc()->getShowMessageError()) { QString tmp(i18n("Error in cell %1\n\n")); tmp = tmp.arg( util_cellName( m_pTable, m_iColumn, m_iRow ) ); tmp += context2.exception()->toString( context2 ); KMessageBox::error( (QWidget*)0L, tmp); } } QString KSpreadCell::encodeFormula( int _col, int _row ) { if ( _col == -1 ) _col = m_iColumn; if ( _row == -1 ) _row = m_iRow; QString erg = ""; if(m_strText.isEmpty()) return m_strText; bool fix1 = FALSE; bool fix2 = FALSE; bool onNumber = false; unsigned int pos = 0; const unsigned int length = m_strText.length(); // All this can surely be made 10 times faster, but I just "ported" it to QString // without any attempt to optimize things -- this is really brittle (Werner) while ( pos < length ) { if ( m_strText[pos] == '"' ) { erg += m_strText[pos++]; while ( pos < length && m_strText[pos] != '"' ) // till the end of the world^H^H^H "string" { erg += m_strText[pos++]; // Allow escaped double quotes (\") if ( pos < length && m_strText[pos] == '\\' && m_strText[pos+1] == '"' ) { erg += m_strText[pos++]; erg += m_strText[pos++]; } } if ( pos < length ) // also copy the trailing double quote erg += m_strText[pos++]; onNumber = false; } else if ( m_strText[pos].isDigit() ) { erg += m_strText[pos++]; fix1 = fix2 = FALSE; onNumber = true; } else if ( m_strText[pos] != '$' && !m_strText[pos].isLetter() ) { erg += m_strText[pos++]; fix1 = fix2 = FALSE; onNumber = false; } else { QString tmp = ""; if ( m_strText[pos] == '$' ) { tmp = "$"; pos++; fix1 = TRUE; } if ( m_strText[pos].isLetter() ) { QString buffer; unsigned int pos2 = 0; while ( pos < length && m_strText[pos].isLetter() ) { tmp += m_strText[pos]; buffer[pos2++] = m_strText[pos++]; } if ( m_strText[pos] == '$' ) { tmp += "$"; pos++; fix2 = TRUE; } if ( m_strText[pos].isDigit() ) { const unsigned int oldPos = pos; while ( pos < length && m_strText[pos].isDigit() ) ++pos; int row = 0; if ( pos != oldPos ) row = m_strText.mid(oldPos, pos-oldPos).toInt(); // Is it a table name || is it a function name like DEC2HEX /* or if we're parsing a number, this could just be the exponential part of it (1.23E4) */ if ( ( m_strText[pos] == '!' ) || m_strText[pos].isLetter() || onNumber ) { erg += tmp; fix1 = fix2 = FALSE; pos = oldPos; } else // It must be a cell identifier { //now calculate the row as integer value int col = 0; col = util_decodeColumnLabelText( buffer ); if ( fix1 ) erg += QString( "$%1" ).arg( col ); else erg += QString( "#%1" ).arg( col - _col ); if ( fix2 ) erg += QString( "$%1#").arg( row ); else erg += QString( "#%1#" ).arg( row - _row ); } } else { erg += tmp; fix1 = fix2 = FALSE; } } else { erg += tmp; fix1 = FALSE; } onNumber = false; } } return erg; } QString KSpreadCell::decodeFormula( const QString &_text, int _col, int _row ) { if ( _col == -1 ) _col = m_iColumn; if ( _row == -1 ) _row = m_iRow; QString erg = ""; unsigned int pos = 0; const unsigned int length = _text.length(); if ( _text.isEmpty() ) return QString(); while ( pos < length ) { if ( _text[pos] == '"' ) { erg += _text[pos++]; while ( pos < length && _text[pos] != '"' ) { erg += _text[pos++]; // Allow escaped double quotes (\") if ( pos < length && _text[pos] == '\\' && _text[pos+1] == '"' ) { erg += _text[pos++]; erg += _text[pos++]; } } if ( pos < length ) erg += _text[pos++]; } else if ( _text[pos] == '#' || _text[pos] == '$' ) { bool fix1 = FALSE; bool fix2 = FALSE; if ( _text[pos++] == '$' ) fix1 = TRUE; int col = 0; unsigned int oldPos = pos; while ( pos < length && ( _text[pos].isDigit() || _text[pos] == '-' ) ) ++pos; if ( pos != oldPos ) col = _text.mid(oldPos, pos-oldPos).toInt(); if ( !fix1 ) col += _col; // Skip '#' or '$' if ( _text[pos++] == '$' ) fix2 = TRUE; int row = 0; oldPos = pos; while ( pos < length && ( _text[pos].isDigit() || _text[pos] == '-' ) ) ++pos; if ( pos != oldPos ) row = _text.mid(oldPos, pos-oldPos).toInt(); if ( !fix2 ) row += _row; // Skip '#' or '$' ++pos; if ( row < 1 || col < 1 || row > KS_rowMax || col > KS_colMax ) { kdDebug(36001) << "KSpreadCell::decodeFormula: row or column out of range (col: " << col << " | row: " << row << ")" << endl; erg = "=\"#### " + i18n("REFERENCE TO COLUMN OR ROW IS OUT OF RANGE") + "\""; return erg; } if ( fix1 ) erg += "$"; erg += util_encodeColumnLabelText(col); //Get column text if ( fix2 ) erg += "$"; erg += QString::number( row ); } else erg += _text[pos++]; } return erg; } void KSpreadCell::freeAllObscuredCells() { // // Free all obscured cells. // if ( !testFlag(Flag_ForceExtra) ) { for ( int x = m_iColumn; x <= m_iColumn + m_iExtraXCells; ++x ) for ( int y = m_iRow; y <= m_iRow + m_iExtraYCells; ++y ) if ( x != m_iColumn || y != m_iRow ) { KSpreadCell *cell = m_pTable->cellAt( x, y ); cell->unobscure(); } m_iExtraXCells = 0; m_iExtraYCells = 0; } } // ##### Are _col and _row really needed ? void KSpreadCell::makeLayout( QPainter &_painter, int _col, int _row ) { // Yes they are: they are useful if this is the default layout, // in which case m_iRow and m_iColumn are 0 and 0, but col and row // are the real coordinates of the cell. m_nbLines = 0; clearFlag(Flag_CellTooShort); freeAllObscuredCells(); ColumnLayout *cl1 = m_pTable->columnLayout( column() ); RowLayout *rl1 = m_pTable->rowLayout( row() ); if( cl1->isHide() || (rl1->height()<=2)) return; /** * RichText */ if ( m_pQML ) { // Calculate how many cells we could use in addition right hand // Never use more then 10 cells. int right = 0; int max_width = width( _col ); bool ende = false; int c; m_pQML->setWidth( &_painter, max_width ); for( c = _col + 1; !ende && c <= _col + 10; ++c ) { KSpreadCell *cell = m_pTable->cellAt( c, _row ); if ( cell && !cell->isEmpty() ) ende = true; else { ColumnLayout *cl = m_pTable->columnLayout( c ); max_width += cl->width(); // Can we make use of extra cells ? int h = m_pQML->height(); m_pQML->setWidth( &_painter, max_width ); if ( m_pQML->height() < h ) ++right; else { max_width -= cl->width(); m_pQML->setWidth( &_painter, max_width ); ende = true; } } } // How may space do we need now ? // m_pQML->setWidth( &_painter, max_width ); int h = m_pQML->height(); int w = m_pQML->width(); kdDebug(36001) << "QML w=" << w << " max=" << max_width << endl; // Occupy the needed extra cells in horizontal direction max_width = width( _col ); ende = ( max_width >= w ); for( c = _col + 1; !ende && c <= _col + right; ++c ) { KSpreadCell *cell = m_pTable->nonDefaultCell( c, _row ); cell->obscure( this ); ColumnLayout *cl = m_pTable->columnLayout( c ); max_width += cl->width(); if ( max_width >= w ) ende = true; } m_iExtraXCells = c - _col - 1; m_iExtraWidth = ( m_iExtraXCells == 0 ? 0 : max_width ); // Occupy the needed extra cells in vertical direction int max_height = height( 0 ); int r = _row; ende = ( max_height >= h ); for( r = _row + 1; !ende && r < _row + 500; ++r ) { bool empty = true; for( c = _col; !empty && c <= _col + m_iExtraXCells; ++c ) { KSpreadCell *cell = m_pTable->cellAt( c, r ); if ( cell && !cell->isEmpty() ) empty = false; } if ( !empty ) ende = true; else { // Occupy this row for( c = _col; c <= _col + m_iExtraXCells; ++c ) { KSpreadCell *cell = m_pTable->nonDefaultCell( c, r ); cell->obscure( this ); } RowLayout *rl = m_pTable->rowLayout( r ); max_height += rl->height(); if ( max_height >= h ) ende = true; } } m_iExtraYCells = r - _row - 1; m_iExtraHeight = ( m_iExtraYCells == 0 ? 0 : max_height ); clearFlag(Flag_LayoutDirty); return; } // Apply text format // (this is the only format that dictates the datatype, it's usually the other way round) if ( formatType() == Text_format ) m_dataType = StringData; /** * A usual numeric, boolean, date, time or string value. */ ///// What's this ??? (David) //QPen tmpPen; //tmpPen.setColor( textColor( _col, _row ) ); //setTextPen(tmpPen); // m_conditionIsTrue = false; //tmpPen = textPen(_col,_row); //// Warning: if you re-enable the line above, apply the textColorPrint // stuff. Never call QPainter::setPen with an invalid pen ! (David) // // Turn the stored value in a string // if ( isFormula() && m_pTable->getShowFormula() ) { m_strOutText = m_strText; } else if ( m_style == ST_Select ) { // If this is a select box, find out about the selected item // in the KSpreadPrivate data struct SelectPrivate *s = (SelectPrivate*)m_pPrivate; m_strOutText = s->text(); } else if ( isBool() ) { m_strOutText = (valueBool()) ? i18n("True") : i18n("False"); } else if( isDate() ) { m_strOutText=util_dateFormat( locale(), valueDate(), formatType() ); } else if( isTime() ) { m_strOutText=util_timeFormat( locale(), valueTime(), formatType() ); } else if ( isNumeric() ) { // First get some locale information if (!decimal_point) { // (decimal_point is static) decimal_point = locale()->decimalSymbol()[0]; kdDebug(36001) << "decimal_point is '" << decimal_point.unicode() << "'" << endl; if ( decimal_point.isNull() ) decimal_point = '.'; } // Scale the value as desired by the user. double v = valueDouble() * factor(column(),row()); // Always unsigned ? if ( floatFormat( _col, _row ) == KSpreadCell::AlwaysUnsigned && v < 0.0) v *= -1.0; // Make a string out of it. QString localizedNumber = createFormat( v, _col, _row ); // Remove trailing zeros and the decimal point if necessary // unless the number has no decimal point if ( precision( _col, _row)== -1 && localizedNumber.find(decimal_point) >= 0 ) { int start=0; if(localizedNumber.find('%')!=-1) start=2; else if(localizedNumber.find( locale()->currencySymbol())==((int)(localizedNumber.length()-locale()->currencySymbol().length()))) start=locale()->currencySymbol().length()+1; else if((start=localizedNumber.find('E'))!=-1) start=localizedNumber.length()-start; else start=0; int i = localizedNumber.length()-start; bool bFinished = FALSE; while ( !bFinished && i > 0 ) { QChar ch = localizedNumber[ i - 1 ]; if ( ch == '0' ) localizedNumber.remove(--i,1); else { bFinished = TRUE; if ( ch == decimal_point ) localizedNumber.remove(--i,1); } } } // Start building the output string with prefix and postfix m_strOutText = ""; if( !prefix( _col, _row ).isEmpty()) m_strOutText += prefix( _col, _row )+" "; m_strOutText += localizedNumber; if( !postfix( _col, _row ).isEmpty()) m_strOutText += " "+postfix( _col, _row ); // This method only calculates the text, and its width. // No need to bother about the color (David) #if 0 // Find the correct color which depends on the conditions // and the sign of the value. KSpreadCondition condition; if ( floatColor( _col, _row ) == KSpreadCell::NegRed && v < 0.0 ) tmpPen.setColor( Qt::red ); else if( conditions.GetCurrentCondition(condition) ) { tmpPen.setColor(condition.colorcond); } #endif } else if ( isFormula() ) { m_strOutText = m_strFormulaOut; } else if( isString() ) { if (!m_strText.isEmpty() && m_strText[0]=='\'' ) m_strOutText = m_strText.right(m_strText.length()-1); else m_strOutText = m_strText; } else // When does this happen ? { kdDebug(36001) << "Please report: final case of makeLayout ... m_dataType=" << m_dataType << " m_strText=" << m_strText << endl; m_strOutText = m_strText; } // Empty text? if ( m_strOutText.isEmpty() ) { m_strOutText = QString::null; if ( isDefault() ) return; } // // Determine the correct font // KSpreadConditional condition; if( conditions.GetCurrentCondition(condition) && !m_pTable->getShowFormula() ) { _painter.setFont( condition.fontcond ); } else { _painter.setFont( textFont(_col,_row ) ); } // Calculate text dimensions textSize(_painter); QFontMetrics fm = _painter.fontMetrics(); // // Calculate the size of the cell // RowLayout *rl = m_pTable->rowLayout( m_iRow ); ColumnLayout *cl = m_pTable->columnLayout( m_iColumn ); int w = cl->width(); int h = rl->height(); // Calculate the extraWidth and extraHeight if we are forced to. if ( testFlag(Flag_ForceExtra) ) { for ( int x = _col + 1; x <= _col + m_iExtraXCells; x++ ) { ColumnLayout *cl = m_pTable->columnLayout( x ); w += cl->width() ; } for ( int y = _row + 1; y <= _row + m_iExtraYCells; y++ ) { RowLayout *rl = m_pTable->rowLayout( y ); h += rl->height() ; } } m_iExtraWidth = w; m_iExtraHeight = h; // Some space for the little button of the combo box if ( m_style == ST_Select ) w -= 16; // Do we need to break the line into multiple lines and are we allowed to // do so? int lines = 1; if ( m_iOutTextWidth > w - 2 * BORDER_SPACE - leftBorderWidth( _col, _row) - rightBorderWidth( _col, _row ) && multiRow(_col, _row ) ) { // copy of m_strOutText QString o = m_strOutText; // No space ? if( o.find(' ') != -1 ) { o += ' '; int start = 0; int pos = 0; int pos1 = 0; m_strOutText = ""; do { pos = o.find(' ',pos ); int width = fm.width( m_strOutText.mid( start, (pos1-start) ) + o.mid( pos1, (pos-pos1) ) ); if ( width <= w - 2 * BORDER_SPACE - leftBorderWidth( _col, _row) - rightBorderWidth( _col, _row ) ) { m_strOutText += o.mid(pos1,(pos-pos1)); pos1 = pos; } else { if(o.at(pos1)==' ') pos1 = pos1 + 1; if(pos1!=0 && pos!=-1) { m_strOutText += "\n" + o.mid( pos1, ( pos - pos1 ) ); lines++; } else m_strOutText += o.mid( pos1, ( pos - pos1 ) ); start = pos1; pos1 = pos; } pos++; } while( o.find( ' ', pos ) != -1 ); } m_iOutTextHeight *= lines; m_nbLines = lines; m_iTextX = 0; // Calculate the maximum width QString t; int i; int pos = 0; m_iOutTextWidth = 0; do { i = m_strOutText.find( "\n", pos ); if ( i == -1 ) t = m_strOutText.mid( pos, m_strOutText.length() - pos ); else { t = m_strOutText.mid( pos, i - pos ); pos = i + 1; } int tw = fm.width( t ); if ( tw > m_iOutTextWidth ) m_iOutTextWidth = tw; } while ( i != -1 ); } // Calculate m_iTextX and m_iTextY offsetAlign(_col,_row); m_fmAscent = fm.ascent(); int indent=0; int a = defineAlignX(); //apply indent if text is align to left not when text is at right or middle if( a==KSpreadCell::Left && !isEmpty()) indent=getIndent(column(),row()); if( verticalText( column(), row() ) ||getAngle(column(), row())!=0) { RowLayout *rl = m_pTable->rowLayout( row() ); if(m_iOutTextHeight>=rl->height()) { setFlag(Flag_CellTooShort); } } // Do we have to occupy additional cells right hand ? if ( m_iOutTextWidth+indent > w - 2 * BORDER_SPACE - leftBorderWidth( _col, _row) - rightBorderWidth( _col, _row ) ) { // No chance. We can not obscure more/less cells. if ( testFlag(Flag_ForceExtra) ) { // The text does not fit in the cell //m_strOutText = "**"; setFlag(Flag_CellTooShort); } else { int c = m_iColumn; int end = 0; // Find free cells right hand to this one while ( !end ) { ColumnLayout *cl2 = m_pTable->columnLayout( c + 1 ); KSpreadCell *cell = m_pTable->visibleCellAt( c + 1, m_iRow ); if ( cell->isEmpty() ) { w += cl2->width() - 1; c++; // Enough space ? if ( m_iOutTextWidth+indent <= w - 2 * BORDER_SPACE - leftBorderWidth( _col, _row) - rightBorderWidth( _col, _row ) ) end = 1; } // Not enough space, but the next cell is not empty else end = -1; } // Dont occupy additional space for right aligned or centered text or values. // ##### Why ? if (( align(_col,_row) == KSpreadCell::Left || align(_col,_row) == KSpreadCell::Undefined) && !isNumeric() && !isBool()) { m_iExtraWidth = w; for( int i = m_iColumn + 1; i <= c; ++i ) { KSpreadCell *cell = m_pTable->nonDefaultCell( i, m_iRow ); cell->obscure( this ); } m_iExtraXCells = c - m_iColumn; //Not enough space if(end==-1) { setFlag(Flag_CellTooShort); } } else { setFlag(Flag_CellTooShort); } } } clearFlag(Flag_LayoutDirty); } QString KSpreadCell::createFormat( double value, int _col, int _row ) { // if precision is -1, ask for a huge number of decimals, we'll remove // the zeros later. Is 8 ok ? int p = (precision(_col,_row) == -1) ? 8 : precision(_col,_row) ; QString localizedNumber= locale()->formatNumber( value, p ); int pos = 0; switch( formatType() ) { case Number: localizedNumber = locale()->formatNumber(value, p); if( floatFormat( _col, _row ) == KSpreadCell::AlwaysSigned && value >= 0 ) { if(locale()->positiveSign().isEmpty()) localizedNumber='+'+localizedNumber; } break; case Percentage: localizedNumber = locale()->formatNumber(value, p)+ " %"; if( floatFormat( _col, _row ) == KSpreadCell::AlwaysSigned && value >= 0 ) { if(locale()->positiveSign().isEmpty()) localizedNumber='+'+localizedNumber; } break; case Money: localizedNumber = locale()->formatMoney(value,locale()->currencySymbol(),p ); if( floatFormat( _col, _row) == KSpreadCell::AlwaysSigned && value >= 0 ) { if(locale()->positiveSign().isNull()) localizedNumber='+'+localizedNumber; } break; case Scientific: localizedNumber= QString::number(value, 'E', p); if((pos=localizedNumber.find('.'))!=-1) localizedNumber=localizedNumber.replace(pos,1,decimal_point); if( floatFormat( _col, _row ) == KSpreadCell::AlwaysSigned && value >= 0 ) { if(locale()->positiveSign().isEmpty()) localizedNumber='+'+localizedNumber; } break; case ShortDate: case TextDate: case date_format1: case date_format2: case date_format3: case date_format4: case date_format5: case date_format6: case date_format7: case date_format8: case date_format9: case date_format10: case date_format11: case date_format12: case date_format13: case date_format14: case date_format15: case date_format16: case date_format17: case Text_format: break; case fraction_half: case fraction_quarter: case fraction_eighth: case fraction_sixteenth: case fraction_tenth: case fraction_hundredth: case fraction_one_digit: case fraction_two_digits: case fraction_three_digits: localizedNumber=util_fractionFormat( value, formatType() ); if( floatFormat( _col, _row ) == KSpreadCell::AlwaysSigned && value >= 0 ) { if(locale()->positiveSign().isEmpty()) localizedNumber='+'+localizedNumber; } break; default : kdDebug(36001)<<"Error in m_eFormatNumber\n"; break; } return localizedNumber; } void KSpreadCell::offsetAlign( int _col,int _row ) { int a = align(_col,_row); RowLayout *rl = m_pTable->rowLayout( _row ); ColumnLayout *cl = m_pTable->columnLayout( _col ); int w = cl->width(); int h = rl->height(); if ( m_iExtraXCells ) w = m_iExtraWidth; if ( m_iExtraYCells ) h = m_iExtraHeight; int tmpAngle=getAngle(_col,_row); switch( alignY(_col,_row) ) { case KSpreadCell::Top: if(tmpAngle!=0) m_iTextY = topBorderWidth( _col, _row) + BORDER_SPACE +m_fmAscent; else { if(tmpAngle<0) m_iTextY = topBorderWidth( _col, _row) + BORDER_SPACE ; else m_iTextY = topBorderWidth( _col, _row) + BORDER_SPACE +(int)(m_fmAscent*cos(tmpAngle*M_PI/180)); } break; case KSpreadCell::Bottom: if(!verticalText(_col,_row) && !multiRow(_col,_row) && !tmpAngle) m_iTextY = h - BORDER_SPACE - bottomBorderWidth( _col, _row ); else if(tmpAngle!=0) { if((h - BORDER_SPACE - m_iOutTextHeight- bottomBorderWidth( _col, _row ))>0) { if( tmpAngle < 0 ) m_iTextY = h - BORDER_SPACE - m_iOutTextHeight- bottomBorderWidth( _col, _row ); else m_iTextY = h - BORDER_SPACE - m_iOutTextHeight- bottomBorderWidth( _col, _row )+(int)(m_fmAscent*cos(tmpAngle*M_PI/180)); } else { if( tmpAngle < 0 ) m_iTextY = topBorderWidth( _col, _row) + BORDER_SPACE ; else m_iTextY = topBorderWidth( _col, _row) + BORDER_SPACE +(int)(m_fmAscent*cos(tmpAngle*M_PI/180)); } } else if( multiRow(_col,_row) ) { int tmpline=m_nbLines; if(m_nbLines>1) tmpline=m_nbLines-1; if((h - BORDER_SPACE - m_iOutTextHeight*m_nbLines- bottomBorderWidth( _col, _row ))>0) m_iTextY = h - BORDER_SPACE - m_iOutTextHeight*tmpline- bottomBorderWidth( _col, _row ); else m_iTextY = topBorderWidth( _col, _row) + BORDER_SPACE +m_fmAscent; } else if((h - BORDER_SPACE - m_iOutTextHeight- bottomBorderWidth( _col, _row ))>0) m_iTextY = h - BORDER_SPACE - m_iOutTextHeight- bottomBorderWidth( _col, _row )+m_fmAscent; else m_iTextY = topBorderWidth( _col, _row) + BORDER_SPACE +m_fmAscent; break; case KSpreadCell::Middle: if(!verticalText(_col,_row) && !multiRow(_col,_row) && !tmpAngle) m_iTextY = ( h - m_iOutTextHeight ) / 2 +m_fmAscent; else if( tmpAngle != 0 ) { if( ( h - m_iOutTextHeight ) > 0 ) { if( tmpAngle < 0 ) m_iTextY = ( h - m_iOutTextHeight ) / 2 ; else m_iTextY = ( h - m_iOutTextHeight ) / 2 +(int)(m_fmAscent*cos(tmpAngle*M_PI/180)); } else { if( tmpAngle < 0 ) m_iTextY = topBorderWidth( _col, _row) + BORDER_SPACE ; else m_iTextY = topBorderWidth( _col, _row) + BORDER_SPACE +(int)(m_fmAscent*cos(tmpAngle*M_PI/180)); } } else if( multiRow(_col,_row) ) { int tmpline=m_nbLines; if(m_nbLines==0) tmpline=1; if(( h - m_iOutTextHeight*tmpline )>0) m_iTextY = ( h - m_iOutTextHeight*tmpline ) / 2 +m_fmAscent; else m_iTextY = topBorderWidth( _col, _row) + BORDER_SPACE +m_fmAscent; } else if(( h - m_iOutTextHeight )>0) m_iTextY = ( h - m_iOutTextHeight ) / 2 +m_fmAscent; else m_iTextY = topBorderWidth( _col, _row) + BORDER_SPACE +m_fmAscent; break; } a=defineAlignX(); if(m_pTable->getShowFormula()) a = KSpreadCell::Left; switch( a ) { case KSpreadCell::Left: m_iTextX = leftBorderWidth( _col, _row) + BORDER_SPACE; break; case KSpreadCell::Right: m_iTextX = w - BORDER_SPACE - m_iOutTextWidth - rightBorderWidth( _col, _row ); break; case KSpreadCell::Center: m_iTextX = ( w - m_iOutTextWidth ) / 2; break; } } void KSpreadCell::textSize( QPainter &_paint ) { QFontMetrics fm = _paint.fontMetrics(); // Horizontal text ? int tmpAngle=getAngle(column(),row()); if( !verticalText(column(),row()) && !tmpAngle ) { m_iOutTextWidth = fm.width( m_strOutText ); int offsetFont=0; if((alignY(column(),row())==KSpreadCell::Bottom)&& textFontUnderline(column(), row() )) { offsetFont=fm.underlinePos()+1; } m_iOutTextHeight = fm.ascent() + fm.descent()+offsetFont ; } // Rotated text ? else if( tmpAngle!= 0 ) { m_iOutTextHeight = static_cast(cos(tmpAngle*M_PI/180)*(fm.ascent() + fm.descent())+abs((int)(fm.width( m_strOutText )*sin(tmpAngle*M_PI/180)))); m_iOutTextWidth = static_cast(abs((int)(sin(tmpAngle*M_PI/180)*(fm.ascent() + fm.descent())))+fm.width( m_strOutText )*cos(tmpAngle*M_PI/180)); //kdDebug(36001)<<"m_iOutTextWidth"<getShowFormula() ) { _paint.setFont( condition.fontcond ); } else { _paint.setFont( textFont(_col,_row ) ); } textSize(_paint); offsetAlign(_col,_row); } bool KSpreadCell::makeFormula() { clearFormula(); KSContext context; // We have to transform the numerical values back into a non-localized form, // so that they can be parsed by kscript (David) // To be moved to a separate function when it is properly implemented... // or should we use strtod on each number found ? Impossible since kscript // would have to parse the localized version... // HACK (only handles decimal point) // ############# Torben: Incredible HACK. Separating parameters in a function call // will be horribly broken since "," -> "." :-(( // ### David: Ouch ! Argl. // // ############# Torben: Do not replace stuff in strings. // // ###### David: we should use KLocale's conversion (there is a method there // for understanding numbers typed the localised way) for each number the // user enters, not once the full formula is set up, then ? // I don't see how we can do that... // Or do you see kscript parsing localized values ? // // Oh, Excel uses ';' to separate function arguments (at least when // the decimal separator is ','), so that it can process a formula with numbers // using ',' as a decimal separator... // Sounds like kscript should have configurable argument separator... // /*QString sDelocalizedText ( m_strText ); int pos=0; while ( ( pos = sDelocalizedText.find( decimal_point, pos ) ) >= 0 ) sDelocalizedText.replace( pos++, 1, "." ); // At least, =2,5+3,2 is turned into =2.5+3.2, which can get parsed... */ m_pCode = m_pTable->doc()->interpreter()->parse( context, m_pTable, /*sDelocalizedText*/m_strText, m_lstDepends ); // Did a syntax error occur ? if ( context.exception() ) { clearFormula(); setFlag(Flag_Error); m_strFormulaOut = "####"; m_dataType = StringData; // correct? m_dValue = 0.0; setFlag(Flag_LayoutDirty); DO_UPDATE; if (m_pTable->doc()->getShowMessageError()) { QString tmp(i18n("Error in cell %1\n\n")); tmp = tmp.arg( util_cellName( m_pTable, m_iColumn, m_iRow ) ); tmp += context.exception()->toString( context ); KMessageBox::error( (QWidget*)0L, tmp); } return false; } /* notify the new dependancy list that we are depending on them now */ NotifyDependancyList(m_lstDepends, true); return true; } void KSpreadCell::clearFormula() { /*notify dependancies that we're not depending on them any more */ NotifyDependancyList(m_lstDepends, false); m_lstDepends.clear(); if ( m_pCode ) { delete m_pCode; m_pCode = NULL; } } bool KSpreadCell::calc(bool delay) { if ( testFlag(Flag_Progress) ) { kdError(36002) << "ERROR: Circle" << endl; setFlag(Flag_Error); m_strFormulaOut = "####"; m_dataType = StringData; // correct? setFlag(Flag_LayoutDirty); if ( m_style == ST_Select ) { SelectPrivate *s = (SelectPrivate*)m_pPrivate; s->parse( m_strFormulaOut ); } // DO_UPDATE; return false; } if ( !isFormula() ) return true; if ( !testFlag(Flag_CalcDirty) ) return true; if (delay) { if ( m_pTable->doc()->delayCalculation() ) { return true; } } setFlag(Flag_LayoutDirty); setFlag(Flag_Progress); clearFlag(Flag_CalcDirty); if (m_pCode == NULL) { makeFormula(); } KSpreadDependency *dep; for ( dep = m_lstDepends.first(); dep != 0L; dep = m_lstDepends.next() ) { for ( int x = dep->Left(); x <= dep->Right(); x++ ) { for ( int y = dep->Top(); y <= dep->Bottom(); y++ ) { KSpreadCell *cell = dep->Table()->cellAt( x, y ); if ( cell == NULL ) { return false; } if ( !cell->calc() ) { m_strFormulaOut = "####"; setFlag(Flag_Error); m_dataType = StringData; //correct? clearFlag(Flag_Progress); if ( m_style == ST_Select ) { SelectPrivate *s = (SelectPrivate*)m_pPrivate; s->parse( m_strFormulaOut ); } setFlag(Flag_LayoutDirty); DO_UPDATE; return false; } } } } KSContext& context = m_pTable->doc()->context(); if ( !m_pCode || !m_pTable->doc()->interpreter()->evaluate( context, m_pCode, m_pTable ) ) { // If we got an error during evaluation ... if ( m_pCode ) { setFlag(Flag_Error); m_strFormulaOut = "####"; m_dataType = StringData; //correct? setFlag(Flag_LayoutDirty); DO_UPDATE; // Print out exception if any if ( context.exception() && m_pTable->doc()->getShowMessageError()) { QString tmp(i18n("Error in cell %1\n\n")); tmp = tmp.arg( util_cellName( m_pTable, m_iColumn, m_iRow ) ); tmp += context.exception()->toString( context ); KMessageBox::error( (QWidget*)0L, tmp); } } // setFlag(Flag_LayoutDirty); clearFlag(Flag_Progress); if ( m_style == ST_Select ) { SelectPrivate *s = (SelectPrivate*)m_pPrivate; s->parse( m_strFormulaOut ); DO_UPDATE; } return false; } else if ( context.value()->type() == KSValue::DoubleType ) { m_dValue = context.value()->doubleValue(); clearFlag(Flag_Error); m_dataType = NumericData; checkNumberFormat(); // auto-chooses number or scientific // Format the result appropriately m_strFormulaOut = createFormat( valueDouble(), m_iColumn, m_iRow ); } else if ( context.value()->type() == KSValue::IntType ) { m_dValue = (double)context.value()->intValue(); clearFlag(Flag_Error); m_dataType = NumericData; checkNumberFormat(); // auto-chooses number or scientific // Format the result appropriately m_strFormulaOut = createFormat( valueDouble(), m_iColumn, m_iRow ); } else if ( context.value()->type() == KSValue::BoolType ) { clearFlag(Flag_Error); m_dataType = BoolData; m_dValue = context.value()->boolValue() ? 1.0 : 0.0; m_strFormulaOut = context.value()->boolValue() ? i18n("True") : i18n("False"); setFormatType(Number); } else if ( context.value()->type() == KSValue::TimeType ) { clearFlag(Flag_Error); m_dataType = TimeData; m_Time = context.value()->timeValue(); //change format FormatType tmpFormat = formatType(); if( tmpFormat != SecondeTime && tmpFormat != Time_format1 && tmpFormat != Time_format2 && tmpFormat != Time_format3) { m_strFormulaOut = locale()->formatTime(valueTime(), false); setFormatType(Time); } else { m_strFormulaOut = util_timeFormat(locale(), valueTime(), formatType()); } } else if ( context.value()->type() == KSValue::DateType) { clearFlag(Flag_Error); m_dataType = DateData; m_Date = context.value()->dateValue(); FormatType tmpFormat = formatType(); if( tmpFormat != TextDate && !(tmpFormat>=200 &&tmpFormat<=216)) { setFormatType(ShortDate); m_strFormulaOut = locale()->formatDate(valueDate(), true); } else { m_strFormulaOut = util_dateFormat( locale(), valueDate(), tmpFormat); } } else if ( context.value()->type() == KSValue::Empty ) { m_dValue = 0.0; clearFlag(Flag_Error); m_dataType = StringData; // Format the result appropriately setFormatType(Number); m_strFormulaOut = createFormat( valueDouble(), m_iColumn, m_iRow ); } else { if ( m_pQML ) delete m_pQML; m_pQML = 0; clearFlag(Flag_Error); m_dataType = StringData; m_strFormulaOut = context.value()->toString( context ); if ( !m_strFormulaOut.isEmpty() && m_strFormulaOut[0] == '!' ) { m_pQML = new QSimpleRichText( m_strFormulaOut.mid(1), QApplication::font() );//, m_pTable->widget() ); } else if( !m_strFormulaOut.isEmpty() && m_strFormulaOut[0]=='\'') { m_strFormulaOut=m_strFormulaOut.right(m_strFormulaOut.length()-1); } else m_strFormulaOut=m_strFormulaOut; setFormatType(Text_format); } if ( m_style == ST_Select ) { SelectPrivate *s = (SelectPrivate*)m_pPrivate; s->parse( m_strFormulaOut ); } clearFlag(Flag_CalcDirty); setFlag(Flag_LayoutDirty); clearFlag(Flag_Progress); DO_UPDATE; return true; } QString KSpreadCell::valueString() const { if ( m_style == ST_Select ) return ((SelectPrivate*)m_pPrivate)->text(); if ( isFormula() ) return m_strFormulaOut; return m_strText; } /** * @param pos describes what to draw. The value for edges are: * top = 1, right = 2, bottom = 3, left = 4. * The values for corners are: * top left = 11, top_right = 12, bottom_right = 13, bottom_left = 14. */ static void paintCellHelper( QPainter& _painter, int _tx, int _ty, int col, int row, int w, int h, int pos, const QRect& marker ) { QPoint p = marker.bottomRight(); switch( pos ) { // top case 1: if ( p.x() == col && p.y() == row - 1 ) { _painter.drawLine( _tx, _ty, _tx + w - 3, _ty ); _painter.fillRect( _tx + w - 2, _ty - 1, 5, 5, _painter.pen().color() ); } else _painter.drawLine( _tx, _ty, _tx + w, _ty ); break; // bottom case 3: if ( p.x() == col && p.y() == row ) { _painter.drawLine( _tx, _ty + h, _tx + w - 3, _ty + h ); _painter.fillRect( _tx + w - 2, _ty + h - 1, 5, 5, _painter.pen().color() ); } else _painter.drawLine( _tx, _ty + h, _tx + w, _ty + h ); break; // left case 4: if ( p.x() == col - 1 && p.y() == row ) { _painter.drawLine( _tx, _ty - 1, _tx, _ty + h - 3 ); _painter.fillRect( _tx - 2, _ty + h - 1, 5, 5, _painter.pen().color() ); } else _painter.drawLine( _tx, _ty - 1, _tx, _ty + h + 2 ); break; // right case 2: if ( p.x() == col && p.y() == row ) { _painter.drawLine( _tx + w, _ty - 1, _tx + w, _ty + h - 3 ); _painter.fillRect( _tx + w - 2, _ty + h - 1, 5, 5, _painter.pen().color() ); } else _painter.drawLine( _tx + w, _ty - 1, _tx + w, _ty + h + 2 ); break; // top left case 11: if ( p.x() == col - 1 && p.y() == row - 1 ) _painter.fillRect( _tx - 2, _ty - 1, 5, 5, _painter.pen().color() ); else _painter.drawLine( _tx, _ty, _tx, _ty + 1 ); break; // top right case 12: _painter.drawLine( _tx + w, _ty - 1, _tx + w, _ty + 1 ); break; // bottom right case 13: _painter.drawLine( _tx + w, _ty + h - 1, _tx + w, _ty + h + 1 ); break; // bottom left case 14: _painter.drawLine( _tx, _ty + h - 1, _tx, _ty + h + 1 ); break; } } void KSpreadCell::paintCell( const QRect& rect, QPainter &painter, QPoint corner, QPoint cellRef, bool drawCursor ) { static int paintingObscured = 0; /* this flag indicates that we are working on drawing the cells that a cell is obscuring. The value is the number of levels down we are currently working -- i.e. a cell obscured by a cell which is obscured by a cell. */ /* if we're working on drawing an obscured cell, that means this cell should have a cell that obscured it. */ Q_ASSERT(!(paintingObscured > 0 && m_pObscuringCell == NULL)); /* the cellref passed in should be this cell -- except if this is the default cell */ Q_ASSERT(!(((cellRef.x() != m_iColumn) || (cellRef.y() != m_iRow)) && !isDefault())); ColumnLayout* colLayout = m_pTable->columnLayout(cellRef.x()); RowLayout* rowLayout = m_pTable->rowLayout(cellRef.y()); int height = (m_iExtraYCells ? m_iExtraHeight : rowLayout->height()); int width = (m_iExtraXCells ? m_iExtraWidth : colLayout->width()); bool selected = m_pTable->isCellSelected(cellRef.x(), cellRef.y()); // Dont draw any selection when printing. if ( painter.device()->isExtDev() || !drawCursor) selected = false; calc(); // Need to make a new layout ? if ( testFlag(Flag_LayoutDirty) ) makeLayout( painter, cellRef.x(), cellRef.y() ); QRect r2( corner.x(), corner.y(), width, height ); if ( !r2.intersects( rect ) ) return; if (!isObscuringForced()) { paintBackground(painter, corner, cellRef, selected); } paintDefaultBorders(painter, corner, cellRef); paintCellBorders(painter, corner, cellRef); /* paint all the cells that this one obscures */ paintingObscured++; paintObscuredCells(rect, painter, corner, cellRef); paintingObscured--; /* now print content, if this cell isn't obscured */ if (!isObscured()) /* don't paint content if this cell is obscured */ { paintCommentIndicator(painter, corner, cellRef); paintMoreTextIndicator(painter, corner, cellRef); /** * QML ? */ if ( m_pQML && (!painter.device()->isExtDev() || !getDontprintText(cellRef.x(), cellRef.y()) )) { painter.save(); m_pQML->draw( &painter, corner.x(), corner.y(), QRegion( QRect( corner.x(), corner.y(), colLayout->width(), rowLayout->height() ) ), QApplication::palette().active(), 0 ); painter.restore(); } /** * Usual Text */ else if ( !m_strOutText.isEmpty() && (!painter.device()->isExtDev() || !getDontprintText(cellRef.x(),cellRef.y()))) { paintText(painter, corner, cellRef, selected); } } /* if (!isObscured()) */ if (drawCursor) { paintMarker(painter, corner, cellRef); } paintPageBorders( painter,corner, cellRef); if (isObscured() && paintingObscured == 0 && !isObscuringForced()) { /* print the cell obscuring this one */ /* if paintingObscured is > 0, that means drawing this cell was triggered while already drawing the obscuring cell -- don't want to cause an infinite loop */ // Determine the dimension of the cell. QPoint obscuringCellRef(obscuringCellsColumn(), obscuringCellsRow()); QPoint obscuringCellLoc( m_pTable->columnPos(obscuringCellsColumn()), m_pTable->rowPos(obscuringCellsRow())); painter.save(); m_pObscuringCell->paintCell( rect, painter, obscuringCellLoc, obscuringCellRef); painter.restore(); } } /* the following code was commented out in the above function. I'll leave it here in case this functionality is ever re-implemented and someone wants some code to start from */ /** * Modification for drawing the button */ /* if ( m_style == KSpreadCell::ST_Button ) { QBrush fill( Qt::lightGray ); QApplication::style().drawControl( QStyle::CE_PushButton, &_painter, this, QRect( _tx + 1, _ty + 1, w2 - 1, h2 - 1 ), defaultColorGroup ); //, selected, &fill ); } */ /** * Modification for drawing the combo box */ /* else if ( m_style == KSpreadCell::ST_Select ) { QApplication::style().drawComboButton( &_painter, _tx + 1, _ty + 1, w2 - 1, h2 - 1, defaultColorGroup, selected ); } */ void KSpreadCell::paintObscuredCells(const QRect& rect, QPainter& painter, QPoint corner, QPoint cellRef) { // This cell is obscuring other ones? Then we redraw their // background and borders before we paint our content there. if ( extraXCells() || extraYCells() ) { int ypos = corner.y(); for( int y = 0; y <= extraYCells(); ++y ) { int xpos = corner.x(); RowLayout* rl = m_pTable->rowLayout( cellRef.y() + y ); for( int x = 0; x <= extraXCells(); ++ x ) { ColumnLayout* cl = m_pTable->columnLayout( cellRef.x() + x ); if ( y != 0 || x != 0 ) { KSpreadCell* cell = m_pTable->cellAt( cellRef.x() + x, cellRef.y() + y ); cell->paintCell( rect, painter, QPoint(xpos, ypos), QPoint(cellRef.x() + x, cellRef.y() + y)); } xpos += cl->width(); } ypos += rl->height(); } } } void KSpreadCell::paintBackground(QPainter& painter, QPoint corner, QPoint cellRef, bool selected) { QColorGroup defaultColorGroup = QApplication::palette().active(); QRect m = m_pTable->marker(); ColumnLayout* colLayout = m_pTable->columnLayout(cellRef.x()); RowLayout* rowLayout = m_pTable->rowLayout(cellRef.y()); int width = (m_iExtraXCells ? m_iExtraWidth : colLayout->width()); int height = (m_iExtraYCells ? m_iExtraHeight : rowLayout->height()); // Determine the correct background color if ( selected && ( cellRef.x() != m.left() || cellRef.y() != m.top() )) { painter.setBackgroundColor( defaultColorGroup.highlight() ); } else { QColor bg = bgColor( cellRef.x(), cellRef.y() ); if (! painter.device()->isExtDev() ) { if ( bg.isValid() ) { painter.setBackgroundColor( bg ); } else painter.setBackgroundColor( defaultColorGroup.base() ); } else { //bad hack but there is a qt bug //so I can print backgroundcolor QBrush bb( bg ); if( !bg.isValid()) bb.setColor(Qt::white); /* I think this just gets erased below....I'll figure it out later. */ painter.fillRect( corner.x(), corner.y(), width, height, bb ); } } // Erase the background of the cell. if ( !painter.device()->isExtDev() ) painter.eraseRect( corner.x(), corner.y(), width, height ); // Draw a background brush QBrush bb = backGroundBrush( cellRef.x(), cellRef.y() ); if( bb.style() != Qt::NoBrush ) { painter.fillRect( corner.x(), corner.y(), width, height, bb ); } } void KSpreadCell::paintDefaultBorders(QPainter& painter, QPoint corner, QPoint cellRef) { QPen left_pen = leftBorderPen( cellRef.x(), cellRef.y() ); QPen top_pen = topBorderPen( cellRef.x(), cellRef.y() ); ColumnLayout* colLayout = m_pTable->columnLayout(cellRef.x()); RowLayout* rowLayout = m_pTable->rowLayout(cellRef.y()); int height = (m_iExtraYCells ? m_iExtraHeight : rowLayout->height()); int width = (m_iExtraXCells ? m_iExtraWidth : colLayout->width()); /* Each cell is responsible for drawing it's top and left portions of the "default" grid. --Or not drawing it if it shouldn't be there.*/ /* should we do the left border? */ if ( left_pen.style() == Qt::NoPen && ( !m_pObscuringCell || m_pObscuringCell->column() == cellRef.x())) { if( table()->getShowGrid() ) { int dt = 0; QPen t = m_pTable->cellAt( cellRef.x(), cellRef.y() - 1 )->leftBorderPen( cellRef.x(), cellRef.y() - 1 ); if ( t.style() != Qt::NoPen ) dt = 1; int db = 0; if ( cellRef.y() < KS_rowMax ) { QPen b = m_pTable->cellAt( cellRef.x(), cellRef.y() + 1 )->leftBorderPen( cellRef.x(), cellRef.y() + 1 ); if ( b.style() != Qt::NoPen ) db = 1; } painter.setPen( table()->doc()->defaultGridPen() ); painter.drawLine( corner.x(), corner.y() + dt, corner.x(), corner.y() + height - db ); } } if ( top_pen.style() == Qt::NoPen && ( !m_pObscuringCell || m_pObscuringCell->row() == cellRef.y() )) { if( table()->getShowGrid() ) { QPen l = m_pTable->cellAt( cellRef.x(), cellRef.y() - 1 )->leftBorderPen( cellRef.x(), cellRef.y() - 1 ); QPen r = m_pTable->cellAt( cellRef.x(), cellRef.y() - 1 )->rightBorderPen( cellRef.x(), cellRef.y() - 1 ); int dl = 0; if ( l.style() != Qt::NoPen ) dl = ( l.width() - 1 ) / 2 + 1; int dr = 0; if ( r.style() != Qt::NoPen ) dr = r.width() / 2; painter.setPen( table()->doc()->defaultGridPen() ); painter.drawLine( corner.x() + dl, corner.y(), corner.x() + width - dr, corner.y() ); } } } void KSpreadCell::paintCommentIndicator(QPainter& painter, QPoint corner, QPoint cellRef) { // Point the little corner if there is a comment attached // to this cell. ColumnLayout* colLayout = m_pTable->columnLayout(cellRef.x()); RowLayout* rowLayout = m_pTable->rowLayout(cellRef.y()); int width = (m_iExtraYCells ? m_iExtraHeight : colLayout->width()); if( !comment(column(),row()).isEmpty() && rowLayout->height() > 2 && colLayout->width() > 10 && !painter.device()->isExtDev() && table()->doc()->getShowCommentIndicator()) { QPointArray point( 3 ); point.setPoint( 0, corner.x() + width - 10, corner.y() ); point.setPoint( 1, corner.x() + width, corner.y() ); point.setPoint( 2, corner.x() + width, corner.y() + 10 ); painter.setBrush( QBrush(Qt::red ) ); painter.setPen( Qt::NoPen ); painter.drawPolygon( point ); } } void KSpreadCell::paintMoreTextIndicator(QPainter& painter, QPoint corner, QPoint cellRef) { ColumnLayout* colLayout = m_pTable->columnLayout(cellRef.x()); RowLayout* rowLayout = m_pTable->rowLayout(cellRef.y()); int height = (m_iExtraYCells ? m_iExtraHeight : rowLayout->height()); int width = (m_iExtraXCells ? m_iExtraWidth : colLayout->width()); //show a red triangle when it's not possible to write all text in cell //don't print the red triangle if we're printing if(testFlag(Flag_CellTooShort) && !painter.device()->isExtDev() && rowLayout->height() > 2 && colLayout->width() > 4) { QPointArray point( 3 ); point.setPoint( 0, corner.x() + width - 4 , (corner.y() + height/2) - 4 ); point.setPoint( 1, corner.x() + width,(corner.y() + height/2) ); point.setPoint( 2, corner.x() + width - 4,(corner.y() + height/2) + 4 ); painter.setBrush( QBrush(Qt::red ) ); painter.setPen( Qt::NoPen ); painter.drawPolygon( point ); } } void KSpreadCell::paintText(QPainter& painter, QPoint corner, QPoint cellRef, bool selected) { ColumnLayout* colLayout = m_pTable->columnLayout(cellRef.x()); RowLayout* rowLayout = m_pTable->rowLayout(cellRef.y()); int width = (m_iExtraYCells ? m_iExtraHeight : colLayout->width()); QRect m = m_pTable->marker(); QColorGroup defaultColorGroup = QApplication::palette().active(); QColor textColorPrint = textColor( cellRef.x(), cellRef.y() ); QPen tmpPen( textColorPrint ); // Resolve the text color if invalid (=default) if(!textColorPrint.isValid()) { if(painter.device()->isExtDev()) textColorPrint = Qt::black; else textColorPrint = QApplication::palette().active().text(); } /* if ( selected && ( _col != m.left() || _row != m.top() ) ) { QPen p( textPen(_col,_row) ); p.setColor( defaultColorGroup.highlightedText() ); _painter.setPen( p ); } else*/ //_painter.setPen( textPen(_col,_row) ); // #### Torben: This looks like duplication to me KSpreadConditional condition; if(conditions.GetCurrentCondition(condition) && !m_pTable->getShowFormula()) { painter.setFont( condition.fontcond ); tmpPen.setColor( condition.colorcond ); } else { painter.setFont( textFont( cellRef.x(), cellRef.y() ) ); if( isNumeric() && !m_pTable->getShowFormula() ) { double v = valueDouble() * factor(column(),row()); if ( floatColor( cellRef.x(), cellRef.y()) == KSpreadCell::NegRed && v < 0.0 ) tmpPen.setColor( Qt::red ); } } if ( selected && ( cellRef.x() != m.left() || cellRef.y() != m.top() ) ) { QPen p( tmpPen ); p.setColor( defaultColorGroup.highlightedText() ); painter.setPen( p ); } else { painter.setPen(tmpPen); } QString tmpText = m_strOutText; int tmpHeight = m_iOutTextHeight; int tmpWidth = m_iOutTextWidth; if( testFlag(Flag_CellTooShort) ) { m_strOutText=textDisplaying( painter ); } //hide zero if(m_pTable->getHideZero() && isNumeric() && valueDouble() * factor(column(),row()) == 0 ) { m_strOutText=QString::null; } if( colLayout->isHide()|| (rowLayout->height()<=2)) { //clear extracell if column or row is hidden freeAllObscuredCells(); m_strOutText=""; } conditionAlign( painter, cellRef.x(), cellRef.y() ); int indent = 0; int offsetCellTooShort = 0; int a = defineAlignX(); //apply indent if text is align to left not when text is at right or middle if( a == KSpreadCell::Left && !isEmpty()) { indent=getIndent(column(),row()); } //made an offset, otherwise ### is under red triangle if( a == KSpreadCell::Right && !isEmpty() && testFlag(Flag_CellTooShort) ) { offsetCellTooShort=4; } QFontMetrics fm2 = painter.fontMetrics(); int offsetFont=0; if((alignY(column(),row()) == KSpreadCell::Bottom)&& textFontUnderline(column(), row() )) { offsetFont=fm2.underlinePos()+1; } int tmpAngle=getAngle( cellRef.x(), cellRef.y() ); if ( !multiRow( cellRef.x(), cellRef.y() ) && !verticalText( cellRef.x(), cellRef.y()) && !tmpAngle) { painter.drawText( indent + corner.x() + m_iTextX - offsetCellTooShort, corner.y() + m_iTextY - offsetFont, m_strOutText ); } else if( tmpAngle != 0) { int angle = tmpAngle; QFontMetrics fm = painter.fontMetrics(); painter.rotate(angle); int x; if(angle > 0) x = indent + corner.x() + m_iTextX; else x = indent + static_cast(corner.x() + m_iTextX - (fm.descent() + fm.ascent()) * sin(angle*M_PI/180)); int y; if(angle > 0) y = corner.y() + m_iTextY; else y = corner.y() + m_iTextY + m_iOutTextHeight; painter.drawText( qRound(x*cos(angle*M_PI/180) + y*sin(angle*M_PI/180)), qRound(-x*sin(angle*M_PI/180) + y*cos(angle*M_PI/180)), m_strOutText ); painter.rotate(-angle); } else if( multiRow( cellRef.x(), cellRef.y()) && !verticalText(cellRef.x(), cellRef.y())) { QString t; int i; int pos = 0; int dy = 0; int dx = 0; QFontMetrics fm = painter.fontMetrics(); do { i = m_strOutText.find( "\n", pos ); if ( i == -1 ) t = m_strOutText.mid( pos, m_strOutText.length() - pos ); else { t = m_strOutText.mid( pos, i - pos ); pos = i + 1; } int a = defineAlignX(); if(m_pTable->getShowFormula()) a = KSpreadCell::Left; // #### Torben: This looks duplicated for me switch( a ) { case KSpreadCell::Left: m_iTextX = leftBorderWidth( cellRef.x(), cellRef.y() ) + BORDER_SPACE; break; case KSpreadCell::Right: m_iTextX = width - BORDER_SPACE - fm.width( t ) - rightBorderWidth( cellRef.x(), cellRef.y() ); break; case KSpreadCell::Center: m_iTextX = ( width - fm.width( t ) ) / 2; } painter.drawText( indent + corner.x() + m_iTextX + dx, corner.y() + m_iTextY + dy, t ); dy += fm.descent() + fm.ascent(); } while ( i != -1 ); } else if(verticalText( cellRef.x(), cellRef.y()) && !m_strOutText.isEmpty()) { QString t; int i=0; int dy = 0; int dx = 0; int j=0; QFontMetrics fm = painter.fontMetrics(); do { i = m_strOutText.length(); t = m_strOutText.at(j); painter.drawText( indent + corner.x() + m_iTextX + dx, corner.y() + m_iTextY + dy, t ); dy += fm.descent() + fm.ascent(); j++; } while ( j != i ); } if(testFlag(Flag_CellTooShort)) { m_strOutText = tmpText; m_iOutTextHeight = tmpHeight; m_iOutTextWidth = tmpWidth; } if(m_pTable->getHideZero() && isNumeric() && valueDouble() * factor(column(),row())==0) { m_strOutText=tmpText; } if( colLayout->isHide()|| (rowLayout->height()<=2)) m_strOutText=tmpText; } void KSpreadCell::paintPageBorders(QPainter& painter, QPoint corner, QPoint cellRef) { if ( painter.device()->isExtDev() ) return; ColumnLayout* colLayout = m_pTable->columnLayout(cellRef.x()); RowLayout* rowLayout = m_pTable->rowLayout(cellRef.y()); int height = (m_iExtraYCells ? m_iExtraHeight : rowLayout->height()); int width = (m_iExtraXCells ? m_iExtraWidth : colLayout->width()); // Draw page borders if ( m_pTable->isShowPageBorders() ) { if ( m_pTable->isOnNewPageY( cellRef.y() ) ) { painter.setPen( Qt::red ); painter.drawLine( corner.x(), corner.y(), corner.x() + width, corner.y() ); } if ( m_pTable->isOnNewPageX( cellRef.x() ) ) { painter.setPen( Qt::red ); painter.drawLine( corner.x(), corner.y(), corner.x(), corner.y() + height ); } } } void KSpreadCell::paintMarker(QPainter& painter, QPoint corner, QPoint cellRef) { if ( painter.device()->isExtDev() ) return; ColumnLayout* colLayout = m_pTable->columnLayout(cellRef.x()); RowLayout* rowLayout = m_pTable->rowLayout(cellRef.y()); /* as far as borders are concerned, we only print our own, even if we are merged into another cell. So don't use m_iExtraWidth/Height */ int height = rowLayout->height(); int width = colLayout->width(); // // Draw the marker // // Some of this code is duplicated in KSpreadCanvas::updateSelection // QRect marker = m_pTable->markerRect(); QRect larger; larger.setCoords( marker.left() - 1, marker.top() - 1, marker.right() + 1, marker.bottom() + 1 ); QPen pen(Qt::black,3); painter.setPen( pen ); // The marker is exactly this cell ? if ( marker.left() == cellRef.x() && marker.right() == cellRef.x() && marker.top() == cellRef.y() && marker.bottom() == cellRef.y()) { paintCellHelper( painter, corner.x(), corner.y(), cellRef.x(), cellRef.y(), width, height, 1, marker ); paintCellHelper( painter, corner.x(), corner.y(), cellRef.x(), cellRef.y(), width, height, 2, marker ); paintCellHelper( painter, corner.x(), corner.y(), cellRef.x(), cellRef.y(), width, height, 3, marker ); paintCellHelper( painter, corner.x(), corner.y(), cellRef.x(), cellRef.y(), width, height, 4, marker ); } else if ( marker.contains( cellRef ) ) { // Upper border ? if ( cellRef.y() == marker.top() ) paintCellHelper( painter, corner.x(), corner.y(), cellRef.x(), cellRef.y(), width, height, 1, marker ); // Left border ? if ( cellRef.x() == marker.left() ) paintCellHelper( painter, corner.x(), corner.y(), cellRef.x(), cellRef.y(), width, height, 4, marker ); // Lower border ? if ( cellRef.y() == marker.bottom() ) paintCellHelper( painter, corner.x(), corner.y(), cellRef.x(), cellRef.y(), width, height, 3, marker ); // Right border ? if ( cellRef.x() == marker.right() ) paintCellHelper( painter, corner.x(), corner.y(), cellRef.x(), cellRef.y(), width, height, 2, marker ); } // Dont obey extra cells else if ( larger.contains( QPoint(cellRef.x(), cellRef.y()) ) ) { // Upper border ? if ( cellRef.x() >= marker.left() && cellRef.x() <= marker.right() && cellRef.y() - 1 == marker.bottom() ) paintCellHelper( painter, corner.x(), corner.y(), cellRef.x(), cellRef.y(), width, height, 1, marker ); // Left border ? if ( cellRef.y() >= marker.top() && cellRef.y() <= marker.bottom() && cellRef.x() - 1 == marker.right() ) paintCellHelper( painter, corner.x(), corner.y(), cellRef.x(), cellRef.y(), width, height, 4, marker ); // Lower border ? if ( cellRef.x() >= marker.left() && cellRef.x() <= marker.right() && cellRef.y() + 1 == marker.top() ) paintCellHelper( painter, corner.x(), corner.y(), cellRef.x(), cellRef.y(), width, height, 3, marker ); // Right border ? if ( cellRef.y() >= marker.top() && cellRef.y() <= marker.bottom() && cellRef.x() + 1 == marker.left() ) paintCellHelper( painter, corner.x(), corner.y(), cellRef.x(), cellRef.y(), width, height, 2, marker ); // Top left corner ? if ( cellRef.y() == marker.bottom() + 1 && cellRef.x() == marker.right() + 1 ) paintCellHelper( painter, corner.x(), corner.y(), cellRef.x(), cellRef.y(), width, height, 11, marker ); // Top right corner ? if ( cellRef.y() == marker.bottom() + 1 && cellRef.x() == marker.left() - 1 ) paintCellHelper( painter, corner.x(), corner.y(), cellRef.x(), cellRef.y(), width, height, 12, marker ); // Bottom right corner ? if ( cellRef.y() == marker.top() - 1 && cellRef.x() == marker.left() - 1 ) paintCellHelper( painter, corner.x(), corner.y(), cellRef.x(), cellRef.y(), width, height, 13, marker ); // Bottom left corner ? if ( cellRef.y() == marker.top() - 1 && cellRef.x() == marker.right() + 1 ) paintCellHelper( painter, corner.x(), corner.y(), cellRef.x(), cellRef.y(), width, height, 14, marker ); } } void KSpreadCell::paintCellBorders(QPainter& painter, QPoint corner, QPoint cellRef) { ColumnLayout* colLayout = m_pTable->columnLayout(cellRef.x()); RowLayout* rowLayout = m_pTable->rowLayout(cellRef.y()); int height = rowLayout->height(); int width = colLayout->width(); /* we might not paint some borders if this cell is merged with another in that direction */ bool paintLeft = true; bool paintRight = true; bool paintTop = true; bool paintBottom = true; if (isObscured()) { int xDiff = cellRef.x() - obscuringCellsColumn(); int yDiff = cellRef.y() - obscuringCellsRow(); paintLeft = xDiff == 0; paintTop = yDiff == 0; paintRight = m_pObscuringCell->extraXCells() == xDiff; paintBottom = m_pObscuringCell->extraYCells() == yDiff; } paintRight = paintRight && (extraXCells() == 0); paintBottom = paintBottom && (extraYCells() == 0); int top_offset = 0; int bottom_offset = 0; int left_offset = 0; int right_offset = 0; // // Determine the pens that should be used for drawing // the borders. // QPen left_pen = leftBorderPen( cellRef.x(), cellRef.y() ); QPen right_pen = rightBorderPen( cellRef.x(), cellRef.y() ); QPen top_pen = topBorderPen( cellRef.x(), cellRef.y() ); QPen bottom_pen = bottomBorderPen( cellRef.x(), cellRef.y() ); if ( left_pen.style() != Qt::NoPen && paintLeft) { int top = ( QMAX( 0, -1 + (int)top_pen.width() ) ) / 2 + ( ( QMAX( 0, -1 + (int)top_pen.width() ) ) % 2 ); int bottom = ( QMAX( 0, -1 + (int)bottom_pen.width() ) ) / 2 + 1; painter.setPen( left_pen ); painter.drawLine( corner.x(), corner.y() - top, corner.x(), corner.y() + height + bottom ); left_offset = left_pen.width() - ( left_pen.width() / 2 ); } if ( right_pen.style() != Qt::NoPen && paintRight) { int top = ( QMAX( 0, -1 + (int)top_pen.width() ) ) / 2 + ( ( QMAX( 0, -1 + (int)top_pen.width() ) ) % 2 ); int bottom = ( QMAX( 0, -1 + (int)bottom_pen.width() ) ) / 2 + 1; painter.setPen( right_pen ); painter.drawLine( width + corner.x(), corner.y() - top, width + corner.x(), corner.y() + height + bottom ); right_offset = right_pen.width() / 2; } if ( top_pen.style() != Qt::NoPen && paintTop) { painter.setPen( top_pen ); painter.drawLine( corner.x(), corner.y(), corner.x() + width, corner.y() ); top_offset = top_pen.width() - ( top_pen.width() / 2 ); } if ( bottom_pen.style() != Qt::NoPen && paintBottom ) { painter.setPen( bottom_pen ); painter.drawLine( corner.x(), height + corner.y(), corner.x() + width, height + corner.y() ); bottom_offset = bottom_pen.width() / 2; } // // Draw diagonal borders. // if (!isObscuringForced()) { if ( fallDiagonalPen( cellRef.x(), cellRef.y() ).style() != Qt::NoPen ) { painter.setPen( fallDiagonalPen(cellRef.x(), cellRef.y()) ); painter.drawLine( corner.x(), corner.y(), corner.x() + width, corner.y() + height ); } if (goUpDiagonalPen( cellRef.x(), cellRef.y() ).style() != Qt::NoPen ) { painter.setPen( goUpDiagonalPen(cellRef.x(), cellRef.y()) ); painter.drawLine( corner.x(), corner.y() + height , corner.x() + width, corner.y() ); } } // // Look at the cells on our corners. It may happen that we // just erased parts of their borders corner, so we might need // to repaint these corners. // KSpreadCell* cell_t = m_pTable->cellAt( cellRef.x(), cellRef.y() - 1 ); KSpreadCell* cell_l = m_pTable->cellAt( cellRef.x() - 1, cellRef.y() ); KSpreadCell* cell_r = 0L; KSpreadCell* cell_b = 0L; if ( cellRef.x() < KS_colMax ) cell_r = m_pTable->cellAt( cellRef.x() + 1, cellRef.y() ); if ( cellRef.y() < KS_rowMax ) cell_b = m_pTable->cellAt( cellRef.x(), cellRef.y() + 1 ); QPen vert_pen, horz_pen; // Fix the borders which meet at the top left corner vert_pen = cell_t->leftBorderPen( cellRef.x(), cellRef.y() - 1 ); if ( vert_pen.style() != Qt::NoPen ) { horz_pen = cell_l->topBorderPen( cellRef.x() - 1, cellRef.y() ); int bottom = ( QMAX( 0, -1 + (int)horz_pen.width() ) ) / 2 + 1; painter.setPen( vert_pen ); painter.drawLine( corner.x(), corner.y(), corner.x(), corner.y() + bottom ); } // Fix the borders which meet at the top right corner vert_pen = cell_t->rightBorderPen( cellRef.x(), cellRef.y() - 1 ); if ( ( vert_pen.style() != Qt::NoPen ) && ( cellRef.x() < KS_colMax ) ) { horz_pen = cell_r->topBorderPen( cellRef.x() + 1, cellRef.y() ); int bottom = ( QMAX( 0, -1 + (int)horz_pen.width() ) ) / 2 + 1; painter.setPen( vert_pen ); painter.drawLine( corner.x() + width, corner.y(), corner.x() + width, corner.y() + bottom ); } // Bottom if ( cellRef.y() < KS_rowMax ) { // Fix the borders which meet at the bottom left corner vert_pen = cell_b->leftBorderPen( cellRef.x(), cellRef.y() + 1 ); if ( vert_pen.style() != Qt::NoPen ) { horz_pen = cell_l->bottomBorderPen( cellRef.x() - 1, cellRef.y() ); int bottom = ( QMAX( 0, -1 + (int)horz_pen.width() ) ) / 2; painter.setPen( vert_pen ); painter.drawLine( corner.x(), corner.y() + height - bottom, corner.x(), corner.y() + height ); } // Fix the borders which meet at the bottom right corner vert_pen = cell_b->rightBorderPen( cellRef.x(), cellRef.y() + 1 ); if ( ( vert_pen.style() != Qt::NoPen ) && ( cellRef.x() < KS_colMax ) ) { horz_pen = cell_r->bottomBorderPen( cellRef.x() + 1, cellRef.y() ); int bottom = ( QMAX( 0, -1 + (int)horz_pen.width() ) ) / 2; painter.setPen( vert_pen ); painter.drawLine( corner.x() + width, corner.y() + height - bottom, corner.x() + width, corner.y() + height ); } } } int KSpreadCell::defineAlignX() { int a = align(column(),row()); if ( a == KSpreadCell::Undefined ) { if ( isBool() || isNumeric() || isDate() || isTime() ) a = KSpreadCell::Right; else a = KSpreadCell::Left; } return a; } QString KSpreadCell::textDisplaying( QPainter &_painter) { QFontMetrics fm = _painter.fontMetrics(); int a=align(column(),row()); if (( a == KSpreadCell::Left || a == KSpreadCell::Undefined) && !isNumeric() && !verticalText( column(),row() )) { //not enough space but align to left int len=0; for (int i=column();i<=column()+m_iExtraXCells;i++) { ColumnLayout *cl2 = m_pTable->columnLayout( i ); len+=cl2->width() - 1; } QString tmp; int tmpIndent=0; if(!isEmpty()) tmpIndent=getIndent(column(),row()); for (int i=m_strOutText.length();i!=0;i--) { tmp=m_strOutText.left(i); if((fm.width(tmp)+tmpIndent)<(len-4-1)) //4 equal lenght of red triangle +1 pixel { if( getAngle(column(), row())!=0) { QString tmp2; RowLayout *rl = m_pTable->rowLayout( row() ); if(m_iOutTextHeight>rl->height()) { for (int j=m_strOutText.length();j!=0;j--) { tmp2=m_strOutText.left(j); if(fm.width(tmp2)<(rl->height()-1)) { return m_strOutText.left(QMIN(tmp.length(),tmp2.length())); } } } else return tmp; } else return tmp; } } return QString(""); } else if(verticalText( column(),row() )) { RowLayout *rl = m_pTable->rowLayout( row() ); int tmpIndent=0; //not enough space but align to left int len=0; for (int i=column();i<=column()+m_iExtraXCells;i++) { ColumnLayout *cl2 = m_pTable->columnLayout( i ); len+=cl2->width() - 1; } if(!isEmpty()) tmpIndent=getIndent(column(),row()); if( ((m_iOutTextWidth+tmpIndent)>len)||m_iOutTextWidth==0) return QString(""); for (int i=m_strOutText.length();i!=0;i--) { if(((fm.ascent() + fm.descent())*i)<(rl->height()-1)) { return m_strOutText.left(i); } } return QString(""); } ColumnLayout *cl = m_pTable->columnLayout( column() ); int w = ( m_iExtraWidth == 0 ) ? cl->width() : m_iExtraWidth; if( isNumeric()) { if( formatType()!=Scientific) { int p = (precision(column(),row()) == -1) ? 8 : precision(column(),row()); double value =valueDouble() * factor(column(),row()); int pos=0; QString localizedNumber= QString::number( (value), 'E', p); if((pos=localizedNumber.find('.'))!=-1) { localizedNumber=localizedNumber.replace(pos,1,decimal_point); } if( floatFormat( column(), row() ) == KSpreadCell::AlwaysSigned && value >= 0 ) { if(locale()->positiveSign().isEmpty()) { localizedNumber='+'+localizedNumber; } } if ( precision(column(),row()) == -1 && localizedNumber.find(decimal_point) >= 0 ) { //duplicate code it's not good I know I will fix it int start=0; if((start=localizedNumber.find('E'))!=-1) { start=localizedNumber.length()-start; } int i = localizedNumber.length()-start; bool bFinished = FALSE; while ( !bFinished && i > 0 ) { QChar ch = localizedNumber[ i - 1 ]; if ( ch == '0' ) { localizedNumber.remove(--i,1); } else { bFinished = TRUE; if ( ch == decimal_point ) { localizedNumber.remove(--i,1); } } } } if(fm.width(localizedNumber)width(), rl->height() ); } // Draw the border if ( !_only_top ) { //_painter.setPen( leftBorderPen ); // Fix a 'bug' in the pens width setting. We still need the upper left corner // of the line but a width > 1 won't work for us. QPen pen; pen.setColor( leftBorderColor( _col, _row) ); if ( m_leftBorderPen.style() == Qt::NoPen ) pen = _grid_pen; else pen = QPen( m_leftBorderPen ); _painter.setPen( pen ); int dx = 0;//int)ceil( (double)( m_leftBorderPen.width() - 1) / 2.0 ); _painter.drawLine( _tx + dx, _ty, _tx + dx, _ty + rl->height() ); } if ( !_only_left ) { //_painter.setPen( topBorderPen ); QPen pen; pen.setColor( topBorderColor( _col, _row ) ); if ( m_topBorderPen.style() == Qt::NoPen ) pen = _grid_pen; else pen = QPen( m_topBorderPen ); _painter.setPen( pen ); int dy = 0;//(int)ceil( (double)( m_topBorderPen.width() - 1) / 2.0 ); _painter.drawLine( _tx, _ty + dy, _tx + cl->width() , _ty + dy ); } if ( !_only_top && !_only_left ) { int dy=0; int dx=0; if ( m_fallDiagonalPen.style() != Qt::NoPen ) { _painter.setPen( m_fallDiagonalPen ); _painter.drawLine( _tx + dx, _ty + dy, _tx + cl->width() , _ty + rl->height() ); } if ( m_goUpDiagonalPen.style() != Qt::NoPen ) { _painter.setPen( m_goUpDiagonalPen ); _painter.drawLine( _tx , _ty +rl->height() , _tx + cl->width(), _ty ); } if( m_backGroundBrush.style()!= Qt::NoBrush) { int left=leftBorderWidth( _col, _row) + BORDER_SPACE; int top=topBorderWidth(_col,_row) + BORDER_SPACE; _painter.setPen(Qt::NoPen); _painter.setBrush(m_backGroundBrush); _painter.drawRect( _tx + left, _ty + top, cl->width()-left-BORDER_SPACE, rl->height() - top - BORDER_SPACE); } } if ( !_only_top && !_only_left ) if ( !m_strOutText.isEmpty() ) { _painter.setPen( m_textPen ); verifyCondition(); if(m_conditionIsTrue && !m_pTable->getShowFormula()) { KSpreadConditional *tmpCondition=0; switch(m_numberOfCond) { case 0: tmpCondition=m_firstCondition; break; case 1: tmpCondition=m_secondCondition; break; case 2: tmpCondition=m_thirdCondition; break; } _painter.setFont( tmpCondition->fontcond ); } else _painter.setFont( textFont(_col,_row ) ); conditionAlign(_painter,_col,_row); _painter.drawText( _tx + m_iTextX, _ty + m_iTextY, m_strOutText ); } } */ int KSpreadCell::width( int _col, KSpreadCanvas *_canvas ) { if ( _col < 0 ) _col = m_iColumn; if ( _canvas ) { if ( testFlag(Flag_ForceExtra) ) return (int)( m_iExtraWidth ); ColumnLayout *cl = m_pTable->columnLayout( _col ); return cl->width( _canvas ); } if ( testFlag(Flag_ForceExtra) ) return m_iExtraWidth; ColumnLayout *cl = m_pTable->columnLayout( _col ); return cl->width(); } int KSpreadCell::height( int _row, KSpreadCanvas *_canvas ) { if ( _row < 0 ) _row = m_iRow; if ( _canvas ) { if ( testFlag(Flag_ForceExtra) ) return (int)( m_iExtraHeight ); RowLayout *rl = m_pTable->rowLayout( _row ); return rl->height( _canvas ); } if ( testFlag(Flag_ForceExtra) ) return m_iExtraHeight; RowLayout *rl = m_pTable->rowLayout( _row ); return rl->height(); } /////////////////////////////////////////// // // Misc Properties. // Reimplementation of KSpreadLayout methods. // /////////////////////////////////////////// const QBrush& KSpreadCell::backGroundBrush( int _col, int _row ) const { if ( m_pObscuringCell ) { // Ask the obscuring cell for a right border //if ( m_pObscuringCell->hasProperty( PBackgroundBrush ) ) return m_pObscuringCell->backGroundBrush( m_pObscuringCell->column(), m_pObscuringCell->row() ); //return m_pTable->emptyBrush(); } return KSpreadLayout::backGroundBrush( _col, _row ); } const QColor& KSpreadCell::bgColor( int _col, int _row ) const { if ( m_pObscuringCell ) { // Ask the obscuring cell for a right border // if ( m_pObscuringCell->hasProperty( PBackgroundColor ) ) { return m_pObscuringCell->bgColor( m_pObscuringCell->column(), m_pObscuringCell->row() ); } //return m_pTable->emptyColor(); } return KSpreadLayout::bgColor( _col, _row ); } /////////////////////////////////////////// // // Borders. // Reimplementation of KSpreadLayout methods. // /////////////////////////////////////////// void KSpreadCell::setLeftBorderPen( const QPen& p ) { KSpreadCell* cell = m_pTable->cellAt( column() - 1, row() ); //what happens on column=1 if ( cell && cell->hasProperty( PRightBorder ) ) cell->clearProperty( PRightBorder ); KSpreadLayout::setLeftBorderPen( p ); } void KSpreadCell::setTopBorderPen( const QPen& p ) { KSpreadCell* cell = m_pTable->cellAt( column(), row() - 1 ); //what happens on row=1 if ( cell && cell->hasProperty( PBottomBorder ) ) cell->clearProperty( PBottomBorder ); KSpreadLayout::setTopBorderPen( p ); } void KSpreadCell::setRightBorderPen( const QPen& p ) { KSpreadCell* cell = 0L; if ( column() < KS_colMax ) cell = m_pTable->cellAt( column() + 1, row() ); if ( cell && cell->hasProperty( PLeftBorder ) ) cell->clearProperty( PLeftBorder ); KSpreadLayout::setRightBorderPen( p ); } void KSpreadCell::setBottomBorderPen( const QPen& p ) { KSpreadCell* cell = 0L; if ( row() < KS_rowMax ) cell = m_pTable->cellAt( column(), row() + 1 ); if ( cell && cell->hasProperty( PTopBorder ) ) cell->clearProperty( PTopBorder ); KSpreadLayout::setBottomBorderPen( p ); } const QPen& KSpreadCell::rightBorderPen( int _col, int _row ) const { if ( !hasProperty( PRightBorder ) && ( _col < KS_colMax ) ) { KSpreadCell * cell = m_pTable->cellAt( _col + 1, _row ); if ( cell->hasProperty( PLeftBorder ) ) return cell->leftBorderPen( _col + 1, _row ); } return KSpreadLayout::rightBorderPen( _col, _row ); } const QPen& KSpreadCell::leftBorderPen( int _col, int _row ) const { if ( !hasProperty( PLeftBorder ) ) { KSpreadCell * cell = m_pTable->cellAt( _col - 1, _row ); if ( cell->hasProperty( PRightBorder ) ) return cell->rightBorderPen( _col - 1, _row ); } return KSpreadLayout::leftBorderPen( _col, _row ); } const QPen& KSpreadCell::bottomBorderPen( int _col, int _row ) const { if ( !hasProperty( PBottomBorder ) && ( _row < KS_rowMax ) ) { KSpreadCell * cell = m_pTable->cellAt( _col, _row + 1 ); if ( cell->hasProperty( PTopBorder ) ) return cell->topBorderPen( _col, _row + 1 ); } return KSpreadLayout::bottomBorderPen( _col, _row ); } const QPen& KSpreadCell::topBorderPen( int _col, int _row ) const { if ( !hasProperty( PTopBorder ) ) { KSpreadCell * cell = m_pTable->cellAt( _col, _row - 1 ); if ( cell->hasProperty( PBottomBorder ) ) return cell->bottomBorderPen( _col, _row - 1 ); } return KSpreadLayout::topBorderPen( _col, _row ); } /////////////////////////////////////////// // // Precision // /////////////////////////////////////////// void KSpreadCell::incPrecision() { if ( !isNumeric() ) return; int tmpPreci=precision(column(),row()); kdDebug(36001) << "incPrecision: tmpPreci = " << tmpPreci << endl; if ( tmpPreci == -1 ) { int pos = m_strOutText.find(decimal_point); if ( pos == -1 ) pos = m_strOutText.find('.'); if ( pos == -1 ) setPrecision(1); else { int start=0; if(m_strOutText.find('%')!=-1) start=2; else if(m_strOutText.find(locale()->currencySymbol())==((int)(m_strOutText.length()-locale()->currencySymbol().length()))) start=locale()->currencySymbol().length()+1; else if((start=m_strOutText.find('E'))!=-1) start=m_strOutText.length()-start; //kdDebug(36001) << "start=" << start << " pos=" << pos << " length=" << m_strOutText.length() << endl; setPrecision( QMAX( 0, (int)m_strOutText.length() - start - pos ) ); } } else if ( tmpPreci < 10 ) { setPrecision(++tmpPreci); } setFlag(Flag_LayoutDirty); } void KSpreadCell::decPrecision() { if ( !isNumeric() ) return; int preciTmp=precision(column(),row()); if ( precision(column(),row()) == -1 ) { int pos = m_strOutText.find(decimal_point); int start=0; if(m_strOutText.find('%')!=-1) start=2; else if(m_strOutText.find(locale()->currencySymbol())==((int)(m_strOutText.length()-locale()->currencySymbol().length()))) start=locale()->currencySymbol().length()+1; else if((start=m_strOutText.find('E'))!=-1) start=m_strOutText.length()-start; else start=0; if ( pos == -1 ) return; setPrecision(m_strOutText.length() - pos - 2-start); if ( preciTmp < 0 ) setPrecision(preciTmp ); setFlag(Flag_LayoutDirty); } else if ( preciTmp > 0 ) { setPrecision(--preciTmp); setFlag(Flag_LayoutDirty); } } void KSpreadCell::setCellText( const QString& _text, bool updateDepends ) { QString oldText=m_strText; setDisplayText( _text, updateDepends ); if(!m_pTable->isLoading() && !testValidity() ) { //reapply old value if action == stop setDisplayText( oldText, updateDepends ); } } void KSpreadCell::setDisplayText( const QString& _text, bool updateDepends ) { clearFlag(Flag_Error); m_strText = _text; // Free all content data if ( m_pQML ) { delete m_pQML; m_pQML = NULL; } clearFormula(); /** * A real formula "=A1+A2*3" was entered. */ if ( !m_strText.isEmpty() && m_strText[0] == '=' ) { setFlag(Flag_LayoutDirty); m_content = Formula; if ( !m_pTable->isLoading() ) { if ( !makeFormula() ) { kdError(36002) << "ERROR: Syntax ERROR" << endl; } } } /** * QML */ else if ( !m_strText.isEmpty() && m_strText[0] == '!' ) { m_pQML = new QSimpleRichText( m_strText.mid(1), QApplication::font() );//, m_pTable->widget() ); setFlag(Flag_LayoutDirty); m_content = RichText; m_dataType = OtherData; } /** * Some numeric value or a string. */ else { m_content = Text; // Find out what data type it is checkTextInput(); setFlag(Flag_LayoutDirty); } /** * Special handling for selection boxes */ if ( m_style == ST_Select && !m_pTable->isLoading() ) { if ( testFlag(Flag_CalcDirty) ) calc(); SelectPrivate *s = (SelectPrivate*)m_pPrivate; if ( m_content == Formula ) s->parse( m_strFormulaOut ); else s->parse( m_strText ); kdDebug(36001) << "SELECT " << s->text() << endl; checkTextInput(); // is this necessary? // setFlag(Flag_LayoutDirty); } setCalcDirtyFlag(); if ( updateDepends ) update(); } bool KSpreadCell::testValidity() { bool valid = false; if( m_Validity != NULL ) { if( isNumeric() && (m_Validity->m_allow == Allow_Number || (m_Validity->m_allow == Allow_Integer && valueDouble() == ceil(valueDouble())))) { switch( m_Validity->m_cond) { case Equal: valid = ( valueDouble() - m_Validity->valMin < DBL_EPSILON && valueDouble() - m_Validity->valMin > (0.0 - DBL_EPSILON)); break; case Superior: valid = (valueDouble() > m_Validity->valMin); break; case Inferior: valid = (valueDouble() valMin); break; case SuperiorEqual: valid = (valueDouble() >= m_Validity->valMin); break; case InferiorEqual: valid = (valueDouble() <= m_Validity->valMin); break; case Between: valid = ( valueDouble() >= m_Validity->valMin && valueDouble() <= m_Validity->valMax); break; case Different: valid = (valueDouble() < m_Validity->valMin || valueDouble() > m_Validity->valMax); break; default : break; } } else if(m_Validity->m_allow==Allow_Text) { valid = ( m_dataType == StringData ); } else if(m_Validity->m_allow==Allow_TextLength) { if( m_dataType == StringData ) { int len = m_strOutText.length(); switch( m_Validity->m_cond) { case Equal: if (len == m_Validity->valMin) valid = true; break; case Superior: if(len > m_Validity->valMin) valid = true; break; case Inferior: if(len < m_Validity->valMin) valid = true; break; case SuperiorEqual: if(len >= m_Validity->valMin) valid = true; break; case InferiorEqual: if(len <= m_Validity->valMin) valid = true; break; case Between: if(len >= m_Validity->valMin && len <= m_Validity->valMax) valid = true; break; case Different: if(len valMin || len >m_Validity->valMax) valid = true; break; default : break; } } } else if(m_Validity->m_allow == Allow_Time && isTime()) { switch( m_Validity->m_cond) { case Equal: valid = (valueTime() == m_Validity->timeMin); break; case Superior: valid = (valueTime() > m_Validity->timeMin); break; case Inferior: valid = (valueTime() < m_Validity->timeMin); break; case SuperiorEqual: valid = (valueTime() >= m_Validity->timeMin); break; case InferiorEqual: valid = (valueTime() <= m_Validity->timeMin); break; case Between: valid = (valueTime() >= m_Validity->timeMin && valueTime() <= m_Validity->timeMax); break; case Different: valid = (valueTime() < m_Validity->timeMin || valueTime() > m_Validity->timeMax); break; default : break; } } else if(m_Validity->m_allow == Allow_Date && isDate()) { switch( m_Validity->m_cond) { case Equal: valid = (valueDate() == m_Validity->dateMin); break; case Superior: valid = (valueDate() > m_Validity->dateMin); break; case Inferior: valid = (valueDate() < m_Validity->dateMin); break; case SuperiorEqual: valid = (valueDate() >= m_Validity->dateMin); break; case InferiorEqual: valid = (valueDate() <= m_Validity->dateMin); break; case Between: valid = (valueDate() >= m_Validity->dateMin && valueDate() <= m_Validity->dateMax); break; case Different: valid = (valueDate() < m_Validity->dateMin || valueDate() > m_Validity->dateMax); break; default : break; } } } else { valid= true; } if(!valid &&m_Validity != NULL ) { switch (m_Validity->m_action) { case Stop: KMessageBox::error((QWidget*)0L, m_Validity->message, m_Validity->title); break; case Warning: KMessageBox::warningYesNo((QWidget*)0L, m_Validity->message, m_Validity->title); break; case Information: KMessageBox::information((QWidget*)0L, m_Validity->message, m_Validity->title); break; } } return (valid || m_Validity == NULL || m_Validity->m_action != Stop); } void KSpreadCell::setValue( double _d ) { clearFlag(Flag_Error); m_strText = QString::number( _d ); // Free all content data delete m_pQML; m_pQML = 0; clearFormula(); clearFlag(Flag_Error); m_dataType = NumericData; m_dValue = _d; setFlag(Flag_LayoutDirty); m_content = Text; // Do not update formulas and stuff here if ( !m_pTable->isLoading() ) update(); } void KSpreadCell::update() { kdDebug(36002) << util_cellName( m_iColumn, m_iRow ) << " update" << endl; if ( m_pObscuringCell ) { m_pObscuringCell->setLayoutDirtyFlag(); m_pObscuringCell->setDisplayDirtyFlag(); m_pTable->updateCell( m_pObscuringCell, m_pObscuringCell->column(), m_pObscuringCell->row() ); } setFlag(Flag_DisplayDirty); updateDepending(); if ( testFlag(Flag_DisplayDirty) ) m_pTable->updateCell( this, m_iColumn, m_iRow ); } void KSpreadCell::updateDepending() { if ( testFlag(Flag_UpdatingDeps) || (!m_pTable->getAutoCalc()) ) { return; } kdDebug(36002) << util_cellName( m_iColumn, m_iRow ) << " updateDepending" << endl; KSpreadDependency* d = NULL; setFlag(Flag_UpdatingDeps); setFlag(Flag_CalcDirty); // Every cell that references us must calculate with this new value for (d = m_lstDependingOnMe.first(); d != NULL; d = m_lstDependingOnMe.next()) { for (int c = d->Left(); c <= d->Right(); c++) { for (int r = d->Top(); r <= d->Bottom(); r++) { d->Table()->cellAt( c, r )->updateDepending(); } } } calc(); kdDebug(36002) << util_cellName( m_iColumn, m_iRow ) << " updateDepending done" << endl; clearFlag(Flag_UpdatingDeps); updateChart(); } void KSpreadCell::setCalcDirtyFlag() { KSpreadDependency* d = NULL; if ( testFlag(Flag_CalcDirty) ) { /* we need to avoid recursion */ return; } setFlag(Flag_CalcDirty); /* if this cell is dirty, every cell that references this one is dirty */ for (d = m_lstDependingOnMe.first(); d != NULL; d = m_lstDependingOnMe.next()) { for (int c = d->Left(); c <= d->Right(); c++) { for (int r = d->Top(); r <= d->Bottom(); r++) { d->Table()->cellAt( c, r )->setCalcDirtyFlag(); } } } if ( m_content != Formula ) { /* we set it temporarily to true to handle recursion (although that shouldn't happen if it's not a formula - we might as well be safe). */ clearFlag(Flag_CalcDirty); } } bool KSpreadCell::updateChart(bool refresh) { // Update a chart for example if it depends on this cell. if ( m_iRow != 0 && m_iColumn != 0 ) { CellBinding *bind; for ( bind = m_pTable->firstCellBinding(); bind != 0L; bind = m_pTable->nextCellBinding() ) { if ( bind->contains( m_iColumn, m_iRow ) ) { if (!refresh) return true; bind->cellChanged( this ); - return true; } } + return true; } return false; } void KSpreadCell::checkTextInput() { // Goal of this method: determine m_dataType, and the value of the cell clearFlag(Flag_Error); m_dValue = 0; Q_ASSERT( m_content == Text ); if ( m_content != Text ) { m_dataType = OtherData; return; } // Get the text from that cell (using result of formula if any) QString str = m_strText; if ( m_style == ST_Select ) str = (static_cast(m_pPrivate))->text(); else if ( isFormula() ) str = m_strFormulaOut; // If the text is empty, we don't have a value (we use StringData for empty string) // If the user stated explicitely that he wanted text (using the format or using a quote), // then we don't parse as a value, but as string. if ( str.isEmpty() || formatType() == Text_format || str.at(0)=='\'' ) { m_dataType = StringData; if(m_pTable->getFirstLetterUpper() && !m_strText.isEmpty()) m_strText=m_strText[0].upper()+m_strText.right(m_strText.length()-1); //setFormatType(Text_format); // shouldn't be necessary. Won't apply with StringData anyway. return; } // Try parsing as various datatypes, to find the type of the cell // First as bool if ( tryParseBool( str ) ) return; // Then as a number QString strStripped = str.stripWhiteSpace(); if ( tryParseNumber( strStripped ) ) { if ( strStripped.contains('E') || strStripped.contains('e') ) setFormatType(Scientific); else checkNumberFormat(); return; } // Test if text is a percent value, ending with a '%' // It's a bit dirty to do this here, but we have to because the % gets // saved into the XML file. It would be cleaner to save only the numerical value // and treat the trailing % as pure formatting. if( str.at(str.length()-1)=='%') { QString strTrimmed = str.left(str.length()-1); if ( tryParseNumber( strTrimmed ) ) { m_dValue /= 100.0; if ( formatType() != Percentage ) { setFormatType(Percentage); setPrecision(0); // Only set the precision if the format wasn't percentage. } setFactor(100.0); return; } } // Test for money number bool ok; double money = locale()->readMoney(str, &ok); if ( ok ) { m_dValue = money; m_dataType = NumericData; setFormatType(Money); setFactor(1.0); setPrecision(2); return; } if ( tryParseDate( str ) ) { FormatType tmpFormat = formatType(); if(tmpFormat!=TextDate && !(tmpFormat>=200 && tmpFormat<=216)) // ### { //test if it's a short date or text date. if((locale()->formatDate(valueDate(), false) == str)) setFormatType(TextDate); else setFormatType(ShortDate); } m_strText = str; return; } if ( tryParseTime( str ) ) { // Force default time format if format isn't time FormatType tmpFormat = formatType(); if( tmpFormat!=SecondeTime && tmpFormat!=Time_format1 && tmpFormat!=Time_format2 && tmpFormat!=Time_format3) setFormatType(Time); // Parsing as time acts like an autoformat: we even change m_strText m_strText=locale()->formatTime(valueTime(), true); return; } // Nothing particular found, then this is simply a string m_dataType = StringData; if(m_pTable->getFirstLetterUpper() && !m_strText.isEmpty()) m_strText=m_strText[0].upper()+m_strText.right(m_strText.length()-1); } bool KSpreadCell::tryParseBool( const QString& str ) { if ( str.lower() == "true" || str.lower() == i18n("True").lower() ) { m_dValue = 1.0; m_dataType = BoolData; return true; } if ( str.lower() == "false" || str.lower() == i18n("false").lower() ) { m_dValue = 0.0; m_dataType = BoolData; return true; } return false; } bool KSpreadCell::tryParseNumber( const QString& str ) { // First try to understand the number using the locale bool ok; double value = locale()->readNumber(str, &ok); // If not, try with the '.' as decimal separator if ( !ok ) value = str.toDouble(&ok); if ( ok ) { kdDebug(36001) << "KSpreadCell::tryParseNumber '" << str << "' successfully parsed as number: " << value << endl; m_dValue = value; m_dataType = NumericData; return true; } return false; } bool KSpreadCell::tryParseDate( const QString& str ) { bool valid = false; QDate tmpDate = locale()->readDate(str, &valid); if (!valid) { // Try without the year // The tricky part is that we need to remove any separator around the year // For instance %Y-%m-%d becomes %m-%d and %d/%m/%Y becomes %d/%m // If the year is in the middle, say %m-%Y/%d, we'll remove the sep. before it (%m/%d). QString fmt = locale()->dateFormatShort(); int yearPos = fmt.find( "%Y", 0, false ); if ( yearPos > -1 ) { if ( yearPos == 0 ) { fmt.remove( 0, 2 ); while ( fmt[0] != '%' ) fmt.remove( 0, 1 ); } else { fmt.remove( yearPos, 2 ); for ( ; yearPos > 0 && fmt[yearPos-1] != '%'; --yearPos ) fmt.remove( yearPos, 1 ); } //kdDebug(36001) << "KSpreadCell::tryParseDate short format w/o date: " << fmt << endl; tmpDate = locale()->readDate( str, fmt, &valid ); } } if (valid) { Q_ASSERT( tmpDate.isValid() ); //KLocale::readDate( QString ) doesn't support long dates... // (David: it does now...) // _If_ the input is a long date, check if the first character isn't a number... // (David: why? this looks specific to some countries) // Deactivating for now. If you reactivate, please explain better (David). //if ( str.contains( ' ' ) == 0 ) //No spaces " " in short dates... { m_dataType = DateData; m_Date = tmpDate; return true; } } return false; } bool KSpreadCell::tryParseTime( const QString& str ) { bool valid = false; QTime tmpTime = locale()->readTime(str,&valid); if (!valid) { if(locale()->use12Clock()) { QString stringPm=i18n("pm"); QString stringAm=i18n("am"); int pos=0; if((pos=str.find(stringPm))!=-1) { QString tmp=str.mid(0,str.length()-stringPm.length()); tmp=tmp.simplifyWhiteSpace(); tmpTime=locale()->readTime(tmp+" "+stringPm, &valid); if(!valid) tmpTime=locale()->readTime(tmp+":00 "+stringPm, &valid); } else if((pos=str.find(stringAm))!=-1) { QString tmp=str.mid(0,str.length()-stringAm.length()); tmp=tmp.simplifyWhiteSpace(); tmpTime=locale()->readTime(tmp+" "+stringAm, &valid); if (!valid) tmpTime=locale()->readTime(tmp+":00 "+stringAm, &valid); } } } if(valid) { m_dataType = TimeData; m_Time = tmpTime; } return valid; } void KSpreadCell::checkNumberFormat() { if ( formatType() == Number && m_dataType == NumericData ) { if ( valueDouble() > 1e+10 ) setFormatType( Scientific ); } } bool KSpreadCell::cellDependsOn(KSpreadTable *table, int col, int row) { bool isdep = FALSE; KSpreadDependency *dep; for ( dep = m_lstDepends.first(); dep != 0L && !isdep; dep = m_lstDepends.next() ) { if (dep->Table() == table && dep->Left() <= col && dep->Right() >= col && dep->Top() <= row && dep->Bottom() >= row) { isdep = TRUE; } } return isdep; } QDomElement KSpreadCell::save( QDomDocument& doc, int _x_offset, int _y_offset, bool force ) { // Save the position of this cell QDomElement cell = doc.createElement( "cell" ); cell.setAttribute( "row", m_iRow - _y_offset ); cell.setAttribute( "column", m_iColumn - _x_offset ); if ( !action().isEmpty() ) cell.setAttribute( "action", action() ); // // Save the formatting information // QDomElement format = KSpreadLayout::save( doc, force ); if ( format.hasChildNodes() || format.attributes().length() ) // don't save empty tags cell.appendChild( format ); if ( isForceExtraCells() ) { if ( extraXCells() ) format.setAttribute( "colspan", extraXCells() ); if ( extraYCells() ) format.setAttribute( "rowspan", extraYCells() ); } if ( style() ) format.setAttribute( "style", (int)m_style ); QDomElement conditionElement = conditions.SaveConditions(doc); if ( !conditionElement.isNull() ) { cell.appendChild( conditionElement ); } if( m_Validity!=0 ) { QDomElement validity = doc.createElement("validity"); QDomElement param=doc.createElement("param"); param.setAttribute("cond",(int)m_Validity->m_cond); param.setAttribute("action",(int)m_Validity->m_action); param.setAttribute("allow",(int)m_Validity->m_allow); param.setAttribute("valmin",m_Validity->valMin); param.setAttribute("valmax",m_Validity->valMax); validity.appendChild(param); QDomElement title = doc.createElement( "title" ); title.appendChild( doc.createTextNode( m_Validity->title ) ); validity.appendChild( title ); QDomElement message = doc.createElement( "message" ); message.appendChild( doc.createCDATASection( m_Validity->message ) ); validity.appendChild( message ); QString tmp; if( m_Validity->timeMin.isValid()) { QDomElement timeMin = doc.createElement( "timemin" ); tmp=m_Validity->timeMin.toString(); timeMin.appendChild( doc.createTextNode( tmp ) ); validity.appendChild( timeMin ); } if( m_Validity->timeMax.isValid()) { QDomElement timeMax = doc.createElement( "timemax" ); tmp=m_Validity->timeMax.toString(); timeMax.appendChild( doc.createTextNode( tmp ) ); validity.appendChild( timeMax ); } if(m_Validity->dateMin.isValid()) { QDomElement dateMin = doc.createElement( "datemin" ); QString tmp("%1/%2/%3"); tmp = tmp.arg(m_Validity->dateMin.year()).arg(m_Validity->dateMin.month()).arg(m_Validity->dateMin.day()); dateMin.appendChild( doc.createTextNode( tmp ) ); validity.appendChild( dateMin ); } if( m_Validity->dateMax.isValid()) { QDomElement dateMax = doc.createElement( "datemax" ); QString tmp("%1/%2/%3"); tmp = tmp.arg(m_Validity->dateMax.year()).arg(m_Validity->dateMax.month()).arg(m_Validity->dateMax.day()); dateMax.appendChild( doc.createTextNode( tmp ) ); validity.appendChild( dateMax ); } cell.appendChild( validity ); } if ( !m_strComment.isEmpty() ) { QDomElement comment = doc.createElement( "comment" ); comment.appendChild( doc.createCDATASection( m_strComment ) ); cell.appendChild( comment ); } // // Save the text // if ( !m_strText.isEmpty() ) { // Formulas need to be encoded to ensure that they // are position independent. if ( isFormula() ) { calc(); QDomElement text = doc.createElement( "text" ); text.appendChild( doc.createTextNode( encodeFormula() ) ); cell.appendChild( text ); /* we still want to save the results of the formula */ QDomElement formulaResult = doc.createElement( "result" ); QString str( m_strOutText ); saveCellResult( doc, formulaResult, str ); cell.appendChild( formulaResult ); } // Have to be saved in some CDATA section because of too many // special charatcers. else if ( content() == RichText )//|| content() == VisualFormula ) { QDomElement text = doc.createElement( "text" ); text.appendChild( doc.createCDATASection( m_strText ) ); cell.appendChild( text ); } else { // Save the cell contents (in a locale-independent way) QString str( m_strText ); QDomElement text = doc.createElement( "text" ); saveCellResult( doc, text, str ); cell.appendChild( text ); } } if ( cell.hasChildNodes() || cell.attributes().length() > 2 ) // don't save empty tags // (the >2 is due to "row" and "column" attributes) return cell; else return QDomElement(); } bool KSpreadCell::saveCellResult( QDomDocument& doc, QDomElement& result, QString defaultStr ) { QString str = defaultStr; result.setAttribute( "dataType", dataTypeToString( m_dataType ) ); if( m_dataType == DateData ) { str = "%1/%2/%3"; str = str.arg(valueDate().year()).arg(valueDate().month()). arg(valueDate().day()); } else if( m_dataType == TimeData ) { str = valueTime().toString(); } else if ( m_dataType == BoolData ) { // See comment in KSpreadCell::loadCellData //str = m_dValue == 1.0 ? "true" : "false"; str = m_strText; } else if ( m_dataType == NumericData ) { str = QString::number(valueDouble(), 'g', DBL_DIG); } result.appendChild( doc.createTextNode( str ) ); return true; /* really isn't much of a way for this function to fail */ } bool KSpreadCell::load( const QDomElement& cell, int _xshift, int _yshift, PasteMode pm, Operation op ) { bool ok; // // First of all determine in which row and column this // cell belongs. // m_iRow = cell.attribute( "row" ).toInt( &ok ) + _yshift; if ( !ok ) return false; m_iColumn = cell.attribute( "column" ).toInt( &ok ) + _xshift; if ( !ok ) return false; if ( cell.hasAttribute( "action" ) ) setAction( cell.attribute("action") ); // Validation if ( m_iRow < 1 || m_iRow > KS_rowMax ) { kdDebug(36001) << "KSpreadCell::load: Value out of Range Cell:row=" << m_iRow << endl; return false; } if ( m_iColumn < 1 || m_iColumn > KS_colMax ) { kdDebug(36001) << "KSpreadCell::load: Value out of Range Cell:column=" << m_iColumn << endl; return false; } // // Load formatting information. // QDomElement f = cell.namedItem( "format" ).toElement(); if ( !f.isNull() && ( pm == Normal || pm == Format || pm == NoBorder ) ) { // send pm parameter. Didn't load Borders if pm==NoBorder if ( !KSpreadLayout::load( f,pm ) ) return false; if ( f.hasAttribute( "colspan" ) ) { int i = f.attribute("colspan").toInt( &ok ); if ( !ok ) return false; // Validation if ( i < 0 || i > KS_spanMax ) { kdDebug(36001) << "Value out of range Cell::colspan=" << i << endl; return false; } m_iExtraXCells = i; if ( i > 0 ) { setFlag(Flag_ForceExtra); } } if ( f.hasAttribute( "rowspan" ) ) { int i = f.attribute("rowspan").toInt( &ok ); if ( !ok ) return false; // Validation if ( i < 0 || i > KS_spanMax ) { kdDebug(36001) << "Value out of range Cell::rowspan=" << i << endl; return false; } m_iExtraYCells = i; if ( i > 0 ) { setFlag(Flag_ForceExtra); } } if(testFlag(Flag_ForceExtra)) { forceExtraCells(m_iColumn,m_iRow,m_iExtraXCells,m_iExtraYCells); } } // // Load the condition section of a cell. // QDomElement conditionsElement = cell.namedItem( "condition" ).toElement(); if ( !conditionsElement.isNull()) { conditions.LoadConditions( conditionsElement ); } QDomElement validity = cell.namedItem( "validity" ).toElement(); if ( !validity.isNull()) { QDomElement param = validity.namedItem( "param" ).toElement(); if(!param.isNull()) { m_Validity=new KSpreadValidity; if ( param.hasAttribute( "cond" ) ) { m_Validity->m_cond=(Conditional) param.attribute("cond").toInt( &ok ); if ( !ok ) return false; } if ( param.hasAttribute( "action" ) ) { m_Validity->m_action=(Action) param.attribute("action").toInt( &ok ); if ( !ok ) return false; } if ( param.hasAttribute( "allow" ) ) { m_Validity->m_allow=(Allow) param.attribute("allow").toInt( &ok ); if ( !ok ) return false; } if ( param.hasAttribute( "valmin" ) ) { m_Validity->valMin=param.attribute("valmin").toDouble( &ok ); if ( !ok ) return false; } if ( param.hasAttribute( "valmax" ) ) { m_Validity->valMax=param.attribute("valmax").toDouble( &ok ); if ( !ok ) return false; } } QDomElement title = validity.namedItem( "title" ).toElement(); if(!title.isNull()) { m_Validity->title= title.text(); } QDomElement message = validity.namedItem( "message" ).toElement(); if(!message.isNull()) { m_Validity->message= message.text(); } QDomElement timeMin = validity.namedItem( "timemin" ).toElement(); if ( !timeMin.isNull() ) { m_Validity->timeMin=toTime(timeMin); } QDomElement timeMax = validity.namedItem( "timemax" ).toElement(); if ( !timeMax.isNull() ) { m_Validity->timeMax=toTime(timeMax); } QDomElement dateMin = validity.namedItem( "datemin" ).toElement(); if ( !dateMin.isNull() ) { m_Validity->dateMin=toDate(dateMin); } QDomElement dateMax = validity.namedItem( "datemax" ).toElement(); if ( !dateMax.isNull() ) { m_Validity->dateMax=toDate(dateMax); } } // // Load the comment // QDomElement comment = cell.namedItem( "comment" ).toElement(); if ( !comment.isNull() && ( pm == ::Normal || pm == ::Comment || pm == ::NoBorder )) { QString t = comment.text(); //t = t.stripWhiteSpace(); setComment( t ); } // // The real content of the cell is loaded here. It is stored in // the "text" tag, which contains either a text or a CDATA section. // QDomElement text = cell.namedItem( "text" ).toElement(); if (!text.isNull() && (pm == ::Normal || pm == ::Text || pm == ::NoBorder )) { /* older versions mistakenly put the datatype attribute on the cell instead of the text. Just move it over in case we're parsing an old document */ if ( cell.hasAttribute( "dataType" ) ) // new docs { text.setAttribute( "dataType", cell.attribute( "dataType" ) ); } loadCellData(text, op); } if ( !f.isNull() && f.hasAttribute( "style" ) ) setStyle( (Style)f.attribute("style").toInt() ); return true; } bool KSpreadCell::loadCellData(QDomElement text, Operation op ) { QString t = text.text(); t = t.stripWhiteSpace(); // A formula like =A1+A2 ? if( t[0] == '=' ) { clearFormula(); t = decodeFormula( t, m_iColumn, m_iRow ); m_strText = pasteOperation( t, m_strText, op ); setFlag(Flag_LayoutDirty); clearFlag(Flag_Error); m_content = Formula; if ( !m_pTable->isLoading() ) // i.e. when pasting if ( !makeFormula() ) kdError(36002) << "ERROR: Syntax ERROR" << endl; } else { bool newStyleLoading = true; if ( text.hasAttribute( "dataType" ) ) // new docs { m_dataType = stringToDataType( text.attribute( "dataType" ) ); } else // old docs: do the ugly solution of calling checkTextInput to parse the text { // ...except for date/time FormatType cellFormatType = formatType(); if ((cellFormatType==KSpreadCell::TextDate || cellFormatType==KSpreadCell::ShortDate ||((int)(cellFormatType)>=200 && (int)(cellFormatType)<=217)) && ( t.contains('/') == 2 )) m_dataType = DateData; else if ( (cellFormatType==KSpreadCell::Time || cellFormatType==KSpreadCell::SecondeTime ||cellFormatType==KSpreadCell::Time_format1 ||cellFormatType==KSpreadCell::Time_format2 ||cellFormatType==KSpreadCell::Time_format3) && ( t.contains(':') == 2 ) ) m_dataType = TimeData; else { m_strText = pasteOperation( t, m_strText, op ); checkTextInput(); //kdDebug(36001) << "KSpreadCell::load called checkTextInput, got m_dataType=" << dataTypeToString( m_dataType ) << " t=" << t << endl; newStyleLoading = false; } } if ( newStyleLoading ) { m_dValue = 0.0; clearFlag(Flag_Error); switch ( m_dataType ) { case BoolData: { #if 0 // Problem: saving simply 'true' and 'false' means we don't know // if we should restore it as true/false, True/False or i18n("True")/i18n("False") .... // OTOH saving the original text means an environment, in another language, will not parse it. // We should save both, the bool value and the text... if ( t == "false" ) m_dValue = 0.0; else if ( t == "true" ) m_dValue = 1.0; else kdWarning() << "Cell with BoolData, should be true or false: " << t << endl; #endif m_strText = pasteOperation( t, m_strText, op ); bool ok = tryParseBool( m_strText ); if ( !ok ) kdWarning(36001) << "Couldn't parse " << t << " as bool." << endl; break; } case NumericData: { bool ok = false; m_dValue = t.toDouble(&ok); // We save in non-localized format m_strText = pasteOperation( t, m_strText, op ); if ( !ok ) kdWarning(36001) << "Couldn't parse '" << t << "' as number." << endl; if ( formatType() == Percentage ) { setFactor(100.0); // should have been already done by loadLayout m_strText += '%'; } break; } case DateData: { int pos = t.find('/'); int year = t.mid(0,pos).toInt(); int pos1 = t.find('/',pos+1); int month = t.mid(pos+1,((pos1-1)-pos)).toInt(); int day = t.right(t.length()-pos1-1).toInt(); m_Date = QDate(year,month,day); if(valueDate().isValid() ) // Should always be the case for new docs m_strText = locale()->formatDate( valueDate(), true ); else { // This happens with old docs, when format is set wrongly to date m_strText = pasteOperation( t, m_strText, op ); checkTextInput(); } break; } case TimeData: { int hours = -1; int minutes = -1; int second = -1; int pos, pos1; pos = t.find(':'); hours = t.mid(0,pos).toInt(); pos1 = t.find(':',pos+1); minutes = t.mid(pos+1,((pos1-1)-pos)).toInt(); second = t.right(t.length()-pos1-1).toInt(); m_Time = QTime(hours,minutes,second); if(valueTime().isValid() ) // Should always be the case for new docs m_strText = locale()->formatTime( valueTime(), true ); else { // This happens with old docs, when format is set wrongly to time m_strText = pasteOperation( t, m_strText, op ); checkTextInput(); } break; } // A StringData, QML or a visual formula default: // Set the cell's text m_strText = pasteOperation( t, m_strText, op ); } setFlag(Flag_LayoutDirty); } } if ( !m_pTable->isLoading() ) setCellText(m_strText); return true; } QTime KSpreadCell::toTime(QDomElement &element) { QString t = element.text(); t = t.stripWhiteSpace(); int hours = -1; int minutes = -1; int second = -1; int pos, pos1; pos = t.find(':'); hours = t.mid(0,pos).toInt(); pos1 = t.find(':',pos+1); minutes = t.mid(pos+1,((pos1-1)-pos)).toInt(); second = t.right(t.length()-pos1-1).toInt(); m_Time = QTime(hours,minutes,second); return valueTime(); } QDate KSpreadCell::toDate(QDomElement &element) { QString t = element.text(); int pos; int pos1; int year = -1; int month = -1; int day = -1; pos = t.find('/'); year = t.mid(0,pos).toInt(); pos1 = t.find('/',pos+1); month = t.mid(pos+1,((pos1-1)-pos)).toInt(); day = t.right(t.length()-pos1-1).toInt(); m_Date = QDate(year,month,day); return valueDate(); } const char* KSpreadCell::s_dataTypeToString[] = { "Str", "Bool", "Num", "Date", "Time", "Other", 0 }; QString KSpreadCell::dataTypeToString( DataType dt ) const { Q_ASSERT( dt <= LastDataType ); if ( dt <= LastDataType ) return QString::fromLatin1( s_dataTypeToString[ dt ] ); else return QString::null; // error } KSpreadCell::DataType KSpreadCell::stringToDataType( const QString& str ) const { for ( int i = 0 ; s_dataTypeToString[i] ; ++i ) if ( str == s_dataTypeToString[i] ) return static_cast(i); kdWarning(36001) << "Unknown datatype " << str << endl; return StringData; } QString KSpreadCell::pasteOperation( QString new_text, QString old_text, Operation op ) { if ( op == OverWrite ) return new_text; QString tmp_op; QString tmp; QString old; if( !new_text.isEmpty() && new_text[0] == '=' ) { tmp = new_text.right( new_text.length() - 1 ); } else { tmp = new_text; } if ( old_text.isEmpty() && ( op == Add || op == Mul || op == Sub || op == Div ) ) { old_text = "=0"; } if( !old_text.isEmpty() && old_text[0] == '=' ) { old = old_text.right( old_text.length() - 1 ); } else { old = old_text; } bool b1, b2; tmp.toDouble( &b1 ); old.toDouble( &b2 ); if (b1 && !b2 && old.length() == 0) { old = "0"; b2 = true; } if( b1 && b2 ) { switch( op ) { case Add: tmp_op = "="+QString::number(old.toDouble()+tmp.toDouble()); break; case Mul : tmp_op = "="+QString::number(old.toDouble()*tmp.toDouble()); break; case Sub: tmp_op = "="+QString::number(old.toDouble()-tmp.toDouble()); break; case Div: tmp_op = "="+QString::number(old.toDouble()/tmp.toDouble()); break; default: Q_ASSERT( 0 ); } tmp_op = decodeFormula( tmp_op, m_iColumn, m_iRow ); setFlag(Flag_LayoutDirty); clearFlag(Flag_Error); m_content = Formula; return tmp_op; } else if ( ( new_text[0] == '=' && old_text[0] == '=' ) || ( b1 && old_text[0] == '=' ) || ( new_text[0] == '=' && b2 ) ) { switch( op ) { case Add: tmp_op="=("+old+")+"+"("+tmp+")"; break; case Mul : tmp_op="=("+old+")*"+"("+tmp+")"; break; case Sub: tmp_op="=("+old+")-"+"("+tmp+")"; break; case Div: tmp_op="=("+old+")/"+"("+tmp+")"; break; default : Q_ASSERT( 0 ); } clearFormula(); tmp_op = decodeFormula( tmp_op, m_iColumn, m_iRow ); setFlag(Flag_LayoutDirty); clearFlag(Flag_Error); m_content = Formula; return tmp_op; } new_text = decodeFormula( new_text, m_iColumn, m_iRow ); setFlag(Flag_LayoutDirty); clearFlag(Flag_Error); m_content = Formula; return new_text; } void KSpreadCell::setStyle( Style _s ) { if ( m_style == _s ) return; m_style = _s; setFlag(Flag_LayoutDirty); if ( m_pPrivate ) delete m_pPrivate; m_pPrivate = 0; if ( _s != ST_Select ) return; m_pPrivate = new SelectPrivate( this ); SelectPrivate *s = (SelectPrivate*)m_pPrivate; if ( isFormula() ) s->parse( m_strFormulaOut ); else s->parse( m_strText ); checkTextInput(); // is this necessary? setFlag(Flag_LayoutDirty); if ( !m_pTable->isLoading() ) update(); } QString KSpreadCell::testAnchor( int _x, int _y ) { if ( !m_pQML ) return QString::null; return m_pQML->anchorAt( QPoint( _x, _y ) ); } void KSpreadCell::tableDies() { // Avoid unobscuring the cells in the destructor. m_iExtraXCells = 0; m_iExtraYCells = 0; m_nextCell = 0; m_previousCell = 0; } KSpreadCell::~KSpreadCell() { if ( m_nextCell ) m_nextCell->setPreviousCell( m_previousCell ); if ( m_previousCell ) m_previousCell->setNextCell( m_nextCell ); if ( m_pPrivate ) delete m_pPrivate; if ( m_pQML ) delete m_pQML; if(m_Validity!=0) delete m_Validity; // Unobscure cells. for( int x = 0; x <= m_iExtraXCells; ++x ) for( int y = (x == 0) ? 1 : 0; // avoid looking at (+0,+0) y <= m_iExtraYCells; ++y ) { KSpreadCell* cell = m_pTable->cellAt( m_iColumn + x, m_iRow + y ); if ( cell ) cell->unobscure(); } } bool KSpreadCell::operator > ( const KSpreadCell & cell ) const { if ( isNumeric() ) // ### what about bools ? { if ( cell.isNumeric() ) return valueDouble() > cell.valueDouble(); else return false; // numbers are always < than texts } else if(isDate()) { if( cell.isDate() ) return valueDate() > cell.valueDate(); else if (cell.isNumeric()) return true; else return false; //date are always < than texts and time } else if(isTime()) { if( cell.isTime() ) return valueTime() > cell.valueTime(); else if( cell.isDate()) return true; //time are always > than date else if( cell.isNumeric()) return true; else return false; //time are always < than texts } else return valueString().compare(cell.valueString()) > 0; } bool KSpreadCell::operator < ( const KSpreadCell & cell ) const { if ( isNumeric() ) { if ( cell.isNumeric() ) return valueDouble() < cell.valueDouble(); else return true; // numbers are always < than texts } else if(isDate()) { if( cell.isDate() ) return valueDate() < cell.valueDate(); else if( cell.isNumeric()) return false; else return true; //date are always < than texts and time } else if(isTime()) { if( cell.isTime() ) return valueTime() < cell.valueTime(); else if(cell.isDate()) return false; //time are always > than date else if( cell.isNumeric()) return false; else return true; //time are always < than texts } else return valueString().compare(cell.valueString()) < 0; } bool KSpreadCell::isDefault() const { return ( m_iColumn == 0 ); } void KSpreadCell::NotifyDepending( int col, int row, KSpreadTable* table, bool isDepending ) { if (isDefault()) { return; } KSpreadDependency *d = NULL; bool alreadyInList = false; /* see if this cell is already in the list */ for (d = m_lstDependingOnMe.first(); d != NULL && !alreadyInList; d = m_lstDependingOnMe.next() ) { alreadyInList = (d->Left() <= row && d->Right() >= row && d->Top() <= col && d->Bottom() >= col && d->Table() == table); } if (isDepending && !alreadyInList) { /* if we're supposed to add it and it's not already in there, add it */ d = new KSpreadDependency(col, row, table); m_lstDependingOnMe.prepend(d); } else if (!isDepending && alreadyInList) { /* if we're supposed to remove it and it actually was there, then remove it */ m_lstDependingOnMe.remove(); } return; } void KSpreadCell::NotifyDependancyList(QPtrList lst, bool isDepending) { KSpreadDependency *d = NULL; for (d = lst.first(); d != NULL; d = lst.next()) { for (int c = d->Left(); c <= d->Right(); c++) { for (int r = d->Top(); r <= d->Bottom(); r++) { d->Table()->cellAt( c, r )->NotifyDepending(m_iColumn, m_iRow, m_pTable, isDepending); } } } } int KSpreadCell::obscuringCellsColumn() { return (m_pObscuringCell != NULL) ? m_pObscuringCell->column() : 0; } int KSpreadCell::obscuringCellsRow() { return (m_pObscuringCell != NULL) ? m_pObscuringCell->row() : 0; } QValueList KSpreadCell::GetConditionList() { return conditions.GetConditionList(); } void KSpreadCell::SetConditionList(QValueList newList) { conditions.SetConditionList(newList); } bool KSpreadCell::hasError() const { return testFlag(Flag_Error); } bool KSpreadCell::calcDirtyFlag() { return (m_content == Formula ? false : testFlag(Flag_CalcDirty)); } bool KSpreadCell::layoutDirtyFlag() const { return testFlag(Flag_LayoutDirty); } void KSpreadCell::clearDisplayDirtyFlag() { clearFlag(Flag_DisplayDirty); } void KSpreadCell::setDisplayDirtyFlag() { setFlag(Flag_DisplayDirty); } bool KSpreadCell::isForceExtraCells() const { return testFlag(Flag_ForceExtra); } void KSpreadCell::clearFlag( CellFlags flag ) { m_flagsMask &= ~(Q_UINT32)flag; } void KSpreadCell::setFlag( CellFlags flag ) { m_flagsMask |= (Q_UINT32)flag; } bool KSpreadCell::testFlag( CellFlags flag ) const { return ( m_flagsMask & (Q_UINT32)flag ); } /*************************************************** * * SelectPrivate * ***************************************************/ void SelectPrivate::parse( const QString& _text ) { m_lstItems.clear(); if ( _text.isEmpty() ) return; m_lstItems = QStringList::split( '\\', _text ); if ( m_iIndex != -1 && m_iIndex < (int)m_lstItems.count() ) { } else if ( m_lstItems.count() > 0 ) m_iIndex = 0; else m_iIndex = -1; } void SelectPrivate::slotItemSelected( int _id ) { m_iIndex = _id; m_pCell->setLayoutDirtyFlag(); m_pCell->checkTextInput(); // is this necessary ? m_pCell->update(); m_pCell->table()->updateCell( m_pCell, m_pCell->column(), m_pCell->row() ); } QString SelectPrivate::text() const { if ( m_iIndex == -1 ) return QString::null; return m_lstItems[ m_iIndex ]; } KSpreadCellPrivate* SelectPrivate::copy( KSpreadCell* cell ) { SelectPrivate* p = new SelectPrivate( cell ); p->m_lstItems = m_lstItems; p->m_iIndex = m_iIndex; return p; } #include "kspread_cell.moc" diff --git a/kspread/kspread_table.cc b/kspread/kspread_table.cc index 45f594c0d88..9d8b76f569e 100644 --- a/kspread/kspread_table.cc +++ b/kspread/kspread_table.cc @@ -1,6907 +1,6886 @@ /* This file is part of the KDE project Copyright (C) 1998, 1999 Torben Weis 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include #include #include #include #include #include #include #include #include #include #include "kspread_global.h" #include "kspread_undo.h" #include "kspread_map.h" #include "kspread_doc.h" #include "kspread_util.h" #include "kspread_canvas.h" #include "KSpreadTableIface.h" #include #include #include /***************************************************************************** * * CellBinding * *****************************************************************************/ CellBinding::CellBinding( KSpreadTable *_table, const QRect& _area ) { m_rctDataArea = _area; m_pTable = _table; m_pTable->addCellBinding( this ); m_bIgnoreChanges = false; } CellBinding::~CellBinding() { m_pTable->removeCellBinding( this ); } void CellBinding::cellChanged( KSpreadCell *_cell ) { if ( m_bIgnoreChanges ) return; emit changed( _cell ); } bool CellBinding::contains( int _x, int _y ) { return m_rctDataArea.contains( QPoint( _x, _y ) ); } /***************************************************************************** * * ChartBinding * *****************************************************************************/ ChartBinding::ChartBinding( KSpreadTable *_table, const QRect& _area, ChartChild *_child ) : CellBinding( _table, _area ) { m_child = _child; } ChartBinding::~ChartBinding() { } void ChartBinding::cellChanged( KSpreadCell* ) { kdDebug(36001) << "######### void ChartBinding::cellChanged( KSpreadCell* )" << endl; if ( m_bIgnoreChanges ) return; kdDebug(36001) << "with=" << m_rctDataArea.width() << " height=" << m_rctDataArea.height() << endl; KoChart::Data matrix( m_rctDataArea.height(), m_rctDataArea.width() ); KSpreadCell* cell; for ( int y = 0; y < m_rctDataArea.height(); y++ ) for ( int x = 0; x < m_rctDataArea.width(); x++ ) { cell = m_pTable->cellAt( m_rctDataArea.left() + x, m_rctDataArea.top() + y ); if ( cell && cell->isNumeric() ) matrix.cell( y, x ) = KoChart::Value( cell->valueDouble() ); else if ( cell ) matrix.cell( y, x ) = KoChart::Value( cell->valueString() ); else matrix.cell( y, x ) = KoChart::Value(); } // ######### Kalle may be interested in that, too /* Chart::Range range; range.top = m_rctDataArea.top(); range.left = m_rctDataArea.left(); range.right = m_rctDataArea.right(); range.bottom = m_rctDataArea.bottom(); range.table = m_pTable->name(); */ m_child->chart()->setData( matrix ); // Force a redraw of the chart on all views table()->emit_polygonInvalidated( m_child->framePointArray() ); } /******************************************************************/ /* Class: KSpreadTextDrag */ /******************************************************************/ KSpreadTextDrag::KSpreadTextDrag( QWidget * dragSource, const char * name ) : QTextDrag( dragSource, name ) { } KSpreadTextDrag::~KSpreadTextDrag() { } QByteArray KSpreadTextDrag::encodedData( const char * mime ) const { if ( strcmp( selectionMimeType(), mime ) == 0) return m_kspread; else return QTextDrag::encodedData( mime ); } bool KSpreadTextDrag::canDecode( QMimeSource* e ) { if ( e->provides( selectionMimeType() ) ) return true; return QTextDrag::canDecode(e); } const char * KSpreadTextDrag::format( int i ) const { if ( i < 4 ) // HACK, but how to do otherwise ?? return QTextDrag::format(i); else if ( i == 4 ) return selectionMimeType(); else return 0; } const char * KSpreadTextDrag::selectionMimeType() { return "application/x-kspread-snippet"; } /***************************************************************************** * * KSpreadTable * *****************************************************************************/ int KSpreadTable::s_id = 0L; QIntDict* KSpreadTable::s_mapTables; KSpreadTable* KSpreadTable::find( int _id ) { if ( !s_mapTables ) return 0L; return (*s_mapTables)[ _id ]; } KSpreadTable::KSpreadTable( KSpreadMap *_map, const QString &tableName, const char *_name ) : QObject( _map, _name ) { if ( s_mapTables == 0L ) s_mapTables = new QIntDict; m_id = s_id++; s_mapTables->insert( m_id, this ); m_defaultLayout = new KSpreadLayout( this ); m_emptyPen.setStyle( Qt::NoPen ); m_marker.setCoords( 1, 1, 1, 1 ); m_pMap = _map; m_pDoc = _map->doc(); m_dcop = 0; dcopObject(); m_bShowPageBorders = FALSE; m_lstCellBindings.setAutoDelete( FALSE ); m_strName = tableName; // m_lstChildren.setAutoDelete( true ); m_cells.setAutoDelete( true ); m_rows.setAutoDelete( true ); m_columns.setAutoDelete( true ); m_pDefaultCell = new KSpreadCell( this, 0, 0 ); m_pDefaultRowLayout = new RowLayout( this, 0 ); m_pDefaultRowLayout->setDefault(); m_pDefaultColumnLayout = new ColumnLayout( this, 0 ); m_pDefaultColumnLayout->setDefault(); // No selection is active m_rctSelection.setCoords( 0, 0, 0, 0 ); m_pWidget = new QWidget(); m_pPainter = new QPainter; m_pPainter->begin( m_pWidget ); m_iMaxColumn = 256; m_iMaxRow = 256; m_ulSizeMaxX = KS_colMax * m_pDefaultColumnLayout->width(); // default is max cols * default width m_ulSizeMaxY = KS_rowMax * m_pDefaultRowLayout->height(); // default is max rows * default height m_bScrollbarUpdates = true; setHidden( false ); m_bShowGrid=true; m_bPrintGrid=false; m_bShowFormula=false; m_bLcMode=false; m_bShowColumnNumber=false; m_bHideZero=false; m_bFirstLetterUpper=false; m_bAutoCalc=true; // Get a unique name so that we can offer scripting if ( !_name ) { QCString s; s.sprintf("Table%i", s_id ); QObject::setName( s.data() ); } m_oldPos=QPoint(1,1); m_iScrollPosX=0; m_iScrollPosY=0; } bool KSpreadTable::isEmpty( unsigned long int x, unsigned long int y ) { KSpreadCell* c = cellAt( x, y ); if ( !c || c->isEmpty() ) return true; return false; } const ColumnLayout* KSpreadTable::columnLayout( int _column ) const { const ColumnLayout *p = m_columns.lookup( _column ); if ( p != 0L ) return p; return m_pDefaultColumnLayout; } ColumnLayout* KSpreadTable::columnLayout( int _column ) { ColumnLayout *p = m_columns.lookup( _column ); if ( p != 0L ) return p; return m_pDefaultColumnLayout; } const RowLayout* KSpreadTable::rowLayout( int _row ) const { const RowLayout *p = m_rows.lookup( _row ); if ( p != 0L ) return p; return m_pDefaultRowLayout; } RowLayout* KSpreadTable::rowLayout( int _row ) { RowLayout *p = m_rows.lookup( _row ); if ( p != 0L ) return p; return m_pDefaultRowLayout; } int KSpreadTable::leftColumn( int _xpos, int &_left, KSpreadCanvas *_canvas ) { if ( _canvas ) { _xpos += _canvas->xOffset(); _left = -_canvas->xOffset(); } else _left = 0; int col = 1; int x = columnLayout( col )->width( _canvas ); while ( x < _xpos ) { // Should never happen if ( col > KS_colMax - 1 ) { kdDebug(36001) << "KSpreadTable:leftColumn: invalid column (col: " << col + 1 << ")" << endl; return 1; } _left += columnLayout( col )->width( _canvas ); col++; x += columnLayout( col )->width( _canvas ); } return col; } int KSpreadTable::rightColumn( int _xpos, KSpreadCanvas *_canvas ) { if ( _canvas ) _xpos += _canvas->xOffset(); int col = 1; int x = 0; while ( x < _xpos ) { // Should never happen if ( col > KS_colMax ) { kdDebug(36001) << "KSpreadTable:rightColumn: invalid column (col: " << col << ")" << endl; return KS_colMax + 1; } x += columnLayout( col )->width( _canvas ); col++; } return col - 1; } int KSpreadTable::topRow( int _ypos, int & _top, KSpreadCanvas *_canvas ) { if ( _canvas ) { _ypos += _canvas->yOffset(); _top = -_canvas->yOffset(); } else _top = 0; int row = 1; int y = rowLayout( row )->height( _canvas ); while ( y < _ypos ) { // Should never happen if ( row > KS_rowMax - 1 ) { kdDebug(36001) << "KSpreadTable:topRow: invalid row (row: " << row + 1 << ")" << endl; return 1; } _top += rowLayout( row )->height( _canvas ); row++; y += rowLayout( row )->height( _canvas); } return row; } int KSpreadTable::bottomRow( int _ypos, KSpreadCanvas *_canvas ) { if ( _canvas ) _ypos += _canvas->yOffset(); int row = 1; int y = 0; while ( y < _ypos ) { // Should never happen if ( row > KS_rowMax ) { kdDebug(36001) << "KSpreadTable:bottomRow: invalid row (row: " << row << ")" << endl; return KS_rowMax + 1; } y += rowLayout( row )->height( _canvas ); row++; } return row - 1; } int KSpreadTable::columnPos( int _col, KSpreadCanvas *_canvas ) { int x = 0; if ( _canvas ) x -= _canvas->xOffset(); for ( int col = 1; col < _col; col++ ) { // Should never happen if ( col > KS_colMax ) { kdDebug(36001) << "KSpreadTable:columnPos: invalid column (col: " << col << ")" << endl; return x; } x += columnLayout( col )->width( _canvas ); } return x; } int KSpreadTable::rowPos( int _row, KSpreadCanvas *_canvas ) { int y = 0; if ( _canvas ) y -= _canvas->yOffset(); /* _row--; //we need the height until the questioned row, so we need the heights of the previous ones int row = 0; RowLayout *p; kdDebug(36001) << "row1: " << row << " y: " << y << endl; while ( ( p = m_rows.getNextRowLayoutDown( row ) ) && ( p->row() < _row ) ) { y += p->height( _canvas ); y += ( p->row() - row - 1 ) * 20; kdDebug(36001) << "row (n)): " << row << " p->row()" << p->row() << " y: " << y << endl; row = p->row(); } kdDebug(36001) << "row2: " << row << " y: " << y << endl; // if ( p != NULL ) { kdDebug(36001) << "arow2: " << row << " y: " << y << endl; // y += p->height( _canvas ); kdDebug(36001) << "_row2: " << _row << " y: " << y << endl; y += ( _row - row ) * 20; } kdDebug(36001) << "row3: " << row << " y: " << y << endl; kdDebug(36001) << endl; */ for ( int row = 1 ; row < _row ; row++ ) { // Should never happen if ( row > KS_rowMax ) { kdDebug(36001) << "KSpreadTable:rowPos: invalid row (row: " << row << ")" << endl; return y; } y += rowLayout( row )->height( _canvas ); } return y; } void KSpreadTable::adjustSizeMaxX ( int _x ) { m_ulSizeMaxX += _x; } void KSpreadTable::adjustSizeMaxY ( int _y ) { m_ulSizeMaxY += _y; } KSpreadCell* KSpreadTable::visibleCellAt( int _column, int _row, bool _scrollbar_update ) { KSpreadCell* cell = cellAt( _column, _row, _scrollbar_update ); if ( cell->isObscured() ) return cellAt( cell->obscuringCellsColumn(), cell->obscuringCellsRow(), _scrollbar_update ); return cell; } KSpreadCell* KSpreadTable::firstCell() { return m_cells.firstCell(); } RowLayout* KSpreadTable::firstRow() { return m_rows.first(); } ColumnLayout* KSpreadTable::firstCol() { return m_columns.first(); } const KSpreadCell* KSpreadTable::cellAt( int _column, int _row ) const { const KSpreadCell *p = m_cells.lookup( _column, _row ); if ( p != 0L ) return p; return m_pDefaultCell; } KSpreadCell* KSpreadTable::cellAt( int _column, int _row, bool _scrollbar_update ) { if ( _column > KS_colMax ) { _column = KS_colMax; kdDebug (36001) << "KSpreadTable::cellAt: column range: (col: " << _column << ")" << endl; } if ( _row > KS_rowMax) { kdDebug (36001) << "KSpreadTable::cellAt: row out of range: (row: " << _row << ")" << endl; _row = KS_rowMax; } if ( _scrollbar_update && m_bScrollbarUpdates ) { checkRangeHBorder( _column ); checkRangeVBorder( _row ); } KSpreadCell *p = m_cells.lookup( _column, _row ); if ( p != 0L ) return p; return m_pDefaultCell; } ColumnLayout* KSpreadTable::nonDefaultColumnLayout( int _column, bool force_creation ) { ColumnLayout *p = m_columns.lookup( _column ); if ( p != 0L || !force_creation ) return p; p = new ColumnLayout( this, _column ); p->setWidth( m_pDefaultColumnLayout->width() ); //p->setWidth( static_cast(colWidth) ); m_columns.insertElement( p, _column ); return p; } RowLayout* KSpreadTable::nonDefaultRowLayout( int _row, bool force_creation ) { RowLayout *p = m_rows.lookup( _row ); if ( p != 0L || !force_creation ) return p; p = new RowLayout( this, _row ); // TODO: copy the default RowLayout here!! p->setHeight( m_pDefaultRowLayout->height() ); //Laurent : // I used heightOfRow because before it doesn't work: // we used POINT_TO_MM and after MM_TO_POINT // POINT_TO_MM != 1/MM_TO_POINT // so it didn't give the good result // p->setHeight(static_cast(heightOfRow)); m_rows.insertElement( p, _row ); return p; } KSpreadCell* KSpreadTable::nonDefaultCell( int _column, int _row, bool _scrollbar_update ) { if ( _scrollbar_update && m_bScrollbarUpdates ) { checkRangeHBorder( _column ); checkRangeVBorder( _row ); } KSpreadCell *p = m_cells.lookup( _column, _row ); if ( p != 0L ) return p; KSpreadCell *cell = new KSpreadCell( this, _column, _row ); insertCell( cell ); return cell; } void KSpreadTable::setText( int _row, int _column, const QString& _text, bool updateDepends ) { KSpreadCell *cell = nonDefaultCell( _column, _row ); if ( !m_pDoc->undoBuffer()->isLocked() ) { KSpreadUndoSetText *undo = new KSpreadUndoSetText( m_pDoc, this, cell->text(), _column, _row,cell->formatType() ); m_pDoc->undoBuffer()->appendUndo( undo ); } // The cell will force a display refresh itself, so we dont have to care here. cell->setCellText( _text, updateDepends ); //refresh anchor if(_text.at(0)=='!') emit sig_updateView( this, QRect(_column,_row,_column,_row) ); } void KSpreadTable::setLayoutDirtyFlag() { KSpreadCell* c = m_cells.firstCell(); for( ; c; c = c->nextCell() ) c->setLayoutDirtyFlag(); } void KSpreadTable::setCalcDirtyFlag() { KSpreadCell* c = m_cells.firstCell(); for( ; c; c = c->nextCell() ) { if(!(c->isObscured() &&c->isObscuringForced())) c->setCalcDirtyFlag(); } } void KSpreadTable::recalc() { // First set all cells as dirty setCalcDirtyFlag(); this->calc(); } void KSpreadTable::calc() { KSpreadCell* c = m_cells.firstCell(); for( ; c; c = c->nextCell() ) { c->calc(); } } void KSpreadTable::setChooseRect( const QRect &_sel ) { if ( _sel == m_chooseRect ) return; QRect old( m_chooseRect ); m_chooseRect = _sel; emit sig_changeChooseSelection( this, old, m_chooseRect ); } void KSpreadTable::unselect() { // No selection? Then do nothing. if ( m_rctSelection.left() == 0 ) return; QRect r = m_rctSelection; // Discard the selection m_rctSelection.setCoords( 0, 0, 0, 0 ); // Emit signal so that the views can update. emit sig_unselect( this, r ); } void KSpreadTable::setMarker( const QPoint& _point, KSpreadCanvas *_canvas ) { setSelection( QRect(), _point, _canvas ); } QRect KSpreadTable::markerRect() const { QRect r; if ( m_rctSelection.left() == 0 ) r = m_marker; else r = m_rctSelection; if ( r.topLeft() == r.bottomRight() ) { const KSpreadCell* cell = cellAt( r.left(), r.top() ); if ( cell->extraXCells() || cell->extraYCells() ) r.setCoords( r.left(), r.top(), r.left() + cell->extraXCells(), r.top() + cell->extraYCells() ); } return r; } QRect KSpreadTable::marker() const { return m_marker; } void KSpreadTable::setSelection( const QRect &_sel, KSpreadCanvas *_canvas ) { m_oldPos=QPoint( m_marker.topLeft()); if ( _sel.left() == 0 ) setSelection( _sel, m_marker.topLeft(), _canvas ); else { if ( m_marker.topLeft() != _sel.topLeft() && m_marker.topRight() != _sel.topRight() && m_marker.bottomLeft() != _sel.bottomLeft() && m_marker.bottomRight() != _sel.bottomRight() ) setSelection( _sel, _sel.topLeft(), _canvas ); else setSelection( _sel, m_marker.topLeft(), _canvas ); } } void KSpreadTable::setSelection( const QRect &_sel, const QPoint& m, KSpreadCanvas *_canvas ) { if ( _sel == m_rctSelection && m == m_marker.topLeft() ) return; // We want to see whether a single cell was clicked like a button. // This is only of interest if no cell was selected before if ( _sel.left() == 0 ) { // So we test first whether only a single cell was selected KSpreadCell *cell = cellAt( m_rctSelection.left(), m_rctSelection.top() ); // Did we mark only a single cell ? // Take care: One cell may obscure other cells ( extra size! ). if ( m_rctSelection.left() + cell->extraXCells() == m_rctSelection.right() && m_rctSelection.top() + cell->extraYCells() == m_rctSelection.bottom() ) cell->clicked( _canvas ); } QRect old_marker = m_marker; QRect old( m_rctSelection ); m_rctSelection = _sel; KSpreadCell* cell = cellAt( m.x(), m.y() ); if ( cell->extraXCells() || cell->extraYCells()) m_marker.setCoords( m.x(), m.y(), m.x() + cell->extraXCells(), m.y() + cell->extraYCells() ); else if(cell->isObscuringForced()) { KSpreadCell* cell2 = cellAt( cell->obscuringCellsColumn(), cell->obscuringCellsRow() ); QRect extraArea; extraArea.setCoords( cell->obscuringCellsColumn(), cell->obscuringCellsRow(), cell->obscuringCellsColumn() + cell2->extraXCells(), cell->obscuringCellsRow() + cell2->extraYCells() ); if(extraArea.contains(m.x(),m.y())) { m_marker=extraArea; } else { m_oldPos=QPoint( m.x(),m.y()); m_marker = QRect( m, m ); } } else { m_oldPos=QPoint( m.x(),m.y()); m_marker = QRect( m, m ); } emit sig_changeSelection( this, old, old_marker ); } /* 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 KSpreadUndoCellLayout; difference in title only) { rows selected: if condition Y clear properties X,X' of cells; set properties X,X' of rowlayouts emit complete update; } { columns selected: if condition Y clear properties X,X' of cells; set properties X,X' of columnlayouts; 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 KSpreadUndoAction* KSpreadTable::CellWorkerTypeA::createUndoAction( KSpreadDoc* doc, KSpreadTable* table, QRect& r ) { QString title = getUndoTitle(); return new KSpreadUndoCellLayout( doc, table, r, title ); } KSpreadTable::SelectionType KSpreadTable::workOnCells( const QPoint& _marker, CellWorker& worker ) { // see what is selected; if nothing, take marker position bool selected = ( m_rctSelection.left() != 0 ); QRect r( m_rctSelection ); if ( !selected ) r.setCoords( _marker.x(), _marker.y(), _marker.x(), _marker.y() ); // create cells in rows if complete columns selected KSpreadCell *cell; if ( !worker.type_B && selected && isColumnSelected() ) { for ( RowLayout* rw =m_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 == m_pDefaultCell ) // '&& worker.create_if_default' unneccessary as never used in type A { cell = new KSpreadCell( this, i, rw->row() ); insertCell( cell ); } } } } } // create an undo action if ( !m_pDoc->undoBuffer()->isLocked() ) { KSpreadUndoAction *undo = worker.createUndoAction( m_pDoc, this, r ); // test if the worker has an undo action if ( undo != 0L ) m_pDoc->undoBuffer()->appendUndo( undo ); } // complete rows selected ? if ( selected && isRowSelected() ) { int row; for ( KSpreadCell* cell = m_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 layouts for ( int i=m_rctSelection.top(); i<=m_rctSelection.bottom(); i++ ) { RowLayout *rw=nonDefaultRowLayout(i); worker.doWork( rw ); } if ( worker.emit_signal ) emit sig_updateView( this ); } return CompleteRows; } // complete columns selected ? else if ( selected && isColumnSelected() ) { int col; for ( KSpreadCell* cell = m_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 layouts for ( int i=m_rctSelection.left(); i<=m_rctSelection.right(); i++ ) { ColumnLayout *cl=nonDefaultColumnLayout(i); worker.doWork( cl ); } KSpreadCell *cell; for ( RowLayout* rw =m_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 == m_pDefaultCell ) { cell = new KSpreadCell( 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 { KSpreadCell *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 == m_pDefaultCell ) { cell = new KSpreadCell( this, x, y ); insertCell( cell ); } if ( cell != m_pDefaultCell ) worker.doWork( cell, true, x, y ); } } if ( worker.emit_signal ) emit sig_updateView( this, r ); return CellRegion; } } struct SetSelectionFontWorker : public KSpreadTable::CellWorkerTypeA { const char *_font; int _size; signed char _bold; signed char _italic; signed char _underline; signed char _strike; SetSelectionFontWorker( const char *font, int size, signed char bold, signed char italic,signed char underline, signed char strike ) : _font( font ), _size( size ), _bold( bold ), _italic( italic ), _underline( underline ), _strike( strike ) { } QString getUndoTitle() { return i18n("Change font"); } bool testCondition( RowLayout* rw ) { return ( rw->hasProperty( KSpreadCell::PFont ) ); } void doWork( RowLayout* rw ) { if ( _font ) rw->setTextFontFamily( _font ); if ( _size > 0 ) rw->setTextFontSize( _size ); if ( _italic >= 0 ) rw->setTextFontItalic( (bool)_italic ); if ( _bold >= 0 ) rw->setTextFontBold( (bool)_bold ); if ( _underline >= 0 ) rw->setTextFontUnderline( (bool)_underline ); if ( _strike >= 0 ) rw->setTextFontStrike( (bool)_strike ); } void doWork( ColumnLayout* cl ) { if ( _font ) cl->setTextFontFamily( _font ); if ( _size > 0 ) cl->setTextFontSize( _size ); if ( _italic >= 0 ) cl->setTextFontItalic( (bool)_italic ); if ( _bold >= 0 ) cl->setTextFontBold( (bool)_bold ); if ( _underline >= 0 ) cl->setTextFontUnderline( (bool)_underline ); if ( _strike >= 0 ) cl->setTextFontStrike( (bool)_strike ); } void prepareCell( KSpreadCell* cell ) { cell->clearProperty( KSpreadCell::PFont ); cell->clearNoFallBackProperties( KSpreadCell::PFont ); } bool testCondition( KSpreadCell* cell ) { return ( !cell->isObscuringForced() ); } void doWork( KSpreadCell* cell, bool cellRegion, int, int ) { if ( cellRegion ) cell->setDisplayDirtyFlag(); if ( _font ) cell->setTextFontFamily( _font ); if ( _size > 0 ) cell->setTextFontSize( _size ); if ( _italic >= 0 ) cell->setTextFontItalic( (bool)_italic ); if ( _bold >= 0 ) cell->setTextFontBold( (bool)_bold ); if ( _underline >= 0 ) cell->setTextFontUnderline( (bool)_underline ); if ( _strike >= 0 ) cell->setTextFontStrike( (bool)_strike ); if ( cellRegion ) cell->clearDisplayDirtyFlag(); } }; void KSpreadTable::setSelectionFont( const QPoint &_marker, const char *_font, int _size, signed char _bold, signed char _italic,signed char _underline, signed char _strike ) { SetSelectionFontWorker w( _font, _size, _bold, _italic, _underline, _strike ); workOnCells( _marker, w ); } struct SetSelectionSizeWorker : public KSpreadTable::CellWorkerTypeA { int _size, size; SetSelectionSizeWorker( int __size, int size2 ) : _size( __size ), size( size2 ) { } QString getUndoTitle() { return i18n("Change font"); } bool testCondition( RowLayout* rw ) { return ( rw->hasProperty( KSpreadCell::PFont ) ); } void doWork( RowLayout* rw ) { rw->setTextFontSize( size + _size) ; } void doWork( ColumnLayout* cl ) { cl->setTextFontSize( size + _size ); } void prepareCell( KSpreadCell* cell ) { cell->clearProperty( KSpreadCell::PFont ); cell->clearNoFallBackProperties( KSpreadCell::PFont ); } bool testCondition( KSpreadCell* cell ) { return ( !cell->isObscuringForced() ); } void doWork( KSpreadCell* cell, bool cellRegion, int, int ) { if ( cellRegion ) cell->setDisplayDirtyFlag(); cell->setTextFontSize( size + _size ); if ( cellRegion ) cell->clearDisplayDirtyFlag(); } }; void KSpreadTable::setSelectionSize( const QPoint &_marker, int _size ) { int size; KSpreadCell* c; c=cellAt(_marker.x(), _marker.y()); size=c->textFontSize(_marker.x(), _marker.y()); SetSelectionSizeWorker w( _size, size ); workOnCells( _marker, w ); } struct SetSelectionUpperLowerWorker : public KSpreadTable::CellWorker { int _type; SetSelectionUpperLowerWorker( int type ) : KSpreadTable::CellWorker( false ), _type( type ) { } class KSpreadUndoAction* createUndoAction( KSpreadDoc* doc, KSpreadTable* table, QRect& r ) { return new KSpreadUndoChangeAreaTextCell( doc, table, r ); } bool testCondition( KSpreadCell* c ) { return ( !c->isNumeric() && !c->isBool() &&!c->isFormula() && !c->isDefault() && !c->text().isEmpty() && c->text()[0] != '*' && c->text()[0] != '!' && !c->isObscuringForced() ); } void doWork( KSpreadCell* 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 KSpreadTable::setSelectionUpperLower( const QPoint &_marker, int _type ) { SetSelectionUpperLowerWorker w( _type ); workOnCells( _marker, w ); } struct SetSelectionFirstLetterUpperWorker : public KSpreadTable::CellWorker { SetSelectionFirstLetterUpperWorker( ) : KSpreadTable::CellWorker( false ) { } class KSpreadUndoAction* createUndoAction( KSpreadDoc* doc, KSpreadTable* table, QRect& r ) { return new KSpreadUndoChangeAreaTextCell( doc, table, r ); } bool testCondition( KSpreadCell* c ) { return ( !c->isNumeric() && !c->isBool() &&!c->isFormula() && !c->isDefault() && !c->text().isEmpty() && c->text()[0] != '*' && c->text()[0] != '!' && !c->isObscuringForced() ); } void doWork( KSpreadCell* cell, bool, int, int ) { cell->setDisplayDirtyFlag(); QString tmp = cell->text(); int len = tmp.length(); cell->setCellText( (tmp.at(0).upper()+tmp.right(len-1)) ); cell->clearDisplayDirtyFlag(); } }; void KSpreadTable::setSelectionfirstLetterUpper( const QPoint &_marker) { SetSelectionFirstLetterUpperWorker w; workOnCells( _marker, w ); } struct SetSelectionVerticalTextWorker : public KSpreadTable::CellWorker { bool _b; SetSelectionVerticalTextWorker( bool b ) : KSpreadTable::CellWorker( ), _b( b ) { } class KSpreadUndoAction* createUndoAction( KSpreadDoc* doc, KSpreadTable* table, QRect& r ) { QString title=i18n("Vertical Text"); return new KSpreadUndoCellLayout( doc, table, r, title ); } bool testCondition( KSpreadCell* cell ) { return ( !cell->isObscuringForced() ); } void doWork( KSpreadCell* cell, bool, int, int ) { cell->setDisplayDirtyFlag(); cell->setVerticalText( _b ); cell->setMultiRow( false ); cell->setAngle( 0 ); cell->clearDisplayDirtyFlag(); } }; void KSpreadTable::setSelectionVerticalText( const QPoint &_marker, bool _b ) { SetSelectionVerticalTextWorker w( _b ); workOnCells( _marker, w ); } struct SetSelectionCommentWorker : public KSpreadTable::CellWorker { QString _comment; SetSelectionCommentWorker( QString comment ) : KSpreadTable::CellWorker( ), _comment( comment ) { } class KSpreadUndoAction* createUndoAction( KSpreadDoc* doc, KSpreadTable* table, QRect& r ) { QString title=i18n("Add comment"); return new KSpreadUndoCellLayout( doc, table, r, title ); } bool testCondition( KSpreadCell* cell ) { return ( !cell->isObscuringForced() ); } void doWork( KSpreadCell* cell, bool, int, int ) { cell->setDisplayDirtyFlag(); cell->setComment( _comment ); cell->clearDisplayDirtyFlag(); } }; void KSpreadTable::setSelectionComment( const QPoint &_marker, const QString &_comment) { SetSelectionCommentWorker w( _comment ); workOnCells( _marker, w ); } struct SetSelectionAngleWorker : public KSpreadTable::CellWorkerTypeA { int _value; SetSelectionAngleWorker( int value ) : _value( value ) { } QString getUndoTitle() { return i18n("Change angle"); } bool testCondition( RowLayout* rw ) { return ( rw->hasProperty( KSpreadCell::PAngle ) ); } void doWork( RowLayout* rw ) { rw->setAngle( _value ); } void doWork( ColumnLayout* cl ) { cl->setAngle( _value ); } void prepareCell( KSpreadCell* cell ) { cell->clearProperty( KSpreadCell::PAngle ); cell->clearNoFallBackProperties( KSpreadCell::PAngle ); } bool testCondition( KSpreadCell* cell ) { return ( !cell->isObscuringForced() ); } void doWork( KSpreadCell* cell, bool cellRegion, int, int ) { if ( cellRegion ) cell->setDisplayDirtyFlag(); cell->setAngle( _value ); if ( cellRegion ) { cell->setVerticalText(false); cell->setMultiRow( false ); cell->clearDisplayDirtyFlag(); } } }; void KSpreadTable::setSelectionAngle( const QPoint &_marker, int _value ) { SetSelectionAngleWorker w( _value ); workOnCells( _marker, w ); } struct SetSelectionRemoveCommentWorker : public KSpreadTable::CellWorker { SetSelectionRemoveCommentWorker( ) : KSpreadTable::CellWorker( false ) { } class KSpreadUndoAction* createUndoAction( KSpreadDoc* doc, KSpreadTable* table, QRect& r ) { QString title=i18n("Remove comment"); return new KSpreadUndoCellLayout( doc, table, r, title ); } bool testCondition( KSpreadCell* cell ) { return ( !cell->isObscuringForced() ); } void doWork( KSpreadCell* cell, bool, int, int ) { cell->setDisplayDirtyFlag(); cell->setComment( "" ); cell->clearDisplayDirtyFlag(); } }; void KSpreadTable::setSelectionRemoveComment( const QPoint &_marker) { SetSelectionRemoveCommentWorker w; workOnCells( _marker, w ); } struct SetSelectionTextColorWorker : public KSpreadTable::CellWorkerTypeA { const QColor& tb_Color; SetSelectionTextColorWorker( const QColor& _tb_Color ) : tb_Color( _tb_Color ) { } QString getUndoTitle() { return i18n("Change text color"); } bool testCondition( RowLayout* rw ) { return ( rw->hasProperty( KSpreadCell::PTextPen ) ); } void doWork( RowLayout* rw ) { rw->setTextColor( tb_Color ); } void doWork( ColumnLayout* cl ) { cl->setTextColor( tb_Color ); } void prepareCell( KSpreadCell* cell ) { cell->clearProperty( KSpreadCell::PTextPen ); cell->clearNoFallBackProperties( KSpreadCell::PTextPen ); } bool testCondition( KSpreadCell* cell ) { return ( !cell->isObscuringForced() ); } void doWork( KSpreadCell* cell, bool cellRegion, int, int ) { if ( cellRegion ) cell->setDisplayDirtyFlag(); cell->setTextColor( tb_Color ); if ( cellRegion ) cell->clearDisplayDirtyFlag(); } }; void KSpreadTable::setSelectionTextColor( const QPoint &_marker, const QColor &tb_Color ) { SetSelectionTextColorWorker w( tb_Color ); workOnCells( _marker, w ); } struct SetSelectionBgColorWorker : public KSpreadTable::CellWorkerTypeA { const QColor& bg_Color; SetSelectionBgColorWorker( const QColor& _bg_Color ) : bg_Color( _bg_Color ) { } QString getUndoTitle() { return i18n("Change background color"); } bool testCondition( RowLayout* rw ) { return ( rw->hasProperty( KSpreadCell::PBackgroundColor ) ); } void doWork( RowLayout* rw ) { rw->setBgColor( bg_Color ); } void doWork( ColumnLayout* cl ) { cl->setBgColor( bg_Color ); } void prepareCell( KSpreadCell* cell ) { cell->clearProperty( KSpreadCell::PBackgroundColor ); cell->clearNoFallBackProperties( KSpreadCell::PBackgroundColor ); } bool testCondition( KSpreadCell* cell ) { return ( !cell->isObscuringForced() ); } void doWork( KSpreadCell* cell, bool cellRegion, int, int ) { if ( cellRegion ) cell->setDisplayDirtyFlag(); cell->setBgColor( bg_Color ); if ( cellRegion ) cell->clearDisplayDirtyFlag(); } }; void KSpreadTable::setSelectionbgColor( const QPoint &_marker, const QColor &bg_Color ) { SetSelectionBgColorWorker w( bg_Color ); workOnCells( _marker, w ); } struct SetSelectionBorderColorWorker : public KSpreadTable::CellWorker { const QColor& bd_Color; SetSelectionBorderColorWorker( const QColor& _bd_Color ) : KSpreadTable::CellWorker( false ), bd_Color( _bd_Color ) { } class KSpreadUndoAction* createUndoAction( KSpreadDoc* doc, KSpreadTable* table, QRect& r ) { QString title=i18n("Change border color"); return new KSpreadUndoCellLayout( doc, table, r, title ); } bool testCondition( KSpreadCell* cell ) { return ( !cell->isObscuringForced() ); } void doWork( KSpreadCell* cell, bool, int, int ) { cell->setDisplayDirtyFlag(); int it_Row = cell->row(); int it_Col = cell->column(); if ( cell->topBorderStyle( it_Row, it_Col )!=Qt::NoPen ) cell->setTopBorderColor( bd_Color ); if ( cell->leftBorderStyle( it_Row, it_Col )!=Qt::NoPen ) cell->setLeftBorderColor( bd_Color ); if ( cell->fallDiagonalStyle( it_Row, it_Col )!=Qt::NoPen ) cell->setFallDiagonalColor( bd_Color ); if ( cell->goUpDiagonalStyle( it_Row, it_Col )!=Qt::NoPen ) cell->setGoUpDiagonalColor( bd_Color ); if ( cell->bottomBorderStyle( it_Row, it_Col )!=Qt::NoPen ) cell->setBottomBorderColor( bd_Color ); if ( cell->rightBorderStyle( it_Row, it_Col )!=Qt::NoPen ) cell->setRightBorderColor( bd_Color ); cell->clearDisplayDirtyFlag(); } }; void KSpreadTable::setSelectionBorderColor( const QPoint &_marker, const QColor &bd_Color ) { SetSelectionBorderColorWorker w( bd_Color ); workOnCells( _marker, w ); } void KSpreadTable::setSeries( const QPoint &_marker, double start, double end, double step, Series mode, Series type) { doc()->emitBeginOperation(); QString 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 numberOfCells = (int) ((start - end) / step + 1); /* initialize for linear */ 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; } KSpreadCell *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 */ QRect 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++ ) { cell = cellAt( _marker.x(), y ); if ( cell->isObscuringForced() ) { /* case 2. */ undoRegion.setLeft(QMIN(undoRegion.left(), cell->obscuringCellsColumn())); cell = cellAt( cell->obscuringCellsColumn(), cell->obscuringCellsRow() ); } /* 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 ); } else if(mode == Row) { for ( x = _marker.x(); x <=(_marker.x() + numberOfCells - 1); x++ ) { /* see the code above for a column series for a description of what is going on here. */ cell = cellAt( x,_marker.y() ); if ( cell->isObscuringForced() ) { undoRegion.setTop(QMIN(undoRegion.top(), cell->obscuringCellsRow())); cell = cellAt( cell->obscuringCellsColumn(), cell->obscuringCellsRow() ); } numberOfCells += cell->extraXCells(); x += cell->extraXCells(); } undoRegion.setBottom( _marker.y() ); undoRegion.setRight( x - 1 ); } if ( !m_pDoc->undoBuffer()->isLocked() ) { KSpreadUndoChangeAreaTextCell *undo = new KSpreadUndoChangeAreaTextCell( m_pDoc, this, undoRegion ); m_pDoc->undoBuffer()->appendUndo( undo ); } x = _marker.x(); y = _marker.y(); /* now we're going to actually loop through and set the values */ double incr; if (step >= 0) { for ( incr = start; incr <= end; ) { cell = nonDefaultCell( x, y ); if (cell->isObscuringForced()) { cell = cellAt( cell->obscuringCellsColumn(), cell->obscuringCellsRow()); } cell->setCellText(cellText.setNum( incr )); if (mode == Column) { ++y; if (cell->isForceExtraCells()) { y += cell->extraYCells(); } } else if (mode == Row) { ++x; if (cell->isForceExtraCells()) { x += cell->extraXCells(); } } 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 ); if (cell->isObscuringForced()) { cell = cellAt( cell->obscuringCellsColumn(), cell->obscuringCellsRow()); } cell->setCellText(cellText.setNum( incr )); if (mode == Column) { ++y; if (cell->isForceExtraCells()) { y += cell->extraYCells(); } } else if (mode == Row) { ++x; if (cell->isForceExtraCells()) { x += cell->extraXCells(); } } 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; } } } doc()->emitEndOperation(); } struct SetSelectionPercentWorker : public KSpreadTable::CellWorkerTypeA { bool b; SetSelectionPercentWorker( bool _b ) : b( _b ) { } QString getUndoTitle() { return i18n("Format percent"); } bool testCondition( RowLayout* rw ) { return ( rw->hasProperty( KSpreadCell::PFactor ) ); } void doWork( RowLayout* rw ) { rw->setFactor( b ? 100.0 : 1.0 ); //rw->setPrecision( 0 ); rw->setFormatType( b ? KSpreadCell::Percentage : KSpreadCell::Number); } void doWork( ColumnLayout* cl ) { cl->setFactor( b ? 100.0 : 1.0 ); cl->setFormatType( b ? KSpreadCell::Percentage : KSpreadCell::Number); } void prepareCell( KSpreadCell* cell ) { cell->clearProperty(KSpreadCell::PFactor); cell->clearNoFallBackProperties( KSpreadCell::PFactor ); cell->clearProperty(KSpreadCell::PFormatType); cell->clearNoFallBackProperties( KSpreadCell::PFormatType ); } bool testCondition( KSpreadCell* cell ) { return ( !cell->isObscuringForced() ); } void doWork( KSpreadCell* cell, bool cellRegion, int, int ) { if ( cellRegion ) cell->setDisplayDirtyFlag(); cell->setFactor( b ? 100.0 : 1.0 ); cell->setFormatType( b ? KSpreadCell::Percentage : KSpreadCell::Number); if ( cellRegion ) cell->clearDisplayDirtyFlag(); } }; void KSpreadTable::setSelectionPercent( const QPoint &_marker, bool b ) { SetSelectionPercentWorker w( b ); workOnCells( _marker, w ); } void KSpreadTable::refreshRemoveAreaName(const QString &_areaName) { KSpreadCell* c = m_cells.firstCell(); QString tmp="'"+_areaName+"'"; for( ;c; c = c->nextCell() ) { if(c->isFormula() ) { if(c->text().find(tmp)!=-1) { if ( !c->makeFormula() ) kdError(36002) << "ERROR: Syntax ERROR" << endl; } } } } void KSpreadTable::changeCellTabName(QString old_name,QString new_name) { KSpreadCell* c = m_cells.firstCell(); for( ;c; c = c->nextCell() ) { if(c->isFormula() || c->content()==KSpreadCell::RichText) { if(c->text().find(old_name)!=-1) { int nb = c->text().contains(old_name+"!"); QString tmp=old_name+"!"; int len = tmp.length(); tmp=c->text(); for( int i=0; isetCellText(tmp); } } } } bool KSpreadTable::shiftRow( const QRect &rect,bool makeUndo ) { if ( !m_pDoc->undoBuffer()->isLocked() &&makeUndo) { KSpreadUndoInsertCellRow *undo = new KSpreadUndoInsertCellRow( m_pDoc, this,rect ); m_pDoc->undoBuffer()->appendUndo( 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 = m_cells.shiftRow( QPoint(rect.left(),i) ); if( !result ) res=false; } } QPtrListIterator it( map()->tableList() ); for( ; it.current(); ++it ) { for(int i=rect.top();i<=rect.bottom();i++) it.current()->changeNameCellRef( QPoint(rect.left(),i), false, KSpreadTable::ColumnInsert, name() ,(rect.right()-rect.left()+1)); } refreshChart(QPoint(rect.left(),rect.top()), false, KSpreadTable::ColumnInsert); recalc(); refreshMergedCell(); emit sig_updateView( this ); return res; } bool KSpreadTable::shiftColumn( const QRect& rect,bool makeUndo ) { if ( !m_pDoc->undoBuffer()->isLocked() &&makeUndo) { KSpreadUndoInsertCellCol *undo = new KSpreadUndoInsertCellCol( m_pDoc, this,rect); m_pDoc->undoBuffer()->appendUndo( 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 = m_cells.shiftColumn( QPoint(i,rect.top()) ); if(!result) res=false; } } QPtrListIterator it( map()->tableList() ); for( ; it.current(); ++it ) { for(int i=rect.left();i<=rect.right();i++) it.current()->changeNameCellRef( QPoint(i,rect.top()), false, KSpreadTable::RowInsert, name() ,(rect.bottom()-rect.top()+1)); } refreshChart(/*marker*/QPoint(rect.left(),rect.top()), false, KSpreadTable::RowInsert); recalc(); refreshMergedCell(); emit sig_updateView( this ); return res; } void KSpreadTable::unshiftColumn( const QRect & rect,bool makeUndo ) { if ( !m_pDoc->undoBuffer()->isLocked() &&makeUndo) { KSpreadUndoRemoveCellCol *undo = new KSpreadUndoRemoveCellCol( m_pDoc, this,rect); m_pDoc->undoBuffer()->appendUndo( undo ); } for(int i =rect.top();i<=rect.bottom();i++) for(int j=rect.left();j<=rect.right();j++) m_cells.remove(j,i); for(int i =rect.left();i<=rect.right();i++) for(int j=0;j<=(rect.bottom()-rect.top());j++) m_cells.unshiftColumn( QPoint(i,rect.top()) ); QPtrListIterator it( map()->tableList() ); for( ; it.current(); ++it ) for(int i=rect.left();i<=rect.right();i++) it.current()->changeNameCellRef( QPoint(i,rect.top()), false, KSpreadTable::RowRemove, name(),(rect.bottom()-rect.top()+1) ); refreshChart( QPoint(rect.left(),rect.top()), false, KSpreadTable::RowRemove ); refreshMergedCell(); recalc(); emit sig_updateView( this ); } void KSpreadTable::unshiftRow( const QRect & rect,bool makeUndo ) { if ( !m_pDoc->undoBuffer()->isLocked() &&makeUndo) { KSpreadUndoRemoveCellRow *undo = new KSpreadUndoRemoveCellRow( m_pDoc, this,rect ); m_pDoc->undoBuffer()->appendUndo( undo ); } for(int i =rect.top();i<=rect.bottom();i++) for(int j=rect.left();j<=rect.right();j++) m_cells.remove(j,i); for(int i =rect.top();i<=rect.bottom();i++) for(int j=0;j<=(rect.right()-rect.left());j++) m_cells.unshiftRow( QPoint(rect.left(),i) ); QPtrListIterator it( map()->tableList() ); for( ; it.current(); ++it ) for(int i=rect.top();i<=rect.bottom();i++) it.current()->changeNameCellRef( QPoint(rect.left(),i), false, KSpreadTable::ColumnRemove, name(),(rect.right()-rect.left()+1) ); refreshChart(QPoint(rect.left(),rect.top()), false, KSpreadTable::ColumnRemove ); refreshMergedCell(); recalc(); emit sig_updateView( this ); } bool KSpreadTable::insertColumn( int col, int nbCol, bool makeUndo ) { if ( !m_pDoc->undoBuffer()->isLocked() && makeUndo) { KSpreadUndoInsertColumn *undo = new KSpreadUndoInsertColumn( m_pDoc, this, col, nbCol ); m_pDoc->undoBuffer()->appendUndo( undo ); } bool res=true; bool result; for( int i=0; i<=nbCol; i++ ) { // Recalculate range max (minus size of last column) m_ulSizeMaxX -= columnLayout( KS_colMax )->width(); result = m_cells.insertColumn( col ); m_columns.insertColumn( col ); if(!result) res=false; //Recalculate range max (plus size of new column) m_ulSizeMaxX += columnLayout( col+i )->width(); } QPtrListIterator it( map()->tableList() ); for( ; it.current(); ++it ) it.current()->changeNameCellRef( QPoint( col, 1 ), true, KSpreadTable::ColumnInsert, name(), nbCol+1 ); refreshChart( QPoint( col, 1 ), true, KSpreadTable::ColumnInsert ); refreshMergedCell(); recalc(); emit sig_updateHBorder( this ); emit sig_updateView( this ); return res; } bool KSpreadTable::insertRow( int row, int nbRow, bool makeUndo ) { if ( !m_pDoc->undoBuffer()->isLocked() && makeUndo) { KSpreadUndoInsertRow *undo = new KSpreadUndoInsertRow( m_pDoc, this, row, nbRow ); m_pDoc->undoBuffer()->appendUndo( undo ); } bool res=true; bool result; for( int i=0; i<=nbRow; i++ ) { // Recalculate range max (minus size of last row) m_ulSizeMaxY -= rowLayout( KS_rowMax )->height(); result = m_cells.insertRow( row ); m_rows.insertRow( row ); if( !result ) res = false; //Recalculate range max (plus size of new row) m_ulSizeMaxY += rowLayout( row )->height(); } QPtrListIterator it( map()->tableList() ); for( ; it.current(); ++it ) it.current()->changeNameCellRef( QPoint( 1, row ), true, KSpreadTable::RowInsert, name(), nbRow+1 ); refreshChart( QPoint( 1, row ), true, KSpreadTable::RowInsert ); refreshMergedCell(); recalc(); emit sig_updateVBorder( this ); emit sig_updateView( this ); return res; } void KSpreadTable::removeColumn( int col, int nbCol, bool makeUndo ) { if ( !m_pDoc->undoBuffer()->isLocked() && makeUndo) { KSpreadUndoRemoveColumn *undo = new KSpreadUndoRemoveColumn( m_pDoc, this, col, nbCol ); m_pDoc->undoBuffer()->appendUndo( undo ); } for( int i=0; i<=nbCol; i++ ) { // Recalculate range max (minus size of removed column) m_ulSizeMaxX -= columnLayout( col )->width(); m_cells.removeColumn( col ); m_columns.removeColumn( col ); //Recalculate range max (plus size of new column) m_ulSizeMaxX += columnLayout( KS_colMax )->width(); } QPtrListIterator it( map()->tableList() ); for( ; it.current(); ++it ) it.current()->changeNameCellRef( QPoint( col, 1 ), true, KSpreadTable::ColumnRemove, name(), nbCol+1 ); refreshChart( QPoint( col, 1 ), true, KSpreadTable::ColumnRemove ); recalc(); refreshMergedCell(); emit sig_updateHBorder( this ); emit sig_updateView( this ); } void KSpreadTable::removeRow( int row, int nbRow, bool makeUndo ) { if ( !m_pDoc->undoBuffer()->isLocked() && makeUndo ) { KSpreadUndoRemoveRow *undo = new KSpreadUndoRemoveRow( m_pDoc, this, row, nbRow ); m_pDoc->undoBuffer()->appendUndo( undo ); } for( int i=0; i<=nbRow; i++ ) { // Recalculate range max (minus size of removed row) m_ulSizeMaxY -= rowLayout( row )->height(); m_cells.removeRow( row ); m_rows.removeRow( row ); //Recalculate range max (plus size of new row) m_ulSizeMaxY += rowLayout( KS_rowMax )->height(); } QPtrListIterator it( map()->tableList() ); for( ; it.current(); ++it ) it.current()->changeNameCellRef( QPoint( 1, row ), true, KSpreadTable::RowRemove, name(), nbRow+1 ); refreshChart( QPoint( 1, row ), true, KSpreadTable::RowRemove ); recalc(); refreshMergedCell(); emit sig_updateVBorder( this ); emit sig_updateView( this ); } void KSpreadTable::hideRow( int _row, int nbRow, QValueList_list ) { if ( !m_pDoc->undoBuffer()->isLocked() ) { KSpreadUndoHideRow *undo ; if( nbRow!=-1 ) undo= new KSpreadUndoHideRow( m_pDoc, this, _row, nbRow ); else undo= new KSpreadUndoHideRow( m_pDoc, this, _row, nbRow, _list ); m_pDoc->undoBuffer()->appendUndo( undo ); } RowLayout *rl; if( nbRow!=-1 ) { for( int i=0; i<=nbRow; i++ ) { rl=nonDefaultRowLayout( _row+i ); rl->setHide(true); } } else { QValueList::Iterator it; for( it = _list.begin(); it != _list.end(); ++it ) { rl=nonDefaultRowLayout( *it ); rl->setHide(true); } } emitHideRow(); } void KSpreadTable::emitHideRow() { emit sig_updateVBorder( this ); emit sig_updateView( this ); } void KSpreadTable::showRow( int _row, int nbRow, QValueList_list ) { if ( !m_pDoc->undoBuffer()->isLocked() ) { KSpreadUndoShowRow *undo; if(nbRow!=-1) undo = new KSpreadUndoShowRow( m_pDoc, this, _row,nbRow ); else undo = new KSpreadUndoShowRow( m_pDoc, this, _row,nbRow, _list ); m_pDoc->undoBuffer()->appendUndo( undo ); } RowLayout *rl; if( nbRow!=-1 ) { for( int i=0; i<=nbRow; i++ ) { rl=nonDefaultRowLayout( _row + i ); rl->setHide( false ); } } else { QValueList::Iterator it; for( it = _list.begin(); it != _list.end(); ++it ) { rl=nonDefaultRowLayout( *it ); rl->setHide( false ); } } emit sig_updateVBorder( this ); emit sig_updateView( this ); } void KSpreadTable::hideColumn( int _col, int nbCol, QValueList_list ) { if ( !m_pDoc->undoBuffer()->isLocked() ) { KSpreadUndoHideColumn *undo; if( nbCol!=-1 ) undo= new KSpreadUndoHideColumn( m_pDoc, this, _col, nbCol ); else undo= new KSpreadUndoHideColumn( m_pDoc, this, _col, nbCol, _list ); m_pDoc->undoBuffer()->appendUndo( undo ); } ColumnLayout *cl; if( nbCol != -1 ) { for( int i=0; i<=nbCol; i++ ) { cl=nonDefaultColumnLayout( _col + i ); cl->setHide( true ); } } else { QValueList::Iterator it; for( it = _list.begin(); it != _list.end(); ++it ) { cl=nonDefaultColumnLayout( *it ); cl->setHide( true ); } } emitHideColumn(); } void KSpreadTable::emitHideColumn() { emit sig_updateHBorder( this ); emit sig_updateView( this ); } void KSpreadTable::showColumn( int _col, int nbCol, QValueList_list ) { if ( !m_pDoc->undoBuffer()->isLocked() ) { KSpreadUndoShowColumn *undo; if( nbCol != -1 ) undo = new KSpreadUndoShowColumn( m_pDoc, this, _col, nbCol ); else undo = new KSpreadUndoShowColumn( m_pDoc, this, _col, nbCol, _list ); m_pDoc->undoBuffer()->appendUndo( undo ); } ColumnLayout *cl; if( nbCol != -1 ) { for( int i=0; i<=nbCol; i++ ) { cl=nonDefaultColumnLayout( _col + i ); cl->setHide( false ); } } else { QValueList::Iterator it; for( it = _list.begin(); it != _list.end(); ++it ) { cl=nonDefaultColumnLayout( *it ); cl->setHide( false ); } } emit sig_updateHBorder( this ); emit sig_updateView( this ); } void KSpreadTable::refreshChart(const QPoint & pos, bool fullRowOrColumn, ChangeRef ref) { KSpreadCell * c = m_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) + if (c == 0L) { - CellBinding * bind = firstCellBinding(); - if ( bind != 0L ) - bind->cellChanged( 0 ); + CellBinding * bind; + for ( bind = firstCellBinding(); bind != 0L; bind = nextCellBinding() ) + { + bind->cellChanged( 0 ); + } + // CellBinding * bind = firstCellBinding(); + // if ( bind != 0L ) + // bind->cellChanged( 0 ); } - // CellBinding * bind; - // for ( bind = firstCellBinding(); bind != 0L; bind = nextCellBinding() ) - // { - // bind->cellChanged( 0 ); - // } } void KSpreadTable::refreshMergedCell() { KSpreadCell* c = m_cells.firstCell(); for( ;c; c = c->nextCell() ) { if(c->isForceExtraCells()) c->forceExtraCells( c->column(), c->row(), c->extraXCells(), c->extraYCells() ); } } QRect KSpreadTable::selectionCellMerged(const QRect &_sel) { QRect selection(_sel); if( isColumnSelected() || isRowSelected() ) return selection; else { int top=selection.top(); int left=selection.left(); int bottom=selection.bottom(); int right=selection.right(); KSpreadCell *cell; for ( int x = selection.left(); x <= selection.right(); x++ ) for ( int y = selection.top(); y <= selection.bottom(); y++ ) { cell = cellAt( x, y ); if( cell->isForceExtraCells()) { right=QMAX(right,cell->extraXCells()+x); bottom=QMAX(bottom,cell->extraYCells()+y); } else if ( cell->isObscured() && cell->isObscuringForced() ) { int moveX=cell->obscuringCellsColumn(); int moveY=cell->obscuringCellsRow(); cell = cellAt( moveX, moveY ); left=QMIN(left,moveX); top=QMIN(top,moveY); bottom=QMAX(bottom,moveY+cell->extraYCells()); right=QMAX(right,moveX+cell->extraXCells()); } } selection.setCoords(left,top,right,bottom); } return selection; } void KSpreadTable::changeNameCellRef(const QPoint & pos, bool fullRowOrColumn, ChangeRef ref, QString tabname, int nbCol) { bool correctDefaultTableName = (tabname == name()); // for cells without table ref (eg "A1") KSpreadCell* c = m_cells.firstCell(); for( ;c; c = c->nextCell() ) { if(c->isFormula()) { QString origText = c->text(); unsigned int i = 0; QString newText; bool correctTableName = correctDefaultTableName; //bool previousCorrectTableName = false; QChar origCh; for ( ; i < origText.length(); ++i ) { origCh = origText[i]; if ( origCh != ':' && origCh != '$' && !origCh.isLetter() ) { newText += origCh; // Reset the "correct table indicator" correctTableName = correctDefaultTableName; } else // Letter or dollar : maybe start of cell name/range // (or even ':', like in a range - note that correctTable is kept in this case) { // Collect everything that forms a name (cell name or table name) QString str; bool tableNameFound = false; //Table names need spaces for( ; ( i < origText.length() ) && // until the end ( ( origText[i].isLetter() || origText[i].isDigit() || origText[i] == '$' ) || // all text and numbers are welcome ( tableNameFound && origText[i].isSpace() ) ) //in case of a table name, we include spaces too ; ++i ) { str += origText[i]; if ( origText[i] == '!' ) tableNameFound = true; } // Was it a table name ? if ( origText[i] == '!' ) { newText += str + '!'; // Copy it (and the '!') // Look for the table name right before that '!' correctTableName = ( newText.right( tabname.length()+1 ) == tabname+"!" ); } else // It must be a cell identifier { // Parse it KSpreadPoint point( str ); if (point.isValid()) { int col = point.pos.x(); int row = point.pos.y(); // Update column if ( point.columnFixed ) newText += '$'; if(ref==ColumnInsert && correctTableName && col>=pos.x() // Column after the new one : +1 && ( fullRowOrColumn || row == pos.y() ) ) // All rows or just one { newText += util_encodeColumnLabelText( col+nbCol ); } else if(ref==ColumnRemove && correctTableName && col > pos.x() // Column after the deleted one : -1 && ( fullRowOrColumn || row == pos.y() ) ) // All rows or just one { newText += util_encodeColumnLabelText( col-nbCol ); } else newText += util_encodeColumnLabelText( col ); // Update row if ( point.rowFixed ) newText += '$'; if(ref==RowInsert && correctTableName && row >= pos.y() // Row after the new one : +1 && ( fullRowOrColumn || col == pos.x() ) ) // All columns or just one { newText += QString::number( row+nbCol ); } else if(ref==RowRemove && correctTableName && row > pos.y() // Column after the deleted one : -1 && ( fullRowOrColumn || col == pos.x() ) ) // All columns or just one { newText += QString::number( row-nbCol ); } else newText += QString::number( row ); } 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]; } } } c->setCellText(newText, false /* no recalc deps for each, done independently */ ); } } } void KSpreadTable::find( const QPoint &_marker, QString _find, long options, KSpreadCanvas *canvas ) { // Identify the region of interest. QRect region( m_rctSelection ); if (options & KoFindDialog::SelectedText) { bool selectionValid = ( m_rctSelection.left() != 0 ); // Complete rows selected ? if ( isRowSelected() ) { } // Complete columns selected ? else if ( isColumnSelected() ) { } else { if ( !selectionValid ) region.setCoords( _marker.x(), _marker.y(), _marker.x(), _marker.y() ); } } else { // All cells. region.setCoords( 0, 0, m_iMaxColumn, m_iMaxRow ); } // Create the class that handles all the actual Find stuff, and connect it to its // local slots. KoFind dialog( _find, options ); QObject::connect( &dialog, SIGNAL( highlight( const QString &, int, int, const QRect & ) ), canvas, SLOT( highlight( const QString &, int, int, const QRect & ) ) ); // Now do the finding... QRect cellRegion( 0, 0, 0, 0 ); bool bck = options & KoFindDialog::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 & KoFindDialog::FromCursor ) { colStart = _marker.x(); rowStart = _marker.y(); } KSpreadCell *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() ) { QString text = cell->text(); cellRegion.setTop( row ); cellRegion.setLeft( col ); if ( !dialog.find( text, cellRegion ) ) return; } } } } void KSpreadTable::replace( const QPoint &_marker, QString _find, QString _replace, long options, KSpreadCanvas *canvas ) { // Identify the region of interest. QRect region( m_rctSelection ); if (options & KoReplaceDialog::SelectedText) { bool selectionValid = ( m_rctSelection.left() != 0 ); // Complete rows selected ? if ( isRowSelected() ) { } // Complete columns selected ? else if ( isColumnSelected() ) { } else { if ( !selectionValid ) region.setCoords( _marker.x(), _marker.y(), _marker.x(), _marker.y() ); } } else { // All cells. region.setCoords( 0, 0, m_iMaxRow, m_iMaxColumn ); } // Create the class that handles all the actual replace stuff, and connect it to its // local slots. KoReplace dialog( _find, _replace, options ); QObject::connect( &dialog, SIGNAL( highlight( const QString &, int, int, const QRect & ) ), canvas, SLOT( highlight( const QString &, int, int, const QRect & ) ) ); QObject::connect( &dialog, SIGNAL( replace( const QString &, int, int,int, const QRect & ) ), canvas, SLOT( replace( const QString &, int, int,int, const QRect & ) ) ); // Now do the replacing... if ( !m_pDoc->undoBuffer()->isLocked() ) { KSpreadUndoChangeAreaTextCell *undo = new KSpreadUndoChangeAreaTextCell( m_pDoc, this, region ); m_pDoc->undoBuffer()->appendUndo( undo ); } QRect cellRegion( 0, 0, 0, 0 ); bool bck = options & KoFindDialog::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 & KoFindDialog::FromCursor ) { colStart = _marker.x(); rowStart = _marker.y(); } KSpreadCell *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() ) { QString text = cell->text(); cellRegion.setTop( row ); cellRegion.setLeft( col ); if (!dialog.replace( text, cellRegion )) return; } } } } void KSpreadTable::borderBottom( const QPoint &_marker,const QColor &_color ) { QRect r( m_rctSelection ); if ( m_rctSelection.left()==0 ) { KSpreadCell *cell = cellAt( _marker.x(), _marker.y() ); r.setCoords( _marker.x(), _marker.y(), _marker.x() + cell->extraXCells(), _marker.y() + cell->extraYCells() ); } QPen pen( _color,1,SolidLine); // Complete rows selected ? if ( isRowSelected() ) { if ( !m_pDoc->undoBuffer()->isLocked() ) { QString title=i18n("Change border"); KSpreadUndoCellLayout *undo = new KSpreadUndoCellLayout( m_pDoc, this, r, title ); m_pDoc->undoBuffer()->appendUndo( undo ); } int row; KSpreadCell* c = m_cells.firstCell(); for( ;c; c = c->nextCell() ) { row = c->row(); if ( m_rctSelection.bottom() == row ) { c->clearProperty( KSpreadCell::PBottomBorder ); c->clearNoFallBackProperties( KSpreadCell::PBottomBorder ); } } RowLayout *rw=nonDefaultRowLayout(m_rctSelection.bottom()); rw->setBottomBorderPen(pen); emit sig_updateView( this ); return; } // Complete columns selected ? else if ( isColumnSelected() ) { //nothing return; } else { if ( !m_pDoc->undoBuffer()->isLocked() ) { QString title=i18n("Change border"); KSpreadUndoCellLayout *undo = new KSpreadUndoCellLayout( m_pDoc, this, r,title ); m_pDoc->undoBuffer()->appendUndo( undo ); } KSpreadCell* cell; int y; for ( int x = r.left(); x <= r.right(); x++ ) { y = r.bottom(); cell = nonDefaultCell( x, y ); cell->setBottomBorderPen(pen); } emit sig_updateView( this, r ); } } void KSpreadTable::borderRight( const QPoint &_marker,const QColor &_color ) { QRect r( m_rctSelection ); if ( m_rctSelection.left()==0 ) { KSpreadCell *cell = cellAt( _marker.x(), _marker.y() ); r.setCoords( _marker.x(), _marker.y(), _marker.x() + cell->extraXCells(), _marker.y() + cell->extraYCells() ); } QPen pen( _color,1,SolidLine); // Complete rows selected ? if ( isRowSelected() ) { //nothing return; } // Complete columns selected ? else if ( isColumnSelected() ) { if ( !m_pDoc->undoBuffer()->isLocked() ) { QString title=i18n("Change border"); KSpreadUndoCellLayout *undo = new KSpreadUndoCellLayout( m_pDoc, this, r,title ); m_pDoc->undoBuffer()->appendUndo( undo ); } KSpreadCell* c = m_cells.firstCell(); int col; for( ;c; c = c->nextCell() ) { col = c->column(); if ( m_rctSelection.right() == col &&!c->isObscuringForced()) { c->clearProperty( KSpreadCell::PRightBorder ); c->clearNoFallBackProperties( KSpreadCell::PRightBorder ); } } RowLayout* rw =m_rows.first(); /* for( ; rw; rw = rw->next() ) { if ( !rw->isDefault() && (rw->hasProperty(KSpreadCell::PRightBorder))) { for(int i=m_rctSelection.left();i<=m_rctSelection.right();i++) { KSpreadCell *cell = nonDefaultCell( i, rw->row() ); } } } */ ColumnLayout *cl=nonDefaultColumnLayout(m_rctSelection.right()); cl->setRightBorderPen(pen); KSpreadCell* cell; rw =m_rows.first(); for( ; rw; rw = rw->next() ) { if ( !rw->isDefault() && (rw->hasProperty(KSpreadCell::PRightBorder))) { for(int i=m_rctSelection.left();i<=m_rctSelection.right();i++) { cell = nonDefaultCell( i, rw->row() ); cell->setRightBorderPen(pen); } } } emit sig_updateView( this ); return; } else { if ( !m_pDoc->undoBuffer()->isLocked() ) { QString title=i18n("Change border"); KSpreadUndoCellLayout *undo = new KSpreadUndoCellLayout( m_pDoc, this, r,title ); m_pDoc->undoBuffer()->appendUndo( undo ); } KSpreadCell* cell; int x; for ( int y = r.top(); y <= r.bottom(); y++ ) { x = r.right(); cell = nonDefaultCell( x, y ); cell->setRightBorderPen(pen); } emit sig_updateView( this, r ); } } void KSpreadTable::borderLeft( const QPoint &_marker, const QColor &_color ) { QString title=i18n("Change border"); QRect r( m_rctSelection ); if ( m_rctSelection.left()==0 ) { KSpreadCell *cell = cellAt( _marker.x(), _marker.y() ); r.setCoords( _marker.x(), _marker.y(), _marker.x() + cell->extraXCells(), _marker.y() + cell->extraYCells() ); } QPen pen( _color,1,SolidLine); // Complete columns selected ? if ( isColumnSelected() ) { RowLayout* rw =m_rows.first(); /* for( ; rw; rw = rw->next() ) { if ( !rw->isDefault() && (rw->hasProperty(KSpreadCell::PLeftBorder))) { for(int i=m_rctSelection.left();i<=m_rctSelection.right();i++) { KSpreadCell *cell = nonDefaultCell( i, rw->row() ); } } } */ if ( !m_pDoc->undoBuffer()->isLocked() ) { KSpreadUndoCellLayout *undo = new KSpreadUndoCellLayout( m_pDoc, this, r, title ); m_pDoc->undoBuffer()->appendUndo( undo ); } KSpreadCell* c = m_cells.firstCell(); int col; for( ;c; c = c->nextCell() ) { col = c->column(); if ( col==m_rctSelection.left() ) { c->clearProperty( KSpreadCell::PLeftBorder ); c->clearNoFallBackProperties( KSpreadCell::PLeftBorder ); } } ColumnLayout *cl=nonDefaultColumnLayout(m_rctSelection.left()); cl->setLeftBorderPen(pen); KSpreadCell* cell; rw =m_rows.first(); for( ; rw; rw = rw->next() ) { if ( !rw->isDefault() && (rw->hasProperty(KSpreadCell::PLeftBorder))) { for(int i=m_rctSelection.left();i<=m_rctSelection.right();i++) { cell = nonDefaultCell( i, rw->row() ); cell->setLeftBorderPen(pen); } } } emit sig_updateView( this ); return; } else { if ( !m_pDoc->undoBuffer()->isLocked() ) { KSpreadUndoCellLayout *undo = new KSpreadUndoCellLayout( m_pDoc, this, r,title ); m_pDoc->undoBuffer()->appendUndo( undo ); } KSpreadCell* cell; int x; for ( int y = r.top(); y <= r.bottom(); y++ ) { x = r.left(); cell = nonDefaultCell( x, y ); cell->setLeftBorderPen(pen); } emit sig_updateView( this, r ); } } void KSpreadTable::borderTop( const QPoint &_marker,const QColor &_color ) { /* duplicate code in kspread_dlg_layout.cc That needs fixed at some point */ QRect r( m_rctSelection ); if ( m_rctSelection.left()==0 ) { KSpreadCell *cell = cellAt( _marker.x(), _marker.y() ); r.setCoords( _marker.x(), _marker.y(), _marker.x() + cell->extraXCells(), _marker.y() + cell->extraYCells() ); } QString title=i18n("Change border"); QPen pen( _color,1,SolidLine); // Complete rows selected ? if ( isRowSelected() ) { if ( !m_pDoc->undoBuffer()->isLocked() ) { KSpreadUndoCellLayout *undo = new KSpreadUndoCellLayout( m_pDoc, this, r,title ); m_pDoc->undoBuffer()->appendUndo( undo ); } KSpreadCell* c = m_cells.firstCell(); int row; for( ;c; c = c->nextCell() ) { row = c->row(); if ( m_rctSelection.top() == row ) { c->clearProperty( KSpreadCell::PTopBorder ); c->clearNoFallBackProperties( KSpreadCell::PTopBorder ); } } RowLayout *rw=nonDefaultRowLayout(m_rctSelection.top()); rw->setTopBorderPen(pen); emit sig_updateView( this ); return; } // Complete columns selected ? -- the top will just be row 1, then // so it's the same as in no rows/columns selected else { if ( !m_pDoc->undoBuffer()->isLocked() ) { KSpreadUndoCellLayout *undo = new KSpreadUndoCellLayout( m_pDoc, this, r,title ); m_pDoc->undoBuffer()->appendUndo( undo ); } KSpreadCell* cell; int y; for ( int x = r.left(); x <= r.right(); x++ ) { y = r.top(); cell = nonDefaultCell( x, y ); cell->setTopBorderPen(pen); } emit sig_updateView( this, r ); } } void KSpreadTable::borderOutline( const QPoint &_marker,const QColor &_color ) { QRect r( m_rctSelection ); if ( m_rctSelection.left()==0 ) { KSpreadCell *cell = cellAt( _marker.x(), _marker.y() ); r.setCoords( _marker.x(), _marker.y(), _marker.x() + cell->extraXCells(), _marker.y() + cell->extraYCells() ); } if ( !m_pDoc->undoBuffer()->isLocked() ) { QString title=i18n("Change border"); KSpreadUndoCellLayout *undo = new KSpreadUndoCellLayout( m_pDoc, this, r,title ); m_pDoc->undoBuffer()->appendUndo( undo ); } QPen pen( _color,1,SolidLine); // Complete rows selected ? if ( isRowSelected() ) { KSpreadCell* c = m_cells.firstCell(); int row; for( ;c; c = c->nextCell() ) { row = c->row(); if ( m_rctSelection.top() == row ) { c->clearProperty( KSpreadCell::PTopBorder ); c->clearNoFallBackProperties( KSpreadCell::PTopBorder ); } else if ( m_rctSelection.bottom() == row) { c->clearProperty( KSpreadCell::PBottomBorder ); c->clearNoFallBackProperties( KSpreadCell::PBottomBorder ); } } RowLayout *rw=nonDefaultRowLayout(m_rctSelection.top()); rw->setTopBorderPen(pen); rw=nonDefaultRowLayout(m_rctSelection.bottom()); rw->setBottomBorderPen(pen); KSpreadCell* cell; for ( int y = r.top(); y <= r.bottom(); y++ ) { cell = nonDefaultCell( r.left(), y ); cell->setLeftBorderPen(pen); } emit sig_updateView( this ); return; } // Complete columns selected ? else if ( isColumnSelected() ) { KSpreadCell* c = m_cells.firstCell(); for( ;c; c = c->nextCell() ) { int row = c->row(); if ( m_rctSelection.left() == row ) { c->clearProperty( KSpreadCell::PLeftBorder ); c->clearNoFallBackProperties( KSpreadCell::PLeftBorder ); } else if ( m_rctSelection.right() == row) { c->clearProperty( KSpreadCell::PRightBorder ); c->clearNoFallBackProperties( KSpreadCell::PRightBorder ); } } ColumnLayout *cl=nonDefaultColumnLayout(m_rctSelection.left()); cl->setLeftBorderPen(pen); cl=nonDefaultColumnLayout(m_rctSelection.right()); cl->setRightBorderPen(pen); KSpreadCell* cell; for ( int x = r.left(); x <= r.right(); x++ ) { cell = nonDefaultCell( x, r.top() ); cell->setTopBorderPen(pen); } emit sig_updateView( this ); return; } else { for ( int x = r.left(); x <= r.right(); x++ ) { nonDefaultCell( x, r.top() )->setTopBorderPen(pen); nonDefaultCell( x, r.bottom() )->setBottomBorderPen(pen); } for ( int y = r.top(); y <= r.bottom(); y++ ) { nonDefaultCell( r.left(), y )->setLeftBorderPen(pen); nonDefaultCell( r.right(), y )->setRightBorderPen(pen); } emit sig_updateView( this, r ); } } struct SetSelectionBorderAllWorker : public KSpreadTable::CellWorkerTypeA { QPen pen; SetSelectionBorderAllWorker( const QColor& color ) : pen( color, 1, QPen::SolidLine ) { } QString getUndoTitle() { return i18n("Change border"); } bool testCondition( RowLayout* rw ) { return ( rw->hasProperty( KSpreadCell::PRightBorder ) || rw->hasProperty( KSpreadCell::PLeftBorder ) || rw->hasProperty( KSpreadCell::PTopBorder ) || rw->hasProperty( KSpreadCell::PBottomBorder ) ); } void doWork( RowLayout* rw ) { rw->setTopBorderPen( pen ); rw->setRightBorderPen( pen ); rw->setLeftBorderPen( pen ); rw->setBottomBorderPen( pen ); } void doWork( ColumnLayout* cl ) { cl->setTopBorderPen( pen ); cl->setRightBorderPen( pen ); cl->setLeftBorderPen( pen ); cl->setBottomBorderPen( pen ); } void prepareCell( KSpreadCell* c ) { c->clearProperty( KSpreadCell::PTopBorder ); c->clearNoFallBackProperties( KSpreadCell::PTopBorder ); c->clearProperty( KSpreadCell::PBottomBorder ); c->clearNoFallBackProperties( KSpreadCell::PBottomBorder ); c->clearProperty( KSpreadCell::PLeftBorder ); c->clearNoFallBackProperties( KSpreadCell::PLeftBorder ); c->clearProperty( KSpreadCell::PRightBorder ); c->clearNoFallBackProperties( KSpreadCell::PRightBorder ); } bool testCondition(KSpreadCell */*cell*/) { return true; } void doWork( KSpreadCell* cell, bool, int, int ) { //if ( cellRegion ) // cell->setDisplayDirtyFlag(); cell->setTopBorderPen( pen ); cell->setRightBorderPen( pen ); cell->setLeftBorderPen( pen ); cell->setBottomBorderPen( pen ); //if ( cellRegion ) // cell->clearDisplayDirtyFlag(); } }; void KSpreadTable::borderAll( const QPoint &_marker,const QColor &_color ) { SetSelectionBorderAllWorker w( _color ); workOnCells( _marker, w ); } struct SetSelectionBorderRemoveWorker : public KSpreadTable::CellWorkerTypeA { QPen pen; SetSelectionBorderRemoveWorker() : pen( Qt::black, 1, Qt::NoPen ) { } QString getUndoTitle() { return i18n("Change border"); } bool testCondition( RowLayout* rw ) { return ( rw->hasProperty( KSpreadCell::PRightBorder ) || rw->hasProperty( KSpreadCell::PLeftBorder ) || rw->hasProperty( KSpreadCell::PTopBorder ) || rw->hasProperty( KSpreadCell::PBottomBorder ) || rw->hasProperty( KSpreadCell::PFallDiagonal ) || rw->hasProperty( KSpreadCell::PGoUpDiagonal ) ); } void doWork( RowLayout* rw ) { rw->setTopBorderPen( pen ); rw->setRightBorderPen( pen ); rw->setLeftBorderPen( pen ); rw->setBottomBorderPen( pen); rw->setFallDiagonalPen( pen ); rw->setGoUpDiagonalPen (pen ); } void doWork( ColumnLayout* cl ) { cl->setTopBorderPen( pen ); cl->setRightBorderPen( pen ); cl->setLeftBorderPen( pen ); cl->setBottomBorderPen( pen); cl->setFallDiagonalPen( pen ); cl->setGoUpDiagonalPen (pen ); } void prepareCell( KSpreadCell* c ) { c->clearProperty( KSpreadCell::PTopBorder ); c->clearNoFallBackProperties( KSpreadCell::PTopBorder ); c->clearProperty( KSpreadCell::PLeftBorder ); c->clearNoFallBackProperties( KSpreadCell::PLeftBorder ); c->clearProperty( KSpreadCell::PRightBorder ); c->clearNoFallBackProperties( KSpreadCell::PRightBorder ); c->clearProperty( KSpreadCell::PBottomBorder ); c->clearNoFallBackProperties( KSpreadCell::PBottomBorder ); c->clearProperty( KSpreadCell::PFallDiagonal ); c->clearNoFallBackProperties( KSpreadCell::PFallDiagonal ); c->clearProperty( KSpreadCell::PGoUpDiagonal ); c->clearNoFallBackProperties( KSpreadCell::PGoUpDiagonal ); } bool testCondition(KSpreadCell* /*cell*/ ){ return true; } void doWork( KSpreadCell* cell, bool, int, int ) { //if ( cellRegion ) // cell->setDisplayDirtyFlag(); cell->setTopBorderPen( pen ); cell->setRightBorderPen( pen ); cell->setLeftBorderPen( pen ); cell->setBottomBorderPen( pen); cell->setFallDiagonalPen( pen ); cell->setGoUpDiagonalPen (pen ); //if ( cellRegion ) // cell->clearDisplayDirtyFlag(); } }; void KSpreadTable::borderRemove( const QPoint &_marker ) { SetSelectionBorderRemoveWorker w; workOnCells( _marker, w ); } void KSpreadTable::sortByRow( int ref_row, SortingOrder mode ) { KSpreadPoint point; point.table = this; point.tableName = m_strName; point.pos = QPoint( selectionRect().left(), selectionRect().top() ); point.columnFixed = false; point.rowFixed = false; sortByRow( ref_row, 0, 0, mode, mode, mode, 0, false, false, point ); } void KSpreadTable::sortByColumn( int ref_column, SortingOrder mode ) { KSpreadPoint point; point.table = this; point.tableName = m_strName; point.pos = QPoint( selectionRect().left(), selectionRect().top() ); point.columnFixed = false; point.rowFixed = false; sortByColumn( ref_column, 0, 0, mode, mode, mode, 0, false, false, point ); } void KSpreadTable::checkCellContent(KSpreadCell * cell1, KSpreadCell * cell2, int & ret) { if ( cell1->isEmpty() ) { ret = 1; return; } else if ( cell1->isObscured() && cell1->isObscuringForced() ) { ret = 1; return; } else if ( cell2->isEmpty() ) { ret = 2; return; } ret = 0; } void KSpreadTable::sortByRow( int key1, int key2, int key3, SortingOrder order1, SortingOrder order2, SortingOrder order3, QStringList const * firstKey, bool copyLayout, bool headerRow, KSpreadPoint const & outputPoint ) { QRect r( selectionRect() ); Q_ASSERT( order1 == Increase || order1 == Decrease ); // It may not happen that entire columns are selected. Q_ASSERT( isColumnSelected() == FALSE ); // Are entire rows selected ? if ( isRowSelected() ) { 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. KSpreadCell * c = m_cells.firstCell(); for( ; c; c = c->nextCell() ) { int row = c->row(); int col = c->column(); // Is the cell in the selected columns ? if ( !c->isEmpty() && row >= r.top() && row <= r.bottom()) { if ( col > r.right() ) r.rRight() = col; if ( col < r.left() ) r.rLeft() = col; } } // Any cells to sort here ? if ( r.right() < r.left() ) return; } QRect target( outputPoint.pos.x(), outputPoint.pos.y(), r.width(), r.height() ); doc()->emitBeginOperation(); if ( !m_pDoc->undoBuffer()->isLocked() ) { KSpreadUndoSort *undo = new KSpreadUndoSort( m_pDoc, this, target ); m_pDoc->undoBuffer()->appendUndo( 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, copyLayout ); } } } // 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... KSpreadCell * cell; KSpreadCell * cell1; KSpreadCell * cell2; KSpreadCell * bestCell; int status = 0; for ( int d = target.left(); d <= target.right(); ++d ) { cell1 = cellAt( d, key1 ); if ( cell1->isObscured() && cell1->isObscuringForced() ) { int moveX = cell1->obscuringCellsColumn(); cell = cellAt( moveX, key1 ); cell1 = cellAt( moveX + cell->extraXCells() + 1, moveX ); d = moveX + 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; KSpreadCell * cell22 = cellAt( d, key2 ); KSpreadCell * bestCell2 = cellAt( x, key2 ); if ( cell22->isEmpty() ) { /* No need to swap */ continue; } else if ( cell22->isObscured() && cell22->isObscuringForced() ) { /* 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; KSpreadCell * cell23 = cellAt( d, key3 ); KSpreadCell * bestCell3 = cellAt( x, key3 ); if ( cell23->isEmpty() ) { /* No need to swap */ continue; } else if ( cell23->isObscured() && cell23->isObscuringForced() ) { /* 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; KSpreadCell * cell22 = cellAt( d, key2 ); KSpreadCell * bestCell2 = cellAt( x, key2 ); - // kdDebug() << "Equality! Testing now " << d << ", " << x << " in " << key2 << endl; - 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; } - // kdDebug() << "Not empty: " << cell22->text() << " <=> " << bestCell2->text() << " O: " << order2 << endl; 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 - // kdDebug() << "Right order" << endl; continue; } else { // they are equal, check 3rd key - // kdDebug() << "equals "<< endl; if (key3 == 0) continue; KSpreadCell * cell23 = cellAt( d, key3 ); KSpreadCell * 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(); - kdDebug() << "Header Row: " << headerRow << endl; if (headerRow) ++top; for( int y = target.bottom(); y >= top; --y ) { if ( y != key1 && y != key2 && y != key3 ) swapCells( d, y, bestX, y, copyLayout ); } if (key3 > 0) swapCells( d, key3, bestX, key3, copyLayout ); if (key2 > 0) swapCells( d, key2, bestX, key2, copyLayout ); swapCells( d, key1, bestX, key1, copyLayout ); } } // for (d = ...; ...; ++d) doc()->emitEndOperation(); } void KSpreadTable::sortByColumn( int key1, int key2, int key3, SortingOrder order1, SortingOrder order2, SortingOrder order3, QStringList const * firstKey, bool copyLayout, bool headerRow, KSpreadPoint const & outputPoint ) { QRect r( m_rctSelection ); Q_ASSERT( order1 == Increase || order1 == Decrease ); // It may not happen that entire rows are selected. Q_ASSERT( isRowSelected() == FALSE ); // Are entire columns selected ? if ( isColumnSelected() ) { 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. KSpreadCell * c = m_cells.firstCell(); for( ; c; c = c->nextCell() ) { int row = c->row(); int col = c->column(); // Is the cell in the selected columns ? if ( !c->isEmpty() && col >= r.left() && col <= r.right()) { if ( row > r.bottom() ) r.rBottom() = row; if ( row < r.top() ) r.rTop() = row; } } // Any cells to sort here ? if ( r.bottom() < r.top() ) return; } QRect target( outputPoint.pos.x(), outputPoint.pos.y(), r.width(), r.height() ); - // kdDebug() << "Sel rect: " << r.left() << "; " << r.top() << " - " << r.right() << ", " << r.bottom() << endl; - // kdDebug() << "Target rect: " << target.left() << "; " << target.top() << " - " << target.right() << ", " << target.bottom() << endl; - if ( !m_pDoc->undoBuffer()->isLocked() ) { KSpreadUndoSort *undo = new KSpreadUndoSort( m_pDoc, this, target ); m_pDoc->undoBuffer()->appendUndo( 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 - kdDebug() << "x,y: " << x << ", " << y << ", TargetLeft " << targetLeft << ", TargetTop: " << targetTop << endl; copyCells( sourceLeft + x, sourceTop + y, targetLeft + x, targetTop + y, copyLayout ); } } } // 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) :-) KSpreadCell * cell; KSpreadCell * cell1; KSpreadCell * cell2; KSpreadCell * bestCell; int status = 0; - // kdDebug() << "Key1: " << key1 << ", Key2 " << key2 << ", Key3 " << key3 - // << ", Rect TL: " << target.top() << ", " << target.left() << endl; - 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->isObscuringForced() ) { int moveY = cell1->obscuringCellsRow(); cell = cellAt( key1, moveY ); cell1 = cellAt( key1, moveY+cell->extraYCells() + 1 ); d = moveY + 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->isObscuringForced() ) { /* 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; - kdDebug() << "Switch: i1 < i2" << endl; continue; } if ( i1 == i2 ) { // check 2nd key if (key2 <= 0) continue; KSpreadCell * cell22 = cellAt( key2, d ); KSpreadCell * bestCell2 = cellAt( key2, y ); if ( cell22->isEmpty() ) { /* No need to swap */ continue; } else if ( cell22->isObscured() && cell22->isObscuringForced() ) { /* 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; KSpreadCell * cell23 = cellAt( key3, d ); KSpreadCell * 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 { - // kdDebug() << "Cell2 equals bestCell!" << endl; // *cell2 equals *bestCell // check 2nd key if (key2 == 0) continue; KSpreadCell * cell22 = cellAt( key2, d ); KSpreadCell * bestCell2 = cellAt( key2, y ); if ( cell22->isEmpty() ) { /* No need to swap */ continue; } else if ( cell22->isObscured() && cell22->isObscuringForced() ) { /* 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; KSpreadCell * cell23 = cellAt( key3, d ); KSpreadCell * bestCell3 = cellAt( key3, y ); if ( cell23->isEmpty() ) { /* No need to swap */ continue; } else if ( cell23->isObscured() && cell23->isObscuringForced() ) { /* 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) ) { - // kdDebug() << "First cell greater if increase, lesser if decrease " << cell23->text() << ", " << bestCell3->text() - // << ", bestY: " << bestY << endl; bestCell = cell2; bestY = y; continue; } else { // they are really equal or already in the correct order // no swap necessary - // kdDebug() << "Key2 in correct order" << endl; continue; } } } } - // kdDebug() << "R: " << target.left() << ", " << target.right() << ", T: " << target.left() << ", " << target.right() << endl; // 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, copyLayout ); } if (key3 > 0) swapCells( key3, d, key3, bestY, copyLayout ); if (key2 > 0) swapCells( key2, d, key2, bestY, copyLayout ); swapCells( key1, d, key1, bestY, copyLayout ); } } // for (d = ...; ...; ++d) doc()->emitEndOperation(); } // from - to - copyLayout void KSpreadTable::copyCells( int x1, int y1, int x2, int y2, bool cpLayout ) { KSpreadCell * sourceCell = cellAt( x1, y1 ); KSpreadCell * targetCell = cellAt( x2, y2 ); - // kdDebug() << "Source: (" << x1 << ", " << y1 << ") " << sourceCell->text() << endl; - if ( sourceCell->isDefault() ) { // if the source and target is default there is nothing to copy if ( targetCell->isDefault() ) { return; } else // overwrite target with defaultcell { targetCell = new KSpreadCell( this, x2, y2 ); insertCell( targetCell ); return; } } if ( targetCell->isDefault() ) { targetCell = new KSpreadCell( this, x2, y2 ); insertCell( targetCell ); } // TODO: check if this enough targetCell->copyContent( sourceCell ); - // kdDebug() << "Target: (" << x2 << ", " << y2 << ") " << sourceCell->text() << endl; /* if ( !sourceCell->isFormula() ) { targetCell->copyContent( sourceCell ); } else { targetCell->setCellText( targetCell->decodeFormula( sourceCell->encodeFormula() ) ); targetCell->setCalcDirtyFlag(); targetCell->calc(false); } */ if (cpLayout) { targetCell->copyLayout( sourceCell ); /* targetCell->setAlign( sourceCell->align( x1, y1 ) ); targetCell->setAlignY( sourceCell->alignY( x1, y1 ) ); targetCell->setTextFont( sourceCell->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->setPrefix( sourceCell->prefix( x1, y1 ) ); targetCell->setPostfix( sourceCell->postfix( x1, y1 ) ); targetCell->setFloatFormat( sourceCell->floatFormat( x1, y1 ) ); targetCell->setFloatColor( sourceCell->floatColor( x1, y1 ) ); targetCell->setFactor( sourceCell->factor( 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 KSpreadTable::swapCells( int x1, int y1, int x2, int y2, bool cpLayout ) { KSpreadCell * ref1 = cellAt( x1, y1 ); KSpreadCell * 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 table. Swapping // the layout while sorting is not what you would expect // as a user. if (!ref1->isFormula() && !ref2->isFormula()) { KSpreadCell *tmp = new KSpreadCell( this, -1, -1 ); tmp->copyContent( ref1 ); ref1->copyContent( ref2 ); ref2->copyContent( tmp ); delete tmp; } else if ( ref1->isFormula() && ref2->isFormula() ) { QString 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() ) { QString d = ref1->encodeFormula(); ref1->setCellText(ref2->text(), true); ref1->setAction(ref2->action()); ref2->setCellText(ref2->decodeFormula(d), true); ref2->setCalcDirtyFlag(); ref2->calc(false); } else if (!ref1->isFormula() && ref2->isFormula() ) { QString d = ref2->encodeFormula(); ref2->setCellText(ref1->text(), true); ref2->setAction(ref1->action()); ref1->setCellText(ref1->decodeFormula(d), true); ref1->setCalcDirtyFlag(); ref1->calc(false); } if (cpLayout) { KSpreadLayout::Align a = ref1->align( ref1->column(), ref1->row() ); ref1->setAlign( ref2->align( ref2->column(), ref2->row() ) ); ref2->setAlign(a); KSpreadLayout::AlignY ay = ref1->alignY( ref1->column(), ref1->row() ); ref1->setAlignY( ref2->alignY( ref2->column(), ref2->row() ) ); ref2->setAlignY(ay); QFont textFont = ref1->textFont( ref1->column(), ref1->row() ); ref1->setTextFont( ref2->textFont( ref2->column(), ref2->row() ) ); ref2->setTextFont(textFont); QColor textColor = ref1->textColor( ref1->column(), ref1->row() ); ref1->setTextColor( ref2->textColor( ref2->column(), ref2->row() ) ); ref2->setTextColor(textColor); QColor bgColor = ref1->bgColor( ref1->column(), ref1->row() ); ref1->setBgColor( ref2->bgColor( ref2->column(), ref2->row() ) ); ref2->setBgColor(bgColor); QPen lbp = ref1->leftBorderPen( ref1->column(), ref1->row() ); ref1->setLeftBorderPen( ref2->leftBorderPen( ref2->column(), ref2->row() ) ); ref2->setLeftBorderPen(lbp); QPen tbp = ref1->topBorderPen( ref1->column(), ref1->row() ); ref1->setTopBorderPen( ref2->topBorderPen( ref2->column(), ref2->row() ) ); ref2->setTopBorderPen(tbp); QPen bbp = ref1->bottomBorderPen( ref1->column(), ref1->row() ); ref1->setBottomBorderPen( ref2->bottomBorderPen( ref2->column(), ref2->row() ) ); ref2->setBottomBorderPen(bbp); QPen rbp = ref1->rightBorderPen( ref1->column(), ref1->row() ); ref1->setRightBorderPen( ref2->rightBorderPen( ref2->column(), ref2->row() ) ); ref2->setRightBorderPen(rbp); QPen fdp = ref1->fallDiagonalPen( ref1->column(), ref1->row() ); ref1->setFallDiagonalPen( ref2->fallDiagonalPen( ref2->column(), ref2->row() ) ); ref2->setFallDiagonalPen(fdp); QPen udp = ref1->goUpDiagonalPen( ref1->column(), ref1->row() ); ref1->setGoUpDiagonalPen( ref2->goUpDiagonalPen( ref2->column(), ref2->row() ) ); ref2->setGoUpDiagonalPen(udp); QBrush bgBrush = ref1->backGroundBrush( ref1->column(), ref1->row() ); ref1->setBackGroundBrush( ref2->backGroundBrush( ref2->column(), ref2->row() ) ); ref2->setBackGroundBrush(bgBrush); int pre = ref1->precision( ref1->column(), ref1->row() ); ref1->setPrecision( ref2->precision( ref2->column(), ref2->row() ) ); ref2->setPrecision(pre); QString prefix = ref1->prefix( ref1->column(), ref1->row() ); ref1->setPrefix( ref2->prefix( ref2->column(), ref2->row() ) ); ref2->setPrefix(prefix); QString postfix = ref1->postfix( ref1->column(), ref1->row() ); ref1->setPostfix( ref2->postfix( ref2->column(), ref2->row() ) ); ref2->setPostfix(postfix); KSpreadLayout::FloatFormat f = ref1->floatFormat( ref1->column(), ref1->row() ); ref1->setFloatFormat( ref2->floatFormat( ref2->column(), ref2->row() ) ); ref2->setFloatFormat(f); KSpreadLayout::FloatColor c = ref1->floatColor( ref1->column(), ref1->row() ); ref1->setFloatColor( ref2->floatColor( ref2->column(), ref2->row() ) ); ref2->setFloatColor(c); double fact = ref1->factor( ref1->column(), ref1->row() ); ref1->setFactor( ref2->factor( ref2->column(), ref2->row() ) ); ref2->setFactor(fact); bool multi = ref1->multiRow( ref1->column(), ref1->row() ); ref1->setMultiRow( ref2->multiRow( ref2->column(), ref2->row() ) ); ref2->setMultiRow(multi); bool vert = ref1->verticalText( ref1->column(), ref1->row() ); ref1->setVerticalText( ref2->verticalText( ref2->column(), ref2->row() ) ); ref2->setVerticalText(vert); KSpreadCell::Style style = ref1->style(); ref1->setStyle( ref2->style() ); ref2->setStyle(style); bool print = ref1->getDontprintText( ref1->column(), ref1->row() ); ref1->setDontPrintText( ref2->getDontprintText( ref2->column(), ref2->row() ) ); ref2->setDontPrintText(print); int ind = ref1->getIndent( ref1->column(), ref1->row() ); ref1->setIndent( ref2->getIndent( ref2->column(), ref2->row() ) ); ref2->setIndent(ind); QValueList conditionList = ref1->GetConditionList(); ref1->SetConditionList(ref2->GetConditionList()); ref2->SetConditionList(conditionList); QString com = ref1->comment( ref1->column(), ref1->row() ); ref1->setComment( ref2->comment( ref2->column(), ref2->row() ) ); ref2->setComment(com); int angle = ref1->getAngle( ref1->column(), ref1->row() ); ref1->setAngle( ref2->getAngle( ref2->column(), ref2->row() ) ); ref2->setAngle(angle); KSpreadLayout::FormatType form = ref1->getFormatType( ref1->column(), ref1->row() ); ref1->setFormatType( ref2->getFormatType( ref2->column(), ref2->row() ) ); ref2->setFormatType(form); } } void KSpreadTable::refreshPreference() { if(getAutoCalc()) recalc(); emit sig_updateHBorder( this ); emit sig_updateView( this ); } bool KSpreadTable::areaIsEmpty() { bool selected = ( m_rctSelection.left() != 0 ); // Complete rows selected ? if ( isRowSelected() ) { KSpreadCell* c = m_cells.firstCell(); int row; for( ;c; c = c->nextCell() ) { row = c->row(); if ( m_rctSelection.top() <= row && m_rctSelection.bottom() >= row &&!c->isObscuringForced() && !c->text().isEmpty()) { return false; } } } // Complete columns selected ? else if ( isColumnSelected() ) { KSpreadCell* c = m_cells.firstCell(); int col; for( ;c; c = c->nextCell() ) { col = c->column(); if ( m_rctSelection.left() <= col && m_rctSelection.right() >= col &&!c->isObscuringForced() && !c->text().isEmpty()) { return false; } } } else { KSpreadCell *cell; QRect r( m_rctSelection ); if ( !selected ) r.setCoords( marker().x(), marker().y(), marker().x(), marker().y() ); for ( int x = r.left(); x <= r.right(); x++ ) for ( int y = r.top(); y <= r.bottom(); y++ ) { cell = cellAt( x, y ); if(!cell->isObscuringForced() && !cell->text().isEmpty()) { return false; } } } return true; } struct SetSelectionMultiRowWorker : public KSpreadTable::CellWorker { bool enable; SetSelectionMultiRowWorker( bool _enable ) : KSpreadTable::CellWorker( ), enable( _enable ) { } class KSpreadUndoAction* createUndoAction( KSpreadDoc* doc, KSpreadTable* table, QRect& r ) { QString title=i18n("Multirow"); return new KSpreadUndoCellLayout( doc, table, r, title ); } bool testCondition( KSpreadCell* cell ) { return ( !cell->isObscuringForced() ); } void doWork( KSpreadCell* cell, bool, int, int ) { cell->setDisplayDirtyFlag(); cell->setMultiRow( enable ); cell->setVerticalText( false ); cell->setAngle( 0 ); cell->clearDisplayDirtyFlag(); } }; void KSpreadTable::setSelectionMultiRow( const QPoint &_marker, bool enable ) { SetSelectionMultiRowWorker w( enable ); workOnCells( _marker, w ); } struct SetSelectionAlignWorker : public KSpreadTable::CellWorkerTypeA { KSpreadLayout::Align _align; SetSelectionAlignWorker( KSpreadLayout::Align align ) : _align( align ) { } QString getUndoTitle() { return i18n("Change horizontal alignment"); } bool testCondition( RowLayout* rw ) { return ( rw->hasProperty( KSpreadCell::PAlign ) ); } void doWork( RowLayout* rw ) { rw->setAlign( _align ); } void doWork( ColumnLayout* cl ) { cl->setAlign( _align ); } void prepareCell( KSpreadCell* c ) { c->clearProperty( KSpreadCell::PAlign ); c->clearNoFallBackProperties( KSpreadCell::PAlign ); } bool testCondition( KSpreadCell* cell ) { return ( !cell->isObscuringForced() ); } void doWork( KSpreadCell* cell, bool cellRegion, int, int ) { if ( cellRegion ) cell->setDisplayDirtyFlag(); cell->setAlign( _align ); if ( cellRegion ) cell->clearDisplayDirtyFlag(); } }; void KSpreadTable::setSelectionAlign( const QPoint &_marker, KSpreadLayout::Align _align ) { SetSelectionAlignWorker w( _align ); workOnCells( _marker, w ); } struct SetSelectionAlignYWorker : public KSpreadTable::CellWorkerTypeA { KSpreadLayout::AlignY _alignY; SetSelectionAlignYWorker( KSpreadLayout::AlignY alignY ) : _alignY( alignY ) { } QString getUndoTitle() { return i18n("Change vertical alignment"); } bool testCondition( RowLayout* rw ) { return ( rw->hasProperty( KSpreadCell::PAlignY ) ); } void doWork( RowLayout* rw ) { rw->setAlignY( _alignY ); } void doWork( ColumnLayout* cl ) { cl->setAlignY( _alignY ); } void prepareCell( KSpreadCell* c ) { c->clearProperty( KSpreadCell::PAlignY ); c->clearNoFallBackProperties( KSpreadCell::PAlignY ); } bool testCondition( KSpreadCell* cell ) { return ( !cell->isObscuringForced() ); } void doWork( KSpreadCell* cell, bool cellRegion, int, int ) { if ( cellRegion ) cell->setDisplayDirtyFlag(); cell->setAlignY( _alignY ); if ( cellRegion ) cell->clearDisplayDirtyFlag(); } }; void KSpreadTable::setSelectionAlignY( const QPoint &_marker, KSpreadLayout::AlignY _alignY ) { SetSelectionAlignYWorker w( _alignY ); workOnCells( _marker, w ); } struct SetSelectionPrecisionWorker : public KSpreadTable::CellWorker { int _delta; SetSelectionPrecisionWorker( int delta ) : KSpreadTable::CellWorker( ), _delta( delta ) { } class KSpreadUndoAction* createUndoAction( KSpreadDoc* doc, KSpreadTable* table, QRect& r ) { QString title=i18n("Change precision"); return new KSpreadUndoCellLayout( doc, table, r, title ); } bool testCondition( KSpreadCell* cell ) { return ( !cell->isObscuringForced() ); } void doWork( KSpreadCell* cell, bool, int, int ) { cell->setDisplayDirtyFlag(); if ( _delta == 1 ) cell->incPrecision(); else cell->decPrecision(); cell->clearDisplayDirtyFlag(); } }; void KSpreadTable::setSelectionPrecision( const QPoint &_marker, int _delta ) { SetSelectionPrecisionWorker w( _delta ); workOnCells( _marker, w ); } struct SetSelectionMoneyFormatWorker : public KSpreadTable::CellWorkerTypeA { bool b; KSpreadDoc *m_pDoc; SetSelectionMoneyFormatWorker( bool _b,KSpreadDoc* _doc ) : b( _b ), m_pDoc(_doc) { } QString getUndoTitle() { return i18n("Format money"); } bool testCondition( RowLayout* rw ) { return ( rw->hasProperty( KSpreadCell::PFormatType ) || rw->hasProperty( KSpreadCell::PPrecision ) || rw->hasProperty( KSpreadCell::PFactor ) ); } void doWork( RowLayout* rw ) { rw->setFormatType( b ? KSpreadCell::Money : KSpreadCell::Number ); rw->setFactor( 1.0 ); rw->setPrecision( b ? m_pDoc->locale()->fracDigits() : 0 ); } void doWork( ColumnLayout* cl ) { cl->setFormatType( b ? KSpreadCell::Money : KSpreadCell::Number ); cl->setFactor( 1.0 ); cl->setPrecision( b ? m_pDoc->locale()->fracDigits() : 0 ); } void prepareCell( KSpreadCell* c ) { c->clearProperty( KSpreadCell::PFactor ); c->clearNoFallBackProperties( KSpreadCell::PFactor ); c->clearProperty( KSpreadCell::PPrecision ); c->clearNoFallBackProperties( KSpreadCell::PPrecision ); c->clearProperty( KSpreadCell::PFormatType ); c->clearNoFallBackProperties( KSpreadCell::PFormatType ); } bool testCondition( KSpreadCell* cell ) { return ( !cell->isObscuringForced() ); } void doWork( KSpreadCell* cell, bool cellRegion, int, int ) { if ( cellRegion ) cell->setDisplayDirtyFlag(); cell->setFormatType( b ? KSpreadCell::Money : KSpreadCell::Number ); cell->setFactor( 1.0 ); cell->setPrecision( b ? m_pDoc->locale()->fracDigits() : 0 ); if ( cellRegion ) cell->clearDisplayDirtyFlag(); } }; void KSpreadTable::setSelectionMoneyFormat( const QPoint &_marker, bool b ) { SetSelectionMoneyFormatWorker w( b,doc() ); workOnCells( _marker, w ); } struct IncreaseIndentWorker : public KSpreadTable::CellWorkerTypeA { int tmpIndent, valIndent; IncreaseIndentWorker( int _tmpIndent, int _valIndent ) : tmpIndent( _tmpIndent ), valIndent( _valIndent ) { } QString getUndoTitle() { return i18n("Increase indent"); } bool testCondition( RowLayout* rw ) { return ( rw->hasProperty( KSpreadCell::PIndent ) ); } void doWork( RowLayout* rw ) { rw->setIndent( tmpIndent+valIndent ); rw->setAlign( KSpreadCell::Left ); } void doWork( ColumnLayout* cl ) { cl->setIndent( tmpIndent+valIndent ); cl->setAlign( KSpreadCell::Left ); } void prepareCell( KSpreadCell* c ) { c->clearProperty( KSpreadCell::PIndent ); c->clearNoFallBackProperties( KSpreadCell::PIndent ); c->clearProperty( KSpreadCell::PAlign ); c->clearNoFallBackProperties( KSpreadCell::PAlign ); } bool testCondition( KSpreadCell* cell ) { return ( !cell->isObscuringForced() ); } void doWork( KSpreadCell* cell, bool cellRegion, int x, int y ) { if ( cellRegion ) { if(cell->align(x,y)!=KSpreadCell::Left) { cell->setAlign(KSpreadCell::Left); cell->setIndent(0); } cell->setDisplayDirtyFlag(); cell->setIndent( /* ### ??? --> */ cell->getIndent(x,y) /* <-- */ +valIndent ); cell->clearDisplayDirtyFlag(); } else { cell->setIndent( tmpIndent+valIndent); cell->setAlign( KSpreadCell::Left); } } }; void KSpreadTable::increaseIndent( const QPoint &_marker ) { int valIndent = doc()->getIndentValue(); KSpreadCell* c = cellAt( _marker.x(), _marker.y() ); int tmpIndent = c->getIndent( _marker.x(), _marker.y() ); IncreaseIndentWorker w( tmpIndent, valIndent ); workOnCells( _marker, w ); } struct DecreaseIndentWorker : public KSpreadTable::CellWorkerTypeA { int tmpIndent, valIndent; DecreaseIndentWorker( int _tmpIndent, int _valIndent ) : tmpIndent( _tmpIndent ), valIndent( _valIndent ) { } QString getUndoTitle() { return i18n("Decrease indent"); } bool testCondition( RowLayout* rw ) { return ( rw->hasProperty( KSpreadCell::PIndent ) ); } void doWork( RowLayout* rw ) { rw->setIndent( QMAX(0, tmpIndent - valIndent) ); } void doWork( ColumnLayout* cl ) { cl->setIndent( QMAX(0, tmpIndent - valIndent) ); } void prepareCell( KSpreadCell* c ) { c->clearProperty( KSpreadCell::PIndent ); c->clearNoFallBackProperties( KSpreadCell::PIndent ); } bool testCondition( KSpreadCell* cell ) { return ( !cell->isObscuringForced() ); } void doWork( KSpreadCell* cell, bool cellRegion, int x, int y ) { if ( cellRegion ) { cell->setDisplayDirtyFlag(); cell->setIndent( QMAX(0, cell->getIndent(x,y) - valIndent) ); cell->clearDisplayDirtyFlag(); } else { cell->setIndent( QMAX(0, tmpIndent - valIndent) ); } } }; void KSpreadTable::decreaseIndent( const QPoint &_marker ) { int valIndent = doc()->getIndentValue(); KSpreadCell* c = cellAt( _marker.x(), _marker.y() ); int tmpIndent = c->getIndent( _marker.x(), _marker.y() ); DecreaseIndentWorker w( tmpIndent, valIndent ); workOnCells( _marker, w ); } int KSpreadTable::adjustColumn( const QPoint& _marker, int _col ) { int long_max=0; if( _col == -1 ) { if ( isColumnSelected() ) { KSpreadCell* c = m_cells.firstCell(); int col; for( ;c; c = c->nextCell() ) { col = c->column(); if ( m_rctSelection.left() <= col && m_rctSelection.right() >= col ) { if( !c->isEmpty() && !c->isObscured()) { c->conditionAlign(painter(),col,c->row()); if( c->textWidth() > long_max ) { int indent=0; int a = c->align(c->column(),c->row()); if ( a == KSpreadCell::Undefined ) { if ( c->isNumeric() || c->isDate() || c->isTime()) a = KSpreadCell::Right; else a = KSpreadCell::Left; } if( a==KSpreadCell::Left) indent=c->getIndent(c->column(),c->row() ); long_max = indent+c->textWidth() + c->leftBorderWidth(c->column(),c->row() ) + c->rightBorderWidth(c->column(),c->row() ); } } } } } } else { QRect r( m_rctSelection ); if( r.left() == 0 || r.right() == 0 || r.top() == 0 || r.bottom() == 0 ) { r.setCoords( _marker.x(), _marker.y(), _marker.x(), _marker.y() ); } if ( isColumnSelected() ) { KSpreadCell* c = m_cells.firstCell(); int col; for( ;c; c = c->nextCell() ) { col = c->column(); if ( m_rctSelection.left() <= col && m_rctSelection.right() >= col ) { if( !c->isEmpty() && !c->isObscured()) { c->conditionAlign(painter(),col,c->row()); if( c->textWidth() > long_max ) { int indent=0; int a = c->align(c->column(),c->row()); if ( a == KSpreadCell::Undefined ) { if ( c->isNumeric() || c->isDate() || c->isTime()) a = KSpreadCell::Right; else a = KSpreadCell::Left; } if( a==KSpreadCell::Left) indent=c->getIndent(c->column(),c->row() ); long_max = indent+c->textWidth() + c->leftBorderWidth(c->column(),c->row() ) + c->rightBorderWidth(c->column(),c->row() ); } } } } } else { int x = _col; KSpreadCell *cell; for ( int y = r.top(); y <= r.bottom(); y++ ) { cell = cellAt( x, y ); if( cell != m_pDefaultCell && !cell->isEmpty() && !cell->isObscured()) { cell->conditionAlign(painter(),x,y); if(cell->textWidth() > long_max ) { int indent=0; int a = cell->align(x,y); if ( a == KSpreadCell::Undefined ) { if ( cell->isNumeric() || cell->isDate() || cell->isTime()) a = KSpreadCell::Right; else a = KSpreadCell::Left; } if( a==KSpreadCell::Left) indent=cell->getIndent(x,y ); long_max = indent+cell->textWidth() + cell->leftBorderWidth(cell->column(),cell->row() ) + cell->rightBorderWidth(cell->column(),cell->row() ); } } } } } //add 4 because long_max is the long of the text //but column has borders if( long_max == 0 ) return -1; else return ( long_max + 4 ); } int KSpreadTable::adjustRow( const QPoint &_marker, int _row ) { int long_max=0; if( _row == -1 ) //No special row is defined, so use selected rows { if ( isRowSelected() ) { KSpreadCell* c = m_cells.firstCell(); int row; for( ; c; c = c->nextCell() ) { row = c->row(); if ( m_rctSelection.top() <= row && m_rctSelection.bottom() >= row ) { if( !c->isEmpty() && !c->isObscured() ) { c->conditionAlign( painter(), c->column(), row ); if( c->textHeight() > long_max ) long_max = c->textHeight() + c->topBorderWidth( c->column(), c->row() ) + c->bottomBorderWidth( c->column(), c->row() ); } } } } } else { QRect r( m_rctSelection ); if( r.left() == 0 || r.right() == 0 || r.top() == 0 || r.bottom() == 0 ) { r.setCoords( _marker.x(), _marker.y(), _marker.x(), _marker.y() ); } if ( isRowSelected() ) { KSpreadCell* c = m_cells.firstCell(); int row; for( ; c; c = c->nextCell() ) { row = c->row(); if ( m_rctSelection.top() <= row && m_rctSelection.bottom() >= row ) { if( !c->isEmpty() && !c->isObscured() ) { c->conditionAlign( painter(), c->column(), row ); if( c->textHeight() > long_max ) long_max = c->textHeight() + c->topBorderWidth( c->column(), c->row() ) + c->bottomBorderWidth( c->column(), c->row() ); } } } } else { int y = _row; KSpreadCell *cell; for ( int x = r.left(); x <= r.right(); x++ ) { cell = cellAt( x, y ); if( cell != m_pDefaultCell && !cell->isEmpty() && !cell->isObscured()) { cell->conditionAlign( painter(), x, y ); if( cell->textHeight() > long_max ) long_max = cell->textHeight() + cell->topBorderWidth( cell->column(), cell->row() ) + cell->bottomBorderWidth( cell->column(), cell->row() ); } } } } //add 4 because long_max is the long of the text //but row has borders if( long_max == 0 ) return -1; else return ( long_max + 4 ); } struct ClearTextSelectionWorker : public KSpreadTable::CellWorker { ClearTextSelectionWorker( ) : KSpreadTable::CellWorker( ) { } class KSpreadUndoAction* createUndoAction( KSpreadDoc* doc, KSpreadTable* table, QRect& r ) { return new KSpreadUndoChangeAreaTextCell( doc, table, r ); } bool testCondition( KSpreadCell* cell ) { return ( !cell->isObscured() ); } void doWork( KSpreadCell* cell, bool, int, int ) { cell->setCellText( "" ); } }; bool KSpreadTable::isRowSelected (){ //selectionRect() contains a QRect with the current selection if ( isRowSelected( selectionRect() ) ){ return TRUE; } return FALSE; } bool KSpreadTable::isRowSelected (const QRect &_rect){ //If a row is selected, then it must have a selection area from left 1 to right KS_colMax if ( (_rect.left() == 1) && (_rect.right() == KS_colMax) ){ return TRUE; } return FALSE; } bool KSpreadTable::isColumnSelected (){ //selectionRect() contains a QRect with the current selection if ( isColumnSelected( selectionRect() ) ){ return TRUE; } return FALSE; } bool KSpreadTable::isColumnSelected (const QRect &_rect){ //If a column is selected, then it must have a selection area from top 1 to bottom KS_rowMax if ( (_rect.top() == 1) && (_rect.bottom() == KS_rowMax) ){ return TRUE; } return FALSE; } void KSpreadTable::clearTextSelection( const QPoint &_marker ) { if(areaIsEmpty()) return; ClearTextSelectionWorker w; workOnCells( _marker, w ); } struct ClearValiditySelectionWorker : public KSpreadTable::CellWorker { ClearValiditySelectionWorker( ) : KSpreadTable::CellWorker( ) { } class KSpreadUndoAction* createUndoAction( KSpreadDoc* doc, KSpreadTable* table, QRect& r ) { return new KSpreadUndoConditional( doc, table, r ); } bool testCondition( KSpreadCell* cell ) { return ( !cell->isObscured() ); } void doWork( KSpreadCell* cell, bool, int, int ) { cell->removeValidity(); } }; void KSpreadTable::clearValiditySelection( const QPoint &_marker ) { if(areaIsEmpty()) return; ClearValiditySelectionWorker w; workOnCells( _marker, w ); } struct ClearConditionalSelectionWorker : public KSpreadTable::CellWorker { ClearConditionalSelectionWorker( ) : KSpreadTable::CellWorker( ) { } class KSpreadUndoAction* createUndoAction( KSpreadDoc* doc, KSpreadTable* table, QRect& r ) { return new KSpreadUndoConditional( doc, table, r ); } bool testCondition( KSpreadCell* cell ) { return ( !cell->isObscured() ); } void doWork( KSpreadCell* cell, bool, int, int ) { QValueList emptyList; cell->SetConditionList(emptyList); } }; void KSpreadTable::clearConditionalSelection( const QPoint &_marker ) { if(areaIsEmpty()) return; ClearConditionalSelectionWorker w; workOnCells( _marker, w ); } struct DefaultSelectionWorker : public KSpreadTable::CellWorker { DefaultSelectionWorker( ) : KSpreadTable::CellWorker( true, false, true ) { } class KSpreadUndoAction* createUndoAction( KSpreadDoc* doc, KSpreadTable* table, QRect& r ) { QString title=i18n("Default parameters"); return new KSpreadUndoCellLayout( doc, table, r, title ); } bool testCondition( KSpreadCell* ) { return true; } void doWork( KSpreadCell* cell, bool, int, int ) { cell->defaultStyle(); } }; void KSpreadTable::defaultSelection( const QPoint &_marker ) { DefaultSelectionWorker w; SelectionType st = workOnCells( _marker, w ); switch ( st ) { case CompleteRows: RowLayout *rw; for ( int i=m_rctSelection.top(); i<=m_rctSelection.bottom(); i++ ) { rw = nonDefaultRowLayout( i ); rw->defaultStyleLayout(); } emit sig_updateView( this, m_rctSelection ); return; case CompleteColumns: ColumnLayout *cl; for ( int i=m_rctSelection.left(); i<=m_rctSelection.right(); i++ ) { cl=nonDefaultColumnLayout( i ); cl->defaultStyleLayout(); } emit sig_updateView( this, m_rctSelection ); return; case CellRegion: QRect r( m_rctSelection ); if ( m_rctSelection.left() == 0 ) r.setCoords( _marker.x(), _marker.y(), _marker.x(), _marker.y() ); emit sig_updateView( this, r ); return; } } struct SetConditionalWorker : public KSpreadTable::CellWorker { QValueList conditionList; SetConditionalWorker( QValueList _tmp ) : KSpreadTable::CellWorker( ), conditionList( _tmp ) { } class KSpreadUndoAction* createUndoAction( KSpreadDoc* doc, KSpreadTable* table, QRect& r ) { return new KSpreadUndoConditional( doc, table, r ); } bool testCondition( KSpreadCell* ) { return true; } void doWork( KSpreadCell* cell, bool, int, int ) { if ( !cell->isObscured() ) // TODO: isObscuringForced()??? { cell->SetConditionList(conditionList); cell->setDisplayDirtyFlag(); } } }; void KSpreadTable::setConditional( const QRect & _marker, QValueList const & newConditions) { if ( !m_pDoc->undoBuffer()->isLocked() ) { KSpreadUndoConditional * undo = new KSpreadUndoConditional( m_pDoc, this, _marker ); m_pDoc->undoBuffer()->appendUndo( undo ); } int l = _marker.left(); int r = _marker.right(); int t = _marker.top(); int b = _marker.bottom(); KSpreadCell * cell; for (int x = l; x <= r; ++x) { for (int y = t; y <= b; ++y) { cell = cellAt( x, y ); if ( cell->isDefault() ) { cell = new KSpreadCell( this, x, y ); insertCell( cell ); } if ( cell->isObscuringForced() ) continue; cell->SetConditionList(newConditions); cell->setDisplayDirtyFlag(); } } emit sig_updateView( this, _marker ); } struct SetValidityWorker : public KSpreadTable::CellWorker { KSpreadValidity tmp; SetValidityWorker( KSpreadValidity _tmp ) : KSpreadTable::CellWorker( ), tmp( _tmp ) { } class KSpreadUndoAction* createUndoAction( KSpreadDoc* doc, KSpreadTable* table, QRect& r ) { return new KSpreadUndoConditional( doc, table, r ); } bool testCondition( KSpreadCell* ) { return true; } void doWork( KSpreadCell* cell, bool, int, int ) { if ( !cell->isObscured() ) { cell->setDisplayDirtyFlag(); if ( tmp.m_allow==Allow_All ) cell->removeValidity(); else { KSpreadValidity *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_allow=tmp.m_allow; tmpValidity->timeMin=tmp.timeMin; tmpValidity->timeMax=tmp.timeMax; tmpValidity->dateMin=tmp.dateMin; tmpValidity->dateMax=tmp.dateMax; } cell->clearDisplayDirtyFlag(); } } }; void KSpreadTable::setValidity(const QPoint &_marker,KSpreadValidity tmp ) { SetValidityWorker w( tmp ); workOnCells( _marker, w ); } struct GetWordSpellingWorker : public KSpreadTable::CellWorker { QString& listWord; GetWordSpellingWorker( QString& _listWord ) : KSpreadTable::CellWorker( false, false, true ), listWord( _listWord ) { } class KSpreadUndoAction* createUndoAction( KSpreadDoc*, KSpreadTable*, QRect& ) { return 0L; } bool testCondition( KSpreadCell* ) { return true; } void doWork( KSpreadCell* c, bool cellRegion, int, int ) { if ( !c->isObscured() || cellRegion /* ### ??? */ ) { if ( !c->isFormula() && !c->isNumeric() && !c->valueString().isEmpty() && !c->isTime() && !c->isDate() && c->content() != KSpreadCell::VisualFormula && !c->text().isEmpty()) { listWord+=c->text()+'\n'; } } } }; QString KSpreadTable::getWordSpelling(const QPoint &_marker ) { QString listWord; GetWordSpellingWorker w( listWord ); workOnCells( _marker, w ); return listWord; } struct SetWordSpellingWorker : public KSpreadTable::CellWorker { QStringList& list; int pos; SetWordSpellingWorker( QStringList& _list ) : KSpreadTable::CellWorker( false, false, true ), list( _list ), pos( 0 ) { } class KSpreadUndoAction* createUndoAction( KSpreadDoc* doc, KSpreadTable* table, QRect& r ) { return new KSpreadUndoChangeAreaTextCell( doc, table, r ); } bool testCondition( KSpreadCell* ) { return true; } void doWork( KSpreadCell* c, bool cellRegion, int, int ) { if ( !c->isObscured() || cellRegion /* ### ??? */ ) { if ( !c->isFormula() && !c->isNumeric() && !c->valueString().isEmpty() && !c->isTime() && !c->isDate() && c->content() != KSpreadCell::VisualFormula && !c->text().isEmpty()) { c->setCellText( list[pos] ); pos++; } } } }; void KSpreadTable::setWordSpelling(const QPoint &_marker, const QString _listWord ) { QStringList list = QStringList::split ( '\n', _listWord ); SetWordSpellingWorker w( list ); workOnCells( _marker, w ); } QString KSpreadTable::copyAsText( const QPoint &_marker ) { // No selection ? => copy active cell if ( m_rctSelection.left() == 0 ) { KSpreadCell * cell = cellAt( _marker.x(), _marker.y() ); if( !cell->isDefault() ) return cell->strOutText(); return ""; } int x; int y; unsigned int max = 1; QString result; KSpreadCell *cell; for (y = m_rctSelection.top(); y <= m_rctSelection.bottom(); ++y) { for (x = m_rctSelection.left(); x <= m_rctSelection.right(); ++x) { cell = cellAt( x, y ); if( !cell->isDefault() ) { if ( cell->strOutText().length() > max ) max = cell->strOutText().length(); } } } ++max; for (y = m_rctSelection.top(); y <= m_rctSelection.bottom(); ++y) { for (x = m_rctSelection.left(); x <= m_rctSelection.right(); ++x) { cell = cellAt( x, y ); if( !cell->isDefault() ) { int l = max - cell->strOutText().length(); if (cell->align(x, y) == KSpreadLayout::Right || cell->defineAlignX() == KSpreadLayout::Right ) { for ( int i = 0; i < l; ++i ) result += " "; result += cell->strOutText(); } else if (cell->align(x, y) == KSpreadLayout::Left || cell->defineAlignX() == KSpreadLayout::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 += " "; } } result += "\n"; } return result; } void KSpreadTable::copySelection( const QPoint &_marker ) { QRect rct; // No selection ? => copy active cell if ( m_rctSelection.left() == 0 ) rct.setCoords( _marker.x(), _marker.y(), _marker.x(), _marker.y() ); else rct = selectionRect(); QDomDocument doc = saveCellRect( rct ); // Save to buffer QBuffer buffer; buffer.open( IO_WriteOnly ); QTextStream str( &buffer ); str.setEncoding( QTextStream::UnicodeUTF8 ); str << doc; buffer.close(); KSpreadTextDrag * kd = new KSpreadTextDrag( 0L ); kd->setPlain( copyAsText(_marker) ); kd->setKSpread( buffer.buffer() ); QApplication::clipboard()->setData( kd ); } void KSpreadTable::cutSelection( const QPoint &_marker ) { copySelection( _marker ); deleteSelection( _marker ); } void KSpreadTable::paste( const QPoint &_marker,bool makeUndo, PasteMode sp, Operation op,bool insert,int insertTo ) { QMimeSource* mime = QApplication::clipboard()->data(); if ( !mime ) return; QByteArray b; if ( mime->provides( KSpreadTextDrag::selectionMimeType() ) ) { b = mime->encodedData( KSpreadTextDrag::selectionMimeType() ); } else if( mime->provides( "text/plain" ) ) { // Note: QClipboard::text() seems to do a better job than encodedData( "text/plain" ) // In particular it handles charsets (in the mimetype). Copied from KPresenter ;-) QString _text = QApplication::clipboard()->text(); pasteTextPlain( _text, _marker); return; } else return; paste( b, _marker, makeUndo, sp, op, insert, insertTo ); } void KSpreadTable::pasteTextPlain( QString &_text, const QPoint &_marker) { // QString tmp; // tmp= QString::fromLocal8Bit(_mime->encodedData( "text/plain" )); if( _text.isEmpty() ) return; QString tmp = _text; int i; int mx = _marker.x(); int my = _marker.y(); 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; } KSpreadCell * cell = cellAt( mx, my ); if ( rows == 1 ) { if ( !m_pDoc->undoBuffer()->isLocked() ) { KSpreadUndoSetText * undo = new KSpreadUndoSetText( m_pDoc, this , cell->text(), mx, my, cell->formatType() ); m_pDoc->undoBuffer()->appendUndo( undo ); } } else { QRect rect(mx, my, mx, my + rows - 1); KSpreadUndoChangeAreaTextCell * undo = new KSpreadUndoChangeAreaTextCell( m_pDoc, this , rect ); m_pDoc->undoBuffer()->appendUndo( undo ); } i = 0; QString rowtext; while ( i < rows ) { int p = 0; p = tmp.find('\n'); if (p < 0) p = tmp.length(); rowtext = tmp.left(p); if ( cell->isDefault() ) { cell = new KSpreadCell( this, mx, my + i ); insertCell( cell ); } cell->setCellText( rowtext ); cell->updateChart(); // next cell ++i; cell = cellAt( 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 KSpreadTable::paste( const QByteArray& b, const QPoint &_marker, bool makeUndo, PasteMode sp, Operation op, bool insert, int insertTo ) { kdDebug(36001) << "Parsing " << b.size() << " bytes" << endl; QBuffer buffer( b ); buffer.open( IO_ReadOnly ); QDomDocument doc; doc.setContent( &buffer ); buffer.close(); // ##### TODO: Test for parsing errors int mx = _marker.x(); int my = _marker.y(); if ( (selectionRect().x() > 0) && (selectionRect().y() > 0) ) { if (selectionRect().x() < mx) mx = selectionRect().x(); if (selectionRect().y() < my) my = selectionRect().y(); } loadSelection( doc, mx - 1, my - 1, makeUndo, sp, op, insert, insertTo ); } bool KSpreadTable::loadSelection( const QDomDocument& doc, int _xshift, int _yshift, bool makeUndo, PasteMode sp, Operation op, bool insert, int insertTo ) { QDomElement e = doc.documentElement(); if (!isLoading() && makeUndo) loadSelectionUndo( doc, _xshift, _yshift, insert, insertTo ); int rowsInClpbrd = e.attribute( "rows" ).toInt(); int columnsInClpbrd = e.attribute( "columns" ).toInt(); // find size of rectangle that we want to paste to (either clipboard size or current selection) const int pasteWidth = ( selectionRect().left() != 0 && selectionRect().width() >= columnsInClpbrd && isRowSelected() == FALSE && e.namedItem( "rows" ).toElement().isNull() ) ? selectionRect().width() : columnsInClpbrd; const int pasteHeight = ( selectionRect().left() != 0 && selectionRect().height() >= rowsInClpbrd && isColumnSelected() == FALSE && e.namedItem( "columns" ).toElement().isNull() ) ? selectionRect().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; */ if ( !e.namedItem( "columns" ).toElement().isNull() ) { _yshift = 0; // Clear the existing columns for( int i = 1; i <= pasteWidth; ++i ) { if(!insert) { m_cells.clearColumn( _xshift + i ); m_columns.removeElement( _xshift + i ); } } // Insert column layouts QDomElement c = e.firstChild().toElement(); for( ; !c.isNull(); c = c.nextSibling().toElement() ) { if ( c.tagName() == "column" ) { ColumnLayout *cl = new ColumnLayout( this, 0 ); if ( cl->load( c, _xshift,sp ) ) insertColumnLayout( cl ); else delete cl; } } } if ( !e.namedItem( "rows" ).toElement().isNull() ) { _xshift = 0; // Clear the existing rows for( int i = 1; i <= pasteHeight; ++i ) { m_cells.clearRow( _yshift + i ); m_rows.removeElement( _yshift + i ); } // Insert row layouts QDomElement c = e.firstChild().toElement(); for( ; !c.isNull(); c = c.nextSibling().toElement() ) { if ( c.tagName() == "row" ) { RowLayout *cl = new RowLayout( this, 0 ); if ( cl->load( c, _yshift,sp ) ) insertRowLayout( cl ); else delete cl; } } } KSpreadCell* refreshCell = 0; KSpreadCell *cell; KSpreadCell *cellLoad; QDomElement c = e.firstChild().toElement(); for( ; !c.isNull(); c = c.nextSibling().toElement() ) { if ( c.tagName() == "cell" ) { int row = c.attribute( "row" ).toInt() + _yshift; int col = c.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; bool needInsert = FALSE; cell = cellAt( col + coff, row + roff ); if ( ( cell->isDefault() ) || ( op == OverWrite && sp == Normal ) ) //we will generate a new or replace the existing cell { // Don't use insertCell with the cell itself, it will be deleted! // Therefore we need a temporary cell cellLoad, where we first load the data and then use this // to replace the existing one. cellLoad = new KSpreadCell( this, col + coff, row + roff ); if ( !cellLoad->load( c, _xshift + coff, _yshift + roff, sp, op ) ) { delete cell; //is this correct? Do we really delete the existing cell on failure? delete cellLoad; //Failure->we don't replace->actively delete the temporary cell } else { insertCell( cellLoad ); } } else // we simply load into the existing one { if ( !cell->load( c, _xshift + coff, _yshift + roff, sp, op ) ) { delete cell; //is this correct? Do we really delete the existing cell on failure? } } cell = cellAt( col + coff, row + roff ); if( !refreshCell && cell->updateChart( false ) ) { refreshCell = cell; } } } } } //refresh chart after that you paste all cells if ( refreshCell ) refreshCell->updateChart(); m_pDoc->setModified( true ); if(!isLoading()) refreshMergedCell(); emit sig_updateView( this ); emit sig_updateHBorder( this ); emit sig_updateVBorder( this ); return true; } void KSpreadTable::loadSelectionUndo( const QDomDocument & doc,int _xshift, int _yshift,bool insert,int insertTo) { QDomElement e = doc.documentElement(); QDomElement c = e.firstChild().toElement(); int rowsInClpbrd = e.attribute( "rows" ).toInt(); int columnsInClpbrd = e.attribute( "columns" ).toInt(); // find rect that we paste to const int pasteWidth = ( selectionRect().left() != 0 && selectionRect().width() >= columnsInClpbrd && isRowSelected() == FALSE && e.namedItem( "rows" ).toElement().isNull() ) ? selectionRect().width() : columnsInClpbrd; const int pasteHeight = ( selectionRect().left() != 0 && selectionRect().height() >= rowsInClpbrd && isColumnSelected() == FALSE && e.namedItem( "columns" ).toElement().isNull() ) ? selectionRect().height() : rowsInClpbrd; QRect rect; if ( !e.namedItem( "columns" ).toElement().isNull() ) { if ( !m_pDoc->undoBuffer()->isLocked() ) { KSpreadUndoCellPaste *undo = new KSpreadUndoCellPaste( m_pDoc, this, pasteWidth, 0, _xshift,_yshift,rect,insert ); m_pDoc->undoBuffer()->appendUndo( undo ); } if(insert) insertColumn( _xshift+1,pasteWidth-1,false); return; } if ( !e.namedItem( "rows" ).toElement().isNull() ) { if ( !m_pDoc->undoBuffer()->isLocked() ) { KSpreadUndoCellPaste *undo = new KSpreadUndoCellPaste( m_pDoc, this, 0,pasteHeight, _xshift,_yshift,rect,insert ); m_pDoc->undoBuffer()->appendUndo( undo ); } if(insert) insertRow( _yshift+1,pasteHeight-1,false); return; } rect.setRect( _xshift+1, _yshift+1, pasteWidth, pasteHeight ); if(!c.isNull()) { if ( !m_pDoc->undoBuffer()->isLocked() ) { KSpreadUndoCellPaste *undo = new KSpreadUndoCellPaste( m_pDoc, this, 0,0,_xshift,_yshift,rect,insert,insertTo ); m_pDoc->undoBuffer()->appendUndo( undo ); } if(insert) { if(insertTo==-1) shiftRow(rect,false); else if(insertTo==1) shiftColumn(rect,false); } } } bool KSpreadTable::testAreaPasteInsert() { QMimeSource* mime = QApplication::clipboard()->data(); if ( !mime ) return false; QByteArray b; if ( mime->provides( "application/x-kspread-snippet" ) ) b = mime->encodedData( "application/x-kspread-snippet" ); else return false; QBuffer buffer( b ); buffer.open( IO_ReadOnly ); QDomDocument doc; doc.setContent( &buffer ); buffer.close(); QDomElement e = doc.documentElement(); if ( !e.namedItem( "columns" ).toElement().isNull() ) return false; if ( !e.namedItem( "rows" ).toElement().isNull() ) return false; QDomElement c = e.firstChild().toElement(); for( ; !c.isNull(); c = c.nextSibling().toElement() ) { if ( c.tagName() == "cell" ) return true; } return false; } void KSpreadTable::deleteCells( const QRect& rect ) { // A list of all cells we want to delete. QPtrStack cellStack; QRect tmpRect; bool extraCell=false; if(rect.width()==1 && rect.height()==1) { KSpreadCell *cell = nonDefaultCell( rect.x(), rect.y() ); if(cell->isForceExtraCells()) { extraCell=true; tmpRect=rect; } } else if(rect.contains(m_marker.x(),m_marker.y()) &&m_rctSelection.left()==0) { KSpreadCell *cell = nonDefaultCell( m_marker.x(), m_marker.y() ); if(cell->isForceExtraCells()) { extraCell=true; tmpRect=QRect(m_marker.x(),m_marker.y(),1,1); } } KSpreadCell* c = m_cells.firstCell(); for( ;c; c = c->nextCell() ) { if ( !c->isDefault() && c->row() >= rect.top() && c->row() <= rect.bottom() && c->column() >= rect.left() && c->column() <= rect.right() ) cellStack.push( c ); } m_cells.setAutoDelete( false ); // Remove the cells from the table while ( !cellStack.isEmpty() ) { KSpreadCell *cell = cellStack.pop(); m_cells.remove( cell->column(), cell->row() ); cell->updateDepending(); delete cell; } m_cells.setAutoDelete( true ); setLayoutDirtyFlag(); // Since obscured cells might have been deleted we // have to reenforce it. c = m_cells.firstCell(); for( ;c; c = c->nextCell() ) if ( c->isForceExtraCells() && !c->isDefault() ) c->forceExtraCells( c->column(), c->row(), c->extraXCells(), c->extraYCells() ); if(extraCell) { setSelection(tmpRect); unselect(); } m_pDoc->setModified( true ); } void KSpreadTable::deleteSelection( const QPoint& _marker ) { QRect r( m_rctSelection ); if ( r.left() == 0 ) r = QRect( _marker.x(), _marker.y(), 1, 1 ); if ( !m_pDoc->undoBuffer()->isLocked() ) { KSpreadUndoDelete *undo = new KSpreadUndoDelete( m_pDoc, this, r ); m_pDoc->undoBuffer()->appendUndo( undo ); } if ( !m_pDoc->undoBuffer()->isLocked() ) { KSpreadUndoDelete *undo = new KSpreadUndoDelete( m_pDoc, this, r ); m_pDoc->undoBuffer()->appendUndo( undo ); } // Entire rows selected ? if ( isRowSelected() ) { for( int i = r.top(); i <= r.bottom(); ++i ) { m_cells.clearRow( i ); m_rows.removeElement( i ); } emit sig_updateVBorder( this ); } // Entire columns selected ? else if ( isColumnSelected() ) { for( int i = r.left(); i <= r.right(); ++i ) { m_cells.clearColumn( i ); m_columns.removeElement( i ); } emit sig_updateHBorder( this ); } else { deleteCells( r ); } refreshMergedCell(); emit sig_updateView( this ); } void KSpreadTable::refreshView(const QRect& rect) { QRect tmp(rect); KSpreadCell* c = m_cells.firstCell(); for( ;c; c = c->nextCell() ) { if ( !c->isDefault() && c->row() >= rect.top() && c->row() <= rect.bottom() && c->column() >= rect.left() && c->column() <= rect.right() ) if(c->isForceExtraCells()) { int right=QMAX(tmp.right(),c->column()+c->extraXCells()); int bottom=QMAX(tmp.bottom(),c->row()+c->extraYCells()); tmp.setRight(right); tmp.setBottom(bottom); } } deleteCells( rect ); emit sig_updateView( this, tmp ); } void KSpreadTable::updateView(const QRect& rect) { emit sig_updateView( this, rect ); } void KSpreadTable::changeMergedCell( int m_iCol, int m_iRow, int m_iExtraX, int m_iExtraY) { if( m_iExtraX==0 && m_iExtraY==0) { dissociateCell( QPoint( m_iCol,m_iRow),false); return; } KSpreadCell *cell = nonDefaultCell( m_iCol, m_iRow ); if(cell->isForceExtraCells()) dissociateCell( QPoint( m_iCol,m_iRow),false); cell->forceExtraCells( m_iCol,m_iRow, m_iExtraX,m_iExtraY); setMarker(QPoint(m_iCol,m_iRow)); refreshMergedCell(); QRect rect; rect.setCoords(m_iCol,m_iRow,m_iCol+m_iExtraX,m_iRow+m_iExtraY); emit sig_updateView( this, rect ); } void KSpreadTable::mergeCell( const QPoint &_marker, bool makeUndo) { if(m_rctSelection.left() == 0) return; int x=_marker.x(); int y=_marker.y(); if( _marker.x() > m_rctSelection.left() ) x = m_rctSelection.left(); if( _marker.y() > m_rctSelection.top() ) y = m_rctSelection.top(); KSpreadCell *cell = nonDefaultCell( x , y ); if ( !m_pDoc->undoBuffer()->isLocked() && makeUndo) { KSpreadUndoMergedCell *undo = new KSpreadUndoMergedCell( m_pDoc, this, x ,y,cell->extraXCells() ,cell->extraYCells()); m_pDoc->undoBuffer()->appendUndo( undo ); } cell->forceExtraCells( x ,y, abs(m_rctSelection.right() -m_rctSelection.left()), abs(m_rctSelection.bottom() - m_rctSelection.top())); setMarker(QPoint(x,y)); if(getAutoCalc()) recalc(); emit sig_updateView( this, m_rctSelection ); } void KSpreadTable::dissociateCell( const QPoint &_marker,bool makeUndo) { KSpreadCell *cell = nonDefaultCell( _marker.x(), _marker.y() ); if(!cell->isForceExtraCells()) return; if ( !m_pDoc->undoBuffer()->isLocked() && makeUndo) { KSpreadUndoMergedCell *undo = new KSpreadUndoMergedCell( m_pDoc, this, _marker.x() ,_marker.y(),cell->extraXCells() ,cell->extraYCells()); m_pDoc->undoBuffer()->appendUndo( undo ); } int x=cell->extraXCells(); if( x == 0 ) x=1; int y = cell->extraYCells(); if( y == 0 ) y=1; cell->forceExtraCells( _marker.x() ,_marker.y(), 0, 0 ); QRect selection( _marker.x(), _marker.y(), x, y ); setSelection(selection); unselect(); refreshMergedCell(); emit sig_updateView( this, selection ); } bool KSpreadTable::testListChoose(const QPoint &_marker) { QRect selection( selectionRect() ); if(selection.left()==0) selection.setCoords(_marker.x(),_marker.y(),_marker.x(),_marker.y()); KSpreadCell *cell = cellAt( _marker.x(), _marker.y() ); QString tmp=cell->text(); KSpreadCell* c = firstCell(); bool different=false; int col; for( ;c; c = c->nextCell() ) { col = c->column(); if ( selection.left() <= col && selection.right() >= col &&!c->isObscuringForced()&& !(col==_marker.x()&& c->row()==_marker.y())) { if(!c->isFormula() && !c->isNumeric() && !c->valueString().isEmpty() && !c->isTime() &&!c->isDate() && c->content() != KSpreadCell::VisualFormula) { if(c->text()!=tmp) different=true; } } } return different; } void KSpreadTable::print( QPainter &painter, KPrinter *_printer ) { kdDebug(36001)<<"PRINTING ...."<defaultGridPen(); QPen nopen; nopen.setStyle( NoPen ); m_pDoc->setDefaultGridPen( nopen ); } // // Find maximum right/bottom cell with content // QRect cell_range; cell_range.setCoords( 1, 1, 1, 1 ); KSpreadCell* c = m_cells.firstCell(); for( ;c; c = c->nextCell() ) { if ( c->needsPrinting() ) { if ( c->column() > cell_range.right() ) cell_range.setRight( c->column() ); if ( c->row() > cell_range.bottom() ) cell_range.setBottom( c->row() ); } } // Now look at the children QPtrListIterator cit( m_pDoc->children() ); int dummy; int i; for( ; cit.current(); ++cit ) { QRect bound = cit.current()->boundingRect(); i = leftColumn( bound.right(), dummy ); if ( i > cell_range.right() ) cell_range.setRight( i ); i = topRow( bound.bottom(), dummy ); if ( i > cell_range.bottom() ) cell_range.setBottom( i ); } // // Find out how many pages need printing // and which cells to print on which page. // QValueList page_list; QValueList page_frame_list; // How much space is on every page for table content ? QRect rect; rect.setCoords( 0, 0, (int)( MM_TO_POINT ( m_pDoc->printableWidth() )), (int)( MM_TO_POINT ( m_pDoc->printableHeight() )) ); // Up to this row everything is already handled int bottom = 0; // Start of the next page int top = 1; // Calculate all pages, but if we are embedded, print only the first one while ( bottom < cell_range.bottom() /* && page_list.count() == 0 */ ) { kdDebug(36001) << "bottom=" << bottom << " bottom_range=" << cell_range.bottom() << endl; // Up to this column everything is already printed int right = 0; // Start of the next page int left = 1; while ( right < cell_range.right() ) { kdDebug(36001) << "right=" << right << " right_range=" << cell_range.right() << endl; QRect page_range; page_range.setLeft( left ); page_range.setTop( top ); int col = left; int x = columnLayout( col )->width(); while ( x < rect.width() ) { col++; x += columnLayout( col )->width(); } // We want to print at least one column if ( col == left ) col = left + 1; page_range.setRight( col - 1 ); int row = top; int y = rowLayout( row )->height(); while ( y < rect.height() ) { row++; y += rowLayout( row )->height(); } // We want to print at least one row if ( row == top ) row = top + 1; page_range.setBottom( row - 1 ); right = page_range.right(); left = page_range.right() + 1; bottom = page_range.bottom(); // // Test wether there is anything on the page at all. // // Look at the cells bool empty = TRUE; for( int r = page_range.top(); empty && ( r <= page_range.bottom() ); ++r ) for( int c = page_range.left(); empty && ( c <= page_range.right() ); ++c ) if ( cellAt( c, r )->needsPrinting() ) empty = FALSE; // Look for children QRect view( columnPos( page_range.left() ), rowPos( page_range.top() ), rect.width(), rect.height() ); QPtrListIterator it( m_pDoc->children() ); for( ; empty && it.current(); ++it ) { QRect bound = it.current()->boundingRect(); if ( bound.intersects( view ) ) empty = FALSE; } if ( !empty ) { page_list.append( page_range ); page_frame_list.append( view ); } } top = bottom + 1; } kdDebug(36001)<<"PRINTING "<< page_list.count()<<" pages"<::Iterator it = page_list.begin(); QValueList::Iterator fit = page_frame_list.begin(); int w; for( ; it != page_list.end(); ++it, ++fit, ++pagenr ) { // print head line QFont font( "Times", 10 ); painter.setFont( font ); QFontMetrics fm = painter.fontMetrics(); w = fm.width( m_pDoc->headLeft( pagenr, m_strName ) ); if ( w > 0 ) painter.drawText( (int)( MM_TO_POINT ( m_pDoc->leftBorder() )), (int)( MM_TO_POINT ( 10.0 )), m_pDoc->headLeft( pagenr, m_strName ) ); w = fm.width( m_pDoc->headMid( pagenr, m_strName.latin1() ) ); if ( w > 0 ) painter.drawText( (int)( MM_TO_POINT ( m_pDoc->leftBorder()) + ( MM_TO_POINT ( m_pDoc->printableWidth()) - (float)w ) / 2.0 ), (int)( MM_TO_POINT ( 10.0 )), m_pDoc->headMid( pagenr, m_strName ) ); w = fm.width( m_pDoc->headRight( pagenr, m_strName ) ); if ( w > 0 ) painter.drawText( (int)( MM_TO_POINT ( m_pDoc->leftBorder()) + MM_TO_POINT ( m_pDoc->printableWidth()) - (float)w ), (int)( MM_TO_POINT ( 10.0 )), m_pDoc->headRight( pagenr, m_strName) ); // print foot line w = fm.width( m_pDoc->footLeft( pagenr, m_strName ) ); if ( w > 0 ) painter.drawText( (int)( MM_TO_POINT ( m_pDoc->leftBorder() )), (int)( MM_TO_POINT ( m_pDoc->paperHeight() - 10.0 )), m_pDoc->footLeft( pagenr, m_strName ) ); w = fm.width( m_pDoc->footMid( pagenr, m_strName ) ); if ( w > 0 ) painter.drawText( (int)( MM_TO_POINT ( m_pDoc->leftBorder() )+ ( MM_TO_POINT ( m_pDoc->printableWidth()) - (float)w ) / 2.0 ), (int)( MM_TO_POINT ( m_pDoc->paperHeight() - 10.0 ) ), m_pDoc->footMid( pagenr, m_strName ) ); w = fm.width( m_pDoc->footRight( pagenr, m_strName ) ); if ( w > 0 ) painter.drawText( (int)( MM_TO_POINT ( m_pDoc->leftBorder()) + MM_TO_POINT ( m_pDoc->printableWidth()) - (float)w ), (int)( MM_TO_POINT ( m_pDoc->paperHeight() - 10.0 ) ), m_pDoc->footRight( pagenr, m_strName ) ); painter.translate( MM_TO_POINT ( m_pDoc->leftBorder()), MM_TO_POINT ( m_pDoc->topBorder() )); // Print the page printPage( painter, *it, *fit ); painter.translate( - MM_TO_POINT ( m_pDoc->leftBorder()), - MM_TO_POINT ( m_pDoc->topBorder() )); if ( pagenr < (int)page_list.count() ) _printer->newPage(); } if ( !m_bPrintGrid ) { // Restore the grid pen m_pDoc->setDefaultGridPen( gridPen ); } m_bShowGrid = oldShowGrid; } void KSpreadTable::printPage( QPainter &_painter, const QRect& page_range, const QRect& view ) { // kdDebug(36001) << "Rect x=" << page_range->left() << " y=" << page_range->top() << ", w=" // << page_range->width() << " h=" << page_range->height() << endl; // // Draw the cells. // int ypos = 0; int xpos; KSpreadCell *cell; RowLayout *row_lay; ColumnLayout *col_lay; for ( int y = page_range.top(); y <= page_range.bottom(); y++ ) { row_lay = rowLayout( y ); xpos = 0; for ( int x = page_range.left(); x <= page_range.right(); x++ ) { col_lay = columnLayout( x ); cell = cellAt( x, y ); QRect r( 0, 0, view.width(), view.height() ); cell->paintCell( r, _painter, QPoint(xpos, ypos), QPoint(x,y)); xpos += col_lay->width(); } ypos += row_lay->height(); } // // Draw the children // QPtrListIterator it( m_pDoc->children() ); for( ; it.current(); ++it ) { QString tmp=QString("Testing child %1/%2 %3/%4 against view %5/%6 %7/%8") .arg(it.current()->contentRect().left()) .arg(it.current()->contentRect().top()) .arg(it.current()->contentRect().right()) .arg(it.current()->contentRect().bottom()) .arg(view.left()).arg(view.top()).arg(view.right()).arg(view.bottom() ); kdDebug(36001)<boundingRect(); if ( ((KSpreadChild*)it.current())->table() == this && bound.intersects( view ) ) { _painter.save(); _painter.translate( -view.left(), -view.top() ); it.current()->transform( _painter ); it.current()->document()->paintEverything( _painter, it.current()->contentRect(), it.current()->isTransparent() ); _painter.restore(); } } } QDomDocument KSpreadTable::saveCellRect( const QRect &_rect ) { QDomDocument doc( "spreadsheet-snippet" ); doc.appendChild( doc.createProcessingInstruction( "xml", "version=\"1.0\" encoding=\"UTF-8\"" ) ); QDomElement spread = doc.createElement( "spreadsheet-snippet" ); spread.setAttribute( "rows", _rect.bottom() - _rect.top() + 1 ); spread.setAttribute( "columns", _rect.right() - _rect.left() + 1 ); doc.appendChild( spread ); // // Entire rows selected ? // if ( isRowSelected( _rect ) ) { QDomElement rows = doc.createElement("rows"); rows.setAttribute( "count", _rect.bottom() - _rect.top() + 1 ); spread.appendChild( rows ); // Save all cells. KSpreadCell* c = m_cells.firstCell(); for( ;c; c = c->nextCell() ) { if ( !c->isDefault()&&!c->isObscuringForced() ) { QPoint p( c->column(), c->row() ); if ( _rect.contains( p ) ) spread.appendChild( c->save( doc, 0, _rect.top() - 1 ) ); } } // ##### Inefficient // Save the row layouts if there are any RowLayout* lay; for( int y = _rect.top(); y <= _rect.bottom(); ++y ) { lay = rowLayout( y ); if ( lay && !lay->isDefault() ) { QDomElement e = lay->save( doc, _rect.top() - 1 ); if ( !e.isNull() ) spread.appendChild( e ); } } return doc; } // // Entire columns selected ? // if ( isColumnSelected( _rect ) ) { QDomElement columns = doc.createElement("columns"); columns.setAttribute( "count", _rect.right() - _rect.left() + 1 ); spread.appendChild( columns ); // Save all cells. KSpreadCell* c = m_cells.firstCell(); for( ;c; c = c->nextCell() ) { if ( !c->isDefault()&&!c->isObscuringForced()) { QPoint p( c->column(), c->row() ); if ( _rect.contains( p ) ) spread.appendChild( c->save( doc, _rect.left() - 1, 0 ) ); } } // ##### Inefficient // Save the column layouts if there are any ColumnLayout* lay; for( int x = _rect.left(); x <= _rect.right(); ++x ) { lay = columnLayout( x ); if ( lay && !lay->isDefault() ) { QDomElement e = lay->save( doc, _rect.left() - 1 ); if ( !e.isNull() ) spread.appendChild( e ); } } return doc; } // Save all cells. /*KSpreadCell* c = m_cells.firstCell(); for( ;c; c = c->nextCell() ) { if ( !c->isDefault() && !c->isObscuringForced()) { QPoint p( c->column(), c->row() ); if ( _rect.contains( p ) ) spread.appendChild( c->save( doc, _rect.left() - 1, _rect.top() - 1 ,true)); } } */ //store all cell //when they don't exist we created them //because it's necessary when there is a layout on a column/row //but I remove cell which is inserted. KSpreadCell *cell; bool insert; for (int i=_rect.left();i<=_rect.right();i++) for(int j=_rect.top();j<=_rect.bottom();j++) { insert = false; cell = cellAt( i, j ); if ( cell == m_pDefaultCell ) { cell = new KSpreadCell( this, i, j ); insertCell( cell ); insert=true; } spread.appendChild( cell->save( doc, _rect.left() - 1, _rect.top() - 1 ,true)); if( insert ) m_cells.remove(i,j); } return doc; } QDomElement KSpreadTable::save( QDomDocument& doc ) { QDomElement table = doc.createElement( "table" ); table.setAttribute( "name", m_strName ); table.setAttribute( "grid", (int)m_bShowGrid); table.setAttribute( "printGrid", (int)m_bPrintGrid); table.setAttribute( "hide", (int)m_bTableHide); table.setAttribute( "formular", (int)m_bShowFormula); table.setAttribute( "borders", (int)m_bShowPageBorders); table.setAttribute( "lcmode", (int)m_bLcMode); table.setAttribute( "columnnumber", (int)m_bShowColumnNumber); table.setAttribute( "hidezero", (int)m_bHideZero); table.setAttribute( "firstletterupper", (int)m_bFirstLetterUpper); table.setAttribute( "borders1.2", 1); // Save all cells. KSpreadCell* c = m_cells.firstCell(); for( ;c; c = c->nextCell() ) { if ( !c->isDefault() ) { QDomElement e = c->save( doc ); if ( !e.isNull() ) table.appendChild( e ); } } // Save all RowLayout objects. RowLayout* rl = m_rows.first(); for( ; rl; rl = rl->next() ) { if ( !rl->isDefault() ) { QDomElement e = rl->save( doc ); if ( e.isNull() ) return QDomElement(); table.appendChild( e ); } } // Save all ColumnLayout objects. ColumnLayout* cl = m_columns.first(); for( ; cl; cl = cl->next() ) { if ( !cl->isDefault() ) { QDomElement e = cl->save( doc ); if ( e.isNull() ) return QDomElement(); table.appendChild( e ); } } QPtrListIterator chl( m_pDoc->children() ); for( ; chl.current(); ++chl ) { if ( ((KSpreadChild*)chl.current())->table() == this ) { QDomElement e; KSpreadChild * child = (KSpreadChild *) chl.current(); // stupid hack :-( has anybody a better solution? if ( child->inherits("ChartChild") ) { e = ((ChartChild *) child)->save( doc ); } else e = chl.current()->save( doc ); if ( e.isNull() ) return QDomElement(); table.appendChild( e ); } } return table; } bool KSpreadTable::isLoading() { return m_pDoc->isLoading(); } bool KSpreadTable::loadXML( const QDomElement& table ) { bool ok = false; m_strName = table.attribute( "name" ); if ( m_strName.isEmpty() ) { m_pDoc->setErrorMessage( i18n("Invalid document. Table name is empty") ); return false; } if( table.hasAttribute( "grid" ) ) { m_bShowGrid = (int)table.attribute("grid").toInt( &ok ); // we just ignore 'ok' - if it didn't work, go on } if( table.hasAttribute( "printGrid" ) ) { m_bPrintGrid = (int)table.attribute("printGrid").toInt( &ok ); // we just ignore 'ok' - if it didn't work, go on } if( table.hasAttribute( "hide" ) ) { m_bTableHide = (int)table.attribute("hide").toInt( &ok ); // we just ignore 'ok' - if it didn't work, go on } if( table.hasAttribute( "formular" ) ) { m_bShowFormula = (int)table.attribute("formular").toInt( &ok ); // we just ignore 'ok' - if it didn't work, go on } if( table.hasAttribute( "borders" ) ) { m_bShowPageBorders = (int)table.attribute("borders").toInt( &ok ); // we just ignore 'ok' - if it didn't work, go on } if( table.hasAttribute( "lcmode" ) ) { m_bLcMode = (int)table.attribute("lcmode").toInt( &ok ); // we just ignore 'ok' - if it didn't work, go on } if( table.hasAttribute( "columnnumber" ) ) { m_bShowColumnNumber = (int)table.attribute("columnnumber").toInt( &ok ); // we just ignore 'ok' - if it didn't work, go on } if( table.hasAttribute( "hidezero" ) ) { m_bHideZero = (int)table.attribute("hidezero").toInt( &ok ); // we just ignore 'ok' - if it didn't work, go on } if( table.hasAttribute( "firstletterupper" ) ) { m_bFirstLetterUpper = (int)table.attribute("firstletterupper").toInt( &ok ); // we just ignore 'ok' - if it didn't work, go on } // Load the cells QDomNode n = table.firstChild(); while( !n.isNull() ) { QDomElement e = n.toElement(); if ( !e.isNull() && e.tagName() == "cell" ) { KSpreadCell *cell = new KSpreadCell( this, 0, 0 ); if ( cell->load( e, 0, 0 ) ) insertCell( cell, false /* don't update depending cells */ ); else delete cell; // Allow error handling: just skip invalid cells } else if ( !e.isNull() && e.tagName() == "row" ) { RowLayout *rl = new RowLayout( this, 0 ); if ( rl->load( e ) ) insertRowLayout( rl ); else delete rl; } else if ( !e.isNull() && e.tagName() == "column" ) { ColumnLayout *cl = new ColumnLayout( this, 0 ); if ( cl->load( e ) ) insertColumnLayout( cl ); else delete cl; } else if ( !e.isNull() && e.tagName() == "object" ) { KSpreadChild *ch = new KSpreadChild( m_pDoc, this ); if ( ch->load( e ) ) insertChild( ch ); else delete ch; } else if ( !e.isNull() && e.tagName() == "chart" ) { ChartChild *ch = new ChartChild( m_pDoc, this ); if ( ch->load( e ) ) insertChild( ch ); else delete ch; } n = n.nextSibling(); } if( !table.hasAttribute( "borders1.2" ) ) { convertObscuringBorders(); } return true; } bool KSpreadTable::loadChildren( KoStore* _store ) { QPtrListIterator it( m_pDoc->children() ); for( ; it.current(); ++it ) { if ( ((KSpreadChild*)it.current())->table() == this ) { if ( !it.current()->loadDocument( _store ) ) return false; } } return true; } void KSpreadTable::setShowPageBorders( bool b ) { if ( b == m_bShowPageBorders ) return; m_bShowPageBorders = b; emit sig_updateView( this ); } bool KSpreadTable::isOnNewPageX( int _column ) { int col = 1; float x = columnLayout( col )->mmWidth(); while ( ( col <= _column ) && ( col < KS_colMax ) ) { if ( x > m_pDoc->printableWidth() ) { if ( col == _column ) return TRUE; else x = columnLayout( col )->mmWidth(); } col++; x += columnLayout( col )->mmWidth(); } return FALSE; } bool KSpreadTable::isOnNewPageY( int _row ) { int row = 1; float y = rowLayout( row )->mmHeight(); while ( ( row <= _row ) && ( row < KS_rowMax ) ) { if ( y > m_pDoc->printableHeight() ) { if ( row == _row ) return TRUE; else y = rowLayout( row )->mmHeight(); } row++; y += rowLayout( row )->mmHeight(); } return FALSE; } void KSpreadTable::addCellBinding( CellBinding *_bind ) { m_lstCellBindings.append( _bind ); m_pDoc->setModified( true ); } void KSpreadTable::removeCellBinding( CellBinding *_bind ) { m_lstCellBindings.removeRef( _bind ); m_pDoc->setModified( true ); } KSpreadTable* KSpreadTable::findTable( const QString & _name ) { if ( !m_pMap ) return 0L; return m_pMap->findTable( _name ); } // ###### Torben: Use this one instead of m_cells.insert() void KSpreadTable::insertCell( KSpreadCell *_cell, bool _updateDepend ) { // First store the existing dependency list, before the old cell is deleted // QPtrList _cellsDependOnMe = cellAt( _cell->column(), _cell->row() )->cellsDependOnMe(); if ( _updateDepend ) { //Then set the existing dependency list to a new empty on, so the link to the list doesn't get deleted // _cell->setCellsDependOnMe( QPtrList() ); } m_cells.insert( _cell, _cell->column(), _cell->row() ); /* immediately check for cells that are depending on this one. This can happen if a dependancy range * is given which included cells that were the default cell, but are just now being inserted into the * cell list for real */ /* a full scan of all the tables isn't fun, but that was already happening every time a cell is updated anyway. So this isn't any worse than what we've had */ if ( _updateDepend ) { // cellAt( _cell->column(), _cell->row() )->setCellsDependOnMe( _cellsDependOnMe ); QPtrListIterator it( map()->tableList() ); KSpreadCell* c; for( ; it.current(); ++it ) { c = it.current()->firstCell(); for( ; c; c = c->nextCell() ) { if ( c->cellDependsOn(this, _cell->column(), _cell->row()) ) { _cell->NotifyDepending( c->column(), c->row(), it.current(), true); } } } } if ( m_bScrollbarUpdates ) { checkRangeHBorder ( _cell->column() ); checkRangeVBorder ( _cell->row() ); } } void KSpreadTable::insertColumnLayout( ColumnLayout *l ) { m_columns.insertElement( l, l->column() ); } void KSpreadTable::insertRowLayout( RowLayout *l ) { m_rows.insertElement( l, l->row() ); } void KSpreadTable::update() { KSpreadCell* c = m_cells.firstCell(); for( ;c; c = c->nextCell() ) { updateCell(c, c->column(), c->row()); } } void KSpreadTable::updateCell( KSpreadCell *cell, int _column, int _row ) { if ( doc()->isLoading() || doc()->delayCalculation() || (!getAutoCalc())) return; // Get the size int left = columnPos( _column ); int top = rowPos( _row ); int right = left + cell->extraWidth(); int bottom = top + cell->extraHeight(); // Need to calculate ? if ( cell->calcDirtyFlag() ) cell->calc(); // Need to make layout ? if ( cell->layoutDirtyFlag() ) cell->makeLayout( painter(), _column, _row ); // Perhaps the size changed now ? right = QMAX( right, left + cell->extraWidth() ); bottom = QMAX( bottom, top + cell->extraHeight() ); // Force redraw QPointArray arr( 4 ); arr.setPoint( 0, left, top ); arr.setPoint( 1, right, top ); arr.setPoint( 2, right, bottom ); arr.setPoint( 3, left, bottom ); // ##### Hmmmm, why not draw the cell directly ? // That will be faster. emit sig_polygonInvalidated( arr ); cell->clearDisplayDirtyFlag(); } void KSpreadTable::emit_polygonInvalidated( const QPointArray& arr ) { emit sig_polygonInvalidated( arr ); } void KSpreadTable::emit_updateRow( RowLayout *_layout, int _row ) { if ( doc()->isLoading() ) return; KSpreadCell* c = m_cells.firstCell(); for( ;c; c = c->nextCell() ) if ( c->row() == _row ) c->setLayoutDirtyFlag(); emit sig_updateVBorder( this ); emit sig_updateView( this ); emit sig_maxRow(maxRow()); _layout->clearDisplayDirtyFlag(); } void KSpreadTable::emit_updateColumn( ColumnLayout *_layout, int _column ) { if ( doc()->isLoading() ) return; KSpreadCell* c = m_cells.firstCell(); for( ;c; c = c->nextCell() ) if ( c->column() == _column ) c->setLayoutDirtyFlag(); emit sig_updateHBorder( this ); emit sig_updateView( this ); emit sig_maxColumn( maxColumn() ); _layout->clearDisplayDirtyFlag(); } void KSpreadTable::insertChart( const QRect& _rect, KoDocumentEntry& _e, const QRect& _data ) { kdDebug(36001) << "Creating document" << endl; KoDocument* doc = _e.createDoc(); kdDebug(36001) << "Created" << endl; if ( !doc ) // Error message is already displayed, so just return return; kdDebug(36001) << "NOW FETCHING INTERFACE" << endl; if ( !doc->initDoc() ) return; ChartChild * ch = new ChartChild( m_pDoc, this, doc, _rect ); ch->setDataArea( _data ); ch->update(); // m_pDoc->insertChild( ch ); insertChild( ch ); KoChart::WizardExtension * wiz = ch->chart()->wizardExtension(); if ( wiz ) wiz->show(); } void KSpreadTable::insertChild( const QRect& _rect, KoDocumentEntry& _e ) { KoDocument* doc = _e.createDoc( m_pDoc ); if ( !doc ) { kdDebug() << "Error inserting child!" << endl; return; } doc->initDoc(); KSpreadChild* ch = new KSpreadChild( m_pDoc, this, doc, _rect ); insertChild( ch ); } void KSpreadTable::insertChild( KSpreadChild *_child ) { // m_lstChildren.append( _child ); m_pDoc->insertChild( _child ); emit sig_polygonInvalidated( _child->framePointArray() ); } void KSpreadTable::deleteChild( KSpreadChild* child ) { QPointArray polygon = child->framePointArray(); emit sig_removeChild( child ); delete child; emit sig_polygonInvalidated( polygon ); } void KSpreadTable::changeChildGeometry( KSpreadChild *_child, const QRect& _rect ) { _child->setGeometry( _rect ); emit sig_updateChildGeometry( _child ); } /* QPtrListIterator KSpreadTable::childIterator() { return QPtrListIterator ( m_lstChildren ); } */ bool KSpreadTable::saveChildren( KoStore* _store, const QString &_path ) { int i = 0; QPtrListIterator it( m_pDoc->children() ); for( ; it.current(); ++it ) { if ( ((KSpreadChild*)it.current())->table() == this ) { QString path = QString( "%1/%2" ).arg( _path ).arg( i++ ); if ( !it.current()->document()->saveToStore( _store, path ) ) return false; } } return true; } KSpreadTable::~KSpreadTable() { s_mapTables->remove( m_id ); //when you remove all table (close file) //you must reinit s_id otherwise there is not //the good name between map and table if( s_mapTables->count()==0) s_id=0L; KSpreadCell* c = m_cells.firstCell(); for( ; c; c = c->nextCell() ) c->tableDies(); m_cells.clear(); // cells destructor needs table to still exist m_pPainter->end(); delete m_pPainter; delete m_pWidget; delete m_defaultLayout; delete m_pDefaultCell; delete m_pDefaultRowLayout; delete m_pDefaultColumnLayout; delete m_dcop; } void KSpreadTable::checkRangeHBorder ( int _column ) { if ( m_bScrollbarUpdates && _column > m_iMaxColumn ) { m_iMaxColumn = _column; emit sig_maxColumn( _column ); } } void KSpreadTable::checkRangeVBorder ( int _row ) { if ( m_bScrollbarUpdates && _row > m_iMaxRow ) { m_iMaxRow = _row; emit sig_maxRow( _row ); } } void KSpreadTable::enableScrollBarUpdates( bool _enable ) { m_bScrollbarUpdates = _enable; } DCOPObject* KSpreadTable::dcopObject() { if ( !m_dcop ) m_dcop = new KSpreadTableIface( this ); return m_dcop; } void KSpreadTable::hideTable(bool _hide) { setHidden(_hide); if(_hide) emit sig_TableHidden(this); else emit sig_TableShown(this); } void KSpreadTable::removeTable() { emit sig_TableRemoved(this); } void KSpreadTable::setActiveTable() { emit sig_maxColumn( maxColumn() ); emit sig_maxRow( maxRow() ); emit sig_TableActivated( this ); emit sig_updateVBorder( this ); emit sig_updateHBorder( this ); emit sig_updateView( this ); } bool KSpreadTable::setTableName( const QString& name, bool init, bool makeUndo ) { if ( map()->findTable( name ) ) return FALSE; if ( m_strName == name ) return TRUE; QString old_name = m_strName; m_strName = name; if ( init ) return TRUE; QPtrListIterator it( map()->tableList() ); for( ; it.current(); ++it ) it.current()->changeCellTabName( old_name, name ); if(makeUndo) { if ( !m_pDoc->undoBuffer()->isLocked() ) { KSpreadUndoAction* undo = new KSpreadUndoSetTableName( doc(), this, old_name ); m_pDoc->undoBuffer()->appendUndo( undo ); } } m_pDoc->changeAreaTableName(old_name,name); emit sig_nameChanged( this, old_name ); return TRUE; } void KSpreadTable::updateLocale() { KSpreadCell* c = m_cells.firstCell(); for( ;c; c = c->nextCell() ) { QString _text = c->text(); c->setDisplayText( _text, false/* no recalc deps for each, done independently */ ); } recalc(); } KSpreadCell* KSpreadTable::getFirstCellColumn(int col) { return m_cells.getFirstCellColumn(col); } KSpreadCell* KSpreadTable::getLastCellColumn(int col) { return m_cells.getLastCellColumn(col); } KSpreadCell* KSpreadTable::getFirstCellRow(int row) { return m_cells.getFirstCellRow(row); } KSpreadCell* KSpreadTable::getLastCellRow(int row) { return m_cells.getLastCellRow(row); } KSpreadCell* KSpreadTable::getNextCellUp(int col, int row) { return m_cells.getNextCellUp(col, row); } KSpreadCell* KSpreadTable::getNextCellDown(int col, int row) { return m_cells.getNextCellDown(col, row); } KSpreadCell* KSpreadTable::getNextCellLeft(int col, int row) { return m_cells.getNextCellLeft(col, row); } KSpreadCell* KSpreadTable::getNextCellRight(int col, int row) { return m_cells.getNextCellRight(col, row); } bool KSpreadTable::isCellSelected(int column, int row) { bool selected; KSpreadCell* cell = cellAt( column, row ); selected = selectionRect().contains( QPoint( column, row ) ); if ( cell->isObscured() ) { selected = selectionRect().contains( QPoint( cell->obscuringCellsColumn(), cell->obscuringCellsRow() ) ); } return selected; } void KSpreadTable::convertObscuringBorders() { /* a word of explanation here: beginning with KSpread 1.2 (actually, cvs of Mar 28, 2002), border information is stored differently. Previously, for a cell obscuring a region, the entire region's border's data would be stored in the obscuring cell. This caused some data loss in certain situations. After that date, each cell stores its own border data, and prints it even if it is an obscured cell (as long as that border isn't across an obscuring border). Anyway, this function is used when loading a file that was stored with the old way of borders. All new files have the table attribute "borders1.2" so if that isn't in the file, all the border data will be converted here. It's a bit of a hack but I can't think of a better way and it's not *that* bad of a hack.:-) */ KSpreadCell* c = m_cells.firstCell(); QPen topPen, bottomPen, leftPen, rightPen; for( ;c; c = c->nextCell() ) { if (c->extraXCells() > 0 || c->extraYCells() > 0) { topPen = c->topBorderPen(c->column(), c->row()); leftPen = c->leftBorderPen(c->column(), c->row()); rightPen = c->rightBorderPen(c->column(), c->row()); bottomPen = c->bottomBorderPen(c->column(), c->row()); c->setTopBorderStyle(Qt::NoPen); c->setLeftBorderStyle(Qt::NoPen); c->setRightBorderStyle(Qt::NoPen); c->setBottomBorderStyle(Qt::NoPen); for (int x = c->column(); x < c->column() + c->extraXCells(); x++) { nonDefaultCell( x, c->row() )->setTopBorderPen(topPen); nonDefaultCell( x, c->row() + c->extraYCells() )-> setBottomBorderPen(bottomPen); } for (int y = c->row(); y < c->row() + c->extraYCells(); y++) { nonDefaultCell( c->column(), y )->setLeftBorderPen(leftPen); nonDefaultCell( c->column() + c->extraXCells(), y )-> setRightBorderPen(rightPen); } } } } #ifndef NDEBUG void KSpreadTable::printDebug() { int iMaxColumn = maxColumn(); int iMaxRow = maxRow(); kdDebug(36001) << "Cell | Content | DataT | Text" << endl; KSpreadCell *cell; for ( int currentrow = 1 ; currentrow < iMaxRow ; ++currentrow ) { for ( int currentcolumn = 1 ; currentcolumn < iMaxColumn ; currentcolumn++ ) { cell = cellAt( currentcolumn, currentrow ); if ( !cell->isDefault() && !cell->isEmpty() ) { QString cellDescr = util_cellName( currentcolumn, currentrow ); cellDescr = cellDescr.rightJustify( 4,' ' ); //QString cellDescr = "Cell "; //cellDescr += QString::number(currentrow).rightJustify(3,'0') + ','; //cellDescr += QString::number(currentcolumn).rightJustify(3,'0') + ' '; cellDescr += " | "; static const char* s_contentString[] = { "Text ", "RichTxt ", "Formula ", "VisForm ", "ERROR " }; cellDescr += s_contentString[ cell->content() ]; cellDescr += " | "; cellDescr += cell->dataTypeToString( cell->dataType() ).rightJustify(5,' '); cellDescr += " | "; cellDescr += cell->text(); if ( cell->content() == KSpreadCell::Formula ) cellDescr += QString(" [result: %1]").arg( cell->valueString() ); kdDebug(36001) << cellDescr << endl; } } } } #endif /********************************************************** * * KSpreadChild * **********************************************************/ KSpreadChild::KSpreadChild( KSpreadDoc *parent, KSpreadTable *_table, KoDocument* doc, const QRect& geometry ) : KoDocumentChild( parent, doc, geometry ) { m_pTable = _table; } KSpreadChild::KSpreadChild( KSpreadDoc *parent, KSpreadTable *_table ) : KoDocumentChild( parent ) { m_pTable = _table; } KSpreadChild::~KSpreadChild() { } /********************************************************** * * ChartChild * **********************************************************/ ChartChild::ChartChild( KSpreadDoc *_spread, KSpreadTable *_table, KoDocument* doc, const QRect& geometry ) : KSpreadChild( _spread, _table, doc, geometry ) { m_pBinding = 0; } ChartChild::ChartChild( KSpreadDoc *_spread, KSpreadTable *_table ) : KSpreadChild( _spread, _table ) { m_pBinding = 0; } ChartChild::~ChartChild() { if ( m_pBinding ) delete m_pBinding; } void ChartChild::setDataArea( const QRect& _data ) { if ( m_pBinding == 0L ) m_pBinding = new ChartBinding( m_pTable, _data, this ); else m_pBinding->setDataArea( _data ); } void ChartChild::update() { if ( m_pBinding ) m_pBinding->cellChanged( 0 ); } bool ChartChild::load( const QDomElement& element ) { if ( !KSpreadChild::load( element ) ) return false; if ( element.hasAttribute( "left-cell" ) && element.hasAttribute( "top-cell" ) && element.hasAttribute( "right-cell" ) && element.hasAttribute( "bottom-cell" ) ) { QRect r; r.setCoords( element.attribute( "left-cell" ).toInt(), element.attribute( "top-cell" ).toInt(), element.attribute( "right-cell" ).toInt(), element.attribute( "bottom-cell" ).toInt() ); setDataArea( r ); } return true; } QDomElement ChartChild::save( QDomDocument& doc ) { QDomElement element = KSpreadChild::save( doc ); element.setTagName( "chart" ); element.setAttribute( "left-cell", m_pBinding->dataArea().left() ); element.setAttribute( "right-cell", m_pBinding->dataArea().right() ); element.setAttribute( "top-cell", m_pBinding->dataArea().top() ); element.setAttribute( "bottom-cell", m_pBinding->dataArea().bottom() ); return element; } bool ChartChild::loadDocument( KoStore* _store ) { bool res = KSpreadChild::loadDocument( _store ); if ( !res ) return res; // Did we see a cell rectangle ? if ( !m_pBinding ) return true; update(); return true; } KoChart::Part* ChartChild::chart() { assert( document()->inherits( "KoChart::Part" ) ); return static_cast( document() ); } #include "kspread_table.moc"