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       disconnect( d->canvas->choice(), SIGNAL(changed(const Region&)),
00747                   d->canvas->view(), SLOT(slotScrollChoice(const Region&)) );
00748       d->canvas->doc()->emitBeginOperation();
00749       setUpdateChoice(false);
00750 
00751       Tokens tokens = d->highlighter->formulaTokens();
00752       d->canvas->choice()->update(); // set the old one dirty
00753       d->canvas->choice()->clear();
00754       Region tmpRegion;
00755       Region::ConstIterator it;
00756 
00757       //A list of regions which have already been highlighted on the spreadsheet.
00758       //This is so that we don't end up highlighting the same region twice in two different
00759       //colours.
00760       QValueList<Region> alreadyUsedRegions;
00761 
00762       for (uint i = 0; i < tokens.count(); ++i)
00763       {
00764         Token token = tokens[i];
00765         Token::Type type = token.type();
00766         if (type == Token::Cell || type == Token::Range)
00767         {
00768           Region region(d->canvas->view(), token.text());
00769           it = region.constBegin();
00770 
00771           if (!alreadyUsedRegions.contains(region))
00772           {
00773             QRect r=(*it)->rect();
00774 
00775             if (d->canvas->choice()->isEmpty())
00776                 d->canvas->choice()->initialize((*it)->rect(), (*it)->sheet());
00777             else
00778                 d->canvas->choice()->extend((*it)->rect(), (*it)->sheet());
00779 
00780             alreadyUsedRegions.append(region);
00781           }
00782         }
00783       }
00784       setUpdateChoice(true);
00785       d->canvas->doc()->emitEndOperation(*d->canvas->choice());
00786       connect( d->canvas->choice(), SIGNAL(changed(const Region&)),
00787                d->canvas->view(), SLOT(slotScrollChoice(const Region&)) );
00788     }
00789   }
00790 }
00791 
00792 void CellEditor::slotTextCursorChanged(QTextCursor* cursor)
00793 {
00794   QTextStringChar *chr = cursor->paragraph()->at( cursor->index() );
00795   int h = cursor->paragraph()->lineHeightOfChar( cursor->index() );
00796   int x = cursor->paragraph()->rect().x() + chr->x;
00797   int y, dummy;
00798   cursor->paragraph()->lineHeightOfChar( cursor->index(), &dummy, &y );
00799   y += cursor->paragraph()->rect().y();
00800 
00801   d->globalCursorPos = d->textEdit->mapToGlobal( d->textEdit-> contentsToViewport( QPoint( x, y + h ) ) );
00802 }
00803 
00804 void CellEditor::cut()
00805 {
00806   d->textEdit->cut();
00807 }
00808 
00809 void CellEditor::paste()
00810 {
00811   d->textEdit->paste();
00812 }
00813 
00814 void CellEditor::copy()
00815 {
00816   d->textEdit->copy();
00817 }
00818 
00819 void CellEditor::setEditorFont(QFont const & font, bool updateSize)
00820 {
00821   QFont tmpFont( font );
00822   tmpFont.setPointSizeFloat( 0.01 * canvas()->doc()->zoom() * tmpFont.pointSizeFloat() );
00823   d->textEdit->setFont( tmpFont );
00824 
00825   if (updateSize)
00826   {
00827     QFontMetrics fm( d->textEdit->font() );
00828     d->fontLength = fm.width('x');
00829 
00830     int mw = fm.width( d->textEdit->text() ) + d->fontLength;
00831     // don't make it smaller: then we would have to repaint the obscured cells
00832     if (mw < width())
00833       mw = width();
00834 
00835     int mh = fm.height();
00836     if (mh < height())
00837       mh = height();
00838 
00839     setGeometry(x(), y(), mw, mh);
00840   }
00841 }
00842 
00843 void CellEditor::slotCompletionModeChanged(KGlobalSettings::Completion _completion)
00844 {
00845   canvas()->view()->doc()->setCompletionMode( _completion );
00846 }
00847 
00848 void CellEditor::slotTextChanged()
00849 {
00850 //   kdDebug() << k_funcinfo << endl;
00851 
00852   //FIXME - text() may return richtext?
00853   QString t = text();
00854 
00855   if (t.length() > d->length)
00856   {
00857     d->length = t.length();
00858 
00859   QFontMetrics fm(d->textEdit->font());
00860   // - requiredWidth = width of text plus some spacer characters
00861   int requiredWidth = fm.width(t) + (2*fm.width('x'));
00862 
00863   //For normal single-row cells, the text editor must be expanded horizontally to
00864   //allow the text to fit if the new text is too wide
00865   //For multi-row (word-wrap enabled) cells, the text editor must expand vertically to
00866   //allow for new rows of text & the width of the text editor is not affected
00867   if (d->textEdit->wordWrap() == QTextEdit::NoWrap)
00868   {
00869     if (requiredWidth > width())
00870     {
00871       if (t.isRightToLeft())
00872       {
00873         setGeometry(x() - requiredWidth + width(), y(), requiredWidth,height());
00874       }
00875       else
00876       {
00877         setGeometry(x(), y(), requiredWidth,height());
00878       }
00879     }
00880   }
00881   else
00882   {
00883     int requiredHeight = d->textEdit->heightForWidth(width());
00884 
00885     if (requiredHeight > height())
00886     {
00887       setGeometry(x(), y(), width(), requiredHeight);
00888     }
00889   }
00890 
00891    /* // allocate more space than needed. Otherwise it might be too slow
00892     d->length = t.length();
00893 
00894     // Too slow for long texts
00895     // QFontMetrics fm( d->textEdit->font() );
00896     //  int mw = fm.width( t ) + fm.width('x');
00897     int mw = d->fontLength * d->length;
00898 
00899     if (mw < width())
00900       mw = width();
00901 
00902     if (t.isRightToLeft())
00903       setGeometry(x() - mw + width(), y(), mw, height());
00904     else
00905       setGeometry(x(), y(), mw, height());
00906 
00907     d->length -= 2; */
00908   }
00909 
00910   if ( (cell()->formatType()) == Percentage_format )
00911   {
00912     if ( (t.length() == 1) && t[0].isDigit() )
00913     {
00914       QString tmp = t + " %";
00915       d->textEdit->setText(tmp);
00916       d->textEdit->setCursorPosition(0,1);
00917       return;
00918     }
00919   }
00920 
00921   canvas()->view()->editWidget()->setText( t );
00922   // canvas()->view()->editWidget()->setCursorPosition( d->textEdit->cursorPosition() );
00923 }
00924 
00925 void CellEditor::setCheckChoice(bool state)
00926 {
00927   d->checkChoice = state;
00928 }
00929 
00930 bool CellEditor::checkChoice()
00931 {
00932   if (!d->checkChoice)
00933     return false;
00934 
00935 //   // prevent recursion
00936 //   d->checkChoice = false; // TODO nescessary?
00937 
00938   d->length_namecell = 0;
00939   d->currentToken = 0;
00940 
00941   QString text = d->textEdit->text();
00942   if ( text[0] != '=' )
00943   {
00944     canvas()->setChooseMode(false);
00945   }
00946   else
00947   {
00948     int para, cur;
00949     d->textEdit->getCursorPosition(&para, &cur);
00950 
00951     Tokens tokens = d->highlighter->formulaTokens();
00952 
00953     // empty formula?
00954     if (tokens.count() < 1)
00955     {
00956       canvas()->startChoose();
00957     }
00958     else
00959     {
00960       Token token;
00961       for (uint i = 0; i < tokens.count(); ++i)
00962       {
00963         if (tokens[i].pos() >= cur - 1) // without '='
00964         {
00965           break;
00966         }
00967         token = tokens[i];
00968         d->currentToken = i;
00969       }
00970 
00971       Token::Type type = token.type();
00972       if (type == Token::Operator && token.asOperator() != Token::RightPar)
00973       {
00974         canvas()->setChooseMode(true);
00975       }
00976       else if (type == Token::Cell || type == Token::Range)
00977       {
00978         d->length_namecell = token.text().length();
00979         canvas()->setChooseMode(true);
00980       }
00981       else
00982       {
00983         canvas()->setChooseMode(false);
00984       }
00985     }
00986   }
00987 
00988 //   d->checkChoice = true;
00989 
00990   return true;
00991 }
00992 
00993 void CellEditor::setUpdateChoice(bool state)
00994 {
00995   d->updateChoice = state;
00996 }
00997 
00998 void CellEditor::updateChoice()
00999 {
01000 //   kdDebug() << k_funcinfo << endl;
01001 
01002   if (!d->updateChoice)
01003     return;
01004 
01005 //   // prevent recursion
01006 //   d->updateChoice = false; // TODO nescessary?
01007   d->updatingChoice = true;
01008 
01009   Selection* choice = d->canvas->choice();
01010 
01011   if (choice->isEmpty())
01012     return;
01013 
01014   if (!choice->activeElement())
01015     return;
01016 
01017   // only one element TODO
01018   if (++choice->constBegin() == choice->constEnd())
01019   {
01020   }
01021 
01022   QString name_cell = choice->activeSubRegionName();
01023 
01024   Tokens tokens = d->highlighter->formulaTokens();
01025   uint start = 1;
01026   uint length = 0;
01027   if (!tokens.empty())
01028   {
01029     Token token = tokens[d->currentToken];
01030     Token::Type type = token.type();
01031     if (type == Token::Cell || type == Token::Range)
01032     {
01033       start = token.pos() + 1; // don't forget the '='!
01034       length = token.text().length();
01035     }
01036     else
01037     {
01038       start = token.pos() + token.text().length() + 1;
01039     }
01040   }
01041 
01042   d->length_namecell = name_cell.length();
01043   d->length_text = text().length();
01044     //kdDebug(36001) << "updateChooseMarker2 len=" << d->length_namecell << endl;
01045 
01046   QString oldText = text();
01047   QString newText = oldText.left(start) + name_cell + oldText.right(d->length_text - start - length);
01048 
01049   setCheckChoice( false );
01050   setText( newText );
01051   setCheckChoice( true );
01052   setCursorPosition( start + d->length_namecell );
01053 
01054   d->canvas->view()->editWidget()->setText( newText );
01055     //kdDebug(36001) << "old=" << old << " len=" << d->length_namecell << " pos=" << pos << endl;
01056 
01057 //   d->updateChoice = false;
01058   d->updatingChoice = false;
01059 }
01060 
01061 void CellEditor::resizeEvent( QResizeEvent* )
01062 {
01063     d->textEdit->setGeometry( 0, 0, width(), height() );
01064 }
01065 
01066 void CellEditor::handleKeyPressEvent( QKeyEvent * _ev )
01067 {
01068   if (_ev->key() == Qt::Key_F4)
01069   {
01070     if (d->textEdit == 0)
01071     {
01072       QApplication::sendEvent( d->textEdit, _ev );
01073       return;
01074     }
01075 
01076     QRegExp exp("(\\$?)([a-zA-Z]+)(\\$?)([0-9]+)$");
01077 
01078     int para,cur;
01079     d->textEdit->getCursorPosition(&para,&cur);
01080    // int cur = d->textEdit->cursorPosition();
01081     QString tmp, tmp2;
01082     int n = -1;
01083 
01084     // this is ugly, and sort of hack
01085     // FIXME rewrite to use the real Tokenizer
01086     unsigned i;
01087     for( i = 0; i < 10; i++ )
01088     {
01089       tmp =  d->textEdit->text().left( cur+i );
01090       tmp2 = d->textEdit->text().right( d->textEdit->text().length() - cur - i );
01091 
01092       n = exp.search(tmp);
01093       if( n >= 0 ) break;
01094     }
01095 
01096     if (n == -1) return;
01097 
01098     QString newPart;
01099     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     else if ((exp.cap(1) == "$") && (exp.cap(3) != "$"))
01104       newPart = exp.cap(2) + "$" + exp.cap(4);
01105     else if ((exp.cap(1) != "$") && (exp.cap(3) == "$"))
01106       newPart = exp.cap(2) + exp.cap(4);
01107 
01108     QString newString = tmp.left(n);
01109     newString += newPart;
01110     cur = newString.length() - i;
01111     newString += tmp2;
01112 
01113     d->textEdit->setText(newString);
01114     d->textEdit->setCursorPosition( 0, cur );
01115 
01116     _ev->accept();
01117 
01118     return;
01119   }
01120 
01121   // Send the key event to the KLineEdit
01122   QApplication::sendEvent( d->textEdit, _ev );
01123 }
01124 
01125 void CellEditor::handleIMEvent( QIMEvent * _ev )
01126 {
01127     // send the IM event to the KLineEdit
01128     QApplication::sendEvent( d->textEdit, _ev );
01129 }
01130 
01131 QString CellEditor::text() const
01132 {
01133     return d->textEdit->text();
01134 }
01135 
01136 void CellEditor::setText(QString text)
01137 {
01138   d->textEdit->setText(text);
01139   //Usability : It is usually more convenient if the cursor is positioned at the end of the text so it can
01140   //be quickly deleted using the backspace key
01141 
01142   //This also ensures that the caret is sized correctly for the text
01143   d->textEdit->setCursorPosition(0,text.length());
01144 
01145     if (d->fontLength == 0)
01146     {
01147       QFontMetrics fm( d->textEdit->font() );
01148       d->fontLength = fm.width('x');
01149     }
01150 }
01151 
01152 int CellEditor::cursorPosition() const
01153 {
01154   int para,cur;
01155   d->textEdit->getCursorPosition(&para,&cur);
01156   return cur;
01157    // return d->textEdit->cursorPosition();
01158 }
01159 
01160 void CellEditor::setCursorPosition( int pos )
01161 {
01162     d->textEdit->setCursorPosition(0,pos);
01163     canvas()->view()->editWidget()->setCursorPosition( pos );
01164 }
01165 
01166 bool CellEditor::eventFilter( QObject* o, QEvent* e )
01167 {
01168     // Only interested in KTextEdit
01169     if ( o != d->textEdit )
01170         return false;
01171     if ( e->type() == QEvent::FocusOut )
01172     {
01173         canvas()->setLastEditorWithFocus( Canvas::CellEditor );
01174         return false;
01175     }
01176 
01177     if ( e->type() == QEvent::KeyPress || e->type() == QEvent::KeyRelease )
01178     {
01179         QKeyEvent* k = (QKeyEvent*)e;
01180         if (!(k->state() & Qt::ShiftButton) || canvas()->chooseMode())
01181         {
01182           //If the user presses the return key to finish editing this cell, choose mode must be turned off first
01183           //otherwise it will merely select a different cell
01184           if (k->key() == Key_Return || k->key() == Key_Enter)
01185           {
01186             kdDebug() << "CellEditor::eventFilter: canvas()->endChoose();" << endl;
01187             canvas()->endChoose();
01188           }
01189 
01190           //NB - Added check for Key_Return when migrating text edit from KLineEdit to KTextEdit, since
01191           //normal behaviour for KTextEdit is to swallow return key presses
01192           if ( k->key() == Key_Up || k->key() == Key_Down ||
01193                 k->key() == Key_Next || k->key() == Key_Prior ||
01194                 k->key() == Key_Escape || k->key() == Key_Tab ||
01195                 k->key() == Key_Return || k->key() == Key_Enter)
01196           {
01197               // Send directly to canvas
01198               QApplication::sendEvent( parent(), e );
01199               return true;
01200           }
01201         }
01202         else if ( k->state() & Qt::ShiftButton && ( k->key() == Key_Return || k->key() == Key_Enter ) )
01203         {
01204             // enable content wrapping
01205             d->cell->format()->setMultiRow( true );
01206         }
01207         // End choosing. May be restarted by CellEditor::slotTextChanged
01208         if ( e->type() == QEvent::KeyPress && !k->text().isEmpty() )
01209         {
01210           canvas()->setChooseMode(false);
01211         }
01212         // forward Left/Right keys - so that pressing left/right in this
01213         // editor leaves editing mode ... otherwise editing is annoying
01214         // left/right arrows still work with the F2-editor.
01215 
01216         // Forward left & right arrows to parent, unless this editor has been set to capture arrow key events
01217         // Changed to this behaviour for consistancy with OO Calc & MS Office.
01218         if ( ((k->key() == Qt::Key_Left) || (k->key() == Qt::Key_Right)) && (!d->captureAllKeyEvents)) {
01219           QApplication::sendEvent (parent(), e);
01220           return true;
01221         }
01222     }
01223 
01224     return false;
01225 }
01226 
01227 void CellEditor::setCursorToRange(uint pos)
01228 {
01229 //   kdDebug() << k_funcinfo << endl;
01230 
01231   d->updatingChoice = true;
01232   uint counter = 0;
01233   Tokens tokens = d->highlighter->formulaTokens();
01234   for (uint i = 0; i < tokens.count(); ++i)
01235   {
01236     Token token = tokens[i];
01237     Token::Type type = token.type();
01238     if (type == Token::Cell || type == Token::Range)
01239     {
01240       if (counter == pos)
01241       {
01242         setCursorPosition(token.pos() + token.text().length() + 1);
01243       }
01244       counter++;
01245     }
01246   }
01247   d->updatingChoice = false;
01248 }
01249 
01250 
01251 
01252 /*****************************************************************************
01253  *
01254  * ComboboxLocationEditWidget
01255  *
01256  ****************************************************************************/
01257 
01258 ComboboxLocationEditWidget::ComboboxLocationEditWidget( QWidget * _parent,
01259                                                       View * _view )
01260     : KComboBox( _parent, "ComboboxLocationEditWidget" )
01261 {
01262     m_locationWidget = new LocationEditWidget( _parent, _view );
01263     setLineEdit( m_locationWidget );
01264     insertItem( "" );
01265 
01266     QValueList<Reference>::Iterator it;
01267     QValueList<Reference> area = _view->doc()->listArea();
01268     for ( it = area.begin(); it != area.end(); ++it )
01269         slotAddAreaName( (*it).ref_name);
01270     connect( this, SIGNAL( activated ( const QString & ) ), m_locationWidget, SLOT( slotActivateItem() ) );
01271 }
01272 
01273 
01274 void ComboboxLocationEditWidget::slotAddAreaName( const QString &_name)
01275 {
01276     insertItem( _name );
01277     m_locationWidget->addCompletionItem( _name );
01278 }
01279 
01280 void ComboboxLocationEditWidget::slotRemoveAreaName( const QString &_name )
01281 {
01282     for ( int i = 0; i<count(); i++ )
01283     {
01284         if ( text(i)==_name )
01285         {
01286             removeItem( i );
01287             break;
01288         }
01289     }
01290     m_locationWidget->removeCompletionItem( _name );
01291 }
01292 
01293 
01294 
01295 /*****************************************************************************
01296  *
01297  * LocationEditWidget
01298  *
01299  ****************************************************************************/
01300 
01301 LocationEditWidget::LocationEditWidget( QWidget * _parent,
01302                                                       View * _view )
01303     : KLineEdit( _parent, "LocationEditWidget" ),
01304       m_pView(_view)
01305 {
01306     setCompletionObject( &completionList,true );
01307     setCompletionMode(KGlobalSettings::CompletionAuto  );
01308 }
01309 
01310 void LocationEditWidget::addCompletionItem( const QString &_item )
01311 {
01312     kdDebug()<<"  LocationEditWidget::addCompletionItem add :"<<_item<<endl;
01313     if ( completionList.items().contains( _item) == 0 )
01314     {
01315         completionList.addItem( _item );
01316         kdDebug()<<" _utem :"<<_item<<endl;
01317         kdDebug()<<" completionList.items().count()"<<completionList.items().count()<<endl;
01318     }
01319 }
01320 
01321 void LocationEditWidget::removeCompletionItem( const QString &_item )
01322 {
01323     completionList.removeItem( _item );
01324 }
01325 
01326 void LocationEditWidget::slotActivateItem()
01327 {
01328     activateItem();
01329 }
01330 
01331 bool LocationEditWidget::activateItem()
01332 {
01333     QString ltext = text();
01334     QString tmp = ltext.lower();
01335     QValueList<Reference>::Iterator it;
01336     QValueList<Reference> area = m_pView->doc()->listArea();
01337     for ( it = area.begin(); it != area.end(); ++it )
01338     {
01339         if ((*it).ref_name == tmp)
01340         {
01341             QString tmp = (*it).sheet_name;
01342             tmp += "!";
01343             tmp += util_rangeName((*it).rect);
01344             m_pView->selectionInfo()->initialize( Region(m_pView,tmp) );
01345             return true;
01346         }
01347     }
01348 
01349     // Set the cell component to uppercase:
01350     // Sheet1!a1 -> Sheet1!A2
01351     int pos = ltext.find('!');
01352     if ( pos !=- 1 )
01353         tmp = ltext.left(pos)+ltext.mid(pos).upper();
01354     else
01355         tmp = ltext.upper();
01356 
01357     // Selection entered in location widget
01358     if ( ltext.contains( ':' ) )
01359       m_pView->selectionInfo()->initialize( Region(m_pView,tmp) );
01360     // Location entered in location widget
01361     else
01362     {
01363       Region region(m_pView,tmp);
01364         bool validName = true;
01365         for (unsigned int i = 0; i < ltext.length(); ++i)
01366         {
01367             if (!ltext[i].isLetter())
01368             {
01369                 validName = false;
01370                 break;
01371             }
01372         }
01373         if ( !region.isValid() && validName)
01374         {
01375             QRect rect( m_pView->selectionInfo()->selection() );
01376             Sheet * t = m_pView->activeSheet();
01377             // set area name on current selection/cell
01378 
01379             m_pView->doc()->addAreaName(rect, ltext.lower(), t->sheetName());
01380         }
01381 
01382         if (!validName)
01383         {
01384           m_pView->selectionInfo()->initialize(region);
01385         }
01386     }
01387 
01388     // Set the focus back on the canvas.
01389     m_pView->canvasWidget()->setFocus();
01390     return false;
01391 }
01392 
01393 
01394 void LocationEditWidget::keyPressEvent( QKeyEvent * _ev )
01395 {
01396     // Do not handle special keys and accelerators. This is
01397     // done by QLineEdit.
01398     if ( _ev->state() & ( Qt::AltButton | Qt::ControlButton ) )
01399     {
01400         QLineEdit::keyPressEvent( _ev );
01401         // Never allow that keys are passed on to the parent.
01402         _ev->accept();
01403 
01404         return;
01405     }
01406 
01407     // Handle some special keys here. Eve
01408     switch( _ev->key() )
01409     {
01410     case Key_Return:
01411     case Key_Enter:
01412     {
01413         if ( activateItem() )
01414             return;
01415         _ev->accept();
01416     }
01417     break;
01418     // Escape pressed, restore original value
01419     case Key_Escape:
01420         // #### Torben says: This is duplicated code. Bad.
01421         if ( m_pView->selectionInfo()->isSingular() ) {
01422             setText( Cell::columnName( m_pView->canvasWidget()->markerColumn() )
01423                      + QString::number( m_pView->canvasWidget()->markerRow() ) );
01424         } else {
01425             setText( Cell::columnName( m_pView->selectionInfo()->lastRange().left() )
01426                      + QString::number( m_pView->selectionInfo()->lastRange().top() )
01427                      + ":"
01428                      + Cell::columnName( m_pView->selectionInfo()->lastRange().right() )
01429                      + QString::number( m_pView->selectionInfo()->lastRange().bottom() ) );
01430         }
01431         m_pView->canvasWidget()->setFocus();
01432         _ev->accept();
01433         break;
01434     default:
01435         QLineEdit::keyPressEvent( _ev );
01436         // Never allow that keys are passed on to the parent.
01437         _ev->accept();
01438     }
01439 }
01440 
01441 
01442 
01443 /****************************************************************
01444  *
01445  * EditWidget
01446  * The line-editor that appears above the sheet and allows to
01447  * edit the cells content.
01448  *
01449  ****************************************************************/
01450 
01451 EditWidget::EditWidget( QWidget *_parent, Canvas *_canvas,
01452                                       QButton *cancelButton, QButton *okButton )
01453   : QLineEdit( _parent, "EditWidget" )
01454 {
01455   m_pCanvas = _canvas;
01456   Q_ASSERT(m_pCanvas != NULL);
01457   // Those buttons are created by the caller, so that they are inserted
01458   // properly in the layout - but they are then managed here.
01459   m_pCancelButton = cancelButton;
01460   m_pOkButton = okButton;
01461   isArray = false;
01462 
01463   installEventFilter(m_pCanvas);
01464 
01465   if ( !m_pCanvas->doc()->isReadWrite() || !m_pCanvas->activeSheet() )
01466     setEnabled( false );
01467 
01468   QObject::connect( m_pCancelButton, SIGNAL( clicked() ),
01469                     this, SLOT( slotAbortEdit() ) );
01470   QObject::connect( m_pOkButton, SIGNAL( clicked() ),
01471                     this, SLOT( slotDoneEdit() ) );
01472 
01473   setEditMode( false ); // disable buttons
01474 }
01475 
01476 void EditWidget::showEditWidget(bool _show)
01477 {
01478     if (_show)
01479   {
01480       m_pCancelButton->show();
01481       m_pOkButton->show();
01482       show();
01483   }
01484     else
01485   {
01486       m_pCancelButton->hide();
01487       m_pOkButton->hide();
01488       hide();
01489   }
01490 }
01491 
01492 void EditWidget::slotAbortEdit()
01493 {
01494     m_pCanvas->deleteEditor( false /*discard changes*/ );
01495     // will take care of the buttons
01496 }
01497 
01498 void EditWidget::slotDoneEdit()
01499 {
01500   m_pCanvas->deleteEditor( true /*keep changes*/, isArray);
01501   isArray = false;
01502   // will take care of the buttons
01503 }
01504 
01505 void EditWidget::keyPressEvent ( QKeyEvent* _ev )
01506 {
01507     // Dont handle special keys and accelerators, except Enter ones
01508     if (( ( _ev->state() & ( Qt::AltButton | Qt::ControlButton ) )
01509          || ( _ev->state() & Qt::ShiftButton )
01510          || ( _ev->key() == Key_Shift )
01511          || ( _ev->key() == Key_Control ) )
01512       && (_ev->key() != Key_Return) && (_ev->key() != Key_Enter))
01513     {
01514         QLineEdit::keyPressEvent( _ev );
01515         _ev->accept();
01516         return;
01517     }
01518 
01519   if ( !m_pCanvas->doc()->isReadWrite() )
01520     return;
01521 
01522   if ( !m_pCanvas->editor() )
01523   {
01524     // Start editing the current cell
01525     m_pCanvas->createEditor( Canvas::CellEditor,false );
01526   }
01527   CellEditor * cellEditor = (CellEditor*) m_pCanvas->editor();
01528 
01529   switch ( _ev->key() )
01530   {
01531     case Key_Down:
01532     case Key_Up:
01533     case Key_Return:
01534     case Key_Enter:
01535       cellEditor->setText( text());
01536       // Don't allow to start a chooser when pressing the arrow keys
01537       // in this widget, since only up and down would work anyway.
01538       // This is why we call slotDoneEdit now, instead of sending
01539       // to the canvas.
01540       //QApplication::sendEvent( m_pCanvas, _ev );
01541       isArray = (_ev->state() & Qt::AltButton) &&
01542           (_ev->state() & Qt::ControlButton);
01543       slotDoneEdit();
01544       m_pCanvas->view()->updateEditWidget();
01545       _ev->accept();
01546       break;
01547     case Key_F2:
01548       cellEditor->setFocus();
01549       cellEditor->setText( text());
01550       cellEditor->setCursorPosition(cursorPosition());
01551       break;
01552     default:
01553 
01554       QLineEdit::keyPressEvent( _ev );
01555 
01556       setFocus();
01557       cellEditor->setCheckChoice( false );
01558       cellEditor->setText( text() );
01559       cellEditor->setCheckChoice( true );
01560       cellEditor->setCursorPosition( cursorPosition() );
01561   }
01562 }
01563 
01564 void EditWidget::setEditMode( bool mode )
01565 {
01566   m_pCancelButton->setEnabled(mode);
01567   m_pOkButton->setEnabled(mode);
01568 }
01569 
01570 void EditWidget::focusOutEvent( QFocusEvent* ev )
01571 {
01572   //kdDebug(36001) << "EditWidget lost focus" << endl;
01573   // See comment about setLastEditorWithFocus
01574   m_pCanvas->setLastEditorWithFocus( Canvas::EditWidget );
01575 
01576   QLineEdit::focusOutEvent( ev );
01577 }
01578 
01579 void EditWidget::setText( const QString& t )
01580 {
01581   if ( t == text() ) // Why this? (David)
01582     return;
01583 
01584   QLineEdit::setText( t );
01585 }
01586 
01587 
01588 
01589 #include "kspread_editors.moc"
KDE Home | KDE Accessibility Home | Description of Access Keys