AbaKus – a complex calculator
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.

893 lines
22KB

  1. /* This file was part of the SpeedCrunch project
  2. Copyright (C) 2004,2005 Ariya Hidayat <ariya@kde.org>
  3. And is now part of abakus.
  4. Copyright (c) 2005 Michael Pyne <michael.pyne@kdemail.net>
  5. This program is free software; you can redistribute it and/or
  6. modify it under the terms of the GNU General Public License
  7. as published by the Free Software Foundation; either version 2
  8. of the License, or (at your option) any later version.
  9. This program is distributed in the hope that it will be useful,
  10. but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  12. GNU General Public License for more details.
  13. You should have received a copy of the GNU General Public License
  14. along with this program; if not, write to the Free Software
  15. Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
  16. */
  17. #include "function.h"
  18. #include "valuemanager.h"
  19. #include "editor.h"
  20. #include "evaluator.h"
  21. #include "result.h"
  22. #include <tqapplication.h>
  23. #include <tqlabel.h>
  24. #include <tqlineedit.h>
  25. #include <tqlistbox.h>
  26. #include <tqpainter.h>
  27. #include <tqregexp.h>
  28. #include <tqstringlist.h>
  29. #include <tqstyle.h>
  30. #include <tqsyntaxhighlighter.h>
  31. #include <tqtimer.h>
  32. #include <tqtooltip.h>
  33. #include <tqmessagebox.h>
  34. #include <tqvbox.h>
  35. #include <netwm.h>
  36. #include <fixx11h.h> // netwm.h includes X11 headers which conflict with qevent
  37. #include <tqevent.h>
  38. #include <kdebug.h>
  39. #include <algorithm>
  40. // XXX: QT 4: Replace this with qBinaryFind().
  41. using std::binary_search;
  42. class CalcResultLabel : public TQLabel
  43. {
  44. public:
  45. CalcResultLabel(TQWidget *parent, const char *name, int WFlags) :
  46. TQLabel(parent, name, WFlags)
  47. {
  48. }
  49. protected:
  50. virtual void mousePressEvent(TQMouseEvent *)
  51. {
  52. hide();
  53. }
  54. };
  55. class EditorHighlighter : public TQSyntaxHighlighter
  56. {
  57. public:
  58. EditorHighlighter( Editor* );
  59. int highlightParagraph ( const TQString & text, int );
  60. private:
  61. Editor* editor;
  62. };
  63. class Editor::Private
  64. {
  65. public:
  66. Evaluator* eval;
  67. TQStringList history;
  68. int index;
  69. bool autoCompleteEnabled;
  70. EditorCompletion* completion;
  71. TQTimer* completionTimer;
  72. bool autoCalcEnabled;
  73. char format;
  74. int decimalDigits;
  75. TQTimer* autoCalcTimer;
  76. TQLabel* autoCalcLabel;
  77. bool syntaxHighlightEnabled;
  78. EditorHighlighter* highlighter;
  79. TQMap<ColorType,TQColor> highlightColors;
  80. TQTimer* matchingTimer;
  81. };
  82. class EditorCompletion::Private
  83. {
  84. public:
  85. Editor* editor;
  86. TQVBox *completionPopup;
  87. TQListBox *completionListBox;
  88. };
  89. class ChoiceItem: public TQListBoxText
  90. {
  91. public:
  92. ChoiceItem( TQListBox*, const TQString& );
  93. void setMinNameWidth (int w) { minNameWidth = w; }
  94. int nameWidth() const;
  95. protected:
  96. void paint( TQPainter* p );
  97. private:
  98. TQString item;
  99. TQString desc;
  100. int minNameWidth;
  101. };
  102. ChoiceItem::ChoiceItem( TQListBox* listBox, const TQString& text ):
  103. TQListBoxText( listBox, text ), minNameWidth(0)
  104. {
  105. TQStringList list = TQStringList::split( ':', text );
  106. if( list.count() ) item = list[0];
  107. if( list.count()>1 ) desc = list[1];
  108. }
  109. // Returns width of this particular list item's name.
  110. int ChoiceItem::nameWidth() const
  111. {
  112. if(item.isEmpty())
  113. return 0;
  114. TQFontMetrics fm = listBox()->fontMetrics();
  115. return fm.width( item );
  116. }
  117. void ChoiceItem::paint( TQPainter* painter )
  118. {
  119. int itemHeight = height( listBox() );
  120. TQFontMetrics fm = painter->fontMetrics();
  121. int yPos = ( ( itemHeight - fm.height() ) / 2 ) + fm.ascent();
  122. painter->drawText( 3, yPos, item );
  123. //int xPos = fm.width( item );
  124. int xPos = TQMAX(fm.width(item), minNameWidth);
  125. if( !isSelected() )
  126. painter->setPen( listBox()->tqpalette().disabled().text().dark() );
  127. painter->drawText( 10 + xPos, yPos, desc );
  128. }
  129. EditorHighlighter::EditorHighlighter( Editor* e ):
  130. TQSyntaxHighlighter( e )
  131. {
  132. editor = e;
  133. }
  134. int EditorHighlighter::highlightParagraph ( const TQString & text, int )
  135. {
  136. if( !editor->isSyntaxHighlightEnabled() )
  137. {
  138. setFormat( 0, text.length(), editor->tqcolorGroup().text() );
  139. return 0;
  140. }
  141. TQStringList fnames = FunctionManager::instance()->functionList(FunctionManager::All);
  142. fnames.sort(); // Sort list so we can bin search it.
  143. Tokens tokens = Evaluator::scan( text );
  144. for( unsigned i = 0; i < tokens.count(); i++ )
  145. {
  146. Token& token = tokens[i];
  147. TQString text = token.text().lower();
  148. TQFont font = editor->font();
  149. TQColor color = TQt::black;
  150. switch( token.type() )
  151. {
  152. case Token::Number:
  153. color = editor->highlightColor( Editor::Number );
  154. break;
  155. case Token::Identifier:
  156. {
  157. color = editor->highlightColor( Editor::Variable );
  158. if( binary_search( fnames.constBegin(), fnames.constEnd(), text) ) {
  159. color = editor->highlightColor( Editor::FunctionName );
  160. }
  161. }
  162. break;
  163. case Token::Operator:
  164. break;
  165. default: break;
  166. };
  167. if( token.pos() >= 0 ) {
  168. setFormat( token.pos(), token.text().length(), font, color );
  169. }
  170. }
  171. return 0;
  172. }
  173. Editor::Editor( TQWidget* parent, const char* name ):
  174. TQTextEdit( parent, name )
  175. {
  176. d = new Private;
  177. d->eval = 0;
  178. d->index = 0;
  179. d->autoCompleteEnabled = true;
  180. d->completion = new EditorCompletion( this );
  181. d->completionTimer = new TQTimer( this );
  182. d->autoCalcEnabled = true;
  183. d->syntaxHighlightEnabled = true;
  184. d->highlighter = new EditorHighlighter( this );
  185. d->autoCalcTimer = new TQTimer( this );
  186. d->matchingTimer = new TQTimer( this );
  187. tqsetSizePolicy( TQSizePolicy::Expanding, TQSizePolicy::Fixed );
  188. setWordWrap( NoWrap );
  189. setHScrollBarMode( AlwaysOff );
  190. setVScrollBarMode( AlwaysOff );
  191. setTextFormat( PlainText );
  192. setAutoFormatting( AutoNone );
  193. setTabChangesFocus( true );
  194. setLinkUnderline( false );
  195. connect( d->completion, TQT_SIGNAL( selectedCompletion( const TQString& ) ),
  196. TQT_SLOT( autoComplete( const TQString& ) ) );
  197. connect( this, TQT_SIGNAL( textChanged() ), TQT_SLOT( checkAutoComplete() ) );
  198. connect( d->completionTimer, TQT_SIGNAL( timeout() ), TQT_SLOT( triggerAutoComplete() ) );
  199. connect( this, TQT_SIGNAL( textChanged() ), TQT_SLOT( checkMatching() ) );
  200. connect( d->matchingTimer, TQT_SIGNAL( timeout() ), TQT_SLOT( doMatchingLeft() ) );
  201. connect( d->matchingTimer, TQT_SIGNAL( timeout() ), TQT_SLOT( doMatchingRight() ) );
  202. connect( this, TQT_SIGNAL( textChanged() ), TQT_SLOT( checkAutoCalc() ) );
  203. connect( d->autoCalcTimer, TQT_SIGNAL( timeout() ), TQT_SLOT( autoCalc() ) );
  204. d->autoCalcLabel = new CalcResultLabel( 0, "autocalc", WStyle_StaysOnTop |
  205. WStyle_Customize | WStyle_NoBorder | WStyle_Tool | WX11BypassWM );
  206. d->autoCalcLabel->setFrameStyle( TQFrame::Plain | TQFrame::Box );
  207. d->autoCalcLabel->setPalette( TQToolTip::palette() );
  208. d->autoCalcLabel->hide();
  209. setHighlightColor( Number, TQColor(0,0,127) );
  210. setHighlightColor( FunctionName, TQColor(85,0,0) );
  211. setHighlightColor( Variable, TQColor(0,85,0) );
  212. setHighlightColor( MatchedPar, TQColor(255,255,183) );
  213. }
  214. Editor::~Editor()
  215. {
  216. d->autoCalcLabel->hide();
  217. delete d;
  218. }
  219. TQSize Editor::tqsizeHint() const
  220. {
  221. constPolish();
  222. TQFontMetrics fm = fontMetrics();
  223. int h = TQMAX(fm.lineSpacing(), 14);
  224. int w = fm.width( 'x' ) * 20;
  225. int m = frameWidth() * 2;
  226. return( tqstyle().tqsizeFromContents(TQStyle::CT_LineEdit, this,
  227. TQSize( w + m, h + m + 4 ).
  228. expandedTo(TQApplication::globalStrut())));
  229. }
  230. TQStringList Editor::history() const
  231. {
  232. return d->history;
  233. }
  234. void Editor::setHistory( const TQStringList& h )
  235. {
  236. d->history = h;
  237. d->index = d->history.count();
  238. }
  239. bool Editor::autoCompleteEnabled() const
  240. {
  241. return d->autoCompleteEnabled;
  242. }
  243. void Editor::setAutoCompleteEnabled( bool enable )
  244. {
  245. d->autoCompleteEnabled = enable;
  246. }
  247. bool Editor::autoCalcEnabled() const
  248. {
  249. return d->autoCalcEnabled;
  250. }
  251. void Editor::setAutoCalcEnabled( bool enable )
  252. {
  253. d->autoCalcEnabled = enable;
  254. }
  255. void Editor::setFormat( char format )
  256. {
  257. d->format = format;
  258. }
  259. void Editor::setDecimalDigits( int digits )
  260. {
  261. d->decimalDigits = digits;
  262. }
  263. void Editor::appendHistory( const TQString& text )
  264. {
  265. if( text.isEmpty() ) return;
  266. TQString lastText;
  267. if( d->history.count() )
  268. lastText = d->history[ d->history.count()-1 ];
  269. if( text == lastText ) return;
  270. d->history.append( text );
  271. d->index = d->history.count()-1;
  272. }
  273. void Editor::clearHistory()
  274. {
  275. d->history.clear();
  276. d->index = 0;
  277. }
  278. void Editor::squelchNextAutoCalc()
  279. {
  280. d->autoCalcTimer->stop();
  281. }
  282. void Editor::setText(const TQString &txt)
  283. {
  284. TQTextEdit::setText(txt);
  285. squelchNextAutoCalc();
  286. }
  287. void Editor::checkAutoComplete()
  288. {
  289. if( !d->autoCompleteEnabled ) return;
  290. d->completionTimer->stop();
  291. d->completionTimer->start( 500, true );
  292. }
  293. void Editor::checkMatching()
  294. {
  295. if( !d->syntaxHighlightEnabled ) return;
  296. d->matchingTimer->stop();
  297. d->matchingTimer->start( 200, true );
  298. }
  299. void Editor::checkAutoCalc()
  300. {
  301. // Calc-As-You-Type
  302. if( !d->autoCalcEnabled ) return;
  303. d->autoCalcTimer->stop();
  304. d->autoCalcTimer->start( 1000, true );
  305. d->autoCalcLabel->hide();
  306. }
  307. void Editor::doMatchingLeft()
  308. {
  309. if( !d->syntaxHighlightEnabled ) return;
  310. // tokenize the expression
  311. int para = 0, curPos = 0;
  312. getCursorPosition( &para, &curPos );
  313. // check for right par
  314. TQString subtext = text().left( curPos );
  315. Tokens tokens = Evaluator::scan( subtext );
  316. if( !tokens.valid() ) return;
  317. if( tokens.count()<1 ) return;
  318. Token lastToken = tokens[ tokens.count()-1 ];
  319. // right par ?
  320. if( lastToken.isOperator() )
  321. if( lastToken.asOperator() == Token::RightPar )
  322. if( lastToken.pos() == curPos-1 )
  323. {
  324. // find the matching left par
  325. unsigned par = 1;
  326. int k = 0;
  327. Token matchToken;
  328. int matchPos = -1;
  329. for( k = tokens.count()-2; k >= 0; k-- )
  330. {
  331. if( par < 1 ) break;
  332. Token matchToken = tokens[k];
  333. if( matchToken.isOperator() )
  334. {
  335. if( matchToken.asOperator() == Token::RightPar )
  336. par++;
  337. if( matchToken.asOperator() == Token::LeftPar )
  338. par--;
  339. if( par == 0 ) matchPos = matchToken.pos();
  340. }
  341. }
  342. if( matchPos >= 0 )
  343. {
  344. setSelection( 0, matchPos, 0, matchPos+1, 2 );
  345. setSelection( 0, lastToken.pos(), 0, lastToken.pos()+1, 1 );
  346. setCursorPosition( para, curPos );
  347. }
  348. }
  349. }
  350. void Editor::doMatchingRight()
  351. {
  352. if( !d->syntaxHighlightEnabled ) return;
  353. // tokenize the expression
  354. int para = 0, curPos = 0;
  355. getCursorPosition( &para, &curPos );
  356. // check for left par
  357. TQString subtext = text().right( text().length() - curPos );
  358. Tokens tokens = Evaluator::scan( subtext );
  359. if( !tokens.valid() ) return;
  360. if( tokens.count()<1 ) return;
  361. Token firstToken = tokens[ 0 ];
  362. // left par ?
  363. if( firstToken.isOperator() )
  364. if( firstToken.asOperator() == Token::LeftPar )
  365. if( firstToken.pos() == 0 )
  366. {
  367. // find the matching right par
  368. unsigned par = 1;
  369. unsigned int k = 0;
  370. Token matchToken;
  371. int matchPos = -1;
  372. for( k = 1; k < tokens.count(); k++ )
  373. {
  374. if( par < 1 ) break;
  375. Token matchToken = tokens[k];
  376. if( matchToken.isOperator() )
  377. {
  378. if( matchToken.asOperator() == Token::LeftPar )
  379. par++;
  380. if( matchToken.asOperator() == Token::RightPar )
  381. par--;
  382. if( par == 0 ) matchPos = matchToken.pos();
  383. }
  384. }
  385. if( matchPos >= 0 )
  386. {
  387. setSelection( 0, curPos+matchPos, 0, curPos+matchPos+1, 2 );
  388. setSelection( 0, curPos+firstToken.pos(), 0, curPos+firstToken.pos()+1, 1 );
  389. setCursorPosition( para, curPos );
  390. }
  391. }
  392. }
  393. void Editor::triggerAutoComplete()
  394. {
  395. if( !d->autoCompleteEnabled ) return;
  396. // tokenize the expression (don't worry, this is very fast)
  397. // faster now that it uses flex. ;)
  398. int para = 0, curPos = 0;
  399. getCursorPosition( &para, &curPos );
  400. TQString subtext = text().left( curPos );
  401. Tokens tokens = Evaluator::scan( subtext );
  402. if(!tokens.valid())
  403. {
  404. kdWarning() << "invalid tokens.\n";
  405. return;
  406. }
  407. if(tokens.isEmpty() || subtext.endsWith(" "))
  408. return;
  409. Token lastToken = tokens[ tokens.count()-1 ];
  410. // last token must be an identifier
  411. if( !lastToken.isIdentifier() )
  412. return;
  413. TQString id = lastToken.text();
  414. if( id.isEmpty() )
  415. return;
  416. // find matches in function names
  417. TQStringList fnames = FunctionManager::instance()->functionList(FunctionManager::All);
  418. TQStringList choices;
  419. for( unsigned i=0; i<fnames.count(); i++ )
  420. if( fnames[i].tqstartsWith( id, false ) )
  421. {
  422. TQString str = fnames[i];
  423. ::Function* f = FunctionManager::instance()->function( str );
  424. if( f && !f->description.isEmpty() )
  425. str.append( ':' ).append( f->description );
  426. choices.append( str );
  427. }
  428. choices.sort();
  429. // find matches in variables names
  430. TQStringList vchoices;
  431. TQStringList values = ValueManager::instance()->valueNames();
  432. for(TQStringList::ConstIterator it = values.begin(); it != values.end(); ++it)
  433. if( (*it).tqstartsWith( id, false ) )
  434. {
  435. TQString choice = ValueManager::description(*it);
  436. if(choice.isEmpty())
  437. choice = ValueManager::instance()->value(*it).toString();
  438. vchoices.append( TQString("%1:%2").tqarg( *it, choice ) );
  439. }
  440. vchoices.sort();
  441. choices += vchoices;
  442. // no match, don't bother with completion
  443. if( !choices.count() ) return;
  444. // one match, complete it for the user
  445. if( choices.count()==1 )
  446. {
  447. TQString str = TQStringList::split( ':', choices[0] )[0];
  448. // single perfect match, no need to give choices.
  449. if(str == id.lower())
  450. return;
  451. str = str.remove( 0, id.length() );
  452. int para = 0, curPos = 0;
  453. getCursorPosition( &para, &curPos );
  454. blockSignals( true );
  455. insert( str );
  456. setSelection( 0, curPos, 0, curPos+str.length() );
  457. blockSignals( false );
  458. return;
  459. }
  460. // present the user with completion choices
  461. d->completion->showCompletion( choices );
  462. }
  463. void Editor::autoComplete( const TQString& item )
  464. {
  465. if( !d->autoCompleteEnabled || item.isEmpty() )
  466. return;
  467. int para = 0, curPos = 0;
  468. getCursorPosition( &para, &curPos );
  469. TQString subtext = text().left( curPos );
  470. Tokens tokens = Evaluator::scan( subtext );
  471. if( !tokens.valid() || tokens.count() < 1 )
  472. return;
  473. Token lastToken = tokens[ tokens.count()-1 ];
  474. if( !lastToken.isIdentifier() )
  475. return;
  476. TQStringList str = TQStringList::split( ':', item );
  477. blockSignals( true );
  478. setSelection( 0, lastToken.pos(), 0, lastToken.pos()+lastToken.text().length() );
  479. insert( str[0] );
  480. blockSignals( false );
  481. }
  482. void Editor::autoCalc()
  483. {
  484. if( !d->autoCalcEnabled )
  485. return;
  486. TQString str = Evaluator::autoFix( text() );
  487. if( str.isEmpty() )
  488. return;
  489. // too short? do not bother...
  490. Tokens tokens = Evaluator::scan( str );
  491. if( tokens.count() < 2 )
  492. return;
  493. // If we're using set for a function don't try.
  494. TQRegExp setFn("\\s*set.*\\(.*=");
  495. if( str.find(setFn) != -1 )
  496. return;
  497. // strip off assignment operator, e.g. "x=1+2" becomes "1+2" only
  498. // the reason is that we want only to evaluate (on the fly) the expression,
  499. // not to update (put the result in) the variable
  500. if( tokens.count() > 2 && tokens[0].isIdentifier() &&
  501. tokens[1].asOperator() == Token::Equal )
  502. {
  503. Tokens::const_iterator it = tokens.begin();
  504. ++it;
  505. ++it; // Skip first two tokens.
  506. // Reconstruct string to evaluate using the tokens.
  507. str = "";
  508. while(it != tokens.end())
  509. {
  510. str += (*it).text();
  511. str += ' ';
  512. ++it;
  513. }
  514. }
  515. Abakus::number_t result = parseString(str.latin1());
  516. if( Result::lastResult()->type() == Result::Value )
  517. {
  518. TQString ss = TQString("Result: <b>%2</b>").tqarg(result.toString());
  519. d->autoCalcLabel->setText( ss );
  520. d->autoCalcLabel->adjustSize();
  521. // reposition nicely
  522. TQPoint pos = mapToGlobal( TQPoint( 0, 0 ) );
  523. pos.setY( pos.y() - d->autoCalcLabel->height() - 1 );
  524. d->autoCalcLabel->move( pos );
  525. d->autoCalcLabel->show();
  526. d->autoCalcLabel->raise();
  527. // do not show it forever
  528. TQTimer::singleShot( 5000, d->autoCalcLabel, TQT_SLOT( hide()) );
  529. }
  530. else
  531. {
  532. // invalid expression
  533. d->autoCalcLabel->hide();
  534. }
  535. }
  536. TQString Editor::formatNumber( const Abakus::number_t &value ) const
  537. {
  538. return value.toString();
  539. }
  540. void Editor::historyBack()
  541. {
  542. if( d->history.isEmpty() )
  543. return;
  544. d->index--;
  545. if( d->index < 0 )
  546. d->index = 0;
  547. setText( d->history[ d->index ] );
  548. setCursorPosition( 0, text().length() );
  549. ensureCursorVisible();
  550. }
  551. void Editor::historyForward()
  552. {
  553. if( d->history.isEmpty() )
  554. return;
  555. d->index++;
  556. if( d->index >= (int) d->history.count() )
  557. d->index = d->history.count() - 1;
  558. setText( d->history[ d->index ] );
  559. setCursorPosition( 0, text().length() );
  560. ensureCursorVisible();
  561. }
  562. void Editor::keyPressEvent( TQKeyEvent* e )
  563. {
  564. if( e->key() == Key_Up )
  565. {
  566. historyBack();
  567. e->accept();
  568. return;
  569. }
  570. if( e->key() == Key_Down )
  571. {
  572. historyForward();
  573. e->accept();
  574. return;
  575. }
  576. if( e->key() == Key_Enter || e->key() == Key_Return )
  577. {
  578. emit returnPressed();
  579. return;
  580. }
  581. if( e->key() == Key_Left ||
  582. e->key() == Key_Right ||
  583. e->key() == Key_Home ||
  584. e->key() == Key_End )
  585. {
  586. checkMatching();
  587. }
  588. TQTextEdit::keyPressEvent( e );
  589. }
  590. void Editor::wheelEvent( TQWheelEvent *e )
  591. {
  592. if( e->delta() > 0 )
  593. historyBack();
  594. else if( e->delta() < 0 )
  595. historyForward();
  596. e->accept();
  597. }
  598. void Editor::setSyntaxHighlight( bool enable )
  599. {
  600. d->syntaxHighlightEnabled = enable;
  601. d->highlighter->rehighlight();
  602. }
  603. bool Editor::isSyntaxHighlightEnabled() const
  604. {
  605. return d->syntaxHighlightEnabled;
  606. }
  607. void Editor::setHighlightColor( ColorType type, TQColor color )
  608. {
  609. d->highlightColors[ type ] = color;
  610. setSelectionAttributes( 1, highlightColor( Editor::MatchedPar ), false );
  611. setSelectionAttributes( 2, highlightColor( Editor::MatchedPar ), false );
  612. d->highlighter->rehighlight();
  613. }
  614. TQColor Editor::highlightColor( ColorType type )
  615. {
  616. return d->highlightColors[ type ];
  617. }
  618. EditorCompletion::EditorCompletion( Editor* editor ): TQObject( editor )
  619. {
  620. d = new Private;
  621. d->editor = editor;
  622. d->completionPopup = new TQVBox( editor->tqtopLevelWidget(), 0, WType_Popup );
  623. d->completionPopup->setFrameStyle( TQFrame::Box | TQFrame::Plain );
  624. d->completionPopup->setLineWidth( 1 );
  625. d->completionPopup->installEventFilter( this );
  626. d->completionPopup->tqsetSizePolicy( TQSizePolicy::Expanding, TQSizePolicy::Minimum);
  627. d->completionListBox = new TQListBox( d->completionPopup );
  628. d->completionPopup->setFocusProxy( d->completionListBox );
  629. d->completionListBox->setFrameStyle( TQFrame::NoFrame );
  630. d->completionListBox->setVariableWidth( true );
  631. d->completionListBox->installEventFilter( this );
  632. }
  633. EditorCompletion::~EditorCompletion()
  634. {
  635. delete d;
  636. }
  637. bool EditorCompletion::eventFilter( TQObject *obj, TQEvent *ev )
  638. {
  639. if ( TQT_BASE_OBJECT(obj) == TQT_BASE_OBJECT(d->completionPopup) || TQT_BASE_OBJECT(obj) == TQT_BASE_OBJECT(d->completionListBox) )
  640. {
  641. if ( ev->type() == TQEvent::KeyPress )
  642. {
  643. TQKeyEvent *ke = (TQKeyEvent*)ev;
  644. if ( ke->key() == Key_Enter || ke->key() == Key_Return )
  645. {
  646. doneCompletion();
  647. return true;
  648. }
  649. else if ( ke->key() == Key_Left || ke->key() == Key_Right ||
  650. ke->key() == Key_Up || ke->key() == Key_Down ||
  651. ke->key() == Key_Home || ke->key() == Key_End ||
  652. ke->key() == Key_Prior || ke->key() == Key_Next )
  653. return false;
  654. d->completionPopup->close();
  655. d->editor->setFocus();
  656. TQApplication::sendEvent( d->editor, ev );
  657. return true;
  658. }
  659. if ( ev->type() == TQEvent::MouseButtonDblClick )
  660. {
  661. doneCompletion();
  662. return true;
  663. }
  664. }
  665. return false;
  666. }
  667. void EditorCompletion::doneCompletion()
  668. {
  669. d->completionPopup->close();
  670. d->editor->setFocus();
  671. emit selectedCompletion( d->completionListBox->currentText() );
  672. }
  673. void EditorCompletion::showCompletion( const TQStringList &choices )
  674. {
  675. static bool shown = false;
  676. if( !choices.count() ) return;
  677. d->completionListBox->clear();
  678. int maxWidth = 0;
  679. for( unsigned i = 0; i < choices.count(); i++ ) {
  680. ChoiceItem *item = new ChoiceItem( d->completionListBox, choices[i] );
  681. int itemMaxWidth = item->nameWidth();
  682. if(itemMaxWidth > maxWidth)
  683. maxWidth = itemMaxWidth;
  684. }
  685. for(unsigned i = 0; i < d->completionListBox->count(); ++i) {
  686. ChoiceItem *item = static_cast<ChoiceItem *>(d->completionListBox->item(i));
  687. item->setMinNameWidth(maxWidth);
  688. }
  689. d->completionListBox->setCurrentItem( 0 );
  690. // size of the pop-up
  691. d->completionPopup->setMaximumHeight( 120 );
  692. d->completionPopup->resize( d->completionListBox->tqsizeHint() +
  693. TQSize( d->completionListBox->verticalScrollBar()->width() + 4,
  694. d->completionListBox->horizontalScrollBar()->height() + 4 ) );
  695. if(!shown)
  696. {
  697. d->completionPopup->show();
  698. TQTimer::singleShot ( 0, this, TQT_SLOT(moveCompletionPopup()) );
  699. }
  700. else
  701. {
  702. moveCompletionPopup();
  703. d->completionPopup->show();
  704. }
  705. }
  706. void EditorCompletion::moveCompletionPopup()
  707. {
  708. int h = d->completionListBox->height();
  709. int w = d->completionListBox->width();
  710. // position, reference is editor's cursor position in global coord
  711. TQFontMetrics fm( d->editor->font() );
  712. int para = 0, curPos = 0;
  713. d->editor->getCursorPosition( &para, &curPos );
  714. int pixelsOffset = fm.width( d->editor->text(), curPos );
  715. pixelsOffset -= d->editor->contentsX();
  716. TQPoint pos = d->editor->mapToGlobal( TQPoint( pixelsOffset, d->editor->height() ) );
  717. // if popup is partially invisible, move to other position
  718. NETRootInfo info(d->completionPopup->x11Display(),
  719. NET::CurrentDesktop | NET::WorkArea | NET::NumberOfDesktops,
  720. -1, false);
  721. info.activate(); // wtf is this needed for?
  722. NETRect NETarea = info.workArea(info.currentDesktop());
  723. TQRect area(NETarea.pos.x, NETarea.pos.y, NETarea.size.width, NETarea.size.height);
  724. if( pos.y() + h > area.y() + area.height() )
  725. pos.setY( pos.y() - h - d->editor->height() );
  726. if( pos.x() + w > area.x() + area.width() )
  727. pos.setX( area.x() + area.width() - w );
  728. d->completionPopup->move( pos );
  729. d->completionListBox->setFocus();
  730. }
  731. #include "editor.moc"
  732. // vim: set et sw=2 ts=8: