KOffice – TDE office suite
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1590 lines
44KB

  1. /* This file is part of the KDE project
  2. Copyright 1999-2006 The KSpread Team <koffice-devel@kde.org>
  3. This library is free software; you can redistribute it and/or
  4. modify it under the terms of the GNU Library General Public
  5. License as published by the Free Software Foundation; either
  6. version 2 of the License, or (at your option) any later version.
  7. This library is distributed in the hope that it will be useful,
  8. but WITHOUT ANY WARRANTY; without even the implied warranty of
  9. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  10. Library General Public License for more details.
  11. You should have received a copy of the GNU Library General Public License
  12. along with this library; see the file COPYING.LIB. If not, write to
  13. the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
  14. * Boston, MA 02110-1301, USA.
  15. */
  16. #include "kspread_editors.h"
  17. #include "kspread_canvas.h"
  18. #include "kspread_cell.h"
  19. #include "kspread_doc.h"
  20. #include "selection.h"
  21. #include "kspread_sheet.h"
  22. #include "kspread_view.h"
  23. #include "kspread_util.h"
  24. #include "formula.h"
  25. #include "functions.h"
  26. #include <tdelistbox.h>
  27. #include <tqapplication.h>
  28. #include <tqlistbox.h>
  29. #include <tqtimer.h>
  30. #include <tqlabel.h>
  31. #include <tqvbox.h>
  32. #include <tqvaluelist.h>
  33. #include <tqrichtext_p.h>
  34. //#include <klineedit.h>
  35. #include <ktextedit.h>
  36. #include <tqapplication.h>
  37. #include <tqbutton.h>
  38. #include <tqfont.h>
  39. #include <tqfontmetrics.h>
  40. #include <tqregexp.h>
  41. #include <kdebug.h>
  42. using namespace KSpread;
  43. /*****************************************************************************
  44. *
  45. * FormulaEditorHighlighter
  46. *
  47. ****************************************************************************/
  48. namespace KSpread
  49. {
  50. class FormulaEditorHighlighter::Private
  51. {
  52. public:
  53. Private()
  54. {
  55. canvas = 0;
  56. tokens = Tokens();
  57. rangeCount = 0;
  58. rangeChanged = false;
  59. }
  60. // source for cell reference checking
  61. Canvas* canvas;
  62. Tokens tokens;
  63. uint rangeCount;
  64. bool rangeChanged;
  65. };
  66. FormulaEditorHighlighter::FormulaEditorHighlighter(TQTextEdit* textEdit, Canvas* canvas)
  67. : TQSyntaxHighlighter(textEdit)
  68. {
  69. d = new Private();
  70. d->canvas = canvas;
  71. }
  72. FormulaEditorHighlighter::~FormulaEditorHighlighter()
  73. {
  74. delete d;
  75. }
  76. const Tokens& FormulaEditorHighlighter::formulaTokens() const
  77. {
  78. return d->tokens;
  79. }
  80. int FormulaEditorHighlighter::highlightParagraph(const TQString& text, int /* endStateOfLastPara */)
  81. {
  82. // reset syntax highlighting
  83. setFormat(0, text.length(), TQt::black);
  84. // save the old ones to identify range changes
  85. Tokens oldTokens = d->tokens;
  86. // interpret the text as formula
  87. // we accept invalid/incomplete formulas
  88. Formula f;
  89. d->tokens = f.scan(text);
  90. TQFont editorFont = textEdit()->currentFont();
  91. TQFont font;
  92. uint oldRangeCount = d->rangeCount;
  93. d->rangeCount = 0;
  94. TQValueList<TQColor> colors = d->canvas->choice()->colors();
  95. TQValueList<Range> alreadyFoundRanges;
  96. for (uint i = 0; i < d->tokens.count(); ++i)
  97. {
  98. Token token = d->tokens[i];
  99. Token::Type type = token.type();
  100. switch (type)
  101. {
  102. case Token::Cell:
  103. case Token::Range:
  104. {
  105. // don't compare, if we have already found a change
  106. if (!d->rangeChanged && i < oldTokens.count() && token.text() != oldTokens[i].text())
  107. {
  108. d->rangeChanged = true;
  109. }
  110. Range newRange( token.text() );
  111. if (!alreadyFoundRanges.contains(newRange))
  112. {
  113. alreadyFoundRanges.append(newRange);
  114. d->rangeCount++;
  115. }
  116. setFormat(token.pos() + 1, token.text().length(), colors[ alreadyFoundRanges.findIndex(newRange) % colors.size()] );
  117. }
  118. break;
  119. case Token::Boolean: // True, False (also i18n-ized)
  120. /* font = TQFont(editorFont);
  121. font.setBold(true);
  122. setFormat(token.pos() + 1, token.text().length(), font);*/
  123. break;
  124. case Token::Identifier: // function name or named area*/
  125. /* font = TQFont(editorFont);
  126. font.setBold(true);
  127. setFormat(token.pos() + 1, token.text().length(), font);*/
  128. break;
  129. case Token::Unknown:
  130. case Token::Integer: // 14, 3, 1977
  131. case Token::Float: // 3.141592, 1e10, 5.9e-7
  132. case Token::String: // "KOffice", "The quick brown fox..."
  133. case Token::Operator: // +, *, /, -
  134. {
  135. switch (token.asOperator())
  136. {
  137. case Token::LeftPar:
  138. case Token::RightPar:
  139. //Check where this brace is in relation to the cursor and highlight it if necessary.
  140. handleBrace( i );
  141. break;
  142. default:
  143. break;
  144. }
  145. }
  146. break;
  147. }
  148. }
  149. if (oldRangeCount != d->rangeCount)
  150. d->rangeChanged = true;
  151. return 0;
  152. }
  153. void FormulaEditorHighlighter::handleBrace( uint index )
  154. {
  155. int cursorParagraph;
  156. int cursorPos;
  157. const Token& token = d->tokens.at( index );
  158. textEdit()->getCursorPosition( &cursorParagraph , &cursorPos );
  159. int distance = cursorPos-token.pos();
  160. int opType = token.asOperator();
  161. bool highlightBrace=false;
  162. //Check where the cursor is in relation to this left or right parenthesis token.
  163. //Only one pair of braces should be highlighted at a time, and if the cursor
  164. //is between two braces, the inner-most pair should be highlighted.
  165. if ( opType == Token::LeftPar )
  166. {
  167. //If cursor is directly to the left of this left brace, highlight it
  168. if ( distance == 1 )
  169. highlightBrace=true;
  170. else
  171. //Cursor is directly to the right of this left brace, highlight it unless
  172. //there is another left brace to the right (in which case that should be highlighted instead as it
  173. //is the inner-most brace)
  174. if (distance==2)
  175. if ( (index == d->tokens.count()-1) || ( d->tokens.at(index+1).asOperator() != Token::LeftPar) )
  176. highlightBrace=true;
  177. }
  178. else
  179. {
  180. //If cursor is directly to the right of this right brace, highlight it
  181. if ( distance == 2 )
  182. highlightBrace=true;
  183. else
  184. //Cursor is directly to the left of this right brace, so highlight it unless
  185. //there is another right brace to the left (in which case that should be highlighted instead as it
  186. //is the inner-most brace)
  187. if ( distance == 1 )
  188. if ( (index == 0) || (d->tokens.at(index-1).asOperator() != Token::RightPar) )
  189. highlightBrace=true;
  190. }
  191. if (highlightBrace)
  192. {
  193. TQFont font = TQFont( textEdit()->currentFont() );
  194. font.setBold(true);
  195. setFormat(token.pos() + 1, token.text().length(), font);
  196. int matching = findMatchingBrace( index );
  197. if (matching != -1)
  198. {
  199. Token matchingBrace = d->tokens.at(matching);
  200. setFormat( matchingBrace.pos() + 1 , matchingBrace.text().length() , font);
  201. }
  202. }
  203. }
  204. int FormulaEditorHighlighter::findMatchingBrace(int pos)
  205. {
  206. int depth=0;
  207. int step=0;
  208. Tokens tokens = d->tokens;
  209. //If this is a left brace we need to step forwards through the text to find the matching right brace,
  210. //otherwise, it is a right brace so we need to step backwards through the text to find the matching left
  211. //brace.
  212. if (tokens.at(pos).asOperator() == Token::LeftPar)
  213. step = 1;
  214. else
  215. step = -1;
  216. for (int index=pos ; (index >= 0) && (index < (int) tokens.count() ) ; index += step )
  217. {
  218. if (tokens.at(index).asOperator() == Token::LeftPar)
  219. depth++;
  220. if (tokens.at(index).asOperator() == Token::RightPar)
  221. depth--;
  222. if (depth == 0)
  223. {
  224. return index;
  225. }
  226. }
  227. return -1;
  228. }
  229. uint FormulaEditorHighlighter::rangeCount() const
  230. {
  231. return d->rangeCount;
  232. }
  233. bool FormulaEditorHighlighter::rangeChanged() const
  234. {
  235. return d->rangeChanged;
  236. }
  237. void FormulaEditorHighlighter::resetRangeChanged()
  238. {
  239. d->rangeChanged=false;
  240. }
  241. } // namespace KSpread
  242. /*****************************************************************************
  243. *
  244. * FunctionCompletion
  245. *
  246. ****************************************************************************/
  247. class FunctionCompletion::Private
  248. {
  249. public:
  250. CellEditor* editor;
  251. TQVBox *completionPopup;
  252. TDEListBox *completionListBox;
  253. TQLabel* hintLabel;
  254. };
  255. FunctionCompletion::FunctionCompletion( CellEditor* editor ):
  256. TQObject( editor )
  257. {
  258. d = new Private;
  259. d->editor = editor;
  260. d->hintLabel = 0;
  261. d->completionPopup = new TQVBox( editor->topLevelWidget(), 0, WType_Popup );
  262. d->completionPopup->setFrameStyle( TQFrame::Box | TQFrame::Plain );
  263. d->completionPopup->setLineWidth( 1 );
  264. d->completionPopup->installEventFilter( this );
  265. d->completionPopup->setSizePolicy( TQSizePolicy::Expanding, TQSizePolicy::Minimum);
  266. d->completionListBox = new TDEListBox( d->completionPopup );
  267. d->completionPopup->setFocusProxy( d->completionListBox );
  268. d->completionListBox->setFrameStyle( TQFrame::NoFrame );
  269. d->completionListBox->setVariableWidth( true );
  270. d->completionListBox->installEventFilter( this );
  271. connect( d->completionListBox, TQT_SIGNAL(selected(const TQString&)), this,
  272. TQT_SLOT(itemSelected(const TQString&)) );
  273. connect( d->completionListBox, TQT_SIGNAL(highlighted(const TQString&)), this,
  274. TQT_SLOT(itemSelected(const TQString&)) );
  275. d->hintLabel = new TQLabel( 0, "autocalc", TQt::WStyle_StaysOnTop |
  276. TQt::WStyle_Customize | TQt::WStyle_NoBorder | TQt::WStyle_Tool | TQt::WX11BypassWM );
  277. d->hintLabel->setFrameStyle( TQFrame::Plain | TQFrame::Box );
  278. d->hintLabel->setPalette( TQToolTip::palette() );
  279. d->hintLabel->hide();
  280. }
  281. FunctionCompletion::~FunctionCompletion()
  282. {
  283. delete d->hintLabel;
  284. delete d;
  285. }
  286. void FunctionCompletion::itemSelected( const TQString& item )
  287. {
  288. KSpread::FunctionDescription* desc;
  289. desc = KSpread::FunctionRepository::self()->functionInfo(item);
  290. if(!desc)
  291. {
  292. d->hintLabel->hide();
  293. return;
  294. }
  295. TQString helpText = desc->helpText()[0];
  296. if( helpText.isEmpty() )
  297. {
  298. d->hintLabel->hide();
  299. return;
  300. }
  301. helpText.append("</qt>").prepend("<qt>");
  302. d->hintLabel->setText( helpText );
  303. d->hintLabel->adjustSize();
  304. // reposition nicely
  305. TQPoint pos = d->editor->mapToGlobal( TQPoint( d->editor->width(), 0 ) );
  306. pos.setY( pos.y() - d->hintLabel->height() - 1 );
  307. d->hintLabel->move( pos );
  308. d->hintLabel->show();
  309. d->hintLabel->raise();
  310. // do not show it forever
  311. //TQTimer::singleShot( 5000, d->hintLabel, TQT_SLOT( hide()) );
  312. }
  313. bool FunctionCompletion::eventFilter( TQObject *obj, TQEvent *ev )
  314. {
  315. if ( TQT_BASE_OBJECT(obj) == TQT_BASE_OBJECT(d->completionPopup) || TQT_BASE_OBJECT(obj) == TQT_BASE_OBJECT(d->completionListBox) )
  316. {
  317. if ( ev->type() == TQEvent::KeyPress )
  318. {
  319. TQKeyEvent *ke = (TQKeyEvent*)ev;
  320. if ( ke->key() == Key_Enter || ke->key() == Key_Return )
  321. {
  322. doneCompletion();
  323. return true;
  324. }
  325. else if ( ke->key() == Key_Left || ke->key() == Key_Right ||
  326. ke->key() == Key_Up || ke->key() == Key_Down ||
  327. ke->key() == Key_Home || ke->key() == Key_End ||
  328. ke->key() == Key_Prior || ke->key() == Key_Next )
  329. return false;
  330. d->hintLabel->hide();
  331. d->completionPopup->close();
  332. d->editor->setFocus();
  333. TQApplication::sendEvent( d->editor, ev );
  334. return true;
  335. }
  336. if ( ev->type() == TQEvent::MouseButtonDblClick )
  337. {
  338. doneCompletion();
  339. return true;
  340. }
  341. }
  342. return false;
  343. }
  344. void FunctionCompletion::doneCompletion()
  345. {
  346. d->hintLabel->hide();
  347. d->completionPopup->close();
  348. d->editor->setFocus();
  349. emit selectedCompletion( d->completionListBox->currentText() );
  350. }
  351. void FunctionCompletion::showCompletion( const TQStringList &choices )
  352. {
  353. if( !choices.count() ) return;
  354. d->completionListBox->clear();
  355. for( unsigned i = 0; i < choices.count(); i++ )
  356. new TQListBoxText( (TQListBox*)d->completionListBox, choices[i] );
  357. d->completionListBox->setCurrentItem( 0 );
  358. // size of the pop-up
  359. d->completionPopup->setMaximumHeight( 100 );
  360. d->completionPopup->resize( d->completionListBox->sizeHint() +
  361. TQSize( d->completionListBox->verticalScrollBar()->width() + 4,
  362. d->completionListBox->horizontalScrollBar()->height() + 4 ) );
  363. int h = d->completionListBox->height();
  364. int w = d->completionListBox->width();
  365. TQPoint pos = d->editor->globalCursorPosition();
  366. // if popup is partially invisible, move to other position
  367. // FIXME check it if it works in Xinerama multihead
  368. int screen_num = TQApplication::desktop()->screenNumber( d->completionPopup );
  369. TQRect screen = TQApplication::desktop()->screenGeometry( screen_num );
  370. if( pos.y() + h > screen.y()+screen.height() )
  371. pos.setY( pos.y() - h - d->editor->height() );
  372. if( pos.x() + w > screen.x()+screen.width() )
  373. pos.setX( screen.x()+screen.width() - w );
  374. d->completionPopup->move( pos );
  375. d->completionListBox->setFocus();
  376. d->completionPopup->show();
  377. }
  378. /****************************************************************************
  379. *
  380. * CellEditor
  381. *
  382. ****************************************************************************/
  383. class CellEditor::Private
  384. {
  385. public:
  386. Cell* cell;
  387. Canvas* canvas;
  388. KTextEdit* textEdit;
  389. FormulaEditorHighlighter* highlighter;
  390. FunctionCompletion* functionCompletion;
  391. TQTimer* functionCompletionTimer;
  392. TQPoint globalCursorPos;
  393. bool captureAllKeyEvents : 1;
  394. bool checkChoice : 1;
  395. bool updateChoice : 1;
  396. bool updatingChoice : 1;
  397. uint length;
  398. uint fontLength;
  399. uint length_namecell;
  400. uint length_text;
  401. uint currentToken;
  402. uint rangeCount;
  403. };
  404. CellEditor::CellEditor( Cell* _cell, Canvas* _parent, bool captureAllKeyEvents, const char* _name )
  405. : TQWidget( _parent, _name )
  406. {
  407. d = new Private();
  408. d->cell = _cell;
  409. d->canvas = _parent;
  410. d->textEdit = new KTextEdit(this);
  411. d->globalCursorPos = TQPoint();
  412. d->captureAllKeyEvents = captureAllKeyEvents;
  413. d->checkChoice = true;
  414. d->updateChoice = true;
  415. d->updatingChoice = false;
  416. d->length = 0;
  417. d->fontLength = 0;
  418. d->length_namecell = 0;
  419. d->length_text = 0;
  420. d->currentToken = 0;
  421. d->rangeCount = 0;
  422. //TODO - Get rid of TQTextEdit margins, this doesn't seem easily possible in TQt 3.3, so a job for TQt 4 porting.
  423. d->textEdit->setHScrollBarMode(TQScrollView::AlwaysOff);
  424. d->textEdit->setVScrollBarMode(TQScrollView::AlwaysOff);
  425. d->textEdit->setFrameStyle(TQFrame::NoFrame);
  426. d->textEdit->setLineWidth(0);
  427. d->textEdit->installEventFilter( this );
  428. d->highlighter = new FormulaEditorHighlighter(d->textEdit, _parent);
  429. d->functionCompletion = new FunctionCompletion( this );
  430. d->functionCompletionTimer = new TQTimer( this );
  431. connect( d->functionCompletion, TQT_SIGNAL( selectedCompletion( const TQString& ) ),
  432. TQT_SLOT( functionAutoComplete( const TQString& ) ) );
  433. connect( d->textEdit, TQT_SIGNAL( textChanged() ), TQT_SLOT( checkFunctionAutoComplete() ) );
  434. connect( d->functionCompletionTimer, TQT_SIGNAL( timeout() ),
  435. TQT_SLOT( triggerFunctionAutoComplete() ) );
  436. if (!cell()->format()->multiRow(cell()->column(),cell()->row()))
  437. d->textEdit->setWordWrap(TQTextEdit::NoWrap);
  438. else
  439. d->textEdit->setWrapPolicy(TQTextEdit::AtWordOrDocumentBoundary);
  440. //TODO - Custom KTextEdit class which supports text completion
  441. /*
  442. d->textEdit->setFrame( false );
  443. d->textEdit->setCompletionMode((TDEGlobalSettings::Completion)canvas()->view()->doc()->completionMode() );
  444. d->textEdit->setCompletionObject( &canvas()->view()->doc()->completion(),true );
  445. */
  446. setFocusProxy( d->textEdit );
  447. connect( d->textEdit, TQT_SIGNAL( cursorPositionChanged(int,int) ), this, TQT_SLOT (slotCursorPositionChanged(int,int)));
  448. connect( d->textEdit, TQT_SIGNAL( cursorPositionChanged(TQTextCursor*) ), this, TQT_SLOT (slotTextCursorChanged(TQTextCursor*)));
  449. connect( d->textEdit, TQT_SIGNAL( textChanged() ), this, TQT_SLOT( slotTextChanged() ) );
  450. // connect( d->textEdit, TQT_SIGNAL(completionModeChanged( TDEGlobalSettings::Completion )),this,TQT_SLOT (slotCompletionModeChanged(TDEGlobalSettings::Completion)));
  451. // A choose should always start at the edited cell
  452. // canvas()->setChooseMarkerRow( canvas()->markerRow() );
  453. // canvas()->setChooseMarkerColumn( canvas()->markerColumn() );
  454. // set font size according to zoom factor
  455. TQFont font( _cell->format()->font() );
  456. font.setPointSizeFloat( 0.01 * _parent->doc()->zoom() * font.pointSizeFloat() );
  457. d->textEdit->setFont( font );
  458. if (d->fontLength == 0)
  459. {
  460. TQFontMetrics fm( d->textEdit->font() );
  461. d->fontLength = fm.width('x');
  462. }
  463. }
  464. CellEditor::~CellEditor()
  465. {
  466. canvas()->endChoose();
  467. delete d->highlighter;
  468. delete d->functionCompletion;
  469. delete d->functionCompletionTimer;
  470. delete d;
  471. }
  472. Cell* CellEditor::cell() const
  473. {
  474. return d->cell;
  475. }
  476. Canvas* CellEditor::canvas() const
  477. {
  478. return d->canvas;
  479. }
  480. TQPoint CellEditor::globalCursorPosition() const
  481. {
  482. return d->globalCursorPos;
  483. }
  484. void CellEditor::checkFunctionAutoComplete()
  485. {
  486. d->functionCompletionTimer->stop();
  487. d->functionCompletionTimer->start( 2000, true );
  488. }
  489. void CellEditor::triggerFunctionAutoComplete()
  490. {
  491. // tokenize the expression (don't worry, this is very fast)
  492. int para = 0, curPos = 0;
  493. d->textEdit->getCursorPosition( &para, &curPos );
  494. TQString subtext = d->textEdit->text().left( curPos );
  495. KSpread::Formula f;
  496. KSpread::Tokens tokens = f.scan( subtext );
  497. if( !tokens.valid() ) return;
  498. if( tokens.count()<1 ) return;
  499. KSpread::Token lastToken = tokens[ tokens.count()-1 ];
  500. // last token must be an identifier
  501. if( !lastToken.isIdentifier() ) return;
  502. TQString id = lastToken.text();
  503. if( id.length() < 1 ) return;
  504. // find matches in function names
  505. TQStringList fnames = KSpread::FunctionRepository::self()->functionNames();
  506. TQStringList choices;
  507. for( unsigned i=0; i<fnames.count(); i++ )
  508. if( fnames[i].startsWith( id, false ) )
  509. choices.append( fnames[i] );
  510. choices.sort();
  511. // no match, don't bother with completion
  512. if( !choices.count() ) return;
  513. // single perfect match, no need to give choices
  514. if( choices.count()==1 )
  515. if( choices[0].lower() == id.lower() )
  516. return;
  517. // present the user with completion choices
  518. d->functionCompletion->showCompletion( choices );
  519. }
  520. void CellEditor::functionAutoComplete( const TQString& item )
  521. {
  522. if( item.isEmpty() ) return;
  523. int para = 0, curPos = 0;
  524. d->textEdit->getCursorPosition( &para, &curPos );
  525. TQString subtext = text().left( curPos );
  526. KSpread::Formula f;
  527. KSpread::Tokens tokens = f.scan( subtext );
  528. if( !tokens.valid() ) return;
  529. if( tokens.count()<1 ) return;
  530. KSpread::Token lastToken = tokens[ tokens.count()-1 ];
  531. if( !lastToken.isIdentifier() ) return;
  532. d->textEdit->blockSignals( true );
  533. d->textEdit->setSelection( 0, lastToken.pos()+1, 0, lastToken.pos()+lastToken.text().length()+1 );
  534. d->textEdit->insert( item );
  535. d->textEdit->blockSignals( false );
  536. }
  537. void CellEditor::slotCursorPositionChanged(int /* para */, int pos)
  538. {
  539. // kdDebug() << k_funcinfo << endl;
  540. // TODO Stefan: optimize this function!
  541. // turn choose mode on/off
  542. if (!checkChoice())
  543. return;
  544. d->highlighter->rehighlight();
  545. Tokens tokens = d->highlighter->formulaTokens();
  546. uint rangeCounter = 0;
  547. uint currentRange = 0;
  548. uint regionStart = 0;
  549. uint regionEnd = 0;
  550. bool lastWasASemicolon = false;
  551. d->currentToken = 0;
  552. uint rangeCount = d->highlighter->rangeCount();
  553. d->rangeCount = rangeCount;
  554. Token token;
  555. Token::Type type;
  556. // search the current token
  557. // determine the subregion number, btw
  558. for (uint i = 0; i < tokens.count(); ++i)
  559. {
  560. if (tokens[i].pos() >= pos - 1) // without '='
  561. {
  562. /* kdDebug() << "token.pos >= cursor.pos" << endl;*/
  563. type = tokens[i].type();
  564. if (type == Token::Cell || type == Token::Range)
  565. {
  566. if (lastWasASemicolon)
  567. {
  568. regionEnd = rangeCounter++;
  569. lastWasASemicolon = false;
  570. continue;
  571. }
  572. }
  573. if (type == Token::Operator && tokens[i].asOperator() == Token::Semicolon)
  574. {
  575. lastWasASemicolon = true;
  576. continue;
  577. }
  578. lastWasASemicolon = false;
  579. break;
  580. }
  581. token = tokens[i];
  582. d->currentToken = i;
  583. type = token.type();
  584. if (type == Token::Cell || type == Token::Range)
  585. {
  586. if (!lastWasASemicolon)
  587. {
  588. regionStart = rangeCounter;
  589. }
  590. regionEnd = rangeCounter;
  591. currentRange = rangeCounter++;
  592. }
  593. // semicolons are use as deliminiters in regions
  594. if (type == Token::Operator)
  595. {
  596. if (token.asOperator() == Token::Semicolon)
  597. {
  598. lastWasASemicolon = true;
  599. }
  600. else
  601. {
  602. lastWasASemicolon = false;
  603. // set the region start to the next element
  604. regionStart = currentRange + 1;
  605. regionEnd = regionStart - 1; // len = 0
  606. }
  607. }
  608. }
  609. // kdDebug() << "regionStart = " << regionStart/* << endl*/
  610. // << ", regionEnd = " << regionEnd/* << endl*/
  611. // << ", currentRange = " << currentRange << endl;
  612. d->canvas->choice()->setActiveElement(currentRange);
  613. d->canvas->choice()->setActiveSubRegion(regionStart, regionEnd-regionStart+1);
  614. // triggered by keyboard action?
  615. if (!d->updatingChoice)
  616. {
  617. if (d->highlighter->rangeChanged())
  618. {
  619. d->highlighter->resetRangeChanged();
  620. disconnect( d->canvas->choice(), TQT_SIGNAL(changed(const Region&)),
  621. d->canvas->view(), TQT_SLOT(slotScrollChoice(const Region&)) );
  622. d->canvas->doc()->emitBeginOperation();
  623. setUpdateChoice(false);
  624. Tokens tokens = d->highlighter->formulaTokens();
  625. d->canvas->choice()->update(); // set the old one dirty
  626. d->canvas->choice()->clear();
  627. Region tmpRegion;
  628. Region::ConstIterator it;
  629. //A list of regions which have already been highlighted on the spreadsheet.
  630. //This is so that we don't end up highlighting the same region twice in two different
  631. //colours.
  632. TQValueList<Region> alreadyUsedRegions;
  633. for (uint i = 0; i < tokens.count(); ++i)
  634. {
  635. Token token = tokens[i];
  636. Token::Type type = token.type();
  637. if (type == Token::Cell || type == Token::Range)
  638. {
  639. Region region(d->canvas->view(), token.text());
  640. it = region.constBegin();
  641. if (!alreadyUsedRegions.contains(region))
  642. {
  643. TQRect r=(*it)->rect();
  644. if (d->canvas->choice()->isEmpty())
  645. d->canvas->choice()->initialize((*it)->rect(), (*it)->sheet());
  646. else
  647. d->canvas->choice()->extend((*it)->rect(), (*it)->sheet());
  648. alreadyUsedRegions.append(region);
  649. }
  650. }
  651. }
  652. setUpdateChoice(true);
  653. d->canvas->doc()->emitEndOperation(*d->canvas->choice());
  654. connect( d->canvas->choice(), TQT_SIGNAL(changed(const Region&)),
  655. d->canvas->view(), TQT_SLOT(slotScrollChoice(const Region&)) );
  656. }
  657. }
  658. }
  659. void CellEditor::slotTextCursorChanged(TQTextCursor* cursor)
  660. {
  661. TQTextStringChar *chr = cursor->paragraph()->at( cursor->index() );
  662. int h = cursor->paragraph()->lineHeightOfChar( cursor->index() );
  663. int x = cursor->paragraph()->rect().x() + chr->x;
  664. int y, dummy;
  665. cursor->paragraph()->lineHeightOfChar( cursor->index(), &dummy, &y );
  666. y += cursor->paragraph()->rect().y();
  667. d->globalCursorPos = d->textEdit->mapToGlobal( d->textEdit-> contentsToViewport( TQPoint( x, y + h ) ) );
  668. }
  669. void CellEditor::cut()
  670. {
  671. d->textEdit->cut();
  672. }
  673. void CellEditor::paste()
  674. {
  675. d->textEdit->paste();
  676. }
  677. void CellEditor::copy()
  678. {
  679. d->textEdit->copy();
  680. }
  681. void CellEditor::setEditorFont(TQFont const & font, bool updateSize)
  682. {
  683. TQFont tmpFont( font );
  684. tmpFont.setPointSizeFloat( 0.01 * canvas()->doc()->zoom() * tmpFont.pointSizeFloat() );
  685. d->textEdit->setFont( tmpFont );
  686. if (updateSize)
  687. {
  688. TQFontMetrics fm( d->textEdit->font() );
  689. d->fontLength = fm.width('x');
  690. int mw = fm.width( d->textEdit->text() ) + d->fontLength;
  691. // don't make it smaller: then we would have to repaint the obscured cells
  692. if (mw < width())
  693. mw = width();
  694. int mh = fm.height();
  695. if (mh < height())
  696. mh = height();
  697. setGeometry(x(), y(), mw, mh);
  698. }
  699. }
  700. void CellEditor::slotCompletionModeChanged(TDEGlobalSettings::Completion _completion)
  701. {
  702. canvas()->view()->doc()->setCompletionMode( _completion );
  703. }
  704. void CellEditor::slotTextChanged()
  705. {
  706. // kdDebug() << k_funcinfo << endl;
  707. //FIXME - text() may return richtext?
  708. TQString t = text();
  709. if (t.length() > d->length)
  710. {
  711. d->length = t.length();
  712. TQFontMetrics fm(d->textEdit->font());
  713. // - requiredWidth = width of text plus some spacer characters
  714. int requiredWidth = fm.width(t) + (2*fm.width('x'));
  715. //For normal single-row cells, the text editor must be expanded horizontally to
  716. //allow the text to fit if the new text is too wide
  717. //For multi-row (word-wrap enabled) cells, the text editor must expand vertically to
  718. //allow for new rows of text & the width of the text editor is not affected
  719. if (d->textEdit->wordWrap() == TQTextEdit::NoWrap)
  720. {
  721. if (requiredWidth > width())
  722. {
  723. if (t.isRightToLeft())
  724. {
  725. setGeometry(x() - requiredWidth + width(), y(), requiredWidth,height());
  726. }
  727. else
  728. {
  729. setGeometry(x(), y(), requiredWidth,height());
  730. }
  731. }
  732. }
  733. else
  734. {
  735. int requiredHeight = d->textEdit->heightForWidth(width());
  736. if (requiredHeight > height())
  737. {
  738. setGeometry(x(), y(), width(), requiredHeight);
  739. }
  740. }
  741. /* // allocate more space than needed. Otherwise it might be too slow
  742. d->length = t.length();
  743. // Too slow for long texts
  744. // TQFontMetrics fm( d->textEdit->font() );
  745. // int mw = fm.width( t ) + fm.width('x');
  746. int mw = d->fontLength * d->length;
  747. if (mw < width())
  748. mw = width();
  749. if (t.isRightToLeft())
  750. setGeometry(x() - mw + width(), y(), mw, height());
  751. else
  752. setGeometry(x(), y(), mw, height());
  753. d->length -= 2; */
  754. }
  755. if ( (cell()->formatType()) == Percentage_format )
  756. {
  757. if ( (t.length() == 1) && t[0].isDigit() )
  758. {
  759. TQString tmp = t + " %";
  760. d->textEdit->setText(tmp);
  761. d->textEdit->setCursorPosition(0,1);
  762. return;
  763. }
  764. }
  765. canvas()->view()->editWidget()->setText( t );
  766. // canvas()->view()->editWidget()->setCursorPosition( d->textEdit->cursorPosition() );
  767. }
  768. void CellEditor::setCheckChoice(bool state)
  769. {
  770. d->checkChoice = state;
  771. }
  772. bool CellEditor::checkChoice()
  773. {
  774. if (!d->checkChoice)
  775. return false;
  776. // // prevent recursion
  777. // d->checkChoice = false; // TODO nescessary?
  778. d->length_namecell = 0;
  779. d->currentToken = 0;
  780. TQString text = d->textEdit->text();
  781. if ( text[0] != '=' )
  782. {
  783. canvas()->setChooseMode(false);
  784. }
  785. else
  786. {
  787. int para, cur;
  788. d->textEdit->getCursorPosition(&para, &cur);
  789. Tokens tokens = d->highlighter->formulaTokens();
  790. // empty formula?
  791. if (tokens.count() < 1)
  792. {
  793. canvas()->startChoose();
  794. }
  795. else
  796. {
  797. Token token;
  798. for (uint i = 0; i < tokens.count(); ++i)
  799. {
  800. if (tokens[i].pos() >= cur - 1) // without '='
  801. {
  802. break;
  803. }
  804. token = tokens[i];
  805. d->currentToken = i;
  806. }
  807. Token::Type type = token.type();
  808. if (type == Token::Operator && token.asOperator() != Token::RightPar)
  809. {
  810. canvas()->setChooseMode(true);
  811. }
  812. else if (type == Token::Cell || type == Token::Range)
  813. {
  814. d->length_namecell = token.text().length();
  815. canvas()->setChooseMode(true);
  816. }
  817. else
  818. {
  819. canvas()->setChooseMode(false);
  820. }
  821. }
  822. }
  823. // d->checkChoice = true;
  824. return true;
  825. }
  826. void CellEditor::setUpdateChoice(bool state)
  827. {
  828. d->updateChoice = state;
  829. }
  830. void CellEditor::updateChoice()
  831. {
  832. // kdDebug() << k_funcinfo << endl;
  833. if (!d->updateChoice)
  834. return;
  835. // // prevent recursion
  836. // d->updateChoice = false; // TODO nescessary?
  837. d->updatingChoice = true;
  838. Selection* choice = d->canvas->choice();
  839. if (choice->isEmpty())
  840. return;
  841. if (!choice->activeElement())
  842. return;
  843. // only one element TODO
  844. if (++choice->constBegin() == choice->constEnd())
  845. {
  846. }
  847. TQString name_cell = choice->activeSubRegionName();
  848. Tokens tokens = d->highlighter->formulaTokens();
  849. uint start = 1;
  850. uint length = 0;
  851. if (!tokens.empty())
  852. {
  853. Token token = tokens[d->currentToken];
  854. Token::Type type = token.type();
  855. if (type == Token::Cell || type == Token::Range)
  856. {
  857. start = token.pos() + 1; // don't forget the '='!
  858. length = token.text().length();
  859. }
  860. else
  861. {
  862. start = token.pos() + token.text().length() + 1;
  863. }
  864. }
  865. d->length_namecell = name_cell.length();
  866. d->length_text = text().length();
  867. //kdDebug(36001) << "updateChooseMarker2 len=" << d->length_namecell << endl;
  868. TQString oldText = text();
  869. TQString newText = oldText.left(start) + name_cell + oldText.right(d->length_text - start - length);
  870. setCheckChoice( false );
  871. setText( newText );
  872. setCheckChoice( true );
  873. setCursorPosition( start + d->length_namecell );
  874. d->canvas->view()->editWidget()->setText( newText );
  875. //kdDebug(36001) << "old=" << old << " len=" << d->length_namecell << " pos=" << pos << endl;
  876. // d->updateChoice = false;
  877. d->updatingChoice = false;
  878. }
  879. void CellEditor::resizeEvent( TQResizeEvent* )
  880. {
  881. d->textEdit->setGeometry( 0, 0, width(), height() );
  882. }
  883. void CellEditor::handleKeyPressEvent( TQKeyEvent * _ev )
  884. {
  885. if (_ev->key() == TQt::Key_F4)
  886. {
  887. if (d->textEdit == 0)
  888. {
  889. TQApplication::sendEvent( d->textEdit, _ev );
  890. return;
  891. }
  892. TQRegExp exp("(\\$?)([a-zA-Z]+)(\\$?)([0-9]+)$");
  893. int para,cur;
  894. d->textEdit->getCursorPosition(&para,&cur);
  895. // int cur = d->textEdit->cursorPosition();
  896. TQString tmp, tmp2;
  897. int n = -1;
  898. // this is ugly, and sort of hack
  899. // FIXME rewrite to use the real Tokenizer
  900. unsigned i;
  901. for( i = 0; i < 10; i++ )
  902. {
  903. tmp = d->textEdit->text().left( cur+i );
  904. tmp2 = d->textEdit->text().right( d->textEdit->text().length() - cur - i );
  905. n = exp.search(tmp);
  906. if( n >= 0 ) break;
  907. }
  908. if (n == -1) return;
  909. TQString newPart;
  910. if ((exp.cap(1) == "$") && (exp.cap(3) == "$"))
  911. newPart = "$" + exp.cap(2) + exp.cap(4);
  912. else if ((exp.cap(1) != "$") && (exp.cap(3) != "$"))
  913. newPart = "$" + exp.cap(2) + "$" + exp.cap(4);
  914. else if ((exp.cap(1) == "$") && (exp.cap(3) != "$"))
  915. newPart = exp.cap(2) + "$" + exp.cap(4);
  916. else if ((exp.cap(1) != "$") && (exp.cap(3) == "$"))
  917. newPart = exp.cap(2) + exp.cap(4);
  918. TQString newString = tmp.left(n);
  919. newString += newPart;
  920. cur = newString.length() - i;
  921. newString += tmp2;
  922. d->textEdit->setText(newString);
  923. d->textEdit->setCursorPosition( 0, cur );
  924. _ev->accept();
  925. return;
  926. }
  927. // Send the key event to the KLineEdit
  928. TQApplication::sendEvent( d->textEdit, _ev );
  929. }
  930. void CellEditor::handleIMEvent( TQIMEvent * _ev )
  931. {
  932. // send the IM event to the KLineEdit
  933. TQApplication::sendEvent( d->textEdit, _ev );
  934. }
  935. TQString CellEditor::text() const
  936. {
  937. return d->textEdit->text();
  938. }
  939. void CellEditor::setText(TQString text)
  940. {
  941. d->textEdit->setText(text);
  942. //Usability : It is usually more convenient if the cursor is positioned at the end of the text so it can
  943. //be quickly deleted using the backspace key
  944. //This also ensures that the caret is sized correctly for the text
  945. d->textEdit->setCursorPosition(0,text.length());
  946. if (d->fontLength == 0)
  947. {
  948. TQFontMetrics fm( d->textEdit->font() );
  949. d->fontLength = fm.width('x');
  950. }
  951. }
  952. int CellEditor::cursorPosition() const
  953. {
  954. int para,cur;
  955. d->textEdit->getCursorPosition(&para,&cur);
  956. return cur;
  957. // return d->textEdit->cursorPosition();
  958. }
  959. void CellEditor::setCursorPosition( int pos )
  960. {
  961. d->textEdit->setCursorPosition(0,pos);
  962. canvas()->view()->editWidget()->setCursorPosition( pos );
  963. }
  964. bool CellEditor::eventFilter( TQObject* o, TQEvent* e )
  965. {
  966. // Only interested in KTextEdit
  967. if ( TQT_BASE_OBJECT(o) != TQT_BASE_OBJECT(d->textEdit) )
  968. return false;
  969. if ( e->type() == TQEvent::FocusOut )
  970. {
  971. canvas()->setLastEditorWithFocus( Canvas::CellEditor );
  972. return false;
  973. }
  974. if ( e->type() == TQEvent::KeyPress || e->type() == TQEvent::KeyRelease )
  975. {
  976. TQKeyEvent* k = (TQKeyEvent*)e;
  977. if (!(k->state() & TQt::ShiftButton) || canvas()->chooseMode())
  978. {
  979. //If the user presses the return key to finish editing this cell, choose mode must be turned off first
  980. //otherwise it will merely select a different cell
  981. if (k->key() == Key_Return || k->key() == Key_Enter)
  982. {
  983. kdDebug() << "CellEditor::eventFilter: canvas()->endChoose();" << endl;
  984. canvas()->endChoose();
  985. }
  986. //NB - Added check for Key_Return when migrating text edit from KLineEdit to KTextEdit, since
  987. //normal behaviour for KTextEdit is to swallow return key presses
  988. if ( k->key() == Key_Up || k->key() == Key_Down ||
  989. k->key() == Key_Next || k->key() == Key_Prior ||
  990. k->key() == Key_Escape || k->key() == Key_Tab ||
  991. k->key() == Key_Return || k->key() == Key_Enter)
  992. {
  993. // Send directly to canvas
  994. TQApplication::sendEvent( parent(), e );
  995. return true;
  996. }
  997. }
  998. else if ( k->state() & TQt::ShiftButton && ( k->key() == Key_Return || k->key() == Key_Enter ) )
  999. {
  1000. // enable content wrapping
  1001. d->cell->format()->setMultiRow( true );
  1002. }
  1003. // End choosing. May be restarted by CellEditor::slotTextChanged
  1004. if ( e->type() == TQEvent::KeyPress && !k->text().isEmpty() )
  1005. {
  1006. canvas()->setChooseMode(false);
  1007. }
  1008. // forward Left/Right keys - so that pressing left/right in this
  1009. // editor leaves editing mode ... otherwise editing is annoying
  1010. // left/right arrows still work with the F2-editor.
  1011. // Forward left & right arrows to parent, unless this editor has been set to capture arrow key events
  1012. // Changed to this behaviour for consistancy with OO Calc & MS Office.
  1013. if ( ((k->key() == TQt::Key_Left) || (k->key() == TQt::Key_Right)) && (!d->captureAllKeyEvents)) {
  1014. TQApplication::sendEvent (parent(), e);
  1015. return true;
  1016. }
  1017. }
  1018. return false;
  1019. }
  1020. void CellEditor::setCursorToRange(uint pos)
  1021. {
  1022. // kdDebug() << k_funcinfo << endl;
  1023. d->updatingChoice = true;
  1024. uint counter = 0;
  1025. Tokens tokens = d->highlighter->formulaTokens();
  1026. for (uint i = 0; i < tokens.count(); ++i)
  1027. {
  1028. Token token = tokens[i];
  1029. Token::Type type = token.type();
  1030. if (type == Token::Cell || type == Token::Range)
  1031. {
  1032. if (counter == pos)
  1033. {
  1034. setCursorPosition(token.pos() + token.text().length() + 1);
  1035. }
  1036. counter++;
  1037. }
  1038. }
  1039. d->updatingChoice = false;
  1040. }
  1041. /*****************************************************************************
  1042. *
  1043. * ComboboxLocationEditWidget
  1044. *
  1045. ****************************************************************************/
  1046. ComboboxLocationEditWidget::ComboboxLocationEditWidget( TQWidget * _parent,
  1047. View * _view )
  1048. : KComboBox( _parent, "ComboboxLocationEditWidget" )
  1049. {
  1050. m_locationWidget = new LocationEditWidget( _parent, _view );
  1051. setLineEdit( m_locationWidget );
  1052. insertItem( "" );
  1053. TQValueList<Reference>::Iterator it;
  1054. TQValueList<Reference> area = _view->doc()->listArea();
  1055. for ( it = area.begin(); it != area.end(); ++it )
  1056. slotAddAreaName( (*it).ref_name);
  1057. connect( this, TQT_SIGNAL( activated ( const TQString & ) ), m_locationWidget, TQT_SLOT( slotActivateItem() ) );
  1058. }
  1059. void ComboboxLocationEditWidget::slotAddAreaName( const TQString &_name)
  1060. {
  1061. insertItem( _name );
  1062. m_locationWidget->addCompletionItem( _name );
  1063. }
  1064. void ComboboxLocationEditWidget::slotRemoveAreaName( const TQString &_name )
  1065. {
  1066. for ( int i = 0; i<count(); i++ )
  1067. {
  1068. if ( text(i)==_name )
  1069. {
  1070. removeItem( i );
  1071. break;
  1072. }
  1073. }
  1074. m_locationWidget->removeCompletionItem( _name );
  1075. }
  1076. /*****************************************************************************
  1077. *
  1078. * LocationEditWidget
  1079. *
  1080. ****************************************************************************/
  1081. LocationEditWidget::LocationEditWidget( TQWidget * _parent,
  1082. View * _view )
  1083. : KLineEdit( _parent, "LocationEditWidget" ),
  1084. m_pView(_view)
  1085. {
  1086. setCompletionObject( &completionList,true );
  1087. setCompletionMode(TDEGlobalSettings::CompletionAuto );
  1088. }
  1089. void LocationEditWidget::addCompletionItem( const TQString &_item )
  1090. {
  1091. kdDebug()<<" LocationEditWidget::addCompletionItem add :"<<_item<<endl;
  1092. if ( completionList.items().contains( _item) == 0 )
  1093. {
  1094. completionList.addItem( _item );
  1095. kdDebug()<<" _utem :"<<_item<<endl;
  1096. kdDebug()<<" completionList.items().count()"<<completionList.items().count()<<endl;
  1097. }
  1098. }
  1099. void LocationEditWidget::removeCompletionItem( const TQString &_item )
  1100. {
  1101. completionList.removeItem( _item );
  1102. }
  1103. void LocationEditWidget::slotActivateItem()
  1104. {
  1105. activateItem();
  1106. }
  1107. bool LocationEditWidget::activateItem()
  1108. {
  1109. TQString ltext = text();
  1110. TQString tmp = ltext.lower();
  1111. TQValueList<Reference>::Iterator it;
  1112. TQValueList<Reference> area = m_pView->doc()->listArea();
  1113. for ( it = area.begin(); it != area.end(); ++it )
  1114. {
  1115. if ((*it).ref_name == tmp)
  1116. {
  1117. TQString tmp = (*it).sheet_name;
  1118. tmp += "!";
  1119. tmp += util_rangeName((*it).rect);
  1120. m_pView->selectionInfo()->initialize( Region(m_pView,tmp) );
  1121. return true;
  1122. }
  1123. }
  1124. // Set the cell component to uppercase:
  1125. // Sheet1!a1 -> Sheet1!A2
  1126. int pos = ltext.find('!');
  1127. if ( pos !=- 1 )
  1128. tmp = ltext.left(pos)+ltext.mid(pos).upper();
  1129. else
  1130. tmp = ltext.upper();
  1131. // Selection entered in location widget
  1132. if ( ltext.contains( ':' ) )
  1133. m_pView->selectionInfo()->initialize( Region(m_pView,tmp) );
  1134. // Location entered in location widget
  1135. else
  1136. {
  1137. Region region(m_pView,tmp);
  1138. bool validName = true;
  1139. for (unsigned int i = 0; i < ltext.length(); ++i)
  1140. {
  1141. if (!ltext[i].isLetter())
  1142. {
  1143. validName = false;
  1144. break;
  1145. }
  1146. }
  1147. if ( !region.isValid() && validName)
  1148. {
  1149. TQRect rect( m_pView->selectionInfo()->selection() );
  1150. Sheet * t = m_pView->activeSheet();
  1151. // set area name on current selection/cell
  1152. m_pView->doc()->addAreaName(rect, ltext.lower(), t->sheetName());
  1153. }
  1154. if (!validName)
  1155. {
  1156. m_pView->selectionInfo()->initialize(region);
  1157. }
  1158. }
  1159. // Set the focus back on the canvas.
  1160. m_pView->canvasWidget()->setFocus();
  1161. return false;
  1162. }
  1163. void LocationEditWidget::keyPressEvent( TQKeyEvent * _ev )
  1164. {
  1165. // Do not handle special keys and accelerators. This is
  1166. // done by TQLineEdit.
  1167. if ( _ev->state() & ( TQt::AltButton | TQt::ControlButton ) )
  1168. {
  1169. TQLineEdit::keyPressEvent( _ev );
  1170. // Never allow that keys are passed on to the parent.
  1171. _ev->accept();
  1172. return;
  1173. }
  1174. // Handle some special keys here. Eve
  1175. switch( _ev->key() )
  1176. {
  1177. case Key_Return:
  1178. case Key_Enter:
  1179. {
  1180. if ( activateItem() )
  1181. return;
  1182. _ev->accept();
  1183. }
  1184. break;
  1185. // Escape pressed, restore original value
  1186. case Key_Escape:
  1187. // #### Torben says: This is duplicated code. Bad.
  1188. if ( m_pView->selectionInfo()->isSingular() ) {
  1189. setText( Cell::columnName( m_pView->canvasWidget()->markerColumn() )
  1190. + TQString::number( m_pView->canvasWidget()->markerRow() ) );
  1191. } else {
  1192. setText( Cell::columnName( m_pView->selectionInfo()->lastRange().left() )
  1193. + TQString::number( m_pView->selectionInfo()->lastRange().top() )
  1194. + ":"
  1195. + Cell::columnName( m_pView->selectionInfo()->lastRange().right() )
  1196. + TQString::number( m_pView->selectionInfo()->lastRange().bottom() ) );
  1197. }
  1198. m_pView->canvasWidget()->setFocus();
  1199. _ev->accept();
  1200. break;
  1201. default:
  1202. TQLineEdit::keyPressEvent( _ev );
  1203. // Never allow that keys are passed on to the parent.
  1204. _ev->accept();
  1205. }
  1206. }
  1207. /****************************************************************
  1208. *
  1209. * EditWidget
  1210. * The line-editor that appears above the sheet and allows to
  1211. * edit the cells content.
  1212. *
  1213. ****************************************************************/
  1214. EditWidget::EditWidget( TQWidget *_parent, Canvas *_canvas,
  1215. TQButton *cancelButton, TQButton *okButton )
  1216. : TQLineEdit( _parent, "EditWidget" )
  1217. {
  1218. m_pCanvas = _canvas;
  1219. Q_ASSERT(m_pCanvas != NULL);
  1220. // Those buttons are created by the caller, so that they are inserted
  1221. // properly in the layout - but they are then managed here.
  1222. m_pCancelButton = cancelButton;
  1223. m_pOkButton = okButton;
  1224. isArray = false;
  1225. installEventFilter(m_pCanvas);
  1226. if ( !m_pCanvas->doc()->isReadWrite() || !m_pCanvas->activeSheet() )
  1227. setEnabled( false );
  1228. TQObject::connect( m_pCancelButton, TQT_SIGNAL( clicked() ),
  1229. this, TQT_SLOT( slotAbortEdit() ) );
  1230. TQObject::connect( m_pOkButton, TQT_SIGNAL( clicked() ),
  1231. this, TQT_SLOT( slotDoneEdit() ) );
  1232. setEditMode( false ); // disable buttons
  1233. }
  1234. void EditWidget::showEditWidget(bool _show)
  1235. {
  1236. if (_show)
  1237. {
  1238. m_pCancelButton->show();
  1239. m_pOkButton->show();
  1240. show();
  1241. }
  1242. else
  1243. {
  1244. m_pCancelButton->hide();
  1245. m_pOkButton->hide();
  1246. hide();
  1247. }
  1248. }
  1249. void EditWidget::slotAbortEdit()
  1250. {
  1251. m_pCanvas->deleteEditor( false /*discard changes*/ );
  1252. // will take care of the buttons
  1253. }
  1254. void EditWidget::slotDoneEdit()
  1255. {
  1256. m_pCanvas->deleteEditor( true /*keep changes*/, isArray);
  1257. isArray = false;
  1258. // will take care of the buttons
  1259. }
  1260. void EditWidget::keyPressEvent ( TQKeyEvent* _ev )
  1261. {
  1262. // Dont handle special keys and accelerators, except Enter ones
  1263. if (( ( _ev->state() & ( TQt::AltButton | TQt::ControlButton ) )
  1264. || ( _ev->state() & TQt::ShiftButton )
  1265. || ( _ev->key() == Key_Shift )
  1266. || ( _ev->key() == Key_Control ) )
  1267. && (_ev->key() != Key_Return) && (_ev->key() != Key_Enter))
  1268. {
  1269. TQLineEdit::keyPressEvent( _ev );
  1270. _ev->accept();
  1271. return;
  1272. }
  1273. if ( !m_pCanvas->doc()->isReadWrite() )
  1274. return;
  1275. if ( !m_pCanvas->editor() )
  1276. {
  1277. // Start editing the current cell
  1278. m_pCanvas->createEditor( Canvas::CellEditor,false );
  1279. }
  1280. CellEditor * cellEditor = (CellEditor*) m_pCanvas->editor();
  1281. switch ( _ev->key() )
  1282. {
  1283. case Key_Down:
  1284. case Key_Up:
  1285. case Key_Return:
  1286. case Key_Enter:
  1287. cellEditor->setText( text());
  1288. // Don't allow to start a chooser when pressing the arrow keys
  1289. // in this widget, since only up and down would work anyway.
  1290. // This is why we call slotDoneEdit now, instead of sending
  1291. // to the canvas.
  1292. //TQApplication::sendEvent( m_pCanvas, _ev );
  1293. isArray = (_ev->state() & TQt::AltButton) &&
  1294. (_ev->state() & TQt::ControlButton);
  1295. slotDoneEdit();
  1296. m_pCanvas->view()->updateEditWidget();
  1297. _ev->accept();
  1298. break;
  1299. case Key_F2:
  1300. cellEditor->setFocus();
  1301. cellEditor->setText( text());
  1302. cellEditor->setCursorPosition(cursorPosition());
  1303. break;
  1304. default:
  1305. TQLineEdit::keyPressEvent( _ev );
  1306. setFocus();
  1307. cellEditor->setCheckChoice( false );
  1308. cellEditor->setText( text() );
  1309. cellEditor->setCheckChoice( true );
  1310. cellEditor->setCursorPosition( cursorPosition() );
  1311. }
  1312. }
  1313. void EditWidget::setEditMode( bool mode )
  1314. {
  1315. m_pCancelButton->setEnabled(mode);
  1316. m_pOkButton->setEnabled(mode);
  1317. }
  1318. void EditWidget::focusOutEvent( TQFocusEvent* ev )
  1319. {
  1320. //kdDebug(36001) << "EditWidget lost focus" << endl;
  1321. // See comment about setLastEditorWithFocus
  1322. m_pCanvas->setLastEditorWithFocus( Canvas::EditWidget );
  1323. TQLineEdit::focusOutEvent( ev );
  1324. }
  1325. void EditWidget::setText( const TQString& t )
  1326. {
  1327. if ( t == text() ) // Why this? (David)
  1328. return;
  1329. TQLineEdit::setText( t );
  1330. }
  1331. #include "kspread_editors.moc"