kspread

kspread_editors.cc

00001 /* This file is part of the KDE project
00002 
00003    Copyright 1999-2006 The KSpread Team <koffice-devel@kde.org>
00004 
00005    This library is free software; you can redistribute it and/or
00006    modify it under the terms of the GNU Library General Public
00007    License as published by the Free Software Foundation; either
00008    version 2 of the License, or (at your option) any later version.
00009 
00010    This library is distributed in the hope that it will be useful,
00011    but WITHOUT ANY WARRANTY; without even the implied warranty of
00012    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00013    Library General Public License for more details.
00014 
00015    You should have received a copy of the GNU Library General Public License
00016    along with this library; see the file COPYING.LIB.  If not, write to
00017    the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
00018  * Boston, MA 02110-1301, USA.
00019 */
00020 
00021 #include "kspread_editors.h"
00022 #include "kspread_canvas.h"
00023 #include "kspread_cell.h"
00024 #include "kspread_doc.h"
00025 #include "selection.h"
00026 #include "kspread_sheet.h"
00027 #include "kspread_view.h"
00028 #include "kspread_util.h"
00029 #include "formula.h"
00030 #include "functions.h"
00031 
00032 #include <klistbox.h>
00033 
00034 #include <qapplication.h>
00035 #include <qlistbox.h>
00036 #include <qtimer.h>
00037 #include <qlabel.h>
00038 #include <qvbox.h>
00039 #include <qvaluelist.h>
00040 #include <private/qrichtext_p.h>
00041 
00042 //#include <klineedit.h>
00043 #include <ktextedit.h>
00044 #include <qapplication.h>
00045 #include <qbutton.h>
00046 #include <qfont.h>
00047 #include <qfontmetrics.h>
00048 #include <qregexp.h>
00049 #include <kdebug.h>
00050 
00051 using namespace KSpread;
00052 
00053 
00054 
00055 /*****************************************************************************
00056  *
00057  * FormulaEditorHighlighter
00058  *
00059  ****************************************************************************/
00060 
00061 namespace KSpread
00062 {
00063 
00064 class FormulaEditorHighlighter::Private
00065 {
00066 public:
00067   Private()
00068   {
00069     canvas = 0;
00070     tokens = Tokens();
00071     rangeCount = 0;
00072     rangeChanged = false;
00073   }
00074 
00075   // source for cell reference checking
00076   Canvas* canvas;
00077   Tokens tokens;
00078   uint rangeCount;
00079   bool rangeChanged;
00080 };
00081 
00082 
00083 FormulaEditorHighlighter::FormulaEditorHighlighter(QTextEdit* textEdit, Canvas* canvas)
00084   : QSyntaxHighlighter(textEdit)
00085 {
00086   d = new Private();
00087   d->canvas = canvas;
00088 }
00089 
00090 FormulaEditorHighlighter::~FormulaEditorHighlighter()
00091 {
00092   delete d;
00093 }
00094 
00095 const Tokens& FormulaEditorHighlighter::formulaTokens() const
00096 {
00097   return d->tokens;
00098 }
00099 
00100 int FormulaEditorHighlighter::highlightParagraph(const QString& text, int /* endStateOfLastPara */)
00101 {
00102   // reset syntax highlighting
00103   setFormat(0, text.length(), Qt::black);
00104 
00105   // save the old ones to identify range changes
00106   Tokens oldTokens = d->tokens;
00107 
00108   // interpret the text as formula
00109   // we accept invalid/incomplete formulas
00110   Formula f;
00111   d->tokens = f.scan(text);
00112 
00113   QFont editorFont = textEdit()->currentFont();
00114   QFont font;
00115 
00116   uint oldRangeCount = d->rangeCount;
00117 
00118   d->rangeCount = 0;
00119   QValueList<QColor> colors = d->canvas->choice()->colors();
00120   QValueList<Range> alreadyFoundRanges;
00121 
00122   for (uint i = 0; i < d->tokens.count(); ++i)
00123   {
00124     Token token = d->tokens[i];
00125     Token::Type type = token.type();
00126 
00127     switch (type)
00128     {
00129       case Token::Cell:
00130       case Token::Range:
00131         {
00132             // don't compare, if we have already found a change
00133             if (!d->rangeChanged && i < oldTokens.count() && token.text() != oldTokens[i].text())
00134             {
00135                 d->rangeChanged = true;
00136             }
00137 
00138             Range newRange( token.text() );
00139 
00140             if (!alreadyFoundRanges.contains(newRange))
00141             {
00142                 alreadyFoundRanges.append(newRange);
00143                 d->rangeCount++;
00144             }
00145             setFormat(token.pos() + 1, token.text().length(), colors[ alreadyFoundRanges.findIndex(newRange) % colors.size()] );
00146         }
00147         break;
00148       case Token::Boolean:     // True, False (also i18n-ized)
00149 /*        font = QFont(editorFont);
00150         font.setBold(true);
00151         setFormat(token.pos() + 1, token.text().length(), font);*/
00152         break;
00153       case Token::Identifier:   // function name or named area*/
00154 /*        font = QFont(editorFont);
00155         font.setBold(true);
00156         setFormat(token.pos() + 1, token.text().length(), font);*/
00157         break;
00158 
00159       case Token::Unknown:
00160       case Token::Integer:     // 14, 3, 1977
00161       case Token::Float:       // 3.141592, 1e10, 5.9e-7
00162       case Token::String:      // "KOffice", "The quick brown fox..."
00163       case Token::Operator:    // +, *, /, -
00164         {
00165             switch (token.asOperator())
00166             {
00167                 case Token::LeftPar:
00168                 case Token::RightPar:
00169                     //Check where this brace is in relation to the cursor and highlight it if necessary.
00170                     handleBrace( i );
00171                     break;
00172                 default:
00173                     break;
00174             }
00175         }
00176         break;
00177     }
00178   }
00179 
00180   if (oldRangeCount != d->rangeCount)
00181     d->rangeChanged = true;
00182 
00183   return 0;
00184 }
00185 
00186 void FormulaEditorHighlighter::handleBrace( uint index )
00187 {
00188   int cursorParagraph;
00189   int cursorPos;
00190   const Token& token = d->tokens.at( index );
00191 
00192   textEdit()->getCursorPosition( &cursorParagraph , &cursorPos );
00193 
00194   int distance = cursorPos-token.pos();
00195   int opType = token.asOperator();
00196   bool highlightBrace=false;
00197 
00198   //Check where the cursor is in relation to this left or right parenthesis token.
00199   //Only one pair of braces should be highlighted at a time, and if the cursor
00200   //is between two braces, the inner-most pair should be highlighted.
00201 
00202   if ( opType == Token::LeftPar )
00203   {
00204     //If cursor is directly to the left of this left brace, highlight it
00205     if ( distance == 1 )
00206       highlightBrace=true;
00207     else
00208         //Cursor is directly to the right of this left brace, highlight it unless
00209         //there is another left brace to the right (in which case that should be highlighted instead as it
00210         //is the inner-most brace)
00211         if (distance==2)
00212             if ( (index == d->tokens.count()-1) || ( d->tokens.at(index+1).asOperator() != Token::LeftPar) )
00213           highlightBrace=true;
00214 
00215   }
00216   else
00217   {
00218     //If cursor is directly to the right of this right brace, highlight it
00219     if ( distance == 2 )
00220       highlightBrace=true;
00221     else
00222         //Cursor is directly to the left of this right brace, so highlight it unless
00223         //there is another right brace to the left (in which case that should be highlighted instead as it
00224         //is the inner-most brace)
00225       if ( distance == 1 )
00226         if ( (index == 0) || (d->tokens.at(index-1).asOperator() != Token::RightPar) )
00227           highlightBrace=true;
00228   }
00229 
00230   if (highlightBrace)
00231   {
00232     QFont font = QFont( textEdit()->currentFont() );
00233     font.setBold(true);
00234     setFormat(token.pos() + 1, token.text().length(), font);
00235 
00236     int matching = findMatchingBrace( index );
00237 
00238     if (matching != -1)
00239     {
00240       Token matchingBrace = d->tokens.at(matching);
00241       setFormat( matchingBrace.pos() + 1 , matchingBrace.text().length() , font);
00242     }
00243   }
00244 }
00245 
00246 int FormulaEditorHighlighter::findMatchingBrace(int pos)
00247 {
00248     int depth=0;
00249     int step=0;
00250 
00251     Tokens tokens = d->tokens;
00252 
00253     //If this is a left brace we need to step forwards through the text to find the matching right brace,
00254     //otherwise, it is a right brace so we need to step backwards through the text to find the matching left
00255     //brace.
00256     if (tokens.at(pos).asOperator() == Token::LeftPar)
00257         step = 1;
00258     else
00259         step = -1;
00260 
00261     for (int index=pos ; (index >= 0) && (index < (int) tokens.count() ) ; index += step  )
00262     {
00263         if (tokens.at(index).asOperator() == Token::LeftPar)
00264             depth++;
00265         if (tokens.at(index).asOperator() == Token::RightPar)
00266             depth--;
00267 
00268         if (depth == 0)
00269         {
00270             return index;
00271         }
00272     }
00273 
00274     return -1;
00275 }
00276 
00277 uint FormulaEditorHighlighter::rangeCount() const
00278 {
00279   return d->rangeCount;
00280 }
00281 
00282 bool FormulaEditorHighlighter::rangeChanged() const
00283 {
00284   return d->rangeChanged;
00285 }
00286 
00287 void FormulaEditorHighlighter::resetRangeChanged()
00288 {
00289     d->rangeChanged=false;
00290 }
00291 
00292 } // namespace KSpread
00293 
00294 
00295 
00296 /*****************************************************************************
00297  *
00298  * FunctionCompletion
00299  *
00300  ****************************************************************************/
00301 
00302 class FunctionCompletion::Private
00303 {
00304 public:
00305   CellEditor* editor;
00306   QVBox *completionPopup;
00307   KListBox *completionListBox;
00308   QLabel* hintLabel;
00309 };
00310 
00311 FunctionCompletion::FunctionCompletion( CellEditor* editor ):
00312 QObject( editor )
00313 {
00314   d = new Private;
00315   d->editor = editor;
00316   d->hintLabel = 0;
00317 
00318   d->completionPopup = new QVBox( editor->topLevelWidget(), 0, WType_Popup );
00319   d->completionPopup->setFrameStyle( QFrame::Box | QFrame::Plain );
00320   d->completionPopup->setLineWidth( 1 );
00321   d->completionPopup->installEventFilter( this );
00322   d->completionPopup->setSizePolicy( QSizePolicy::Expanding, QSizePolicy::Minimum);
00323 
00324   d->completionListBox = new KListBox( d->completionPopup );
00325   d->completionPopup->setFocusProxy( d->completionListBox );
00326   d->completionListBox->setFrameStyle( QFrame::NoFrame );
00327   d->completionListBox->setVariableWidth( true );
00328   d->completionListBox->installEventFilter( this );
00329   connect( d->completionListBox, SIGNAL(selected(const QString&)), this,
00330     SLOT(itemSelected(const QString&)) );
00331   connect( d->completionListBox, SIGNAL(highlighted(const QString&)), this,
00332     SLOT(itemSelected(const QString&)) );
00333 
00334   d->hintLabel = new QLabel( 0, "autocalc", Qt::WStyle_StaysOnTop |
00335     Qt::WStyle_Customize | Qt::WStyle_NoBorder | Qt::WStyle_Tool |  Qt::WX11BypassWM );
00336   d->hintLabel->setFrameStyle( QFrame::Plain | QFrame::Box );
00337   d->hintLabel->setPalette( QToolTip::palette() );
00338   d->hintLabel->hide();
00339 }
00340 
00341 FunctionCompletion::~FunctionCompletion()
00342 {
00343       delete d->hintLabel;
00344       delete d;
00345 }
00346 
00347 void FunctionCompletion::itemSelected( const QString& item )
00348 {
00349     KSpread::FunctionDescription* desc;
00350     desc = KSpread::FunctionRepository::self()->functionInfo(item);
00351     if(!desc)
00352     {
00353         d->hintLabel->hide();
00354         return;
00355     }
00356 
00357     QString helpText = desc->helpText()[0];
00358     if( helpText.isEmpty() )
00359     {
00360         d->hintLabel->hide();
00361         return;
00362     }
00363 
00364     helpText.append("</qt>").prepend("<qt>");
00365     d->hintLabel->setText( helpText );
00366     d->hintLabel->adjustSize();
00367 
00368     // reposition nicely
00369     QPoint pos = d->editor->mapToGlobal( QPoint( d->editor->width(), 0 ) );
00370     pos.setY( pos.y() - d->hintLabel->height() - 1 );
00371     d->hintLabel->move( pos );
00372     d->hintLabel->show();
00373     d->hintLabel->raise();
00374 
00375     // do not show it forever
00376     //QTimer::singleShot( 5000, d->hintLabel, SLOT( hide()) );
00377 }
00378 
00379 bool FunctionCompletion::eventFilter( QObject *obj, QEvent *ev )
00380 {
00381     if ( obj == d->completionPopup || obj == d->completionListBox )
00382     {
00383       if ( ev->type() == QEvent::KeyPress )
00384       {
00385               QKeyEvent *ke = (QKeyEvent*)ev;
00386               if ( ke->key() == Key_Enter || ke->key() == Key_Return  )
00387               {
00388                   doneCompletion();
00389                   return true;
00390               }
00391               else if ( ke->key() == Key_Left || ke->key() == Key_Right ||
00392               ke->key() == Key_Up || ke->key() == Key_Down ||
00393               ke->key() == Key_Home || ke->key() == Key_End ||
00394               ke->key() == Key_Prior || ke->key() == Key_Next )
00395                   return false;
00396 
00397               d->hintLabel->hide();
00398               d->completionPopup->close();
00399               d->editor->setFocus();
00400               QApplication::sendEvent( d->editor, ev );
00401               return true;
00402       }
00403 
00404       if ( ev->type() == QEvent::MouseButtonDblClick )
00405       {
00406           doneCompletion();
00407           return true;
00408       }
00409   }
00410 
00411   return false;
00412 }
00413 
00414 void FunctionCompletion::doneCompletion()
00415 {
00416   d->hintLabel->hide();
00417   d->completionPopup->close();
00418   d->editor->setFocus();
00419   emit selectedCompletion( d->completionListBox->currentText() );
00420 }
00421 
00422 void FunctionCompletion::showCompletion( const QStringList &choices )
00423 {
00424   if( !choices.count() ) return;
00425 
00426   d->completionListBox->clear();
00427   for( unsigned i = 0; i < choices.count(); i++ )
00428     new QListBoxText( (QListBox*)d->completionListBox, choices[i] );
00429   d->completionListBox->setCurrentItem( 0 );
00430 
00431   // size of the pop-up
00432   d->completionPopup->setMaximumHeight( 100 );
00433   d->completionPopup->resize( d->completionListBox->sizeHint() +
00434     QSize( d->completionListBox->verticalScrollBar()->width() + 4,
00435         d->completionListBox->horizontalScrollBar()->height() + 4 ) );
00436   int h = d->completionListBox->height();
00437   int w = d->completionListBox->width();
00438 
00439   QPoint pos = d->editor->globalCursorPosition();
00440 
00441   // if popup is partially invisible, move to other position
00442   // FIXME check it if it works in Xinerama multihead
00443   int screen_num = QApplication::desktop()->screenNumber( d->completionPopup );
00444   QRect screen = QApplication::desktop()->screenGeometry( screen_num );
00445   if( pos.y() + h > screen.y()+screen.height() )
00446     pos.setY( pos.y() - h - d->editor->height() );
00447   if( pos.x() + w > screen.x()+screen.width() )
00448     pos.setX(  screen.x()+screen.width() - w );
00449 
00450   d->completionPopup->move( pos );
00451   d->completionListBox->setFocus();
00452   d->completionPopup->show();
00453 }
00454 
00455 
00456 
00457 /****************************************************************************
00458  *
00459  * CellEditor
00460  *
00461  ****************************************************************************/
00462 
00463 class CellEditor::Private
00464 {
00465 public:
00466   Cell*                     cell;
00467   Canvas*                   canvas;
00468   KTextEdit*                textEdit;
00469   FormulaEditorHighlighter* highlighter;
00470   FunctionCompletion*       functionCompletion;
00471   QTimer*                   functionCompletionTimer;
00472 
00473   QPoint globalCursorPos;
00474 
00475   bool captureAllKeyEvents : 1;
00476   bool checkChoice         : 1;
00477   bool updateChoice        : 1;
00478   bool updatingChoice      : 1;
00479 
00480   uint length;
00481   uint fontLength;
00482   uint length_namecell;
00483   uint length_text;
00484   uint currentToken;
00485   uint rangeCount;
00486 };
00487 
00488 
00489 CellEditor::CellEditor( Cell* _cell, Canvas* _parent, bool captureAllKeyEvents, const char* _name )
00490   : QWidget( _parent, _name )
00491 {
00492   d = new Private();
00493   d->cell = _cell;
00494   d->canvas = _parent;
00495   d->textEdit = new KTextEdit(this);
00496   d->globalCursorPos = QPoint();
00497   d->captureAllKeyEvents = captureAllKeyEvents;
00498   d->checkChoice = true;
00499   d->updateChoice = true;
00500   d->updatingChoice = false;
00501   d->length = 0;
00502   d->fontLength = 0;
00503   d->length_namecell = 0;
00504   d->length_text = 0;
00505   d->currentToken = 0;
00506   d->rangeCount = 0;
00507 
00508 //TODO - Get rid of QTextEdit margins, this doesn't seem easily possible in Qt 3.3, so a job for Qt 4 porting.
00509 
00510   d->textEdit->setHScrollBarMode(QScrollView::AlwaysOff);
00511   d->textEdit->setVScrollBarMode(QScrollView::AlwaysOff);
00512   d->textEdit->setFrameStyle(QFrame::NoFrame);
00513   d->textEdit->setLineWidth(0);
00514   d->textEdit->installEventFilter( this );
00515 
00516   d->highlighter = new FormulaEditorHighlighter(d->textEdit, _parent);
00517 
00518   d->functionCompletion = new FunctionCompletion( this );
00519   d->functionCompletionTimer = new QTimer( this );
00520   connect( d->functionCompletion, SIGNAL( selectedCompletion( const QString& ) ),
00521     SLOT( functionAutoComplete( const QString& ) ) );
00522   connect( d->textEdit, SIGNAL( textChanged() ), SLOT( checkFunctionAutoComplete() ) );
00523   connect( d->functionCompletionTimer, SIGNAL( timeout() ),
00524     SLOT( triggerFunctionAutoComplete() ) );
00525 
00526   if (!cell()->format()->multiRow(cell()->column(),cell()->row()))
00527     d->textEdit->setWordWrap(QTextEdit::NoWrap);
00528   else
00529     d->textEdit->setWrapPolicy(QTextEdit::AtWordOrDocumentBoundary);
00530 
00531 //TODO - Custom KTextEdit class which supports text completion
00532 /*
00533   d->textEdit->setFrame( false );
00534   d->textEdit->setCompletionMode((KGlobalSettings::Completion)canvas()->view()->doc()->completionMode()  );
00535   d->textEdit->setCompletionObject( &canvas()->view()->doc()->completion(),true );
00536 */
00537   setFocusProxy( d->textEdit );
00538 
00539   connect( d->textEdit, SIGNAL( cursorPositionChanged(int,int) ), this, SLOT (slotCursorPositionChanged(int,int)));
00540   connect( d->textEdit, SIGNAL( cursorPositionChanged(QTextCursor*) ), this, SLOT (slotTextCursorChanged(QTextCursor*)));
00541   connect( d->textEdit, SIGNAL( textChanged() ), this, SLOT( slotTextChanged() ) );
00542 
00543 // connect( d->textEdit, SIGNAL(completionModeChanged( KGlobalSettings::Completion )),this,SLOT (slotCompletionModeChanged(KGlobalSettings::Completion)));
00544 
00545   // A choose should always start at the edited cell
00546 //  canvas()->setChooseMarkerRow( canvas()->markerRow() );
00547 //  canvas()->setChooseMarkerColumn( canvas()->markerColumn() );
00548 
00549   // set font size according to zoom factor
00550   QFont font( _cell->format()->font() );
00551   font.setPointSizeFloat( 0.01 * _parent->doc()->zoom() * font.pointSizeFloat() );
00552   d->textEdit->setFont( font );
00553 
00554   if (d->fontLength == 0)
00555   {
00556     QFontMetrics fm( d->textEdit->font() );
00557     d->fontLength = fm.width('x');
00558   }
00559 }
00560 
00561 CellEditor::~CellEditor()
00562 {
00563   canvas()->endChoose();
00564 
00565   delete d->highlighter;
00566   delete d->functionCompletion;
00567   delete d->functionCompletionTimer;
00568   delete d;
00569 }
00570 
00571 Cell* CellEditor::cell() const
00572 {
00573   return d->cell;
00574 }
00575 
00576 Canvas* CellEditor::canvas() const
00577 {
00578   return d->canvas;
00579 }
00580 
00581 QPoint CellEditor::globalCursorPosition() const
00582 {
00583   return d->globalCursorPos;
00584 }
00585 
00586 void CellEditor::checkFunctionAutoComplete()
00587 {
00588   d->functionCompletionTimer->stop();
00589   d->functionCompletionTimer->start( 2000, true );
00590 }
00591 
00592 void CellEditor::triggerFunctionAutoComplete()
00593 {
00594   // tokenize the expression (don't worry, this is very fast)
00595   int para = 0, curPos = 0;
00596   d->textEdit->getCursorPosition( &para, &curPos );
00597   QString subtext = d->textEdit->text().left( curPos );
00598 
00599   KSpread::Formula f;
00600   KSpread::Tokens tokens = f.scan( subtext );
00601   if( !tokens.valid() ) return;
00602   if( tokens.count()<1 ) return;
00603 
00604   KSpread::Token lastToken = tokens[ tokens.count()-1 ];
00605 
00606   // last token must be an identifier
00607   if( !lastToken.isIdentifier() ) return;
00608   QString id = lastToken.text();
00609   if( id.length() < 1 ) return;
00610 
00611   // find matches in function names
00612   QStringList fnames = KSpread::FunctionRepository::self()->functionNames();
00613   QStringList choices;
00614   for( unsigned i=0; i<fnames.count(); i++ )
00615     if( fnames[i].startsWith( id, false ) )
00616       choices.append( fnames[i] );
00617   choices.sort();
00618 
00619   // no match, don't bother with completion
00620   if( !choices.count() ) return;
00621 
00622   // single perfect match, no need to give choices
00623   if( choices.count()==1 )
00624     if( choices[0].lower() == id.lower() )
00625       return;
00626 
00627   // present the user with completion choices
00628   d->functionCompletion->showCompletion( choices );
00629 }
00630 
00631 void CellEditor::functionAutoComplete( const QString& item )
00632 {
00633   if( item.isEmpty() ) return;
00634 
00635   int para = 0, curPos = 0;
00636   d->textEdit->getCursorPosition( &para, &curPos );
00637   QString subtext = text().left( curPos );
00638 
00639   KSpread::Formula f;
00640   KSpread::Tokens tokens = f.scan( subtext );
00641   if( !tokens.valid() ) return;
00642   if( tokens.count()<1 ) return;
00643 
00644   KSpread::Token lastToken = tokens[ tokens.count()-1 ];
00645   if( !lastToken.isIdentifier() ) return;
00646 
00647   d->textEdit->blockSignals( true );
00648   d->textEdit->setSelection( 0, lastToken.pos()+1, 0, lastToken.pos()+lastToken.text().length()+1 );
00649   d->textEdit->insert( item );
00650   d->textEdit->blockSignals( false );
00651 }
00652 
00653 void CellEditor::slotCursorPositionChanged(int /* para */, int pos)
00654 {
00655 //   kdDebug() << k_funcinfo << endl;
00656 
00657   // TODO Stefan: optimize this function!
00658 
00659   // turn choose mode on/off
00660   if (!checkChoice())
00661     return;
00662 
00663   d->highlighter->rehighlight();
00664 
00665   Tokens tokens = d->highlighter->formulaTokens();
00666   uint rangeCounter = 0;
00667   uint currentRange = 0;
00668   uint regionStart = 0;
00669   uint regionEnd = 0;
00670   bool lastWasASemicolon = false;
00671   d->currentToken = 0;
00672   uint rangeCount = d->highlighter->rangeCount();
00673   d->rangeCount = rangeCount;
00674 
00675   Token token;
00676   Token::Type type;
00677   // search the current token
00678   // determine the subregion number, btw
00679   for (uint i = 0; i < tokens.count(); ++i)
00680   {
00681     if (tokens[i].pos() >= pos - 1) // without '='
00682     {
00683 /*      kdDebug() << "token.pos >= cursor.pos" << endl;*/
00684       type = tokens[i].type();
00685       if (type == Token::Cell || type == Token::Range)
00686       {
00687         if (lastWasASemicolon)
00688         {
00689           regionEnd = rangeCounter++;
00690           lastWasASemicolon = false;
00691           continue;
00692         }
00693       }
00694       if (type == Token::Operator && tokens[i].asOperator() == Token::Semicolon)
00695       {
00696         lastWasASemicolon = true;
00697         continue;
00698       }
00699       lastWasASemicolon = false;
00700       break;
00701     }
00702     token = tokens[i];
00703     d->currentToken = i;
00704 
00705     type = token.type();
00706     if (type == Token::Cell || type == Token::Range)
00707     {
00708       if (!lastWasASemicolon)
00709       {
00710         regionStart = rangeCounter;
00711       }
00712       regionEnd = rangeCounter;
00713       currentRange = rangeCounter++;
00714     }
00715     // semicolons are use as deliminiters in regions
00716     if (type == Token::Operator)
00717     {
00718       if (token.asOperator() == Token::Semicolon)
00719       {
00720         lastWasASemicolon = true;
00721       }
00722       else
00723       {
00724         lastWasASemicolon = false;
00725         // set the region start to the next element
00726         regionStart = currentRange + 1;
00727         regionEnd = regionStart - 1; // len = 0
00728       }
00729     }
00730   }
00731 
00732 //   kdDebug() << "regionStart = " << regionStart/* << endl*/
00733 //             << ", regionEnd = " << regionEnd/* << endl*/
00734 //             << ", currentRange = " << currentRange << endl;
00735 
00736   d->canvas->choice()->setActiveElement(currentRange);
00737   d->canvas->choice()->setActiveSubRegion(regionStart, regionEnd-regionStart+1);
00738 
00739   // triggered by keyboard action?
00740   if (!d->updatingChoice)
00741   {
00742     if (d->highlighter->rangeChanged())
00743     {
00744       d->highlighter->resetRangeChanged();
00745 
00746       d->canvas->doc()->emitBeginOperation();
00747       setUpdateChoice(false);
00748 
00749       Tokens tokens = d->highlighter->formulaTokens();
00750       d->canvas->choice()->update(); // set the old one dirty
00751       d->canvas->choice()->clear();
00752       Region tmpRegion;
00753       Region::ConstIterator it;
00754 
00755       //A list of regions which have already been highlighted on the spreadsheet.
00756       //This is so that we don't end up highlighting the same region twice in two different
00757       //colours.
00758       QValueList<Region> alreadyUsedRegions;
00759 
00760       for (uint i = 0; i < tokens.count(); ++i)
00761       {
00762         Token token = tokens[i];
00763         Token::Type type = token.type();
00764         if (type == Token::Cell || type == Token::Range)
00765         {
00766           Region region(d->canvas->view(), token.text());
00767           it = region.constBegin();
00768 
00769           if (!alreadyUsedRegions.contains(region))
00770           {
00771             QRect r=(*it)->rect();
00772 
00773             if (d->canvas->choice()->isEmpty())
00774                 d->canvas->choice()->initialize((*it)->rect(), (*it)->sheet());
00775             else
00776                 d->canvas->choice()->extend((*it)->rect(), (*it)->sheet());
00777 
00778             alreadyUsedRegions.append(region);
00779           }
00780         }
00781       }
00782       setUpdateChoice(true);
00783       d->canvas->doc()->emitEndOperation(*d->canvas->choice());
00784     }
00785   }
00786 }
00787 
00788 void CellEditor::slotTextCursorChanged(QTextCursor* cursor)
00789 {
00790   QTextStringChar *chr = cursor->paragraph()->at( cursor->index() );
00791   int h = cursor->paragraph()->lineHeightOfChar( cursor->index() );
00792   int x = cursor->paragraph()->rect().x() + chr->x;
00793   int y, dummy;
00794   cursor->paragraph()->lineHeightOfChar( cursor->index(), &dummy, &y );
00795   y += cursor->paragraph()->rect().y();
00796 
00797   d->globalCursorPos = d->textEdit->mapToGlobal( d->textEdit-> contentsToViewport( QPoint( x, y + h ) ) );
00798 }
00799 
00800 void CellEditor::cut()
00801 {
00802   d->textEdit->cut();
00803 }
00804 
00805 void CellEditor::paste()
00806 {
00807   d->textEdit->paste();
00808 }
00809 
00810 void CellEditor::copy()
00811 {
00812   d->textEdit->copy();
00813 }
00814 
00815 void CellEditor::setEditorFont(QFont const & font, bool updateSize)
00816 {
00817   QFont tmpFont( font );
00818   tmpFont.setPointSizeFloat( 0.01 * canvas()->doc()->zoom() * tmpFont.pointSizeFloat() );
00819   d->textEdit->setFont( tmpFont );
00820 
00821   if (updateSize)
00822   {
00823     QFontMetrics fm( d->textEdit->font() );
00824     d->fontLength = fm.width('x');
00825 
00826     int mw = fm.width( d->textEdit->text() ) + d->fontLength;
00827     // don't make it smaller: then we would have to repaint the obscured cells
00828     if (mw < width())
00829       mw = width();
00830 
00831     int mh = fm.height();
00832     if (mh < height())
00833       mh = height();
00834 
00835     setGeometry(x(), y(), mw, mh);
00836   }
00837 }
00838 
00839 void CellEditor::slotCompletionModeChanged(KGlobalSettings::Completion _completion)
00840 {
00841   canvas()->view()->doc()->setCompletionMode( _completion );
00842 }
00843 
00844 void CellEditor::slotTextChanged()
00845 {
00846 //   kdDebug() << k_funcinfo << endl;
00847 
00848   //FIXME - text() may return richtext?
00849   QString t = text();
00850 
00851   if (t.length() > d->length)
00852   {
00853     d->length = t.length();
00854 
00855   QFontMetrics fm(d->textEdit->font());
00856   // - requiredWidth = width of text plus some spacer characters
00857   int requiredWidth = fm.width(t) + (2*fm.width('x'));
00858 
00859   //For normal single-row cells, the text editor must be expanded horizontally to
00860   //allow the text to fit if the new text is too wide
00861   //For multi-row (word-wrap enabled) cells, the text editor must expand vertically to
00862   //allow for new rows of text & the width of the text editor is not affected
00863   if (d->textEdit->wordWrap() == QTextEdit::NoWrap)
00864   {
00865     if (requiredWidth > width())
00866     {
00867       if (t.isRightToLeft())
00868       {
00869         setGeometry(x() - requiredWidth + width(), y(), requiredWidth,height());
00870       }
00871       else
00872       {
00873         setGeometry(x(), y(), requiredWidth,height());
00874       }
00875     }
00876   }
00877   else
00878   {
00879     int requiredHeight = d->textEdit->heightForWidth(width());
00880 
00881     if (requiredHeight > height())
00882     {
00883       setGeometry(x(), y(), width(), requiredHeight);
00884     }
00885   }
00886 
00887    /* // allocate more space than needed. Otherwise it might be too slow
00888     d->length = t.length();
00889 
00890     // Too slow for long texts
00891     // QFontMetrics fm( d->textEdit->font() );
00892     //  int mw = fm.width( t ) + fm.width('x');
00893     int mw = d->fontLength * d->length;
00894 
00895     if (mw < width())
00896       mw = width();
00897 
00898     if (t.isRightToLeft())
00899       setGeometry(x() - mw + width(), y(), mw, height());
00900     else
00901       setGeometry(x(), y(), mw, height());
00902 
00903     d->length -= 2; */
00904   }
00905 
00906   if ( (cell()->formatType()) == Percentage_format )
00907   {
00908     if ( (t.length() == 1) && t[0].isDigit() )
00909     {
00910       QString tmp = t + " %";
00911       d->textEdit->setText(tmp);
00912       d->textEdit->setCursorPosition(0,1);
00913       return;
00914     }
00915   }
00916 
00917   canvas()->view()->editWidget()->setText( t );
00918   // canvas()->view()->editWidget()->setCursorPosition( d->textEdit->cursorPosition() );
00919 }
00920 
00921 void CellEditor::setCheckChoice(bool state)
00922 {
00923   d->checkChoice = state;
00924 }
00925 
00926 bool CellEditor::checkChoice()
00927 {
00928   if (!d->checkChoice)
00929     return false;
00930 
00931 //   // prevent recursion
00932 //   d->checkChoice = false; // TODO nescessary?
00933 
00934   d->length_namecell = 0;
00935   d->currentToken = 0;
00936 
00937   QString text = d->textEdit->text();
00938   if ( text[0] != '=' )
00939   {
00940     canvas()->setChooseMode(false);
00941   }
00942   else
00943   {
00944     int para, cur;
00945     d->textEdit->getCursorPosition(&para, &cur);
00946 
00947     Tokens tokens = d->highlighter->formulaTokens();
00948 
00949     // empty formula?
00950     if (tokens.count() < 1)
00951     {
00952       canvas()->startChoose();
00953     }
00954     else
00955     {
00956       Token token;
00957       for (uint i = 0; i < tokens.count(); ++i)
00958       {
00959         if (tokens[i].pos() >= cur - 1) // without '='
00960         {
00961           break;
00962         }
00963         token = tokens[i];
00964         d->currentToken = i;
00965       }
00966 
00967       Token::Type type = token.type();
00968       if (type == Token::Operator && token.asOperator() != Token::RightPar)
00969       {
00970         canvas()->setChooseMode(true);
00971       }
00972       else if (type == Token::Cell || type == Token::Range)
00973       {
00974         d->length_namecell = token.text().length();
00975         canvas()->setChooseMode(true);
00976       }
00977       else
00978       {
00979         canvas()->setChooseMode(false);
00980       }
00981     }
00982   }
00983 
00984 //   d->checkChoice = true;
00985 
00986   return true;
00987 }
00988 
00989 void CellEditor::setUpdateChoice(bool state)
00990 {
00991   d->updateChoice = state;
00992 }
00993 
00994 void CellEditor::updateChoice()
00995 {
00996 //   kdDebug() << k_funcinfo << endl;
00997 
00998   if (!d->updateChoice)
00999     return;
01000 
01001 //   // prevent recursion
01002 //   d->updateChoice = false; // TODO nescessary?
01003   d->updatingChoice = true;
01004 
01005   Selection* choice = d->canvas->choice();
01006 
01007   if (choice->isEmpty())
01008     return;
01009 
01010   if (!choice->activeElement())
01011     return;
01012 
01013   // only one element TODO
01014   if (++choice->constBegin() == choice->constEnd())
01015   {
01016   }
01017 
01018   QString name_cell = choice->activeSubRegionName();
01019 
01020   Tokens tokens = d->highlighter->formulaTokens();
01021   uint start = 1;
01022   uint length = 0;
01023   if (!tokens.empty())
01024   {
01025     Token token = tokens[d->currentToken];
01026     Token::Type type = token.type();
01027     if (type == Token::Cell || type == Token::Range)
01028     {
01029       start = token.pos() + 1; // don't forget the '='!
01030       length = token.text().length();
01031     }
01032     else
01033     {
01034       start = token.pos() + token.text().length() + 1;
01035     }
01036   }
01037 
01038   d->length_namecell = name_cell.length();
01039   d->length_text = text().length();
01040     //kdDebug(36001) << "updateChooseMarker2 len=" << d->length_namecell << endl;
01041 
01042   QString oldText = text();
01043   QString newText = oldText.left(start) + name_cell + oldText.right(d->length_text - start - length);
01044 
01045   setCheckChoice( false );
01046   setText( newText );
01047   setCheckChoice( true );
01048   setCursorPosition( start + d->length_namecell );
01049 
01050   d->canvas->view()->editWidget()->setText( newText );
01051     //kdDebug(36001) << "old=" << old << " len=" << d->length_namecell << " pos=" << pos << endl;
01052 
01053 //   d->updateChoice = false;
01054   d->updatingChoice = false;
01055 }
01056 
01057 void CellEditor::resizeEvent( QResizeEvent* )
01058 {
01059     d->textEdit->setGeometry( 0, 0, width(), height() );
01060 }
01061 
01062 void CellEditor::handleKeyPressEvent( QKeyEvent * _ev )
01063 {
01064   if (_ev->key() == Qt::Key_F4)
01065   {
01066     if (d->textEdit == 0)
01067     {
01068       QApplication::sendEvent( d->textEdit, _ev );
01069       return;
01070     }
01071 
01072     QRegExp exp("(\\$?)([a-zA-Z]+)(\\$?)([0-9]+)$");
01073 
01074     int para,cur;
01075     d->textEdit->getCursorPosition(&para,&cur);
01076    // int cur = d->textEdit->cursorPosition();
01077     QString tmp, tmp2;
01078     int n = -1;
01079 
01080     // this is ugly, and sort of hack
01081     // FIXME rewrite to use the real Tokenizer
01082     unsigned i;
01083     for( i = 0; i < 10; i++ )
01084     {
01085       tmp =  d->textEdit->text().left( cur+i );
01086       tmp2 = d->textEdit->text().right( d->textEdit->text().length() - cur - i );
01087 
01088       n = exp.search(tmp);
01089       if( n >= 0 ) break;
01090     }
01091 
01092     if (n == -1) return;
01093 
01094     QString newPart;
01095     if ((exp.cap(1) == "$") && (exp.cap(3) == "$"))
01096       newPart = "$" + exp.cap(2) + exp.cap(4);
01097     else if ((exp.cap(1) != "$") && (exp.cap(3) != "$"))
01098       newPart = "$" + exp.cap(2) + "$" + exp.cap(4);
01099     else if ((exp.cap(1) == "$") && (exp.cap(3) != "$"))
01100       newPart = exp.cap(2) + "$" + exp.cap(4);
01101     else if ((exp.cap(1) != "$") && (exp.cap(3) == "$"))
01102       newPart = exp.cap(2) + exp.cap(4);
01103 
01104     QString newString = tmp.left(n);
01105     newString += newPart;
01106     cur = newString.length() - i;
01107     newString += tmp2;
01108 
01109     d->textEdit->setText(newString);
01110     d->textEdit->setCursorPosition( 0, cur );
01111 
01112     _ev->accept();
01113 
01114     return;
01115   }
01116 
01117   // Send the key event to the KLineEdit
01118   QApplication::sendEvent( d->textEdit, _ev );
01119 }
01120 
01121 void CellEditor::handleIMEvent( QIMEvent * _ev )
01122 {
01123     // send the IM event to the KLineEdit
01124     QApplication::sendEvent( d->textEdit, _ev );
01125 }
01126 
01127 QString CellEditor::text() const
01128 {
01129     return d->textEdit->text();
01130 }
01131 
01132 void CellEditor::setText(QString text)
01133 {
01134   d->textEdit->setText(text);
01135   //Usability : It is usually more convenient if the cursor is positioned at the end of the text so it can
01136   //be quickly deleted using the backspace key
01137 
01138   //This also ensures that the caret is sized correctly for the text
01139   d->textEdit->setCursorPosition(0,text.length());
01140 
01141     if (d->fontLength == 0)
01142     {
01143       QFontMetrics fm( d->textEdit->font() );
01144       d->fontLength = fm.width('x');
01145     }
01146 }
01147 
01148 int CellEditor::cursorPosition() const
01149 {
01150   int para,cur;
01151   d->textEdit->getCursorPosition(&para,&cur);
01152   return cur;
01153    // return d->textEdit->cursorPosition();
01154 }
01155 
01156 void CellEditor::setCursorPosition( int pos )
01157 {
01158     d->textEdit->setCursorPosition(0,pos);
01159     canvas()->view()->editWidget()->setCursorPosition( pos );
01160 }
01161 
01162 bool CellEditor::eventFilter( QObject* o, QEvent* e )
01163 {
01164     // Only interested in KTextEdit
01165     if ( o != d->textEdit )
01166         return false;
01167     if ( e->type() == QEvent::FocusOut )
01168     {
01169         canvas()->setLastEditorWithFocus( Canvas::CellEditor );
01170         return false;
01171     }
01172 
01173     if ( e->type() == QEvent::KeyPress || e->type() == QEvent::KeyRelease )
01174     {
01175         QKeyEvent* k = (QKeyEvent*)e;
01176         if ( !( k->state() & Qt::ShiftButton )|| canvas()->chooseMode())
01177         {
01178           //If the user presses the return key to finish editing this cell, choose mode must be turned off first
01179           //otherwise it will merely select a different cell
01180           if (k->key() == Key_Return || k->key() == Key_Enter)
01181           {
01182             kdDebug() << "CellEditor::eventFilter: canvas()->endChoose();" << endl;
01183             canvas()->endChoose();
01184           }
01185 
01186           //NB - Added check for Key_Return when migrating text edit from KLineEdit to KTextEdit, since
01187           //normal behaviour for KTextEdit is to swallow return key presses
01188           if ( k->key() == Key_Up || k->key() == Key_Down ||
01189                 k->key() == Key_Next || k->key() == Key_Prior ||
01190                 k->key() == Key_Escape || k->key() == Key_Tab ||
01191                 k->key() == Key_Return || k->key() == Key_Enter)
01192           {
01193               // Send directly to canvas
01194               QApplication::sendEvent( parent(), e );
01195               return true;
01196           }
01197         }
01198         // End choosing. May be restarted by CellEditor::slotTextChanged
01199         if ( e->type() == QEvent::KeyPress && !k->text().isEmpty() )
01200         {
01201           canvas()->setChooseMode(false);
01202         }
01203         // forward Left/Right keys - so that pressing left/right in this
01204         // editor leaves editing mode ... otherwise editing is annoying
01205         // left/right arrows still work with the F2-editor.
01206 
01207         // Forward left & right arrows to parent, unless this editor has been set to capture arrow key events
01208         // Changed to this behaviour for consistancy with OO Calc & MS Office.
01209         if ( ((k->key() == Qt::Key_Left) || (k->key() == Qt::Key_Right)) && (!d->captureAllKeyEvents)) {
01210           QApplication::sendEvent (parent(), e);
01211           return true;
01212         }
01213     }
01214 
01215     return false;
01216 }
01217 
01218 void CellEditor::setCursorToRange(uint pos)
01219 {
01220 //   kdDebug() << k_funcinfo << endl;
01221 
01222   d->updatingChoice = true;
01223   uint counter = 0;
01224   Tokens tokens = d->highlighter->formulaTokens();
01225   for (uint i = 0; i < tokens.count(); ++i)
01226   {
01227     Token token = tokens[i];
01228     Token::Type type = token.type();
01229     if (type == Token::Cell || type == Token::Range)
01230     {
01231       if (counter == pos)
01232       {
01233         setCursorPosition(token.pos() + token.text().length() + 1);
01234       }
01235       counter++;
01236     }
01237   }
01238   d->updatingChoice = false;
01239 }
01240 
01241 
01242 
01243 /*****************************************************************************
01244  *
01245  * ComboboxLocationEditWidget
01246  *
01247  ****************************************************************************/
01248 
01249 ComboboxLocationEditWidget::ComboboxLocationEditWidget( QWidget * _parent,
01250                                                       View * _view )
01251     : KComboBox( _parent, "ComboboxLocationEditWidget" )
01252 {
01253     m_locationWidget = new LocationEditWidget( _parent, _view );
01254     setLineEdit( m_locationWidget );
01255     insertItem( "" );
01256 
01257     QValueList<Reference>::Iterator it;
01258     QValueList<Reference> area = _view->doc()->listArea();
01259     for ( it = area.begin(); it != area.end(); ++it )
01260         slotAddAreaName( (*it).ref_name);
01261     connect( this, SIGNAL( activated ( const QString & ) ), m_locationWidget, SLOT( slotActivateItem() ) );
01262 }
01263 
01264 
01265 void ComboboxLocationEditWidget::slotAddAreaName( const QString &_name)
01266 {
01267     insertItem( _name );
01268     m_locationWidget->addCompletionItem( _name );
01269 }
01270 
01271 void ComboboxLocationEditWidget::slotRemoveAreaName( const QString &_name )
01272 {
01273     for ( int i = 0; i<count(); i++ )
01274     {
01275         if ( text(i)==_name )
01276         {
01277             removeItem( i );
01278             break;
01279         }
01280     }
01281     m_locationWidget->removeCompletionItem( _name );
01282 }
01283 
01284 
01285 
01286 /*****************************************************************************
01287  *
01288  * LocationEditWidget
01289  *
01290  ****************************************************************************/
01291 
01292 LocationEditWidget::LocationEditWidget( QWidget * _parent,
01293                                                       View * _view )
01294     : KLineEdit( _parent, "LocationEditWidget" ),
01295       m_pView(_view)
01296 {
01297     setCompletionObject( &completionList,true );
01298     setCompletionMode(KGlobalSettings::CompletionAuto  );
01299 }
01300 
01301 void LocationEditWidget::addCompletionItem( const QString &_item )
01302 {
01303     kdDebug()<<"  LocationEditWidget::addCompletionItem add :"<<_item<<endl;
01304     if ( completionList.items().contains( _item) == 0 )
01305     {
01306         completionList.addItem( _item );
01307         kdDebug()<<" _utem :"<<_item<<endl;
01308         kdDebug()<<" completionList.items().count()"<<completionList.items().count()<<endl;
01309     }
01310 }
01311 
01312 void LocationEditWidget::removeCompletionItem( const QString &_item )
01313 {
01314     completionList.removeItem( _item );
01315 }
01316 
01317 void LocationEditWidget::slotActivateItem()
01318 {
01319     activateItem();
01320 }
01321 
01322 bool LocationEditWidget::activateItem()
01323 {
01324     QString ltext = text();
01325     QString tmp = ltext.lower();
01326     QValueList<Reference>::Iterator it;
01327     QValueList<Reference> area = m_pView->doc()->listArea();
01328     for ( it = area.begin(); it != area.end(); ++it )
01329     {
01330         if ((*it).ref_name == tmp)
01331         {
01332             QString tmp = (*it).sheet_name;
01333             tmp += "!";
01334             tmp += util_rangeName((*it).rect);
01335             m_pView->selectionInfo()->initialize( Region(m_pView,tmp) );
01336             return true;
01337         }
01338     }
01339 
01340     // Set the cell component to uppercase:
01341     // Sheet1!a1 -> Sheet1!A2
01342     int pos = ltext.find('!');
01343     if ( pos !=- 1 )
01344         tmp = ltext.left(pos)+ltext.mid(pos).upper();
01345     else
01346         tmp = ltext.upper();
01347 
01348     // Selection entered in location widget
01349     if ( ltext.contains( ':' ) )
01350       m_pView->selectionInfo()->initialize( Region(m_pView,tmp) );
01351     // Location entered in location widget
01352     else
01353     {
01354       Region region(m_pView,tmp);
01355         bool validName = true;
01356         for (unsigned int i = 0; i < ltext.length(); ++i)
01357         {
01358             if (!ltext[i].isLetter())
01359             {
01360                 validName = false;
01361                 break;
01362             }
01363         }
01364         if ( !region.isValid() && validName)
01365         {
01366             QRect rect( m_pView->selectionInfo()->selection() );
01367             Sheet * t = m_pView->activeSheet();
01368             // set area name on current selection/cell
01369 
01370             m_pView->doc()->addAreaName(rect, ltext.lower(), t->sheetName());
01371         }
01372 
01373         if (!validName)
01374         {
01375           m_pView->selectionInfo()->initialize(region);
01376         }
01377     }
01378 
01379     // Set the focus back on the canvas.
01380     m_pView->canvasWidget()->setFocus();
01381     return false;
01382 }
01383 
01384 
01385 void LocationEditWidget::keyPressEvent( QKeyEvent * _ev )
01386 {
01387     // Do not handle special keys and accelerators. This is
01388     // done by QLineEdit.
01389     if ( _ev->state() & ( Qt::AltButton | Qt::ControlButton ) )
01390     {
01391         QLineEdit::keyPressEvent( _ev );
01392         // Never allow that keys are passed on to the parent.
01393         _ev->accept();
01394 
01395         return;
01396     }
01397 
01398     // Handle some special keys here. Eve
01399     switch( _ev->key() )
01400     {
01401     case Key_Return:
01402     case Key_Enter:
01403     {
01404         if ( activateItem() )
01405             return;
01406         _ev->accept();
01407     }
01408     break;
01409     // Escape pressed, restore original value
01410     case Key_Escape:
01411         // #### Torben says: This is duplicated code. Bad.
01412         if ( m_pView->selectionInfo()->isSingular() ) {
01413             setText( Cell::columnName( m_pView->canvasWidget()->markerColumn() )
01414                      + QString::number( m_pView->canvasWidget()->markerRow() ) );
01415         } else {
01416             setText( Cell::columnName( m_pView->selectionInfo()->lastRange().left() )
01417                      + QString::number( m_pView->selectionInfo()->lastRange().top() )
01418                      + ":"
01419                      + Cell::columnName( m_pView->selectionInfo()->lastRange().right() )
01420                      + QString::number( m_pView->selectionInfo()->lastRange().bottom() ) );
01421         }
01422         m_pView->canvasWidget()->setFocus();
01423         _ev->accept();
01424         break;
01425     default:
01426         QLineEdit::keyPressEvent( _ev );
01427         // Never allow that keys are passed on to the parent.
01428         _ev->accept();
01429     }
01430 }
01431 
01432 
01433 
01434 /****************************************************************
01435  *
01436  * EditWidget
01437  * The line-editor that appears above the sheet and allows to
01438  * edit the cells content.
01439  *
01440  ****************************************************************/
01441 
01442 EditWidget::EditWidget( QWidget *_parent, Canvas *_canvas,
01443                                       QButton *cancelButton, QButton *okButton )
01444   : QLineEdit( _parent, "EditWidget" )
01445 {
01446   m_pCanvas = _canvas;
01447   Q_ASSERT(m_pCanvas != NULL);
01448   // Those buttons are created by the caller, so that they are inserted
01449   // properly in the layout - but they are then managed here.
01450   m_pCancelButton = cancelButton;
01451   m_pOkButton = okButton;
01452   isArray = false;
01453 
01454   installEventFilter(m_pCanvas);
01455 
01456   if ( !m_pCanvas->doc()->isReadWrite() || !m_pCanvas->activeSheet() )
01457     setEnabled( false );
01458 
01459   QObject::connect( m_pCancelButton, SIGNAL( clicked() ),
01460                     this, SLOT( slotAbortEdit() ) );
01461   QObject::connect( m_pOkButton, SIGNAL( clicked() ),
01462                     this, SLOT( slotDoneEdit() ) );
01463 
01464   setEditMode( false ); // disable buttons
01465 }
01466 
01467 void EditWidget::showEditWidget(bool _show)
01468 {
01469     if (_show)
01470   {
01471       m_pCancelButton->show();
01472       m_pOkButton->show();
01473       show();
01474   }
01475     else
01476   {
01477       m_pCancelButton->hide();
01478       m_pOkButton->hide();
01479       hide();
01480   }
01481 }
01482 
01483 void EditWidget::slotAbortEdit()
01484 {
01485     m_pCanvas->deleteEditor( false /*discard changes*/ );
01486     // will take care of the buttons
01487 }
01488 
01489 void EditWidget::slotDoneEdit()
01490 {
01491   m_pCanvas->deleteEditor( true /*keep changes*/, isArray);
01492   isArray = false;
01493   // will take care of the buttons
01494 }
01495 
01496 void EditWidget::keyPressEvent ( QKeyEvent* _ev )
01497 {
01498     // Dont handle special keys and accelerators, except Enter ones
01499     if (( ( _ev->state() & ( Qt::AltButton | Qt::ControlButton ) )
01500          || ( _ev->state() & Qt::ShiftButton )
01501          || ( _ev->key() == Key_Shift )
01502          || ( _ev->key() == Key_Control ) )
01503       && (_ev->key() != Key_Return) && (_ev->key() != Key_Enter))
01504     {
01505         QLineEdit::keyPressEvent( _ev );
01506         _ev->accept();
01507         return;
01508     }
01509 
01510   if ( !m_pCanvas->doc()->isReadWrite() )
01511     return;
01512 
01513   if ( !m_pCanvas->editor() )
01514   {
01515     // Start editing the current cell
01516     m_pCanvas->createEditor( Canvas::CellEditor,false );
01517   }
01518   CellEditor * cellEditor = (CellEditor*) m_pCanvas->editor();
01519 
01520   switch ( _ev->key() )
01521   {
01522     case Key_Down:
01523     case Key_Up:
01524     case Key_Return:
01525     case Key_Enter:
01526       cellEditor->setText( text());
01527       // Don't allow to start a chooser when pressing the arrow keys
01528       // in this widget, since only up and down would work anyway.
01529       // This is why we call slotDoneEdit now, instead of sending
01530       // to the canvas.
01531       //QApplication::sendEvent( m_pCanvas, _ev );
01532       isArray = (_ev->state() & Qt::AltButton) &&
01533           (_ev->state() & Qt::ControlButton);
01534       slotDoneEdit();
01535       m_pCanvas->view()->updateEditWidget();
01536       _ev->accept();
01537       break;
01538     case Key_F2:
01539       cellEditor->setFocus();
01540       cellEditor->setText( text());
01541       cellEditor->setCursorPosition(cursorPosition());
01542       break;
01543     default:
01544 
01545       QLineEdit::keyPressEvent( _ev );
01546 
01547       setFocus();
01548       cellEditor->setCheckChoice( false );
01549       cellEditor->setText( text() );
01550       cellEditor->setCheckChoice( true );
01551       cellEditor->setCursorPosition( cursorPosition() );
01552   }
01553 }
01554 
01555 void EditWidget::setEditMode( bool mode )
01556 {
01557   m_pCancelButton->setEnabled(mode);
01558   m_pOkButton->setEnabled(mode);
01559 }
01560 
01561 void EditWidget::focusOutEvent( QFocusEvent* ev )
01562 {
01563   //kdDebug(36001) << "EditWidget lost focus" << endl;
01564   // See comment about setLastEditorWithFocus
01565   m_pCanvas->setLastEditorWithFocus( Canvas::EditWidget );
01566 
01567   QLineEdit::focusOutEvent( ev );
01568 }
01569 
01570 void EditWidget::setText( const QString& t )
01571 {
01572   if ( t == text() ) // Why this? (David)
01573     return;
01574 
01575   QLineEdit::setText( t );
01576 }
01577 
01578 
01579 
01580 #include "kspread_editors.moc"
KDE Home | KDE Accessibility Home | Description of Access Keys