diff --git a/kspread/dependencies.cc b/kspread/dependencies.cc index 001ee7b4e82..7eac0468d85 100644 --- a/kspread/dependencies.cc +++ b/kspread/dependencies.cc @@ -1,553 +1,553 @@ /* This file is part of the KDE project Copyright 2004 Tomas Mecir 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 "dependencies.h" #include "formula.h" #include "kspread_cell.h" #include "kspread_sheet.h" #include //rows x cols in one cell-chunk; bigger values lead to slower updating //of range-dependencies, lower values will increase memory usage #define CELLCHUNK_ROWS 128 #define CELLCHUNK_COLS 16 namespace KSpread { /** d-pointer of DependencyManager */ class DependencyList { public: DependencyList (KSpreadSheet *s) { sheet = s; }; ~DependencyList () { reset (); }; /** clear internal structures */ void reset (); /** generate list of dependencies of a cell */ void generateDependencies (const KSpreadPoint &cell); /** generate list of dependencies of a range */ void generateDependencies (const KSpreadRange &range); /** generate list of dependencies of a range list */ void generateDependencies (const RangeList &rangeList); /** update cells dependending on a given cell */ void processDependencies (const KSpreadPoint &cell); /** update cells dependending on a cell in a given range */ void processDependencies (const KSpreadRange &range); /** update cells dependending on a given range-list */ void processDependencies (const RangeList &rangeList); /** get dependencies of a cell */ RangeList getDependencies (const KSpreadPoint &cell); /** get cells depending on this cell, either through normal or range dependency */ QValueList getDependants (const KSpreadPoint &cell); protected: /** update structures: cell 1 depends on cell 2 */ void addDependency (const KSpreadPoint &cell1, const KSpreadPoint &cell2); /** update structures: cell depends on a range */ void addRangeDependency (const RangeDependency &rd); /** remove all dependencies of a cell */ void removeDependencies (const KSpreadPoint &cell); /** update all cells depending on a range containing this cell */ void processRangeDependencies (const KSpreadPoint &cell); /** update all cells depending on a range intersecting with this range */ void processRangeDependencies (const KSpreadRange &range); /** update one cell due to changed dependencies */ void updateCell (const KSpreadPoint &cell) const; /** return a leading cell for a given cell (used to store range dependencies effectively) */ KSpreadPoint leadingCell (const KSpreadPoint &cell) const; /** list of leading cells of all cell chunks that this range belongs to */ QValueList leadingCells (const KSpreadRange &range) const; /** retrieve a list of cells that a given cell depends on */ RangeList computeDependencies (const KSpreadPoint &cell) const; /** KSpreadSheet whose dependencies are managed by this instance */ KSpreadSheet *sheet; /** dependencies of each cell */ QMap dependencies; /** list of cells (but NOT ranges) that depend on a cell */ QMap > cellDeps; /** all range dependencies splitted into cell-chunks (TODO: describe) */ QMap > rangeDeps; }; }; using namespace KSpread; DependencyManager::DependencyManager (KSpreadSheet *s) { deps = new DependencyList (s); } DependencyManager::~DependencyManager () { delete deps; deps = 0; } void DependencyManager::reset () { deps->reset(); } void DependencyManager::cellChanged (const KSpreadPoint &cell) { KSpreadCell *c = cell.cell(); //if the cell contains the circle error, we mustn't do anything if (c->testFlag (KSpreadCell::Flag_CircularCalculation)) return; kdDebug(36001) << "updating dependencies for cell (" << c->row() << "," << c->column() << ")" << endl; //don't re-generate dependencies if we're updating dependencies if ( !(c->testFlag (KSpreadCell::Flag_Progress))) deps->generateDependencies (cell); deps->processDependencies (cell); } void DependencyManager::rangeChanged (const KSpreadRange &range) { deps->generateDependencies (range); deps->processDependencies (range); } void DependencyManager::rangeListChanged (const RangeList &rangeList) { deps->generateDependencies (rangeList); deps->processDependencies (rangeList); } RangeList DependencyManager::getDependencies (const KSpreadPoint &cell) { return deps->getDependencies (cell); } QValueList DependencyManager::getDependants (const KSpreadPoint &cell) { return deps->getDependants (cell); } void DependencyList::reset () { dependencies.clear(); cellDeps.clear(); rangeDeps.clear(); } RangeList DependencyList::getDependencies (const KSpreadPoint &cell) { RangeList rl; //look if the cell has any dependencies if (!dependencies.contains (cell)) return rl; //it doesn't - return an empty list //the cell does have dependencies - return them! return dependencies[cell]; } QValueList DependencyList::getDependants (const KSpreadPoint &cell) { QValueList list; //cell dependencies go first if (cellDeps.contains (cell)) list = cellDeps[cell]; //next, append range dependencies KSpreadPoint leading = leadingCell (cell); QValueList::iterator it; if (!rangeDeps.count (leading)) return list; //no range dependencies in this cell chunk -> nothing more to do for (it = rangeDeps[leading].begin(); it != rangeDeps[leading].end(); ++it) { //process all range dependencies, and for each range including the questioned cell, //add the depending cell to the list if ((*it).range.contains (cell)) { KSpreadPoint c; c.setRow ((*it).cellrow); c.setColumn ((*it).cellcolumn); c.table = sheet; list.push_back (c); } } return list; } void DependencyList::addDependency (const KSpreadPoint &cell1, const KSpreadPoint &cell2) { kdDebug(36001) << "Dep. manager: added a dependency" << endl; //cell2 can be in another sheet (inter-sheet dependency) KSpreadSheet *sh = cell2.table; if (!sh) sh = sheet; dependencies[cell1].cells.push_back (cell2); sh->dependencies()->deps->cellDeps[cell2].push_back (cell1); } void DependencyList::addRangeDependency (const RangeDependency &rd) { //target range can be in another sheet (inter-sheet dependency) KSpreadSheet *sh = rd.range.table; if (!sh) sh = sheet; KSpreadPoint cell; cell.table = sheet; cell.setRow (rd.cellrow); cell.setColumn (rd.cellcolumn); dependencies[cell].ranges.push_back (rd.range); QValueList leadings = leadingCells (rd.range); QValueList::iterator it; for (it = leadings.begin(); it != leadings.end(); ++it) sh->dependencies()->deps->rangeDeps[*it].push_back (rd); } void DependencyList::removeDependencies (const KSpreadPoint &cell) { //look if the cell has any dependencies if (!dependencies.contains (cell)) return; //it doesn't - nothing more to do //first we remove cell-dependencies QValueList cells = dependencies[cell].cells; QValueList::iterator it1; for (it1 = cells.begin(); it1 != cells.end(); ++it1) { //get sheet-pointer - needed to handle inter-sheet dependencies correctly KSpreadSheet *sh = (*it1).table; if (!sh) sh = sheet; if (!sh->dependencies()->deps->cellDeps.contains (*it1)) continue; //this should never happen //we no longer depend on this cell QValueList::iterator cit; cit = sh->dependencies()->deps->cellDeps[*it1].find (cell); if (cit != sh->dependencies()->deps->cellDeps[*it1].end()) sh->dependencies()->deps->cellDeps[*it1].erase (cit); } //then range-dependencies are removed QValueList ranges = dependencies[cell].ranges; QValueList::iterator it2; QValueList leads; for (it2 = ranges.begin(); it2 != ranges.end(); ++it2) { //we construct a list of cell-chunks containing a range and merge it //with lists generated from all previous ranges (duplicates are removed) QValueList leadings = leadingCells (*it2); for (it1 = leadings.begin(); it1 != leadings.end(); ++it1) if (!leads.contains (*it1)) leads.push_back (*it1); } for (it1 = leads.begin(); it1 != leads.end(); ++it1) { //get sheet-pointer - needed to handle inter-sheet dependencies correctly KSpreadSheet *sh = (*it1).table; if (!sh) sh = sheet; if (sh->dependencies()->deps->rangeDeps.contains (*it1)) { QValueList::iterator it3; QValueList rdeps = sh->dependencies()->deps->rangeDeps[*it1]; it3 = rdeps.begin(); //erase all range dependencies of this cell in this cell-chunk while (it3 != rdeps.end()) if (((*it3).cellrow == cell.row()) && ((*it3).cellcolumn == cell.column())) it3 = rdeps.erase (it3); else ++it3; //erase the list if we no longer need it if (rdeps.empty()) sh->dependencies()->deps->rangeDeps.erase (*it1); } } //finally, remove the entry about this cell dependencies[cell].cells.clear(); dependencies[cell].ranges.clear(); dependencies.erase (cell); } void DependencyList::generateDependencies (const KSpreadPoint &cell) { //get rid of old dependencies first removeDependencies (cell); //new dependencies only need to be generated if the cell contains a formula KSpreadCell *c = sheet->cellAt (cell.column(), cell.row()); if (!c->isFormula()) return; //now we need to generate dependencies RangeList rl = computeDependencies (cell); //now that we have the new dependencies, we put them into our data structures //and we're done QValueList::iterator it1; QValueList::iterator it2; for (it1 = rl.cells.begin(); it1 != rl.cells.end(); ++it1) addDependency (cell, *it1); for (it2 = rl.ranges.begin(); it2 != rl.ranges.end(); ++it2) { RangeDependency rd; rd.cellrow = cell.row(); rd.cellcolumn = cell.column(); rd.range = *it2; addRangeDependency (rd); } } void DependencyList::generateDependencies (const KSpreadRange &range) { for (int row = range.startRow(); row <= range.endRow(); row++) for (int col = range.startCol(); col <= range.endCol(); col++) { KSpreadPoint c; c.setRow (row); c.setColumn (col); c.table = sheet; generateDependencies (c); } } void DependencyList::generateDependencies (const RangeList &rangeList) { QValueList::const_iterator it1; QValueList::const_iterator it2; for (it1 = rangeList.cells.begin(); it1 != rangeList.cells.end(); ++it1) generateDependencies (*it1); for (it2 = rangeList.ranges.begin(); it2 != rangeList.ranges.end(); ++it2) generateDependencies (*it2); } void DependencyList::processDependencies (const KSpreadPoint &cell) { if (cellDeps.contains (cell)) { QValueList d = cellDeps[cell]; QValueList::iterator it; for (it = d.begin(); it != d.end(); ++it) updateCell (*it); } processRangeDependencies (cell); } void DependencyList::processRangeDependencies (const KSpreadPoint &cell) { KSpreadPoint leading = leadingCell (cell); QValueList::iterator it; if (!rangeDeps.count (leading)) return; //no range dependencies in this cell chunk for (it = rangeDeps[leading].begin(); it != rangeDeps[leading].end(); ++it) { //process all range dependencies, and for each range including the modified cell, //recalc the depending cell if ((*it).range.contains (cell)) { KSpreadPoint c; c.setRow ((*it).cellrow); c.setColumn ((*it).cellcolumn); c.table = sheet; updateCell (c); } } } void DependencyList::processDependencies (const KSpreadRange &range) { //each cell's dependencies need to be updated - that cannot be helped - having a range //only helps with range dependencies for (int row = range.startRow(); row <= range.endRow(); row++) for (int col = range.startCol(); col <= range.endCol(); col++) { KSpreadPoint c; c.setRow (row); c.setColumn (col); c.table = sheet; if (cellDeps.contains (c)) { QValueList d = cellDeps[c]; QValueList::iterator it; for (it = d.begin(); it != d.end(); ++it) updateCell (*it); } } processRangeDependencies (range); } void DependencyList::processRangeDependencies (const KSpreadRange &range) { //TODO: some optimization, so that we don't recompute cells depending of huge //ranges more than once (now we recompute them once per cell-chunk used by their dependency) //This will probably happen as a part of splitting this into dep manager //and recalc manager QValueList leadings = leadingCells (range); QValueList::iterator it; for (it = leadings.begin(); it != leadings.end(); ++it) { if (!rangeDeps.count (*it)) continue; //no range dependencies in this cell chunk QValueList::iterator it2; for (it2 = rangeDeps[*it].begin(); it2 != rangeDeps[*it].end(); ++it2) { //process all range dependencies, and for each range intersecting with our range, //recalc the depending cell if ((*it2).range.intersects (range)) { KSpreadPoint c; c.setRow ((*it2).range.startRow()); c.setColumn ((*it2).range.startCol()); c.table = sheet; updateCell (c); } } } } void DependencyList::processDependencies (const RangeList &rangeList) { QValueList::const_iterator it1; QValueList::const_iterator it2; for (it1 = rangeList.cells.begin(); it1 != rangeList.cells.end(); ++it1) processDependencies (*it1); for (it2 = rangeList.ranges.begin(); it2 != rangeList.ranges.end(); ++it2) processDependencies (*it2); } void DependencyList::updateCell (const KSpreadPoint &cell) const { KSpreadCell *c = cell.cell(); - + //prevent infinite recursion (circular dependencies) if (c->testFlag (KSpreadCell::Flag_Progress)) { kdError(36001) << "ERROR: Circle" << endl; c->setFlag(KSpreadCell::Flag_CircularCalculation); KSpreadValue v; v.setError ( "####" ); c->setValue (v); //clear the computing-dependencies flag c->clearFlag (KSpreadCell::Flag_Progress); return; } kdDebug() << "Updating depending cell (" << c->row() << "," << c->column() << ")" << endl; //set the computing-dependencies flag c->setFlag (KSpreadCell::Flag_Progress); //mark the cell as calc-dirty c->setCalcDirtyFlag(); //recalculate the cell c->calc (false); //clear the computing-dependencies flag c->clearFlag (KSpreadCell::Flag_Progress); } KSpreadPoint DependencyList::leadingCell (const KSpreadPoint &cell) const { KSpreadPoint c; c.setRow (cell.row() - cell.row() % CELLCHUNK_ROWS + 1); c.setColumn (cell.column() - cell.column() % CELLCHUNK_COLS + 1); c.table = cell.table; return c; } QValueList DependencyList::leadingCells (const KSpreadRange &range) const { QValueList cells; KSpreadPoint cell1, cell2, cell; cell1.setRow (range.startRow()); cell1.setColumn (range.startCol()); cell2.setRow (range.endRow()); cell2.setColumn (range.endCol()); cell1.table = range.table; cell2.table = range.table; cell1 = leadingCell (cell1); cell2 = leadingCell (cell2); for (int row = cell1.row(); row <= cell2.row(); row += CELLCHUNK_ROWS) for (int col = cell1.column(); col <= cell2.column(); col += CELLCHUNK_COLS) { cell.setRow (row); cell.setColumn (col); cell.table = range.table; cells.push_back (cell); } return cells; } RangeList DependencyList::computeDependencies (const KSpreadPoint &cell) const { RangeList rl; KSpreadCell *c = cell.cell(); if (!c->isFormula()) return rl; //not a formula -> no dependencies //TODO: when the new parser is in use, KSpreadCell will hold a Formula //instance, hence we'll be able to use that one directly Formula formula; formula.setExpression (c->text()); kdDebug(36001) << "Retrieving dependencies for cell with text \"" << c->text() << "\"" << endl; //now that we have the formula, we ask it to give us the dependencies rl = formula.getDependencies (sheet); return rl; } diff --git a/kspread/dialogs/kspread_dlg_anchor.cc b/kspread/dialogs/kspread_dlg_anchor.cc index d9e47707a67..9a568f72a4f 100644 --- a/kspread/dialogs/kspread_dlg_anchor.cc +++ b/kspread/dialogs/kspread_dlg_anchor.cc @@ -1,485 +1,486 @@ /* This file is part of the KDE project Copyright (C) 2003 Norbert Andres (C) 2002 Philipp Mueller (C) 1999-2002 Laurent Montel (C) 1999 Stephan Kulow (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 "kspread_dlg_anchor.h" #include "kspread_canvas.h" #include "kspread_doc.h" +#include "kspread_editors.h" #include "kspread_sheet.h" #include "kspread_view.h" #include #include #include #include #include #include #include #include #include #include #include #include #include KSpreadLinkDlg::KSpreadLinkDlg( KSpreadView* parent, const char* /*name*/ ) : KDialogBase( KDialogBase::IconList,i18n( "Insert Link") , KDialogBase::Ok | KDialogBase::Cancel, KDialogBase::Ok ) { m_pView = parent; QVBox *page=addVBoxPage( i18n( "Internet" ), QString::null,BarIcon( "html",KIcon::SizeMedium ) ); _internetAnchor = new internetAnchor( parent,page ); page=addVBoxPage( i18n( "Mail" ), QString::null,BarIcon( "mail_generic",KIcon::SizeMedium ) ); _mailAnchor = new mailAnchor( parent,page ); page=addVBoxPage( i18n( "File" ), QString::null,BarIcon( "filenew",KIcon::SizeMedium ) ); _fileAnchor = new fileAnchor( parent,page ); page=addVBoxPage( i18n( "Cell" ), QString::null,BarIcon( "misc",KIcon::SizeMedium ) ); _cellAnchor = new cellAnchor( parent,page ); connect( this, SIGNAL( okClicked( ) ),this,SLOT( slotOk() ) ); resize( 400,300 ); } void KSpreadLinkDlg::slotOk() { QString result; switch( activePageIndex() ) { case 0: result=_internetAnchor->apply(); break; case 1: result=_mailAnchor->apply(); break; case 2: result=_fileAnchor->apply(); break; case 3: result=_cellAnchor->apply(); break; default: kdDebug(36001)<<"Error in KSpreadLinkDlg\n"; } if ( !result.isEmpty() ) setCellText( result ); } void KSpreadLinkDlg::setCellText( const QString &_text ) { m_pView->doc()->emitBeginOperation( false ); KSpreadCell *cell = m_pView->activeTable()->cellAt( m_pView->canvasWidget()->markerColumn(),m_pView->canvasWidget()->markerRow() ); if ( !cell->isDefault() ) { int ret = KMessageBox::warningYesNo( this, i18n( "Cell is not empty.\nDo you want to continue?" ) ); if ( ret == 4 ) { reject(); return; } } //refresh editWidget if (!_text.isEmpty() ) { m_pView->canvasWidget()->setFocus(); m_pView->setText( _text ); m_pView->editWidget()->setText( _text ); accept(); } m_pView->slotUpdateView( m_pView->activeTable() ); //m_pView->doc()->emitEndOperation(); } internetAnchor::internetAnchor( KSpreadView* _view, QWidget *parent , char *name ) : QWidget ( parent,name ) { m_pView=_view; QVBoxLayout *lay1 = new QVBoxLayout( this ); lay1->setMargin( KDialog::marginHint() ); lay1->setSpacing( KDialog::spacingHint() ); QVBoxLayout *lay2 = new QVBoxLayout( lay1 ); lay2->setSpacing( KDialog::spacingHint() ); QLabel* tmpQLabel; tmpQLabel = new QLabel( this ); lay2->addWidget(tmpQLabel ); tmpQLabel->setText( i18n( "Comment:" ) ); text = new QLineEdit( this ); lay2->addWidget( text ); tmpQLabel = new QLabel( this ); lay2->addWidget( tmpQLabel ); tmpQLabel->setText( i18n( "Internet address:" ) ); l_internet = new QLineEdit( this ); lay2->addWidget( l_internet ); bold=new QCheckBox( i18n( "Bold" ),this ); lay2->addWidget( bold ); italic=new QCheckBox( i18n( "Italic" ),this ); lay2->addWidget( italic ); KSeparator* bar1 = new KSeparator( KSeparator::HLine, this ); bar1->setFixedHeight( 10 ); lay2->addWidget( bar1 ); text->setFocus(); } QString internetAnchor::apply() { if ( l_internet->text().isEmpty() || text->text().isEmpty() ) { KMessageBox::error( this, i18n( "Area text or cell is empty." ) ); return QString(); } return createLink(); } QString internetAnchor::createLink() const { QString end_link; QString link; if ( l_internet->text().find( "http://" )!=-1 ) link = "!text()+"\""+">"; else link = "!text()+"\""+">"; if ( bold->isChecked()&&!italic->isChecked() ) { link+=""+text->text()+""; } else if ( !bold->isChecked()&&italic->isChecked() ) { link+=""+text->text()+""; } else if ( bold->isChecked()&&italic->isChecked() ) { link+=""+text->text()+""; } else { link+=text->text()+""; } return link; } mailAnchor::mailAnchor( KSpreadView* _view,QWidget *parent , char *name ) :QWidget ( parent,name ) { m_pView=_view; QVBoxLayout *lay1 = new QVBoxLayout( this ); lay1->setMargin( KDialog::marginHint() ); lay1->setSpacing( KDialog::spacingHint() ); QVBoxLayout *lay2 = new QVBoxLayout( lay1 ); lay2->setSpacing( KDialog::spacingHint() ); QLabel* tmpQLabel; tmpQLabel = new QLabel( this ); lay2->addWidget(tmpQLabel); tmpQLabel->setText(i18n( "Comment:" ) ); text = new QLineEdit( this ); lay2->addWidget( text ); tmpQLabel = new QLabel( this ); lay2->addWidget( tmpQLabel ); tmpQLabel->setText( i18n( "Email:" ) ); l_mail = new QLineEdit( this ); lay2->addWidget( l_mail ); bold=new QCheckBox( i18n( "Bold" ),this ); lay2->addWidget( bold ); italic=new QCheckBox( i18n( "Italic" ),this ); lay2->addWidget( italic ); KSeparator* bar1 = new KSeparator( KSeparator::HLine, this ); bar1->setFixedHeight( 10 ); lay2->addWidget( bar1 ); text->setFocus(); } QString mailAnchor::apply() { if ( l_mail->text().isEmpty() || text->text().isEmpty() ) { KMessageBox::error( this, i18n( "Area text or mail is empty.") ); return QString(); } return createLink(); } QString mailAnchor::createLink() const { QString end_link; QString link; if (l_mail->text().find( "mailto:" )!=-1 ) link = "!text()+"\""+">"; else link = "!text()+"\""+">"; if ( bold->isChecked() && !italic->isChecked() ) { link+=""+text->text()+""; } else if ( !bold->isChecked() && italic->isChecked() ) { link+=""+text->text()+""; } else if ( bold->isChecked() && italic->isChecked() ) { link+=""+text->text()+""; } else { link+=text->text()+""; } return link; } fileAnchor::fileAnchor( KSpreadView* _view,QWidget *parent , char *name ) :QWidget ( parent,name) { m_pView=_view; QVBoxLayout *lay1 = new QVBoxLayout( this ); lay1->setMargin( KDialog::marginHint() ); lay1->setSpacing( KDialog::spacingHint() ); QVBoxLayout *lay2 = new QVBoxLayout( lay1); lay2->setSpacing( KDialog::marginHint() ); QLabel* tmpQLabel; tmpQLabel = new QLabel( this); lay2->addWidget(tmpQLabel); tmpQLabel->setText(i18n("Comment:")); text = new QLineEdit( this ); lay2->addWidget(text); tmpQLabel = new QLabel( this); lay2->addWidget(tmpQLabel); tmpQLabel->setText(i18n("Recent file:")); QComboBox * recentFile = new QComboBox( this ); lay2->addWidget(recentFile); tmpQLabel = new QLabel( this); lay2->addWidget(tmpQLabel); tmpQLabel->setText(i18n("File location:")); //l_file = new QLineEdit( this ); l_file = new KURLRequester( this ); lay2->addWidget(l_file); bold=new QCheckBox(i18n("Bold"),this); lay2->addWidget(bold); italic=new QCheckBox(i18n("Italic"),this); lay2->addWidget(italic); QStringList fileList = KRecentDocument::recentDocuments(); QStringList lst; lst <<""; for (QStringList::ConstIterator it = fileList.begin();it != fileList.end(); ++it) { KDesktopFile f(*it, true /* read only */); if ( !f.readURL().isEmpty()) lst.append( f.readURL()); } if ( lst.count()<= 1 ) { recentFile->clear(); recentFile->insertItem( i18n("No Entries") ); recentFile->setEnabled( false ); } else recentFile->insertStringList( lst); connect( recentFile , SIGNAL(highlighted ( const QString &)), this, SLOT( slotSelectRecentFile( const QString & ))); KSeparator* bar1 = new KSeparator( KSeparator::HLine, this); bar1->setFixedHeight( 10 ); lay2->addWidget( bar1 ); text->setFocus(); } QString fileAnchor::apply() { if ( l_file->lineEdit()->text().isEmpty() || text->text().isEmpty() ) { KMessageBox::error( this, i18n("Area text or mail is empty.") ); return QString(); } return createLink(); } void fileAnchor::slotSelectRecentFile( const QString &_file ) { l_file->lineEdit()->setText(_file ); } QString fileAnchor::createLink() const { QString end_link; QString link; QString tmpText=l_file->lineEdit()->text(); if (tmpText.find("file:/")!=-1) link = "!"; else if (tmpText.at(0)=='/') link = "!"; else link = "!"; if (bold->isChecked()&&!italic->isChecked()) { link+=""+text->text()+""; } else if (!bold->isChecked()&&italic->isChecked()) { link+=""+text->text()+""; } else if (bold->isChecked()&&italic->isChecked()) { link+=""+text->text()+""; } else { link+=text->text()+""; } return link; } cellAnchor::cellAnchor( KSpreadView* _view,QWidget *parent , char *name ) :QWidget ( parent,name) { m_pView=_view; QVBoxLayout *lay1 = new QVBoxLayout( this ); lay1->setMargin( KDialog::marginHint() ); lay1->setSpacing( KDialog::spacingHint() ); QVBoxLayout *lay2 = new QVBoxLayout( lay1); lay2->setSpacing( KDialog::spacingHint() ); QLabel* tmpQLabel; tmpQLabel = new QLabel( this); lay2->addWidget(tmpQLabel); tmpQLabel->setText(i18n("Comment:")); text = new QLineEdit( this ); lay2->addWidget(text); tmpQLabel = new QLabel( this); lay2->addWidget(tmpQLabel); tmpQLabel->setText(i18n("Cell:")); l_cell = new QLineEdit( this ); lay2->addWidget(l_cell); l_cell->setText( "A1" ); bold=new QCheckBox(i18n("Bold"),this); lay2->addWidget(bold); italic=new QCheckBox(i18n("Italic"),this); lay2->addWidget(italic); KSeparator* bar1 = new KSeparator( KSeparator::HLine, this); bar1->setFixedHeight( 10 ); lay2->addWidget( bar1 ); text->setFocus(); } QString cellAnchor::apply() { if ( l_cell->text().isEmpty() || text->text().isEmpty() ) { KMessageBox::error( this, i18n("Area text or cell is empty.") ); return QString(); } return createLink(); } QString cellAnchor::createLink() const { QString end_link; QString link; link = "!activeTable()->tableName()+"!"+l_cell->text().upper()+"\""+">"; if (bold->isChecked()&&!italic->isChecked()) { link+=""+text->text()+""; } else if (!bold->isChecked()&&italic->isChecked()) { link+=""+text->text()+""; } else if (bold->isChecked()&&italic->isChecked()) { link+=""+text->text()+""; } else { link+=text->text()+""; } return link; } #include "kspread_dlg_anchor.moc" diff --git a/kspread/dialogs/kspread_dlg_preference.cc b/kspread/dialogs/kspread_dlg_preference.cc index 438ad814618..4d112282ee2 100644 --- a/kspread/dialogs/kspread_dlg_preference.cc +++ b/kspread/dialogs/kspread_dlg_preference.cc @@ -1,933 +1,934 @@ /* This file is part of the KDE project Copyright (C) 2002-2004 Ariya Hidayat (C) 2002-2003 Norbert Andres (C) 2000-2003 Laurent Montel (C) 2002 John Dailey (C) 2002 Philipp Mueller (C) 2001-2002 David Faure (C) 2001 Werner Trobin (C) 2000 Bernd Johannes Wuebben 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 "kspread_dlg_preference.h" #include "kspread_sheet.h" #include "kspread_sheetprint.h" #include "kspread_doc.h" #include "kspread_canvas.h" #include "kspread_view.h" #include "kspread_locale.h" +#include "kspread_editors.h" #include #include #include #include #include #include #include #include KSpreadpreference::KSpreadpreference( KSpreadView* parent, const char* /*name*/) : KDialogBase(KDialogBase::IconList,i18n("Configure KSpread") , KDialogBase::Ok | KDialogBase::Cancel| KDialogBase::Default, KDialogBase::Ok) { m_pView=parent; connect(this, SIGNAL(okClicked()),this,SLOT(slotApply())); QVBox *page2=addVBoxPage(i18n("Locale Parameters"), QString::null,BarIcon("gohome",KIcon::SizeMedium)); _localePage=new parameterLocale(parent,page2 ); QVBox *page3=addVBoxPage(i18n("Interface"), QString::null,BarIcon("signature", KIcon::SizeMedium) ); _configure = new configure(parent,page3 ); QVBox * page4=addVBoxPage(i18n("Misc"), QString::null,BarIcon("misc",KIcon::SizeMedium) ); _miscParameter = new miscParameters(parent,page4 ); QVBox *page5=addVBoxPage(i18n("Color"), QString::null,BarIcon("colorize",KIcon::SizeMedium) ); _colorParameter=new colorParameters(parent,page5 ); QVBox *page6=addVBoxPage(i18n("Page Layout"), QString::null,BarIcon("edit",KIcon::SizeMedium) ); _layoutPage=new configureLayoutPage(parent,page6 ); QVBox *page7 = addVBoxPage( i18n("Spelling"), i18n("Spell Checker Behavior"), BarIcon("spellcheck", KIcon::SizeMedium) ); _spellPage=new configureSpellPage(parent,page7); } void KSpreadpreference::openPage(int flags) { if(flags & KS_LOCALE) showPage( 0 ); else if(flags & KS_INTERFACE) showPage( 1 ); else if(flags & KS_MISC) showPage( 2 ); else if(flags & KS_COLOR) showPage( 3 ); else if(flags & KS_LAYOUT) showPage( 4 ); else if(flags & KS_SPELLING) showPage( 5 ); } void KSpreadpreference::slotApply() { m_pView->doc()->emitBeginOperation( false ); _configure->apply(); _miscParameter->apply(); _colorParameter->apply(); _layoutPage->apply(); _spellPage->apply(); _localePage->apply(); m_pView->doc()->refreshInterface(); m_pView->slotUpdateView( m_pView->activeTable() ); } void KSpreadpreference::slotDefault() { switch(activePageIndex()) { case 1: _configure->slotDefault(); break; case 2: _miscParameter->slotDefault(); break; case 3: _colorParameter->slotDefault(); break; case 4: _layoutPage->slotDefault(); break; case 5: _spellPage->slotDefault(); break; default: break; } } parameterLocale::parameterLocale( KSpreadView* _view, QVBox *box , char *name ) :QObject ( box->parent(),name) { m_pView = _view; m_bUpdateLocale=false; QGroupBox* tmpQGroupBox = new QVGroupBox( i18n("Parameters"), box, "GroupBox" ); KLocale* locale=_view->doc()->locale(); m_language=new QLabel( tmpQGroupBox,"label"); m_language->setText( i18n("Language: %1").arg( locale->language() )); m_number=new QLabel( tmpQGroupBox,"label6"); m_number->setText( i18n("Number: %1").arg( locale->formatNumber(12.55) )); m_date=new QLabel( tmpQGroupBox,"label1"); m_date->setText( i18n("Date: %1").arg( locale->formatDate(QDate(2000,10,23)) )); m_shortDate=new QLabel( tmpQGroupBox,"label5"); m_shortDate->setText( i18n("Short date: %1").arg( locale->formatDate(QDate(2000,10,23),true) )); m_time=new QLabel( tmpQGroupBox,"label2"); m_time->setText( i18n("Time: %1").arg( locale->formatTime(QTime(15,10,53)) )); m_money=new QLabel( tmpQGroupBox,"label3"); m_money->setText( i18n("Money: %1").arg( locale->formatMoney(12.55) )); m_updateButton=new QPushButton ( i18n("&Update to Locale System"), tmpQGroupBox); connect(m_updateButton, SIGNAL(clicked()),this,SLOT(updateDefaultSystemConfig())); } void parameterLocale::apply() { if (m_bUpdateLocale) { m_pView->doc()->emitBeginOperation( false ); m_pView->doc()->refreshLocale(); m_pView->slotUpdateView( m_pView->activeTable() ); } } void parameterLocale::updateDefaultSystemConfig() { m_bUpdateLocale=true; static_cast(m_pView->doc()->locale())->defaultSystemConfig( ); KLocale* locale=m_pView->doc()->locale(); m_money->setText( i18n("Money: %1").arg( locale->formatMoney(12.55) )); m_time->setText( i18n("Time: %1").arg( locale->formatTime(QTime(15,10,53)) )); m_shortDate->setText( i18n("Short date: %1").arg( locale->formatDate(QDate(2000,10,23),true) )); m_date->setText( i18n("Date: %1").arg( locale->formatDate(QDate(2000,10,23)) )); m_number->setText( i18n("Number: %1").arg( locale->formatNumber(12.55) )); m_language->setText( i18n("Language: %1").arg( locale->language() )); } configure::configure( KSpreadView* _view, QVBox *box , char *name ) :QObject ( box->parent(),name) { m_pView = _view; bool vertical=true; bool horizontal=true; bool rowHeader=true; bool colHeader=true; bool tabbar=true; bool formulaBar=true; bool statusBar=true; m_oldBackupFile = true; QGroupBox* tmpQGroupBox = new QVGroupBox( i18n("Configuration"), box, "GroupBox" ); config = KSpreadFactory::global()->config(); int _page=1; oldRecent=10; oldAutoSaveValue=KoDocument::defaultAutoSave()/60; if( config->hasGroup("Parameters" )) { config->setGroup( "Parameters" ); _page=config->readNumEntry( "NbPage" ,1) ; horizontal=config->readBoolEntry("Horiz ScrollBar",true); vertical=config->readBoolEntry("Vert ScrollBar",true); colHeader=config->readBoolEntry("Column Header",true); rowHeader=config->readBoolEntry("Row Header",true); tabbar=config->readBoolEntry("Tabbar",true); formulaBar=config->readBoolEntry("Formula bar",true); statusBar=config->readBoolEntry("Status bar",true); oldRecent=config->readNumEntry( "NbRecentFile" ,10); oldAutoSaveValue=config->readNumEntry("AutoSave",KoDocument::defaultAutoSave()/60); m_oldBackupFile=config->readBoolEntry("BackupFile",m_oldBackupFile); } nbPage=new KIntNumInput(_page, tmpQGroupBox , 10); nbPage->setRange(1, 10, 1); nbPage->setLabel(i18n("Number of pages open at the &beginning:")); nbRecentFile=new KIntNumInput(oldRecent, tmpQGroupBox , 10); nbRecentFile->setRange(1, 20, 1); nbRecentFile->setLabel(i18n("&Number of recent files:")); autoSaveDelay=new KIntNumInput(oldAutoSaveValue, tmpQGroupBox , 10); autoSaveDelay->setRange(0, 60, 1); autoSaveDelay->setLabel(i18n("Au&tosave (min):")); autoSaveDelay->setSpecialValueText(i18n("No autosave")); autoSaveDelay->setSuffix(i18n("min")); m_createBackupFile = new QCheckBox( i18n("Create backup file"), tmpQGroupBox ); m_createBackupFile->setChecked( m_oldBackupFile ); showVScrollBar=new QCheckBox(i18n("Show &vertical scrollbar"),tmpQGroupBox); showVScrollBar->setChecked(vertical); showHScrollBar=new QCheckBox(i18n("Show &horizontal scrollbar"),tmpQGroupBox); showHScrollBar->setChecked(horizontal); showColHeader=new QCheckBox(i18n("Show c&olumn header"),tmpQGroupBox); showColHeader->setChecked(colHeader); showRowHeader=new QCheckBox(i18n("Show &row header"),tmpQGroupBox); showRowHeader->setChecked(rowHeader); showTabBar =new QCheckBox(i18n("Show ta&bs"),tmpQGroupBox); showTabBar->setChecked(tabbar); showFormulaBar =new QCheckBox(i18n("Sho&w formula toolbar"),tmpQGroupBox); showFormulaBar->setChecked(formulaBar); showStatusBar =new QCheckBox(i18n("Show stat&usbar"),tmpQGroupBox); showStatusBar->setChecked(statusBar); } void configure::slotDefault() { showHScrollBar->setChecked(true); showRowHeader->setChecked(true); showVScrollBar->setChecked(true); showColHeader->setChecked(true); showTabBar->setChecked(true); showFormulaBar->setChecked(true); showStatusBar->setChecked(true); nbPage->setValue(1); nbRecentFile->setValue(10); autoSaveDelay->setValue(KoDocument::defaultAutoSave()/60); m_createBackupFile->setChecked( true ); } void configure::apply() { m_pView->doc()->emitBeginOperation( false ); config->setGroup( "Parameters" ); config->writeEntry( "NbPage", nbPage->value()); KSpreadDoc *doc =m_pView->doc(); bool active=true; active=showHScrollBar->isChecked(); if( m_pView->horzScrollBar()->isVisible()!=active) { config->writeEntry( "Horiz ScrollBar",active); if( active) m_pView->horzScrollBar()->show(); else m_pView->horzScrollBar()->hide(); doc->setShowHorizontalScrollBar(active); } active=showVScrollBar->isChecked(); if( m_pView->vertScrollBar()->isVisible()!=active) { config->writeEntry( "Vert ScrollBar", active); if(active) m_pView->vertScrollBar()->show(); else m_pView->vertScrollBar()->hide(); doc->setShowVerticalScrollBar(active); } active=showColHeader->isChecked(); if( m_pView->hBorderWidget()->isVisible()!=active) { config->writeEntry( "Column Header", active); if( active) m_pView->hBorderWidget()->show(); else m_pView->hBorderWidget()->hide(); doc->setShowColHeader(active); } active=showRowHeader->isChecked(); if( m_pView->vBorderWidget()->isVisible()!=active) { config->writeEntry( "Row Header", active); if( active) m_pView->vBorderWidget()->show(); else m_pView->vBorderWidget()->hide(); doc->setShowRowHeader(active); } active=showTabBar->isChecked(); if(m_pView->tabBar()->isVisible()!=active) { config->writeEntry( "Tabbar", active); if(active) m_pView->tabBar()->show(); else m_pView->tabBar()->hide(); doc->setShowTabBar(active); } active=showFormulaBar->isChecked(); if(m_pView->posWidget()->isVisible()!=active) { config->writeEntry( "Formula bar",active); m_pView->editWidget()->showEditWidget(active); if(active) m_pView->posWidget()->show(); else m_pView->posWidget()->hide(); doc->setShowFormulaBar(active); } active=showStatusBar->isChecked(); config->writeEntry( "Status bar",active); m_pView->showStatusBar( active ); int val=nbRecentFile->value(); if( oldRecent!= val) { config->writeEntry( "NbRecentFile",val); m_pView->changeNbOfRecentFiles(val); } val=autoSaveDelay->value(); if(val!=oldAutoSaveValue) { config->writeEntry( "AutoSave", val ); doc->setAutoSave(val*60); } bool state =m_createBackupFile->isChecked(); if(state!=m_oldBackupFile) { config->writeEntry( "BackupFile", state ); doc->setBackupFile( state); m_oldBackupFile=state; } m_pView->slotUpdateView( m_pView->activeTable() ); } miscParameters::miscParameters( KSpreadView* _view,QVBox *box, char *name ) :QObject ( box->parent(),name) { m_pView = _view; QGroupBox* tmpQGroupBox = new QVGroupBox( i18n("Misc"), box, "GroupBox" ); config = KSpreadFactory::global()->config(); double _indent = 10.0; bool m_bMsgError=false; bool m_bCommentIndicator=true; if( config->hasGroup("Parameters" )) { config->setGroup( "Parameters" ); _indent = config->readNumEntry( "Indent" , 10.0 ) ; m_bMsgError=config->readBoolEntry( "Msg error" ,false) ; m_bCommentIndicator=config->readBoolEntry( "Comment Indicator",true); } QLabel *label=new QLabel(i18n("&Completion mode:"), tmpQGroupBox); typeCompletion=new QComboBox(tmpQGroupBox); label->setBuddy(typeCompletion); QStringList listType; listType+=i18n("None"); listType+=i18n("Manual"); listType+=i18n("Popup"); listType+=i18n("Automatic"); listType+=i18n("Semi-Automatic"); typeCompletion->insertStringList(listType); typeCompletion->setCurrentItem(0); comboChanged=false; connect(typeCompletion,SIGNAL(activated( const QString & )),this,SLOT(slotTextComboChanged(const QString &))); // valIndent = new KDoubleNumInput( _indent, tmpQGroupBox , 10.0 ); valIndent = new KDoubleNumInput( tmpQGroupBox ); valIndent->setRange( KoUnit::toUserValue( 0.0, _view->doc()->getUnit() ), KoUnit::toUserValue( 400.0, _view->doc()->getUnit() ), KoUnit::toUserValue( 10.0, _view->doc()->getUnit()) ); valIndent->setRange( 0.0, 100.0, 10.0 ); valIndent->setValue ( KoUnit::toUserValue( _indent, _view->doc()->getUnit() ) ); valIndent->setLabel(i18n("&Value of indent:")); label=new QLabel(i18n("&Press enter to move selection to:"), tmpQGroupBox); typeOfMove=new QComboBox( tmpQGroupBox); label->setBuddy(typeOfMove); listType.clear(); listType+=i18n("towards to", "Bottom"); listType+=i18n("towards to", "Top"); listType+=i18n("towards to", "Right"); listType+=i18n("towards to", "Left"); listType+=i18n("towards to", "Bottom, First Cell"); typeOfMove->insertStringList(listType); typeOfMove->setCurrentItem(0); msgError= new QCheckBox(i18n("&Show error message"),tmpQGroupBox); msgError->setChecked(m_bMsgError); label=new QLabel(i18n("&Method of calc:"), tmpQGroupBox); typeCalc=new QComboBox( tmpQGroupBox); label->setBuddy(typeCalc); QStringList listTypeCalc; listTypeCalc+=i18n("Sum"); listTypeCalc+=i18n("Min"); listTypeCalc+=i18n("Max"); listTypeCalc+=i18n("Average"); listTypeCalc+=i18n("Count"); listTypeCalc+=i18n("None"); typeCalc->insertStringList(listTypeCalc); typeCalc->setCurrentItem(0); commentIndicator=new QCheckBox(i18n("Show comment &indicator"),tmpQGroupBox); commentIndicator->setChecked(m_bCommentIndicator); initComboBox(); } void miscParameters::slotTextComboChanged(const QString &) { comboChanged=true; } void miscParameters::initComboBox() { KGlobalSettings::Completion tmpCompletion=KGlobalSettings::CompletionAuto; if( config->hasGroup("Parameters" )) { config->setGroup( "Parameters" ); tmpCompletion=( KGlobalSettings::Completion)config->readNumEntry( "Completion Mode" ,KGlobalSettings::CompletionAuto) ; config->writeEntry( "Completion Mode", (int)tmpCompletion); } switch(tmpCompletion ) { case KGlobalSettings::CompletionNone: typeCompletion->setCurrentItem(0); break; case KGlobalSettings::CompletionAuto: typeCompletion->setCurrentItem(3); break; case KGlobalSettings::CompletionMan: typeCompletion->setCurrentItem(4); break; case KGlobalSettings::CompletionShell: typeCompletion->setCurrentItem(1); break; case KGlobalSettings::CompletionPopup: typeCompletion->setCurrentItem(2); break; default : typeCompletion->setCurrentItem(0); break; } switch( m_pView->doc()->getMoveToValue( )) { case KSpread::Bottom: typeOfMove->setCurrentItem(0); break; case KSpread::Left: typeOfMove->setCurrentItem(3); break; case KSpread::Top: typeOfMove->setCurrentItem(1); break; case KSpread::Right: typeOfMove->setCurrentItem(2); break; case KSpread::BottomFirst: typeOfMove->setCurrentItem(4); break; default : typeOfMove->setCurrentItem(0); break; } switch( m_pView->doc()->getTypeOfCalc()) { case SumOfNumber: typeCalc->setCurrentItem(0); break; case Min: typeCalc->setCurrentItem(1); break; case Max: typeCalc->setCurrentItem(2); break; case Average: typeCalc->setCurrentItem(3); break; case Count: typeCalc->setCurrentItem(4); break; case NoneCalc: typeCalc->setCurrentItem(5); break; default : typeCalc->setCurrentItem(0); break; } } void miscParameters::slotDefault() { valIndent->setValue( 10.0 ); typeCompletion->setCurrentItem(3); typeOfMove->setCurrentItem(0); msgError->setChecked(false); typeCalc->setCurrentItem(0); commentIndicator->setChecked(true); } void miscParameters::apply() { config->setGroup( "Parameters" ); KGlobalSettings::Completion tmpCompletion=KGlobalSettings::CompletionNone; switch(typeCompletion->currentItem()) { case 0: tmpCompletion=KGlobalSettings::CompletionNone; break; case 1: tmpCompletion=KGlobalSettings::CompletionShell; break; case 2: tmpCompletion=KGlobalSettings::CompletionPopup; break; case 3: tmpCompletion=KGlobalSettings::CompletionAuto; break; case 4: tmpCompletion=KGlobalSettings::CompletionMan; break; } if(comboChanged) { m_pView->doc()->setCompletionMode(tmpCompletion); config->writeEntry( "Completion Mode", (int)tmpCompletion); } KSpread::MoveTo tmpMoveTo=KSpread::Bottom; switch(typeOfMove->currentItem()) { case 0: tmpMoveTo=KSpread::Bottom; break; case 1: tmpMoveTo=KSpread::Top; break; case 2: tmpMoveTo=KSpread::Right; break; case 3: tmpMoveTo=KSpread::Left; break; case 4: tmpMoveTo=KSpread::BottomFirst; break; } if(tmpMoveTo!=m_pView->doc()->getMoveToValue()) { m_pView->doc()->setMoveToValue(tmpMoveTo); config->writeEntry( "Move", (int)tmpMoveTo); } MethodOfCalc tmpMethodCalc=SumOfNumber; switch(typeCalc->currentItem()) { case 0: tmpMethodCalc =SumOfNumber; break; case 1: tmpMethodCalc=Min; break; case 2: tmpMethodCalc=Max; break; case 3: tmpMethodCalc=Average; break; case 4: tmpMethodCalc=Count; break; case 5: tmpMethodCalc=NoneCalc; break; } if(tmpMethodCalc!=m_pView->doc()->getTypeOfCalc()) { m_pView->doc()->setTypeOfCalc(tmpMethodCalc); config->writeEntry( "Method of Calc", (int)tmpMethodCalc); m_pView->resultOfCalc(); m_pView->initCalcMenu(); } double val = valIndent->value(); if( val != m_pView->doc()->getIndentValue() ) { m_pView->doc()->setIndentValue( val ); config->writeEntry( "Indent", val ); } bool active=msgError->isChecked(); if(active!=m_pView->doc()->getShowMessageError()) { m_pView->doc()->setShowMessageError( active); config->writeEntry( "Msg error" ,(int)active); } active=commentIndicator->isChecked(); if(active!=m_pView->doc()->getShowCommentIndicator()) { m_pView->doc()->setShowCommentIndicator( active); config->writeEntry( "Comment Indicator" ,(int)active); } } colorParameters::colorParameters( KSpreadView* _view,QVBox *box , char *name ) :QObject ( box->parent(),name) { m_pView = _view; config = KSpreadFactory::global()->config(); QColor _gridColor(Qt::lightGray); if ( config->hasGroup("KSpread Color" ) ) { config->setGroup( "KSpread Color" ); _gridColor = config->readColorEntry("GridColor",&_gridColor); } QGroupBox* tmpQGroupBox = new QVGroupBox( i18n("Color"), box, "GroupBox" ); QLabel *label = new QLabel(i18n("&Grid color:"), tmpQGroupBox,"label20" ); gridColor = new KColorButton( _gridColor, Qt::lightGray, tmpQGroupBox ); label->setBuddy(gridColor); QColor _pbColor(Qt::red); if ( config->hasGroup("KSpread Color" ) ) { config->setGroup( "KSpread Color" ); _pbColor = config->readColorEntry("PageBorderColor", &_pbColor); } QLabel * label2 = new QLabel( i18n("&Page borders:"), tmpQGroupBox, "label21" ); pageBorderColor = new KColorButton( _pbColor, Qt::red, tmpQGroupBox ); label2->setBuddy(pageBorderColor); } void colorParameters::apply() { QColor _col = gridColor->color(); if ( m_pView->doc()->gridColor() != _col ) { m_pView->doc()->setGridColor( _col ); config->setGroup( "KSpread Color" ); config->writeEntry( "GridColor", _col ); } QColor _pbColor = pageBorderColor->color(); if ( m_pView->doc()->pageBorderColor() != _pbColor ) { m_pView->doc()->changePageBorderColor( _pbColor ); config->setGroup( "KSpread Color" ); config->writeEntry( "PageBorderColor", _pbColor ); } } void colorParameters::slotDefault() { gridColor->setColor( lightGray ); pageBorderColor->setColor( red ); } configureLayoutPage::configureLayoutPage( KSpreadView* _view,QVBox *box , char *name ) :QObject ( box->parent(),name) { m_pView = _view; QGroupBox* tmpQGroupBox = new QGroupBox( 0, Qt::Vertical, i18n("Default Parameters"), box, "GroupBox" ); tmpQGroupBox->layout()->setSpacing(KDialog::spacingHint()); tmpQGroupBox->layout()->setMargin(KDialog::marginHint()); QGridLayout *grid1 = new QGridLayout(tmpQGroupBox->layout(),8,1); grid1->addRowSpacing( 0, KDialog::marginHint() ); grid1->setRowStretch( 7, 10 ); config = KSpreadFactory::global()->config(); QLabel *label=new QLabel(i18n("Default page &size:"), tmpQGroupBox); grid1->addWidget(label,0,0); defaultSizePage=new QComboBox( tmpQGroupBox); label->setBuddy(defaultSizePage); defaultSizePage->insertStringList( KoPageFormat::allFormats() ); defaultSizePage->setCurrentItem(1); grid1->addWidget(defaultSizePage,1,0); label=new QLabel(i18n("Default page &orientation:"), tmpQGroupBox); grid1->addWidget(label,2,0); defaultOrientationPage=new QComboBox( tmpQGroupBox); label->setBuddy(defaultOrientationPage); QStringList listType; listType+=i18n( "Portrait" ); listType+=i18n( "Landscape" ); defaultOrientationPage->insertStringList(listType); defaultOrientationPage->setCurrentItem(0); grid1->addWidget(defaultOrientationPage,3,0); label=new QLabel(tmpQGroupBox); label->setText(i18n("Default page &units:")); grid1->addWidget(label,4,0); defaultUnit=new QComboBox( tmpQGroupBox); label->setBuddy(defaultUnit); listType.clear(); listType=KoUnit::unitDescription( KoUnit::U_MM ); listType+=KoUnit::unitDescription( KoUnit::U_PT ); listType+=KoUnit::unitDescription( KoUnit::U_INCH ); listType+=KoUnit::unitDescription( KoUnit::U_CM ); listType+=KoUnit::unitDescription( KoUnit::U_PI ); listType+=KoUnit::unitDescription( KoUnit::U_CC ); listType+=KoUnit::unitDescription( KoUnit::U_DD ); listType+=KoUnit::unitDescription( KoUnit::U_DM ); defaultUnit->insertStringList(listType); defaultUnit->setCurrentItem(0); grid1->addWidget(defaultUnit,5,0); initCombo(); } void configureLayoutPage::slotDefault() { defaultSizePage->setCurrentItem(1); defaultOrientationPage->setCurrentItem(0); defaultUnit->setCurrentItem(0); } void configureLayoutPage::initCombo() { paper=1; orientation=0; unit=0; if( config->hasGroup("KSpread Page Layout" )) { config->setGroup( "KSpread Page Layout" ); paper=config->readNumEntry( "Default size page" ,1); orientation=config->readNumEntry( "Default orientation page" ,0); unit=config->readNumEntry( "Default unit page" ,0); } switch (m_pView->doc()->getUnit() ) { case KoUnit::U_MM: unit=0; break; case KoUnit::U_PT: unit=1; break; case KoUnit::U_INCH: unit=2; break; case KoUnit::U_CM: unit=3; break; case KoUnit::U_DM: unit = 4; break; case KoUnit::U_PI: unit = 5; break; case KoUnit::U_DD: unit = 6; break; case KoUnit::U_CC: unit = 7; break; default: unit=3; } defaultUnit->setCurrentItem(unit); defaultSizePage->setCurrentItem(paper); defaultOrientationPage->setCurrentItem(orientation); defaultUnit->setCurrentItem(unit); } void configureLayoutPage::apply() { m_pView->doc()->emitBeginOperation( false ); config->setGroup( "KSpread Page Layout" ); if( paper != defaultSizePage->currentItem() ) { unsigned int sizePage = defaultSizePage->currentItem(); config->writeEntry( "Default size page", sizePage ); m_pView->activeTable()->print()->setPaperFormat( (KoFormat)sizePage ); } if( orientation != defaultOrientationPage->currentItem() ) { unsigned int orientationPage = defaultOrientationPage->currentItem(); config->writeEntry( "Default orientation page", orientationPage ); m_pView->activeTable()->print()->setPaperOrientation( (KoOrientation)orientationPage ); } if( unit != defaultUnit->currentItem() ) { unsigned int unitPage = defaultUnit->currentItem(); config->writeEntry( "Default unit page", unitPage ); m_pView->doc()->setUnit( (KoUnit::Unit)unitPage ); } m_pView->slotUpdateView( m_pView->activeTable() ); } configureSpellPage::configureSpellPage( KSpreadView* _view,QVBox *box , char *name ) :QObject ( box->parent(),name) { m_pView = _view; config = KSpreadFactory::global()->config(); m_spellConfigWidget = new KSpellConfig( box, "spell_check",m_pView->doc()->getKSpellConfig()/*, false*/); dontCheckUpperWord = new QCheckBox( i18n("Do not check upper word"),box); dontCheckTitleCase = new QCheckBox( i18n("Do not check title case"),box); QWidget* spacer = new QWidget( box ); spacer->setSizePolicy( QSizePolicy( QSizePolicy::Minimum, QSizePolicy::Expanding ) ); if( config->hasGroup("KSpell kspread") ) { config->setGroup( "KSpell kspread" ); dontCheckUpperWord->setChecked(config->readBoolEntry("KSpell_dont_check_upper_word",false)); dontCheckTitleCase->setChecked(config->readBoolEntry("KSpell_dont_check_title_case",false)); } //m_spellConfigWidget->addIgnoreList( m_pView->doc()->spellListIgnoreAll() ); } void configureSpellPage::apply() { m_pView->doc()->emitBeginOperation( false ); KSpellConfig *_spellConfig = m_spellConfigWidget; config->setGroup( "KSpell kspread" ); config->writeEntry ("KSpell_NoRootAffix",(int) _spellConfig->noRootAffix ()); config->writeEntry ("KSpell_RunTogether", (int) _spellConfig->runTogether ()); config->writeEntry ("KSpell_Dictionary", _spellConfig->dictionary ()); config->writeEntry ("KSpell_DictFromList",(int) _spellConfig->dictFromList()); config->writeEntry ("KSpell_Encoding", (int) _spellConfig->encoding()); config->writeEntry ("KSpell_Client", _spellConfig->client()); // m_spellConfigWidget->saveDictionary(); KSpreadDoc* doc = m_pView->doc(); doc->setKSpellConfig(*_spellConfig); bool state=dontCheckUpperWord->isChecked(); config->writeEntry ("KSpell_dont_check_upper_word",(int)state); doc->setDontCheckUpperWord(state); state=dontCheckTitleCase->isChecked(); config->writeEntry("KSpell_dont_check_title_case",(int)state); doc->setDontCheckTitleCase(state); //m_pView->doc()->addIgnoreWordAllList( m_spellConfigWidget->ignoreList() ); m_pView->slotUpdateView( m_pView->activeTable() ); } void configureSpellPage::slotDefault() { //FIXME //m_spellConfigWidget->setDefault(); } #include "kspread_dlg_preference.moc" diff --git a/kspread/dialogs/kspread_dlg_series.cc b/kspread/dialogs/kspread_dlg_series.cc index c566f4358e7..3bb95f8e785 100644 --- a/kspread/dialogs/kspread_dlg_series.cc +++ b/kspread/dialogs/kspread_dlg_series.cc @@ -1,212 +1,212 @@ /* This file is part of the KDE project Copyright (C) 2002-2004 Ariya Hidayat (C) 2002-2003 Norbert Andres (C) 2002 John Dailey (C) 1999-2002 Laurent Montel (C) 2001-2002 Philipp Mueller (C) 2000-2001 Werner Trobin (C) 1998-2000 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 "kspread_dlg_series.h" -#include "kspread_canvas.h" #include "kspread_doc.h" +#include "kspread_editors.h" #include "kspread_sheet.h" #include "kspread_view.h" #include #include #include #include #include #include #include #include #include #include #include #include KSpreadSeriesDlg::KSpreadSeriesDlg( KSpreadView* parent, const char* name,const QPoint &_marker) : KDialogBase( parent, name,TRUE,i18n("Series"),Ok|Cancel ) { m_pView = parent; marker=_marker; QWidget *page = new QWidget( this ); setMainWidget(page); QBoxLayout *grid1 = new QHBoxLayout(page); grid1->setSpacing( spacingHint() ); QButtonGroup* gb1 = new QButtonGroup( 2, Qt::Vertical, i18n("Insert Values"), page ); column = new QRadioButton( i18n("Vertical"), gb1 ); QWhatsThis::add(column, i18n("Insert the series vertically, one below the other") ); row = new QRadioButton( i18n("Horizontal"), gb1 ); QWhatsThis::add(row, i18n("Insert the series horizontally, from left to right") ); column->setChecked(true); QButtonGroup* gb2 = new QButtonGroup( 2, Qt::Vertical, i18n("Type"), page ); linear = new QRadioButton( i18n("Linear (2,4,6,...)"), gb2 ); QWhatsThis::add(linear, i18n("Generate a series from 'start' to 'end' and for each step add " "the value provided in step. This creates a series where each value " "is 'step' larger than the value before it.") ); geometric = new QRadioButton( i18n("Geometric (2,4,8,...)"), gb2 ); QWhatsThis::add(geometric, i18n("Generate a series from 'start' to 'end' and for each step multiply " "the value with the value provided in step. Using a step of 5 produces a list like: " "5, 25, 125, 625 since 5 multiplied by 5 (step) equals 25, and that multiplied by 5 equals 125, " "which multiplied by the same step-value of 5 equals 625.") ); linear->setChecked(true); QGroupBox* gb = new QGroupBox( 1, Qt::Vertical, i18n("Parameters"), page ); QWidget *params = new QWidget( gb ); QGridLayout *params_layout = new QGridLayout( params, 3, 2 ); params_layout->setSpacing( spacingHint() ); params_layout->setAutoAdd( true ); new QLabel( i18n( "Start value:" ), params ); start=new KDoubleNumInput(-999999.999, 999999.99, 0.0, 1.0, 3, params); new QLabel( i18n( "Stop value:" ), params ); end=new KDoubleNumInput(-999999.999, 999999.99, 0.0, 1.0, 3, params); new QLabel( i18n( "Step value:" ), params ); step=new KDoubleNumInput(-999999.999, 999999.99, 0.0, 1.0, 3, params); grid1->addWidget(gb); grid1->addWidget(gb1); grid1->addWidget(gb2); start->setFocus(); connect( this, SIGNAL( okClicked() ), this, SLOT( slotOk() ) ); } void KSpreadSeriesDlg::slotOk() { Series mode=Column; //same as Vertical Series type=Linear; // same as Horizontal QString tmp; double dstep, dend, dstart; KSpreadSheet * m_pTable; m_pTable = m_pView->activeTable(); if(column->isChecked()) mode = Column; else if(row->isChecked()) mode = Row; if (linear->isChecked()) type = Linear; else if (geometric->isChecked()) type = Geometric; dstart = start->value(); dend= end->value(); dstep = step->value(); if ( type == Geometric ) { if ( dstart < 0 || dend < 0 ) { KMessageBox::error( this, i18n("End and start value must be positive.") ); return; } if ( dstart > dend && dstep >= 1) { KMessageBox::error( this, i18n("End value must be greater than the start value or the step must be less than '1'.") ); return; } if ( dstart == 0 || dend == 0 || dstep == 0) { KMessageBox::error( this, i18n("None of the Start, Stop or Step values may be equal to zero.") ); return; } if ( dstep == 1) { KMessageBox::error( this, i18n("Step value must be different from 1") ); return; } } if (dstep >= 0) { if (linear->isChecked() && dstep == 0) { KMessageBox::error( this, i18n("The step value must be greater than zero; " "otherwise, the linear series is infinite.") ); step->setFocus(); return; } /* else if (geometric->isChecked() && dstep <= 1) { KMessageBox::error( this, i18n("The step value must be greater than one; " "otherwise, the geometric series is infinite.") ); step->setFocus(); return; } */ else if ( type == Linear && dend < dstart ) { KMessageBox::error( this, i18n("If the start value is greater than the end value the step must be less than zero.") ); return; } } else if (type != Linear) { KMessageBox::error( this, i18n("Step is negative.") ); return; } else { if (dstart <= dend) { KMessageBox::error( this, i18n("If the step is negative, the start value must be greater then the end value.") ); return; } } // double val_end = QMAX(dend, dstart); // double val_start = QMIN(dend, dstart); m_pView->doc()->emitBeginOperation( false ); m_pTable->setSeries( marker, dstart, dend, dstep, mode, type ); KSpreadCell * cell = m_pTable->cellAt( marker.x(), marker.y() ); if ( cell->text() != 0L ) m_pView->editWidget()->setText( cell->text() ); else m_pView->editWidget()->setText( "" ); m_pView->slotUpdateView( m_pView->activeTable() ); accept(); } #include "kspread_dlg_series.moc" diff --git a/kspread/kspread_canvas.cc b/kspread/kspread_canvas.cc index 8b410026dc1..769fe7572c2 100644 --- a/kspread/kspread_canvas.cc +++ b/kspread/kspread_canvas.cc @@ -1,5761 +1,5349 @@ /* This file is part of the KDE project Copyright 1999-2002,2004 Laurent Montel Copyright 1999-2004 David Faure Copyright 2002-2004 Ariya Hidayat Copyright 2004 Meni Livne Copyright 2001-2003 Philipp Mueller Copyright 2002-2003 Norbert Andres Copyright 2003 Hamish Rodda Copyright 2003 Joseph Wenninger Copyright 2003 Lukas Tinkl Copyright 2000-2002 Werner Trobin Copyright 2002 Harri Porten Copyright 2002 John Dailey Copyright 2002 Daniel Naber Copyright 1999-2000 Torben Weis Copyright 1999-2000 Stephan Kulow Copyright 2000 Bernd Wuebben Copyright 2000 Wilco Greven Copyright 2000 Simon Hausmann Copyright 1999 Boris Wedl Copyright 1999 Reginald Stadlbauer 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 "kspread_util.h" #include "kspread_editors.h" #include "kspread_map.h" #include "kspread_undo.h" #include "kspread_canvas.h" #include "kspread_doc.h" #include "kspread_global.h" #include "kspread_view.h" #include "kspread_selection.h" #include "kspread_locale.h" #include #include #include #include #include #include #include #include #include -#include #include #include #include #include #include -KSpreadComboboxLocationEditWidget::KSpreadComboboxLocationEditWidget( QWidget * _parent, - KSpreadView * _view ) - : KComboBox( _parent, "KSpreadComboboxLocationEditWidget" ) -{ - m_locationWidget = new KSpreadLocationEditWidget( _parent, _view ); - setLineEdit( m_locationWidget ); - insertItem( "" ); - - QValueList::Iterator it; - QValueList area = _view->doc()->listArea(); - for ( it = area.begin(); it != area.end(); ++it ) - slotAddAreaName( (*it).ref_name); -} - -void KSpreadComboboxLocationEditWidget::slotAddAreaName( const QString &_name) -{ - insertItem( _name ); -} - -void KSpreadComboboxLocationEditWidget::slotRemoveAreaName( const QString &_name ) -{ - for ( int i = 0; istate() & ( Qt::AltButton | Qt::ControlButton ) ) - { - QLineEdit::keyPressEvent( _ev ); - // Never allow that keys are passed on to the parent. - _ev->accept(); - - return; - } - - // Handle some special keys here. Eve - switch( _ev->key() ) - { - case Key_Return: - case Key_Enter: - { - QString ltext = text(); - QString tmp = ltext.lower(); - QValueList::Iterator it; - QValueList area = m_pView->doc()->listArea(); - for ( it = area.begin(); it != area.end(); ++it ) - { - if ((*it).ref_name == tmp) - { - QString tmp = (*it).table_name; - tmp += "!"; - tmp += util_rangeName((*it).rect); - m_pView->canvasWidget()->gotoLocation( KSpreadRange(tmp, m_pView->doc()->map())); - return; - } - } - - // Set the cell component to uppercase: - // Table1!a1 -> Table1!A2 - int pos = ltext.find('!'); - if ( pos !=- 1 ) - tmp = ltext.left(pos)+ltext.mid(pos).upper(); - else - tmp = ltext.upper(); - - // Selection entered in location widget - if ( ltext.contains( ':' ) ) - m_pView->canvasWidget()->gotoLocation( KSpreadRange( tmp, m_pView->doc()->map() ) ); - // Location entered in location widget - else - { - KSpreadPoint point( tmp, m_pView->doc()->map()); - bool validName = true; - for (unsigned int i = 0; i < ltext.length(); ++i) - { - if (!ltext[i].isLetter()) - { - validName = false; - break; - } - } - if ( !point.isValid() && validName) - { - QRect rect( m_pView->selection() ); - KSpreadSheet * t = m_pView->activeTable(); - // set area name on current selection/cell - - m_pView->doc()->addAreaName(rect, ltext.lower(), - t->tableName()); - } - - if (!validName) - m_pView->canvasWidget()->gotoLocation( point ); - } - - // Set the focus back on the canvas. - m_pView->canvasWidget()->setFocus(); - _ev->accept(); - } - break; - // Escape pressed, restore original value - case Key_Escape: - // #### Torben says: This is duplicated code. Bad. - if ( m_pView->selectionInfo()->singleCellSelection() ) { - setText( KSpreadCell::columnName( m_pView->canvasWidget()->markerColumn() ) - + QString::number( m_pView->canvasWidget()->markerRow() ) ); - } else { - setText( KSpreadCell::columnName( m_pView->selection().left() ) - + QString::number( m_pView->selection().top() ) - + ":" - + KSpreadCell::columnName( m_pView->selection().right() ) - + QString::number( m_pView->selection().bottom() ) ); - } - m_pView->canvasWidget()->setFocus(); - _ev->accept(); - break; - default: - QLineEdit::keyPressEvent( _ev ); - // Never allow that keys are passed on to the parent. - _ev->accept(); - } -} - -/**************************************************************** - * - * KSpreadEditWidget - * The line-editor that appears above the table and allows to - * edit the cells content. - * - ****************************************************************/ - -KSpreadEditWidget::KSpreadEditWidget( QWidget *_parent, KSpreadCanvas *_canvas, - QButton *cancelButton, QButton *okButton ) - : QLineEdit( _parent, "KSpreadEditWidget" ) -{ - m_pCanvas = _canvas; - Q_ASSERT(m_pCanvas != NULL); - // Those buttons are created by the caller, so that they are inserted - // properly in the layout - but they are then managed here. - m_pCancelButton = cancelButton; - m_pOkButton = okButton; - - installEventFilter(m_pCanvas); - - if ( !m_pCanvas->doc()->isReadWrite() || !m_pCanvas->activeTable() ) - setEnabled( false ); - - QObject::connect( m_pCancelButton, SIGNAL( clicked() ), - this, SLOT( slotAbortEdit() ) ); - QObject::connect( m_pOkButton, SIGNAL( clicked() ), - this, SLOT( slotDoneEdit() ) ); - - setEditMode( false ); // disable buttons -} - -void KSpreadEditWidget::showEditWidget(bool _show) -{ - if (_show) - { - m_pCancelButton->show(); - m_pOkButton->show(); - show(); - } - else - { - m_pCancelButton->hide(); - m_pOkButton->hide(); - hide(); - } -} - -void KSpreadEditWidget::slotAbortEdit() -{ - m_pCanvas->deleteEditor( false /*discard changes*/ ); - // will take care of the buttons -} - -void KSpreadEditWidget::slotDoneEdit() -{ - m_pCanvas->deleteEditor( true /*keep changes*/ ); - // will take care of the buttons -} - -void KSpreadEditWidget::keyPressEvent ( QKeyEvent* _ev ) -{ - // Dont handle special keys and accelerators - if ( ( _ev->state() & ( Qt::AltButton | Qt::ControlButton ) ) - || ( _ev->state() & Qt::ShiftButton ) - || ( _ev->key() == Key_Shift ) - || ( _ev->key() == Key_Control ) ) - { - QLineEdit::keyPressEvent( _ev ); - _ev->accept(); - return; - } - - if ( !m_pCanvas->doc()->isReadWrite() ) - return; - - if ( !m_pCanvas->editor() ) - { - // Start editing the current cell - m_pCanvas->createEditor( KSpreadCanvas::CellEditor,false ); - } - KSpreadTextEditor * cellEditor = (KSpreadTextEditor*) m_pCanvas->editor(); - - switch ( _ev->key() ) - { - case Key_Down: - case Key_Up: - case Key_Return: - case Key_Enter: - cellEditor->setText( text()); - // Don't allow to start a chooser when pressing the arrow keys - // in this widget, since only up and down would work anyway. - // This is why we call slotDoneEdit now, instead of sending - // to the canvas. - //QApplication::sendEvent( m_pCanvas, _ev ); - slotDoneEdit(); - m_pCanvas->view()->updateEditWidget(); - _ev->accept(); - break; - case Key_F2: - cellEditor->setFocus(); - cellEditor->setText( text()); - cellEditor->setCursorPosition(cursorPosition()); - break; - default: - - QLineEdit::keyPressEvent( _ev ); - - setFocus(); - cellEditor->blockCheckChoose( TRUE ); - cellEditor->setText( text() ); - cellEditor->blockCheckChoose( FALSE ); - cellEditor->setCursorPosition( cursorPosition() ); - } -} - -void KSpreadEditWidget::setEditMode( bool mode ) -{ - m_pCancelButton->setEnabled(mode); - m_pOkButton->setEnabled(mode); -} - -void KSpreadEditWidget::focusOutEvent( QFocusEvent* ev ) -{ - //kdDebug(36001) << "EditWidget lost focus" << endl; - // See comment about setLastEditorWithFocus - m_pCanvas->setLastEditorWithFocus( KSpreadCanvas::EditWidget ); - - QLineEdit::focusOutEvent( ev ); -} - -void KSpreadEditWidget::setText( const QString& t ) -{ - if ( t == text() ) // Why this? (David) - return; - - QLineEdit::setText( t ); -} - /**************************************************************** * * KSpreadCanvas * ****************************************************************/ KSpreadCanvas::KSpreadCanvas( QWidget *_parent, KSpreadView *_view, KSpreadDoc* _doc ) : QWidget( _parent, "", /*WNorthWestGravity*/ WStaticContents| WResizeNoErase | WRepaintNoErase ), m_dragStart( -1, -1 ), m_dragging( false ) { length_namecell = 0; m_chooseStartTable = NULL; m_pEditor = 0; m_bChoose = FALSE; m_validationInfo = 0L; QWidget::setFocusPolicy( QWidget::StrongFocus ); m_defaultGridPen.setColor( lightGray ); m_defaultGridPen.setWidth( 1 ); m_defaultGridPen.setStyle( SolidLine ); m_dXOffset = 0.0; m_dYOffset = 0.0; m_pView = _view; m_pDoc = _doc; // m_eAction = DefaultAction; m_eMouseAction = NoAction; m_bGeometryStarted = false; // m_bEditDirtyFlag = false; //Now built afterwards(David) //m_pEditWidget = m_pView->editWidget(); m_pPosWidget = m_pView->posWidget(); setBackgroundMode( PaletteBase ); setMouseTracking( TRUE ); m_bMousePressed = false; m_scrollTimer = new QTimer( this ); connect (m_scrollTimer, SIGNAL( timeout() ), this, SLOT( doAutoScroll() ) ); choose_visible = false; setFocus(); installEventFilter( this ); (void)new KSpreadToolTip( this ); setAcceptDrops( true ); } KSpreadCanvas::~KSpreadCanvas() { delete m_scrollTimer; delete m_validationInfo; } bool KSpreadCanvas::eventFilter( QObject *o, QEvent *e ) { /* this canvas event filter acts on events sent to the line edit as well as events to this filter itself. */ if ( !o || !e ) return TRUE; switch ( e->type() ) { case QEvent::KeyPress: { QKeyEvent * keyev = static_cast(e); if (keyev->key()==Key_Tab) { keyPressEvent ( keyev ); return true; } } default: break; } return false; } bool KSpreadCanvas::focusNextPrevChild( bool ) { return TRUE; // Don't allow to go out of the canvas widget by pressing "Tab" } KSpreadSelection* KSpreadCanvas::selectionInfo() const { return m_pView->selectionInfo(); } QRect KSpreadCanvas::selection() const { return m_pView->selectionInfo()->selection(); } QPoint KSpreadCanvas::marker() const { return m_pView->selectionInfo()->marker(); } int KSpreadCanvas::markerColumn() const { return m_pView->selectionInfo()->marker().x(); } int KSpreadCanvas::markerRow() const { return m_pView->selectionInfo()->marker().y(); } double KSpreadCanvas::zoom() const { return m_pView->zoom(); } void KSpreadCanvas::startChoose() { if ( m_bChoose ) return; updateChooseRect(QPoint(0,0), QPoint(0,0)); // It is important to enable this AFTER we set the rect! m_bChoose = TRUE; m_chooseStartTable = activeTable(); } void KSpreadCanvas::startChoose( const QRect& rect ) { if (m_bChoose) return; updateChooseRect(rect.bottomRight(), rect.topLeft()); // It is important to enable this AFTER we set the rect! m_bChoose = TRUE; m_chooseStartTable = activeTable(); } void KSpreadCanvas::endChoose() { if ( !m_bChoose ) return; updateChooseRect(QPoint(0,0), QPoint(0,0)); length_namecell = 0; m_bChoose = FALSE; KSpreadSheet *table=m_pView->doc()->map()->findTable(m_chooseStartTable->tableName()); if (table) m_pView->setActiveTable(table); m_chooseStartTable = 0; } KSpreadHBorder* KSpreadCanvas::hBorderWidget() const { return m_pView->hBorderWidget(); } KSpreadVBorder* KSpreadCanvas::vBorderWidget() const { return m_pView->vBorderWidget(); } QScrollBar* KSpreadCanvas::horzScrollBar() const { return m_pView->horzScrollBar(); } QScrollBar* KSpreadCanvas::vertScrollBar() const { return m_pView->vertScrollBar(); } KSpreadSheet* KSpreadCanvas::findTable( const QString& _name ) const { return m_pDoc->map()->findTable( _name ); } KSpreadSheet* KSpreadCanvas::activeTable() const { return m_pView->activeTable(); } bool KSpreadCanvas::gotoLocation( const KSpreadRange & _range ) { if ( !_range.isValid() ) { KMessageBox::error( this, i18n( "Invalid cell reference" ) ); return false; } KSpreadSheet * table = activeTable(); if ( _range.isTableKnown() ) table = _range.table; if ( !table ) { KMessageBox::error( this, i18n("Unknown table name %1" ).arg( _range.tableName ) ); return false; } gotoLocation( _range.range.topLeft(), table, false ); gotoLocation( _range.range.bottomRight(), table, true ); return true; } bool KSpreadCanvas::gotoLocation( const KSpreadPoint& _cell ) { if ( !_cell.isValid() ) { KMessageBox::error( this, i18n("Invalid cell reference") ); return false; } KSpreadSheet* table = activeTable(); if ( _cell.isTableKnown() ) table = _cell.table; if ( !table ) { KMessageBox::error( this, i18n("Unknown table name %1").arg( _cell.tableName ) ); return false; } gotoLocation( _cell.pos, table ); return true; } void KSpreadCanvas::gotoLocation( QPoint const & location, KSpreadSheet* table, bool extendSelection) { // kdDebug() << "GotoLocation: " << location.x() << ", " << location.x() << endl; if ( table && (table != activeTable() )) m_pView->setActiveTable(table); else table = activeTable(); if (extendSelection) { extendCurrentSelection(location); } else { QPoint topLeft(location); KSpreadCell* cell = table->cellAt(location); if ( cell->isObscured() && cell->isObscuringForced() ) { cell = cell->obscuringCells().first(); topLeft = QPoint(cell->column(), cell->row()); } if (m_bChoose) { updateChooseRect(topLeft, topLeft); if( m_pEditor ) { if( m_chooseStartTable != table ) m_pEditor->hide(); else m_pEditor->show(); } } else { /* anchor and marker should be on the same cell here */ selectionInfo()->setSelection(topLeft, topLeft, table); } } scrollToCell(location); // Perhaps the user is entering a value in the cell. // In this case we may not touch the EditWidget if ( !m_pEditor && !m_bChoose ) m_pView->updateEditWidgetOnPress(); if ( selectionInfo()->singleCellSelection() ) { int col = selectionInfo()->marker().x(); int row = selectionInfo()->marker().y(); KSpreadCell * cell = table->cellAt( col,row ); if ( cell && cell->getValidity(0) && cell->getValidity()->displayValidationInformation) { QString title = cell->getValidity(0)->titleInfo; QString message = cell->getValidity(0)->messageInfo; if ( title.isEmpty() && message.isEmpty() ) return; if ( !m_validationInfo ) m_validationInfo = new QLabel( this ); kdDebug()<<" display info validation\n"; double u = cell->dblWidth( col ); double v = cell->dblHeight( row ); double xpos = table->dblColumnPos( markerColumn() ) - xOffset(); double ypos = table->dblRowPos( markerRow() ) - yOffset(); // Special treatment for obscured cells. if ( cell->isObscured() && cell->isObscuringForced() ) { cell = cell->obscuringCells().first(); int moveX = cell->column(); int moveY = cell->row(); // Use the obscuring cells dimensions u = cell->dblWidth( moveX ); v = cell->dblHeight( moveY ); xpos = table->dblColumnPos( moveX ); ypos = table->dblRowPos( moveY ); } //m_validationInfo->setGeometry( 3, y + 3, len + 2, hei + 2 ); m_validationInfo->setAlignment( Qt::AlignVCenter ); QPainter painter; painter.begin( this ); int len = 0; int hei = 0; QString resultText; if ( !title.isEmpty() ) { len = painter.fontMetrics().width( title ); hei = painter.fontMetrics().height(); resultText = title + "\n"; } if ( !message.isEmpty() ) { int i = 0; int pos = 0; QString t; do { i = message.find( "\n", pos ); if ( i == -1 ) t = message.mid( pos, message.length() - pos ); else { t = message.mid( pos, i - pos ); pos = i + 1; } hei += painter.fontMetrics().height(); len = QMAX( len, painter.fontMetrics().width( t ) ); } while ( i != -1 ); resultText += message; } painter.end(); m_validationInfo->setText( resultText ); KoRect unzoomedMarker( xpos - xOffset()+u, ypos - yOffset()+v, len, hei ); QRect marker( doc()->zoomRect( unzoomedMarker ) ); m_validationInfo->setGeometry( marker ); m_validationInfo->show(); } else { delete m_validationInfo; m_validationInfo = 0L; } } else { delete m_validationInfo; m_validationInfo = 0L; } updatePosWidget(); } void KSpreadCanvas::scrollToCell(QPoint location) { KSpreadSheet* table = activeTable(); if (table == NULL) return; /* we don't need this cell ptr, but this call is necessary to update the scroll bar correctly. I don't like having that as part of the cellAt function but I suppose that's ok for now. */ KSpreadCell* cell = table->cellAt(location.x(), location.y(), true); Q_UNUSED(cell); double unzoomedWidth = doc()->unzoomItX( width() ); double unzoomedHeight = doc()->unzoomItY( height() ); double xpos; if ( table->layoutDirection()==KSpreadSheet::RightToLeft ) xpos = unzoomedWidth - table->dblColumnPos( location.x() ) + xOffset(); else xpos = table->dblColumnPos( location.x() ) - xOffset(); double ypos = table->dblRowPos( location.y() ) - yOffset(); double minY = 40.0; double maxY = unzoomedHeight - 40.0; //kdDebug(36001) << "KSpreadCanvas::gotoLocation : height=" << height() << endl; //kdDebug(36001) << "KSpreadCanvas::gotoLocation : width=" << width() << endl; if ( table->layoutDirection()==KSpreadSheet::RightToLeft ) { double minX = unzoomedWidth - 100.0; // less than that, we scroll double maxX = 100.0; // more than that, we scroll // kdDebug() << "rtl2: XPos: " << xpos << ", min: " << minX << ", maxX: " << maxX << ", Offset: " << xOffset() << endl; // do we need to scroll left if ( xpos > minX ) horzScrollBar()->setValue( horzScrollBar()->maxValue() - doc()->zoomItX( xOffset() - xpos + minX ) ); //do we need to scroll right else if ( xpos < maxX ) { double horzScrollBarValue = xOffset() - xpos + maxX; double horzScrollBarValueMax = table->sizeMaxX() - unzoomedWidth; //We don't want to display any area > KS_colMax widths if ( horzScrollBarValue > horzScrollBarValueMax ) horzScrollBarValue = horzScrollBarValueMax; horzScrollBar()->setValue( horzScrollBar()->maxValue() - doc()->zoomItX( horzScrollBarValue ) ); } } else { double minX = 100.0; // less than that, we scroll double maxX = unzoomedWidth - 100.0; // more than that, we scroll // kdDebug() << "ltr: XPos: " << xpos << ", min: " << minX << ", maxX: " << maxX << endl; // do we need to scroll left if ( xpos < minX ) horzScrollBar()->setValue( doc()->zoomItX( xOffset() + xpos - minX ) ); //do we need to scroll right else if ( xpos > maxX ) { double horzScrollBarValue = xOffset() + xpos - maxX; double horzScrollBarValueMax = table->sizeMaxX() - unzoomedWidth; //We don't want to display any area > KS_colMax widths if ( horzScrollBarValue > horzScrollBarValueMax ) horzScrollBarValue = horzScrollBarValueMax; horzScrollBar()->setValue( doc()->zoomItX( horzScrollBarValue ) ); } } // do we need to scroll up if ( ypos < minY ) vertScrollBar()->setValue( doc()->zoomItY( yOffset() + ypos - minY ) ); // do we need to scroll down else if ( ypos > maxY ) { double vertScrollBarValue = yOffset() + ypos - maxY; double vertScrollBarValueMax = table->sizeMaxY() - unzoomedHeight; //We don't want to display any area > KS_rowMax heights if ( vertScrollBarValue > vertScrollBarValueMax ) vertScrollBarValue = vertScrollBarValueMax; vertScrollBar()->setValue( doc()->zoomItY( vertScrollBarValue ) ); } } void KSpreadCanvas::slotScrollHorz( int _value ) { KSpreadSheet * sheet = activeTable(); if ( sheet == 0L ) return; if ( sheet->layoutDirection()==KSpreadSheet::RightToLeft ) _value = horzScrollBar()->maxValue() - _value; double unzoomedValue = doc()->unzoomItX( _value ); double dwidth = doc()->unzoomItX( width() ); doc()->emitBeginOperation(false); if ( unzoomedValue < 0.0 ) { unzoomedValue = 0.0; kdDebug (36001) << "KSpreadCanvas::slotScrollHorz: value out of range (unzoomedValue: " << unzoomedValue << ")" << endl; } double xpos = sheet->dblColumnPos( QMIN( KS_colMax, m_pView->activeTable()->maxColumn()+10 ) ) - m_dXOffset; if ( unzoomedValue > ( xpos + m_dXOffset ) ) unzoomedValue = xpos + m_dXOffset; sheet->enableScrollBarUpdates( false ); // Relative movement int dx = doc()->zoomItX( m_dXOffset - unzoomedValue ); /* what cells will need painted now? */ QRect area = visibleCells(); double tmp; if (dx > 0) { area.setRight( area.left() ); area.setLeft( sheet->leftColumn( unzoomedValue, tmp ) ); } else { area.setLeft( area.right() ); area.setRight( sheet->rightColumn( dwidth + unzoomedValue ) ); } sheet->setRegionPaintDirty(area); // New absolute position m_dXOffset = unzoomedValue; if ( sheet->layoutDirection()==KSpreadSheet::RightToLeft ) dx = -dx; scroll( dx, 0 ); hBorderWidget()->scroll( dx, 0 ); sheet->enableScrollBarUpdates( true ); doc()->emitEndOperation( sheet->visibleRect( this ) ); } void KSpreadCanvas::slotScrollVert( int _value ) { if ( activeTable() == 0L ) return; doc()->emitBeginOperation(false); double unzoomedValue = doc()->unzoomItY( _value ); if ( unzoomedValue < 0 ) { unzoomedValue = 0; kdDebug (36001) << "KSpreadCanvas::slotScrollVert: value out of range (unzoomedValue: " << unzoomedValue << ")" << endl; } double ypos = activeTable()->dblRowPos( QMIN( KS_rowMax, m_pView->activeTable()->maxRow()+10 ) ); if ( unzoomedValue > ypos ) unzoomedValue = ypos; activeTable()->enableScrollBarUpdates( false ); // Relative movement int dy = doc()->zoomItY( m_dYOffset - unzoomedValue ); /* what cells will need painted now? */ QRect area = visibleCells(); double tmp; if (dy > 0) { area.setBottom(area.top()); area.setTop(activeTable()->topRow(unzoomedValue, tmp)); } else { area.setTop(area.bottom()); area.setBottom(activeTable()->bottomRow(doc()->unzoomItY(height()) + unzoomedValue)); } activeTable()->setRegionPaintDirty( area ); // New absolute position m_dYOffset = unzoomedValue; scroll( 0, dy ); vBorderWidget()->scroll( 0, dy ); activeTable()->enableScrollBarUpdates( true ); doc()->emitEndOperation( activeTable()->visibleRect( this ) ); } void KSpreadCanvas::slotMaxColumn( int _max_column ) { int oldValue = horzScrollBar()->maxValue() - horzScrollBar()->value(); double xpos = activeTable()->dblColumnPos( QMIN( KS_colMax, _max_column + 10 ) ) - xOffset(); double unzoomWidth = doc()->unzoomItX( width() ); //Don't go beyond the maximum column range (KS_colMax) double sizeMaxX = activeTable()->sizeMaxX(); if ( xpos > sizeMaxX - xOffset() - unzoomWidth ) xpos = sizeMaxX - xOffset() - unzoomWidth; horzScrollBar()->setRange( 0, doc()->zoomItX( xpos + xOffset() ) ); if ( activeTable()->layoutDirection()==KSpreadSheet::RightToLeft ) horzScrollBar()->setValue( horzScrollBar()->maxValue() - oldValue ); } void KSpreadCanvas::slotMaxRow( int _max_row ) { double ypos = activeTable()->dblRowPos( QMIN( KS_rowMax, _max_row + 10 ) ) - yOffset(); double unzoomHeight = doc()->unzoomItY( height() ); //Don't go beyond the maximum row range (KS_rowMax) double sizeMaxY = activeTable()->sizeMaxY(); if ( ypos > sizeMaxY - yOffset() - unzoomHeight ) ypos = sizeMaxY - yOffset() - unzoomHeight; vertScrollBar()->setRange( 0, doc()->zoomItY( ypos + yOffset() ) ); } void KSpreadCanvas::mouseMoveEvent( QMouseEvent * _ev ) { // Dont allow modifications if document is readonly. Selecting is no modification if ( (!m_pView->koDocument()->isReadWrite()) && (m_eMouseAction!=Mark)) return; if ( m_dragging ) return; if ( m_dragStart.x() != -1 ) { QPoint p ( (int) _ev->pos().x() + (int) xOffset(), (int) _ev->pos().y() + (int) yOffset() ); if ( ( m_dragStart - p ).manhattanLength() > 4 ) { m_dragging = true; startTheDrag(); m_dragStart.setX( -1 ); } m_dragging = false; return; } // Working on this table ? KSpreadSheet *table = activeTable(); if ( !table ) return; // Special handling for choose mode. if ( m_bChoose ) { chooseMouseMoveEvent( _ev ); return; } double dwidth = doc()->unzoomItX( width() ); double ev_PosX; if ( table->layoutDirection()==KSpreadSheet::RightToLeft ) ev_PosX = dwidth - doc()->unzoomItX( _ev->pos().x() ) + xOffset(); else ev_PosX = doc()->unzoomItX( _ev->pos().x() ) + xOffset(); double ev_PosY = doc()->unzoomItY( _ev->pos().y() ) + yOffset(); double xpos; double ypos; int col = table->leftColumn( ev_PosX, xpos ); int row = table->topRow( ev_PosY, ypos ); if ( col > KS_colMax || row > KS_rowMax ) { return; } QRect rct( selectionInfo()->selection() ); QRect r1; QRect r2; double lx = table->dblColumnPos( rct.left() ); double rx = table->dblColumnPos( rct.right() + 1 ); double ty = table->dblRowPos( rct.top() ); double by = table->dblRowPos( rct.bottom() + 1 ); r1.setLeft( (int) (lx - 1) ); r1.setTop( (int) (ty - 1) ); r1.setRight( (int) (rx + 1) ); r1.setBottom( (int) (by + 1) ); r2.setLeft( (int) (lx + 1) ); r2.setTop( (int) (ty + 1) ); r2.setRight( (int) (rx - 1) ); r2.setBottom( (int) (by - 1) ); QRect selectionHandle = m_pView->selectionInfo()->selectionHandleArea(); // Test whether the mouse is over some anchor { KSpreadCell *cell = table->visibleCellAt( col, row ); QString anchor; if ( table->layoutDirection()==KSpreadSheet::RightToLeft ) anchor = cell->testAnchor( doc()->zoomItX( cell->dblWidth() - ev_PosX + xpos ), doc()->zoomItY( ev_PosY - ypos ) ); else anchor = cell->testAnchor( doc()->zoomItX( ev_PosX - xpos ), doc()->zoomItY( ev_PosY - ypos ) ); if ( !anchor.isEmpty() && anchor != m_strAnchor ) setCursor( KCursor::handCursor() ); m_strAnchor = anchor; } if ( selectionHandle.contains( QPoint( doc()->zoomItX( ev_PosX ), doc()->zoomItY( ev_PosY ) ) ) ) { //If the cursor is over the hanlde, than it might be already on the next cell. //Recalculate the cell! col = table->leftColumn( ev_PosX - doc()->unzoomItX( 2 ), xpos ); row = table->topRow( ev_PosY - doc()->unzoomItY( 2 ), ypos ); if ( !table->isProtected() ) { if ( table->layoutDirection()==KSpreadSheet::RightToLeft ) setCursor( sizeBDiagCursor ); else setCursor( sizeFDiagCursor ); } } else if ( !m_strAnchor.isEmpty() ) { if ( !table->isProtected() ) setCursor( KCursor::handCursor() ); } else if ( r1.contains( QPoint( (int) ev_PosX, (int) ev_PosY ) ) && !r2.contains( QPoint( (int) ev_PosX, (int) ev_PosY ) ) ) setCursor( KCursor::handCursor() ); else setCursor( arrowCursor ); // No marking, selecting etc. in progess? Then quit here. if ( m_eMouseAction == NoAction ) return; // Set the new extent of the selection gotoLocation( QPoint( col, row ), table, true ); } void KSpreadCanvas::mouseReleaseEvent( QMouseEvent* _ev ) { if ( m_scrollTimer->isActive() ) m_scrollTimer->stop(); m_bMousePressed = false; if ( m_bChoose ) { chooseMouseReleaseEvent( _ev ); return; } KSpreadSheet *table = activeTable(); if ( !table ) return; KSpreadSelection* selectionInfo = m_pView->selectionInfo(); QRect s( selection() ); if ( selectionInfo->singleCellSelection() ) { KSpreadCell* cell = table->cellAt( selectionInfo->marker() ); cell->clicked( this ); } // The user started the drag in the lower right corner of the marker ? if ( m_eMouseAction == ResizeCell && !table->isProtected() ) { QPoint selectionAnchor = selectionInfo->selectionAnchor(); int x = selectionAnchor.x(); int y = selectionAnchor.y(); if ( x > s.left()) x = s.left(); if ( y > s.top() ) y = s.top(); KSpreadCell *cell = table->nonDefaultCell( x, y ); if ( !m_pView->doc()->undoLocked() ) { KSpreadUndoMergedCell *undo = new KSpreadUndoMergedCell( m_pView->doc(), table, x, y, cell->extraXCells(), cell->extraYCells() ); m_pView->doc()->addCommand( undo ); } cell->forceExtraCells( x, y, abs( s.right() - s.left() ), abs( s.bottom() - s.top() ) ); m_pView->updateEditWidget(); if ( table->getAutoCalc() ) table->recalc(); } else if ( m_eMouseAction == AutoFill && !table->isProtected() ) { QRect dest = s; table->autofill( m_rctAutoFillSrc, dest ); m_pView->updateEditWidget(); } // The user started the drag in the middle of a cell ? else if ( m_eMouseAction == Mark ) { m_pView->updateEditWidget(); } m_eMouseAction = NoAction; m_dragging = false; m_dragStart.setX( -1 ); } void KSpreadCanvas::processClickSelectionHandle( QMouseEvent *event ) { // Auto fill ? That is done using the left mouse button. if ( event->button() == LeftButton ) { m_eMouseAction = AutoFill; m_rctAutoFillSrc = selection(); } // Resize a cell (done with the right mouse button) ? // But for that to work there must not be a selection. else if ( event->button() == MidButton && selectionInfo()->singleCellSelection()) { m_eMouseAction = ResizeCell; } return; } void KSpreadCanvas::extendCurrentSelection( QPoint cell ) { KSpreadSheet* table = activeTable(); QPoint chooseAnchor = selectionInfo()->getChooseAnchor(); // KSpreadCell* destinationCell = table->cellAt(cell); if ( m_bChoose ) { if ( chooseAnchor.x() == 0 ) { updateChooseRect( cell, cell ); } else { updateChooseRect( cell, chooseAnchor ); } } else { /* the selection simply becomes a box with the anchor and given cell as opposite corners */ selectionInfo()->setSelection( cell, selectionInfo()->selectionAnchor(), table ); } } void KSpreadCanvas::processLeftClickAnchor() { bool isLink = (m_strAnchor.find("http://") == 0 || m_strAnchor.find("mailto:") == 0 || m_strAnchor.find("ftp://") == 0 || m_strAnchor.find("file:") == 0 ); bool isLocalLink = (m_strAnchor.find("file:") == 0); if ( isLink ) { QString question = i18n("Do you want to open this link to '%1'?\n").arg(m_strAnchor); if ( isLocalLink ) { question += i18n("Note that opening a link to a local file may " "compromise your system's security."); } // this will also start local programs, so adding a "don't warn again" // checkbox will probably be too dangerous int choice = KMessageBox::warningYesNo(this, question, i18n("Open Link?")); if ( choice == KMessageBox::Yes ) { (void) new KRun( m_strAnchor ); } } else { gotoLocation( KSpreadPoint( m_strAnchor, m_pDoc->map() ) ); } } void KSpreadCanvas::mousePressEvent( QMouseEvent * _ev ) { if ( _ev->button() == LeftButton ) m_bMousePressed = true; // If in choose mode, we handle the mouse differently. if ( m_bChoose ) { chooseMousePressEvent( _ev ); return; } KSpreadSheet *table = activeTable(); if ( !table ) return; double dwidth = 0.0; double ev_PosX; if ( table->layoutDirection()==KSpreadSheet::RightToLeft ) { dwidth = doc()->unzoomItX( width() ); ev_PosX = dwidth - doc()->unzoomItX( _ev->pos().x() ) + xOffset(); } else ev_PosX = doc()->unzoomItX( _ev->pos().x() ) + xOffset(); double ev_PosY = doc()->unzoomItY( _ev->pos().y() ) + yOffset(); // We were editing a cell -> save value and get out of editing mode if ( m_pEditor ) { deleteEditor( true ); // save changes } m_scrollTimer->start( 50 ); // Remember current values. QRect s( selection() ); // Did we click in the lower right corner of the marker/marked-area ? if ( selectionInfo()->selectionHandleArea().contains( QPoint( doc()->zoomItX( ev_PosX ), doc()->zoomItY( ev_PosY ) ) ) ) { processClickSelectionHandle( _ev ); return; } // In which cell did the user click ? double xpos; double ypos; int col = table->leftColumn( ev_PosX, xpos ); int row = table->topRow( ev_PosY, ypos ); { // start drag ? QRect rct( selectionInfo()->selection() ); QRect r1; QRect r2; { double lx = table->dblColumnPos( rct.left() ); double rx = table->dblColumnPos( rct.right() + 1 ); double ty = table->dblRowPos( rct.top() ); double by = table->dblRowPos( rct.bottom() + 1 ); r1.setLeft( (int) (lx - 1) ); r1.setTop( (int) (ty - 1) ); r1.setRight( (int) (rx + 1) ); r1.setBottom( (int) (by + 1) ); r2.setLeft( (int) (lx + 1) ); r2.setTop( (int) (ty + 1) ); r2.setRight( (int) (rx - 1) ); r2.setBottom( (int) (by - 1) ); } m_dragStart.setX( -1 ); if ( r1.contains( QPoint( (int) ev_PosX, (int) ev_PosY ) ) && !r2.contains( QPoint( (int) ev_PosX, (int) ev_PosY ) ) ) { m_dragStart.setX( (int) ev_PosX ); m_dragStart.setY( (int) ev_PosY ); return; } } // kdDebug() << "Clicked in cell " << col << ", " << row << endl; //you cannot move marker when col > KS_colMax or row > KS_rowMax if ( col > KS_colMax || row > KS_rowMax) { kdDebug(36001) << "KSpreadCanvas::mousePressEvent: col or row is out of range: col: " << col << " row: " << row << endl; return; } // Extending an existing selection with the shift button ? if ( m_pView->koDocument()->isReadWrite() && s.right() != KS_colMax && s.bottom() != KS_rowMax && _ev->state() & ShiftButton ) { gotoLocation( QPoint( col, row ), activeTable(), true ); return; } KSpreadCell *cell = table->cellAt( col, row ); // Go to the upper left corner of the obscuring object if cells are merged if (cell->isObscuringForced()) { cell = cell->obscuringCells().first(); col = cell->column(); row = cell->row(); } // Start a marking action ? if ( !m_strAnchor.isEmpty() && _ev->button() == LeftButton ) { processLeftClickAnchor(); updatePosWidget(); } else if ( _ev->button() == LeftButton ) { m_eMouseAction = Mark; gotoLocation( QPoint( col, row ), activeTable(), false ); } else if ( _ev->button() == RightButton && !s.contains( QPoint( col, row ) ) ) { // No selection or the mouse press was outside of an existing selection ? gotoLocation( QPoint( col, row ), activeTable(), false ); } // Paste operation with the middle button ? if ( _ev->button() == MidButton ) { if ( m_pView->koDocument()->isReadWrite() && !table->isProtected() ) { selectionInfo()->setMarker( QPoint( col, row ), table ); table->paste( QRect(marker(), marker()) ); table->setRegionPaintDirty(QRect(marker(), marker())); } updatePosWidget(); } // Update the edit box m_pView->updateEditWidgetOnPress(); // Context menu ? if ( _ev->button() == RightButton ) { updatePosWidget(); // TODO: Handle anchor QPoint p = mapToGlobal( _ev->pos() ); m_pView->openPopupMenu( p ); } } void KSpreadCanvas::startTheDrag() { KSpreadSheet * table = activeTable(); if ( !table ) return; // right area for start dragging KSpreadTextDrag * d = new KSpreadTextDrag( this ); setCursor( KCursor::handCursor() ); QRect rct( selectionInfo()->selection() ); QDomDocument doc = table->saveCellRect( rct ); // Save to buffer QBuffer buffer; buffer.open( IO_WriteOnly ); QTextStream str( &buffer ); str.setEncoding( QTextStream::UnicodeUTF8 ); str << doc; buffer.close(); d->setPlain( table->copyAsText( selectionInfo() ) ); d->setKSpread( buffer.buffer() ); d->dragCopy(); setCursor( KCursor::arrowCursor() ); } void KSpreadCanvas::chooseMouseMoveEvent( QMouseEvent * _ev ) { if ( !m_bMousePressed ) return; KSpreadSheet * table = activeTable(); if ( !table ) return; double tmp; double ev_PosX; if ( table->layoutDirection()==KSpreadSheet::RightToLeft ) { double dwidth = doc()->unzoomItX( width() ); ev_PosX = dwidth - doc()->unzoomItX( _ev->pos().x() ); } else ev_PosX = doc()->unzoomItX( _ev->pos().x() ); double ev_PosY = doc()->unzoomItY( _ev->pos().y() ); int col = table->leftColumn( (ev_PosX + xOffset()), tmp ); int row = table->topRow( (ev_PosY + yOffset()), tmp ); if ( col > KS_colMax || row > KS_rowMax ) { return; } QPoint chooseMarker = selectionInfo()->getChooseMarker(); // Nothing changed ? if ( row == chooseMarker.y() && col == chooseMarker.x() ) { return; } gotoLocation( QPoint( col, row ), table, ( m_eMouseAction != NoAction ) ); } void KSpreadCanvas::chooseMouseReleaseEvent( QMouseEvent* ) { // gets done in mouseReleaseEvent // m_bMousePressed = FALSE; m_eMouseAction = NoAction; } void KSpreadCanvas::chooseMousePressEvent( QMouseEvent * _ev ) { KSpreadSheet *table = activeTable(); if ( !table ) return; double ev_PosX; if ( table->layoutDirection()==KSpreadSheet::RightToLeft ) { double dwidth = doc()->unzoomItX( width() ); ev_PosX = dwidth - doc()->unzoomItX( _ev->pos().x() ); } else ev_PosX = doc()->unzoomItX( _ev->pos().x() ); double ev_PosY = doc()->unzoomItY( _ev->pos().y() ); double ypos, xpos; int col = table->leftColumn( (ev_PosX + xOffset()), xpos ); int row = table->topRow( (ev_PosY + yOffset()), ypos ); if ( col > KS_colMax || row > KS_rowMax ) { return; } bool extend = ( ( ( !util_isColumnSelected(selection() ) ) && ( !util_isRowSelected(selection() ) ) ) && ( _ev->state() & ShiftButton ) ); gotoLocation( QPoint( col, row ), activeTable(), extend ); if ( _ev->button() == LeftButton ) { m_eMouseAction = Mark; } return; } void KSpreadCanvas::mouseDoubleClickEvent( QMouseEvent* ) { if ( m_pView->koDocument()->isReadWrite() && activeTable() ) createEditor(); } void KSpreadCanvas::wheelEvent( QWheelEvent* _ev ) { if ( _ev->orientation() == Qt::Vertical ) { if ( vertScrollBar() ) QApplication::sendEvent( vertScrollBar(), _ev ); } else if ( horzScrollBar() ) { QApplication::sendEvent( horzScrollBar(), _ev ); } } void KSpreadCanvas::paintEvent( QPaintEvent* _ev ) { if ( m_pDoc->isLoading() ) return; KSpreadSheet* table = activeTable(); if ( !table ) return; // ElapsedTime et( "KSpreadCanvas::paintEvent" ); double dwidth = doc()->unzoomItX( width() ); KoRect rect = doc()->unzoomRect( _ev->rect() & QWidget::rect() ); if ( table->layoutDirection()==KSpreadSheet::RightToLeft ) rect.moveBy( -xOffset(), yOffset() ); else rect.moveBy( xOffset(), yOffset() ); KoPoint tl = rect.topLeft(); KoPoint br = rect.bottomRight(); double tmp; int left_col; int right_col; //Philipp: I don't know why we need the +1, but otherwise we don't get it correctly //Testcase: Move a dialog slowly up left. Sometimes the top/left most points are not painted if ( table->layoutDirection()==KSpreadSheet::RightToLeft ) { right_col = table->leftColumn( dwidth - tl.x(), tmp ); left_col = table->rightColumn( dwidth - br.x() + 1.0 ); } else { left_col = table->leftColumn( tl.x(), tmp ); right_col = table->rightColumn( br.x() + 1.0 ); } int top_row = table->topRow( tl.y(), tmp ); int bottom_row = table->bottomRow( br.y() + 1.0 ); QRect vr( QPoint(left_col, top_row), QPoint(right_col, bottom_row) ); m_pView->doc()->emitBeginOperation( false ); table->setRegionPaintDirty( vr ); m_pView->doc()->emitEndOperation( vr ); } void KSpreadCanvas::focusInEvent( QFocusEvent* ) { if ( !m_pEditor ) return; //kdDebug(36001) << "m_bChoose : " << ( m_bChoose ? "true" : "false" ) << endl; // If we are in editing mode, we redirect the // focus to the CellEditor or EditWidget // And we know which, using lastEditorWithFocus. // This screws up though (David) if ( lastEditorWithFocus() == EditWidget ) { m_pView->editWidget()->setFocus(); //kdDebug(36001) << "Focus to EditWidget" << endl; return; } //kdDebug(36001) << "Redirecting focus to editor" << endl; m_pEditor->setFocus(); } void KSpreadCanvas::focusOutEvent( QFocusEvent* ) { if ( m_scrollTimer->isActive() ) m_scrollTimer->stop(); m_bMousePressed = false; } void KSpreadCanvas::dragMoveEvent( QDragMoveEvent * _ev ) { KSpreadSheet * table = activeTable(); if ( !table ) { _ev->ignore(); return; } _ev->accept( KSpreadTextDrag::canDecode( _ev ) ); double dwidth = doc()->unzoomItX( width() ); double xpos = table->dblColumnPos( selectionInfo()->selection().left() ); double ypos = table->dblRowPos( selectionInfo()->selection().top() ); double width = table->columnFormat( selectionInfo()->selection().left() )->dblWidth( this ); double height = table->rowFormat( selectionInfo()->selection().top() )->dblHeight( this ); QRect r1 ((int) xpos - 1, (int) ypos - 1, (int) width + 3, (int) height + 3); double ev_PosX; if (table->layoutDirection()==KSpreadSheet::RightToLeft) ev_PosX = dwidth - doc()->unzoomItX( _ev->pos().x() ) + xOffset(); else ev_PosX = doc()->unzoomItX( _ev->pos().x() ) + xOffset(); double ev_PosY = doc()->unzoomItY( _ev->pos().y() ) + yOffset(); if ( r1.contains( QPoint ((int) ev_PosX, (int) ev_PosY) ) ) _ev->ignore( r1 ); } void KSpreadCanvas::dragLeaveEvent( QDragLeaveEvent * ) { if ( m_scrollTimer->isActive() ) m_scrollTimer->stop(); } void KSpreadCanvas::dropEvent( QDropEvent * _ev ) { m_dragging = false; KSpreadSheet * table = activeTable(); if ( !table || table->isProtected() ) { _ev->ignore(); return; } double dwidth = doc()->unzoomItX( width() ); double xpos = table->dblColumnPos( selectionInfo()->selection().left() ); double ypos = table->dblRowPos( selectionInfo()->selection().top() ); double width = table->columnFormat( selectionInfo()->selection().left() )->dblWidth( this ); double height = table->rowFormat( selectionInfo()->selection().top() )->dblHeight( this ); QRect r1 ((int) xpos - 1, (int) ypos - 1, (int) width + 3, (int) height + 3); double ev_PosX; if (table->layoutDirection()==KSpreadSheet::RightToLeft) ev_PosX = dwidth - doc()->unzoomItX( _ev->pos().x() ) + xOffset(); else ev_PosX = doc()->unzoomItX( _ev->pos().x() ) + xOffset(); double ev_PosY = doc()->unzoomItY( _ev->pos().y() ) + yOffset(); if ( r1.contains( QPoint ((int) ev_PosX, (int) ev_PosY) ) ) { _ev->ignore( ); return; } else _ev->accept( ); double tmp; int col = table->leftColumn( ev_PosX, tmp ); int row = table->topRow( ev_PosY, tmp ); if ( !KSpreadTextDrag::canDecode( _ev ) ) { _ev->ignore(); return; } QByteArray b; bool makeUndo = true; if ( _ev->provides( KSpreadTextDrag::selectionMimeType() ) ) { if ( KSpreadTextDrag::target() == _ev->source() ) { if ( !m_pDoc->undoLocked() ) { KSpreadUndoDragDrop * undo = new KSpreadUndoDragDrop( m_pDoc, table, selectionInfo()->selection(), QRect( col, row, selectionInfo()->selection().width(), selectionInfo()->selection().height() ) ); m_pDoc->addCommand( undo ); makeUndo = false; } table->deleteSelection( selectionInfo(), false ); } b = _ev->encodedData( KSpreadTextDrag::selectionMimeType() ); table->paste( b, QRect( col, row, 1, 1 ), makeUndo ); if ( _ev->source() == this ) _ev->acceptAction(); _ev->accept(); } else { QString text; if ( !QTextDrag::decode( _ev, text ) ) { _ev->ignore(); return; } // if ( KSpreadTextDrag::target() == _ev->source() ) // table->deleteSelection( selectionInfo() ); table->pasteTextPlain( text, QRect( col, row, 1, 1 ) ); _ev->accept(); if ( _ev->source() == this ) _ev->acceptAction(); return; } } void KSpreadCanvas::resizeEvent( QResizeEvent* _ev ) { double ev_Width = doc()->unzoomItX( _ev->size().width() ); double ev_Height = doc()->unzoomItY( _ev->size().height() ); // If we rise horizontally, then check if we are still within the valid area (KS_colMax) if ( _ev->size().width() > _ev->oldSize().width() ) { int oldValue = horzScrollBar()->maxValue() - horzScrollBar()->value(); if ( ( xOffset() + ev_Width ) > doc()->zoomItX( activeTable()->sizeMaxX() ) ) { horzScrollBar()->setRange( 0, doc()->zoomItX( activeTable()->sizeMaxX() - ev_Width ) ); if ( activeTable()->layoutDirection()==KSpreadSheet::RightToLeft ) horzScrollBar()->setValue( horzScrollBar()->maxValue() - oldValue ); } } // If we lower vertically, then check if the range should represent the maximum range else if ( _ev->size().width() < _ev->oldSize().width() ) { int oldValue = horzScrollBar()->maxValue() - horzScrollBar()->value(); if ( horzScrollBar()->maxValue() == int( doc()->zoomItX( activeTable()->sizeMaxX() ) - ev_Width ) ) { horzScrollBar()->setRange( 0, doc()->zoomItX( activeTable()->sizeMaxX() - ev_Width ) ); if ( activeTable()->layoutDirection()==KSpreadSheet::RightToLeft ) horzScrollBar()->setValue( horzScrollBar()->maxValue() - oldValue ); } } // If we rise vertically, then check if we are still within the valid area (KS_rowMax) if ( _ev->size().height() > _ev->oldSize().height() ) { if ( ( yOffset() + ev_Height ) > doc()->zoomItY( activeTable()->sizeMaxY() ) ) { vertScrollBar()->setRange( 0, doc()->zoomItY( activeTable()->sizeMaxY() - ev_Height ) ); } } // If we lower vertically, then check if the range should represent the maximum range else if ( _ev->size().height() < _ev->oldSize().height() ) { if ( vertScrollBar()->maxValue() == int( doc()->zoomItY( activeTable()->sizeMaxY() ) - ev_Height ) ) { vertScrollBar()->setRange( 0, doc()->zoomItY( activeTable()->sizeMaxY() - ev_Height ) ); } } } -QRect KSpreadCanvas::moveDirection( KSpread::MoveTo direction, bool extendSelection ) +QPoint KSpreadCanvas::cursorPos () { - QPoint destination; QPoint cursor; - if (m_bChoose) { cursor = selectionInfo()->getChooseCursor(); /* if the cursor is unset, pretend we're starting at the regular cursor */ if (cursor.x() == 0 || cursor.y() == 0) - { cursor = selectionInfo()->cursorPosition(); - } } else - { cursor = selectionInfo()->cursorPosition(); - } + + return cursor; +} + +QRect KSpreadCanvas::moveDirection( KSpread::MoveTo direction, bool extendSelection ) +{ + QPoint destination; + QPoint cursor = cursorPos (); QPoint cellCorner = cursor; KSpreadCell* cell = activeTable()->cellAt(cursor.x(), cursor.y()); /* cell is either the same as the marker, or the cell that is forced obscuring the marker cell */ if (cell->isObscuringForced()) { cell = cell->obscuringCells().first(); cellCorner = QPoint(cell->column(), cell->row()); } /* how many cells must we move to get to the next cell? */ int offset = 0; RowFormat *rl = NULL; ColumnFormat *cl = NULL; switch (direction) /* for each case, figure out how far away the next cell is and then keep going one row/col at a time after that until a visible row/col is found NEVER use cell->column() or cell->row() -- it might be a default cell */ { case KSpread::Bottom: offset = cell->mergedYCells() - (cursor.y() - cellCorner.y()) + 1; rl = activeTable()->rowFormat( cursor.y() + offset ); while ( ((cursor.y() + offset) <= KS_rowMax) && rl->isHide()) { offset++; rl = activeTable()->rowFormat( cursor.y() + offset ); } destination = QPoint(cursor.x(), QMIN(cursor.y() + offset, KS_rowMax)); break; case KSpread::Top: offset = (cellCorner.y() - cursor.y()) - 1; rl = activeTable()->rowFormat( cursor.y() + offset ); while ( ((cursor.y() + offset) >= 1) && rl->isHide()) { offset--; rl = activeTable()->rowFormat( cursor.y() + offset ); } destination = QPoint(cursor.x(), QMAX(cursor.y() + offset, 1)); break; case KSpread::Left: offset = (cellCorner.x() - cursor.x()) - 1; cl = activeTable()->columnFormat( cursor.x() + offset ); while ( ((cursor.x() + offset) >= 1) && cl->isHide()) { offset--; cl = activeTable()->columnFormat( cursor.x() + offset ); } destination = QPoint(QMAX(cursor.x() + offset, 1), cursor.y()); break; case KSpread::Right: offset = cell->mergedXCells() - (cursor.x() - cellCorner.x()) + 1; cl = activeTable()->columnFormat( cursor.x() + offset ); while ( ((cursor.x() + offset) <= KS_colMax) && cl->isHide()) { offset++; cl = activeTable()->columnFormat( cursor.x() + offset ); } destination = QPoint(QMIN(cursor.x() + offset, KS_colMax), cursor.y()); break; case KSpread::BottomFirst: offset = cell->mergedYCells() - (cursor.y() - cellCorner.y()) + 1; rl = activeTable()->rowFormat( cursor.y() + offset ); while ( ((cursor.y() + offset) <= KS_rowMax) && rl->isHide()) { ++offset; rl = activeTable()->rowFormat( cursor.y() + offset ); } destination = QPoint( 1, QMIN( cursor.y() + offset, KS_rowMax ) ); break; } gotoLocation(destination, activeTable(), extendSelection); m_pView->updateEditWidget(); return QRect( cursor, destination ); } void KSpreadCanvas::processEnterKey(QKeyEvent* event) { /* save changes to the current editor */ if (!m_bChoose) { deleteEditor( true ); } /* use the configuration setting to see which direction we're supposed to move when enter is pressed. */ KSpread::MoveTo direction = m_pView->doc()->getMoveToValue(); //if shift Button clicked inverse move direction if (event->state() & Qt::ShiftButton) { switch( direction ) { case KSpread::Bottom: direction = KSpread::Top; break; case KSpread::Top: direction = KSpread::Bottom; break; case KSpread::Left: direction = KSpread::Right; break; case KSpread::Right: direction = KSpread::Left; break; case KSpread::BottomFirst: direction = KSpread::BottomFirst; break; } } /* never extend a selection with the enter key -- the shift key reverses direction, not extends the selection */ QRect r( moveDirection( direction, false ) ); m_pDoc->emitEndOperation( r ); } void KSpreadCanvas::processArrowKey( QKeyEvent *event) { /* NOTE: hitting the tab key also calls this function. Don't forget to account for it */ /* save changes to the current editor */ if (!m_bChoose) { deleteEditor( true ); } KSpread::MoveTo direction = KSpread::Bottom; bool makingSelection = event->state() & ShiftButton; switch (event->key()) { case Key_Down: direction = KSpread::Bottom; break; case Key_Up: direction = KSpread::Top; break; case Key_Left: if (activeTable()->layoutDirection()==KSpreadSheet::RightToLeft) direction = KSpread::Right; else direction = KSpread::Left; break; case Key_Right: if (activeTable()->layoutDirection()==KSpreadSheet::RightToLeft) direction = KSpread::Left; else direction = KSpread::Right; break; case Key_Tab: direction = KSpread::Right; break; default: Q_ASSERT(false); break; } QRect r( moveDirection( direction, makingSelection ) ); m_pDoc->emitEndOperation( r ); } void KSpreadCanvas::processEscapeKey(QKeyEvent * event) { if ( m_pEditor ) deleteEditor( false ); event->accept(); // ? - QPoint cursor; - - if (m_bChoose) - { - cursor = selectionInfo()->getChooseCursor(); - /* if the cursor is unset, pretend we're starting at the regular cursor */ - if (cursor.x() == 0 || cursor.y() == 0) - cursor = selectionInfo()->cursorPosition(); - } - else - cursor = selectionInfo()->cursorPosition(); + QPoint cursor = cursorPos(); m_pDoc->emitEndOperation( QRect( cursor, cursor ) ); } bool KSpreadCanvas::processHomeKey(QKeyEvent* event) { bool makingSelection = event->state() & ShiftButton; KSpreadSheet* table = activeTable(); if ( m_pEditor ) // We are in edit mode -> go beginning of line { // (David) Do this for text editor only, not formula editor... // Don't know how to avoid this hack (member var for editor type ?) if ( m_pEditor->inherits("KSpreadTextEditor") ) QApplication::sendEvent( m_pEditWidget, event ); // What to do for a formula editor ? return false; } else { QPoint destination; /* start at the first used cell in the row and cycle through the right until we find a cell that has some output text. But don't look past the current marker. The end result we want is to move to the left to the first cell with text, or just to the first column if there is no more text to the left. But why? In excel, home key sends you to the first column always. We might want to change to that behavior. */ if (event->state() & ControlButton) { /* ctrl + Home will always just send us to location (1,1) */ destination = QPoint( 1, 1 ); } else { QPoint marker = m_bChoose ? selectionInfo()->getChooseMarker() : selectionInfo()->marker(); KSpreadCell * cell = table->getFirstCellRow(marker.y()); while (cell != NULL && cell->column() < marker.x() && cell->isEmpty()) { cell = table->getNextCellRight(cell->column(), cell->row()); } int col = ( cell ? cell->column() : 1 ); if ( col == marker.x()) col = 1; destination = QPoint(col, marker.y()); } if ( selectionInfo()->marker() == destination ) { m_pDoc->emitEndOperation( QRect( destination, destination ) ); return false; } gotoLocation( destination, activeTable(), makingSelection ); } return true; } bool KSpreadCanvas::processEndKey( QKeyEvent *event ) { bool makingSelection = event->state() & ShiftButton; KSpreadSheet* table = activeTable(); KSpreadCell* cell = NULL; QPoint marker = m_bChoose ? selectionInfo()->getChooseMarker() : selectionInfo()->marker(); // move to the last used cell in the row // We are in edit mode -> go beginning of line if ( m_pEditor ) { // (David) Do this for text editor only, not formula editor... // Don't know how to avoid this hack (member var for editor type ?) if ( m_pEditor->inherits("KSpreadTextEditor") ) QApplication::sendEvent( m_pEditWidget, event ); // TODO: What to do for a formula editor ? m_pDoc->emitEndOperation( QRect( marker, marker ) ); return false; } else { int col = 1; cell = table->getLastCellRow(marker.y()); while (cell != NULL && cell->column() > markerColumn() && cell->isEmpty()) { cell = table->getNextCellLeft(cell->column(), cell->row()); } col = (cell == NULL) ? KS_colMax : cell->column(); QPoint destination( col, marker.y() ); if ( destination == marker ) { m_pDoc->emitEndOperation( QRect( destination, destination ) ); return false; } gotoLocation( destination, activeTable(), makingSelection ); } return true; } bool KSpreadCanvas::processPriorKey(QKeyEvent *event) { bool makingSelection = event->state() & ShiftButton; if (!m_bChoose) { deleteEditor( true ); } QPoint marker = m_bChoose ? selectionInfo()->getChooseMarker() : selectionInfo()->marker(); QPoint destination(marker.x(), QMAX(1, marker.y() - 10)); if ( destination == marker ) { m_pDoc->emitEndOperation( QRect( destination, destination ) ); return false; } gotoLocation(destination, activeTable(), makingSelection); return true; } bool KSpreadCanvas::processNextKey(QKeyEvent *event) { bool makingSelection = event->state() & ShiftButton; if (!m_bChoose) { deleteEditor( true /*save changes*/ ); } QPoint marker = m_bChoose ? selectionInfo()->getChooseMarker() : selectionInfo()->marker(); QPoint destination(marker.x(), QMAX(1, marker.y() + 10)); if ( marker == destination ) { m_pDoc->emitEndOperation( QRect( destination, destination ) ); return false; } gotoLocation(destination, activeTable(), makingSelection); return true; } void KSpreadCanvas::processDeleteKey(QKeyEvent* /* event */) { activeTable()->clearTextSelection( selectionInfo() ); m_pView->editWidget()->setText( "" ); - QPoint cursor; - - if ( m_bChoose ) - { - cursor = selectionInfo()->getChooseCursor(); - /* if the cursor is unset, pretend we're starting at the regular cursor */ - if (cursor.x() == 0 || cursor.y() == 0) - cursor = selectionInfo()->cursorPosition(); - } - else - cursor = selectionInfo()->cursorPosition(); + QPoint cursor = cursorPos(); m_pDoc->emitEndOperation( QRect( cursor, cursor ) ); return; } void KSpreadCanvas::processF2Key(QKeyEvent* /* event */) { m_pView->editWidget()->setFocus(); if ( m_pEditor ) m_pView->editWidget()->setCursorPosition( m_pEditor->cursorPosition() - 1 ); m_pView->editWidget()->cursorForward( false ); - QPoint cursor; - - if ( m_bChoose ) - { - cursor = selectionInfo()->getChooseCursor(); - /* if the cursor is unset, pretend we're starting at the regular cursor */ - if (cursor.x() == 0 || cursor.y() == 0) - cursor = selectionInfo()->cursorPosition(); - } - else - cursor = selectionInfo()->cursorPosition(); + + QPoint cursor = cursorPos(); m_pDoc->emitEndOperation( QRect( cursor, cursor ) ); return; } void KSpreadCanvas::processF4Key(QKeyEvent* event) { - /* I have no idea what this is doing. But it was in the code so I'm leaving it + /* passes F4 to the editor (if any), which will process it */ if ( m_pEditor ) { m_pEditor->handleKeyPressEvent( event ); - m_pView->editWidget()->setFocus(); +// m_pView->editWidget()->setFocus(); m_pView->editWidget()->setCursorPosition( m_pEditor->cursorPosition() ); } - QPoint cursor; - - if ( m_bChoose ) - { - cursor = selectionInfo()->getChooseCursor(); - /* if the cursor is unset, pretend we're starting at the regular cursor */ - if (cursor.x() == 0 || cursor.y() == 0) - cursor = selectionInfo()->cursorPosition(); - } - else - cursor = selectionInfo()->cursorPosition(); + QPoint cursor = cursorPos(); m_pDoc->emitEndOperation( QRect( cursor, cursor ) ); return; } void KSpreadCanvas::processOtherKey(QKeyEvent *event) { // No null character ... if ( event->text().isEmpty() || !m_pView->koDocument()->isReadWrite() || !activeTable() || activeTable()->isProtected() ) { event->accept(); } else { if ( !m_pEditor && !m_bChoose ) { // Switch to editing mode createEditor( CellEditor ); m_pEditor->handleKeyPressEvent( event ); } else if ( m_pEditor ) m_pEditor->handleKeyPressEvent( event ); } - QPoint cursor; - - if ( m_bChoose ) - { - cursor = selectionInfo()->getChooseCursor(); - /* if the cursor is unset, pretend we're starting at the regular cursor */ - if (cursor.x() == 0 || cursor.y() == 0) - cursor = selectionInfo()->cursorPosition(); - } - else - cursor = selectionInfo()->cursorPosition(); + QPoint cursor = cursorPos(); m_pDoc->emitEndOperation( QRect( cursor, cursor ) ); return; } bool KSpreadCanvas::processControlArrowKey( QKeyEvent *event ) { bool makingSelection = event->state() & ShiftButton; KSpreadSheet* table = activeTable(); KSpreadCell* cell = NULL; KSpreadCell* lastCell; QPoint destination; bool searchThroughEmpty = TRUE; int row; int col; QPoint marker = m_bChoose ? selectionInfo()->getChooseMarker() : selectionInfo()->marker(); /* here, we want to move to the first or last cell in the given direction that is actually being used. Ignore empty cells and cells on hidden rows/columns */ switch ( event->key() ) { //Ctrl+Key_Up case Key_Up: cell = table->cellAt( marker.x(), marker.y() ); if ( (cell != NULL) && (!cell->isEmpty()) && (marker.y() != 1)) { lastCell = cell; row = marker.y()-1; cell = table->cellAt(cell->column(), row); while ((cell != NULL) && (row > 0) && (!cell->isEmpty()) ) { if (!(table->rowFormat(cell->row())->isHide())) { lastCell = cell; searchThroughEmpty = FALSE; } row--; if ( row > 0 ) cell = table->cellAt(cell->column(), row); } cell = lastCell; } if (searchThroughEmpty) { cell = table->getNextCellUp(marker.x(), marker.y()); while ((cell != NULL) && (cell->isEmpty() || (table->rowFormat(cell->row())->isHide()))) { cell = table->getNextCellUp(cell->column(), cell->row()); } } if (cell == NULL) row = 1; else row = cell->row(); while ( table->rowFormat(row)->isHide() ) { row++; } destination.setX(marker.x()); destination.setY(row); break; //Ctrl+Key_Down case Key_Down: cell = table->cellAt( marker.x(), marker.y() ); if ( (cell != NULL) && (!cell->isEmpty()) && (marker.y() != KS_rowMax)) { lastCell = cell; row = marker.y()+1; cell = table->cellAt(cell->column(), row); while ((cell != NULL) && (row < KS_rowMax) && (!cell->isEmpty()) ) { if (!(table->rowFormat(cell->row())->isHide())) { lastCell = cell; searchThroughEmpty = FALSE; } row++; cell = table->cellAt(cell->column(), row); } cell = lastCell; } if (searchThroughEmpty) { cell = table->getNextCellDown(marker.x(), marker.y()); while ((cell != NULL) && (cell->isEmpty() || (table->rowFormat(cell->row())->isHide()))) { cell = table->getNextCellDown(cell->column(), cell->row()); } } if (cell == NULL) row = marker.y(); else row = cell->row(); while ( table->rowFormat(row)->isHide() ) { row--; } destination.setX(marker.x()); destination.setY(row); break; //Ctrl+Key_Left case Key_Left: if ( table->layoutDirection()==KSpreadSheet::RightToLeft ) { cell = table->cellAt( marker.x(), marker.y() ); if ( (cell != NULL) && (!cell->isEmpty()) && (marker.x() != KS_colMax)) { lastCell = cell; col = marker.x()+1; cell = table->cellAt(col, cell->row()); while ((cell != NULL) && (col < KS_colMax) && (!cell->isEmpty()) ) { if (!(table->columnFormat(cell->column())->isHide())) { lastCell = cell; searchThroughEmpty = FALSE; } col++; cell = table->cellAt(col, cell->row()); } cell = lastCell; } if (searchThroughEmpty) { cell = table->getNextCellRight(marker.x(), marker.y()); while ((cell != NULL) && (cell->isEmpty() || (table->columnFormat(cell->column())->isHide()))) { cell = table->getNextCellRight(cell->column(), cell->row()); } } if (cell == NULL) col = marker.x(); else col = cell->column(); while ( table->columnFormat(col)->isHide() ) { col--; } destination.setX(col); destination.setY(marker.y()); } else { cell = table->cellAt( marker.x(), marker.y() ); if ( (cell != NULL) && (!cell->isEmpty()) && (marker.x() != 1)) { lastCell = cell; col = marker.x()-1; cell = table->cellAt(col, cell->row()); while ((cell != NULL) && (col > 0) && (!cell->isEmpty()) ) { if (!(table->columnFormat(cell->column())->isHide())) { lastCell = cell; searchThroughEmpty = FALSE; } col--; if ( col > 0 ) cell = table->cellAt(col, cell->row()); } cell = lastCell; } if (searchThroughEmpty) { cell = table->getNextCellLeft(marker.x(), marker.y()); while ((cell != NULL) && (cell->isEmpty() || (table->columnFormat(cell->column())->isHide()))) { cell = table->getNextCellLeft(cell->column(), cell->row()); } } if (cell == NULL) col = 1; else col = cell->column(); while ( table->columnFormat(col)->isHide() ) { col++; } destination.setX(col); destination.setY(marker.y()); } break; //Ctrl+Key_Right case Key_Right: if ( table->layoutDirection()==KSpreadSheet::RightToLeft ) { cell = table->cellAt( marker.x(), marker.y() ); if ( (cell != NULL) && (!cell->isEmpty()) && (marker.x() != 1)) { lastCell = cell; col = marker.x()-1; cell = table->cellAt(col, cell->row()); while ((cell != NULL) && (col > 0) && (!cell->isEmpty()) ) { if (!(table->columnFormat(cell->column())->isHide())) { lastCell = cell; searchThroughEmpty = FALSE; } col--; if ( col > 0 ) cell = table->cellAt(col, cell->row()); } cell = lastCell; } if (searchThroughEmpty) { cell = table->getNextCellLeft(marker.x(), marker.y()); while ((cell != NULL) && (cell->isEmpty() || (table->columnFormat(cell->column())->isHide()))) { cell = table->getNextCellLeft(cell->column(), cell->row()); } } if (cell == NULL) col = 1; else col = cell->column(); while ( table->columnFormat(col)->isHide() ) { col++; } destination.setX(col); destination.setY(marker.y()); } else { cell = table->cellAt( marker.x(), marker.y() ); if ( (cell != NULL) && (!cell->isEmpty()) && (marker.x() != KS_colMax)) { lastCell = cell; col = marker.x()+1; cell = table->cellAt(col, cell->row()); while ((cell != NULL) && (col < KS_colMax) && (!cell->isEmpty()) ) { if (!(table->columnFormat(cell->column())->isHide())) { lastCell = cell; searchThroughEmpty = FALSE; } col++; cell = table->cellAt(col, cell->row()); } cell = lastCell; } if (searchThroughEmpty) { cell = table->getNextCellRight(marker.x(), marker.y()); while ((cell != NULL) && (cell->isEmpty() || (table->columnFormat(cell->column())->isHide()))) { cell = table->getNextCellRight(cell->column(), cell->row()); } } if (cell == NULL) col = marker.x(); else col = cell->column(); while ( table->columnFormat(col)->isHide() ) { col--; } destination.setX(col); destination.setY(marker.y()); } break; } if ( marker == destination ) { m_pDoc->emitEndOperation( QRect( destination, destination ) ); return false; } gotoLocation( destination, table, makingSelection ); return true; } void KSpreadCanvas::keyPressEvent ( QKeyEvent * _ev ) { KSpreadSheet * table = activeTable(); if ( !table || formatKeyPress( _ev )) return; // Dont handle the remaining special keys. if ( _ev->state() & ( Qt::AltButton | Qt::ControlButton ) && (_ev->key() != Key_Down) && (_ev->key() != Key_Up) && (_ev->key() != Key_Right) && (_ev->key() != Key_Left) && (_ev->key() != Key_Home) ) { QWidget::keyPressEvent( _ev ); return; } // Always accept so that events are not // passed to the parent. _ev->accept(); m_pDoc->emitBeginOperation(false); switch( _ev->key() ) { case Key_Return: case Key_Enter: processEnterKey( _ev ); return; break; case Key_Down: case Key_Up: case Key_Left: case Key_Right: case Key_Tab: /* a tab behaves just like a right arrow */ if (_ev->state() & ControlButton) { if ( !processControlArrowKey( _ev ) ) return; } else { processArrowKey( _ev ); return; } break; case Key_Escape: processEscapeKey( _ev ); return; break; case Key_Home: if ( !processHomeKey( _ev ) ) return; break; case Key_End: if ( !processEndKey( _ev ) ) return; break; case Key_Prior: /* Page Up */ if ( !processPriorKey( _ev ) ) return; break; case Key_Next: /* Page Down */ if ( !processNextKey( _ev ) ) return; break; case Key_Delete: processDeleteKey( _ev ); return; break; case Key_F2: processF2Key( _ev ); return; break; - + case Key_F4: processF4Key( _ev ); return; break; default: processOtherKey( _ev ); return; break; } + + //most process*Key methods call emitEndOperation, this only gets called in some situations + // (after some move operations) m_pDoc->emitEndOperation( table->visibleRect( this ) ); return; } double KSpreadCanvas::getDouble( KSpreadCell * cell ) { cell->setFactor( 1.0 ); if ( cell->isDate() ) { QDate date = cell->value().asDate(); QDate dummy(1900, 1, 1); return (dummy.daysTo( date ) + 1); } if ( cell->isTime() ) { QTime time = cell->value().asTime(); QTime dummy; return dummy.secsTo( time ); } if ( cell->value().isNumber() ) return cell->value().asFloat(); return 0.0; } void KSpreadCanvas::convertToDouble( KSpreadCell * cell ) { if ( cell->isTime() || cell->isDate() ) cell->setValue( getDouble( cell ) ); cell->setFactor( 1.0 ); } void KSpreadCanvas::convertToPercent( KSpreadCell * cell ) { if ( cell->isTime() || cell->isDate() ) cell->setValue( getDouble( cell ) ); cell->setFactor( 100.0 ); cell->setFormatType (Percentage_format); } void KSpreadCanvas::convertToMoney( KSpreadCell * cell ) { if ( cell->isTime() || cell->isDate() ) cell->setValue( getDouble( cell ) ); cell->setFormatType (Money_format); cell->setFactor( 1.0 ); cell->setPrecision( m_pDoc->locale()->fracDigits() ); } void KSpreadCanvas::convertToTime( KSpreadCell * cell ) { //(Tomas) This is weird. And I mean *REALLY* weird. First, we //generate a time (QTime), then we convert it to text, then //we give the text to the cell and ask it to parse it. Weird... if ( cell->isDefault() || cell->isEmpty() ) return; if ( cell->isDate() ) cell->setValue( getDouble( cell ) ); cell->setFormatType (SecondeTime_format); QTime time = cell->value().asDateTime().time(); int msec = (int) ( (cell->value().asFloat() - (int) cell->value().asFloat())* 1000 ); time = time.addMSecs( msec ); cell->setCellText( time.toString() ); } void KSpreadCanvas::convertToDate( KSpreadCell * cell ) { //(Tomas) This is weird. And I mean *REALLY* weird. First, we //generate a date (QDate), then we convert it to text, then //we give the text to the cell and ask it to parse it. Weird... if ( cell->isDefault() || cell->isEmpty() ) return; if ( cell->isTime() ) cell->setValue( getDouble( cell ) ); cell->setFormatType (ShortDate_format); cell->setFactor( 1.0 ); //TODO: why did we call setValue(), when we override it here? QDate date(1900, 1, 1); date = date.addDays( (int) cell->value().asFloat() - 1 ); date = cell->value().asDateTime().date(); cell->setCellText (m_pDoc->locale()->formatDate (date, true)); } bool KSpreadCanvas::formatKeyPress( QKeyEvent * _ev ) { if (!(_ev->state() & ControlButton )) return false; int key = _ev->key(); if ( key != Key_Exclam && key != Key_At && key != Key_Ampersand && key != Key_Dollar && key != Key_Percent && key != Key_AsciiCircum && key != Key_NumberSign ) return false; KSpreadCell * cell = 0L; KSpreadSheet * table = activeTable(); QRect rect = selection(); m_pDoc->emitBeginOperation(false); table->setRegionPaintDirty( rect ); int right = rect.right(); int bottom = rect.bottom(); if ( !m_pDoc->undoLocked() ) { QString dummy; KSpreadUndoCellFormat * undo = new KSpreadUndoCellFormat( m_pDoc, table, rect, dummy ); m_pDoc->addCommand( undo ); } if ( util_isRowSelected(selection()) ) { for ( int r = rect.top(); r <= bottom; ++r ) { cell = table->getFirstCellRow( r ); while ( cell ) { if ( cell->isObscuringForced() ) { cell = table->getNextCellRight( cell->column(), r ); continue; } - QPen pen; - - switch ( _ev->key() ) - { - case Key_Exclam: - convertToDouble( cell ); - cell->setFormatType (Number_format); - cell->setPrecision( 2 ); - break; - - case Key_Dollar: - convertToMoney( cell ); - break; - - case Key_Percent: - convertToPercent( cell ); - break; - - case Key_At: - convertToTime( cell ); - break; - - case Key_NumberSign: - convertToDate( cell ); - break; - - case Key_AsciiCircum: - cell->setFormatType (Scientific_format); - convertToDouble( cell ); - cell->setFactor( 1.0 ); - break; - - case Key_Ampersand: - if ( r == rect.top() ) - { - pen = QPen( m_pView->borderColor(), 1, SolidLine); - cell->setTopBorderPen( pen ); - } - else if ( r == rect.bottom() ) - { - pen = QPen( m_pView->borderColor(), 1, SolidLine); - cell->setBottomBorderPen( pen ); - } - break; - - default: - m_pDoc->emitEndOperation( rect ); - return false; - } // switch + formatCellByKey (cell, _ev->key(), rect); cell = table->getNextCellRight( cell->column(), r ); } // while (cell) RowFormat * rw = table->nonDefaultRowFormat( r ); QPen pen; switch ( _ev->key() ) { case Key_Exclam: rw->setFormatType (Number_format); rw->setPrecision( 2 ); break; case Key_Dollar: rw->setFormatType (Money_format); rw->setFactor( 1.0 ); rw->setPrecision( m_pDoc->locale()->fracDigits() ); break; case Key_Percent: rw->setFactor( 100.0 ); rw->setFormatType (Percentage_format); break; case Key_At: rw->setFormatType( SecondeTime_format ); rw->setFactor( 1.0 ); break; case Key_NumberSign: rw->setFormatType( ShortDate_format ); rw->setFactor( 1.0 ); break; case Key_AsciiCircum: rw->setFormatType( Scientific_format ); rw->setFactor( 1.0 ); break; case Key_Ampersand: if ( r == rect.top() ) { pen = QPen( m_pView->borderColor(), 1, SolidLine); rw->setTopBorderPen( pen ); } if ( r == rect.bottom() ) { pen = QPen( m_pView->borderColor(), 1, SolidLine); rw->setBottomBorderPen( pen ); } break; default: m_pDoc->emitEndOperation( rect ); return false; } table->emit_updateRow( rw, r ); } m_pDoc->emitEndOperation( rect ); return true; } if ( util_isColumnSelected(selection()) ) { for ( int c = rect.left(); c <= right; ++c ) { cell = table->getFirstCellColumn( c ); while ( cell ) { if ( cell->isObscuringForced() ) { cell = table->getNextCellDown( c, cell->row() ); continue; } - QPen pen; - switch ( _ev->key() ) - { - case Key_Exclam: - convertToDouble( cell ); - cell->setFormatType (Number_format); - cell->setPrecision( 2 ); - break; - - case Key_Dollar: - convertToMoney( cell ); - break; - - case Key_Percent: - convertToPercent( cell ); - break; - - case Key_At: - convertToTime( cell ); - break; - - case Key_NumberSign: - convertToDate( cell ); - break; - - case Key_AsciiCircum: - cell->setFormatType (Scientific_format); - convertToDouble( cell ); - cell->setFactor( 1.0 ); - break; - - case Key_Ampersand: - if ( c == rect.left() ) - { - pen = QPen( m_pView->borderColor(), 1, SolidLine); - cell->setLeftBorderPen( pen ); - } - else if ( c == rect.right() ) - { - pen = QPen( m_pView->borderColor(), 1, SolidLine); - cell->setRightBorderPen( pen ); - } - break; + formatCellByKey (cell, _ev->key(), rect); - default: - m_pDoc->emitEndOperation( rect ); - return false; - } cell = table->getNextCellDown( c, cell->row() ); } ColumnFormat * cw = table->nonDefaultColumnFormat( c ); QPen pen; switch ( _ev->key() ) { case Key_Exclam: cw->setFormatType( Number_format ); cw->setPrecision( 2 ); break; case Key_Dollar: cw->setFormatType( Money_format ); cw->setFactor( 1.0 ); cw->setPrecision( m_pDoc->locale()->fracDigits() ); break; case Key_Percent: cw->setFactor( 100.0 ); cw->setFormatType( Percentage_format ); break; case Key_At: cw->setFormatType( SecondeTime_format ); cw->setFactor( 1.0 ); break; case Key_NumberSign: cw->setFormatType( ShortDate_format ); cw->setFactor( 1.0 ); break; case Key_AsciiCircum: cw->setFactor( 1.0 ); cw->setFormatType( Scientific_format ); break; case Key_Ampersand: if ( c == rect.left() ) { pen = QPen( m_pView->borderColor(), 1, SolidLine); cw->setLeftBorderPen( pen ); } if ( c == rect.right() ) { pen = QPen( m_pView->borderColor(), 1, SolidLine); cw->setRightBorderPen( pen ); } break; default: m_pDoc->emitEndOperation( rect ); return false; } table->emit_updateColumn( cw, c ); } m_pDoc->emitEndOperation( rect ); return true; } for ( int row = rect.top(); row <= bottom; ++row ) { for ( int col = rect.left(); col <= right; ++ col ) { cell = table->nonDefaultCell( col, row ); if ( cell->isObscuringForced() ) continue; + + formatCellByKey (cell, _ev->key(), rect); + } // for left .. right + } // for top .. bottom + _ev->accept(); - QPen pen; - switch ( _ev->key() ) - { - case Key_Exclam: - convertToDouble( cell ); - cell->setFormatType (Number_format); - cell->setPrecision( 2 ); - break; - - case Key_Dollar: - convertToMoney( cell ); - break; + m_pDoc->emitEndOperation( rect ); + return true; +} - case Key_Percent: - convertToPercent( cell ); - break; +bool KSpreadCanvas::formatCellByKey (KSpreadCell *cell, int key, const QRect &rect) +{ + QPen pen; + switch (key) + { + case Key_Exclam: + convertToDouble( cell ); + cell->setFormatType (Number_format); + cell->setPrecision( 2 ); + break; - case Key_At: - convertToTime( cell ); - break; + case Key_Dollar: + convertToMoney( cell ); + break; - case Key_NumberSign: - convertToDate( cell ); - break; + case Key_Percent: + convertToPercent( cell ); + break; - case Key_AsciiCircum: - cell->setFormatType (Scientific_format); - convertToDouble( cell ); - cell->setFactor( 1.0 ); - break; + case Key_At: + convertToTime( cell ); + break; - case Key_Ampersand: - if ( row == rect.top() ) - { - pen = QPen( m_pView->borderColor(), 1, SolidLine); - cell->setTopBorderPen( pen ); - } - if ( row == rect.bottom() ) - { - pen = QPen( m_pView->borderColor(), 1, SolidLine); - cell->setBottomBorderPen( pen ); - } - if ( col == rect.left() ) - { - pen = QPen( m_pView->borderColor(), 1, SolidLine); - cell->setLeftBorderPen( pen ); - } - if ( col == rect.right() ) - { - pen = QPen( m_pView->borderColor(), 1, SolidLine); - cell->setRightBorderPen( pen ); - } - break; + case Key_NumberSign: + convertToDate( cell ); + break; - default: - m_pDoc->emitEndOperation( rect ); - return false; - } // switch - } // for left .. right - } // for top .. bottom - _ev->accept(); + case Key_AsciiCircum: + cell->setFormatType (Scientific_format); + convertToDouble( cell ); + cell->setFactor( 1.0 ); + break; - m_pDoc->emitEndOperation( rect ); + case Key_Ampersand: + if ( cell->row() == rect.top() ) + { + pen = QPen( m_pView->borderColor(), 1, SolidLine); + cell->setTopBorderPen( pen ); + } + if ( cell->row() == rect.bottom() ) + { + pen = QPen( m_pView->borderColor(), 1, SolidLine); + cell->setBottomBorderPen( pen ); + } + if ( cell->column() == rect.left() ) + { + pen = QPen( m_pView->borderColor(), 1, SolidLine); + cell->setLeftBorderPen( pen ); + } + if ( cell->column() == rect.right() ) + { + pen = QPen( m_pView->borderColor(), 1, SolidLine); + cell->setRightBorderPen( pen ); + } + break; + } // switch + return true; } void KSpreadCanvas::doAutoScroll() { if ( !m_bMousePressed ) { m_scrollTimer->stop(); return; } bool select = false; QPoint pos( mapFromGlobal( QCursor::pos() ) ); //Provide progressive scrolling depending on the mouse position if ( pos.y() < 0 ) { vertScrollBar()->setValue ((int) (vertScrollBar()->value() - autoScrollAccelerationY( - pos.y()))); select = true; } else if ( pos.y() > height() ) { vertScrollBar()->setValue ((int) (vertScrollBar()->value() + autoScrollAccelerationY (pos.y() - height()))); select = true; } if ( pos.x() < 0 ) { horzScrollBar()->setValue ((int) (horzScrollBar()->value() - autoScrollAccelerationX( - pos.x() ))); select = true; } else if ( pos.x() > width() ) { horzScrollBar()->setValue ((int) (horzScrollBar()->value() + autoScrollAccelerationX( pos.x() - width()))); select = true; } if ( select ) { QMouseEvent * event = new QMouseEvent(QEvent::MouseMove, pos, 0, 0); mouseMoveEvent( event ); delete event; } //Restart timer m_scrollTimer->start( 50 ); } double KSpreadCanvas::autoScrollAccelerationX( int offset ) { switch( static_cast( offset / 20 ) ) { case 0: return 5.0; case 1: return 20.0; case 2: return doc()->unzoomItX( width() ); case 3: return doc()->unzoomItX( width() ); default: return doc()->unzoomItX( (int) (width() * 5.0) ); } } double KSpreadCanvas::autoScrollAccelerationY( int offset ) { switch( static_cast( offset / 20 ) ) { case 0: return 5.0; case 1: return 20.0; case 2: return doc()->unzoomItY( height() ); case 3: return doc()->unzoomItY( height() ); default: return doc()->unzoomItY( (int) (height() * 5.0) ); } } void KSpreadCanvas::deleteEditor( bool saveChanges ) { if ( !m_pEditor ) return; // We need to set the line-edit out of edit mode, // but only if we are using it (text editor) // A bit of a hack - perhaps we should store the editor mode ? bool textEditor = true; if ( m_pEditor->inherits("KSpreadTextEditor") ) m_pEditWidget->setEditMode( false ); else textEditor = false; QString t = m_pEditor->text(); // Delete the cell editor first and after that update the document. // That means we get a synchronous repaint after the cell editor // widget is gone. Otherwise we may get painting errors. delete m_pEditor; m_pEditor = 0; if ( saveChanges && textEditor ) { if ( t.at(0)=='=' ) { //a formula int openParenthese = t.contains('(' ); int closeParenthese = t.contains(')' ); int diff = QABS( openParenthese - closeParenthese ); if ( openParenthese > closeParenthese ) { for (int i=0; i < diff;i++) { t=t+')'; } } } m_pView->setText( t ); } else m_pView->updateEditWidget(); setFocus(); } void KSpreadCanvas::createEditor() { KSpreadCell * cell = activeTable()->nonDefaultCell( markerColumn(), markerRow(), false ); if ( !createEditor( CellEditor ) ) return; if ( cell ) m_pEditor->setText( cell->text() ); } bool KSpreadCanvas::createEditor( EditorType ed, bool addFocus ) { KSpreadSheet * table = activeTable(); if ( !m_pEditor ) { KSpreadCell * cell = table->nonDefaultCell( marker().x(), marker().y(), false ); if ( table->isProtected() && !cell->notProtected( marker().x(), marker().y() ) ) return false; if ( ed == CellEditor ) { m_pEditWidget->setEditMode( true ); m_pEditor = new KSpreadTextEditor( cell, this ); } double w, h; double min_w = cell->dblWidth( markerColumn() ); double min_h = cell->dblHeight( markerRow() ); if ( cell->isDefault() ) { w = min_w; h = min_h; //kdDebug(36001) << "DEFAULT" << endl; } else { w = cell->extraWidth(); h = cell->extraHeight(); //kdDebug(36001) << "HEIGHT=" << min_h << " EXTRA=" << h << endl; } double xpos; if ( table->layoutDirection() == KSpreadSheet::RightToLeft ) { double dwidth = doc()->unzoomItX( width() ); xpos = dwidth - min_w - table->dblColumnPos( markerColumn() ) + xOffset(); } else xpos = table->dblColumnPos( markerColumn() ) - xOffset(); double ypos = table->dblRowPos( markerRow() ) - yOffset(); QPalette p = m_pEditor->palette(); QColorGroup g( p.active() ); QColor color = cell->textColor( markerColumn(), markerRow() ); if ( !color.isValid() ) color = QApplication::palette().active().text(); g.setColor( QColorGroup::Text, color); color = cell->bgColor( markerColumn(), markerRow() ); if ( !color.isValid() ) color = g.base(); g.setColor( QColorGroup::Background, color ); m_pEditor->setPalette( QPalette( g, p.disabled(), g ) ); QFont tmpFont = cell->textFont( markerColumn(), markerRow() ); tmpFont.setPointSizeFloat( 0.01 * doc()->zoom() * tmpFont.pointSizeFloat() ); m_pEditor->setFont( tmpFont ); KoRect rect( xpos, ypos, w, h ); //needed to circumvent rounding issue with height/width m_pEditor->setGeometry( doc()->zoomRect( rect ) ); m_pEditor->setMinimumSize( QSize( doc()->zoomItX( min_w ), doc()->zoomItY( min_h ) ) ); m_pEditor->show(); //kdDebug(36001) << "FOCUS1" << endl; //Laurent 2001-12-05 //Don't add focus when we create a new editor and //we select text in edit widget otherwise we don't delete //selected text. if ( addFocus ) m_pEditor->setFocus(); //kdDebug(36001) << "FOCUS2" << endl; } return true; } void KSpreadCanvas::closeEditor() { if ( m_bChoose ) return; if ( m_pEditor ) { deleteEditor( true ); // save changes } } void KSpreadCanvas::updateChooseRect(const QPoint &newMarker, const QPoint &newAnchor) { if( !m_bChoose ) return; KSpreadSheet* table = activeTable(); if ( ! table ) return; QPoint oldAnchor = selectionInfo()->getChooseAnchor(); QPoint oldMarker = selectionInfo()->getChooseMarker(); QPoint chooseCursor = selectionInfo()->getChooseCursor(); QRect oldChooseRect = selectionInfo()->getChooseRect(); if ( newMarker == oldMarker && newAnchor == oldAnchor ) { return; } selectionInfo()->setChooseMarker(newMarker); selectionInfo()->setChooseAnchor(newAnchor); QRect newChooseRect = selectionInfo()->getChooseRect(); /* keep the choose cursor updated. If you don't know what the 'cursor' is supposed to represent, check the comments of the regular selection cursor in kspread_selection.h (KSpreadSelection::m_cursorPosition). It's the same thing here except for the choose selection. */ if ( !newChooseRect.contains(chooseCursor) ) { selectionInfo()->setChooseCursor(table, newMarker); } m_pDoc->emitBeginOperation(); setSelectionChangePaintDirty(table, oldChooseRect, newChooseRect); repaint(); m_pDoc->emitEndOperation(); /* this signal is used in the formula editor to update the text display */ emit m_pView->sig_chooseSelectionChanged(activeTable(), newChooseRect); if ( !m_pEditor ) { length_namecell = 0; return; } /* the rest of this function updates the text showing the choose rect */ /***** TODO - should this be here? */ if (newMarker.x() != 0 && newMarker.y() != 0) /* don't update the text if we are removing the marker */ { QString name_cell; if ( m_chooseStartTable != table ) { if ( newMarker == newAnchor ) name_cell = KSpreadCell::fullName( table, newChooseRect.left(), newChooseRect.top() ); else name_cell = util_rangeName( table, newChooseRect ); } else { if ( newMarker == newAnchor ) name_cell = KSpreadCell::name( newChooseRect.left(), newChooseRect.top() ); else name_cell = util_rangeName( newChooseRect ); } int old = length_namecell; length_namecell= name_cell.length(); length_text = m_pEditor->text().length(); //kdDebug(36001) << "updateChooseMarker2 len=" << length_namecell << endl; QString text = m_pEditor->text(); QString res = text.left( m_pEditor->cursorPosition() - old ) + name_cell + text.right( text.length() - m_pEditor->cursorPosition() ); int pos = m_pEditor->cursorPosition() - old; ((KSpreadTextEditor*)m_pEditor)->blockCheckChoose( TRUE ); m_pEditor->setText( res ); ((KSpreadTextEditor*)m_pEditor)->blockCheckChoose( FALSE ); m_pEditor->setCursorPosition( pos + length_namecell ); editWidget()->setText( res ); //kdDebug(36001) << "old=" << old << " len=" << length_namecell << " pos=" << pos << endl; } } void KSpreadCanvas::setSelectionChangePaintDirty(KSpreadSheet* sheet, QRect area1, QRect area2) { QValueList cellRegions; /* first of all, let's not get confused by an unset region at 0,0,0,0 Just reset to region to something ridiculous that will be ignored by a paint call */ if (area1.contains(QPoint(0,0))) { area1.setLeft(-100); area1.setRight(-100); } if (area2.contains(QPoint(0,0))) { area2.setLeft(-50); area2.setRight(-50); } /* let's try to only paint where the selection is actually changing*/ bool newLeft = area1.left() != area2.left(); bool newTop = area1.top() != area2.top(); bool newRight = area1.right() != area2.right(); bool newBottom = area1.bottom() != area2.bottom(); bool topLeftSame = !newLeft && !newTop; bool topRightSame = !newTop && !newRight; bool bottomLeftSame = !newLeft && !newBottom; bool bottomRightSame = !newBottom && !newRight; if (!topLeftSame && !topRightSame && !bottomLeftSame && !bottomRightSame) { /* the two areas are not related. */ /* since the marker/selection border extends into neighboring cells, we want to calculate all the cells bordering these regions. */ ExtendRectBorder(area1); ExtendRectBorder(area2); cellRegions.append(area1); cellRegions.append(area2); } else { /* at least one corner is the same -- let's only paint the extension on corners that are not the same */ /* first, calculate some numbers that we'll use a few times */ int farLeft = QMIN(area1.left(), area2.left()); if (farLeft != 1) farLeft--; int innerLeft = QMAX(area1.left(), area2.left()); if (innerLeft != KS_colMax) innerLeft++; int farTop = QMIN(area1.top(), area2.top()); if (farTop != 1) farTop--; int innerTop = QMAX(area1.top(), area2.top()); if (innerTop != KS_rowMax) innerTop++; int farRight = QMAX(area1.right(), area2.right()); if (farRight != KS_colMax) farRight++; int innerRight = QMIN(area1.right(), area2.right()); if (innerRight != 1) innerRight--; int farBottom = QMAX(area1.bottom(), area2.bottom()); if (farBottom!= KS_rowMax) farBottom++; int innerBottom = QMIN(area1.bottom(), area2.bottom()); if (innerBottom != 1) innerBottom--; if (newLeft) { cellRegions.append(QRect(QPoint(farLeft, farTop), QPoint(innerLeft, farBottom))); } if (newTop) { cellRegions.append(QRect(QPoint(farLeft, farTop), QPoint(farRight, innerTop))); } if (newRight) { cellRegions.append(QRect(QPoint(innerRight, farTop), QPoint(farRight, farBottom))); } if (newBottom) { cellRegions.append(QRect(QPoint(farLeft, innerBottom), QPoint(farRight, farBottom))); } } QValueList::iterator it = cellRegions.begin(); while (it != cellRegions.end()) { sheet->setRegionPaintDirty(*it); it++; } } void KSpreadCanvas::ExtendRectBorder(QRect& area) { ColumnFormat *cl; RowFormat *rl; //look at if column is hiding. //if it's hiding refreshing column+1 (or column -1 ) int left = area.left(); int right = area.right(); int top = area.top(); int bottom = area.bottom(); //Maybe the case for ridiculous settings, see setSelectionChangePaintDirty //No need to extend then, avoids warnings if ( left < 1 && right < 1 ) return; if ( right < KS_colMax ) { do { right++; cl = activeTable()->nonDefaultColumnFormat( right ); } while ( cl->isHide() && right != KS_colMax ); } if ( left > 1 ) { do { left--; cl = activeTable()->nonDefaultColumnFormat( left ); } while ( cl->isHide() && left != 1); } if ( bottom < KS_rowMax ) { do { bottom++; rl = activeTable()->nonDefaultRowFormat( bottom ); } while ( rl->isHide() && bottom != KS_rowMax ); } if ( top > 1 ) { do { top--; rl = activeTable()->nonDefaultRowFormat( top ); } while ( rl->isHide() && top != 1); } area.setLeft(left); area.setRight(right); area.setTop(top); area.setBottom(bottom); } void KSpreadCanvas::updatePosWidget() { QString buffer; // No selection, or only one cell merged selected if ( selectionInfo()->singleCellSelection() ) { if (activeTable()->getLcMode()) { buffer = "L" + QString::number( markerRow() ) + "C" + QString::number( markerColumn() ); } else { buffer = KSpreadCell::columnName( markerColumn() ) + QString::number( markerRow() ); } } else { if (activeTable()->getLcMode()) { buffer = QString::number( (selection().bottom()-selection().top()+1) )+"Lx"; if ( util_isRowSelected( selection() ) ) buffer+=QString::number((KS_colMax-selection().left()+1))+"C"; else buffer+=QString::number((selection().right()-selection().left()+1))+"C"; } else { //encodeColumnLabelText return @@@@ when column >KS_colMax //=> it's not a good display //=> for the moment I display pos of marker buffer=KSpreadCell::columnName( selection().left() ) + QString::number(selection().top()) + ":" + KSpreadCell::columnName( QMIN( KS_colMax, selection().right() ) ) + QString::number(selection().bottom()); //buffer=activeTable()->columnLabel( m_iMarkerColumn ); //buffer+=tmp.setNum(m_iMarkerRow); } } if (buffer != m_pPosWidget->lineEdit()->text()) m_pPosWidget->lineEdit()->setText(buffer); } void KSpreadCanvas::adjustArea(bool makeUndo) { QRect s( selection() ); if (activeTable()->areaIsEmpty(s)) return; if (makeUndo) { if ( !doc()->undoLocked() ) { KSpreadUndoResizeColRow *undo = new KSpreadUndoResizeColRow( doc(),activeTable() , s ); doc()->addCommand( undo ); } } // Columns selected if ( util_isColumnSelected(s) ) { for (int x=s.left(); x <= s.right(); x++ ) { hBorderWidget()->adjustColumn(x,false); } } // Rows selected else if ( util_isRowSelected(s) ) { for(int y = s.top(); y <= s.bottom(); y++ ) { vBorderWidget()->adjustRow(y,false); } } // No selection // Selection of a rectangular area else { for (int x=s.left(); x <= s.right(); x++ ) { hBorderWidget()->adjustColumn(x,false); } for(int y = s.top(); y <= s.bottom(); y++ ) { vBorderWidget()->adjustRow(y,false); } } } void KSpreadCanvas::equalizeRow() { QRect s( selection() ); RowFormat *rl = m_pView->activeTable()->rowFormat(s.top()); int size=rl->height(this); if ( s.top() == s.bottom() ) return; for(int i=s.top()+1;i<=s.bottom();i++) { KSpreadSheet *table = activeTable(); if ( !table ) return; size=QMAX(m_pView->activeTable()->rowFormat(i)->height(this),size); } m_pView->vBorderWidget()->equalizeRow(size); } void KSpreadCanvas::equalizeColumn() { QRect s( selection() ); ColumnFormat *cl = m_pView->activeTable()->columnFormat(s.left()); int size=cl->width(this); if ( s.left() == s.right() ) return; for(int i=s.left()+1;i<=s.right();i++) { size=QMAX(m_pView->activeTable()->columnFormat(i)->width(this),size); } m_pView->hBorderWidget()->equalizeColumn(size); } QRect KSpreadCanvas::visibleCells() { KoRect unzoomedRect = doc()->unzoomRect( QRect( 0, 0, width(), height() ) ); unzoomedRect.moveBy( xOffset(), yOffset() ); double tmp; int left_col = activeTable()->leftColumn( unzoomedRect.left(), tmp ); int right_col = activeTable()->rightColumn( unzoomedRect.right() ); int top_row = activeTable()->topRow( unzoomedRect.top(), tmp ); int bottom_row = activeTable()->bottomRow( unzoomedRect.bottom() ); return QRect( left_col, top_row, right_col - left_col + 1, bottom_row - top_row + 1 ); } //--------------------------------------------- // // Drawing Engine // //--------------------------------------------- void KSpreadCanvas::paintUpdates() { if (activeTable() == NULL) return; QPainter painter(this); //Save clip region QRegion rgnComplete( painter.clipRegion() ); QWMatrix matrix; if ( m_pView ) { matrix = m_pView->matrix(); } else { matrix = painter.worldMatrix(); } painter.save(); clipoutChildren( painter, matrix ); KoRect unzoomedRect = doc()->unzoomRect( QRect( 0, 0, width(), height() ) ); // unzoomedRect.moveBy( xOffset(), yOffset() ); /* paint any visible cell that has the paintDirty flag */ QRect range = visibleCells(); KSpreadCell* cell = NULL; double topPos = activeTable()->dblRowPos(range.top()); double leftPos = activeTable()->dblColumnPos(range.left()); KoPoint dblCorner( leftPos - xOffset(), topPos - yOffset() ); int x; int y; int right = range.right(); int bottom = range.bottom(); KSpreadSheet * sheet = activeTable(); for ( x = range.left(); x <= right; ++x ) { for ( y = range.top(); y <= bottom; ++y ) { if ( sheet->cellIsPaintDirty( QPoint( x, y ) ) ) { cell = sheet->cellAt( x, y ); // recalc and relayout only for non default cells if( !cell->isDefault() ) { cell->calc(); cell->makeLayout( painter, x, y ); } bool paintBordersBottom = false; bool paintBordersRight = false; bool paintBordersLeft = false; bool paintBordersTop = false; QPen bottomPen( cell->effBottomBorderPen( x, y ) ); QPen rightPen( cell->effRightBorderPen( x, y ) ); QPen leftPen( cell->effLeftBorderPen( x, y ) ); QPen topPen( cell->effTopBorderPen( x, y ) ); // paint right border if rightmost cell or if the pen is more "worth" than the left border pen // of the cell on the left or if the cell on the right is not painted. In the latter case get // the pen that is of more "worth" if ( x >= KS_colMax ) paintBordersRight = true; else if ( sheet->cellIsPaintDirty( QPoint( x + 1, y ) ) ) { paintBordersRight = true; if ( cell->effRightBorderValue( x, y ) < sheet->cellAt( x + 1, y )->effLeftBorderValue( x + 1, y ) ) rightPen = sheet->cellAt( x + 1, y )->effLeftBorderPen( x + 1, y ); } else { paintBordersRight = true; if ( cell->effRightBorderValue( x, y ) < sheet->cellAt( x + 1, y )->effLeftBorderValue( x + 1, y ) ) rightPen = sheet->cellAt( x + 1, y )->effLeftBorderPen( x + 1, y ); } // similiar for other borders... // bottom border: if ( y >= KS_rowMax ) paintBordersBottom = true; else if ( sheet->cellIsPaintDirty( QPoint( x, y + 1 ) ) ) { if ( cell->effBottomBorderValue( x, y ) > sheet->cellAt( x, y + 1 )->effTopBorderValue( x, y + 1 ) ) paintBordersBottom = true; } else { paintBordersBottom = true; if ( cell->effBottomBorderValue( x, y ) < sheet->cellAt( x, y + 1 )->effTopBorderValue( x, y + 1 ) ) bottomPen = sheet->cellAt( x, y + 1 )->effTopBorderPen( x, y + 1 ); } // left border: if ( x == 1 ) paintBordersLeft = true; else if ( sheet->cellIsPaintDirty( QPoint( x - 1, y ) ) ) { paintBordersLeft = true; if ( cell->effLeftBorderValue( x, y ) < sheet->cellAt( x - 1, y )->effRightBorderValue( x - 1, y ) ) leftPen = sheet->cellAt( x - 1, y )->effRightBorderPen( x - 1, y ); } else { paintBordersLeft = true; if ( cell->effLeftBorderValue( x, y ) < sheet->cellAt( x - 1, y )->effRightBorderValue( x - 1, y ) ) leftPen = sheet->cellAt( x - 1, y )->effRightBorderPen( x - 1, y ); } // top border: if ( y == 1 ) paintBordersTop = true; else if ( sheet->cellIsPaintDirty( QPoint( x, y - 1 ) ) ) { paintBordersTop = true; if ( cell->effTopBorderValue( x, y ) < sheet->cellAt( x, y - 1 )->effBottomBorderValue( x, y - 1 ) ) topPen = sheet->cellAt( x, y - 1 )->effBottomBorderPen( x, y - 1 ); } else { paintBordersTop = true; if ( cell->effTopBorderValue( x, y ) < sheet->cellAt( x, y - 1 )->effBottomBorderValue( x, y - 1 ) ) topPen = sheet->cellAt( x, y - 1 )->effBottomBorderPen( x, y - 1 ); } cell->paintCell( unzoomedRect, painter, m_pView, dblCorner, QPoint( x, y ), paintBordersRight, paintBordersBottom, paintBordersLeft, paintBordersTop, rightPen, bottomPen, leftPen, topPen ); } dblCorner.setY( dblCorner.y() + sheet->rowFormat( y )->dblHeight( ) ); } dblCorner.setY( topPos - yOffset() ); dblCorner.setX( dblCorner.x() + sheet->columnFormat( x )->dblWidth( ) ); } /* now paint the selection and choose selection */ paintChooseRect(painter, unzoomedRect); paintNormalMarker(painter, unzoomedRect); //restore clip region with children area painter.restore(); painter.setClipRegion( rgnComplete ); paintChildren( painter, matrix ); } void KSpreadCanvas::clipoutChildren( QPainter& painter, QWMatrix& matrix ) { QRegion rgn = painter.clipRegion(); if ( rgn.isEmpty() ) rgn = QRegion( QRect( 0, 0, width(), height() ) ); QPtrListIterator itChild( m_pDoc->children() ); for( ; itChild.current(); ++itChild ) { // if ( ((KSpreadChild*)it.current())->table() == table && // !m_pView->hasDocumentInWindow( it.current()->document() ) ) if ( ( ( KSpreadChild*)itChild.current() )->table() == activeTable() ) { rgn -= itChild.current()->region( matrix ); } } painter.setClipRegion( rgn ); } void KSpreadCanvas::paintChildren( QPainter& painter, QWMatrix& matrix ) { painter.setWorldMatrix( matrix ); QPtrListIterator itChild( m_pDoc->children() ); itChild.toFirst(); for( ; itChild.current(); ++itChild ) { if ( ( ( KSpreadChild*)itChild.current() )->table() == activeTable() && ( m_pView && !m_pView->hasDocumentInWindow( itChild.current()->document() ) ) ) { // #### todo: paint only if child is visible inside rect painter.save(); m_pDoc->paintChild( itChild.current(), painter, m_pView, m_pDoc->zoomedResolutionX(), m_pDoc->zoomedResolutionY() ); painter.restore(); } } } void KSpreadCanvas::paintChooseRect(QPainter& painter, const KoRect &viewRect) { double positions[4]; bool paintSides[4]; QRect chooseRect = m_pView->selectionInfo()->getChooseRect(); if ( chooseRect.left() != 0 ) { QPen pen; pen.setWidth( 2 ); pen.setStyle( DashLine ); retrieveMarkerInfo( chooseRect, viewRect, positions, paintSides ); double left = positions[0]; double top = positions[1]; double right = positions[2]; double bottom = positions[3]; bool paintLeft = paintSides[0]; bool paintTop = paintSides[1]; bool paintRight = paintSides[2]; bool paintBottom = paintSides[3]; RasterOp rop = painter.rasterOp(); painter.setRasterOp( NotROP ); painter.setPen( pen ); if ( paintTop ) { painter.drawLine( doc()->zoomItX( left ), doc()->zoomItY( top ), doc()->zoomItX( right ), doc()->zoomItY( top ) ); } if ( paintLeft ) { painter.drawLine( doc()->zoomItX( left ), doc()->zoomItY( top ), doc()->zoomItX( left ), doc()->zoomItY( bottom ) ); } if ( paintRight ) { painter.drawLine( doc()->zoomItX( right ), doc()->zoomItY( top ), doc()->zoomItX( right ), doc()->zoomItY( bottom ) ); } if ( paintBottom ) { painter.drawLine( doc()->zoomItX( left ), doc()->zoomItY( bottom ), doc()->zoomItX( right ), doc()->zoomItY( bottom ) ); } /* restore the old raster mode */ painter.setRasterOp( rop ); } return; } void KSpreadCanvas::paintNormalMarker(QPainter& painter, const KoRect &viewRect) { if( m_bChoose ) return; double positions[4]; bool paintSides[4]; QRect marker = selection(); QPen pen( Qt::black, 3 ); painter.setPen( pen ); retrieveMarkerInfo( marker, viewRect, positions, paintSides ); painter.setPen( pen ); double left = positions[0]; double top = positions[1]; double right = positions[2]; double bottom = positions[3]; bool paintLeft = paintSides[0]; bool paintTop = paintSides[1]; bool paintRight = paintSides[2]; bool paintBottom = paintSides[3]; /* the extra '-1's thrown in here account for the thickness of the pen. want to look like this: not this: * * * * * * * * * * * * * * . * * * * */ int l = 1; if ( paintTop ) { painter.drawLine( doc()->zoomItX( left ) - l, doc()->zoomItY( top ), doc()->zoomItX( right ) + 2 * l, doc()->zoomItY( top ) ); } if ( activeTable()->layoutDirection()==KSpreadSheet::RightToLeft ) { if ( paintRight ) { painter.drawLine( doc()->zoomItX( right ), doc()->zoomItY( top ), doc()->zoomItX( right ), doc()->zoomItY( bottom ) ); } if ( paintLeft && paintBottom ) { /* then the 'handle' in the bottom left corner is visible. */ painter.drawLine( doc()->zoomItX( left ), doc()->zoomItY( top ), doc()->zoomItX( left ), doc()->zoomItY( bottom ) - 3 ); painter.drawLine( doc()->zoomItX( left ) + 4, doc()->zoomItY( bottom ), doc()->zoomItX( right ) + l + 1, doc()->zoomItY( bottom ) ); painter.fillRect( doc()->zoomItX( left ) - 2, doc()->zoomItY( bottom ) -2, 5, 5, painter.pen().color() ); } else { if ( paintLeft ) { painter.drawLine( doc()->zoomItX( left ), doc()->zoomItY( top ), doc()->zoomItX( left ), doc()->zoomItY( bottom ) ); } if ( paintBottom ) { painter.drawLine( doc()->zoomItX( left ) - l, doc()->zoomItY( bottom ), doc()->zoomItX( right ) + l + 1, doc()->zoomItY( bottom )); } } } else { if ( paintLeft ) { painter.drawLine( doc()->zoomItX( left ), doc()->zoomItY( top ), doc()->zoomItX( left ), doc()->zoomItY( bottom ) ); } if ( paintRight && paintBottom ) { /* then the 'handle' in the bottom right corner is visible. */ painter.drawLine( doc()->zoomItX( right ), doc()->zoomItY( top ), doc()->zoomItX( right ), doc()->zoomItY( bottom ) - 3 ); painter.drawLine( doc()->zoomItX( left ) - l, doc()->zoomItY( bottom ), doc()->zoomItX( right ) - 3, doc()->zoomItY( bottom ) ); painter.fillRect( doc()->zoomItX( right ) - 2, doc()->zoomItY( bottom ) - 2, 5, 5, painter.pen().color() ); } else { if ( paintRight ) { painter.drawLine( doc()->zoomItX( right ), doc()->zoomItY( top ), doc()->zoomItX( right ), doc()->zoomItY( bottom ) ); } if ( paintBottom ) { painter.drawLine( doc()->zoomItX( left ) - l, doc()->zoomItY( bottom ), doc()->zoomItX( right ) + l, doc()->zoomItY( bottom ) ); } } } } void KSpreadCanvas::retrieveMarkerInfo( const QRect &marker, const KoRect &viewRect, double positions[], bool paintSides[] ) { KSpreadSheet * table = activeTable(); if ( !table ) return; double dWidth = doc()->unzoomItX( width() ); double xpos; double x; if ( table->layoutDirection()==KSpreadSheet::RightToLeft ) { xpos = dWidth - table->dblColumnPos( marker.right() ) + xOffset(); x = dWidth - table->dblColumnPos( marker.left() ) + xOffset(); } else { xpos = table->dblColumnPos( marker.left() ) - xOffset(); x = table->dblColumnPos( marker.right() ) - xOffset(); } double ypos = table->dblRowPos( marker.top() ) - yOffset(); const ColumnFormat *columnFormat = table->columnFormat( marker.right() ); double tw = columnFormat->dblWidth( ); double w = x - xpos + tw; double y = table->dblRowPos( marker.bottom() ) - yOffset(); const RowFormat* rowFormat = table->rowFormat( marker.bottom() ); double th = rowFormat->dblHeight( ); double h = ( y - ypos ) + th; /* left, top, right, bottom */ if ( table->layoutDirection()==KSpreadSheet::RightToLeft ) { positions[0] = xpos - tw; positions[2] = xpos - tw + w; } else { positions[0] = xpos; positions[2] = xpos + w; } positions[1] = ypos; positions[3] = ypos + h; /* these vars are used for clarity, the array for simpler function arguments */ double left = positions[0]; double top = positions[1]; double right = positions[2]; double bottom = positions[3]; /* left, top, right, bottom */ paintSides[0] = (viewRect.left() <= left) && (left <= viewRect.right()) && (bottom >= viewRect.top()) && (top <= viewRect.bottom()); paintSides[1] = (viewRect.top() <= top) && (top <= viewRect.bottom()) && (right >= viewRect.left()) && (left <= viewRect.right()); if ( table->layoutDirection()==KSpreadSheet::RightToLeft ) paintSides[2] = (viewRect.left() <= right ) && (right - 1 <= viewRect.right()) && (bottom >= viewRect.top()) && (top <= viewRect.bottom()); else paintSides[2] = (viewRect.left() <= right ) && (right <= viewRect.right()) && (bottom >= viewRect.top()) && (top <= viewRect.bottom()); paintSides[3] = (viewRect.top() <= bottom) && (bottom <= viewRect.bottom()) && (right >= viewRect.left()) && (left <= viewRect.right()); positions[0] = QMAX( left, viewRect.left() ); positions[1] = QMAX( top, viewRect.top() ); positions[2] = QMIN( right, viewRect.right() ); positions[3] = QMIN( bottom, viewRect.bottom() ); } /**************************************************************** * * KSpreadVBorder * ****************************************************************/ KSpreadVBorder::KSpreadVBorder( QWidget *_parent, KSpreadCanvas *_canvas, KSpreadView *_view) : QWidget( _parent, "", /*WNorthWestGravity*/WStaticContents | WResizeNoErase | WRepaintNoErase ) { m_pView = _view; m_pCanvas = _canvas; m_lSize = 0L; setBackgroundMode( PaletteButton ); setMouseTracking( TRUE ); m_bResize = FALSE; m_bSelection = FALSE; m_iSelectionAnchor=1; m_bMousePressed = FALSE; m_scrollTimer = new QTimer( this ); connect (m_scrollTimer, SIGNAL( timeout() ), this, SLOT( doAutoScroll() ) ); } KSpreadVBorder::~KSpreadVBorder() { delete m_scrollTimer; } QSize KSpreadVBorder::sizeHint() const { return QSize( 40, 10 ); } void KSpreadVBorder::mousePressEvent( QMouseEvent * _ev ) { if ( !m_pView->koDocument()->isReadWrite() ) return; if ( _ev->button() == LeftButton ) m_bMousePressed = true; const KSpreadSheet *table = m_pCanvas->activeTable(); assert( table ); double ev_PosY = m_pCanvas->doc()->unzoomItY( _ev->pos().y() ) + m_pCanvas->yOffset(); double dHeight = m_pCanvas->doc()->unzoomItY( height() ); m_bResize = FALSE; m_bSelection = FALSE; // We were editing a cell -> save value and get out of editing mode if ( m_pCanvas->editor() ) { m_pCanvas->deleteEditor( true ); // save changes } m_scrollTimer->start( 50 ); // Find the first visible row and the y position of this row. double y; int row = table->topRow( m_pCanvas->yOffset(), y ); // Did the user click between two rows? while ( y < ( dHeight + m_pCanvas->yOffset() ) && ( !m_bResize ) ) { double h = table->rowFormat( row )->dblHeight(); row++; if ( row > KS_rowMax ) row = KS_rowMax; if ( ( ev_PosY >= y + h - 2 ) && ( ev_PosY <= y + h + 1 ) && !( table->rowFormat( row )->isHide() && row == 1 ) ) m_bResize = TRUE; y += h; } //if row is hide and it's the first row //you mustn't resize it. double tmp2; int tmpRow = table->topRow( ev_PosY - 1, tmp2 ); if ( table->rowFormat( tmpRow )->isHide() && tmpRow == 1 ) m_bResize = false; // So he clicked between two rows ? if ( m_bResize ) { // Determine row to resize double tmp; m_iResizedRow = table->topRow( ev_PosY - 1, tmp ); if ( !table->isProtected() ) paintSizeIndicator( _ev->pos().y(), true ); } else { m_bSelection = TRUE; double tmp; int hit_row = table->topRow( ev_PosY, tmp ); if ( hit_row > KS_rowMax ) return; m_iSelectionAnchor = hit_row; QRect rect = m_pView->selection(); if ( !rect.contains( QPoint(1, hit_row) ) || !( _ev->button() == RightButton ) || ( !util_isRowSelected( rect ) ) ) { QPoint newMarker( 1, hit_row ); QPoint newAnchor( KS_colMax, hit_row ); m_pView->selectionInfo()->setSelection( newMarker, newAnchor, m_pView->activeTable() ); } if ( _ev->button() == RightButton ) { QPoint p = mapToGlobal( _ev->pos() ); m_pView->popupRowMenu( p ); m_bSelection = FALSE; } m_pView->updateEditWidget(); } } void KSpreadVBorder::mouseReleaseEvent( QMouseEvent * _ev ) { if ( m_scrollTimer->isActive() ) m_scrollTimer->stop(); m_bMousePressed = false; if ( !m_pView->koDocument()->isReadWrite() ) return; KSpreadSheet *table = m_pCanvas->activeTable(); assert( table ); double ev_PosY = m_pCanvas->doc()->unzoomItY( _ev->pos().y() ) + m_pCanvas->yOffset(); if ( m_bResize ) { // Remove size indicator painted by paintSizeIndicator QPainter painter; painter.begin( m_pCanvas ); painter.setRasterOp( NotROP ); painter.drawLine( 0, m_iResizePos, m_pCanvas->width(), m_iResizePos ); painter.end(); int start = m_iResizedRow; int end = m_iResizedRow; QRect rect; rect.setCoords( 1, m_iResizedRow, KS_colMax, m_iResizedRow ); if ( util_isRowSelected( m_pView->selection() ) ) { if ( m_pView->selection().contains( QPoint( 1, m_iResizedRow ) ) ) { start = m_pView->selection().top(); end = m_pView->selection().bottom(); rect = m_pView->selection(); } } double height = 0.0; double y = table->dblRowPos( m_iResizedRow ); if ( ev_PosY - y <= 0.0 ) height = 0.0; else height = ev_PosY - y; if ( !table->isProtected() ) { if ( !m_pCanvas->doc()->undoLocked() ) { //just resize if ( height != 0.0 ) { KSpreadUndoResizeColRow *undo = new KSpreadUndoResizeColRow( m_pCanvas->doc(), m_pCanvas->activeTable(), rect ); m_pCanvas->doc()->addCommand( undo ); } else { //hide row KSpreadUndoHideRow *undo = new KSpreadUndoHideRow( m_pCanvas->doc(), m_pCanvas->activeTable(), rect.top(), ( rect.bottom() - rect.top() ) ); m_pCanvas->doc()->addCommand( undo ); } } for( int i = start; i <= end; i++ ) { RowFormat *rl = table->nonDefaultRowFormat( i ); if ( height != 0.0 ) { if ( !rl->isHide() ) rl->setDblHeight( height ); } else rl->setHide( true ); } if ( height == 0.0 ) table->emitHideColumn(); delete m_lSize; m_lSize = 0; } } else if ( m_bSelection ) { QRect rect = m_pView->selection(); // TODO: please don't remove. Right now it's useless, but it's for a future feature // Norbert bool m_frozen = false; if ( m_frozen ) { kdDebug(36001) << "selected: T " << rect.top() << " B " << rect.bottom() << endl; int i; RowFormat * row; QValueListhiddenRows; for ( i = rect.top(); i <= rect.bottom(); ++i ) { row = m_pView->activeTable()->rowFormat( i ); if ( row->isHide() ) { hiddenRows.append(i); } } if ( hiddenRows.count() > 0 ) m_pView->activeTable()->showRow( 0, -1, hiddenRows ); } } m_bSelection = FALSE; m_bResize = FALSE; } void KSpreadVBorder::adjustRow( int _row, bool makeUndo ) { double adjust = -1.0; int select; if ( _row == -1 ) { adjust = m_pCanvas->activeTable()->adjustRow( m_pView->selectionInfo() ); select = m_iSelectionAnchor; } else { adjust = m_pCanvas->activeTable()->adjustRow( m_pView->selectionInfo(), _row ); select = _row; } if ( adjust != -1.0 ) { KSpreadSheet * table = m_pCanvas->activeTable(); assert( table ); if ( _row == -1 ) { RowFormat * rl = table->nonDefaultRowFormat( select ); if ( kAbs( rl->dblHeight() - adjust ) < DBL_EPSILON ) return; } if ( makeUndo && !m_pCanvas->doc()->undoLocked() ) { QRect rect; rect.setCoords( 1, select, KS_colMax, select); KSpreadUndoResizeColRow * undo = new KSpreadUndoResizeColRow( m_pCanvas->doc(), m_pCanvas->activeTable(), rect ); m_pCanvas->doc()->addCommand( undo ); } RowFormat * rl = table->nonDefaultRowFormat( select ); rl->setDblHeight( QMAX( 2.0, adjust ) ); } } void KSpreadVBorder::equalizeRow( double resize ) { KSpreadSheet *table = m_pCanvas->activeTable(); Q_ASSERT( table ); QRect selection( m_pView->selection() ); if ( !m_pCanvas->doc()->undoLocked() ) { KSpreadUndoResizeColRow *undo = new KSpreadUndoResizeColRow( m_pCanvas->doc(), m_pCanvas->activeTable(), selection ); m_pCanvas->doc()->addCommand( undo ); } RowFormat *rl; for ( int i = selection.top(); i <= selection.bottom(); i++ ) { rl = table->nonDefaultRowFormat( i ); resize = QMAX( 2.0, resize); rl->setDblHeight( resize ); } } void KSpreadVBorder::resizeRow( double resize, int nb, bool makeUndo ) { KSpreadSheet *table = m_pCanvas->activeTable(); Q_ASSERT( table ); if ( nb == -1 ) // I don't know, where this is the case { if ( makeUndo && !m_pCanvas->doc()->undoLocked() ) { QRect rect; rect.setCoords( 1, m_iSelectionAnchor, KS_colMax, m_iSelectionAnchor ); KSpreadUndoResizeColRow *undo = new KSpreadUndoResizeColRow( m_pCanvas->doc(), m_pCanvas->activeTable(), rect ); m_pCanvas->doc()->addCommand( undo ); } RowFormat *rl = table->nonDefaultRowFormat( m_iSelectionAnchor ); rl->setDblHeight( QMAX( 2.0, resize ) ); } else { QRect selection( m_pView->selection() ); if ( m_pView->selectionInfo()->singleCellSelection() ) { if ( makeUndo && !m_pCanvas->doc()->undoLocked() ) { QRect rect; rect.setCoords( 1, m_pCanvas->markerRow(), KS_colMax, m_pCanvas->markerRow() ); KSpreadUndoResizeColRow *undo = new KSpreadUndoResizeColRow( m_pCanvas->doc(), m_pCanvas->activeTable(), rect ); m_pCanvas->doc()->addCommand( undo ); } RowFormat *rl = table->nonDefaultRowFormat( m_pCanvas->markerRow() ); rl->setDblHeight( QMAX( 2.0, resize ) ); } else { if ( makeUndo && !m_pCanvas->doc()->undoLocked() ) { KSpreadUndoResizeColRow *undo = new KSpreadUndoResizeColRow( m_pCanvas->doc(), m_pCanvas->activeTable(), selection ); m_pCanvas->doc()->addCommand( undo ); } RowFormat *rl; for ( int i = selection.top(); i<=selection.bottom(); i++ ) { rl = table->nonDefaultRowFormat( i ); rl->setDblHeight( QMAX( 2.0, resize ) ); } } } } void KSpreadVBorder::mouseDoubleClickEvent( QMouseEvent * /*_ev */) { KSpreadSheet *table = m_pCanvas->activeTable(); assert( table ); if ( !m_pView->koDocument()->isReadWrite() || table->isProtected() ) return; adjustRow(); } void KSpreadVBorder::mouseMoveEvent( QMouseEvent * _ev ) { if ( !m_pView->koDocument()->isReadWrite() ) return; KSpreadSheet *table = m_pCanvas->activeTable(); assert( table ); double ev_PosY = m_pCanvas->doc()->unzoomItY( _ev->pos().y() ) + m_pCanvas->yOffset(); double dHeight = m_pCanvas->doc()->unzoomItY( height() ); // The button is pressed and we are resizing ? if ( m_bResize ) { if ( !table->isProtected() ) paintSizeIndicator( _ev->pos().y(), false ); } // The button is pressed and we are selecting ? else if ( m_bSelection ) { double y; int row = table->topRow( ev_PosY, y ); if ( row > KS_rowMax ) return; QPoint newAnchor = m_pView->selectionInfo()->selectionAnchor(); QPoint newMarker = m_pView->selectionInfo()->marker(); newMarker.setY( row ); newAnchor.setY( m_iSelectionAnchor ); m_pView->selectionInfo()->setSelection( newMarker, newAnchor, m_pView->activeTable() ); if ( _ev->pos().y() < 0 ) m_pCanvas->vertScrollBar()->setValue( m_pCanvas->doc()->zoomItY( ev_PosY ) ); else if ( _ev->pos().y() > m_pCanvas->height() ) { if ( row < KS_rowMax ) { RowFormat *rl = table->rowFormat( row + 1 ); y = table->dblRowPos( row + 1 ); m_pCanvas->vertScrollBar()->setValue ((int) (m_pCanvas->doc()->zoomItY (ev_PosY + rl->dblHeight()) - dHeight)); } } } // No button is pressed and the mouse is just moved else { //What is the internal size of 1 pixel const double unzoomedPixel = m_pCanvas->doc()->unzoomItY( 1 ); double y; int tmpRow = table->topRow( m_pCanvas->yOffset(), y ); while ( y < m_pCanvas->doc()->unzoomItY( height() ) + m_pCanvas->yOffset() ) { double h = table->rowFormat( tmpRow )->dblHeight(); //if col is hide and it's the first column //you mustn't resize it. if ( ev_PosY >= y + h - 2 * unzoomedPixel && ev_PosY <= y + h + unzoomedPixel && !( table->rowFormat( tmpRow )->isHide() && tmpRow == 1 ) ) { setCursor( splitVCursor ); return; } y += h; tmpRow++; } setCursor( arrowCursor ); } } void KSpreadVBorder::doAutoScroll() { if ( !m_bMousePressed ) { m_scrollTimer->stop(); return; } QPoint pos( mapFromGlobal( QCursor::pos() ) ); if ( pos.y() < 0 || pos.y() > height() ) { QMouseEvent * event = new QMouseEvent( QEvent::MouseMove, pos, 0, 0 ); mouseMoveEvent( event ); delete event; } //Restart timer m_scrollTimer->start( 50 ); } void KSpreadVBorder::wheelEvent( QWheelEvent* _ev ) { if ( m_pCanvas->vertScrollBar() ) QApplication::sendEvent( m_pCanvas->vertScrollBar(), _ev ); } void KSpreadVBorder::paintSizeIndicator( int mouseY, bool firstTime ) { KSpreadSheet *table = m_pCanvas->activeTable(); assert( table ); QPainter painter; painter.begin( m_pCanvas ); painter.setRasterOp( NotROP ); if ( !firstTime ) painter.drawLine( 0, m_iResizePos, m_pCanvas->width(), m_iResizePos ); m_iResizePos = mouseY; // Dont make the row have a height < 2 pixel. int y = m_pCanvas->doc()->zoomItY( table->dblRowPos( m_iResizedRow ) - m_pCanvas->yOffset() ); if ( m_iResizePos < y + 2 ) m_iResizePos = y; painter.drawLine( 0, m_iResizePos, m_pCanvas->width(), m_iResizePos ); painter.end(); QString tmpSize; if ( m_iResizePos != y ) tmpSize = i18n("Height: %1 %2").arg( KoUnit::toUserValue( m_pCanvas->doc()->unzoomItY( m_iResizePos - y ), m_pView->doc()->getUnit() ) ) .arg( m_pView->doc()->getUnitName() ); else tmpSize = i18n( "Hide Row" ); painter.begin( this ); int len = painter.fontMetrics().width( tmpSize ); int hei = painter.fontMetrics().height(); painter.end(); if ( !m_lSize ) { m_lSize = new QLabel( m_pCanvas ); if ( table->layoutDirection()==KSpreadSheet::RightToLeft ) m_lSize->setGeometry( m_pCanvas->width() - len - 5, y + 3, len + 2, hei + 2 ); else m_lSize->setGeometry( 3, y + 3, len + 2,hei + 2 ); m_lSize->setAlignment( Qt::AlignVCenter ); m_lSize->setText( tmpSize ); m_lSize->show(); } else { if ( table->layoutDirection()==KSpreadSheet::RightToLeft ) m_lSize->setGeometry( m_pCanvas->width() - len - 5, y + 3, len + 2, hei + 2 ); else m_lSize->setGeometry( 3, y + 3, len + 2,hei + 2 ); m_lSize->setText( tmpSize ); } } void KSpreadVBorder::updateRows( int from, int to ) { KSpreadSheet *table = m_pCanvas->activeTable(); if ( !table ) return; int y0 = table->rowPos( from, m_pCanvas ); int y1 = table->rowPos( to+1, m_pCanvas ); update( 0, y0, width(), y1-y0 ); } void KSpreadVBorder::paintEvent( QPaintEvent* _ev ) { KSpreadSheet *table = m_pCanvas->activeTable(); if ( !table ) return; QPainter painter( this ); QPen pen( Qt::black, 1 ); painter.setPen( pen ); // painter.setBackgroundColor( colorGroup().base() ); // painter.eraseRect( _ev->rect() ); //QFontMetrics fm = painter.fontMetrics(); // Matthias Elter: This causes a SEGFAULT in ~QPainter! // Only god and the trolls know why ;-) // bah...took me quite some time to track this one down... painter.setClipRect( _ev->rect() ); double yPos; //Get the top row and the current y-position int y = table->topRow( (m_pCanvas->doc()->unzoomItY( _ev->rect().y() ) + m_pCanvas->yOffset()), yPos ); //Align to the offset yPos = yPos - m_pCanvas->yOffset(); int width = m_pCanvas->doc()->zoomItX( YBORDER_WIDTH ); QFont normalFont = painter.font(); if ( m_pCanvas->doc()->zoom() < 100 ) { normalFont.setPointSizeFloat( 0.01 * m_pCanvas->doc()->zoom() * normalFont.pointSizeFloat() ); } QFont boldFont = normalFont; boldFont.setBold( TRUE ); //several cells selected but not just a cell merged bool area = !( m_pView->selectionInfo()->singleCellSelection() ); //Loop through the rows, until we are out of range while ( yPos <= m_pCanvas->doc()->unzoomItY( _ev->rect().bottom() ) ) { bool highlighted = ( area && y >= m_pView->selection().top() && y <= m_pView->selection().bottom() ); bool selected = ( highlighted && (util_isRowSelected(m_pView->selection())) ); bool current = ( !highlighted && y == m_pView->selection().top() ); const RowFormat *row_lay = table->rowFormat( y ); int zoomedYPos = m_pCanvas->doc()->zoomItY( yPos ); int height = m_pCanvas->doc()->zoomItY( yPos + row_lay->dblHeight() ) - zoomedYPos; if ( selected ) { QBrush fillSelected( colorGroup().brush( QColorGroup::Highlight ) ); qDrawShadePanel( &painter, 0, zoomedYPos, width, height, colorGroup(), FALSE, 1, &fillSelected ); } else if ( highlighted ) { QBrush fillHighlighted( colorGroup().brush( QColorGroup::Background ) ); qDrawShadePanel( &painter, 0, zoomedYPos, width, height, colorGroup(), true, 1, &fillHighlighted ); } else { QBrush fill( colorGroup().brush( QColorGroup::Background ) ); qDrawShadePanel( &painter, 0, zoomedYPos, width, height, colorGroup(), FALSE, 1, &fill ); } QString rowText = QString::number( y ); // Reset painter painter.setFont( normalFont ); painter.setPen( colorGroup().text() ); if ( selected ) painter.setPen( colorGroup().highlightedText() ); else if ( highlighted || current ) painter.setFont( boldFont ); int len = painter.fontMetrics().width( rowText ); if (!row_lay->isHide()) painter.drawText( ( width-len )/2, zoomedYPos + ( height + painter.fontMetrics().ascent() - painter.fontMetrics().descent() ) / 2, rowText ); yPos += row_lay->dblHeight(); y++; } } void KSpreadVBorder::focusOutEvent( QFocusEvent* ) { if ( m_scrollTimer->isActive() ) m_scrollTimer->stop(); m_bMousePressed = false; } /**************************************************************** * * KSpreadHBorder * ****************************************************************/ KSpreadHBorder::KSpreadHBorder( QWidget *_parent, KSpreadCanvas *_canvas,KSpreadView *_view ) : QWidget( _parent, "", /*WNorthWestGravity*/ WStaticContents| WResizeNoErase | WRepaintNoErase ) { m_pView = _view; m_pCanvas = _canvas; m_lSize = 0L; setBackgroundMode( PaletteButton ); setMouseTracking( TRUE ); m_bResize = FALSE; m_bSelection = FALSE; m_iSelectionAnchor=1; m_bMousePressed = FALSE; m_scrollTimer = new QTimer( this ); connect ( m_scrollTimer, SIGNAL( timeout() ), this, SLOT( doAutoScroll() ) ); } KSpreadHBorder::~KSpreadHBorder() { delete m_scrollTimer; } QSize KSpreadHBorder::sizeHint() const { return QSize( 40, 10 ); } void KSpreadHBorder::mousePressEvent( QMouseEvent * _ev ) { if (!m_pView->koDocument()->isReadWrite()) return; if ( _ev->button() == LeftButton ) m_bMousePressed = true; const KSpreadSheet *table = m_pCanvas->activeTable(); assert( table ); // We were editing a cell -> save value and get out of editing mode if ( m_pCanvas->editor() ) { m_pCanvas->deleteEditor( true ); // save changes } m_scrollTimer->start( 50 ); double ev_PosX; double dWidth = m_pCanvas->doc()->unzoomItX( width() ); if ( table->layoutDirection()==KSpreadSheet::RightToLeft ) ev_PosX = dWidth - m_pCanvas->doc()->unzoomItX( _ev->pos().x() ) + m_pCanvas->xOffset(); else ev_PosX = m_pCanvas->doc()->unzoomItX( _ev->pos().x() ) + m_pCanvas->xOffset(); m_bResize = FALSE; m_bSelection = FALSE; // Find the first visible column and the x position of this column. double x; const double unzoomedPixel = m_pCanvas->doc()->unzoomItX( 1 ); if ( table->layoutDirection()==KSpreadSheet::RightToLeft ) { int tmpCol = table->leftColumn( m_pCanvas->xOffset(), x ); kdDebug() << "evPos: " << ev_PosX << ", x: " << x << ", COL: " << tmpCol << endl; while ( ev_PosX > x && ( !m_bResize ) ) { double w = table->columnFormat( tmpCol )->dblWidth(); kdDebug() << "evPos: " << ev_PosX << ", x: " << x << ", w: " << w << ", COL: " << tmpCol << endl; ++tmpCol; if ( tmpCol > KS_colMax ) tmpCol = KS_colMax; //if col is hide and it's the first column //you mustn't resize it. if ( ev_PosX >= x + w - unzoomedPixel && ev_PosX <= x + w + unzoomedPixel && !( table->columnFormat( tmpCol )->isHide() && tmpCol == 1 ) ) { m_bResize = true; } x += w; } //if col is hide and it's the first column //you mustn't resize it. double tmp2; tmpCol = table->leftColumn( dWidth - ev_PosX + 1, tmp2 ); if ( table->columnFormat( tmpCol )->isHide() && tmpCol == 0 ) { kdDebug() << "No resize: " << tmpCol << ", " << table->columnFormat( tmpCol )->isHide() << endl; m_bResize = false; } kdDebug() << "Resize: " << m_bResize << endl; } else { int col = table->leftColumn( m_pCanvas->xOffset(), x ); // Did the user click between two columns? while ( x < ( dWidth + m_pCanvas->xOffset() ) && ( !m_bResize ) ) { double w = table->columnFormat( col )->dblWidth(); col++; if ( col > KS_colMax ) col = KS_colMax; if ( ( ev_PosX >= x + w - unzoomedPixel ) && ( ev_PosX <= x + w + unzoomedPixel ) && !( table->columnFormat( col )->isHide() && col == 1 ) ) m_bResize = TRUE; x += w; } //if col is hide and it's the first column //you mustn't resize it. double tmp2; int tmpCol = table->leftColumn( ev_PosX - 1, tmp2 ); if ( table->columnFormat( tmpCol )->isHide() && tmpCol == 1 ) m_bResize = false; } // So he clicked between two rows ? if ( m_bResize ) { // Determine the column to resize double tmp; if ( table->layoutDirection()==KSpreadSheet::RightToLeft ) { m_iResizedColumn = table->leftColumn( ev_PosX - 1, tmp ); // kdDebug() << "RColumn: " << m_iResizedColumn << ", PosX: " << ev_PosX << endl; if ( !table->isProtected() ) paintSizeIndicator( _ev->pos().x(), true ); } else { m_iResizedColumn = table->leftColumn( ev_PosX - 1, tmp ); if ( !table->isProtected() ) paintSizeIndicator( _ev->pos().x(), true ); } // kdDebug() << "Column: " << m_iResizedColumn << endl; } else { m_bSelection = TRUE; double tmp; int hit_col = table->leftColumn( ev_PosX, tmp ); if ( hit_col > KS_colMax ) return; m_iSelectionAnchor = hit_col; QRect rect = m_pView->selection(); if ( !rect.contains( QPoint( hit_col, 1 ) ) || !( _ev->button() == RightButton ) || ( !util_isColumnSelected( rect ) ) ) { QPoint newMarker( hit_col, 1 ); QPoint newAnchor( hit_col, KS_rowMax ); m_pView->selectionInfo()->setSelection( newMarker, newAnchor, m_pView->activeTable() ); } if ( _ev->button() == RightButton ) { QPoint p = mapToGlobal( _ev->pos() ); m_pView->popupColumnMenu( p ); m_bSelection = FALSE; } m_pView->updateEditWidget(); } } void KSpreadHBorder::mouseReleaseEvent( QMouseEvent * _ev ) { if ( m_scrollTimer->isActive() ) m_scrollTimer->stop(); m_bMousePressed = false; if ( !m_pView->koDocument()->isReadWrite() ) return; KSpreadSheet * table = m_pCanvas->activeTable(); assert( table ); if ( m_bResize ) { double dWidth = m_pCanvas->doc()->unzoomItX( width() ); double ev_PosX; // Remove size indicator painted by paintSizeIndicator QPainter painter; painter.begin( m_pCanvas ); painter.setRasterOp( NotROP ); painter.drawLine( m_iResizePos, 0, m_iResizePos, m_pCanvas->height() ); painter.end(); int start = m_iResizedColumn; int end = m_iResizedColumn; QRect rect; rect.setCoords( m_iResizedColumn, 1, m_iResizedColumn, KS_rowMax ); if ( util_isColumnSelected(m_pView->selection()) ) { if ( m_pView->selection().contains( QPoint( m_iResizedColumn, 1 ) ) ) { start = m_pView->selection().left(); end = m_pView->selection().right(); rect = m_pView->selection(); } } double width = 0.0; double x; if ( table->layoutDirection()==KSpreadSheet::RightToLeft ) ev_PosX = dWidth - m_pCanvas->doc()->unzoomItX( _ev->pos().x() ) + m_pCanvas->xOffset(); else ev_PosX = m_pCanvas->doc()->unzoomItX( _ev->pos().x() ) + m_pCanvas->xOffset(); x = table->dblColumnPos( m_iResizedColumn ); if ( ev_PosX - x <= 0.0 ) width = 0.0; else width = ev_PosX - x; if ( !table->isProtected() ) { if ( !m_pCanvas->doc()->undoLocked() ) { //just resize if ( width != 0.0 ) { KSpreadUndoResizeColRow *undo = new KSpreadUndoResizeColRow( m_pCanvas->doc(), m_pCanvas->activeTable(), rect ); m_pCanvas->doc()->addCommand( undo ); } else {//hide column KSpreadUndoHideColumn *undo = new KSpreadUndoHideColumn( m_pCanvas->doc(), m_pCanvas->activeTable(), rect.left(), (rect.right()-rect.left())); m_pCanvas->doc()->addCommand( undo ); } } for( int i = start; i <= end; i++ ) { ColumnFormat *cl = table->nonDefaultColumnFormat( i ); if ( width != 0.0 ) { if ( !cl->isHide() ) cl->setDblWidth( width ); } else cl->setHide( true ); } if ( width == 0.0 ) table->emitHideRow(); delete m_lSize; m_lSize = 0; } } else if ( m_bSelection ) { QRect rect = m_pView->selection(); // TODO: please don't remove. Right now it's useless, but it's for a future feature // Norbert bool m_frozen = false; if ( m_frozen ) { kdDebug(36001) << "selected: L " << rect.left() << " R " << rect.right() << endl; int i; ColumnFormat * col; QValueListhiddenCols; for ( i = rect.left(); i <= rect.right(); ++i ) { col = m_pView->activeTable()->columnFormat( i ); if ( col->isHide() ) { hiddenCols.append(i); } } if ( hiddenCols.count() > 0 ) m_pView->activeTable()->showColumn( 0, -1, hiddenCols ); } } m_bSelection = FALSE; m_bResize = FALSE; } void KSpreadHBorder::adjustColumn( int _col, bool makeUndo ) { double adjust = -1.0; int select; if ( _col == -1 ) { adjust = m_pCanvas->activeTable()->adjustColumn( m_pView->selectionInfo() ); select = m_iSelectionAnchor; } else { adjust = m_pCanvas->activeTable()->adjustColumn( m_pView->selectionInfo(), _col ); select = _col; } if ( adjust != -1.0 ) { KSpreadSheet * table = m_pCanvas->activeTable(); assert( table ); if ( _col == -1 ) { ColumnFormat * cl = table->nonDefaultColumnFormat( select ); if ( kAbs( cl->dblWidth() - adjust ) < DBL_EPSILON ) return; } if ( makeUndo && !m_pCanvas->doc()->undoLocked() ) { QRect rect; rect.setCoords( select, 1, select, KS_rowMax ); KSpreadUndoResizeColRow *undo = new KSpreadUndoResizeColRow( m_pCanvas->doc(), m_pCanvas->activeTable(), rect ); m_pCanvas->doc()->addCommand( undo ); } ColumnFormat * cl = table->nonDefaultColumnFormat( select ); cl->setDblWidth( QMAX( 2.0, adjust ) ); } } void KSpreadHBorder::equalizeColumn( double resize ) { KSpreadSheet *table = m_pCanvas->activeTable(); Q_ASSERT( table ); QRect selection( m_pView->selection() ); if ( !m_pCanvas->doc()->undoLocked() ) { KSpreadUndoResizeColRow *undo = new KSpreadUndoResizeColRow( m_pCanvas->doc(), m_pCanvas->activeTable(), selection ); m_pCanvas->doc()->addCommand( undo ); } ColumnFormat *cl; for ( int i = selection.left(); i <= selection.right(); i++ ) { cl = table->nonDefaultColumnFormat( i ); resize = QMAX( 2.0, resize ); cl->setDblWidth( resize ); } } void KSpreadHBorder::resizeColumn( double resize, int nb, bool makeUndo ) { KSpreadSheet *table = m_pCanvas->activeTable(); Q_ASSERT( table ); if ( nb == -1 ) { if ( makeUndo && !m_pCanvas->doc()->undoLocked() ) { QRect rect; rect.setCoords( m_iSelectionAnchor, 1, m_iSelectionAnchor, KS_rowMax ); KSpreadUndoResizeColRow *undo = new KSpreadUndoResizeColRow( m_pCanvas->doc(), m_pCanvas->activeTable(), rect ); m_pCanvas->doc()->addCommand( undo ); } ColumnFormat *cl = table->nonDefaultColumnFormat( m_iSelectionAnchor ); cl->setDblWidth( QMAX( 2.0, resize ) ); } else { QRect selection( m_pView->selection() ); if ( m_pView->selectionInfo()->singleCellSelection() ) { if ( makeUndo && !m_pCanvas->doc()->undoLocked() ) { QRect rect; rect.setCoords( m_iSelectionAnchor, 1, m_iSelectionAnchor, KS_rowMax ); KSpreadUndoResizeColRow *undo = new KSpreadUndoResizeColRow( m_pCanvas->doc(), m_pCanvas->activeTable(), rect ); m_pCanvas->doc()->addCommand( undo ); } ColumnFormat *cl = table->nonDefaultColumnFormat( m_pCanvas->markerColumn() ); cl->setDblWidth( QMAX( 2.0, resize ) ); } else { if ( makeUndo && !m_pCanvas->doc()->undoLocked() ) { KSpreadUndoResizeColRow *undo = new KSpreadUndoResizeColRow( m_pCanvas->doc(), m_pCanvas->activeTable(), selection ); m_pCanvas->doc()->addCommand( undo ); } ColumnFormat *cl; for ( int i = selection.left(); i <= selection.right(); i++ ) { cl = table->nonDefaultColumnFormat( i ); cl->setDblWidth( QMAX( 2.0, resize ) ); } } } } void KSpreadHBorder::mouseDoubleClickEvent( QMouseEvent * /*_ev */) { KSpreadSheet *table = m_pCanvas->activeTable(); assert( table ); if ( !m_pView->koDocument()->isReadWrite() || table->isProtected() ) return; adjustColumn(); } void KSpreadHBorder::mouseMoveEvent( QMouseEvent * _ev ) { if ( !m_pView->koDocument()->isReadWrite() ) return; KSpreadSheet *table = m_pCanvas->activeTable(); assert( table ); double dWidth = m_pCanvas->doc()->unzoomItX( width() ); double ev_PosX; if ( table->layoutDirection()==KSpreadSheet::RightToLeft ) ev_PosX = dWidth - m_pCanvas->doc()->unzoomItX( _ev->pos().x() ) + m_pCanvas->xOffset(); else ev_PosX = m_pCanvas->doc()->unzoomItX( _ev->pos().x() ) + m_pCanvas->xOffset(); // The button is pressed and we are resizing ? if ( m_bResize ) { if ( !table->isProtected() ) paintSizeIndicator( _ev->pos().x(), false ); } // The button is pressed and we are selecting ? else if ( m_bSelection ) { double x; int col = table->leftColumn( ev_PosX, x ); if ( col > KS_colMax ) return; QPoint newMarker = m_pView->selectionInfo()->marker(); QPoint newAnchor = m_pView->selectionInfo()->selectionAnchor(); newMarker.setX( col ); newAnchor.setX( m_iSelectionAnchor ); m_pView->selectionInfo()->setSelection( newMarker, newAnchor, m_pView->activeTable() ); if ( table->layoutDirection()==KSpreadSheet::RightToLeft ) { if ( _ev->pos().x() < width() - m_pCanvas->width() ) { ColumnFormat *cl = table->columnFormat( col + 1 ); x = table->dblColumnPos( col + 1 ); m_pCanvas->horzScrollBar()->setValue ( m_pCanvas->horzScrollBar()->maxValue() - (int) (m_pCanvas->doc()->zoomItX (ev_PosX + cl->dblWidth()) - m_pCanvas->doc()->unzoomItX( m_pCanvas->width() ))); } else if ( _ev->pos().x() > width() ) m_pCanvas->horzScrollBar()->setValue( m_pCanvas->horzScrollBar()->maxValue() - m_pCanvas->doc()->zoomItX( ev_PosX - dWidth + m_pCanvas->doc()->unzoomItX( m_pCanvas->width() ) ) ); } else { if ( _ev->pos().x() < 0 ) m_pCanvas->horzScrollBar()->setValue( m_pCanvas->doc()->zoomItX( ev_PosX ) ); else if ( _ev->pos().x() > m_pCanvas->width() ) { if ( col < KS_colMax ) { ColumnFormat *cl = table->columnFormat( col + 1 ); x = table->dblColumnPos( col + 1 ); m_pCanvas->horzScrollBar()->setValue ((int) (m_pCanvas->doc()->zoomItX (ev_PosX + cl->dblWidth()) - dWidth)); } } } } // No button is pressed and the mouse is just moved else { //What is the internal size of 1 pixel const double unzoomedPixel = m_pCanvas->doc()->unzoomItX( 1 ); double x; if ( table->layoutDirection()==KSpreadSheet::RightToLeft ) { int tmpCol = table->leftColumn( m_pCanvas->xOffset(), x ); while ( ev_PosX > x ) { double w = table->columnFormat( tmpCol )->dblWidth(); ++tmpCol; //if col is hide and it's the first column //you mustn't resize it. if ( ev_PosX >= x + w - unzoomedPixel && ev_PosX <= x + w + unzoomedPixel && !( table->columnFormat( tmpCol )->isHide() && tmpCol == 0 ) ) { setCursor( splitHCursor ); return; } x += w; } setCursor( arrowCursor ); } else { int tmpCol = table->leftColumn( m_pCanvas->xOffset(), x ); while ( x < m_pCanvas->doc()->unzoomItY( width() ) + m_pCanvas->xOffset() ) { double w = table->columnFormat( tmpCol )->dblWidth(); //if col is hide and it's the first column //you mustn't resize it. if ( ev_PosX >= x + w - unzoomedPixel && ev_PosX <= x + w + unzoomedPixel && !( table->columnFormat( tmpCol )->isHide() && tmpCol == 1 ) ) { setCursor( splitHCursor ); return; } x += w; tmpCol++; } setCursor( arrowCursor ); } } } void KSpreadHBorder::doAutoScroll() { if ( !m_bMousePressed ) { m_scrollTimer->stop(); return; } QPoint pos( mapFromGlobal( QCursor::pos() ) ); if ( pos.x() < 0 || pos.x() > width() ) { QMouseEvent * event = new QMouseEvent( QEvent::MouseMove, pos, 0, 0 ); mouseMoveEvent( event ); delete event; } //Restart timer m_scrollTimer->start( 50 ); } void KSpreadHBorder::wheelEvent( QWheelEvent* _ev ) { if ( m_pCanvas->horzScrollBar() ) QApplication::sendEvent( m_pCanvas->horzScrollBar(), _ev ); } void KSpreadHBorder::paintSizeIndicator( int mouseX, bool firstTime ) { KSpreadSheet *table = m_pCanvas->activeTable(); assert( table ); QPainter painter; painter.begin( m_pCanvas ); painter.setRasterOp( NotROP ); if ( !firstTime ) painter.drawLine( m_iResizePos, 0, m_iResizePos, m_pCanvas->height() ); if ( table->layoutDirection()==KSpreadSheet::RightToLeft ) m_iResizePos = mouseX + m_pCanvas->width() - width(); else m_iResizePos = mouseX; // Dont make the column have a width < 2 pixels. int x = m_pCanvas->doc()->zoomItX( table->dblColumnPos( m_iResizedColumn ) - m_pCanvas->xOffset() ); if ( table->layoutDirection()==KSpreadSheet::RightToLeft ) { x = m_pCanvas->width() - x; if ( m_iResizePos > x - 2 ) m_iResizePos = x; } else { if ( m_iResizePos < x + 2 ) m_iResizePos = x; } painter.drawLine( m_iResizePos, 0, m_iResizePos, m_pCanvas->height() ); painter.end(); QString tmpSize; if ( m_iResizePos != x ) tmpSize = i18n("Width: %1 %2") .arg( KGlobal::locale()->formatNumber( KoUnit::toUserValue( m_pCanvas->doc()->unzoomItX( (table->layoutDirection()==KSpreadSheet::RightToLeft) ? x - m_iResizePos : m_iResizePos - x ), m_pView->doc()->getUnit() ))) .arg( m_pView->doc()->getUnitName() ); else tmpSize = i18n( "Hide Column" ); painter.begin( this ); int len = painter.fontMetrics().width( tmpSize ); int hei = painter.fontMetrics().height(); painter.end(); if ( !m_lSize ) { m_lSize = new QLabel( m_pCanvas ); if ( table->layoutDirection()==KSpreadSheet::RightToLeft ) m_lSize->setGeometry( x - len - 5, 3, len + 2, hei + 2 ); else m_lSize->setGeometry( x + 3, 3, len + 2, hei + 2 ); m_lSize->setAlignment( Qt::AlignVCenter ); m_lSize->setText( tmpSize ); m_lSize->show(); } else { if ( table->layoutDirection()==KSpreadSheet::RightToLeft ) m_lSize->setGeometry( x - len - 5, 3, len + 2, hei + 2 ); else m_lSize->setGeometry( x + 3, 3, len + 2, hei + 2 ); m_lSize->setText( tmpSize ); } } void KSpreadHBorder::updateColumns( int from, int to ) { KSpreadSheet *table = m_pCanvas->activeTable(); if ( !table ) return; int x0 = table->columnPos( from, m_pCanvas ); int x1 = table->columnPos( to+1, m_pCanvas ); update( x0, 0, x1-x0, height() ); } void KSpreadHBorder::paintEvent( QPaintEvent* _ev ) { KSpreadSheet * table = m_pCanvas->activeTable(); if ( !table ) return; QPainter painter( this ); QPen pen( Qt::black, 1 ); painter.setPen( pen ); painter.setBackgroundColor( white ); painter.setClipRect( _ev->rect() ); // painter.eraseRect( _ev->rect() ); //QFontMetrics fm = painter.fontMetrics(); // Matthias Elter: This causes a SEGFAULT in ~QPainter! // Only god and the trolls know why ;-) // bah...took me quite some time to track this one down... double xPos; int x; if ( table->layoutDirection()==KSpreadSheet::RightToLeft ) { //Get the left column and the current x-position x = table->leftColumn( int( m_pCanvas->doc()->unzoomItX( width() ) - m_pCanvas->doc()->unzoomItX( _ev->rect().x() ) + m_pCanvas->xOffset() ), xPos ); //Align to the offset xPos = m_pCanvas->doc()->unzoomItX( width() ) - xPos + m_pCanvas->xOffset(); } else { //Get the left column and the current x-position x = table->leftColumn( int( m_pCanvas->doc()->unzoomItX( _ev->rect().x() ) + m_pCanvas->xOffset() ), xPos ); //Align to the offset xPos = xPos - m_pCanvas->xOffset(); } int height = m_pCanvas->doc()->zoomItY( KSpreadFormat::globalRowHeight() + 2 ); QFont normalFont = painter.font(); if ( m_pCanvas->doc()->zoom() < 100 ) { normalFont.setPointSizeFloat( 0.01 * m_pCanvas->doc()->zoom() * normalFont.pointSizeFloat() ); } QFont boldFont = normalFont; boldFont.setBold( TRUE ); KSpreadCell *cell = table->cellAt( m_pView->marker() ); QRect extraCell; extraCell.setCoords( m_pCanvas->markerColumn(), m_pCanvas->markerRow(), m_pCanvas->markerColumn() + cell->extraXCells(), m_pCanvas->markerRow() + cell->extraYCells()); //several cells selected but not just a cell merged bool area = ( m_pView->selection().left()!=0 && extraCell != m_pView->selection() ); if ( table->layoutDirection()==KSpreadSheet::RightToLeft ) { xPos -= table->columnFormat( x + 1 )->dblWidth(); //Loop through the columns, until we are out of range while ( xPos <= m_pCanvas->doc()->unzoomItX( _ev->rect().right() ) ) { bool highlighted = ( area && x >= m_pView->selection().left() && x <= m_pView->selection().right()); bool selected = ( highlighted && util_isColumnSelected( m_pView->selection() ) && ( !util_isRowSelected( m_pView->selection() ) ) ); bool current = ( !highlighted && x == m_pView->selection().left() ); const ColumnFormat * col_lay = table->columnFormat( x ); int zoomedXPos = m_pCanvas->doc()->zoomItX( xPos ); int width = m_pCanvas->doc()->zoomItX( xPos + col_lay->dblWidth() ) - zoomedXPos; if ( selected ) { QBrush fillSelected( colorGroup().brush( QColorGroup::Highlight ) ); qDrawShadePanel( &painter, zoomedXPos, 0, width, height, colorGroup(), FALSE, 1, &fillSelected ); } else if ( highlighted ) { QBrush fillHighlighted( colorGroup().brush( QColorGroup::Background ) ); qDrawShadePanel( &painter, zoomedXPos, 0, width, height, colorGroup(), true, 1, &fillHighlighted ); } else { QBrush fill( colorGroup().brush( QColorGroup::Background ) ); qDrawShadePanel( &painter, zoomedXPos, 0, width, height, colorGroup(), FALSE, 1, &fill ); } // Reset painter painter.setFont( normalFont ); painter.setPen( colorGroup().text() ); if ( selected ) painter.setPen( colorGroup().highlightedText() ); else if ( highlighted || current ) painter.setFont( boldFont ); if ( !m_pView->activeTable()->getShowColumnNumber() ) { QString colText = KSpreadCell::columnName( x ); int len = painter.fontMetrics().width( colText ); if ( !col_lay->isHide() ) painter.drawText( zoomedXPos + ( width - len ) / 2, ( height + painter.fontMetrics().ascent() - painter.fontMetrics().descent() ) / 2, colText ); } else { QString tmp; int len = painter.fontMetrics().width( tmp.setNum(x) ); if (!col_lay->isHide()) painter.drawText( zoomedXPos + ( width - len ) / 2, ( height + painter.fontMetrics().ascent() - painter.fontMetrics().descent() ) / 2, tmp.setNum(x) ); } xPos += col_lay->dblWidth(); --x; } } else { //Loop through the columns, until we are out of range while ( xPos <= m_pCanvas->doc()->unzoomItX( _ev->rect().right() ) ) { bool highlighted = ( area && x >= m_pView->selection().left() && x <= m_pView->selection().right()); bool selected = ( highlighted && util_isColumnSelected( m_pView->selection() ) && ( !util_isRowSelected( m_pView->selection() ) ) ); bool current = ( !highlighted && x == m_pView->selection().left() ); const ColumnFormat *col_lay = table->columnFormat( x ); int zoomedXPos = m_pCanvas->doc()->zoomItX( xPos ); int width = m_pCanvas->doc()->zoomItX( xPos + col_lay->dblWidth() ) - zoomedXPos; if ( selected ) { QBrush fillSelected( colorGroup().brush( QColorGroup::Highlight ) ); qDrawShadePanel( &painter, zoomedXPos, 0, width, height, colorGroup(), FALSE, 1, &fillSelected ); } else if ( highlighted ) { QBrush fillHighlighted( colorGroup().brush( QColorGroup::Background ) ); qDrawShadePanel( &painter, zoomedXPos, 0, width, height, colorGroup(), true, 1, &fillHighlighted ); } else { QBrush fill( colorGroup().brush( QColorGroup::Background ) ); qDrawShadePanel( &painter, zoomedXPos, 0, width, height, colorGroup(), FALSE, 1, &fill ); } // Reset painter painter.setFont( normalFont ); painter.setPen( colorGroup().text() ); if ( selected ) painter.setPen( colorGroup().highlightedText() ); else if ( highlighted || current ) painter.setFont( boldFont ); if ( !m_pView->activeTable()->getShowColumnNumber() ) { QString colText = KSpreadCell::columnName( x ); int len = painter.fontMetrics().width( colText ); if (!col_lay->isHide()) painter.drawText( zoomedXPos + ( width - len ) / 2, ( height + painter.fontMetrics().ascent() - painter.fontMetrics().descent() ) / 2, colText ); } else { QString tmp; int len = painter.fontMetrics().width( tmp.setNum(x) ); if (!col_lay->isHide()) painter.drawText( zoomedXPos + ( width - len ) / 2, ( height + painter.fontMetrics().ascent() - painter.fontMetrics().descent() ) / 2, tmp.setNum(x) ); } xPos += col_lay->dblWidth(); ++x; } } } void KSpreadHBorder::focusOutEvent( QFocusEvent* ) { if ( m_scrollTimer->isActive() ) m_scrollTimer->stop(); m_bMousePressed = false; } /**************************************************************** * * KSpreadToolTip * ****************************************************************/ KSpreadToolTip::KSpreadToolTip( KSpreadCanvas* canvas ) : QToolTip( canvas ), m_canvas( canvas ) { } void KSpreadToolTip::maybeTip( const QPoint& p ) { KSpreadSheet *table = m_canvas->activeTable(); if ( !table ) return; // Over which cell is the mouse ? double ypos, xpos; double dwidth = m_canvas->doc()->unzoomItX( m_canvas->width() ); int col; if ( table->layoutDirection()==KSpreadSheet::RightToLeft ) col = table->leftColumn( (dwidth - m_canvas->doc()->unzoomItX( p.x() ) + m_canvas->xOffset()), xpos ); else col = table->leftColumn( (m_canvas->doc()->unzoomItX( p.x() ) + m_canvas->xOffset()), xpos ); int row = table->topRow( (m_canvas->doc()->unzoomItY( p.y() ) + m_canvas->yOffset()), ypos ); KSpreadCell* cell = table->visibleCellAt( col, row ); if ( !cell ) return; // Get the comment QString comment = cell->comment( col, row ); //If the cell is too short, get the content QString content; if ( cell->testFlag( KSpreadCell::Flag_CellTooShortX ) || cell->testFlag( KSpreadCell::Flag_CellTooShortY ) ) content = cell->strOutText(); if ( content.isEmpty() && comment.isEmpty() ) return; //Append the content text if ( !content.isEmpty() ) { //Add 2 extra lines and a text, when both should be in the tooltip if ( !comment.isEmpty() ) comment = "\n\n" + i18n("Comment:") + "\n" + comment; comment = content + comment; } // Determine position and width of the current cell. cell = table->cellAt( col, row ); double u = cell->dblWidth( col ); double v = cell->dblHeight( row ); // Special treatment for obscured cells. if ( cell->isObscured() && cell->isObscuringForced() ) { cell = cell->obscuringCells().first(); int moveX = cell->column(); int moveY = cell->row(); // Use the obscuring cells dimensions u = cell->dblWidth( moveX ); v = cell->dblHeight( moveY ); xpos = table->dblColumnPos( moveX ); ypos = table->dblRowPos( moveY ); } // Get the cell dimensions if ( table->layoutDirection()==KSpreadSheet::RightToLeft ) { KoRect unzoomedMarker( dwidth - u - xpos + m_canvas->xOffset(), ypos - m_canvas->yOffset(), u, v ); QRect marker( m_canvas->doc()->zoomRect( unzoomedMarker ) ); if ( marker.contains( p ) ) { tip( marker, comment ); } } else { KoRect unzoomedMarker( xpos - m_canvas->xOffset(), ypos - m_canvas->yOffset(), u, v ); QRect marker( m_canvas->doc()->zoomRect( unzoomedMarker ) ); if ( marker.contains( p ) ) { tip( marker, comment ); } } } #include "kspread_canvas.moc" diff --git a/kspread/kspread_canvas.h b/kspread/kspread_canvas.h index e6d39ce1483..5610cda63b3 100644 --- a/kspread/kspread_canvas.h +++ b/kspread/kspread_canvas.h @@ -1,734 +1,677 @@ /* This file is part of the KDE project Copyright 1999-2002,2004 Laurent Montel Copyright 1999-2001,2003 David Faure Copyright 2002-2004 Ariya Hidayat Copyright 2001-2003 Philipp Mueller Copyright 2002-2003 Norbert Andres Copyright 2000-2001 Werner Trobin Copyright 2002 Harri Porten Copyright 2002 John Dailey Copyright 1999-2000 Torben Weis Copyright 2000 Wilco Greven Copyright 1999 Boris Wedl Copyright 1999 Reginald Stadlbauer 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. */ #ifndef KSPREAD_CANVAS #define KSPREAD_CANVAS #include #include #include -#include #include #include #include "kspread_util.h" class KSpreadEditWidget; class KSpreadCanvas; class KSpreadHBorder; class KSpreadVBorder; class KSpreadSheet; class KSpreadDoc; class KSpreadPoint; class KSpreadRange; class KSpreadView; class KSpreadSelection; class KSpreadCellEditor; class KSpreadCell; class QWidget; class QTimer; class QButton; class KSpreadLocationEditWidget; +class KSpreadComboboxLocationEditWidget; class QPainter; class QLabel; class QScrollBar; #define YBORDER_WIDTH 50 #define XBORDER_HEIGHT 20 -class KSpreadComboboxLocationEditWidget : public KComboBox -{ - Q_OBJECT -public: - KSpreadComboboxLocationEditWidget( QWidget *_parent, KSpreadView * _canvas ); - -public slots: - void slotAddAreaName( const QString & ); - void slotRemoveAreaName( const QString & ); - -private: - KSpreadLocationEditWidget *m_locationWidget; -}; - - - /** - * A widget that allows the user to enter an arbitrary - * cell location to goto or cell selection to highlight - */ -class KSpreadLocationEditWidget : public QLineEdit -{ - Q_OBJECT -public: - KSpreadLocationEditWidget( QWidget *_parent, KSpreadView * _canvas ); - KSpreadView * view() const { return m_pView;} -protected: - virtual void keyPressEvent( QKeyEvent * _ev ); -private: - KSpreadView * m_pView; -signals: - void gotoLocation( int, int ); -}; - -/** - * The widget that appears above the table and allows to - * edit the cells content. - */ -class KSpreadEditWidget : public QLineEdit -{ - Q_OBJECT -public: - KSpreadEditWidget( QWidget *parent, KSpreadCanvas *canvas, - QButton *cancelButton, QButton *okButton); - - virtual void setText( const QString& t ); - - // Go into edit mode (enable the buttons) - void setEditMode( bool mode ); - - void showEditWidget(bool _show); -public slots: - void slotAbortEdit(); - void slotDoneEdit(); - -protected: - virtual void keyPressEvent ( QKeyEvent* _ev ); - virtual void focusOutEvent( QFocusEvent* ev ); - -private: - QButton* m_pCancelButton; - QButton* m_pOkButton; - KSpreadCanvas* m_pCanvas; -}; - /** * The canvas builds a part of the GUI of KSpread. * It contains the borders, scrollbars, * editwidget and of course it displays the table. * Especially most of the user interface logic is implemented here. * That means that this class knows what to do when a key is pressed * or if the mouse button was clicked. */ class KSpreadCanvas : public QWidget { friend class KSpreadHBorder; friend class KSpreadVBorder; friend class KSpreadView; Q_OBJECT public: /** * The current action associated with the mouse. * Default is 'NoAction'. */ enum MouseActions { NoAction = 0, Mark = 1, ResizeCell = 2, AutoFill = 3 }; enum EditorType { CellEditor, FormulaEditor, EditWidget }; KSpreadCanvas( QWidget *_parent, KSpreadView *_view, KSpreadDoc* _doc ); ~KSpreadCanvas( ); /** * Called from @ref KSpreadView to complete the construction. Has to * be called before any other method on this object may be invoced. */ void init(); KSpreadCellEditor* editor() const { return m_pEditor ; } /** * If the user chooses some cells during editing a formula, then * this function returns the length of the textual representation. * For example the user selects "Sheet1!A1:B2" then this function * returns 12. */ int chooseTextLen() const { return length_namecell; } KSpreadSelection* selectionInfo() const; QRect selection() const; QPoint marker() const; int markerColumn() const; int markerRow() const; void updateCellRect( const QRect &_rect ); void updateSelection( const QRect& oldSelection, const QPoint& oldMarker ); const QPen& defaultGridPen() const { return m_defaultGridPen; } double zoom() const; /** * Returns the width of the columns before the current screen */ double xOffset() const { return m_dXOffset; } /** * Returns the height of the rows before the current screen */ double yOffset() const { return m_dYOffset; } /** * Return a rect indicating which cell range is currently visible onscreen */ QRect visibleCells(); KSpreadSheet* activeTable() const; KSpreadSheet* findTable( const QString& _name ) const; /** * A convenience function. */ bool gotoLocation( const KSpreadRange & _range ); /** * A convenience function. */ bool gotoLocation( const KSpreadPoint& _cell ); /** * Move the cursor to the specified cell. This may include switching * the table. In addition @ref #KSpreadView::updateEditWidget is called. * * @param location the cell to move to * * @param table the table to move to. If NULL, the active table is used * @param extendSelection determines wether this move of the marker is part * of a selection, that means: The user holds the * shift key and moves the cursor keys. In this case * the selection is updated accordingly. * If this is false, the cell will be the single cell * selected, and the selection anchor will be reset * to this cell. */ void gotoLocation( QPoint const & location, KSpreadSheet* table = NULL, bool extendSelection = false); /** * convenience function */ void gotoLocation( int col, int row, KSpreadSheet* table = NULL, bool extendSelection = false) {gotoLocation(QPoint(col, row), table, extendSelection);} /** * Paint all visible cells that have a paint dirty flag set */ void paintUpdates(); /** * Makes sure a cell is visible onscreen by scrolling up/down and left/right * * @param location the cell coordinates to scroll to */ void scrollToCell(QPoint location); /** * Chooses the correct @ref #EditorType by looking at * the current cells value. By default CellEditor is chosen. */ void createEditor(); bool createEditor( EditorType type, bool addFocus = true ); /** * Deletes the current cell editor. * * @see #createEditor * @see #editor * @param saveChanges if true, the edited text is stored in the cell. * if false, the changes are discarded. */ void deleteEditor( bool saveChanges ); /** * Called from @ref KSpreadEditWidget and KSpreadCellEditor * if they loose the focus because the user started a "choose selection". * This is done because the editor wants to get its focus back afterwards. * But somehow KSpreadCanvas must know whether the EditWidget or the CellEditor * lost the focus when the user clicked on the canvas. */ void setLastEditorWithFocus( EditorType type ) { m_focusEditorType = type; } /** * Switches to choose mode and sets the initial selection to the * position returned by @ref #marker. */ void startChoose(); /** * Switches to choose mode and sets the initial @p selection. */ void startChoose( const QRect& selection ); void endChoose(); bool chooseMode(){ return m_bChoose; } /** * Adjust a area in height and width */ void adjustArea(bool makeUndo=true); void equalizeRow(); void equalizeColumn(); void updatePosWidget(); void closeEditor(); // Created by the view since it's layout is managed there, // but is in fact a sibling of the canvas, which needs to know about it. void setEditWidget( KSpreadEditWidget * ew ) { m_pEditWidget = ew; } KSpreadView* view()const { return m_pView; } KSpreadDoc* doc()const { return m_pDoc; } virtual bool focusNextPrevChild( bool ); bool chooseFormulaArea() const { return m_bChoose;} /** * Depending on the offset in "zoomed" screen pixels * for the horizontal direction, * the function returns the steps in unzoomed points * for the autoscroll acceleration */ double autoScrollAccelerationX( int offset ); /** * Depending on the offset in "zoomed" screen pixels * for the vertical direction, * the function returns the steps in unzoomed points * for the autoscroll acceleration */ double autoScrollAccelerationY( int offset ); public slots: void slotScrollVert( int _value ); void slotScrollHorz( int _value ); void slotMaxColumn( int _max_column ); void slotMaxRow( int _max_row ); protected: virtual void keyPressEvent ( QKeyEvent* _ev ); virtual void paintEvent ( QPaintEvent* _ev ); virtual void mousePressEvent( QMouseEvent* _ev ); virtual void mouseReleaseEvent( QMouseEvent* _ev ); virtual void mouseMoveEvent( QMouseEvent* _ev ); virtual void mouseDoubleClickEvent( QMouseEvent* ); virtual void wheelEvent( QWheelEvent* ); virtual void focusInEvent( QFocusEvent* ); virtual void focusOutEvent( QFocusEvent* ); virtual void resizeEvent( QResizeEvent * _ev ); virtual void dragMoveEvent(QDragMoveEvent * _ev); virtual void dropEvent(QDropEvent * _ev); virtual void dragLeaveEvent(QDragLeaveEvent * _ev); private slots: void doAutoScroll(); private: virtual void chooseMousePressEvent( QMouseEvent* _ev ); virtual void chooseMouseReleaseEvent( QMouseEvent* _ev ); virtual void chooseMouseMoveEvent( QMouseEvent* _ev ); virtual bool eventFilter( QObject *o, QEvent *e ); KSpreadHBorder* hBorderWidget() const; KSpreadVBorder* vBorderWidget() const; QScrollBar* horzScrollBar() const; QScrollBar* vertScrollBar() const; KSpreadEditWidget* editWidget() const { return m_pEditWidget; } void drawChooseMarker( ); void drawChooseMarker( const QRect& ); /** * Clips out the children region from the painter */ void clipoutChildren( QPainter& painter, QWMatrix& matrix ); /** * Paints the children */ void paintChildren( QPainter& painter, QWMatrix& matrix ); /** * @see #setLastEditorWithFocus */ EditorType lastEditorWithFocus() const { return m_focusEditorType; } /** * Hides the marker. Hiding it multiple times means that it has to be shown ( using @ref #showMarker ) multiple times * to become visible again. This function is optimized since it does not create a new painter. */ // void hideMarker( QPainter& ); // void showMarker( QPainter& ); // void drawMarker( QPainter * _painter = 0L ); bool choose_visible; int length_namecell; int length_text; KSpreadView *m_pView; KSpreadDoc* m_pDoc; QTimer * m_scrollTimer; /** * If the user is dragging around with the mouse then this tells us what he is doing. * The user may want to mark cells or he started in the lower right corner * of the marker which is something special. The values for the 2 above * methods are called 'Mark' and 'ResizeCell' or 'AutoFill' depending * on the mouse button used. By default this variable holds * the value 'NoAction'. */ MouseActions m_eMouseAction; /** * Used to indicate whether the user started drawing a rubber band rectangle. */ bool m_bGeometryStarted; QPoint m_ptGeometryStart; QPoint m_ptGeometryEnd; /** * True when the mouse button is pressed */ bool m_bMousePressed; /** * If we use the lower right corner of the marker to start autofilling, then this * rectangle conatins all cells that were already marker when the user started * to mark the rectangle which he wants to become autofilled. * * @see #mousePressEvent * @see #mouseReleeaseEvent */ QRect m_rctAutoFillSrc; /** * If the mouse is over some anchor ( in the sense of HTML anchors ) * then this one is stored here. */ QString m_strAnchor; /** * Non visible range left from current screen * Example: * If the first visible column is 'E', then m_dXOffset stores * the width of the invisible columns 'A' to 'D'. */ double m_dXOffset; /** * Non visible range on top of the current screen * Example: * If the first visible row is '5', then m_dYOffset stores * the height of the invisible rows '1' to '4'. */ double m_dYOffset; /** * Start coordinates for drag and drop */ QPoint m_dragStart; bool m_dragging; KSpreadComboboxLocationEditWidget *m_pPosWidget; KSpreadEditWidget *m_pEditWidget; KSpreadCellEditor *m_pEditor; /** * Used to draw the grey grid that is usually only visible on the * screen, but not by printing on paper. */ QPen m_defaultGridPen; // int m_iMarkerColumn; // int m_iMarkerRow; /** * A value of 1 means that it is visible, every lower value means it is * made invisible multiple times. * * @see #hideMarker * @see #showMarker */ // int m_iMarkerVisible; /** * Is true if the user is to choose a cell. * * @see #startChoose * @see #endChoose * @see KSpreadAssistant2 */ bool m_bChoose; /** * If a choose selection is started (@ref #startChoose) the current * table is saved here. */ KSpreadSheet* m_chooseStartTable; /** * @see #setLastEditorWithFocus * @see #lastEditorWithFocus */ EditorType m_focusEditorType; QLabel *m_validationInfo; private: void startTheDrag(); void paintSelectionChange(QRect area1, QRect area2); /** * Small helper function to take a rect representing a selection onscreen and * extend it one cell in every direction (taking into account hidden columns * and rows) */ void ExtendRectBorder(QRect& area); /* helpers for the paintUpdates function */ void paintChooseRect(QPainter& painter, const KoRect &viewRect); void paintNormalMarker(QPainter& painter, const KoRect &viewRect); void retrieveMarkerInfo( const QRect &marker, const KoRect &viewRect, double positions[], bool paintSides[] ); bool formatKeyPress( QKeyEvent * _ev ); + + /** helper method for formatKeyPress */ + bool formatCellByKey (KSpreadCell *cell, int key, const QRect &rect); + double getDouble( KSpreadCell * cell ); void convertToDouble( KSpreadCell * cell ); void convertToPercent( KSpreadCell * cell ); void convertToMoney( KSpreadCell * cell ); void convertToTime( KSpreadCell * cell ); void convertToDate( KSpreadCell * cell ); void processClickSelectionHandle(QMouseEvent *event); void processLeftClickAnchor(); /** * Helper function used from many places to extend the current selection such as with * a shift-click, mouse drag, shift-arrow key, etc. * * @param cell coordinates of the cell we want to extend towards. */ void extendCurrentSelection(QPoint cell); + /** current cursor position, be it marker of choose marker */ + QPoint cursorPos (); + /** * returns the rect that needs to be redrawn */ QRect moveDirection(KSpread::MoveTo direction, bool extendSelection); void processEnterKey(QKeyEvent *event); void processArrowKey(QKeyEvent *event); void processEscapeKey(QKeyEvent *event); bool processHomeKey(QKeyEvent *event); bool processEndKey(QKeyEvent *event); bool processPriorKey(QKeyEvent *event); bool processNextKey(QKeyEvent *event); void processDeleteKey(QKeyEvent *event); void processF2Key(QKeyEvent *event); void processF4Key(QKeyEvent *event); void processOtherKey(QKeyEvent *event); bool processControlArrowKey(QKeyEvent *event); void updateChooseRect(const QPoint &newMarker, const QPoint &newAnchor); /** * This function sets the paint dirty flag for a selection change. The idea * here is that only the edges of the selection need to change (no matter whether * its the real selection or the 'choose' selection). This calculates which * cells really should look different with the new selection rather than repainting * the entire area */ void setSelectionChangePaintDirty(KSpreadSheet* sheet, QRect area1, QRect area2); }; /** */ class KSpreadHBorder : public QWidget { Q_OBJECT public: KSpreadHBorder( QWidget *_parent, KSpreadCanvas *_canvas, KSpreadView *_view ); ~KSpreadHBorder(); int markerColumn() const { return m_iSelectionAnchor; } void resizeColumn( double resize, int nb = -1, bool makeUndo = true ); void adjustColumn( int _col = -1, bool makeUndo = true ); void equalizeColumn( double resize ); void updateColumns( int from, int to ); QSize sizeHint() const; private slots: void doAutoScroll(); protected: virtual void paintEvent ( QPaintEvent* _ev ); virtual void mousePressEvent( QMouseEvent* _ev ); virtual void mouseReleaseEvent( QMouseEvent* _ev ); virtual void mouseDoubleClickEvent( QMouseEvent* _ev ); virtual void mouseMoveEvent( QMouseEvent* _ev ); virtual void wheelEvent( QWheelEvent* ); virtual void focusOutEvent( QFocusEvent* ev ); void paintSizeIndicator( int mouseX, bool firstTime ); private: KSpreadCanvas *m_pCanvas; KSpreadView *m_pView; QTimer * m_scrollTimer; /** * Flag that inidicates whether the user wants to mark columns. * The user may mark columns by dragging the mouse around in th XBorder widget. * If he is doing that right now, this flag is TRUE. Mention that the user may * also resize columns by dragging the mouse. This case is not covered by this flag. */ bool m_bSelection; /** * The column over which the user pressed the mouse button. * If the user marks columns in the XBorder widget, then this is the initial * column on which he pressed the mouse button. */ int m_iSelectionAnchor; /** * Flag that indicates whether the user resizes a column * The user may resize columns by dragging the mouse around in the HBorder widget. * If he is doing that right now, this flag is TRUE. */ bool m_bResize; /** * The column over which the user pressed the mouse button. * The user may resize columns by dragging the mouse around the XBorder widget. * This is the column over which he pressed the mouse button. This column is going * to be resized. */ int m_iResizedColumn; /** * Last position of the mouse, when resizing. */ int m_iResizePos; /** * The label used for showing the current size, when resizing */ QLabel *m_lSize; /** * True when the mouse button is pressed */ bool m_bMousePressed; private: }; /** */ class KSpreadVBorder : public QWidget { Q_OBJECT public: KSpreadVBorder( QWidget *_parent, KSpreadCanvas *_canvas, KSpreadView *_view ); ~KSpreadVBorder(); int markerRow() const { return m_iSelectionAnchor; } void resizeRow( double resize, int nb = -1, bool makeUndo = true ); void adjustRow( int _row = -1, bool makeUndo = true ); void equalizeRow( double resize ); void updateRows( int from, int to ); QSize sizeHint() const; private slots: void doAutoScroll(); protected: virtual void paintEvent ( QPaintEvent* _ev ); virtual void mousePressEvent( QMouseEvent* _ev ); virtual void mouseReleaseEvent( QMouseEvent* _ev ); virtual void mouseMoveEvent( QMouseEvent* _ev ); virtual void mouseDoubleClickEvent( QMouseEvent* _ev ); virtual void wheelEvent( QWheelEvent* ); virtual void focusOutEvent( QFocusEvent* ev ); void paintSizeIndicator( int mouseY, bool firstTime ); private: KSpreadCanvas *m_pCanvas; KSpreadView *m_pView; QTimer * m_scrollTimer; bool m_bSelection; int m_iSelectionAnchor; bool m_bResize; int m_iResizedRow; int m_iResizePos; /** * The label used for showing the current size, when resizing */ QLabel *m_lSize; /** * True when the mouse button is pressed */ bool m_bMousePressed; }; /* * Tooltip, which displays the comment and cell content, when it's too short */ class KSpreadToolTip : public QToolTip { public: KSpreadToolTip( KSpreadCanvas* canvas ); protected: /** * @reimp */ void maybeTip( const QPoint& p ); private: KSpreadCanvas* m_canvas; }; #endif // KSPREAD_CANVAS diff --git a/kspread/kspread_cell.cc b/kspread/kspread_cell.cc index acd931df4ca..1227b3e3559 100644 --- a/kspread/kspread_cell.cc +++ b/kspread/kspread_cell.cc @@ -1,6386 +1,6388 @@ /* This file is part of the KDE project Copyright 2004 Tomas Mecir Copyright 1999-2002,2004 Laurent Montel Copyright 2002-2004 Ariya Hidayat Copyright 2001-2003 Philipp Mueller Copyright 2002-2003 Norbert Andres Copyright 2003 Reinhart Geiser Copyright 2003-2004 Meni Livne Copyright 2003 Peter Simonsson Copyright 1999-2002 David Faure Copyright 2000-2002 Werner Trobin Copyright 1999,2002 Harri Porten Copyright 2002 John Dailey Copyright 1998-2000 Torben Weis Copyright 2000 Bernd Wuebben Copyright 2000 Simon Hausmann Copyright 1999 Michael Reiher Copyright 1999 Boris Wedl Copyright 1998-1999 Reginald Stadlbauer 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 "kspread_canvas.h" #include "kspread_doc.h" #include "kspread_format.h" #include "kspread_global.h" #include "kspread_map.h" #include "kspread_sheetprint.h" #include "kspread_style.h" #include "kspread_style_manager.h" #include "kspread_util.h" #include "ksploadinginfo.h" #include "kspread_genvalidationstyle.h" #include "kspread_interpreter.h" #include "kspread_locale.h" #include "kspread_view.h" #include "kspread_value.h" #include "valueformatter.h" #include "valueparser.h" #include #include #include #define BORDER_SPACE 1 /** * A pointer to the decimal separator */ namespace KSpreadCell_LNS { QChar decimal_point = '\0'; } using namespace KSpreadCell_LNS; // Some variables are placed in CellExtra because normally they're not required // in simple case of cell(s). For example, most plain text cells don't need // to store information about spanned columns and rows, as this is only // the case with merged cells. // When the cell is getting complex (e.g. merged with other cells, contains // rich text, has validation criteria, etc), this CellExtra is allocated by // CellPrivate and starts to be available. Otherwise, it won't exist at all. class CellExtra { public: // Set when the cell contains rich text // At the moment, it's used to store hyperlink QSimpleRichText *QML; // amount of additional cells int extraXCells; int extraYCells; int mergedXCells; int mergedYCells; // If a cell overlapps other cells, then we have the cells width stored here. // This value does not mean anything unless extraXCells is different from 0. double extraWidth; // If a cell overlapps other cells, then we have the cells height stored here. // This value does not mean anything unless d->extra()->extraYCells is different from 0. double extraHeight; // A list of cells that obscure this one. // If this list is not empty, then this cell is obscured by another // enlarged object. This means that we have to call this object in order // of painting it for example instead of painting 'this'. QValueList obscuringCells; KSpreadConditions* conditions; KSpreadValidity * validity; // Store the number of line when you used multirow (default is 0) int nbLines; private: CellExtra& operator=( const CellExtra& ); }; class CellPrivate { public: // This cell's row. If it is 0, this is the default cell and its row/column can // not be determined. int row; // This cell's column. If it is 0, this is the default cell and its row/column can // not be determined. int column; // Tells which kind of content the cell holds. KSpreadCell::Content content; // Value of the cell, either typed by user or as result of formula KSpreadValue value; // Holds the user's input QString strText; // This is the text we want to display. Not necessarily the same as strText, // e.g. strText="1" and strOutText="1.00" // Also holds value that we got from calculation, formerly known as strFormulaOut QString strOutText; // The parse tree of the real formula (e.g: "=A1*A2"). KSParseNode* code; // position and dimension of displayed text double textX; double textY; double textWidth; double textHeight; // result of "fm.ascent()" in makeLayout. used in offsetAlign. int fmAscent; KSpreadCell* nextCell; KSpreadCell* previousCell; CellPrivate(); ~CellPrivate(); bool hasExtra() { return (cellExtra != 0); }; CellExtra* extra(); private: // "extra stuff", see explanation for CellExtra CellExtra* cellExtra; }; CellPrivate::CellPrivate() { row = 0; column = 0; content= KSpreadCell::Text; value = KSpreadValue::empty(); code = 0; textX = 0.0; textY = 0.0; textWidth = 0.0; textHeight = 0.0; fmAscent = 0; nextCell = 0; previousCell = 0; cellExtra = 0; } CellPrivate::~CellPrivate() { delete cellExtra; } CellExtra* CellPrivate::extra() { if( !cellExtra ) { cellExtra = new CellExtra; cellExtra->QML = 0; cellExtra->conditions = 0; cellExtra->validity = 0; cellExtra->extraXCells = 0; cellExtra->extraYCells = 0; cellExtra->mergedXCells = 0; cellExtra->mergedYCells = 0; cellExtra->extraWidth = 0.0; cellExtra->extraHeight = 0.0; cellExtra->nbLines = 0; } return cellExtra; } /***************************************************************************** * * KSpreadCell * *****************************************************************************/ KSpreadCell::KSpreadCell( KSpreadSheet * _table, int _column, int _row ) : KSpreadFormat( _table, _table->doc()->styleManager()->defaultStyle() ) { d = new CellPrivate; d->row = _row; d->column = _column; clearAllErrors(); } KSpreadCell::KSpreadCell( KSpreadSheet * _table, KSpreadStyle * _style, int _column, int _row ) : KSpreadFormat( _table, _style ) { d = new CellPrivate; d->row = _row; d->column = _column; clearAllErrors(); } KSpreadSheet * KSpreadCell::sheet() const { return m_pTable; } bool KSpreadCell::isDefault() const { return ( d->column == 0 ); } int KSpreadCell::row() const { /* Make sure this isn't called for the default cell. This assert can save you (could have saved me!) the hassle of some very obscure bugs. */ if ( isDefault() ) { kdWarning(36001) << "Error: Calling KSpreadCell::row() for default cell" << endl; return 0; } return d->row; } int KSpreadCell::column() const { /* Make sure this isn't called for the default cell. This assert can save you (could have saved me!) the hassle of some very obscure bugs. */ if ( isDefault() ) { kdWarning(36001) << "Error: Calling KSpreadCell::column() for default cell" << endl; return 0; } return d->column; } QString KSpreadCell::name() const { return name( d->column, d->row ); } QString KSpreadCell::fullName() const { return fullName( table(), d->column, d->row ); } QString KSpreadCell::name( int col, int row ) { return columnName( col ) + QString::number( row ); } QString KSpreadCell::fullName( const KSpreadSheet* s, int col, int row ) { return s->tableName() + "!" + name( col, row ); } QString KSpreadCell::columnName() const { return columnName( d->column ); } QString KSpreadCell::columnName( int column ) { QString str; unsigned digits = 1; unsigned offset = 0; column--; if( column > 4058115285 ) return QString("@@@"); for( unsigned limit = 26; column >= limit+offset; limit *= 26, digits++ ) offset += limit; for( unsigned c = column - offset; digits; --digits, c/=26 ) str.prepend( QChar( 'A' + (c%26) ) ); return str; } KSpreadCell::Content KSpreadCell::content() const { return d->content; } bool KSpreadCell::isFormula() const { return d->content == Formula; } QString KSpreadCell::text() const { return d->strText; } QString KSpreadCell::strOutText() const { return d->strOutText; } const KSpreadValue KSpreadCell::value() const { return d->value; } void KSpreadCell::setValue( const KSpreadValue& v ) { if (v.type() != KSpreadValue::Error) clearAllErrors(); d->value = v; // Free all content data if (d->hasExtra()) { delete d->extra()->QML; d->extra()->QML = 0; } //set the displayed text, if we hold an error value if (d->value.type() == KSpreadValue::Error) d->strOutText = d->value.errorMessage (); setFlag(Flag_LayoutDirty); setFlag(Flag_TextFormatDirty); //value of the cell has changed - trigger necessary actions valueChanged (); m_pTable->setRegionPaintDirty(cellRect()); } KSpreadCell* KSpreadCell::previousCell() const { return d->previousCell; } KSpreadCell* KSpreadCell::nextCell() const { return d->nextCell; } void KSpreadCell::setPreviousCell( KSpreadCell* c ) { d->previousCell = c; } void KSpreadCell::setNextCell( KSpreadCell* c ) { d->nextCell = c; } KSpreadValidity* KSpreadCell::getValidity( int newStruct ) { if ( (!newStruct) && (!d->hasExtra())) //we don't have validity struct and we don't want one return 0; if( ( d->extra()->validity == 0 ) && ( newStruct == -1 ) ) d->extra()->validity = new KSpreadValidity; return d->extra()->validity; } void KSpreadCell::removeValidity() { if (!d->hasExtra()) return; delete d->extra()->validity; d->extra()->validity = 0; } void KSpreadCell::copyFormat( KSpreadCell * _cell ) { copyFormat( _cell->column(), _cell->row() ); } void KSpreadCell::copyFormat( int _column, int _row ) { const 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 ) ); setDontPrintText( cell->getDontprintText(_column, _row ) ); setNotProtected( cell->notProtected(_column, _row ) ); setHideAll(cell->isHideAll(_column, _row ) ); setHideFormula(cell->isHideFormula(_column, _row ) ); setIndent( cell->getIndent(_column, _row ) ); setAngle( cell->getAngle(_column, _row) ); setFormatType( cell->getFormatType(_column, _row) ); Currency c; if ( cell->currencyInfo( c ) ) setCurrency( c ); QValueList conditionList = cell->conditionList(); if (d->hasExtra()) delete d->extra()->conditions; if ( cell->d->hasExtra() && cell->d->extra()->conditions ) setConditionList( conditionList ); else if (d->hasExtra()) d->extra()->conditions = 0; setComment( cell->comment( _column, _row ) ); } void KSpreadCell::copyAll( KSpreadCell *cell ) { Q_ASSERT( !isDefault() ); // trouble ahead... copyFormat( 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 ) ); } else setCellText( cell->text() ); } void KSpreadCell::defaultStyle() { defaultStyleFormat(); if (!d->hasExtra()) return; if ( d->extra()->conditions ) { delete d->extra()->conditions; d->extra()->conditions = 0; } delete d->extra()->validity; d->extra()->validity = 0L; } void KSpreadCell::formatChanged() { setFlag( Flag_LayoutDirty ); setFlag( Flag_TextFormatDirty ); } KSpreadFormat * KSpreadCell::fallbackFormat( int, int row ) { return table()->rowFormat( row ); } const KSpreadFormat * KSpreadCell::fallbackFormat( int, int row ) const { return table()->rowFormat( row ); } void KSpreadCell::forceExtraCells( int _col, int _row, int _x, int _y ) { // Unobscure the objects we obscure right now int extraXCells = d->hasExtra() ? d->extra()->extraXCells : 0; int extraYCells = d->hasExtra() ? d->extra()->extraYCells : 0; for( int x = _col; x <= _col + extraXCells; ++x ) for( int y = _row; y <= _row + extraYCells; ++y ) if ( x != _col || y != _row ) { KSpreadCell * cell = m_pTable->nonDefaultCell( x, y ); cell->unobscure(this); } // disable forcing ? if ( _x == 0 && _y == 0 ) { clearFlag( Flag_ForceExtra ); if (d->hasExtra()) { d->extra()->extraXCells = 0; d->extra()->extraYCells = 0; d->extra()->extraWidth = 0.0; d->extra()->extraHeight = 0.0; d->extra()->mergedXCells = 0; d->extra()->mergedYCells = 0; } //refresh the layout setFlag( Flag_LayoutDirty ); return; } setFlag(Flag_ForceExtra); d->extra()->extraXCells = _x; d->extra()->extraYCells = _y; d->extra()->mergedXCells = _x; d->extra()->mergedYCells = _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, true ); } // Refresh the layout // QPainter painter; // painter.begin( m_pTable->gui()->canvasWidget() ); setFlag( Flag_LayoutDirty ); } void KSpreadCell::move( int col, int row ) { setLayoutDirtyFlag(); setCalcDirtyFlag(); setDisplayDirtyFlag(); //int ex = extraXCells(); //int ey = d->extra()->extraYCells(); if (d->hasExtra()) d->extra()->obscuringCells.clear(); // Unobscure the objects we obscure right now int extraXCells = d->hasExtra() ? d->extra()->extraXCells : 0; int extraYCells = d->hasExtra() ? d->extra()->extraYCells : 0; for( int x = d->column; x <= d->column + extraXCells; ++x ) for( int y = d->row; y <= d->row + extraYCells; ++y ) if ( x != d->column || y != d->row ) { KSpreadCell *cell = m_pTable->nonDefaultCell( x, y ); cell->unobscure(this); } d->column = col; d->row = row; if (d->hasExtra()) { // d->extra()->extraXCells = 0; // d->extra()->extraYCells = 0; d->extra()->mergedXCells = 0; d->extra()->mergedYCells = 0; } //cell value has been changed (because we're another cell now) valueChanged (); // Reobscure cells if we are forced to do so. //if ( m_bForceExtraCells ) // forceExtraCells( col, row, ex, ey ); } void KSpreadCell::setLayoutDirtyFlag( bool format ) { setFlag( Flag_LayoutDirty ); if ( format ) setFlag( Flag_TextFormatDirty ); if (!d->hasExtra()) return; QValueList::iterator it = d->extra()->obscuringCells.begin(); QValueList::iterator end = d->extra()->obscuringCells.end(); for ( ; it != end; ++it ) { (*it)->setLayoutDirtyFlag( format ); } } bool KSpreadCell::needsPrinting() const { if ( isDefault() ) return FALSE; if ( !d->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() || d->strText.isEmpty(); } bool KSpreadCell::isObscured() const { if (!d->hasExtra()) return false; return !( d->extra()->obscuringCells.isEmpty() ); } bool KSpreadCell::isObscuringForced() const { if (!d->hasExtra()) return false; QValueList::const_iterator it = d->extra()->obscuringCells.begin(); QValueList::const_iterator end = d->extra()->obscuringCells.end(); for ( ; it != end; ++it ) { KSpreadCell *cell = *it; if (cell->isForceExtraCells()) { /* the cell might force extra cells, and then overlap even beyond that so just knowing that the obscuring cell forces extra isn't enough. We have to know that this cell is one of the ones it is forcing over. */ if (column() <= cell->column() + cell->d->extra()->mergedXCells && row() <= cell->row() + cell->mergedYCells() ) { return true; } } } return false; } QValueList KSpreadCell::obscuringCells() const { if (!d->hasExtra()) { QValueList empty; return empty; } return d->extra()->obscuringCells; } void KSpreadCell::clearObscuringCells() { if (!d->hasExtra()) return; d->extra()->obscuringCells.clear(); } void KSpreadCell::obscure( KSpreadCell *cell, bool isForcing ) { if (d->hasExtra()) { d->extra()->obscuringCells.remove( cell ); // removes *all* occurrences cell->clearObscuringCells(); } if ( isForcing ) { d->extra()->obscuringCells.prepend( cell ); } else { d->extra()->obscuringCells.append( cell ); } setFlag(Flag_LayoutDirty); m_pTable->setRegionPaintDirty( cellRect() ); } void KSpreadCell::unobscure( KSpreadCell * cell ) { if (d->hasExtra()) d->extra()->obscuringCells.remove( cell ); setFlag( Flag_LayoutDirty ); m_pTable->setRegionPaintDirty( cellRect() ); } void KSpreadCell::clicked( KSpreadCanvas* ) { return; } QString KSpreadCell::encodeFormula( bool _era, int _col, int _row ) { if ( _col == -1 ) _col = d->column; if ( _row == -1 ) _row = d->row; QString erg = ""; if(d->strText.isEmpty()) return d->strText; bool fix1 = FALSE; bool fix2 = FALSE; bool onNumber = false; unsigned int pos = 0; const unsigned int length = d->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 ( d->strText[pos] == '"' ) { erg += d->strText[pos++]; while ( pos < length && d->strText[pos] != '"' ) // till the end of the world^H^H^H "string" { erg += d->strText[pos++]; // Allow escaped double quotes (\") if ( pos < length && d->strText[pos] == '\\' && d->strText[pos+1] == '"' ) { erg += d->strText[pos++]; erg += d->strText[pos++]; } } if ( pos < length ) // also copy the trailing double quote erg += d->strText[pos++]; onNumber = false; } else if ( d->strText[pos].isDigit() ) { erg += d->strText[pos++]; fix1 = fix2 = FALSE; onNumber = true; } else if ( d->strText[pos] != '$' && !d->strText[pos].isLetter() ) { erg += d->strText[pos++]; fix1 = fix2 = FALSE; onNumber = false; } else { QString tmp = ""; if ( d->strText[pos] == '$' ) { tmp = "$"; pos++; fix1 = TRUE; } if ( d->strText[pos].isLetter() ) { QString buffer; unsigned int pos2 = 0; while ( pos < length && d->strText[pos].isLetter() ) { tmp += d->strText[pos]; buffer[pos2++] = d->strText[pos++]; } if ( d->strText[pos] == '$' ) { tmp += "$"; pos++; fix2 = TRUE; } if ( d->strText[pos].isDigit() ) { const unsigned int oldPos = pos; while ( pos < length && d->strText[pos].isDigit() ) ++pos; int row = 0; if ( pos != oldPos ) row = d->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 ( ( d->strText[pos] == '!' ) || d->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 if (_era) erg += QString( "\%%1" ).arg( col ); else erg += QString( "#%1" ).arg( col - _col ); if ( fix2 ) erg += QString( "$%1#").arg( row ); else if (_era) 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 = d->column; if ( _row == -1 ) _row = d->row; 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] == '$' || _text[pos] == '%') { bool abs1 = FALSE; bool abs2 = FALSE; bool era1 = FALSE; // if 1st is relative but encoded absolutely bool era2 = FALSE; switch ( _text[pos++] ) { case '$': abs1 = TRUE; break ; case '%': era1 = TRUE; break ; } 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 ( !abs1 && !era1 ) col += _col; // Skip '#' or '$' switch ( _text[pos++] ) { case '$': abs2 = TRUE; break ; case '%': era2 = TRUE; break ; } 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 ( !abs2 && !era2) 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 ( abs1 ) erg += "$"; erg += KSpreadCell::columnName(col); //Get column text if ( abs2 ) erg += "$"; erg += QString::number( row ); } else erg += _text[pos++]; } return erg; } void KSpreadCell::freeAllObscuredCells() { // // Free all obscured cells. // if (!d->hasExtra()) return; for ( int x = d->column + d->extra()->mergedXCells; x <= d->column + d->extra()->extraXCells; ++x ) for ( int y = d->row + d->extra()->mergedYCells; y <= d->row + d->extra()->extraYCells; ++y ) if ( x != d->column || y != d->row ) { KSpreadCell *cell = m_pTable->cellAt( x, y ); cell->unobscure(this); } d->extra()->extraXCells = d->extra()->mergedXCells; d->extra()->extraYCells = d->extra()->mergedYCells; } // ##### 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 d->row and d->column are 0 and 0, but col and row // are the real coordinates of the cell. // due to QSimpleRichText, always make layout for QML if ( !testFlag( Flag_LayoutDirty ) && ( (!d->hasExtra()) || (!d->extra()->QML) ) ) return; if (d->hasExtra()) d->extra()->nbLines = 0; clearFlag( Flag_CellTooShortX ); clearFlag( Flag_CellTooShortY ); freeAllObscuredCells(); /* but reobscure the ones that are forced obscuring */ if (d->hasExtra()) forceExtraCells( d->column, d->row, d->extra()->mergedXCells, d->extra()->mergedYCells ); ColumnFormat *cl1 = m_pTable->columnFormat( _col ); RowFormat *rl1 = m_pTable->rowFormat( _row ); if ( cl1->isHide() || ( rl1->dblHeight() <= m_pTable->doc()->unzoomItY( 2 ) ) ) { clearFlag( Flag_LayoutDirty ); return; } setOutputText(); // Empty text? if ( d->strOutText.isEmpty() ) { d->strOutText = QString::null; if ( isDefault() ) { clearFlag( Flag_LayoutDirty ); return; } } // // Determine the correct font // applyZoomedFont( _painter, _col, _row ); /** * RichText */ if ( d->hasExtra() && d->extra()->QML ) { delete d->extra()->QML; // TODO: more formatting as QStyleSheet supports QString qml_text; qml_text += QString("").arg( _painter.font().family() ); //if( _painter.font().bold() ) qml_text += ""; //if( _painter.font().italic() ) qml_text += ""; //if( _painter.font().underline() ) qml_text += ""; qml_text += d->strText.mid(1); d->extra()->QML = new QSimpleRichText( qml_text, _painter.font() ); setFlag( Flag_LayoutDirty ); // Calculate how many cells we could use in addition right hand // Never use more then 10 cells. int right = 0; double max_width = dblWidth( _col ); bool ende = false; int c; d->extra()->QML->setWidth( &_painter, (int)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 { ColumnFormat *cl = m_pTable->columnFormat( c ); max_width += cl->dblWidth(); // Can we make use of extra cells ? int h = d->extra()->QML->height(); d->extra()->QML->setWidth( &_painter, int( max_width ) ); if ( d->extra()->QML->height() < h ) ++right; else { max_width -= cl->dblWidth(); d->extra()->QML->setWidth( &_painter, int( max_width ) ); ende = true; } } } // How may space do we need now ? // d->extra()->QML->setWidth( &_painter, max_width ); int h = d->extra()->QML->height(); int w = d->extra()->QML->width(); kdDebug(36001) << "QML w=" << w << " max=" << max_width << endl; // Occupy the needed extra cells in horizontal direction max_width = dblWidth( _col ); ende = ( max_width >= w ); for( c = _col + 1; !ende && c <= _col + right; ++c ) { KSpreadCell *cell = m_pTable->nonDefaultCell( c, _row ); cell->obscure( this ); ColumnFormat *cl = m_pTable->columnFormat( c ); max_width += cl->dblWidth(); if ( max_width >= w ) ende = true; } d->extra()->extraXCells = c - _col - 1; /* we may have used extra cells, but only cells that we were already merged to. */ if( d->extra()->extraXCells < d->extra()->mergedXCells ) { d->extra()->extraXCells = d->extra()->mergedXCells; } else { d->extra()->extraWidth = max_width; } // Occupy the needed extra cells in vertical direction double max_height = dblHeight( _row ); int r = _row; ende = ( max_height >= h ); for( r = _row + 1; !ende && r < _row + 500; ++r ) { bool empty = true; for( c = _col; c <= _col + d->extra()->extraXCells; ++c ) { KSpreadCell *cell = m_pTable->cellAt( c, r ); if ( cell && !cell->isEmpty() ) { empty = false; break; } } if ( !empty ) { ende = true; break; } else { // Occupy this row for( c = _col; c <= _col + d->extra()->extraXCells; ++c ) { KSpreadCell *cell = m_pTable->nonDefaultCell( c, r ); cell->obscure( this ); } RowFormat *rl = m_pTable->rowFormat( r ); max_height += rl->dblHeight(); if ( max_height >= h ) ende = true; } } d->extra()->extraYCells = r - _row - 1; /* we may have used extra cells, but only cells that we were already merged to. */ if( d->extra()->extraYCells < d->extra()->mergedYCells ) { d->extra()->extraYCells = d->extra()->mergedYCells; } else { d->extra()->extraHeight = max_height; } clearFlag( Flag_LayoutDirty ); textSize( _painter ); offsetAlign( _col, _row ); return; } // Calculate text dimensions textSize( _painter ); QFontMetrics fm = _painter.fontMetrics(); // // Calculate the size of the cell // RowFormat *rl = m_pTable->rowFormat( d->row ); ColumnFormat *cl = m_pTable->columnFormat( d->column ); double w = cl->dblWidth(); double h = rl->dblHeight(); // Calculate the extraWidth and extraHeight if we are forced to. /* Use d->extra()->extraWidth/height here? Isn't it already calculated?*/ /* No, they are calculated here only (beside of QML part above) Philipp */ if ( testFlag( Flag_ForceExtra ) ) { int extraXCells = d->hasExtra() ? d->extra()->extraXCells : 0; int extraYCells = d->hasExtra() ? d->extra()->extraYCells : 0; for ( int x = _col + 1; x <= _col + extraXCells; x++ ) { ColumnFormat *cl = m_pTable->columnFormat( x ); w += cl->dblWidth() ; } for ( int y = _row + 1; y <= _row + extraYCells; y++ ) { RowFormat *rl = m_pTable->rowFormat( y ); h += rl->dblHeight() ; } } if (d->hasExtra()) { d->extra()->extraWidth = w; d->extra()->extraHeight = h; } // Do we need to break the line into multiple lines and are we allowed to // do so? int lines = 1; if ( d->textWidth > w - 2 * BORDER_SPACE - leftBorderWidth( _col, _row ) - rightBorderWidth( _col, _row ) && multiRow( _col, _row ) ) { // copy of d->strOutText QString o = d->strOutText; // No space ? if( o.find(' ') != -1 ) { o += ' '; int start = 0; int pos = 0; int pos1 = 0; d->strOutText = ""; do { pos = o.find( ' ', pos ); double width = m_pTable->doc()->unzoomItX( fm.width( d->strOutText.mid( start, (pos1-start) ) + o.mid( pos1, (pos-pos1) ) ) ); if ( width <= w - 2 * BORDER_SPACE - leftBorderWidth( _col, _row ) - rightBorderWidth( _col, _row ) ) { d->strOutText += o.mid( pos1, pos - pos1 ); pos1 = pos; } else { if( o.at( pos1 ) == ' ' ) pos1 = pos1 + 1; if( pos1 != 0 && pos != -1 ) { d->strOutText += "\n" + o.mid( pos1, pos - pos1 ); lines++; } else d->strOutText += o.mid( pos1, pos - pos1 ); start = pos1; pos1 = pos; } pos++; } while( o.find( ' ', pos ) != -1 ); } d->textHeight *= lines; if (lines > 1) d->extra()->nbLines = lines; d->textX = 0.0; // Calculate the maximum width QString t; int i; int pos = 0; d->textWidth = 0.0; do { i = d->strOutText.find( "\n", pos ); if ( i == -1 ) t = d->strOutText.mid( pos, d->strOutText.length() - pos ); else { t = d->strOutText.mid( pos, i - pos ); pos = i + 1; } double tw = m_pTable->doc()->unzoomItX( fm.width( t ) ); if ( tw > d->textWidth ) d->textWidth = tw; } while ( i != -1 ); } d->fmAscent = fm.ascent(); // Calculate d->textX and d->textY offsetAlign( _col, _row ); double indent = 0.0; int a = effAlignX(); //apply indent if text is align to left not when text is at right or middle if( a == KSpreadCell::Left && !isEmpty() ) indent = getIndent( _col, _row ); if( verticalText( _col, _row ) || getAngle( _col, _row ) != 0 ) { RowFormat *rl = m_pTable->rowFormat( _row ); if( d->textHeight >= rl->dblHeight() ) { setFlag( Flag_CellTooShortX ); } } // Do we have to occupy additional cells right hand ? if ( d->textWidth + indent > w - 2 * BORDER_SPACE - leftBorderWidth( _col, _row ) - rightBorderWidth( _col, _row ) ) { int c = d->column; int end = 0; // Find free cells right hand to this one while ( !end ) { ColumnFormat *cl2 = m_pTable->columnFormat( c + 1 ); KSpreadCell *cell = m_pTable->visibleCellAt( c + 1, d->row ); if ( cell->isEmpty() ) { w += cl2->dblWidth() - 1; c++; // Enough space ? if ( d->textWidth + 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. Nor for numeric or boolean, apparently. Also check to make sure we haven't already force-merged enough cells */ /* ##### Why not right/center aligned text? No one knows. Perhaps it has something to do with calculating how much room the text needs in those cases? */ if( align( _col, _row ) == KSpreadCell::Left || align( _col, _row ) == KSpreadCell::Undefined ) { if( c - d->column > d->extra()->mergedXCells ) { d->extra()->extraXCells = c - d->column; d->extra()->extraWidth = w; for( int i = d->column + 1; i <= c; ++i ) { KSpreadCell *cell = m_pTable->nonDefaultCell( i, d->row ); cell->obscure( this ); } //Not enough space if( end == -1 ) { setFlag( Flag_CellTooShortX ); } } else { setFlag( Flag_CellTooShortX ); } } else { setFlag( Flag_CellTooShortX ); } } // Do we have to occupy additional cells at the bottom ? if ( ( (d->hasExtra() && d->extra()->QML) || multiRow( _col, _row ) ) && d->textHeight > h - 2 * BORDER_SPACE - topBorderWidth( _col, _row ) - bottomBorderWidth( _col, _row ) ) { int r = d->row; int end = 0; // Find free cells bottom to this one while ( !end ) { RowFormat *rl2 = m_pTable->rowFormat( r + 1 ); KSpreadCell *cell = m_pTable->visibleCellAt( d->column, r + 1 ); if ( cell->isEmpty() ) { h += rl2->dblHeight() - 1.0; r++; // Enough space ? if ( d->textHeight <= h - 2 * BORDER_SPACE - topBorderWidth( _col, _row ) - bottomBorderWidth( _col, _row ) ) end = 1; } // Not enough space, but the next cell is not empty else end = -1; } /* Check to make sure we haven't already force-merged enough cells */ if( r - d->row > d->extra()->mergedYCells ) { d->extra()->extraYCells = r - d->row; d->extra()->extraHeight = h; for( int i = d->row + 1; i <= r; ++i ) { KSpreadCell *cell = m_pTable->nonDefaultCell( d->column, i ); cell->obscure( this ); } //Not enough space if( end == -1 ) { setFlag( Flag_CellTooShortY ); } } else { setFlag( Flag_CellTooShortY ); } } clearFlag( Flag_LayoutDirty ); return; } void KSpreadCell::valueChanged () { update(); m_pTable->valueChanged (this); } void KSpreadCell::setOutputText() { if ( isDefault() ) { d->strOutText = QString::null; if ( d->hasExtra() && d->extra()->conditions ) d->extra()->conditions->checkMatches(); return; } if ( !testFlag( Flag_TextFormatDirty ) ) return; clearFlag( Flag_TextFormatDirty ); // // Turn the stored value in a string // //should we display the formula? if ( (!hasError()) && isFormula() && m_pTable->getShowFormula() && !( m_pTable->isProtected() && isHideFormula( d->column, d->row ) ) ) d->strOutText = d->strText; else { //we should display real value KSpread::ValueFormatter *formatter = KSpread::ValueFormatter::self(); d->strOutText = formatter->formatText (this, formatType()); } //check conditions if needed if ( d->hasExtra() && d->extra()->conditions ) d->extra()->conditions->checkMatches(); } //used in makeLayout() and calculateTextParameters() void KSpreadCell::offsetAlign( int _col, int _row ) { int a; AlignY ay; int tmpAngle; bool tmpVerticalText; bool tmpMultiRow; int tmpTopBorderWidth = effTopBorderPen( _col, _row ).width(); if ( d->hasExtra() && d->extra()->conditions && d->extra()->conditions->matchedStyle() ) { if ( d->extra()->conditions->matchedStyle()->hasFeature( KSpreadStyle::SAlignX, true ) ) a = d->extra()->conditions->matchedStyle()->alignX(); else a = align( _col, _row ); if ( d->extra()->conditions->matchedStyle()->hasFeature( KSpreadStyle::SVerticalText, true ) ) tmpVerticalText = d->extra()->conditions->matchedStyle()->hasProperty( KSpreadStyle::PVerticalText ); else tmpVerticalText = verticalText( _col, _row ); if ( d->extra()->conditions->matchedStyle()->hasFeature( KSpreadStyle::SMultiRow, true ) ) tmpMultiRow = d->extra()->conditions->matchedStyle()->hasProperty( KSpreadStyle::PMultiRow ); else tmpMultiRow = multiRow( _col, _row ); if ( d->extra()->conditions->matchedStyle()->hasFeature( KSpreadStyle::SAlignY, true ) ) ay = d->extra()->conditions->matchedStyle()->alignY(); else ay = alignY( _col, _row ); if ( d->extra()->conditions->matchedStyle()->hasFeature( KSpreadStyle::SAngle, true ) ) tmpAngle = d->extra()->conditions->matchedStyle()->rotateAngle(); else tmpAngle = getAngle( _col, _row ); } else { a = align( _col, _row ); ay = alignY( _col, _row ); tmpAngle = getAngle( _col, _row ); tmpVerticalText = verticalText( _col, _row ); tmpMultiRow = multiRow( _col, _row ); } RowFormat * rl = m_pTable->rowFormat( _row ); ColumnFormat * cl = m_pTable->columnFormat( _col ); double w = cl->dblWidth(); double h = rl->dblHeight(); if ( d->hasExtra() && d->extra()->extraXCells ) w = d->extra()->extraWidth; if ( d->hasExtra() && d->extra()->extraYCells ) h = d->extra()->extraHeight; switch( ay ) { case KSpreadCell::Top: if ( tmpAngle == 0 ) d->textY = tmpTopBorderWidth + BORDER_SPACE + (double) d->fmAscent / m_pTable->doc()->zoomedResolutionY(); else { if ( tmpAngle < 0 ) d->textY = tmpTopBorderWidth + BORDER_SPACE; else d->textY = tmpTopBorderWidth + BORDER_SPACE + (double)d->fmAscent * cos( tmpAngle * M_PI / 180 ) / m_pTable->doc()->zoomedResolutionY(); } break; case KSpreadCell::Bottom: if ( !tmpVerticalText && !tmpMultiRow && !tmpAngle ) { d->textY = h - BORDER_SPACE - effBottomBorderPen( _col, _row ).width(); if ( d->hasExtra() && d->extra()->QML ) d->textY = d->textY - d->extra()->QML->height(); } else if ( tmpAngle != 0 ) { if ( h - BORDER_SPACE - d->textHeight - effBottomBorderPen( _col, _row ).width() > 0 ) { if ( tmpAngle < 0 ) d->textY = h - BORDER_SPACE - d->textHeight - effBottomBorderPen( _col, _row ).width(); else d->textY = h - BORDER_SPACE - d->textHeight - effBottomBorderPen( _col, _row ).width() + (double) d->fmAscent * cos( tmpAngle * M_PI / 180 ) / m_pTable->doc()->zoomedResolutionY(); } else { if ( tmpAngle < 0 ) d->textY = tmpTopBorderWidth + BORDER_SPACE ; else d->textY = tmpTopBorderWidth + BORDER_SPACE + (double) d->fmAscent * cos( tmpAngle * M_PI / 180 ) / m_pTable->doc()->zoomedResolutionY(); } } else if ( tmpMultiRow ) { int tmpline = d->hasExtra() ? d->extra()->nbLines : 0; if ( tmpline > 1 ) tmpline--; //number of extra lines if( h - BORDER_SPACE - d->textHeight * d->extra()->nbLines - effBottomBorderPen( _col, _row ).width() > 0 ) d->textY = h - BORDER_SPACE - d->textHeight * tmpline - effBottomBorderPen( _col, _row ).width(); else d->textY = tmpTopBorderWidth + BORDER_SPACE + (double) d->fmAscent / m_pTable->doc()->zoomedResolutionY(); } else if ( h - BORDER_SPACE - d->textHeight - effBottomBorderPen( _col, _row ).width() > 0 ) d->textY = h - BORDER_SPACE - d->textHeight - effBottomBorderPen( _col, _row ).width() + (double)d->fmAscent / m_pTable->doc()->zoomedResolutionY(); else d->textY = tmpTopBorderWidth + BORDER_SPACE + (double) d->fmAscent / m_pTable->doc()->zoomedResolutionY(); break; case KSpreadCell::Middle: case KSpreadCell::UndefinedY: if ( !tmpVerticalText && !tmpMultiRow && !tmpAngle ) { d->textY = ( h - d->textHeight ) / 2 + (double) d->fmAscent / m_pTable->doc()->zoomedResolutionY(); } else if ( tmpAngle != 0 ) { if ( h - d->textHeight > 0 ) { if ( tmpAngle < 0 ) d->textY = ( h - d->textHeight ) / 2 ; else d->textY = ( h - d->textHeight ) / 2 + (double) d->fmAscent * cos( tmpAngle * M_PI / 180 ) / m_pTable->doc()->zoomedResolutionY(); } else { if ( tmpAngle < 0 ) d->textY = tmpTopBorderWidth + BORDER_SPACE; else d->textY = tmpTopBorderWidth + BORDER_SPACE + (double)d->fmAscent * cos( tmpAngle * M_PI / 180 ) / m_pTable->doc()->zoomedResolutionY(); } } else if ( tmpMultiRow ) { int tmpline = d->hasExtra() ? d->extra()->nbLines : 0; if ( tmpline == 0 ) tmpline = 1; if ( h - d->textHeight * tmpline > 0 ) d->textY = ( h - d->textHeight * tmpline ) / 2 + (double) d->fmAscent / m_pTable->doc()->zoomedResolutionY(); else d->textY = tmpTopBorderWidth + BORDER_SPACE + (double) d->fmAscent / m_pTable->doc()->zoomedResolutionY(); } else if ( h - d->textHeight > 0 ) d->textY = ( h - d->textHeight ) / 2 + (double)d->fmAscent / m_pTable->doc()->zoomedResolutionY(); else d->textY = tmpTopBorderWidth + BORDER_SPACE + (double)d->fmAscent / m_pTable->doc()->zoomedResolutionY(); break; } a = effAlignX(); if ( m_pTable->getShowFormula() && !( m_pTable->isProtected() && isHideFormula( _col, _row ) ) ) a = KSpreadCell::Left; switch( a ) { case KSpreadCell::Left: d->textX = effLeftBorderPen( _col, _row ).width() + BORDER_SPACE; break; case KSpreadCell::Right: d->textX = w - BORDER_SPACE - d->textWidth - effRightBorderPen( _col, _row ).width(); break; case KSpreadCell::Center: d->textX = ( w - d->textWidth ) / 2; break; } } //used in makeLayout() and calculateTextParameters() void KSpreadCell::textSize( QPainter &_paint ) { QFontMetrics fm = _paint.fontMetrics(); // Horizontal text ? int tmpAngle; int _row = row(); int _col = column(); bool tmpVerticalText; bool fontUnderlined; AlignY ay; if ( d->hasExtra() && d->extra()->conditions && d->extra()->conditions->matchedStyle() ) { if ( d->extra()->conditions->matchedStyle()->hasFeature( KSpreadStyle::SAngle, true ) ) tmpAngle = d->extra()->conditions->matchedStyle()->rotateAngle(); else tmpAngle = getAngle( _col, _row ); if ( d->extra()->conditions->matchedStyle()->hasFeature( KSpreadStyle::SVerticalText, true ) ) tmpVerticalText = d->extra()->conditions->matchedStyle()->hasProperty( KSpreadStyle::PVerticalText ); else tmpVerticalText = verticalText( _col, _row ); if ( d->extra()->conditions->matchedStyle()->hasFeature( KSpreadStyle::SAlignY, true ) ) ay = d->extra()->conditions->matchedStyle()->alignY(); else ay = alignY( _col, _row ); if ( d->extra()->conditions->matchedStyle()->hasFeature( KSpreadStyle::SFontFlag, true ) ) fontUnderlined = ( d->extra()->conditions->matchedStyle()->fontFlags() && (uint) KSpreadStyle::FUnderline ); else fontUnderlined = textFontUnderline( _col, _row ); } else { tmpAngle = getAngle( _col, _row ); tmpVerticalText = verticalText( _col, _row ); ay = alignY( _col, _row ); fontUnderlined = textFontUnderline( _col, _row ); } if( d->hasExtra() && d->extra()->QML ) { d->textWidth = m_pTable->doc()->unzoomItX( d->extra()->QML->widthUsed() ); d->textHeight = m_pTable->doc()->unzoomItY( d->extra()->QML->height() ); return; } if ( !tmpVerticalText && !tmpAngle ) { d->textWidth = m_pTable->doc()->unzoomItX( fm.width( d->strOutText ) ); int offsetFont = 0; if ( ( ay == KSpreadCell::Bottom ) && fontUnderlined ) { offsetFont = fm.underlinePos() + 1; } d->textHeight = m_pTable->doc()->unzoomItY( fm.ascent() + fm.descent() + offsetFont ); } // Rotated text ? else if ( tmpAngle!= 0 ) { d->textHeight = m_pTable->doc()->unzoomItY( int( cos( tmpAngle * M_PI / 180 ) * ( fm.ascent() + fm.descent() ) + abs( int( ( fm.width( d->strOutText ) * sin( tmpAngle * M_PI / 180 ) ) ) ) ) ); d->textWidth = m_pTable->doc()->unzoomItX( int( abs( int( ( sin( tmpAngle * M_PI / 180 ) * ( fm.ascent() + fm.descent() ) ) ) ) + fm.width( d->strOutText ) * cos ( tmpAngle * M_PI / 180 ) ) ); //kdDebug(36001)<<"d->textWidth"<textWidth<<"d->textHeight"<textHeight<strOutText.length(); i++ ) width = QMAX( width, fm.width( d->strOutText.at( i ) ) ); d->textWidth = m_pTable->doc()->unzoomItX( width ); d->textHeight = m_pTable->doc()->unzoomItY( ( fm.ascent() + fm.descent() ) * d->strOutText.length() ); } } //used in makeLayout() and calculateTextParameters() void KSpreadCell::applyZoomedFont( QPainter &painter, int _col, int _row ) { QFont tmpFont( textFont( _col, _row ) ); if ( d->hasExtra() && d->extra()->conditions && d->extra()->conditions->matchedStyle() ) { KSpreadStyle * s = d->extra()->conditions->matchedStyle(); if ( s->hasFeature( KSpreadStyle::SFontSize, true ) ) tmpFont.setPointSizeFloat( s->fontSize() ); if ( s->hasFeature( KSpreadStyle::SFontFlag, true ) ) { uint flags = s->fontFlags(); tmpFont.setBold( flags & (uint) KSpreadStyle::FBold ); tmpFont.setUnderline( flags & (uint) KSpreadStyle::FUnderline ); tmpFont.setItalic( flags & (uint) KSpreadStyle::FItalic ); tmpFont.setStrikeOut( flags & (uint) KSpreadStyle::FStrike ); } if ( s->hasFeature( KSpreadStyle::SFontFamily, true ) ) tmpFont.setFamily( s->fontFamily() ); } // else /* * could somebody please explaint why we check for isProtected or isHideFormula here * if ( d->extra()->conditions && d->extra()->conditions->currentCondition( condition ) && !(m_pTable->getShowFormula() && !( m_pTable->isProtected() && isHideFormula( d->column, d->row ) ) ) ) { if ( condition.fontcond ) tmpFont = *(condition.fontcond); else tmpFont = condition.style->font(); } */ tmpFont.setPointSizeFloat( 0.01 * m_pTable->doc()->zoom() * tmpFont.pointSizeFloat() ); painter.setFont( tmpFont ); } //used in KSpreadSheet::adjustColumnHelper and KSpreadSheet::adjustRow void KSpreadCell::calculateTextParameters( QPainter &_paint, int _col, int _row ) { applyZoomedFont( _paint, _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 ( d->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... */ d->code = m_pTable->doc()->interpreter()->parse( context, m_pTable, /*sDelocalizedText*/d->strText ); // Did a syntax error occur ? if ( context.exception() ) { clearFormula(); setFlag(Flag_ParseError); KSpreadValue v; v.setError ( "####" ); setValue (v); if (m_pTable->doc()->getShowMessageError()) { QString tmp(i18n("Error in cell %1\n\n")); tmp = tmp.arg( fullName() ); tmp += context.exception()->toString( context ); KMessageBox::error( (QWidget*)0L, tmp); } return false; } //our value has been changed //TODO: is this necessary? valueChanged (); return true; } void KSpreadCell::clearFormula() { delete d->code; d->code = 0L; } bool KSpreadCell::calc(bool delay) { if ( !isFormula() ) return true; if ( d->code == 0 ) { if ( testFlag( Flag_ParseError ) ) // there was a parse error return false; else { /* we were probably at a "isLoading() = true" state when we originally * parsed */ makeFormula(); if ( d->code == 0 ) // there was a parse error return false; } } if ( !testFlag( Flag_CalcDirty ) ) return true; if ( delay ) { if ( m_pTable->doc()->delayCalculation() ) return true; } setFlag(Flag_LayoutDirty); setFlag(Flag_TextFormatDirty); clearFlag(Flag_CalcDirty); KSContext& context = m_pTable->doc()->context(); if ( !m_pTable->doc()->interpreter()->evaluate( context, d->code, m_pTable, this ) ) { // If we got an error during evaluation ... setFlag(Flag_ParseError); setFlag(Flag_LayoutDirty); KSpreadValue v; v.setError( "####" ); setValue (v); // Print out exception if any if ( context.exception() && m_pTable->doc()->getShowMessageError()) { QString tmp(i18n("Error in cell %1\n\n")); tmp = tmp.arg( fullName() ); tmp += context.exception()->toString( context ); KMessageBox::error( (QWidget*)0L, tmp); } // setFlag(Flag_LayoutDirty); clearFlag(Flag_CalcDirty); return false; } //d->strOutText will be set in setOutputText (called by makeLayout), //so we needn't worry about that here else if ( context.value()->type() == KSValue::DoubleType ) { setValue ( KSpreadValue( context.value()->doubleValue() ) ); checkNumberFormat(); // auto-chooses number or scientific } else if ( context.value()->type() == KSValue::IntType ) { setValue ( KSpreadValue( (int)context.value()->intValue() ) ); checkNumberFormat(); // auto-chooses number or scientific } else if ( context.value()->type() == KSValue::BoolType ) { setValue ( KSpreadValue( context.value()->boolValue() ) ); setFormatType(Number_format); } else if ( context.value()->type() == KSValue::TimeType ) { setValue( KSpreadValue( context.value()->timeValue() ) ); //change format FormatType tmpFormat = formatType(); if (!isTime()) { tmpFormat = Time_format; setFormatType (Time_format); } } else if ( context.value()->type() == KSValue::DateType) { setValue ( KSpreadValue( context.value()->dateValue() ) ); FormatType tmpFormat = formatType(); if (!isDate()) { tmpFormat = ShortDate_format; setFormatType (ShortDate_format); } } else if ( context.value()->type() == KSValue::Empty ) { setValue (KSpreadValue::empty()); // Format the result appropriately setFormatType(Number_format); } else { //FIXME m_dataType = StringData; QString str = context.value()->toString( context ); setValue (KSpreadValue (str)); if (!str.isEmpty() && str[0] == '!' ) { d->extra()->QML = new QSimpleRichText(str.mid(1), QApplication::font()); //, m_pTable->widget() ); } setFormatType(Text_format); } clearFlag(Flag_CalcDirty); setFlag(Flag_LayoutDirty); return true; } void KSpreadCell::paintCell( const KoRect & rect, QPainter & painter, KSpreadView * view, const KoPoint & coordinate, const QPoint &cellRef, bool paintBorderRight, bool paintBorderBottom, bool paintBorderLeft, bool paintBorderTop, QPen & rightPen, QPen & bottomPen, QPen & leftPen, QPen & topPen, bool drawCursor ) { if ( testFlag( Flag_PaintingCell ) ) return; setFlag( Flag_PaintingCell ); 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 && d->extra()->obscuringCells.isEmpty())); /* the cellref passed in should be this cell -- except if this is the default cell */ Q_ASSERT(!(((cellRef.x() != d->column) || (cellRef.y() != d->row)) && !isDefault())); double left = coordinate.x(); ColumnFormat * colFormat = m_pTable->columnFormat( cellRef.x() ); RowFormat * rowFormat = m_pTable->rowFormat( cellRef.y() ); double width, height; if (d->hasExtra()) { width = d->extra()->extraXCells ? d->extra()->extraWidth : colFormat->dblWidth(); height = d->extra()->extraYCells ? d->extra()->extraHeight : rowFormat->dblHeight(); } else { width = colFormat->dblWidth(); height = rowFormat->dblHeight(); } if ( m_pTable->layoutDirection()==KSpreadSheet::RightToLeft && view && view->canvasWidget() ) { double dwidth = view->doc()->unzoomItX(view->canvasWidget()->width()); left = dwidth - coordinate.x() - width; } const KoRect cellRect( left, coordinate.y(), width, height ); bool selected = false; if ( view != NULL ) { selected = view->selection().contains( cellRef ); /* but the cell doesn't look selected if this is the marker cell */ KSpreadCell * cell = m_pTable->cellAt( view->marker() ); QPoint bottomRight( view->marker().x() + cell->extraXCells(), view->marker().y() + cell->extraYCells() ); QRect markerArea( view->marker(), bottomRight ); selected = selected && !( markerArea.contains( cellRef ) ); // Dont draw any selection when printing. if ( painter.device()->isExtDev() || !drawCursor ) selected = false; } // Need to make a new layout ? /* TODO - this needs to be taken out eventually - it is done in canvas::paintUpdates */ if ( testFlag( Flag_LayoutDirty ) ) makeLayout( painter, cellRef.x(), cellRef.y() ); if ( !cellRect.intersects( rect ) ) { clearFlag( Flag_PaintingCell ); return; } QColor backgroundColor; if ( d->hasExtra() && d->extra()->conditions &&d->extra()->conditions->matchedStyle() && d->extra()->conditions->matchedStyle()->hasFeature( KSpreadStyle::SBackgroundColor, true ) ) backgroundColor = d->extra()->conditions->matchedStyle()->bgColor(); else backgroundColor = bgColor( cellRef.x(), cellRef.y() ); if ( !isObscuringForced() ) paintBackground( painter, cellRect, cellRef, selected, backgroundColor ); if( painter.device()->devType() != QInternal::Printer ) paintDefaultBorders( painter, rect, cellRect, cellRef, paintBorderRight, paintBorderBottom, paintBorderLeft, paintBorderTop, rightPen, bottomPen, leftPen, topPen ); /* paint all the cells that this one obscures */ paintingObscured++; paintObscuredCells( rect, painter, view, cellRect, cellRef, paintBorderRight, paintBorderBottom, paintBorderLeft, paintBorderTop, rightPen, bottomPen, leftPen, topPen ); paintingObscured--; //If we print pages then we disable clipping otherwise borders are cut in the middle at the page borders if ( painter.device()->isExtDev() ) painter.setClipping( false ); if ( !isObscuringForced() ) paintCellBorders( painter, rect, cellRect, cellRef, paintBorderRight, paintBorderBottom, paintBorderLeft, paintBorderTop, rightPen, bottomPen, leftPen, topPen ); if ( painter.device()->isExtDev() ) painter.setClipping( true ); paintCellDiagonalLines( painter, cellRect, cellRef ); paintPageBorders( painter, cellRect, cellRef, paintBorderRight, paintBorderBottom ); /* now print content, if this cell isn't obscured */ if ( !isObscured() ) /* don't paint content if this cell is obscured */ { if ( !painter.device()->isExtDev() || m_pTable->print()->printCommentIndicator() ) paintCommentIndicator( painter, cellRect, cellRef, backgroundColor ); if ( !painter.device()->isExtDev() || m_pTable->print()->printFormulaIndicator() ) paintFormulaIndicator( painter, cellRect, backgroundColor ); paintMoreTextIndicator( painter, cellRect, backgroundColor ); /** * QML ? */ if ( d->hasExtra() && d->extra()->QML && ( !painter.device()->isExtDev() || !getDontprintText( cellRef.x(), cellRef.y() ) ) && !( m_pTable->isProtected() && isHideAll( cellRef.x(), cellRef.y() ) ) ) { paintText( painter, cellRect, cellRef ); } /** * Usual Text */ else if ( !d->strOutText.isEmpty() && ( !painter.device()->isExtDev() || !getDontprintText( cellRef.x(), cellRef.y() ) ) && !( m_pTable->isProtected() && isHideAll( cellRef.x(), cellRef.y() ) ) ) { paintText( painter, cellRect, cellRef ); } } if ( isObscured() && paintingObscured == 0 ) { /* print the cells 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 */ /* Store the obscuringCells list in a list of QPoint(column, row) This avoids crashes during the iteration through obscuringCells, when the cells may get non valid or the list itself gets changed during a call of obscuringCell->paintCell (this happens e.g. when there is an updateDepend) */ if (d->hasExtra()) { QValueList listPoints; QValueList::iterator it = d->extra()->obscuringCells.begin(); QValueList::iterator end = d->extra()->obscuringCells.end(); for ( ; it != end; ++it ) { KSpreadCell *obscuringCell = *it; listPoints.append( QPoint( obscuringCell->column(), obscuringCell->row() ) ); } QValueList::iterator it1 = listPoints.begin(); QValueList::iterator end1 = listPoints.end(); for ( ; it1 != end1; ++it1 ) { QPoint obscuringCellRef = *it1; KSpreadCell *obscuringCell = m_pTable->cellAt( obscuringCellRef.x(), obscuringCellRef.y() ); if( obscuringCell != 0 ) { double x = m_pTable->dblColumnPos( obscuringCellRef.x() ); double y = m_pTable->dblRowPos( obscuringCellRef.y() ); if( view != 0 ) { x -= view->canvasWidget()->xOffset(); y -= view->canvasWidget()->yOffset(); } KoPoint corner( x, y ); painter.save(); QPen rp( obscuringCell->effRightBorderPen( obscuringCellRef.x(), obscuringCellRef.y() ) ); QPen bp( obscuringCell->effBottomBorderPen( obscuringCellRef.x(), obscuringCellRef.y() ) ); QPen lp( obscuringCell->effLeftBorderPen( obscuringCellRef.x(), obscuringCellRef.y() ) ); QPen tp( obscuringCell->effTopBorderPen( obscuringCellRef.x(), obscuringCellRef.y() ) ); obscuringCell->paintCell( rect, painter, view, corner, obscuringCellRef, true, true, true, true, rp, bp, lp, tp ); painter.restore(); } } } } clearFlag( Flag_PaintingCell ); } /* 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 ( d->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 ( d->style == KSpreadCell::ST_Select ) { QApplication::style().drawComboButton( &_painter, _tx + 1, _ty + 1, w2 - 1, h2 - 1, defaultColorGroup, selected ); } */ void KSpreadCell::paintObscuredCells(const KoRect& rect, QPainter& painter, KSpreadView* view, const KoRect &cellRect, const QPoint &cellRef, bool paintBorderRight, bool paintBorderBottom, bool paintBorderLeft, bool paintBorderTop, QPen & rightPen, QPen & bottomPen, QPen & leftPen, QPen & topPen ) { // This cell is obscuring other ones? Then we redraw their // background and borders before we paint our content there. if ( extraXCells() || extraYCells() ) { double ypos = cellRect.y(); int maxY = extraYCells(); int maxX = extraXCells(); for( int y = 0; y <= maxY; ++y ) { double xpos = cellRect.x(); RowFormat* rl = m_pTable->rowFormat( cellRef.y() + y ); for( int x = 0; x <= maxX; ++ x ) { ColumnFormat * cl = m_pTable->columnFormat( cellRef.x() + x ); if ( y != 0 || x != 0 ) { KSpreadCell * cell = m_pTable->cellAt( cellRef.x() + x, cellRef.y() + y ); KoPoint corner( xpos, ypos ); cell->paintCell( rect, painter, view, corner, QPoint( cellRef.x() + x, cellRef.y() + y ), paintBorderRight, paintBorderBottom, paintBorderLeft, paintBorderTop, rightPen, bottomPen, leftPen, topPen ); } xpos += cl->dblWidth(); } ypos += rl->dblHeight(); } } } void KSpreadCell::paintBackground( QPainter& painter, const KoRect &cellRect, const QPoint &cellRef, bool selected, QColor &backgroundColor ) { QColorGroup defaultColorGroup = QApplication::palette().active(); QRect zoomedCellRect = table()->doc()->zoomRect( cellRect ); /* If this is not the KS_rowMax and/or KS_colMax, then we reduce width and/or height by one. This is due to the fact that the right/bottom most pixel is shared with the left/top most pixel of the following cell. Only in the case of KS_colMax/KS_rowMax we need to draw even this pixel, as there isn't a following cell to draw the background pixel. */ if( cellRef.x() != KS_colMax ) { zoomedCellRect.setWidth( zoomedCellRect.width() - 1 ); } if( cellRef.y() != KS_rowMax ) { zoomedCellRect.setHeight( zoomedCellRect.height() - 1 ); } // Determine the correct background color if( selected ) { painter.setBackgroundColor( defaultColorGroup.highlight() ); } else { QColor bg( backgroundColor ); 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 ); painter.fillRect( zoomedCellRect, bb ); return; } } // Erase the background of the cell. if ( !painter.device()->isExtDev() ) painter.eraseRect( zoomedCellRect ); // Draw a background brush QBrush bb; if ( d->hasExtra() && d->extra()->conditions && d->extra()->conditions->matchedStyle() && d->extra()->conditions->matchedStyle()->hasFeature( KSpreadStyle::SBackgroundBrush, true ) ) bb = d->extra()->conditions->matchedStyle()->backGroundBrush(); else bb = backGroundBrush( cellRef.x(), cellRef.y() ); if( bb.style() != Qt::NoBrush ) { painter.fillRect( zoomedCellRect, bb ); } backgroundColor = painter.backgroundColor(); } void KSpreadCell::paintDefaultBorders( QPainter& painter, const KoRect &rect, const KoRect &cellRect, const QPoint &cellRef, bool paintBorderRight, bool paintBorderBottom, bool paintBorderLeft, bool paintBorderTop, QPen const & rightPen, QPen const & bottomPen, QPen const & leftPen, QPen const & topPen ) { KSpreadDoc* doc = table()->doc(); KSpreadSheet::LayoutDirection sheetDir = m_pTable->layoutDirection(); /* 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. It's even responsible to paint the right and bottom, if it is the last cell on a print out*/ bool paintTop; bool paintLeft; bool paintBottom; bool paintRight; paintLeft = ( paintBorderLeft && leftPen.style() == Qt::NoPen && table()->getShowGrid() ); paintRight = ( paintBorderRight && rightPen.style() == Qt::NoPen && table()->getShowGrid() ); paintTop = ( paintBorderTop && topPen.style() == Qt::NoPen && table()->getShowGrid() ); paintBottom = ( paintBorderBottom && table()->getShowGrid() && bottomPen.style() == Qt::NoPen ); if (d->hasExtra()) { QValueList::const_iterator it = d->extra()->obscuringCells.begin(); QValueList::const_iterator end = d->extra()->obscuringCells.end(); for ( ; it != end; ++it ) { KSpreadCell *cell = *it; paintLeft = paintLeft && ( cell->column() == cellRef.x() ); paintTop = paintTop && ( cell->row() == cellRef.y() ); paintBottom = false; paintRight = false; } } /* should we do the left border? */ if ( paintLeft ) { int dt = 0; int db = 0; if ( cellRef.x() > 1 ) { QPen t = m_pTable->cellAt( cellRef.x() - 1, cellRef.y() )->effTopBorderPen( cellRef.x() - 1, cellRef.y() ); QPen b = m_pTable->cellAt( cellRef.x() - 1, cellRef.y() )->effBottomBorderPen( cellRef.x() - 1, cellRef.y() ); if ( t.style() != Qt::NoPen ) dt = ( t.width() + 1 )/2; if ( b.style() != Qt::NoPen ) db = ( t.width() / 2); } painter.setPen( QPen( table()->doc()->gridColor(), 1, Qt::SolidLine ) ); //If we are on paper printout, we limit the length of the lines //On paper, we always have full cells, on screen not if ( painter.device()->isExtDev() ) { painter.drawLine( doc->zoomItX( QMAX( rect.left(), cellRect.x() ) ), doc->zoomItY( QMAX( rect.top(), cellRect.y() + dt ) ), doc->zoomItX( QMIN( rect.right(), cellRect.x() ) ), doc->zoomItY( QMIN( rect.bottom(), cellRect.bottom() - db ) ) ); } else { if ( sheetDir == KSpreadSheet::RightToLeft ) painter.drawLine( doc->zoomItX( cellRect.right() ), doc->zoomItY( cellRect.y() + dt ), doc->zoomItX( cellRect.right() ), doc->zoomItY( cellRect.bottom() - db ) ); else painter.drawLine( doc->zoomItX( cellRect.x() ), doc->zoomItY( cellRect.y() + dt ), doc->zoomItX( cellRect.x() ), doc->zoomItY( cellRect.bottom() - db ) ); } } /* should we do the right border? */ if ( paintRight ) { int dt = 0; int db = 0; if ( cellRef.x() < KS_colMax ) { QPen t = m_pTable->cellAt( cellRef.x() + 1, cellRef.y() )->effTopBorderPen( cellRef.x() + 1, cellRef.y() ); QPen b = m_pTable->cellAt( cellRef.x() + 1, cellRef.y() )->effBottomBorderPen( cellRef.x() + 1, cellRef.y() ); if ( t.style() != Qt::NoPen ) dt = ( t.width() + 1 )/2; if ( b.style() != Qt::NoPen ) db = ( t.width() / 2); } painter.setPen( QPen( table()->doc()->gridColor(), 1, Qt::SolidLine ) ); //If we are on paper printout, we limit the length of the lines //On paper, we always have full cells, on screen not if ( painter.device()->isExtDev() ) { painter.drawLine( doc->zoomItX( QMAX( rect.left(), cellRect.right() ) ), doc->zoomItY( QMAX( rect.top(), cellRect.y() + dt ) ), doc->zoomItX( QMIN( rect.right(), cellRect.right() ) ), doc->zoomItY( QMIN( rect.bottom(), cellRect.bottom() - db ) ) ); } else { if ( sheetDir == KSpreadSheet::RightToLeft ) painter.drawLine( doc->zoomItX( cellRect.x() ), doc->zoomItY( cellRect.y() + dt ), doc->zoomItX( cellRect.x() ), doc->zoomItY( cellRect.bottom() - db ) ); else painter.drawLine( doc->zoomItX( cellRect.right() ), doc->zoomItY( cellRect.y() + dt ), doc->zoomItX( cellRect.right() ), doc->zoomItY( cellRect.bottom() - db ) ); } } /* should we do the top border? */ if ( paintTop ) { int dl = 0; int dr = 0; if ( cellRef.y() > 1 ) { QPen l = m_pTable->cellAt( cellRef.x(), cellRef.y() - 1 )->effLeftBorderPen( cellRef.x(), cellRef.y() - 1 ); QPen r = m_pTable->cellAt( cellRef.x(), cellRef.y() - 1 )->effRightBorderPen( cellRef.x(), cellRef.y() - 1 ); if ( l.style() != Qt::NoPen ) dl = ( l.width() - 1 ) / 2 + 1; if ( r.style() != Qt::NoPen ) dr = r.width() / 2; } painter.setPen( QPen( table()->doc()->gridColor(), 1, Qt::SolidLine ) ); //If we are on paper printout, we limit the length of the lines //On paper, we always have full cells, on screen not if ( painter.device()->isExtDev() ) { painter.drawLine( doc->zoomItX( QMAX( rect.left(), cellRect.x() + dl ) ), doc->zoomItY( QMAX( rect.top(), cellRect.y() ) ), doc->zoomItX( QMIN( rect.right(), cellRect.right() - dr ) ), doc->zoomItY( QMIN( rect.bottom(), cellRect.y() ) ) ); } else { painter.drawLine( doc->zoomItX( cellRect.x() + dl ), doc->zoomItY( cellRect.y() ), doc->zoomItX( cellRect.right() - dr ), doc->zoomItY( cellRect.y() ) ); } } /* should we do the bottom border? */ if ( paintBottom ) { int dl = 0; int dr = 0; if ( cellRef.y() < KS_rowMax ) { QPen l = m_pTable->cellAt( cellRef.x(), cellRef.y() + 1 )->effLeftBorderPen( cellRef.x(), cellRef.y() + 1 ); QPen r = m_pTable->cellAt( cellRef.x(), cellRef.y() + 1 )->effRightBorderPen( cellRef.x(), cellRef.y() + 1 ); if ( l.style() != Qt::NoPen ) dl = ( l.width() - 1 ) / 2 + 1; if ( r.style() != Qt::NoPen ) dr = r.width() / 2; } painter.setPen( QPen( table()->doc()->gridColor(), 1, Qt::SolidLine ) ); //If we are on paper printout, we limit the length of the lines //On paper, we always have full cells, on screen not if ( painter.device()->isExtDev() ) { painter.drawLine( doc->zoomItX( QMAX( rect.left(), cellRect.x() + dl ) ), doc->zoomItY( QMAX( rect.top(), cellRect.bottom() ) ), doc->zoomItX( QMIN( rect.right(), cellRect.right() - dr ) ), doc->zoomItY( QMIN( rect.bottom(), cellRect.bottom() ) ) ); } else { painter.drawLine( doc->zoomItX( cellRect.x() + dl ), doc->zoomItY( cellRect.bottom() ), doc->zoomItX( cellRect.right() - dr ), doc->zoomItY( cellRect.bottom() ) ); } } } void KSpreadCell::paintCommentIndicator( QPainter& painter, const KoRect &cellRect, const QPoint &/*cellRef*/, QColor &backgroundColor ) { KSpreadDoc * doc = table()->doc(); // Point the little corner if there is a comment attached // to this cell. if ( ( m_mask & (uint) PComment ) && cellRect.width() > 10.0 && cellRect.height() > 10.0 && ( table()->print()->printCommentIndicator() || ( !painter.device()->isExtDev() && doc->getShowCommentIndicator() ) ) ) { QColor penColor = Qt::red; //If background has high red part, switch to blue if ( qRed( backgroundColor.rgb() ) > 127 && qGreen( backgroundColor.rgb() ) < 80 && qBlue( backgroundColor.rgb() ) < 80 ) { penColor = Qt::blue; } QPointArray point( 3 ); if ( m_pTable->layoutDirection()==KSpreadSheet::RightToLeft ) { point.setPoint( 0, doc->zoomItX( cellRect.x() + 6.0 ), doc->zoomItY( cellRect.y() ) ); point.setPoint( 1, doc->zoomItX( cellRect.x() ), doc->zoomItY( cellRect.y() ) ); point.setPoint( 2, doc->zoomItX( cellRect.x() ), doc->zoomItY( cellRect.y() + 6.0 ) ); } else { point.setPoint( 0, doc->zoomItX( cellRect.right() - 5.0 ), doc->zoomItY( cellRect.y() ) ); point.setPoint( 1, doc->zoomItX( cellRect.right() ), doc->zoomItY( cellRect.y() ) ); point.setPoint( 2, doc->zoomItX( cellRect.right() ), doc->zoomItY( cellRect.y() + 5.0 ) ); } painter.setBrush( QBrush( penColor ) ); painter.setPen( Qt::NoPen ); painter.drawPolygon( point ); } } // small blue rectangle if this cell holds a formula void KSpreadCell::paintFormulaIndicator( QPainter& painter, const KoRect &cellRect, QColor &backgroundColor ) { if( isFormula() && m_pTable->getShowFormulaIndicator() && cellRect.width() > 10.0 && cellRect.height() > 10.0 ) { KSpreadDoc* doc = table()->doc(); QColor penColor = Qt::blue; //If background has high blue part, switch to red if( qRed( backgroundColor.rgb() ) < 80 && qGreen( backgroundColor.rgb() ) < 80 && qBlue( backgroundColor.rgb() ) > 127 ) { penColor = Qt::red; } QPointArray point( 3 ); if ( m_pTable->layoutDirection()==KSpreadSheet::RightToLeft ) { point.setPoint( 0, doc->zoomItX( cellRect.right() - 6.0 ), doc->zoomItY( cellRect.bottom() ) ); point.setPoint( 1, doc->zoomItX( cellRect.right() ), doc->zoomItY( cellRect.bottom() ) ); point.setPoint( 2, doc->zoomItX( cellRect.right() ), doc->zoomItY( cellRect.bottom() - 6.0 ) ); } else { point.setPoint( 0, doc->zoomItX( cellRect.x() ), doc->zoomItY( cellRect.bottom() - 6.0 ) ); point.setPoint( 1, doc->zoomItX( cellRect.x() ), doc->zoomItY( cellRect.bottom() ) ); point.setPoint( 2, doc->zoomItX( cellRect.x() + 6.0 ), doc->zoomItY( cellRect.bottom() ) ); } painter.setBrush( QBrush( penColor ) ); painter.setPen( Qt::NoPen ); painter.drawPolygon( point ); } } void KSpreadCell::paintMoreTextIndicator( QPainter& painter, const KoRect &cellRect, QColor &backgroundColor ) { //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_CellTooShortX ) && !painter.device()->isExtDev() && cellRect.height() > 4.0 && cellRect.width() > 4.0 ) { KSpreadDoc* doc = table()->doc(); QColor penColor = Qt::red; //If background has high red part, switch to blue if( qRed( backgroundColor.rgb() ) > 127 && qGreen( backgroundColor.rgb() ) < 80 && qBlue( backgroundColor.rgb() ) < 80 ) { penColor = Qt::blue; } QPointArray point( 3 ); if ( d->strOutText.isRightToLeft() ) { point.setPoint( 0, doc->zoomItX( cellRect.left() + 4.0 ), doc->zoomItY( cellRect.y() + cellRect.height() / 2.0 -4.0 ) ); point.setPoint( 1, doc->zoomItX( cellRect.left() ), doc->zoomItY( cellRect.y() + cellRect.height() / 2.0 )); point.setPoint( 2, doc->zoomItX( cellRect.left() + 4.0 ), doc->zoomItY( cellRect.y() + cellRect.height() / 2.0 +4.0 ) ); } else { point.setPoint( 0, doc->zoomItX( cellRect.right() - 4.0 ), doc->zoomItY( cellRect.y() + cellRect.height() / 2.0 - 4.0 ) ); point.setPoint( 1, doc->zoomItX( cellRect.right() ), doc->zoomItY( cellRect.y() + cellRect.height() / 2.0 ) ); point.setPoint( 2, doc->zoomItX( cellRect.right() - 4.0 ), doc->zoomItY( cellRect.y() + cellRect.height() / 2.0 + 4.0 ) ); } painter.setBrush( QBrush( penColor ) ); painter.setPen( Qt::NoPen ); painter.drawPolygon( point ); } } void KSpreadCell::paintText( QPainter& painter, const KoRect &cellRect, const QPoint &cellRef ) { KSpreadDoc* doc = table()->doc(); ColumnFormat* colFormat = m_pTable->columnFormat( cellRef.x() ); QColorGroup defaultColorGroup = QApplication::palette().active(); QColor textColorPrint = effTextColor( cellRef.x(), cellRef.y() ); // Resolve the text color if invalid (=default) if ( !textColorPrint.isValid() ) { if ( painter.device()->isExtDev() ) textColorPrint = Qt::black; else textColorPrint = QApplication::palette().active().text(); } QPen tmpPen( textColorPrint ); //Set the font according to condition applyZoomedFont( painter, cellRef.x(), cellRef.y() ); //Check for red font color for negative values if ( !d->hasExtra() || !d->extra()->conditions || !d->extra()->conditions->matchedStyle() ) { if ( value().isNumber() && !( m_pTable->getShowFormula() && !( m_pTable->isProtected() && isHideFormula( d->column, d->row ) ) ) ) { double v = value().asFloat() * factor( column(),row() ); if ( floatColor( cellRef.x(), cellRef.y()) == KSpreadCell::NegRed && v < 0.0 ) tmpPen.setColor( Qt::red ); } } /**** For now I am commenting this out -- with the default color display you can read normal text through a highlighted background. Maybe this isn't always the case, though, and we can put the highlighted text color back in. In that case, we need to somewhere in here figure out if the text overlaps another cell outside of the selection, otherwise that portion of the text will be printed white on white. So just that portion would need to be painted again in the normal color. This should probably be done eventually, anyway, because I like using the reverse text color for highlighted cells. I just don't like extending the cell 'highlight' background outside of the selection rectangle because it looks REALLY ugly. if ( selected && ( cellRef.x() != marker.x() || cellRef.y() != marker.y() ) ) { QPen p( tmpPen ); p.setColor( defaultColorGroup.highlightedText() ); painter.setPen( p ); } else { painter.setPen(tmpPen); } */ painter.setPen( tmpPen ); QString tmpText = d->strOutText; double tmpHeight = d->textHeight; double tmpWidth = d->textWidth; if( testFlag( Flag_CellTooShortX ) ) { d->strOutText = textDisplaying( painter ); } //hide zero if ( m_pTable->getHideZero() && value().isNumber() && value().asFloat() * factor( column(), row() ) == 0 ) { d->strOutText = QString::null; } if ( colFormat->isHide() || ( cellRect.height() <= 2 ) ) { //clear extra cell if column or row is hidden freeAllObscuredCells(); /* TODO: This looks dangerous...must check when I have time */ d->strOutText = ""; } double indent = 0.0; double offsetCellTooShort = 0.0; int a = effAlignX(); //apply indent if text is align to left not when text is at right or middle if ( a == KSpreadCell::Left && !isEmpty() ) { if ( d->hasExtra() && d->extra()->conditions && d->extra()->conditions->matchedStyle() && d->extra()->conditions->matchedStyle()->hasFeature( KSpreadStyle::SIndent, true ) ) indent = d->extra()->conditions->matchedStyle()->indent(); else indent = getIndent( column(), row() ); } //made an offset, otherwise ### is under red triangle if ( a == KSpreadCell::Right && !isEmpty() && testFlag( Flag_CellTooShortX ) ) { offsetCellTooShort = m_pTable->doc()->unzoomItX( 4 ); } QFontMetrics fm2 = painter.fontMetrics(); double offsetFont = 0.0; if ( ( alignY( column(), row() ) == KSpreadCell::Bottom ) && textFontUnderline( column(), row() ) ) { offsetFont = m_pTable->doc()->unzoomItX( fm2.underlinePos() + 1 ); } int tmpAngle; bool tmpMultiRow; bool tmpVerticalText; if ( d->hasExtra() && d->extra()->conditions && d->extra()->conditions->matchedStyle() ) { if ( d->extra()->conditions->matchedStyle()->hasFeature( KSpreadStyle::SAngle, true ) ) tmpAngle = d->extra()->conditions->matchedStyle()->rotateAngle(); else tmpAngle = getAngle( cellRef.x(), cellRef.y() ); if ( d->extra()->conditions->matchedStyle()->hasFeature( KSpreadStyle::SVerticalText, true ) ) tmpVerticalText = d->extra()->conditions->matchedStyle()->hasProperty( KSpreadStyle::PVerticalText ); else tmpVerticalText = verticalText( cellRef.x(), cellRef.y() ); if ( d->extra()->conditions->matchedStyle()->hasFeature( KSpreadStyle::SMultiRow, true ) ) tmpMultiRow = d->extra()->conditions->matchedStyle()->hasProperty( KSpreadStyle::PMultiRow ); else tmpMultiRow = multiRow( cellRef.x(), cellRef.y() ); } else { tmpAngle = getAngle( cellRef.x(), cellRef.y() ); tmpVerticalText = verticalText( cellRef.x(), cellRef.y() ); tmpMultiRow = multiRow( cellRef.x(), cellRef.y() ); } if ( !tmpMultiRow && !tmpVerticalText && !tmpAngle ) { if( !d->hasExtra() || !d->extra()->QML ) painter.drawText( doc->zoomItX( indent + cellRect.x() + d->textX - offsetCellTooShort ), doc->zoomItY( cellRect.y() + d->textY - offsetFont ), d->strOutText ); else d->extra()->QML->draw( &painter, doc->zoomItX( indent + cellRect.x() + d->textX ), doc->zoomItY( cellRect.y() + d->textY - offsetFont ), QRegion( doc->zoomRect( KoRect( cellRect.x(), cellRect.y(), cellRect.width(), cellRect.height() ) ) ), QApplication::palette().active(), 0 ); } else if ( tmpAngle != 0 ) { int angle = tmpAngle; QFontMetrics fm = painter.fontMetrics(); painter.rotate( angle ); double x; if ( angle > 0 ) x = indent + cellRect.x() + d->textX; else x = indent + cellRect.x() + d->textX - ( fm.descent() + fm.ascent() ) * sin( angle * M_PI / 180 ); double y; if ( angle > 0 ) y = cellRect.y() + d->textY; else y = cellRect.y() + d->textY + d->textHeight; painter.drawText( doc->zoomItX( x * cos( angle * M_PI / 180 ) + y * sin( angle * M_PI / 180 ) ), doc->zoomItY( -x * sin( angle * M_PI / 180 ) + y * cos( angle * M_PI / 180 ) ), d->strOutText ); painter.rotate( -angle ); } else if ( tmpMultiRow && !tmpVerticalText ) { QString t; int i; int pos = 0; double dy = 0.0; QFontMetrics fm = painter.fontMetrics(); do { i = d->strOutText.find( "\n", pos ); if ( i == -1 ) t = d->strOutText.mid( pos, d->strOutText.length() - pos ); else { t = d->strOutText.mid( pos, i - pos ); pos = i + 1; } int a = effAlignX(); if ( m_pTable->getShowFormula() && !( m_pTable->isProtected() && isHideFormula( d->column, d->row ) ) ) a = KSpreadCell::Left; // #### Torben: This looks duplicated for me switch( a ) { case KSpreadCell::Left: d->textX = effLeftBorderPen( cellRef.x(), cellRef.y() ).width() + BORDER_SPACE; break; case KSpreadCell::Right: d->textX = cellRect.width() - BORDER_SPACE - doc->unzoomItX( fm.width( t ) ) - effRightBorderPen( cellRef.x(), cellRef.y() ).width(); break; case KSpreadCell::Center: d->textX = ( cellRect.width() - doc->unzoomItX( fm.width( t ) ) ) / 2; } painter.drawText( doc->zoomItX( indent + cellRect.x() + d->textX ), doc->zoomItY( cellRect.y() + d->textY + dy ), t ); dy += doc->unzoomItY( fm.descent() + fm.ascent() ); } while ( i != -1 ); } else if ( tmpVerticalText && !d->strOutText.isEmpty() ) { QString t; int i = 0; int len = 0; double dy = 0.0; QFontMetrics fm = painter.fontMetrics(); do { len = d->strOutText.length(); t = d->strOutText.at( i ); painter.drawText( doc->zoomItX( indent + cellRect.x() + d->textX ), doc->zoomItY( cellRect.y() + d->textY + dy ), t ); dy += doc->unzoomItY( fm.descent() + fm.ascent() ); i++; } while ( i != len ); } if ( testFlag( Flag_CellTooShortX ) ) { d->strOutText = tmpText; d->textHeight = tmpHeight; d->textWidth = tmpWidth; } if ( m_pTable->getHideZero() && value().isNumber() && value().asFloat() * factor( column(), row() ) == 0 ) { d->strOutText = tmpText; } if ( colFormat->isHide() || ( cellRect.height() <= 2 ) ) d->strOutText = tmpText; } void KSpreadCell::paintPageBorders( QPainter& painter, const KoRect &cellRect, const QPoint &cellRef, bool paintBorderRight, bool paintBorderBottom ) { if ( painter.device()->isExtDev() ) return; KSpreadSheetPrint* print = m_pTable->print(); KSpreadSheet::LayoutDirection sheetDir = m_pTable->layoutDirection(); // Draw page borders if( m_pTable->isShowPageBorders() ) { if( cellRef.x() >= print->printRange().left() && cellRef.x() <= print->printRange().right() + 1 && cellRef.y() >= print->printRange().top() && cellRef.y() <= print->printRange().bottom() + 1 ) { KSpreadDoc* doc = table()->doc(); if ( print->isOnNewPageX( cellRef.x() ) && ( cellRef.y() <= print->printRange().bottom() ) ) { painter.setPen( table()->doc()->pageBorderColor() ); if ( sheetDir == KSpreadSheet::RightToLeft ) painter.drawLine( doc->zoomItX( cellRect.right() ), doc->zoomItY( cellRect.y() ), doc->zoomItX( cellRect.right() ), doc->zoomItY( cellRect.bottom() ) ); else painter.drawLine( doc->zoomItX( cellRect.x() ), doc->zoomItY( cellRect.y() ), doc->zoomItX( cellRect.x() ), doc->zoomItY( cellRect.bottom() ) ); } if ( print->isOnNewPageY( cellRef.y() ) && ( cellRef.x() <= print->printRange().right() ) ) { painter.setPen( table()->doc()->pageBorderColor() ); painter.drawLine( doc->zoomItX( cellRect.x() ), doc->zoomItY( cellRect.y() ), doc->zoomItX( cellRect.right() ), doc->zoomItY( cellRect.y() ) ); } if( paintBorderRight ) { if ( print->isOnNewPageX( cellRef.x() + 1 ) && ( cellRef.y() <= print->printRange().bottom() ) ) { painter.setPen( table()->doc()->pageBorderColor() ); if ( sheetDir == KSpreadSheet::RightToLeft ) painter.drawLine( doc->zoomItX( cellRect.x() ), doc->zoomItY( cellRect.y() ), doc->zoomItX( cellRect.x() ), doc->zoomItY( cellRect.bottom() ) ); else painter.drawLine( doc->zoomItX( cellRect.right() ), doc->zoomItY( cellRect.y() ), doc->zoomItX( cellRect.right() ), doc->zoomItY( cellRect.bottom() ) ); } } if( paintBorderBottom ) { if ( print->isOnNewPageY( cellRef.y() + 1 ) && ( cellRef.x() <= print->printRange().right() ) ) { painter.setPen( table()->doc()->pageBorderColor() ); painter.drawLine( doc->zoomItX( cellRect.x() ), doc->zoomItY( cellRect.bottom() ), doc->zoomItX( cellRect.right() ), doc->zoomItY( cellRect.bottom() ) ); } } } } } void KSpreadCell::paintCellBorders( QPainter& painter, const KoRect& rect, const KoRect &cellRect, const QPoint &cellRef, bool paintRight, bool paintBottom, bool paintLeft, bool paintTop, QPen & rightPen, QPen & bottomPen, QPen & leftPen, QPen & topPen ) { KSpreadDoc * doc = table()->doc(); KSpreadSheet::LayoutDirection sheetDir = m_pTable->layoutDirection(); /* we might not paint some borders if this cell is merged with another in that direction bool paintLeft = paintBorderLeft; bool paintRight = paintBorderRight; bool paintTop = paintBorderTop; bool paintBottom = paintBorderBottom; */ // paintRight = paintRight && ( extraXCells() == 0 ); // paintBottom = paintBottom && ( d->extra()->extraYCells() == 0 ); if (d->hasExtra()) { QValueList::const_iterator it = d->extra()->obscuringCells.begin(); QValueList::const_iterator end = d->extra()->obscuringCells.end(); for ( ; it != end; ++it ) { KSpreadCell* cell = *it; int xDiff = cellRef.x() - cell->column(); int yDiff = cellRef.y() - cell->row(); paintLeft = paintLeft && xDiff == 0; paintTop = paintTop && yDiff == 0; paintRight = paintRight && cell->extraXCells() == xDiff; paintBottom = paintBottom && cell->extraYCells() == yDiff; } } // // Determine the pens that should be used for drawing // the borders. // int left_penWidth = QMAX( 1, doc->zoomItX( leftPen.width() ) ); int right_penWidth = QMAX( 1, doc->zoomItX( rightPen.width() ) ); int top_penWidth = QMAX( 1, doc->zoomItY( topPen.width() ) ); int bottom_penWidth = QMAX( 1, doc->zoomItY( bottomPen.width() ) ); leftPen.setWidth( left_penWidth ); rightPen.setWidth( right_penWidth ); topPen.setWidth( top_penWidth ); bottomPen.setWidth( bottom_penWidth ); if ( paintLeft && leftPen.style() != Qt::NoPen ) { int top = ( QMAX( 0, -1 + top_penWidth ) ) / 2 + ( ( QMAX( 0, -1 + top_penWidth ) ) % 2 ); int bottom = ( QMAX( 0, -1 + bottom_penWidth ) ) / 2 + 1; painter.setPen( leftPen ); //If we are on paper printout, we limit the length of the lines //On paper, we always have full cells, on screen not if ( painter.device()->isExtDev() ) { painter.drawLine( QMAX( doc->zoomItX( rect.left() ), doc->zoomItX( cellRect.x() ) ), QMAX( doc->zoomItY( rect.top() ), doc->zoomItY( cellRect.y() ) - top ), QMIN( doc->zoomItX( rect.right() ), doc->zoomItX( cellRect.x() ) ), QMIN( doc->zoomItY( rect.bottom() ), doc->zoomItY( cellRect.bottom() ) + bottom ) ); } else { if ( sheetDir == KSpreadSheet::RightToLeft ) painter.drawLine( doc->zoomItX( cellRect.right() ), doc->zoomItY( cellRect.y() ) - top, doc->zoomItX( cellRect.right() ), doc->zoomItY( cellRect.bottom() ) + bottom ); else painter.drawLine( doc->zoomItX( cellRect.x() ), doc->zoomItY( cellRect.y() ) - top, doc->zoomItX( cellRect.x() ), doc->zoomItY( cellRect.bottom() ) + bottom ); } } if ( paintRight && rightPen.style() != Qt::NoPen ) { int top = ( QMAX( 0, -1 + top_penWidth ) ) / 2 + ( ( QMAX( 0, -1 + top_penWidth ) ) % 2 ); int bottom = ( QMAX( 0, -1 + bottom_penWidth ) ) / 2 + 1; painter.setPen( rightPen ); //If we are on paper printout, we limit the length of the lines //On paper, we always have full cells, on screen not if ( painter.device()->isExtDev() ) { painter.drawLine( QMAX( doc->zoomItX( rect.left() ), doc->zoomItX( cellRect.right() ) ), QMAX( doc->zoomItY( rect.top() ), doc->zoomItY( cellRect.y() ) - top ), QMIN( doc->zoomItX( rect.right() ), doc->zoomItX( cellRect.right() ) ), QMIN( doc->zoomItY( rect.bottom() ), doc->zoomItY( cellRect.bottom() ) + bottom ) ); } else { double r = cellRect.right(); if ( sheetDir == KSpreadSheet::RightToLeft ) painter.drawLine( doc->zoomItX( cellRect.x() ), doc->zoomItY( cellRect.y() ) - top, doc->zoomItX( cellRect.x() ), doc->zoomItY( cellRect.bottom() ) + bottom ); else painter.drawLine( doc->zoomItX( r ), doc->zoomItY( cellRect.y() ) - top, doc->zoomItX( r ), doc->zoomItY( cellRect.bottom() ) + bottom ); } } if ( paintTop && topPen.style() != Qt::NoPen ) { painter.setPen( topPen ); //If we are on paper printout, we limit the length of the lines //On paper, we always have full cells, on screen not if ( painter.device()->isExtDev() ) { painter.drawLine( doc->zoomItX( QMAX( rect.left(), cellRect.x() ) ), doc->zoomItY( QMAX( rect.top(), cellRect.y() ) ), doc->zoomItX( QMIN( rect.right(), cellRect.right() ) ), doc->zoomItY( QMIN( rect.bottom(), cellRect.y() ) ) ); } else { painter.drawLine( doc->zoomItX( cellRect.x() ), doc->zoomItY( cellRect.y() ), doc->zoomItX( cellRect.right() ), doc->zoomItY( cellRect.y() ) ); } } if ( paintBottom && bottomPen.style() != Qt::NoPen ) { painter.setPen( bottomPen ); //If we are on paper printout, we limit the length of the lines //On paper, we always have full cells, on screen not if ( painter.device()->isExtDev() ) { painter.drawLine( doc->zoomItX( QMAX( rect.left(), cellRect.x() ) ), doc->zoomItY( QMAX( rect.top(), cellRect.bottom() ) ), doc->zoomItX( QMIN( rect.right(), cellRect.right() ) ), doc->zoomItY( QMIN( rect.bottom(), cellRect.bottom() ) ) ); } else { painter.drawLine( doc->zoomItX( cellRect.x() ), doc->zoomItY( cellRect.bottom() ), doc->zoomItX( cellRect.right() ), doc->zoomItY( cellRect.bottom() ) ); } } // // 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. // QPen vert_pen, horz_pen; int vert_penWidth, horz_penWidth; // Fix the borders which meet at the top left corner if ( m_pTable->cellAt( cellRef.x(), cellRef.y() - 1 )->effLeftBorderValue( cellRef.x(), cellRef.y() - 1 ) >= m_pTable->cellAt( cellRef.x() - 1, cellRef.y() - 1 )->effRightBorderValue( cellRef.x() - 1, cellRef.y() - 1 ) ) vert_pen = m_pTable->cellAt( cellRef.x(), cellRef.y() - 1 )->effLeftBorderPen( cellRef.x(), cellRef.y() - 1 ); else vert_pen = m_pTable->cellAt( cellRef.x() - 1, cellRef.y() - 1 )->effRightBorderPen( cellRef.x() - 1, cellRef.y() - 1 ); vert_penWidth = QMAX( 1, doc->zoomItX( vert_pen.width() ) ); vert_pen.setWidth( vert_penWidth ); if ( vert_pen.style() != Qt::NoPen ) { if ( m_pTable->cellAt( cellRef.x() - 1, cellRef.y() )->effTopBorderValue( cellRef.x() - 1, cellRef.y() ) >= m_pTable->cellAt( cellRef.x() - 1, cellRef.y() - 1 )->effBottomBorderValue( cellRef.x() - 1, cellRef.y() - 1 ) ) horz_pen = m_pTable->cellAt( cellRef.x() - 1, cellRef.y() )->effTopBorderPen( cellRef.x() - 1, cellRef.y() ); else horz_pen = m_pTable->cellAt( cellRef.x() - 1, cellRef.y() - 1 )->effBottomBorderPen( cellRef.x() - 1, cellRef.y() - 1 ); horz_penWidth = QMAX( 1, doc->zoomItY( horz_pen.width() ) ); int bottom = ( QMAX( 0, -1 + horz_penWidth ) ) / 2 + 1; painter.setPen( vert_pen ); //If we are on paper printout, we limit the length of the lines //On paper, we always have full cells, on screen not if ( painter.device()->isExtDev() ) { painter.drawLine( QMAX( doc->zoomItX( rect.left() ), doc->zoomItX( cellRect.x() ) ), QMAX( doc->zoomItY( rect.top() ), doc->zoomItY( cellRect.y() ) ), QMIN( doc->zoomItX( rect.right() ), doc->zoomItX( cellRect.x() ) ), QMIN( doc->zoomItY( rect.bottom() ), doc->zoomItY( cellRect.y() ) + bottom ) ); } else { if ( sheetDir == KSpreadSheet::RightToLeft ) painter.drawLine( doc->zoomItX( cellRect.right() ), doc->zoomItY( cellRect.y() ), doc->zoomItX( cellRect.right() ), doc->zoomItY( cellRect.y() ) + bottom ); else painter.drawLine( doc->zoomItX( cellRect.x() ), doc->zoomItY( cellRect.y() ), doc->zoomItX( cellRect.x() ), doc->zoomItY( cellRect.y() ) + bottom ); } } // Fix the borders which meet at the top right corner if ( m_pTable->cellAt( cellRef.x(), cellRef.y() - 1 )->effRightBorderValue( cellRef.x(), cellRef.y() - 1 ) >= m_pTable->cellAt( cellRef.x() + 1, cellRef.y() - 1 )->effLeftBorderValue( cellRef.x() + 1, cellRef.y() - 1 ) ) vert_pen = m_pTable->cellAt( cellRef.x(), cellRef.y() - 1 )->effRightBorderPen( cellRef.x(), cellRef.y() - 1 ); else vert_pen = m_pTable->cellAt( cellRef.x() + 1, cellRef.y() - 1 )->effLeftBorderPen( cellRef.x() + 1, cellRef.y() - 1 ); // vert_pen = effRightBorderPen( cellRef.x(), cellRef.y() - 1 ); vert_penWidth = QMAX( 1, doc->zoomItX( vert_pen.width() ) ); vert_pen.setWidth( vert_penWidth ); if ( ( vert_pen.style() != Qt::NoPen ) && ( cellRef.x() < KS_colMax ) ) { if ( m_pTable->cellAt( cellRef.x() + 1, cellRef.y() )->effTopBorderValue( cellRef.x() + 1, cellRef.y() ) >= m_pTable->cellAt( cellRef.x() + 1, cellRef.y() - 1 )->effBottomBorderValue( cellRef.x() + 1, cellRef.y() - 1 ) ) horz_pen = m_pTable->cellAt( cellRef.x() + 1, cellRef.y() )->effTopBorderPen( cellRef.x() + 1, cellRef.y() ); else horz_pen = m_pTable->cellAt( cellRef.x() + 1, cellRef.y() - 1 )->effBottomBorderPen( cellRef.x() + 1, cellRef.y() - 1 ); // horz_pen = effTopBorderPen( cellRef.x() + 1, cellRef.y() ); horz_penWidth = QMAX( 1, doc->zoomItY( horz_pen.width() ) ); int bottom = ( QMAX( 0, -1 + horz_penWidth ) ) / 2 + 1; painter.setPen( vert_pen ); //If we are on paper printout, we limit the length of the lines //On paper, we always have full cells, on screen not if ( painter.device()->isExtDev() ) { painter.drawLine( QMAX( doc->zoomItX( rect.left() ), doc->zoomItX( cellRect.right() ) ), QMAX( doc->zoomItY( rect.top() ), doc->zoomItY( cellRect.y() ) ), QMIN( doc->zoomItX( rect.right() ), doc->zoomItX( cellRect.right() ) ), QMIN( doc->zoomItY( rect.bottom() ), doc->zoomItY( cellRect.y() ) + bottom ) ); } else { if ( sheetDir == KSpreadSheet::RightToLeft ) painter.drawLine( doc->zoomItX( cellRect.x() ), doc->zoomItY( cellRect.y() ), doc->zoomItX( cellRect.x() ), doc->zoomItY( cellRect.y() ) + bottom ); else painter.drawLine( doc->zoomItX( cellRect.right() ), doc->zoomItY( cellRect.y() ), doc->zoomItX( cellRect.right() ), doc->zoomItY( cellRect.y() ) + bottom ); } } // Bottom if ( cellRef.y() < KS_rowMax ) { // Fix the borders which meet at the bottom left corner if ( m_pTable->cellAt( cellRef.x(), cellRef.y() + 1 )->effLeftBorderValue( cellRef.x(), cellRef.y() + 1 ) >= m_pTable->cellAt( cellRef.x() - 1, cellRef.y() + 1 )->effRightBorderValue( cellRef.x() - 1, cellRef.y() + 1 ) ) vert_pen = m_pTable->cellAt( cellRef.x(), cellRef.y() + 1 )->effLeftBorderPen( cellRef.x(), cellRef.y() + 1 ); else vert_pen = m_pTable->cellAt( cellRef.x() - 1, cellRef.y() + 1 )->effRightBorderPen( cellRef.x() - 1, cellRef.y() + 1 ); // vert_pen = effLeftBorderPen( cellRef.x(), cellRef.y() + 1 ); vert_penWidth = QMAX( 1, doc->zoomItY( vert_pen.width() ) ); vert_pen.setWidth( vert_penWidth ); if ( vert_pen.style() != Qt::NoPen ) { if ( m_pTable->cellAt( cellRef.x() - 1, cellRef.y() )->effBottomBorderValue( cellRef.x() - 1, cellRef.y() ) >= m_pTable->cellAt( cellRef.x() - 1, cellRef.y() + 1 )->effTopBorderValue( cellRef.x() - 1, cellRef.y() + 1 ) ) horz_pen = m_pTable->cellAt( cellRef.x() - 1, cellRef.y() )->effBottomBorderPen( cellRef.x() - 1, cellRef.y() ); else horz_pen = m_pTable->cellAt( cellRef.x() - 1, cellRef.y() + 1 )->effTopBorderPen( cellRef.x() - 1, cellRef.y() + 1 ); // horz_pen = effBottomBorderPen( cellRef.x() - 1, cellRef.y() ); horz_penWidth = QMAX( 1, doc->zoomItX( horz_pen.width() ) ); int bottom = ( QMAX( 0, -1 + horz_penWidth ) ) / 2; painter.setPen( vert_pen ); //If we are on paper printout, we limit the length of the lines //On paper, we always have full cells, on screen not if ( painter.device()->isExtDev() ) { painter.drawLine( QMAX( doc->zoomItX( rect.left() ), doc->zoomItX( cellRect.x() ) ), QMAX( doc->zoomItY( rect.top() ), doc->zoomItY( cellRect.bottom() ) - bottom ), QMIN( doc->zoomItX( rect.right() ), doc->zoomItX( cellRect.x() ) ), QMIN( doc->zoomItY( rect.bottom() ), doc->zoomItY( cellRect.bottom() ) ) ); } else { if ( sheetDir == KSpreadSheet::RightToLeft ) painter.drawLine( doc->zoomItX( cellRect.right() ), doc->zoomItY( cellRect.bottom() ) - bottom, doc->zoomItX( cellRect.right() ), doc->zoomItY( cellRect.bottom() ) ); else painter.drawLine( doc->zoomItX( cellRect.x() ), doc->zoomItY( cellRect.bottom() ) - bottom, doc->zoomItX( cellRect.x() ), doc->zoomItY( cellRect.bottom() ) ); } } // Fix the borders which meet at the bottom right corner if ( m_pTable->cellAt( cellRef.x(), cellRef.y() + 1 )->effRightBorderValue( cellRef.x(), cellRef.y() + 1 ) >= m_pTable->cellAt( cellRef.x() + 1, cellRef.y() + 1 )->effLeftBorderValue( cellRef.x() + 1, cellRef.y() + 1 ) ) vert_pen = m_pTable->cellAt( cellRef.x(), cellRef.y() + 1 )->effRightBorderPen( cellRef.x(), cellRef.y() + 1 ); else vert_pen = m_pTable->cellAt( cellRef.x() + 1, cellRef.y() + 1 )->effLeftBorderPen( cellRef.x() + 1, cellRef.y() + 1 ); // vert_pen = effRightBorderPen( cellRef.x(), cellRef.y() + 1 ); vert_penWidth = QMAX( 1, doc->zoomItY( vert_pen.width() ) ); vert_pen.setWidth( vert_penWidth ); if ( ( vert_pen.style() != Qt::NoPen ) && ( cellRef.x() < KS_colMax ) ) { if ( m_pTable->cellAt( cellRef.x() + 1, cellRef.y() )->effBottomBorderValue( cellRef.x() + 1, cellRef.y() ) >= m_pTable->cellAt( cellRef.x() + 1, cellRef.y() + 1 )->effTopBorderValue( cellRef.x() + 1, cellRef.y() + 1 ) ) horz_pen = m_pTable->cellAt( cellRef.x() + 1, cellRef.y() )->effBottomBorderPen( cellRef.x() + 1, cellRef.y() ); else horz_pen = m_pTable->cellAt( cellRef.x() + 1, cellRef.y() + 1 )->effTopBorderPen( cellRef.x() + 1, cellRef.y() + 1 ); // horz_pen = effBottomBorderPen( cellRef.x() + 1, cellRef.y() ); horz_penWidth = QMAX( 1, doc->zoomItX( horz_pen.width() ) ); int bottom = ( QMAX( 0, -1 + horz_penWidth ) ) / 2; painter.setPen( vert_pen ); //If we are on paper printout, we limit the length of the lines //On paper, we always have full cells, on screen not if ( painter.device()->isExtDev() ) { painter.drawLine( QMAX( doc->zoomItX( rect.left() ), doc->zoomItX( cellRect.right() ) ), QMAX( doc->zoomItY( rect.top() ), doc->zoomItY( cellRect.bottom() ) - bottom ), QMIN( doc->zoomItX( rect.right() ), doc->zoomItX( cellRect.right() ) ), QMIN( doc->zoomItY( rect.bottom() ),doc->zoomItY( cellRect.bottom() ) ) ); } else { if ( sheetDir == KSpreadSheet::RightToLeft ) painter.drawLine( doc->zoomItX( cellRect.x() ), doc->zoomItY( cellRect.bottom() ) - bottom, doc->zoomItX( cellRect.x() ), doc->zoomItY( cellRect.bottom() ) ); else painter.drawLine( doc->zoomItX( cellRect.right() ), doc->zoomItY( cellRect.bottom() ) - bottom, doc->zoomItX( cellRect.right() ), doc->zoomItY( cellRect.bottom() ) ); } } } } void KSpreadCell::paintCellDiagonalLines( QPainter& painter, const KoRect &cellRect, const QPoint &cellRef ) { if ( !isObscuringForced() ) { if ( effFallDiagonalPen( cellRef.x(), cellRef.y() ).style() != Qt::NoPen ) { KSpreadDoc* doc = table()->doc(); painter.setPen( effFallDiagonalPen( cellRef.x(), cellRef.y() ) ); painter.drawLine( doc->zoomItX( cellRect.x() ), doc->zoomItY( cellRect.y() ), doc->zoomItX( cellRect.right() ), doc->zoomItY( cellRect.bottom() ) ); } if ( effGoUpDiagonalPen( cellRef.x(), cellRef.y() ).style() != Qt::NoPen ) { KSpreadDoc* doc = table()->doc(); painter.setPen( effGoUpDiagonalPen( cellRef.x(), cellRef.y() ) ); painter.drawLine( doc->zoomItX( cellRect.x() ), doc->zoomItY( cellRect.bottom() ), doc->zoomItX( cellRect.right() ), doc->zoomItY( cellRect.y() ) ); } } } int KSpreadCell::defineAlignX() { int a = align( column(), row() ); if ( a == KSpreadCell::Undefined ) { if ( value().isBoolean() || value().isNumber() || (value().isString() && value().asString()[0].direction() == QChar::DirR )) a = KSpreadCell::Right; else a = KSpreadCell::Left; } return a; } int KSpreadCell::effAlignX() { if ( d->hasExtra() && d->extra()->conditions && d->extra()->conditions->matchedStyle() && d->extra()->conditions->matchedStyle()->hasFeature( KSpreadStyle::SAlignX, true ) ) return d->extra()->conditions->matchedStyle()->alignX(); return defineAlignX(); } //used in paintText //cuts strOutText, so that it only holds the part that can be displayed QString KSpreadCell::textDisplaying( QPainter &_painter ) { QFontMetrics fm = _painter.fontMetrics(); int a = align( column(), row() ); if (( a == KSpreadCell::Left || a == KSpreadCell::Undefined) && !value().isNumber() && !verticalText( column(),row() )) { //not enough space but align to left double len = 0.0; int extraXCells = d->hasExtra() ? d->extra()->extraXCells : 0; for ( int i = column(); i <= column() + extraXCells; i++ ) { ColumnFormat *cl2 = m_pTable->columnFormat( i ); len += cl2->dblWidth() - 1.0; //-1.0 because the pixel in between 2 cells is shared between both cells } QString tmp; double tmpIndent = 0.0; if( !isEmpty() ) tmpIndent = getIndent( column(), row() ); for( int i = d->strOutText.length(); i != 0; i-- ) { tmp = d->strOutText.left(i); if( m_pTable->doc()->unzoomItX( fm.width( tmp ) ) + tmpIndent < len - 4.0 - 1.0 ) //4 equal lenght of red triangle +1 point { if( getAngle( column(), row() ) != 0 ) { QString tmp2; RowFormat *rl = m_pTable->rowFormat( row() ); if( d->textHeight > rl->dblHeight() ) { for ( int j = d->strOutText.length(); j != 0; j-- ) { tmp2 = d->strOutText.left( j ); if( m_pTable->doc()->unzoomItY( fm.width( tmp2 ) ) < rl->dblHeight() - 1.0 ) { return d->strOutText.left( QMIN( tmp.length(), tmp2.length() ) ); } } } else return tmp; } else return tmp; } } return QString( "" ); } else if( verticalText( column(), row() ) ) { RowFormat *rl = m_pTable->rowFormat( row() ); double tmpIndent = 0.0; //not enough space but align to left double len = 0.0; int extraXCells = d->hasExtra() ? d->extra()->extraXCells : 0; for( int i = column(); i <= column() + extraXCells; i++ ) { ColumnFormat *cl2 = m_pTable->columnFormat( i ); len += cl2->dblWidth() - 1.0; //-1.0 because the pixel in between 2 cells is shared between both cells } if( !isEmpty() ) tmpIndent = getIndent( column(), row() ); if( ( d->textWidth + tmpIndent > len ) || d->textWidth == 0.0 ) return QString( "" ); for ( int i = d->strOutText.length(); i != 0; i-- ) { if( m_pTable->doc()->unzoomItY( fm.ascent() + fm.descent() ) * i < rl->dblHeight() - 1.0 ) { return d->strOutText.left( i ); } } return QString( "" ); } ColumnFormat *cl = m_pTable->columnFormat( column() ); double w = cl->dblWidth(); if ( d->hasExtra() && (d->extra()->extraWidth != 0.0) ) w = d->extra()->extraWidth; if( value().isNumber()) { if( formatType() != Scientific_format ) { int p = (precision(column(),row()) == -1) ? 8 : precision(column(),row()); double val =value().asFloat() * factor(column(),row()); int pos=0; QString localizedNumber= QString::number( (val), 'E', p); if((pos=localizedNumber.find('.'))!=-1) { localizedNumber = localizedNumber.replace( pos, 1, decimal_point ); } if( floatFormat( column(), row() ) == KSpreadCell::AlwaysSigned && val >= 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 ( m_pTable->doc()->unzoomItX( fm.width( localizedNumber ) ) < w && !( m_pTable->getShowFormula() && !( m_pTable->isProtected() && isHideFormula( d->column, d->row ) ) ) ) { return localizedNumber; } } /* What is this doing and is it broken with the new error handling? */ QString str( "####" ); int i; for( i=4; i != 0; i-- ) { if( m_pTable->doc()->unzoomItX( fm.width( str.right( i ) ) ) < w - 4.0 - 1.0 ) { break; } } return str.right( i );//QString("###"); } else { QString tmp; for ( int i = d->strOutText.length(); i != 0; i-- ) { tmp = d->strOutText.left( i ); if( m_pTable->doc()->unzoomItX( fm.width( tmp ) ) < w - 4.0 - 1.0 ) //4 equals lenght of red triangle +1 pixel { return tmp; } } } return QString::null; } double KSpreadCell::dblWidth( int _col, const KSpreadCanvas *_canvas ) const { if ( _col < 0 ) _col = d->column; if ( _canvas ) { if ( testFlag(Flag_ForceExtra) ) return d->extra()->extraWidth; const ColumnFormat *cl = m_pTable->columnFormat( _col ); return cl->dblWidth( _canvas ); } if ( testFlag(Flag_ForceExtra) ) return d->extra()->extraWidth; const ColumnFormat *cl = m_pTable->columnFormat( _col ); return cl->dblWidth(); } int KSpreadCell::width( int _col, const KSpreadCanvas *_canvas ) const { return int( dblWidth( _col, _canvas ) ); } double KSpreadCell::dblHeight( int _row, const KSpreadCanvas *_canvas ) const { if ( _row < 0 ) _row = d->row; if ( _canvas ) { if ( testFlag(Flag_ForceExtra) ) return d->extra()->extraHeight; const RowFormat *rl = m_pTable->rowFormat( _row ); return rl->dblHeight( _canvas ); } if ( testFlag(Flag_ForceExtra) ) return d->extra()->extraHeight; const RowFormat *rl = m_pTable->rowFormat( _row ); return rl->dblHeight(); } int KSpreadCell::height( int _row, const KSpreadCanvas *_canvas ) const { return int( dblHeight( _row, _canvas ) ); } /////////////////////////////////////////// // // Misc Properties. // Reimplementation of KSpreadFormat methods. // /////////////////////////////////////////// const QBrush& KSpreadCell::backGroundBrush( int _col, int _row ) const { if ( d->hasExtra() && (!d->extra()->obscuringCells.isEmpty()) ) { const KSpreadCell* cell = d->extra()->obscuringCells.first(); return cell->backGroundBrush( cell->column(), cell->row() ); } return KSpreadFormat::backGroundBrush( _col, _row ); } const QColor& KSpreadCell::bgColor( int _col, int _row ) const { if ( d->hasExtra() && (!d->extra()->obscuringCells.isEmpty()) ) { const KSpreadCell* cell = d->extra()->obscuringCells.first(); return cell->bgColor( cell->column(), cell->row() ); } return KSpreadFormat::bgColor( _col, _row ); } /////////////////////////////////////////// // // Borders. // Reimplementation of KSpreadFormat methods. // /////////////////////////////////////////// void KSpreadCell::setLeftBorderPen( const QPen& p ) { if ( column() == 1 ) { KSpreadCell* cell = m_pTable->cellAt( column() - 1, row() ); if ( cell && cell->hasProperty( PRightBorder ) && m_pTable->cellAt( column(), row() ) == this ) cell->clearProperty( PRightBorder ); } KSpreadFormat::setLeftBorderPen( p ); } void KSpreadCell::setTopBorderPen( const QPen& p ) { if ( row() == 1 ) { KSpreadCell* cell = m_pTable->cellAt( column(), row() - 1 ); if ( cell && cell->hasProperty( PBottomBorder ) && m_pTable->cellAt( column(), row() ) == this ) cell->clearProperty( PBottomBorder ); } KSpreadFormat::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 ) && m_pTable->cellAt( column(), row() ) == this ) cell->clearProperty( PLeftBorder ); KSpreadFormat::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 ) && m_pTable->cellAt( column(), row() ) == this ) cell->clearProperty( PTopBorder ); KSpreadFormat::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 && cell->hasProperty( PLeftBorder ) ) return cell->leftBorderPen( _col + 1, _row ); } return KSpreadFormat::rightBorderPen( _col, _row ); } const QPen& KSpreadCell::leftBorderPen( int _col, int _row ) const { if ( !hasProperty( PLeftBorder ) ) { const KSpreadCell * cell = m_pTable->cellAt( _col - 1, _row ); if ( cell && cell->hasProperty( PRightBorder ) ) return cell->rightBorderPen( _col - 1, _row ); } return KSpreadFormat::leftBorderPen( _col, _row ); } const QPen& KSpreadCell::bottomBorderPen( int _col, int _row ) const { if ( !hasProperty( PBottomBorder ) && ( _row < KS_rowMax ) ) { const KSpreadCell * cell = m_pTable->cellAt( _col, _row + 1 ); if ( cell && cell->hasProperty( PTopBorder ) ) return cell->topBorderPen( _col, _row + 1 ); } return KSpreadFormat::bottomBorderPen( _col, _row ); } const QPen& KSpreadCell::topBorderPen( int _col, int _row ) const { if ( !hasProperty( PTopBorder ) ) { const KSpreadCell * cell = m_pTable->cellAt( _col, _row - 1 ); if ( cell->hasProperty( PBottomBorder ) ) return cell->bottomBorderPen( _col, _row - 1 ); } return KSpreadFormat::topBorderPen( _col, _row ); } const QColor & KSpreadCell::effTextColor( int col, int row ) const { if ( d->hasExtra() && d->extra()->conditions && d->extra()->conditions->matchedStyle() && d->extra()->conditions->matchedStyle()->hasFeature( KSpreadStyle::STextPen, true ) ) return d->extra()->conditions->matchedStyle()->pen().color(); return textColor( col, row ); } const QPen& KSpreadCell::effLeftBorderPen( int col, int row ) const { if ( isObscuringForced() ) { KSpreadCell * cell = d->extra()->obscuringCells.first(); return cell->effLeftBorderPen( cell->column(), cell->row() ); } if ( d->hasExtra() && d->extra()->conditions && d->extra()->conditions->matchedStyle() && d->extra()->conditions->matchedStyle()->hasFeature( KSpreadStyle::SLeftBorder, true ) ) return d->extra()->conditions->matchedStyle()->leftBorderPen(); return KSpreadFormat::leftBorderPen( col, row ); } const QPen& KSpreadCell::effTopBorderPen( int col, int row ) const { if ( isObscuringForced() ) { KSpreadCell * cell = d->extra()->obscuringCells.first(); return cell->effTopBorderPen( cell->column(), cell->row() ); } if ( d->hasExtra() && d->extra()->conditions && d->extra()->conditions->matchedStyle() && d->extra()->conditions->matchedStyle()->hasFeature( KSpreadStyle::STopBorder, true ) ) return d->extra()->conditions->matchedStyle()->topBorderPen(); return KSpreadFormat::topBorderPen( col, row ); } const QPen& KSpreadCell::effRightBorderPen( int col, int row ) const { if ( isObscuringForced() ) { KSpreadCell * cell = d->extra()->obscuringCells.first(); return cell->effRightBorderPen( cell->column(), cell->row() ); } if ( d->hasExtra() && d->extra()->conditions && d->extra()->conditions->matchedStyle() && d->extra()->conditions->matchedStyle()->hasFeature( KSpreadStyle::SRightBorder, true ) ) return d->extra()->conditions->matchedStyle()->rightBorderPen(); return KSpreadFormat::rightBorderPen( col, row ); } const QPen& KSpreadCell::effBottomBorderPen( int col, int row ) const { if ( isObscuringForced() ) { KSpreadCell * cell = d->extra()->obscuringCells.first(); return cell->effBottomBorderPen( cell->column(), cell->row() ); } if ( d->hasExtra() && d->extra()->conditions && d->extra()->conditions->matchedStyle() && d->extra()->conditions->matchedStyle()->hasFeature( KSpreadStyle::SBottomBorder, true ) ) return d->extra()->conditions->matchedStyle()->bottomBorderPen(); return KSpreadFormat::bottomBorderPen( col, row ); } const QPen & KSpreadCell::effGoUpDiagonalPen( int col, int row ) const { if ( d->hasExtra() && d->extra()->conditions && d->extra()->conditions->matchedStyle() && d->extra()->conditions->matchedStyle()->hasFeature( KSpreadStyle::SGoUpDiagonal, true ) ) return d->extra()->conditions->matchedStyle()->goUpDiagonalPen(); return KSpreadFormat::goUpDiagonalPen( col, row ); } const QPen & KSpreadCell::effFallDiagonalPen( int col, int row ) const { if ( d->hasExtra() && d->extra()->conditions && d->extra()->conditions->matchedStyle() && d->extra()->conditions->matchedStyle()->hasFeature( KSpreadStyle::SFallDiagonal, true ) ) return d->extra()->conditions->matchedStyle()->fallDiagonalPen(); return KSpreadFormat::fallDiagonalPen( col, row ); } uint KSpreadCell::effBottomBorderValue( int col, int row ) const { if ( isObscuringForced() ) { KSpreadCell * cell = d->extra()->obscuringCells.first(); return cell->effBottomBorderValue( cell->column(), cell->row() ); } if ( d->hasExtra() && d->extra()->conditions && d->extra()->conditions->matchedStyle() ) return d->extra()->conditions->matchedStyle()->bottomPenValue(); return KSpreadFormat::bottomBorderValue( col, row ); } uint KSpreadCell::effRightBorderValue( int col, int row ) const { if ( isObscuringForced() ) { KSpreadCell * cell = d->extra()->obscuringCells.first(); return cell->effRightBorderValue( cell->column(), cell->row() ); } if ( d->hasExtra() && d->extra()->conditions && d->extra()->conditions->matchedStyle() ) return d->extra()->conditions->matchedStyle()->rightPenValue(); return KSpreadFormat::rightBorderValue( col, row ); } uint KSpreadCell::effLeftBorderValue( int col, int row ) const { if ( isObscuringForced() ) { KSpreadCell * cell = d->extra()->obscuringCells.first(); return cell->effLeftBorderValue( cell->column(), cell->row() ); } if ( d->hasExtra() && d->extra()->conditions && d->extra()->conditions->matchedStyle() ) return d->extra()->conditions->matchedStyle()->leftPenValue(); return KSpreadFormat::leftBorderValue( col, row ); } uint KSpreadCell::effTopBorderValue( int col, int row ) const { if ( isObscuringForced() ) { KSpreadCell * cell = d->extra()->obscuringCells.first(); return cell->effTopBorderValue( cell->column(), cell->row() ); } if ( d->hasExtra() && d->extra()->conditions && d->extra()->conditions->matchedStyle() ) return d->extra()->conditions->matchedStyle()->topPenValue(); return KSpreadFormat::topBorderValue( col, row ); } /////////////////////////////////////////// // // Precision // /////////////////////////////////////////// void KSpreadCell::incPrecision() { //TODO: This is ugly. Why not simply regenerate the text to display? Tomas if ( !value().isNumber() ) return; int tmpPreci = precision( column(), row() ); kdDebug(36001) << "incPrecision: tmpPreci = " << tmpPreci << endl; if ( tmpPreci == -1 ) { int pos = d->strOutText.find(decimal_point); if ( pos == -1 ) pos = d->strOutText.find('.'); if ( pos == -1 ) setPrecision(1); else { int start = 0; if ( d->strOutText.find('%') != -1 ) start = 2; else if ( d->strOutText.find(locale()->currencySymbol()) == ((int)(d->strOutText.length()-locale()->currencySymbol().length())) ) start = locale()->currencySymbol().length() + 1; else if ( (start=d->strOutText.find('E')) != -1 ) start = d->strOutText.length() - start; //kdDebug(36001) << "start=" << start << " pos=" << pos << " length=" << d->strOutText.length() << endl; setPrecision( QMAX( 0, (int)d->strOutText.length() - start - pos ) ); } } else if ( tmpPreci < 10 ) { setPrecision( ++tmpPreci ); } setFlag(Flag_LayoutDirty); } void KSpreadCell::decPrecision() { //TODO: This is ugly. Why not simply regenerate the text to display? Tomas if ( !value().isNumber() ) return; int preciTmp = precision( column(), row() ); // kdDebug(36001) << "decPrecision: tmpPreci = " << tmpPreci << endl; if ( precision(column(),row()) == -1 ) { int pos = d->strOutText.find( decimal_point ); int start = 0; if ( d->strOutText.find('%') != -1 ) start = 2; else if ( d->strOutText.find(locale()->currencySymbol()) == ((int)(d->strOutText.length()-locale()->currencySymbol().length())) ) start = locale()->currencySymbol().length() + 1; else if ( (start = d->strOutText.find('E')) != -1 ) start = d->strOutText.length() - start; else start = 0; if ( pos == -1 ) return; setPrecision(d->strOutText.length() - pos - 2 - start); // if ( preciTmp < 0 ) // setPrecision( preciTmp ); } else if ( preciTmp > 0 ) { setPrecision( --preciTmp ); } setFlag( Flag_LayoutDirty ); } //set numerical value //used in KSpreadSheet::setSeries (nowhere else yet) void KSpreadCell::setNumber( double number ) { setValue( KSpreadValue( number ) ); d->content = Text; d->strText.setNum( number ); checkNumberFormat(); } void KSpreadCell::setCellText( const QString& _text, bool asText ) { QString ctext = _text; if( ctext.length() > 5000 ) ctext = ctext.left( 5000 ); if ( asText ) { d->strOutText = ctext; d->strText = ctext; d->content = Text; setValue( KSpreadValue( ctext ) ); return; } QString oldText = d->strText; setDisplayText( ctext ); if(!m_pTable->isLoading() && !testValidity() ) { //reapply old value if action == stop setDisplayText( oldText ); } } void KSpreadCell::setDisplayText( const QString& _text ) { m_pTable->doc()->emitBeginOperation( false ); d->strText = _text; /** * A real formula "=A1+A2*3" was entered. */ if ( !d->strText.isEmpty() && d->strText[0] == '=' ) { setFlag(Flag_LayoutDirty); setFlag(Flag_TextFormatDirty); d->content = Formula; if ( !m_pTable->isLoading() ) { if ( !makeFormula() ) { kdError(36001) << "ERROR: Syntax ERROR" << endl; } } } /** * QML */ else if ( !d->strText.isEmpty() && d->strText[0] == '!' ) { d->extra()->QML = new QSimpleRichText( d->strText.mid(1), QApplication::font() );//, m_pTable->widget() ); setFlag(Flag_LayoutDirty); setFlag(Flag_TextFormatDirty); d->content = RichText; } /** * Some numeric value or a string. */ else { d->content = Text; // Find out what data type it is checkTextInput(); setFlag(Flag_LayoutDirty); setFlag(Flag_TextFormatDirty); } m_pTable->doc()->emitEndOperation( QRect( d->column, d->row, 1, 1 ) ); } void KSpreadCell::update() { /* those obscuring us need to redo their layout cause they can't obscure us now that we've got text. This includes cells obscuring cells that we are obscuring */ for (int x = d->column; x <= d->column + extraXCells(); x++) { for (int y = d->row; y <= d->row + extraYCells(); y++) { KSpreadCell* cell = m_pTable->cellAt(x,y); cell->setLayoutDirtyFlag(); } } setCalcDirtyFlag(); /* TODO - is this a good place for this? */ updateChart(true); } bool KSpreadCell::testValidity() const { bool valid = false; if( d->hasExtra() && d->extra()->validity && d->extra()->validity->m_allow != Allow_All ) { //fixme if ( d->extra()->validity->allowEmptyCell && d->strText.isEmpty() ) return true; if( value().isNumber() && (d->extra()->validity->m_allow == Allow_Number || (d->extra()->validity->m_allow == Allow_Integer && value().asFloat() == ceil(value().asFloat())))) { switch( d->extra()->validity->m_cond) { case Equal: valid = ( value().asFloat() - d->extra()->validity->valMin < DBL_EPSILON && value().asFloat() - d->extra()->validity->valMin > (0.0 - DBL_EPSILON)); break; case DifferentTo: valid = !( ( value().asFloat() - d->extra()->validity->valMin < DBL_EPSILON && value().asFloat() - d->extra()->validity->valMin > (0.0 - DBL_EPSILON)) ); break; case Superior: valid = ( value().asFloat() > d->extra()->validity->valMin); break; case Inferior: valid = ( value().asFloat() extra()->validity->valMin); break; case SuperiorEqual: valid = ( value().asFloat() >= d->extra()->validity->valMin); break; case InferiorEqual: valid = (value().asFloat() <= d->extra()->validity->valMin); break; case Between: valid = ( value().asFloat() >= d->extra()->validity->valMin && value().asFloat() <= d->extra()->validity->valMax); break; case Different: valid = (value().asFloat() < d->extra()->validity->valMin || value().asFloat() > d->extra()->validity->valMax); break; default : break; } } else if(d->extra()->validity->m_allow==Allow_Text) { valid = value().isString(); } else if(d->extra()->validity->m_allow==Allow_TextLength) { if( value().isString() ) { int len = d->strOutText.length(); switch( d->extra()->validity->m_cond) { case Equal: if (len == d->extra()->validity->valMin) valid = true; break; case DifferentTo: if (len != d->extra()->validity->valMin) valid = true; break; case Superior: if(len > d->extra()->validity->valMin) valid = true; break; case Inferior: if(len < d->extra()->validity->valMin) valid = true; break; case SuperiorEqual: if(len >= d->extra()->validity->valMin) valid = true; break; case InferiorEqual: if(len <= d->extra()->validity->valMin) valid = true; break; case Between: if(len >= d->extra()->validity->valMin && len <= d->extra()->validity->valMax) valid = true; break; case Different: if(len extra()->validity->valMin || len >d->extra()->validity->valMax) valid = true; break; default : break; } } } else if(d->extra()->validity->m_allow == Allow_Time && isTime()) { switch( d->extra()->validity->m_cond) { case Equal: valid = (value().asTime() == d->extra()->validity->timeMin); break; case DifferentTo: valid = (value().asTime() != d->extra()->validity->timeMin); break; case Superior: valid = (value().asTime() > d->extra()->validity->timeMin); break; case Inferior: valid = (value().asTime() < d->extra()->validity->timeMin); break; case SuperiorEqual: valid = (value().asTime() >= d->extra()->validity->timeMin); break; case InferiorEqual: valid = (value().asTime() <= d->extra()->validity->timeMin); break; case Between: valid = (value().asTime() >= d->extra()->validity->timeMin && value().asTime() <= d->extra()->validity->timeMax); break; case Different: valid = (value().asTime() < d->extra()->validity->timeMin || value().asTime() > d->extra()->validity->timeMax); break; default : break; } } else if(d->extra()->validity->m_allow == Allow_Date && isDate()) { switch( d->extra()->validity->m_cond) { case Equal: valid = (value().asDate() == d->extra()->validity->dateMin); break; case DifferentTo: valid = (value().asDate() != d->extra()->validity->dateMin); break; case Superior: valid = (value().asDate() > d->extra()->validity->dateMin); break; case Inferior: valid = (value().asDate() < d->extra()->validity->dateMin); break; case SuperiorEqual: valid = (value().asDate() >= d->extra()->validity->dateMin); break; case InferiorEqual: valid = (value().asDate() <= d->extra()->validity->dateMin); break; case Between: valid = (value().asDate() >= d->extra()->validity->dateMin && value().asDate() <= d->extra()->validity->dateMax); break; case Different: valid = (value().asDate() < d->extra()->validity->dateMin || value().asDate() > d->extra()->validity->dateMax); break; default : break; } } } else { valid= true; } if(!valid &&d->extra()->validity != NULL && d->extra()->validity->displayMessage) { switch (d->extra()->validity->m_action ) { case Stop: KMessageBox::error((QWidget*)0L, d->extra()->validity->message, d->extra()->validity->title); break; case Warning: KMessageBox::warningYesNo((QWidget*)0L, d->extra()->validity->message, d->extra()->validity->title); break; case Information: KMessageBox::information((QWidget*)0L, d->extra()->validity->message, d->extra()->validity->title); break; } } if (!d->hasExtra()) return true; //okay if there's no validity return (valid || d->extra()->validity == NULL || d->extra()->validity->m_action != Stop); } FormatType KSpreadCell::formatType() const { return getFormatType( d->column, d->row ); } double KSpreadCell::textWidth() const { return d->textWidth; } double KSpreadCell::textHeight() const { return d->textHeight; } int KSpreadCell::mergedXCells() const { return d->hasExtra() ? d->extra()->mergedXCells : 0; } int KSpreadCell::mergedYCells() const { return d->hasExtra() ? d->extra()->mergedYCells : 0; } int KSpreadCell::extraXCells() const { return d->hasExtra() ? d->extra()->extraXCells : 0; } int KSpreadCell::extraYCells() const { return d->hasExtra() ? d->extra()->extraYCells : 0; } double KSpreadCell::extraWidth() const { return d->hasExtra() ? d->extra()->extraWidth : 0; } double KSpreadCell::extraHeight() const { return d->hasExtra() ? d->extra()->extraHeight : 0; } bool KSpreadCell::isDate() const { FormatType ft = formatType(); // workaround, since date/time is stored as floating-point return value().isNumber() && ( ft == ShortDate_format || ft == TextDate_format || ( (ft >= date_format1) && (ft <= date_format26) ) ); } bool KSpreadCell::isTime() const { FormatType ft = formatType(); // workaround, since date/time is stored as floating-point return value().isNumber() && ( ( (ft >= Time_format) && (ft <= Time_format8) ) ); } void KSpreadCell::setCalcDirtyFlag() { if ( d->content != Formula ) { //don't set the flag if we don't hold a formula clearFlag(Flag_CalcDirty); return; } setFlag(Flag_CalcDirty); m_pTable->setRegionPaintDirty(cellRect()); } bool KSpreadCell::updateChart(bool refresh) { // Update a chart for example if it depends on this cell. if ( d->row != 0 && d->column != 0 ) { CellBinding *bind; for ( bind = m_pTable->firstCellBinding(); bind != 0L; bind = m_pTable->nextCellBinding() ) { if ( bind->contains( d->column, d->row ) ) { if (!refresh) return true; bind->cellChanged( this ); } } return true; } return false; } void KSpreadCell::checkTextInput() { // Goal of this method: determine the value of the cell clearAllErrors(); d->value = KSpreadValue::empty(); Q_ASSERT( d->content == Text ); // Get the text from that cell QString str = d->strText; KSpread::ValueParser::self()->parse (str, this); // Parsing as time acts like an autoformat: we even change d->strText // [h]:mm:ss -> might get set by ValueParser if (isTime() && (formatType() != Time_format7)) d->strText = locale()->formatTime( value().asDateTime().time(), true); // convert first letter to uppercase ? if (m_pTable->getFirstLetterUpper() && value().isString() && (!d->strText.isEmpty())) { QString str = value().asString(); setValue( KSpreadValue( str[0].upper() + str.right( str.length()-1 ) ) ); } } //used in calc, setNumber, ValueParser void KSpreadCell::checkNumberFormat() { if ( formatType() == Number_format && value().isNumber() ) { if ( value().asFloat() > 1e+10 ) setFormatType( Scientific_format ); } } QDomElement KSpreadCell::save( QDomDocument& doc, int _x_offset, int _y_offset, bool force, bool copy, bool era ) { // Save the position of this cell QDomElement cell = doc.createElement( "cell" ); cell.setAttribute( "row", d->row - _y_offset ); cell.setAttribute( "column", d->column - _x_offset ); // // Save the formatting information // QDomElement format = KSpreadFormat::save( doc, d->column, d->row, force, copy ); 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 ( d->hasExtra() && d->extra()->conditions ) { QDomElement conditionElement = d->extra()->conditions->saveConditions( doc ); if ( !conditionElement.isNull() ) cell.appendChild( conditionElement ); } if ( d->hasExtra() && (d->extra()->validity != 0) ) { QDomElement validity = doc.createElement("validity"); QDomElement param=doc.createElement("param"); param.setAttribute("cond",(int)d->extra()->validity->m_cond); param.setAttribute("action",(int)d->extra()->validity->m_action); param.setAttribute("allow",(int)d->extra()->validity->m_allow); param.setAttribute("valmin",d->extra()->validity->valMin); param.setAttribute("valmax",d->extra()->validity->valMax); param.setAttribute("displaymessage",d->extra()->validity->displayMessage); validity.appendChild(param); QDomElement title = doc.createElement( "title" ); title.appendChild( doc.createTextNode( d->extra()->validity->title ) ); validity.appendChild( title ); QDomElement message = doc.createElement( "message" ); message.appendChild( doc.createCDATASection( d->extra()->validity->message ) ); validity.appendChild( message ); QString tmp; if ( d->extra()->validity->timeMin.isValid() ) { QDomElement timeMin = doc.createElement( "timemin" ); tmp=d->extra()->validity->timeMin.toString(); timeMin.appendChild( doc.createTextNode( tmp ) ); validity.appendChild( timeMin ); } if ( d->extra()->validity->timeMax.isValid() ) { QDomElement timeMax = doc.createElement( "timemax" ); tmp=d->extra()->validity->timeMax.toString(); timeMax.appendChild( doc.createTextNode( tmp ) ); validity.appendChild( timeMax ); } if ( d->extra()->validity->dateMin.isValid() ) { QDomElement dateMin = doc.createElement( "datemin" ); QString tmp("%1/%2/%3"); tmp = tmp.arg(d->extra()->validity->dateMin.year()).arg(d->extra()->validity->dateMin.month()).arg(d->extra()->validity->dateMin.day()); dateMin.appendChild( doc.createTextNode( tmp ) ); validity.appendChild( dateMin ); } if ( d->extra()->validity->dateMax.isValid() ) { QDomElement dateMax = doc.createElement( "datemax" ); QString tmp("%1/%2/%3"); tmp = tmp.arg(d->extra()->validity->dateMax.year()).arg(d->extra()->validity->dateMax.month()).arg(d->extra()->validity->dateMax.day()); dateMax.appendChild( doc.createTextNode( tmp ) ); validity.appendChild( dateMax ); } cell.appendChild( validity ); } if ( m_strComment ) { QDomElement comment = doc.createElement( "comment" ); comment.appendChild( doc.createCDATASection( *m_strComment ) ); cell.appendChild( comment ); } // // Save the text // if ( !d->strText.isEmpty() ) { // Formulas need to be encoded to ensure that they // are position independent. if ( isFormula() ) { QDomElement text = doc.createElement( "text" ); // if we are cutting to the clipboard, relative references need to be encoded absolutely text.appendChild( doc.createTextNode( encodeFormula( era ) ) ); cell.appendChild( text ); /* we still want to save the results of the formula */ QDomElement formulaResult = doc.createElement( "result" ); saveCellResult( doc, formulaResult, d->strOutText ); 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( d->strText ) ); cell.appendChild( text ); } else { // Save the cell contents (in a locale-independent way) QDomElement text = doc.createElement( "text" ); saveCellResult( doc, text, d->strText ); 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 str ) { QString dataType = "Other"; // fallback if ( value().isNumber() ) { if ( isDate() ) { // serial number of date QDate dd = value().asDateTime().date(); dataType = "Date"; str = "%1/%2/%3"; str = str.arg(dd.year()).arg(dd.month()).arg(dd.day()); } else if( isTime() ) { // serial number of time dataType = "Time"; str = value().asDateTime().time().toString(); } else { // real number dataType = "Num"; str = QString::number(value().asFloat(), 'g', DBL_DIG); } } if ( value().isBoolean() ) { dataType = "Bool"; str = value().asBoolean() ? "true" : "false"; } if ( value().isString() ) { dataType = "Str"; str = value().asString(); } result.setAttribute( "dataType", dataType ); if ( !d->strOutText.isEmpty() ) result.setAttribute( "outStr", d->strOutText ); result.appendChild( doc.createTextNode( str ) ); return true; /* really isn't much of a way for this function to fail */ } void KSpreadCell::saveOasisAnnotation( KoXmlWriter &xmlwriter ) { if ( m_strComment ) { xmlwriter.startElement( "office:annotation" ); QStringList text = QStringList::split( "\n", *m_strComment ); for ( QStringList::Iterator it = text.begin(); it != text.end(); ++it ) { xmlwriter.startElement( "text:p" ); xmlwriter.addTextNode( *it ); xmlwriter.endElement(); } xmlwriter.endElement(); } } void KSpreadCell::saveOasisCellStyle( KoGenStyle ¤tCellStyle ) { KSpreadFormat::saveOasisCellStyle( currentCellStyle, column(), row() ); if ( d->hasExtra() && d->extra()->conditions ) d->extra()->conditions->saveOasisConditions( currentCellStyle ); } bool KSpreadCell::saveOasis( KoXmlWriter& xmlwriter, KoGenStyles &mainStyles, int row, int column, int maxCols, int &repeated, KSpreadGenValidationStyles &valStyle ) { if ( !isObscuringForced() ) xmlwriter.startElement( "table:table-cell" ); else xmlwriter.startElement( "table:covered-table-cell" ); #if 0 //add font style QFont font; KSpreadValue const value( cell->value() ); if ( !cell->isDefault() ) { font = cell->textFont( i, row ); m_styles.addFont( font ); if ( cell->hasProperty( KSpreadFormat::PComment ) ) hasComment = true; } #endif KoGenStyle currentCellStyle( KSpreadDoc::STYLE_CELL /*Name?????*/ ); saveOasisCellStyle( currentCellStyle ); xmlwriter.addAttribute( "table:style-name", mainStyles.lookup( currentCellStyle, "ce" ) ); // group empty cells with the same style if ( isEmpty() && !hasProperty( KSpreadFormat::PComment ) && !isObscuringForced() && !isForceExtraCells() ) { int j = column + 1; while ( j <= maxCols ) { KSpreadCell *nextCell = m_pTable->cellAt( j, row ); KoGenStyle nextCellStyle( KSpreadDoc::STYLE_CELL /*Name?????*/ ); nextCell->saveOasisCellStyle( nextCellStyle ); if ( nextCell->isEmpty() && !nextCell->hasProperty( KSpreadFormat::PComment ) && ( nextCellStyle==currentCellStyle ) && !isObscuringForced() && !isForceExtraCells() ) ++repeated; else break; ++j; } if ( repeated > 1 ) xmlwriter.addAttribute( "table:number-columns-repeated", QString::number( repeated ) ); } saveOasisAnnotation( xmlwriter ); if ( value().isBoolean() ) { xmlwriter.addAttribute( "office:value-type", "boolean" ); xmlwriter.addAttribute( "office:boolean-value", ( value().asBoolean() ? "true" : "false" ) ); } else if ( value().isNumber() ) { FormatType type = formatType(); if ( type == Percentage_format ) { xmlwriter.addAttribute( "office:value-type", "percentage" ); xmlwriter.addAttribute( "office:value", QString::number( value().asFloat() ) ); } else if ( type == Money_format ) { xmlwriter.addAttribute( "office:value-type", "currency" ); // TODO: add code of currency //xmlwriter.addAttribute( "tableoffice:currency", locale()->currencySymbol() ); xmlwriter.addAttribute( "office:value", QString::number( value().asFloat() ) ); } else if ( isDate() ) { xmlwriter.addAttribute( "office:value-type", "date" ); xmlwriter.addAttribute( "office:date-value", value().asDate().toString( Qt::ISODate ) ); } else if ( isTime() ) { xmlwriter.addAttribute( "office:value-type", "time" ); xmlwriter.addAttribute( "office:time-value", value().asTime().toString( "PThhHmmMssS" ) ); } else { xmlwriter.addAttribute( "office:value-type", "float" ); xmlwriter.addAttribute( "office:value", QString::number( value().asFloat() ) ); } } else if ( value().isString() ) { xmlwriter.addAttribute( "office:value-type", "string" ); xmlwriter.addAttribute( "office:string-value", value().asString() ); } else { kdDebug() << "Type: " << value().type() << endl; } if (d->hasExtra() && d->extra()->validity) { KSpreadGenValidationStyle styleVal(d->extra()->validity); xmlwriter.addAttribute( "table:validation-name", valStyle.lookup( styleVal ) ); } if ( isFormula() ) { kdDebug() << "Formula found" << endl; QString formula( convertFormulaToOasisFormat( text() ) ); xmlwriter.addAttribute( "table:formula", formula ); } if ( isForceExtraCells() ) { int colSpan = mergedXCells() + 1; int rowSpan = mergedYCells() + 1; if ( colSpan > 1 ) xmlwriter.addAttribute( "table:number-columns-spanned", QString::number( colSpan ) ); if ( rowSpan > 1 ) xmlwriter.addAttribute( "table:number-rows-spanned", QString::number( rowSpan ) ); } if ( !isEmpty() ) { xmlwriter.startElement( "text:p" ); xmlwriter.addTextNode(strOutText()); xmlwriter.endElement(); } xmlwriter.endElement(); return true; } QString KSpreadCell::convertFormulaToOasisFormat( const QString & formula ) const { QString s; QRegExp exp("(\\$?)([a-zA-Z]+)(\\$?)([0-9]+)"); int n = exp.search( formula, 0 ); kdDebug() << "Exp: " << formula << ", n: " << n << ", Length: " << formula.length() << ", Matched length: " << exp.matchedLength() << endl; bool inQuote1 = false; bool inQuote2 = false; int i = 0; int l = (int) formula.length(); if ( l <= 0 ) return formula; while ( i < l ) { if ( ( n != -1 ) && ( n < i ) ) { n = exp.search( formula, i ); kdDebug() << "Exp: " << formula.right( l - i ) << ", n: " << n << endl; } if ( formula[i] == '"' ) { inQuote1 = !inQuote1; s += formula[i]; ++i; continue; } if ( formula[i] == '\'' ) { // named area inQuote2 = !inQuote2; ++i; continue; } if ( inQuote1 || inQuote2 ) { s += formula[i]; ++i; continue; } if ( ( formula[i] == '=' ) && ( formula[i + 1] == '=' ) ) { s += '='; ++i;++i; continue; } if ( formula[i] == '!' ) { insertBracket( s ); s += '.'; ++i; continue; } if ( formula[i] == ',' ) { s += '.'; ++i; continue; } if ( n == i ) { int ml = exp.matchedLength(); if ( formula[ i + ml ] == '!' ) { kdDebug() << "No cell ref but sheet name" << endl; s += formula[i]; ++i; continue; } if ( ( i > 0 ) && ( formula[i - 1] != '!' ) ) s += "[."; for ( int j = 0; j < ml; ++j ) { s += formula[i]; ++i; } s += ']'; continue; } s += formula[i]; ++i; } return s; } void KSpreadCell::loadOasisConditional( QDomElement * style ) { if ( style )//safe { QDomElement elementItem = style->firstChild().toElement(); elementItem = elementItem.firstChild().toElement(); if ( !elementItem.isNull() ) { if (d->hasExtra()) delete d->extra()->conditions; d->extra()->conditions = new KSpreadConditions( this ); d->extra()->conditions->loadOasisConditions( elementItem ); d->extra()->conditions->checkMatches(); } } } bool KSpreadCell::loadOasis( const QDomElement &element, const KoOasisStyles& oasisStyles ) { QString text; kdDebug()<<" table:style-name :"<, e.g. links text = subText.text(); if ( subText.hasAttribute( "xlink:href" ) ) { QString link = subText.attribute( "xlink:href" ); text = "!" + text + ""; d->extra()->QML = new QSimpleRichText( text.mid(1), QApplication::font() );//, m_pTable->widget() ); d->strText = text; } } else { text = textP.text(); // our text, could contain formating for value or result of formul setCellText( text ); setValue( text ); } } bool isFormula = false; if ( element.hasAttribute( "table:formula" ) ) { kdDebug()<<" formula :"< 0 ) year = value.left( p1 ).toInt( &ok ); kdDebug() << "year: " << value.left( p1 ) << endl; int p2 = value.find( '-', ++p1 ); if ( ok ) month = value.mid( p1, p2 - p1 ).toInt( &ok ); kdDebug() << "month: " << value.mid( p1, p2 - p1 ) << endl; if ( ok ) day = value.right( value.length() - p2 - 1 ).toInt( &ok ); kdDebug() << "day: " << value.right( value.length() - p2 ) << endl; if ( ok ) { setValue( QDate( year, month, day ) ); setFormatType (ShortDate_format); kdDebug() << "Set QDate: " << year << " - " << month << " - " << day << endl; } } else if ( valuetype == "time" ) { QString value = element.attribute( "office:value" ); if ( value.isEmpty() ) value = element.attribute( "office:time-value" ); kdDebug() << "Type: time: " << value << endl; // "PT15H10M12S" int hours = 0, minutes = 0, seconds = 0; int l = value.length(); QString num; bool ok = false; for ( int i = 0; i < l; ++i ) { if ( value[i].isNumber() ) { num += value[i]; continue; } else if ( value[i] == 'H' ) hours = num.toInt( &ok ); else if ( value[i] == 'M' ) minutes = num.toInt( &ok ); else if ( value[i] == 'S' ) seconds = num.toInt( &ok ); else continue; kdDebug() << "Num: " << num << endl; num = ""; if ( !ok ) break; } kdDebug() << "Hours: " << hours << ", " << minutes << ", " << seconds << endl; if ( ok ) { // KSpreadValue kval( timeToNum( hours, minutes, seconds ) ); // cell->setValue( kval ); setValue( QTime( hours % 24, minutes, seconds ) ); setFormatType (Time_format); } } else if( valuetype == "string" ) { QString value = element.attribute( "office:value" ); if ( value.isEmpty() ) value = element.attribute( "office:string-value" ); setValue( value ); setFormatType (Text_format); } else kdDebug()<<" type of value found : "< 1 || rowSpan > 1 ) forceExtraCells( d->column, d->row, colSpan - 1, rowSpan - 1 ); // cell comment/annotation QDomElement annotationElement = element.namedItem( "office:annotation" ).toElement(); if ( !annotationElement.isNull() ) { QString comment; QDomNode node = annotationElement.firstChild(); while( !node.isNull() ) { QDomElement commentElement = node.toElement(); if( !commentElement.isNull() ) if( commentElement.tagName() == "text:p" ) { if( !comment.isEmpty() ) comment.append( '\n' ); comment.append( commentElement.text() ); } node = node.nextSibling(); } if( !comment.isEmpty() ) setComment( comment ); } return true; } void KSpreadCell::loadOasisValidation( const QString& validationName ) { kdDebug()<<"validationName:"<doc()->loadingInfo()->validation( validationName); if (d->hasExtra()) delete d->extra()->validity; d->extra()->validity = new KSpreadValidity; if ( element.hasAttribute( "table:condition" ) ) { QString valExpression = element.attribute( "table:condition" ); kdDebug()<<" element.attribute( table:condition ) "<' | '<=' | '>=' | '=' | '!=' //Value ::= NumberValue | String | Formula //A Formula is a formula without an equals (=) sign at the beginning. See section 8.1.3 for more information. //A String comprises one or more characters surrounded by quotation marks. //A NumberValue is a whole or decimal number. It must not contain comma separators for numbers of 1000 or greater. //ExtendedTrueCondition if ( valExpression.contains( "cell-content-text-length()" ) ) { //"cell-content-text-length()>45" valExpression = valExpression.remove("cell-content-text-length()" ); kdDebug()<<" valExpression = :"<extra()->validity->m_allow = Allow_TextLength; loadOasisValidationCondition( valExpression ); } else if ( valExpression.contains( "cell-content-is-text()" ) ) { d->extra()->validity->m_allow = Allow_Text; } //cell-content-text-length-is-between(Value, Value) | cell-content-text-length-is-not-between(Value, Value) else if ( valExpression.contains( "cell-content-text-length-is-between" ) ) { d->extra()->validity->m_allow = Allow_TextLength; d->extra()->validity->m_cond = Between; valExpression = valExpression.remove( "cell-content-text-length-is-between(" ); kdDebug()<<" valExpression :"<extra()->validity->m_allow = Allow_TextLength; d->extra()->validity->m_cond = Different; valExpression = valExpression.remove( "cell-content-text-length-is-not-between(" ); kdDebug()<<" valExpression :"<extra()->validity->m_allow = Allow_Number; valExpression = valExpression.remove( "cell-content-is-whole-number() and " ); } else if (valExpression.contains( "cell-content-is-decimal-number()" ) ) { d->extra()->validity->m_allow = Allow_Integer; valExpression = valExpression.remove( "cell-content-is-decimal-number() and " ); } else if (valExpression.contains( "cell-content-is-date()" ) ) { d->extra()->validity->m_allow = Allow_Date; valExpression = valExpression.remove( "cell-content-is-date() and " ); } else if (valExpression.contains( "cell-content-is-time()" ) ) { d->extra()->validity->m_allow = Allow_Time; valExpression = valExpression.remove( "cell-content-is-time() and " ); } kdDebug()<<"valExpression :"<extra()->validity->m_cond = Between; } if ( valExpression.contains( "cell-content-is-not-between(" ) ) { valExpression = valExpression.remove( "cell-content-is-not-between(" ); valExpression = valExpression.remove( ")" ); QStringList listVal = QStringList::split( ",", valExpression ); loadOasisValidationValue( listVal ); d->extra()->validity->m_cond = Different; } } } if ( element.hasAttribute( "table:allow-empty-cell" ) ) { kdDebug()<<" element.hasAttribute( table:allow-empty-cell ) :"<extra()->validity->allowEmptyCell = ( ( element.attribute( "table:allow-empty-cell" )=="true" ) ? true : false ); } if ( element.hasAttribute( "table:base-cell-address" ) ) { //todo what is it ? } QDomElement help = element.namedItem( "table:help-message" ).toElement(); if ( !help.isNull() ) { if ( help.hasAttribute( "table:title" ) ) { kdDebug()<<"help.attribute( table:title ) :"<extra()->validity->titleInfo = help.attribute( "table:title" ); } if ( help.hasAttribute( "table:display" ) ) { kdDebug()<<"help.attribute( table:display ) :"<extra()->validity->displayValidationInformation = ( ( help.attribute( "table:display" )=="true" ) ? true : false ); } QDomElement attrText = help.namedItem( "text:p" ).toElement(); if ( !attrText.isNull() ) { kdDebug()<<"help text :"<extra()->validity->messageInfo = attrText.text(); } } QDomElement error = element.namedItem( "table:error-message" ).toElement(); if ( !error.isNull() ) { if ( error.hasAttribute( "table:title" ) ) d->extra()->validity->title = error.attribute( "table:title" ); if ( error.hasAttribute( "table:message-type" ) ) { QString str = error.attribute( "table:message-type" ); if ( str == "warning" ) d->extra()->validity->m_action = Warning; else if ( str == "information" ) d->extra()->validity->m_action = Information; else if ( str == "stop" ) d->extra()->validity->m_action = Stop; else kdDebug()<<"validation : message type unknown :"<extra()->validity->displayMessage = (error.attribute( "table:display" )=="true"); } QDomElement attrText = error.namedItem( "text:p" ).toElement(); if ( !attrText.isNull() ) d->extra()->validity->message = attrText.text(); } } void KSpreadCell::loadOasisValidationValue( const QStringList &listVal ) { bool ok = false; kdDebug()<<" listVal[0] :"<extra()->validity->m_allow == Allow_Date ) { d->extra()->validity->dateMin = QDate::fromString( listVal[0] ); d->extra()->validity->dateMax = QDate::fromString( listVal[1] ); } else if ( d->extra()->validity->m_allow == Allow_Time ) { d->extra()->validity->timeMin = QTime::fromString( listVal[0] ); d->extra()->validity->timeMax = QTime::fromString( listVal[1] ); } else { d->extra()->validity->valMin = listVal[0].toDouble(&ok); if ( !ok ) { d->extra()->validity->valMin = listVal[0].toInt(&ok); if ( !ok ) kdDebug()<<" Try to parse this value :"<extra()->validity->valMin = listVal[0]; #endif } ok=false; d->extra()->validity->valMax = listVal[1].toDouble(&ok); if ( !ok ) { d->extra()->validity->valMax = listVal[1].toInt(&ok); if ( !ok ) kdDebug()<<" Try to parse this value :"<extra()->validity->valMax = listVal[1]; #endif } } } void KSpreadCell::loadOasisValidationCondition( QString &valExpression ) { QString value; if (valExpression.find( "<=" )==0 ) { value = valExpression.remove( 0,2 ); d->extra()->validity->m_cond = InferiorEqual; } else if (valExpression.find( ">=" )==0 ) { value = valExpression.remove( 0,2 ); d->extra()->validity->m_cond = SuperiorEqual; } else if (valExpression.find( "!=" )==0 ) { //add Differentto attribute value = valExpression.remove( 0,2 ); d->extra()->validity->m_cond = DifferentTo; } else if ( valExpression.find( "<" )==0 ) { value = valExpression.remove( 0,1 ); d->extra()->validity->m_cond = Inferior; } else if(valExpression.find( ">" )==0 ) { value = valExpression.remove( 0,1 ); d->extra()->validity->m_cond = Superior; } else if (valExpression.find( "=" )==0 ) { value = valExpression.remove( 0,1 ); d->extra()->validity->m_cond = Equal; } else kdDebug()<<" I don't know how to parse it :"<extra()->validity->m_allow == Allow_Date ) { d->extra()->validity->dateMin = QDate::fromString( value ); } else if (d->extra()->validity->m_allow == Allow_Date ) { d->extra()->validity->timeMin = QTime::fromString( value ); } else { bool ok = false; d->extra()->validity->valMin = value.toDouble(&ok); if ( !ok ) { d->extra()->validity->valMin = value.toInt(&ok); if ( !ok ) kdDebug()<<" Try to parse this value :"<extra()->validity->valMin = value; #endif } } } bool KSpreadCell::load( const QDomElement & cell, int _xshift, int _yshift, PasteMode pm, Operation op, bool paste ) { bool ok; // // First of all determine in which row and column this // cell belongs. // d->row = cell.attribute( "row" ).toInt( &ok ) + _yshift; if ( !ok ) return false; d->column = cell.attribute( "column" ).toInt( &ok ) + _xshift; if ( !ok ) return false; // Validation if ( d->row < 1 || d->row > KS_rowMax ) { kdDebug(36001) << "KSpreadCell::load: Value out of Range Cell:row=" << d->row << endl; return false; } if ( d->column < 1 || d->column > KS_colMax ) { kdDebug(36001) << "KSpreadCell::load: Value out of Range Cell:column=" << d->column << 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 ( !KSpreadFormat::load( f, pm, paste ) ) 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; } if (i || d->hasExtra()) d->extra()->extraXCells = 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; } if (i || d->hasExtra()) d->extra()->extraYCells = i; if ( i > 0 ) { setFlag(Flag_ForceExtra); } } if ( testFlag( Flag_ForceExtra ) ) { if (d->hasExtra()) forceExtraCells( d->column, d->row, d->extra()->extraXCells, d->extra()->extraYCells ); } } // // Load the condition section of a cell. // QDomElement conditionsElement = cell.namedItem( "condition" ).toElement(); if ( !conditionsElement.isNull()) { if (d->hasExtra()) delete d->extra()->conditions; d->extra()->conditions = new KSpreadConditions( this ); d->extra()->conditions->loadConditions( conditionsElement ); d->extra()->conditions->checkMatches(); } QDomElement validity = cell.namedItem( "validity" ).toElement(); if ( !validity.isNull()) { QDomElement param = validity.namedItem( "param" ).toElement(); if(!param.isNull()) { d->extra()->validity = new KSpreadValidity; if ( param.hasAttribute( "cond" ) ) { d->extra()->validity->m_cond = (Conditional) param.attribute("cond").toInt( &ok ); if ( !ok ) return false; } if ( param.hasAttribute( "action" ) ) { d->extra()->validity->m_action = (Action) param.attribute("action").toInt( &ok ); if ( !ok ) return false; } if ( param.hasAttribute( "allow" ) ) { d->extra()->validity->m_allow = (Allow) param.attribute("allow").toInt( &ok ); if ( !ok ) return false; } if ( param.hasAttribute( "valmin" ) ) { d->extra()->validity->valMin = param.attribute("valmin").toDouble( &ok ); if ( !ok ) return false; } if ( param.hasAttribute( "valmax" ) ) { d->extra()->validity->valMax = param.attribute("valmax").toDouble( &ok ); if ( !ok ) return false; } if ( param.hasAttribute( "displaymessage" ) ) { d->extra()->validity->displayMessage = ( bool )param.attribute("displaymessage").toInt(); } } QDomElement title = validity.namedItem( "title" ).toElement(); if (!title.isNull()) { d->extra()->validity->title = title.text(); } QDomElement message = validity.namedItem( "message" ).toElement(); if (!message.isNull()) { d->extra()->validity->message = message.text(); } QDomElement timeMin = validity.namedItem( "timemin" ).toElement(); if ( !timeMin.isNull() ) { d->extra()->validity->timeMin = toTime(timeMin); } QDomElement timeMax = validity.namedItem( "timemax" ).toElement(); if ( !timeMax.isNull() ) { d->extra()->validity->timeMax = toTime(timeMax); } QDomElement dateMin = validity.namedItem( "datemin" ).toElement(); if ( !dateMin.isNull() ) { d->extra()->validity->dateMin = toDate(dateMin); } QDomElement dateMax = validity.namedItem( "datemax" ).toElement(); if ( !dateMax.isNull() ) { d->extra()->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 || pm == ::Result ) ) { /* 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" ) ); } QDomElement result = cell.namedItem( "result" ).toElement(); QString txt = text.text(); if ((pm == ::Result) && (txt[0] == '=')) // paste text of the element, if we want to paste result // and the source cell contains a formula d->strText = result.text(); else //otherwise copy everything loadCellData(text, op); if ( !result.isNull() ) { QString dataType; QString t = result.text(); if ( result.hasAttribute( "dataType" ) ) dataType = result.attribute( "dataType" ); if ( result.hasAttribute( "outStr" ) ) { d->strOutText = result.attribute( "outStr" ); if ( !d->strOutText.isEmpty() ) clearFlag( Flag_TextFormatDirty ); } bool clear = true; // boolean ? if( dataType == "Bool" ) { if ( t == "false" ) setValue( true ); else if ( t == "true" ) setValue( false ); else clear = false; } else if( dataType == "Num" ) { bool ok = false; double dd = t.toDouble( &ok ); if ( ok ) setValue ( dd ); else clear = false; } else if( dataType == "Date" ) { bool ok = false; double dd = t.toDouble( &ok ); if ( ok ) setValue ( dd ); else { 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(); QDate date( year, month, day ); if ( date.isValid() ) setValue( date ); else clear = false; } } else if( dataType == "Time" ) { bool ok = false; double dd = t.toDouble( &ok ); if ( ok ) setValue( dd ); else { 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(); QTime time( hours, minutes, second ); if ( time.isValid() ) setValue( time ); else clear = false; } } else { setValue( t ); } if ( clear ) clearFlag( Flag_CalcDirty ); } } return true; } bool KSpreadCell::loadCellData(const QDomElement & text, Operation op ) { QString t = text.text(); t = t.stripWhiteSpace(); setFlag(Flag_LayoutDirty); setFlag(Flag_TextFormatDirty); // A formula like =A1+A2 ? if( t[0] == '=' ) { t = decodeFormula( t, d->column, d->row ); d->strText = pasteOperation( t, d->strText, op ); setFlag(Flag_CalcDirty); clearAllErrors(); d->content = Formula; if ( !m_pTable->isLoading() ) // i.e. when pasting if ( !makeFormula() ) kdError(36001) << "ERROR: Syntax ERROR" << endl; } // rich text ? else if (t[0] == '!' ) { d->extra()->QML = new QSimpleRichText( t.mid(1), QApplication::font() );//, m_pTable->widget() ); d->strText = t; } else { bool newStyleLoading = true; QString dataType; if ( text.hasAttribute( "dataType" ) ) // new docs { dataType = 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 (isDate() && ( t.contains('/') == 2 )) dataType = "Date"; else if (isTime() && ( t.contains(':') == 2 ) ) dataType = "Time"; else { d->strText = pasteOperation( t, d->strText, op ); checkTextInput(); //kdDebug(36001) << "KSpreadCell::load called checkTextInput, got dataType=" << dataType << " t=" << t << endl; newStyleLoading = false; } } if ( newStyleLoading ) { d->value = KSpreadValue::empty(); clearAllErrors(); // boolean ? if( dataType == "Bool" ) { if ( t == "false" ) setValue( true ); else if ( t == "true" ) setValue( false ); else kdWarning() << "Cell with BoolData, should be true or false: " << t << endl; } // number ? else if( dataType == "Num" ) { bool ok = false; setValue ( KSpreadValue( t.toDouble(&ok) ) ); // We save in non-localized format if ( !ok ) { kdWarning(36001) << "Couldn't parse '" << t << "' as number." << endl; } /* We will need to localize the text version of the number */ KLocale* locale = m_pTable->doc()->locale(); /* KLocale::formatNumber requires the precision we want to return. */ int precision = t.length() - t.find('.') - 1; if ( formatType() == Percentage_format ) { setFactor( 100.0 ); // should have been already done by loadFormat t = locale->formatNumber( value().asFloat() * 100.0, precision ); d->strText = pasteOperation( t, d->strText, op ); d->strText += '%'; } else { t = locale->formatNumber(value().asFloat(), precision); d->strText = pasteOperation( t, d->strText, op ); } } // date ? else if( dataType == "Date" ) { 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(); setValue( QDate(year,month,day) ); if ( value().asDate().isValid() ) // Should always be the case for new docs d->strText = locale()->formatDate( value().asDate(), true ); else // This happens with old docs, when format is set wrongly to date { d->strText = pasteOperation( t, d->strText, op ); checkTextInput(); } } // time ? else if( dataType == "Time" ) { 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(); setValue( QTime(hours,minutes,second) ); if ( value().asTime().isValid() ) // Should always be the case for new docs d->strText = locale()->formatTime( value().asTime(), true ); else // This happens with old docs, when format is set wrongly to time { d->strText = pasteOperation( t, d->strText, op ); checkTextInput(); } } else { // Set the cell's text d->strText = pasteOperation( t, d->strText, op ); setValue( d->strText ); } } } if ( text.hasAttribute( "outStr" ) ) // very new docs { d->strOutText = text.attribute( "outStr" ); if ( !d->strOutText.isEmpty() ) clearFlag( Flag_TextFormatDirty ); } if ( !m_pTable->isLoading() ) setCellText( d->strText ); if ( d->hasExtra() && d->extra()->conditions ) d->extra()->conditions->checkMatches(); return true; } QTime KSpreadCell::toTime(const QDomElement &element) { //TODO: can't we use tryParseTime (after modification) instead? 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(); setValue( KSpreadValue( QTime(hours,minutes,second)) ); return value().asTime(); } QDate KSpreadCell::toDate(const 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(); setValue( KSpreadValue( QDate(year,month,day) ) ); return value().asDate(); } QString KSpreadCell::pasteOperation( const QString &new_text, const 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 = "=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 ); } setFlag(Flag_LayoutDirty); clearAllErrors(); d->content = Text; 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 ); } tmp_op = decodeFormula( tmp_op, d->column, d->row ); setFlag(Flag_LayoutDirty); clearAllErrors(); d->content = Formula; return tmp_op; } tmp = decodeFormula( new_text, d->column, d->row ); setFlag(Flag_LayoutDirty); clearAllErrors(); d->content = Formula; return tmp; } QString KSpreadCell::testAnchor( int _x, int _y ) const { if ( !d->hasExtra() || !d->extra()->QML ) return QString::null; return d->extra()->QML->anchorAt( QPoint( _x, _y ) ); } void KSpreadCell::tableDies() { // Avoid unobscuring the cells in the destructor. if (d->hasExtra()) { d->extra()->extraXCells = 0; d->extra()->extraYCells = 0; d->extra()->mergedXCells = 0; d->extra()->mergedYCells = 0; } d->nextCell = 0; d->previousCell = 0; } KSpreadCell::~KSpreadCell() { if ( d->nextCell ) d->nextCell->setPreviousCell( d->previousCell ); if ( d->previousCell ) d->previousCell->setNextCell( d->nextCell ); if (d->hasExtra()) { delete d->extra()->QML; delete d->extra()->validity; } delete d->code; // Unobscure cells. int extraXCells = d->hasExtra() ? d->extra()->extraXCells : 0; int extraYCells = d->hasExtra() ? d->extra()->extraYCells : 0; for( int x = 0; x <= extraXCells; ++x ) for( int y = (x == 0) ? 1 : 0; // avoid looking at (+0,+0) y <= extraYCells; ++y ) { KSpreadCell* cell = m_pTable->cellAt( d->column + x, d->row + y ); if ( cell ) cell->unobscure(this); } d->value = KSpreadValue::empty(); - valueChanged (); + + if (!isDefault()) + valueChanged (); //our value has been changed (is now null), but only if we aren't default delete d; } bool KSpreadCell::operator > ( const KSpreadCell & cell ) const { if ( value().isNumber() ) // ### what about bools ? { if ( cell.value().isNumber() ) return value().asFloat() > cell.value().asFloat(); else return false; // numbers are always < than texts } else if(isDate()) { if( cell.isDate() ) return value().asDate() > cell.value().asDate(); else if (cell.value().isNumber()) return true; else return false; //date are always < than texts and time } else if(isTime()) { if( cell.isTime() ) return value().asTime() > cell.value().asTime(); else if( cell.isDate()) return true; //time are always > than date else if( cell.value().isNumber()) return true; else return false; //time are always < than texts } else { if ( KSpreadMap::respectCase ) return value().asString().compare(cell.value().asString()) > 0; else return ( value().asString() ).lower().compare(cell.value().asString().lower()) > 0; } } bool KSpreadCell::operator < ( const KSpreadCell & cell ) const { if ( value().isNumber() ) { if ( cell.value().isNumber() ) return value().asFloat() < cell.value().asFloat(); else return true; // numbers are always < than texts } else if(isDate()) { if( cell.isDate() ) return value().asDateTime().date() < cell.value().asDateTime().date(); else if( cell.value().isNumber()) return false; else return true; //date are always < than texts and time } else if(isTime()) { if( cell.isTime() ) return value().asDateTime().time() < cell.value().asDateTime().time(); else if(cell.isDate()) return false; //time are always > than date else if( cell.value().isNumber()) return false; else return true; //time are always < than texts } else { if ( KSpreadMap::respectCase ) return value().asString().compare(cell.value().asString()) < 0; else return ( value().asString() ).lower().compare(cell.value().asString().lower()) < 0; } } QRect KSpreadCell::cellRect() { Q_ASSERT(!isDefault()); return QRect(QPoint(d->column, d->row), QPoint(d->column, d->row)); } QValueList KSpreadCell::conditionList() const { if ( !d->hasExtra() || !d->extra()->conditions ) { QValueList emptyList; return emptyList; } return d->extra()->conditions->conditionList(); } void KSpreadCell::setConditionList( const QValueList & newList ) { if (d->hasExtra()) delete d->extra()->conditions; d->extra()->conditions = new KSpreadConditions( this ); d->extra()->conditions->setConditionList( newList ); d->extra()->conditions->checkMatches(); } bool KSpreadCell::hasError() const { return ( testFlag(Flag_ParseError) || testFlag(Flag_CircularCalculation) || testFlag(Flag_DependancyError)); } void KSpreadCell::clearAllErrors() { clearFlag( Flag_ParseError ); clearFlag( Flag_CircularCalculation ); clearFlag( Flag_DependancyError ); } bool KSpreadCell::calcDirtyFlag() { return ( (d->content == Formula) ? testFlag( Flag_CalcDirty ) : false ); } 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 ); } void KSpreadCell::checkForNamedAreas( QString & formula ) const { int l = formula.length(); int i = 0; QString word; int start = 0; while ( i < l ) { if ( formula[i].isLetterOrNumber() ) { word += formula[i]; ++i; continue; } if ( !word.isEmpty() ) { if ( table()->doc()->loadingInfo()->findWordInAreaList(word) ) { formula = formula.replace( start, word.length(), "'" + word + "'" ); l = formula.length(); ++i; kdDebug() << "Formula: " << formula << ", L: " << l << ", i: " << i + 1 <doc()->loadingInfo()->findWordInAreaList(word) ) { formula = formula.replace( start, word.length(), "'" + word + "'" ); l = formula.length(); ++i; kdDebug() << "Formula: " << formula << ", L: " << l << ", i: " << i + 1 < + + 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 "kspread_editors.h" #include "kspread_canvas.h" #include "kspread_cell.h" #include "kspread_doc.h" +#include "kspread_selection.h" +#include "kspread_sheet.h" #include "kspread_view.h" #include #include +#include #include #include #include #include /******************************************** * * KSpreadCellEditor * ********************************************/ KSpreadCellEditor::KSpreadCellEditor( KSpreadCell* _cell, KSpreadCanvas* _parent, const char* _name ) : QWidget( _parent, _name ) { m_pCell = _cell; m_pCanvas = _parent; setFocusPolicy( QWidget::StrongFocus ); } KSpreadCellEditor::~KSpreadCellEditor() { } /******************************************** * * KSpreadTextEditor * ********************************************/ KSpreadTextEditor::KSpreadTextEditor( KSpreadCell* _cell, KSpreadCanvas* _parent, const char* _name ) : KSpreadCellEditor( _cell, _parent, _name ), m_sizeUpdate(false), m_length(0), m_fontLength(0) { m_pEdit = new KLineEdit( this ); m_pEdit->installEventFilter( this ); m_pEdit->setFrame( FALSE ); m_pEdit->setCompletionMode((KGlobalSettings::Completion)canvas()->view()->doc()->completionMode() ); m_pEdit->setCompletionObject( &canvas()->view()->doc()->completion(),true ); setFocusProxy( m_pEdit ); connect( m_pEdit, SIGNAL( textChanged( const QString& ) ), this, SLOT( slotTextChanged( const QString& ) ) ); connect( m_pEdit, SIGNAL(completionModeChanged( KGlobalSettings::Completion )),this,SLOT (slotCompletionModeChanged(KGlobalSettings::Completion))); // A choose should always start at the edited cell // canvas()->setChooseMarkerRow( canvas()->markerRow() ); // canvas()->setChooseMarkerColumn( canvas()->markerColumn() ); m_blockCheck = FALSE; // set font size according to zoom factor QFont font( _cell->font() ); font.setPointSizeFloat( 0.01 * _parent->doc()->zoom() * font.pointSizeFloat() ); m_pEdit->setFont( font ); if (m_fontLength == 0) { QFontMetrics fm( m_pEdit->font() ); m_fontLength = fm.width('x'); } } KSpreadTextEditor::~KSpreadTextEditor() { canvas()->endChoose(); } void KSpreadTextEditor::cut() { if(m_pEdit) m_pEdit->cut(); } void KSpreadTextEditor::paste() { if( m_pEdit) m_pEdit->paste(); } void KSpreadTextEditor::copy() { if( m_pEdit) m_pEdit->copy(); } void KSpreadTextEditor::setEditorFont(QFont const & font, bool updateSize) { if (!m_pEdit) return; QFont tmpFont( font ); tmpFont.setPointSizeFloat( 0.01 * canvas()->doc()->zoom() * tmpFont.pointSizeFloat() ); m_pEdit->setFont( tmpFont ); if (updateSize) { QFontMetrics fm( m_pEdit->font() ); m_fontLength = fm.width('x'); int mw = fm.width( m_pEdit->text() ) + m_fontLength; // don't make it smaller: then we would have to repaint the obscured cells if (mw < width()) mw = width(); int mh = fm.height(); if (mh < height()) mh = height(); setGeometry(x(), y(), mw, mh); m_sizeUpdate = true; } } void KSpreadTextEditor::slotCompletionModeChanged(KGlobalSettings::Completion _completion) { canvas()->view()->doc()->setCompletionMode( _completion ); } void KSpreadTextEditor::slotTextChanged( const QString& t ) { // if ( canvas->chooseCursorPosition() >= 0 ) // m_pEdit->setCursorPosition( canvas->chooseCursorPosition() ); if (!checkChoose()) return; if (t.length() > m_length) { // allocate more space than needed. Otherwise it might be too slow m_length = t.length() + 5; // Too slow for long texts // QFontMetrics fm( m_pEdit->font() ); // int mw = fm.width( t ) + fm.width('x'); int mw = m_fontLength * m_length; if (mw < width()) mw = width(); if (t.isRightToLeft()) setGeometry(x() - mw + width(), y(), mw, height()); else setGeometry(x(), y(), mw, height()); m_length -= 2; } if ( (cell()->formatType()) == Percentage_format ) { if ( (t.length() == 1) && t[0].isDigit() ) { QString tmp = t + " %"; m_pEdit->setText(tmp); m_pEdit->setCursorPosition(1); return; } } canvas()->view()->editWidget()->setText( t ); // canvas()->view()->editWidget()->setCursorPosition( m_pEdit->cursorPosition() ); } bool KSpreadTextEditor::checkChoose() { if ( m_blockCheck ) return false; QString t = m_pEdit->text(); if ( t[0] != '=' ) canvas()->endChoose(); else { QChar r = t[ m_pEdit->cursorPosition() - 1 - canvas()->chooseTextLen() ]; if ( ( r == '*' || r == '|' || r == '&' || r == '-' || r == '+' || r == '/' || r == '!' || r == '(' || r == '^' || r == ',' || r == '%' || r == '[' || r == '{' || r == '~' || r == '=' || r == ';' || r == '>' || r == '<') ) { canvas()->startChoose(); } else { canvas()->endChoose(); } } return true; } void KSpreadTextEditor::resizeEvent( QResizeEvent* ) { m_pEdit->setGeometry( 0, 0, width(), height() ); } void KSpreadTextEditor::handleKeyPressEvent( QKeyEvent * _ev ) { if (_ev->key() == Qt::Key_F4) { if (m_pEdit == 0) { QApplication::sendEvent( m_pEdit, _ev ); return; } QRegExp exp("(\\$?)([a-zA-Z]+)(\\$?)([0-9]+)$"); int cur = m_pEdit->cursorPosition(); QString tmp, tmp2; int n = -1; // this is ugly, and sort of hack // FIXME rewrite to use the real KSpreadTokenizer unsigned i; for( i = 0; i < 10; i++ ) { tmp = m_pEdit->text().left( cur+i ); tmp2 = m_pEdit->text().right( m_pEdit->text().length() - cur - i ); n = exp.search(tmp); if( n >= 0 ) break; } if (n == -1) return; QString newPart; if ((exp.cap(1) == "$") && (exp.cap(3) == "$")) newPart = "$" + exp.cap(2) + exp.cap(4); else if ((exp.cap(1) != "$") && (exp.cap(3) != "$")) newPart = "$" + exp.cap(2) + "$" + exp.cap(4); else if ((exp.cap(1) == "$") && (exp.cap(3) != "$")) newPart = exp.cap(2) + "$" + exp.cap(4); else if ((exp.cap(1) != "$") && (exp.cap(3) == "$")) newPart = exp.cap(2) + exp.cap(4); QString newString = tmp.left(n); newString += newPart; cur = newString.length() - i; newString += tmp2; m_pEdit->setText(newString); - m_pEdit->setFocus(); m_pEdit->setCursorPosition( cur ); _ev->accept(); return; } // Send the key event to the QLineEdit QApplication::sendEvent( m_pEdit, _ev ); } QString KSpreadTextEditor::text() const { return m_pEdit->text(); } void KSpreadTextEditor::setText(QString text) { if (m_pEdit != 0) m_pEdit->setText(text); if (m_fontLength == 0) { QFontMetrics fm( m_pEdit->font() ); m_fontLength = fm.width('x'); } } int KSpreadTextEditor::cursorPosition() const { return m_pEdit->cursorPosition(); } void KSpreadTextEditor::setCursorPosition( int pos ) { m_pEdit->setCursorPosition(pos); canvas()->view()->editWidget()->setCursorPosition( pos ); checkChoose(); } void KSpreadTextEditor::insertFormulaChar(int /*c*/) { } bool KSpreadTextEditor::eventFilter( QObject* o, QEvent* e ) { // Only interested in QLineEdit if ( o != m_pEdit ) return FALSE; if ( e->type() == QEvent::FocusOut ) { canvas()->setLastEditorWithFocus( KSpreadCanvas::CellEditor ); return FALSE; } if ( e->type() == QEvent::KeyPress || e->type() == QEvent::KeyRelease ) { QKeyEvent* k = (QKeyEvent*)e; if ( !( k->state() & Qt::ShiftButton )|| canvas()->chooseFormulaArea()) { if ( k->key() == Key_Up || k->key() == Key_Down || k->key() == Key_Next || k->key() == Key_Prior || k->key() == Key_Escape || k->key() == Key_Tab ) { // Send directly to canvas QApplication::sendEvent( parent(), e ); return TRUE; } } // End choosing. May be restarted by KSpreadTextEditor::slotTextChanged if ( e->type() == QEvent::KeyPress && !k->text().isEmpty() ) { //kdDebug(36001) << "eventFilter End Choose" << endl; canvas()->endChoose(); //kdDebug(36001) << "Cont" << endl; } } return FALSE; } +KSpreadComboboxLocationEditWidget::KSpreadComboboxLocationEditWidget( QWidget * _parent, + KSpreadView * _view ) + : KComboBox( _parent, "KSpreadComboboxLocationEditWidget" ) +{ + m_locationWidget = new KSpreadLocationEditWidget( _parent, _view ); + setLineEdit( m_locationWidget ); + insertItem( "" ); + + QValueList::Iterator it; + QValueList area = _view->doc()->listArea(); + for ( it = area.begin(); it != area.end(); ++it ) + slotAddAreaName( (*it).ref_name); +} + +void KSpreadComboboxLocationEditWidget::slotAddAreaName( const QString &_name) +{ + insertItem( _name ); +} + +void KSpreadComboboxLocationEditWidget::slotRemoveAreaName( const QString &_name ) +{ + for ( int i = 0; istate() & ( Qt::AltButton | Qt::ControlButton ) ) + { + QLineEdit::keyPressEvent( _ev ); + // Never allow that keys are passed on to the parent. + _ev->accept(); + + return; + } + + // Handle some special keys here. Eve + switch( _ev->key() ) + { + case Key_Return: + case Key_Enter: + { + QString ltext = text(); + QString tmp = ltext.lower(); + QValueList::Iterator it; + QValueList area = m_pView->doc()->listArea(); + for ( it = area.begin(); it != area.end(); ++it ) + { + if ((*it).ref_name == tmp) + { + QString tmp = (*it).table_name; + tmp += "!"; + tmp += util_rangeName((*it).rect); + m_pView->canvasWidget()->gotoLocation( KSpreadRange(tmp, m_pView->doc()->map())); + return; + } + } + + // Set the cell component to uppercase: + // Table1!a1 -> Table1!A2 + int pos = ltext.find('!'); + if ( pos !=- 1 ) + tmp = ltext.left(pos)+ltext.mid(pos).upper(); + else + tmp = ltext.upper(); + + // Selection entered in location widget + if ( ltext.contains( ':' ) ) + m_pView->canvasWidget()->gotoLocation( KSpreadRange( tmp, m_pView->doc()->map() ) ); + // Location entered in location widget + else + { + KSpreadPoint point( tmp, m_pView->doc()->map()); + bool validName = true; + for (unsigned int i = 0; i < ltext.length(); ++i) + { + if (!ltext[i].isLetter()) + { + validName = false; + break; + } + } + if ( !point.isValid() && validName) + { + QRect rect( m_pView->selection() ); + KSpreadSheet * t = m_pView->activeTable(); + // set area name on current selection/cell + + m_pView->doc()->addAreaName(rect, ltext.lower(), + t->tableName()); + } + + if (!validName) + m_pView->canvasWidget()->gotoLocation( point ); + } + + // Set the focus back on the canvas. + m_pView->canvasWidget()->setFocus(); + _ev->accept(); + } + break; + // Escape pressed, restore original value + case Key_Escape: + // #### Torben says: This is duplicated code. Bad. + if ( m_pView->selectionInfo()->singleCellSelection() ) { + setText( KSpreadCell::columnName( m_pView->canvasWidget()->markerColumn() ) + + QString::number( m_pView->canvasWidget()->markerRow() ) ); + } else { + setText( KSpreadCell::columnName( m_pView->selection().left() ) + + QString::number( m_pView->selection().top() ) + + ":" + + KSpreadCell::columnName( m_pView->selection().right() ) + + QString::number( m_pView->selection().bottom() ) ); + } + m_pView->canvasWidget()->setFocus(); + _ev->accept(); + break; + default: + QLineEdit::keyPressEvent( _ev ); + // Never allow that keys are passed on to the parent. + _ev->accept(); + } +} + +/**************************************************************** + * + * KSpreadEditWidget + * The line-editor that appears above the table and allows to + * edit the cells content. + * + ****************************************************************/ + +KSpreadEditWidget::KSpreadEditWidget( QWidget *_parent, KSpreadCanvas *_canvas, + QButton *cancelButton, QButton *okButton ) + : QLineEdit( _parent, "KSpreadEditWidget" ) +{ + m_pCanvas = _canvas; + Q_ASSERT(m_pCanvas != NULL); + // Those buttons are created by the caller, so that they are inserted + // properly in the layout - but they are then managed here. + m_pCancelButton = cancelButton; + m_pOkButton = okButton; + + installEventFilter(m_pCanvas); + + if ( !m_pCanvas->doc()->isReadWrite() || !m_pCanvas->activeTable() ) + setEnabled( false ); + + QObject::connect( m_pCancelButton, SIGNAL( clicked() ), + this, SLOT( slotAbortEdit() ) ); + QObject::connect( m_pOkButton, SIGNAL( clicked() ), + this, SLOT( slotDoneEdit() ) ); + + setEditMode( false ); // disable buttons +} + +void KSpreadEditWidget::showEditWidget(bool _show) +{ + if (_show) + { + m_pCancelButton->show(); + m_pOkButton->show(); + show(); + } + else + { + m_pCancelButton->hide(); + m_pOkButton->hide(); + hide(); + } +} + +void KSpreadEditWidget::slotAbortEdit() +{ + m_pCanvas->deleteEditor( false /*discard changes*/ ); + // will take care of the buttons +} + +void KSpreadEditWidget::slotDoneEdit() +{ + m_pCanvas->deleteEditor( true /*keep changes*/ ); + // will take care of the buttons +} + +void KSpreadEditWidget::keyPressEvent ( QKeyEvent* _ev ) +{ + // Dont handle special keys and accelerators + if ( ( _ev->state() & ( Qt::AltButton | Qt::ControlButton ) ) + || ( _ev->state() & Qt::ShiftButton ) + || ( _ev->key() == Key_Shift ) + || ( _ev->key() == Key_Control ) ) + { + QLineEdit::keyPressEvent( _ev ); + _ev->accept(); + return; + } + + if ( !m_pCanvas->doc()->isReadWrite() ) + return; + + if ( !m_pCanvas->editor() ) + { + // Start editing the current cell + m_pCanvas->createEditor( KSpreadCanvas::CellEditor,false ); + } + KSpreadTextEditor * cellEditor = (KSpreadTextEditor*) m_pCanvas->editor(); + + switch ( _ev->key() ) + { + case Key_Down: + case Key_Up: + case Key_Return: + case Key_Enter: + cellEditor->setText( text()); + // Don't allow to start a chooser when pressing the arrow keys + // in this widget, since only up and down would work anyway. + // This is why we call slotDoneEdit now, instead of sending + // to the canvas. + //QApplication::sendEvent( m_pCanvas, _ev ); + slotDoneEdit(); + m_pCanvas->view()->updateEditWidget(); + _ev->accept(); + break; + case Key_F2: + cellEditor->setFocus(); + cellEditor->setText( text()); + cellEditor->setCursorPosition(cursorPosition()); + break; + default: + + QLineEdit::keyPressEvent( _ev ); + + setFocus(); + cellEditor->blockCheckChoose( TRUE ); + cellEditor->setText( text() ); + cellEditor->blockCheckChoose( FALSE ); + cellEditor->setCursorPosition( cursorPosition() ); + } +} + +void KSpreadEditWidget::setEditMode( bool mode ) +{ + m_pCancelButton->setEnabled(mode); + m_pOkButton->setEnabled(mode); +} + +void KSpreadEditWidget::focusOutEvent( QFocusEvent* ev ) +{ + //kdDebug(36001) << "EditWidget lost focus" << endl; + // See comment about setLastEditorWithFocus + m_pCanvas->setLastEditorWithFocus( KSpreadCanvas::EditWidget ); + + QLineEdit::focusOutEvent( ev ); +} + +void KSpreadEditWidget::setText( const QString& t ) +{ + if ( t == text() ) // Why this? (David) + return; + + QLineEdit::setText( t ); +} + #include "kspread_editors.moc" diff --git a/kspread/kspread_editors.h b/kspread/kspread_editors.h index 926141e2f34..ca8b70c7da4 100644 --- a/kspread/kspread_editors.h +++ b/kspread/kspread_editors.h @@ -1,82 +1,175 @@ +/* This file is part of the KDE project + + Copyright 1999-2004 The KSpread Team + + 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. +*/ + + #ifndef __kspread_editors_h__ #define __kspread_editors_h__ #include #include +#include + class KSpreadCell; class KSpreadCanvas; +class KSpreadView; + +class KSpreadLocationEditWidget; class QFont; +class QButton; class KLineEdit; + class KSpreadCellEditor : public QWidget { Q_OBJECT public: KSpreadCellEditor( KSpreadCell*, KSpreadCanvas* _parent = 0, const char* _name = 0 ); ~KSpreadCellEditor(); KSpreadCell* cell()const { return m_pCell; } virtual void handleKeyPressEvent( QKeyEvent* _ev ) = 0; virtual void setEditorFont(QFont const & font, bool updateSize) = 0; virtual QString text() const = 0; virtual void setText(QString text) = 0; virtual int cursorPosition() const = 0; virtual void setCursorPosition(int pos) = 0; // virtual void setFocus() = 0; virtual void insertFormulaChar(int c) = 0; virtual void cut(){}; virtual void paste(){}; virtual void copy(){}; KSpreadCanvas* canvas()const { return m_pCanvas; } private: KSpreadCell* m_pCell; KSpreadCanvas* m_pCanvas; }; class KSpreadTextEditor : public KSpreadCellEditor { Q_OBJECT public: KSpreadTextEditor( KSpreadCell*, KSpreadCanvas* _parent = 0, const char* _name = 0 ); ~KSpreadTextEditor(); virtual void handleKeyPressEvent( QKeyEvent* _ev ); virtual void setEditorFont(QFont const & font, bool updateSize); virtual QString text() const; virtual void setText(QString text); virtual int cursorPosition() const; virtual void setCursorPosition(int pos); // virtual void setFocus(); virtual void insertFormulaChar(int c); virtual void cut(); virtual void paste(); virtual void copy(); bool checkChoose(); void blockCheckChoose( bool b ) { m_blockCheck = b; } bool sizeUpdate() const { return m_sizeUpdate; } private slots: void slotTextChanged( const QString& text ); void slotCompletionModeChanged(KGlobalSettings::Completion _completion); protected: void resizeEvent( QResizeEvent* ); /** * Steals some key events from the QLineEdit and sends * it to the @ref KSpreadCancvas ( its parent ) instead. */ bool eventFilter( QObject* o, QEvent* e ); private: //QLineEdit* m_pEdit; KLineEdit* m_pEdit; bool m_blockCheck; bool m_sizeUpdate; uint m_length; int m_fontLength; }; +class KSpreadComboboxLocationEditWidget : public KComboBox +{ + Q_OBJECT +public: + KSpreadComboboxLocationEditWidget( QWidget *_parent, KSpreadView * _canvas ); + +public slots: + void slotAddAreaName( const QString & ); + void slotRemoveAreaName( const QString & ); + +private: + KSpreadLocationEditWidget *m_locationWidget; +}; + + + /** + * A widget that allows the user to enter an arbitrary + * cell location to goto or cell selection to highlight + */ +class KSpreadLocationEditWidget : public QLineEdit +{ + Q_OBJECT +public: + KSpreadLocationEditWidget( QWidget *_parent, KSpreadView * _canvas ); + KSpreadView * view() const { return m_pView;} +protected: + virtual void keyPressEvent( QKeyEvent * _ev ); +private: + KSpreadView * m_pView; +signals: + void gotoLocation( int, int ); +}; + +/** + * The widget that appears above the table and allows to + * edit the cells content. + */ +class KSpreadEditWidget : public QLineEdit +{ + Q_OBJECT +public: + KSpreadEditWidget( QWidget *parent, KSpreadCanvas *canvas, + QButton *cancelButton, QButton *okButton); + + virtual void setText( const QString& t ); + + // Go into edit mode (enable the buttons) + void setEditMode( bool mode ); + + void showEditWidget(bool _show); +public slots: + void slotAbortEdit(); + void slotDoneEdit(); + +protected: + virtual void keyPressEvent ( QKeyEvent* _ev ); + virtual void focusOutEvent( QFocusEvent* ev ); + +private: + QButton* m_pCancelButton; + QButton* m_pOkButton; + KSpreadCanvas* m_pCanvas; +}; + + #endif