TDE core libraries
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.

katecodecompletion.cpp 17KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566
  1. /* This file is part of the KDE libraries
  2. Copyright (C) 2001 Joseph Wenninger <jowenn@kde.org>
  3. Copyright (C) 2002 John Firebaugh <jfirebaugh@kde.org>
  4. Copyright (C) 2001 by Victor Röder <Victor_Roeder@GMX.de>
  5. Copyright (C) 2002 by Roberto Raggi <roberto@kdevelop.org>
  6. This library is free software; you can redistribute it and/or
  7. modify it under the terms of the GNU Library General Public
  8. License version 2 as published by the Free Software Foundation.
  9. This library 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 GNU
  12. Library General Public License for more details.
  13. You should have received a copy of the GNU Library General Public License
  14. along with this library; see the file COPYING.LIB. If not, write to
  15. the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
  16. Boston, MA 02110-1301, USA.
  17. */
  18. /******** Partly based on the ArgHintWidget of Qt3 by Trolltech AS *********/
  19. /* Trolltech doesn't mind, if we license that piece of code as LGPL, because there isn't much
  20. * left from the desigener code */
  21. #include "katecodecompletion.h"
  22. #include "katecodecompletion.moc"
  23. #include "katedocument.h"
  24. #include "kateview.h"
  25. #include "katerenderer.h"
  26. #include "kateconfig.h"
  27. #include "katefont.h"
  28. #include <kdebug.h>
  29. #include <tqwhatsthis.h>
  30. #include <tqvbox.h>
  31. #include <tqlistbox.h>
  32. #include <tqtimer.h>
  33. #include <tqtooltip.h>
  34. #include <tqapplication.h>
  35. #include <tqsizegrip.h>
  36. #include <tqfontmetrics.h>
  37. #include <tqlayout.h>
  38. #include <tqregexp.h>
  39. /**
  40. * This class is used as the codecompletion listbox. It can be resized according to its contents,
  41. * therfor the needed size is provided by sizeHint();
  42. *@short Listbox showing codecompletion
  43. *@author Jonas B. Jacobi <j.jacobi@gmx.de>
  44. */
  45. class KateCCListBox : public TQListBox
  46. {
  47. public:
  48. /**
  49. @short Create a new CCListBox
  50. */
  51. KateCCListBox (TQWidget* parent = 0, const char* name = 0, WFlags f = 0):TQListBox(parent, name, f)
  52. {
  53. }
  54. TQSize sizeHint() const
  55. {
  56. int count = this->count();
  57. int height = 20;
  58. int tmpwidth = 8;
  59. //FIXME the height is for some reasons at least 3 items heigh, even if there is only one item in the list
  60. if (count > 0)
  61. if(count < 11)
  62. height = count * itemHeight(0);
  63. else {
  64. height = 10 * itemHeight(0);
  65. tmpwidth += verticalScrollBar()->width();
  66. }
  67. int maxcount = 0, tmpcount = 0;
  68. for (int i = 0; i < count; ++i)
  69. if ( (tmpcount = fontMetrics().width(text(i)) ) > maxcount)
  70. maxcount = tmpcount;
  71. if (maxcount > TQApplication::desktop()->width()){
  72. tmpwidth = TQApplication::desktop()->width() - 5;
  73. height += horizontalScrollBar()->height();
  74. } else
  75. tmpwidth += maxcount;
  76. return TQSize(tmpwidth,height);
  77. }
  78. };
  79. class KateCompletionItem : public TQListBoxText
  80. {
  81. public:
  82. KateCompletionItem( TQListBox* lb, KTextEditor::CompletionEntry entry )
  83. : TQListBoxText( lb )
  84. , m_entry( entry )
  85. {
  86. if( entry.postfix == "()" ) { // should be configurable
  87. setText( entry.prefix + " " + entry.text + entry.postfix );
  88. } else {
  89. setText( entry.prefix + " " + entry.text + " " + entry.postfix);
  90. }
  91. }
  92. KTextEditor::CompletionEntry m_entry;
  93. };
  94. KateCodeCompletion::KateCodeCompletion( KateView* view )
  95. : TQObject( view, "Kate Code Completion" )
  96. , m_view( view )
  97. , m_commentLabel( 0 )
  98. {
  99. m_completionPopup = new TQVBox( 0, 0, (WFlags)WType_Popup );
  100. m_completionPopup->setFrameStyle( TQFrame::Box | TQFrame::Plain );
  101. m_completionPopup->setLineWidth( 1 );
  102. m_completionListBox = new KateCCListBox( m_completionPopup );
  103. m_completionListBox->setFrameStyle( TQFrame::NoFrame );
  104. //m_completionListBox->setCornerWidget( new TQSizeGrip( m_completionListBox) );
  105. m_completionListBox->setFocusProxy( m_view->m_viewInternal );
  106. m_completionListBox->installEventFilter( this );
  107. m_completionPopup->resize(m_completionListBox->sizeHint() + TQSize(2,2));
  108. m_completionPopup->installEventFilter( this );
  109. m_completionPopup->setFocusProxy( m_view->m_viewInternal );
  110. m_pArgHint = new KateArgHint( m_view );
  111. connect( m_pArgHint, TQT_SIGNAL(argHintHidden()),
  112. this, TQT_SIGNAL(argHintHidden()) );
  113. connect( m_view, TQT_SIGNAL(cursorPositionChanged()),
  114. this, TQT_SLOT(slotCursorPosChanged()) );
  115. }
  116. KateCodeCompletion::~KateCodeCompletion()
  117. {
  118. delete m_completionPopup;
  119. }
  120. bool KateCodeCompletion::codeCompletionVisible () {
  121. return m_completionPopup->isVisible();
  122. }
  123. void KateCodeCompletion::showCompletionBox(
  124. TQValueList<KTextEditor::CompletionEntry> complList, int offset, bool casesensitive )
  125. {
  126. kdDebug(13035) << "showCompletionBox " << endl;
  127. if ( codeCompletionVisible() ) return;
  128. m_caseSensitive = casesensitive;
  129. m_complList = complList;
  130. m_offset = offset;
  131. m_view->cursorPositionReal( &m_lineCursor, &m_colCursor );
  132. m_colCursor -= offset;
  133. updateBox( true );
  134. }
  135. bool KateCodeCompletion::eventFilter( TQObject *o, TQEvent *e )
  136. {
  137. if ( TQT_BASE_OBJECT(o) != TQT_BASE_OBJECT(m_completionPopup) &&
  138. TQT_BASE_OBJECT(o) != TQT_BASE_OBJECT(m_completionListBox) &&
  139. TQT_BASE_OBJECT(o) != TQT_BASE_OBJECT(m_completionListBox->viewport()) )
  140. return false;
  141. if( e->type() == TQEvent::Hide )
  142. {
  143. //don't use abortCompletion() as aborting here again will send abort signal
  144. //even on successfull completion we will emit completionAborted() twice...
  145. m_completionPopup->hide();
  146. delete m_commentLabel;
  147. m_commentLabel = 0;
  148. return false;
  149. }
  150. if ( e->type() == TQEvent::MouseButtonDblClick ) {
  151. doComplete();
  152. return false;
  153. }
  154. if ( e->type() == TQEvent::MouseButtonPress ) {
  155. TQTimer::singleShot(0, this, TQT_SLOT(showComment()));
  156. return false;
  157. }
  158. return false;
  159. }
  160. void KateCodeCompletion::handleKey (TQKeyEvent *e)
  161. {
  162. // close completion if you move out of range
  163. if ((e->key() == Key_Up) && (m_completionListBox->currentItem() == 0))
  164. {
  165. abortCompletion();
  166. m_view->setFocus();
  167. return;
  168. }
  169. // keyboard movement
  170. if( (e->key() == Key_Up) || (e->key() == Key_Down ) ||
  171. (e->key() == Key_Home ) || (e->key() == Key_End) ||
  172. (e->key() == Key_Prior) || (e->key() == Key_Next ))
  173. {
  174. TQTimer::singleShot(0,this,TQT_SLOT(showComment()));
  175. TQApplication::sendEvent( m_completionListBox, (TQEvent*)e );
  176. return;
  177. }
  178. // update the box
  179. updateBox();
  180. }
  181. void KateCodeCompletion::doComplete()
  182. {
  183. KateCompletionItem* item = static_cast<KateCompletionItem*>(
  184. m_completionListBox->item(m_completionListBox->currentItem()));
  185. if( item == 0 )
  186. return;
  187. TQString text = item->m_entry.text;
  188. TQString currentLine = m_view->currentTextLine();
  189. int len = m_view->cursorColumnReal() - m_colCursor;
  190. TQString currentComplText = currentLine.mid(m_colCursor,len);
  191. TQString add = text.mid(currentComplText.length());
  192. if( item->m_entry.postfix == "()" )
  193. add += "(";
  194. emit filterInsertString(&(item->m_entry),&add);
  195. m_view->insertText(add);
  196. complete( item->m_entry );
  197. m_view->setFocus();
  198. }
  199. void KateCodeCompletion::abortCompletion()
  200. {
  201. m_completionPopup->hide();
  202. delete m_commentLabel;
  203. m_commentLabel = 0;
  204. emit completionAborted();
  205. }
  206. void KateCodeCompletion::complete( KTextEditor::CompletionEntry entry )
  207. {
  208. m_completionPopup->hide();
  209. delete m_commentLabel;
  210. m_commentLabel = 0;
  211. emit completionDone( entry );
  212. emit completionDone();
  213. }
  214. void KateCodeCompletion::updateBox( bool )
  215. {
  216. if( m_colCursor > m_view->cursorColumnReal() ) {
  217. // the cursor is too far left
  218. kdDebug(13035) << "Aborting Codecompletion after sendEvent" << endl;
  219. kdDebug(13035) << m_view->cursorColumnReal() << endl;
  220. abortCompletion();
  221. m_view->setFocus();
  222. return;
  223. }
  224. m_completionListBox->clear();
  225. TQString currentLine = m_view->currentTextLine();
  226. int len = m_view->cursorColumnReal() - m_colCursor;
  227. TQString currentComplText = currentLine.mid(m_colCursor,len);
  228. /* No-one really badly wants those, or?
  229. kdDebug(13035) << "Column: " << m_colCursor << endl;
  230. kdDebug(13035) << "Line: " << currentLine << endl;
  231. kdDebug(13035) << "CurrentColumn: " << m_view->cursorColumnReal() << endl;
  232. kdDebug(13035) << "Len: " << len << endl;
  233. kdDebug(13035) << "Text: '" << currentComplText << "'" << endl;
  234. kdDebug(13035) << "Count: " << m_complList.count() << endl;
  235. */
  236. TQValueList<KTextEditor::CompletionEntry>::Iterator it;
  237. if( m_caseSensitive ) {
  238. for( it = m_complList.begin(); it != m_complList.end(); ++it ) {
  239. if( (*it).text.startsWith(currentComplText) ) {
  240. new KateCompletionItem(m_completionListBox,*it);
  241. }
  242. }
  243. } else {
  244. currentComplText = currentComplText.upper();
  245. for( it = m_complList.begin(); it != m_complList.end(); ++it ) {
  246. if( (*it).text.upper().startsWith(currentComplText) ) {
  247. new KateCompletionItem(m_completionListBox,*it);
  248. }
  249. }
  250. }
  251. if( m_completionListBox->count() == 0 ||
  252. ( m_completionListBox->count() == 1 && // abort if we equaled the last item
  253. currentComplText == m_completionListBox->text(0).stripWhiteSpace() ) ) {
  254. abortCompletion();
  255. m_view->setFocus();
  256. return;
  257. }
  258. kdDebug(13035)<<"KateCodeCompletion::updateBox: Resizing widget"<<endl;
  259. m_completionPopup->resize(m_completionListBox->sizeHint() + TQSize(2,2));
  260. TQPoint p = m_view->mapToGlobal( m_view->cursorCoordinates() );
  261. int x = p.x();
  262. int y = p.y() ;
  263. if ( y + m_completionPopup->height() + m_view->renderer()->config()->fontMetrics( )->height() > TQApplication::desktop()->height() )
  264. y -= (m_completionPopup->height() );
  265. else
  266. y += m_view->renderer()->config()->fontMetrics( )->height();
  267. if (x + m_completionPopup->width() > TQApplication::desktop()->width())
  268. x = TQApplication::desktop()->width() - m_completionPopup->width();
  269. m_completionPopup->move( TQPoint(x,y) );
  270. m_completionListBox->setCurrentItem( 0 );
  271. m_completionListBox->setSelected( 0, true );
  272. m_completionListBox->setFocus();
  273. m_completionPopup->show();
  274. TQTimer::singleShot(0,this,TQT_SLOT(showComment()));
  275. }
  276. void KateCodeCompletion::showArgHint ( TQStringList functionList, const TQString& strWrapping, const TQString& strDelimiter )
  277. {
  278. unsigned int line, col;
  279. m_view->cursorPositionReal( &line, &col );
  280. m_pArgHint->reset( line, col );
  281. m_pArgHint->setArgMarkInfos( strWrapping, strDelimiter );
  282. int nNum = 0;
  283. TQStringList::Iterator end(functionList.end());
  284. for( TQStringList::Iterator it = functionList.begin(); it != end; ++it )
  285. {
  286. kdDebug(13035) << "Insert function text: " << *it << endl;
  287. m_pArgHint->addFunction( nNum, ( *it ) );
  288. nNum++;
  289. }
  290. m_pArgHint->move(m_view->mapToGlobal(m_view->cursorCoordinates() + TQPoint(0,m_view->renderer()->config()->fontMetrics( )->height())) );
  291. m_pArgHint->show();
  292. }
  293. void KateCodeCompletion::slotCursorPosChanged()
  294. {
  295. m_pArgHint->cursorPositionChanged ( m_view, m_view->cursorLine(), m_view->cursorColumnReal() );
  296. }
  297. void KateCodeCompletion::showComment()
  298. {
  299. if (!m_completionPopup->isVisible())
  300. return;
  301. KateCompletionItem* item = static_cast<KateCompletionItem*>(m_completionListBox->item(m_completionListBox->currentItem()));
  302. if( !item )
  303. return;
  304. if( item->m_entry.comment.isEmpty() )
  305. return;
  306. delete m_commentLabel;
  307. m_commentLabel = new KateCodeCompletionCommentLabel( 0, item->m_entry.comment );
  308. m_commentLabel->setFont(TQToolTip::font());
  309. m_commentLabel->setPalette(TQToolTip::palette());
  310. TQPoint rightPoint = m_completionPopup->mapToGlobal(TQPoint(m_completionPopup->width(),0));
  311. TQPoint leftPoint = m_completionPopup->mapToGlobal(TQPoint(0,0));
  312. TQRect screen = TQApplication::desktop()->screenGeometry ( m_commentLabel );
  313. TQPoint finalPoint;
  314. if (rightPoint.x()+m_commentLabel->width() > screen.x() + screen.width())
  315. finalPoint.setX(leftPoint.x()-m_commentLabel->width());
  316. else
  317. finalPoint.setX(rightPoint.x());
  318. m_completionListBox->ensureCurrentVisible();
  319. finalPoint.setY(
  320. m_completionListBox->viewport()->mapToGlobal(m_completionListBox->itemRect(
  321. m_completionListBox->item(m_completionListBox->currentItem())).topLeft()).y());
  322. m_commentLabel->move(finalPoint);
  323. m_commentLabel->show();
  324. }
  325. KateArgHint::KateArgHint( KateView* parent, const char* name )
  326. : TQFrame( parent, name, (WFlags)WType_Popup )
  327. {
  328. setBackgroundColor( black );
  329. setPaletteForegroundColor( Qt::black );
  330. labelDict.setAutoDelete( true );
  331. layout = new TQVBoxLayout( this, 1, 2 );
  332. layout->setAutoAdd( true );
  333. editorView = parent;
  334. m_markCurrentFunction = true;
  335. setFocusPolicy( TQ_StrongFocus );
  336. setFocusProxy( parent );
  337. reset( -1, -1 );
  338. }
  339. KateArgHint::~KateArgHint()
  340. {
  341. }
  342. void KateArgHint::setArgMarkInfos( const TQString& wrapping, const TQString& delimiter )
  343. {
  344. m_wrapping = wrapping;
  345. m_delimiter = delimiter;
  346. m_markCurrentFunction = true;
  347. }
  348. void KateArgHint::reset( int line, int col )
  349. {
  350. m_functionMap.clear();
  351. m_currentFunction = -1;
  352. labelDict.clear();
  353. m_currentLine = line;
  354. m_currentCol = col - 1;
  355. }
  356. void KateArgHint::slotDone(bool completed)
  357. {
  358. hide();
  359. m_currentLine = m_currentCol = -1;
  360. emit argHintHidden();
  361. if (completed)
  362. emit argHintCompleted();
  363. else
  364. emit argHintAborted();
  365. }
  366. void KateArgHint::cursorPositionChanged( KateView* view, int line, int col )
  367. {
  368. if( m_currentCol == -1 || m_currentLine == -1 ){
  369. slotDone(false);
  370. return;
  371. }
  372. int nCountDelimiter = 0;
  373. int count = 0;
  374. TQString currentTextLine = view->doc()->textLine( line );
  375. TQString text = currentTextLine.mid( m_currentCol, col - m_currentCol );
  376. TQRegExp strconst_rx( "\"[^\"]*\"" );
  377. TQRegExp chrconst_rx( "'[^']*'" );
  378. text = text
  379. .replace( strconst_rx, "\"\"" )
  380. .replace( chrconst_rx, "''" );
  381. int index = 0;
  382. while( index < (int)text.length() ){
  383. if( text[index] == m_wrapping[0] ){
  384. ++count;
  385. } else if( text[index] == m_wrapping[1] ){
  386. --count;
  387. } else if( count > 0 && text[index] == m_delimiter[0] ){
  388. ++nCountDelimiter;
  389. }
  390. ++index;
  391. }
  392. if( (m_currentLine > 0 && m_currentLine != line) || (m_currentLine < col) || (count == 0) ){
  393. slotDone(count == 0);
  394. return;
  395. }
  396. // setCurArg ( nCountDelimiter + 1 );
  397. }
  398. void KateArgHint::addFunction( int id, const TQString& prot )
  399. {
  400. m_functionMap[ id ] = prot;
  401. TQLabel* label = new TQLabel( prot.stripWhiteSpace().simplifyWhiteSpace(), this );
  402. label->setBackgroundColor( TQColor(255, 255, 238) );
  403. label->show();
  404. labelDict.insert( id, label );
  405. if( m_currentFunction < 0 )
  406. setCurrentFunction( id );
  407. }
  408. void KateArgHint::setCurrentFunction( int currentFunction )
  409. {
  410. if( m_currentFunction != currentFunction ){
  411. if( currentFunction < 0 )
  412. currentFunction = (int)m_functionMap.size() - 1;
  413. if( currentFunction > (int)m_functionMap.size()-1 )
  414. currentFunction = 0;
  415. if( m_markCurrentFunction && m_currentFunction >= 0 ){
  416. TQLabel* label = labelDict[ m_currentFunction ];
  417. label->setFont( font() );
  418. }
  419. m_currentFunction = currentFunction;
  420. if( m_markCurrentFunction ){
  421. TQLabel* label = labelDict[ currentFunction ];
  422. TQFont fnt( font() );
  423. fnt.setBold( true );
  424. label->setFont( fnt );
  425. }
  426. adjustSize();
  427. }
  428. }
  429. void KateArgHint::show()
  430. {
  431. TQFrame::show();
  432. adjustSize();
  433. }
  434. bool KateArgHint::eventFilter( TQObject*, TQEvent* e )
  435. {
  436. if( isVisible() && e->type() == TQEvent::KeyPress ){
  437. TQKeyEvent* ke = TQT_TQKEYEVENT( e );
  438. if( (ke->state() & ControlButton) && ke->key() == Key_Left ){
  439. setCurrentFunction( currentFunction() - 1 );
  440. ke->accept();
  441. return true;
  442. } else if( ke->key() == Key_Escape ){
  443. slotDone(false);
  444. return false;
  445. } else if( (ke->state() & ControlButton) && ke->key() == Key_Right ){
  446. setCurrentFunction( currentFunction() + 1 );
  447. ke->accept();
  448. return true;
  449. }
  450. }
  451. return false;
  452. }
  453. void KateArgHint::adjustSize( )
  454. {
  455. TQRect screen = TQApplication::desktop()->screenGeometry( pos() );
  456. TQFrame::adjustSize();
  457. if( width() > screen.width() )
  458. resize( screen.width(), height() );
  459. if( x() + width() > screen.x() + screen.width() )
  460. move( screen.x() + screen.width() - width(), y() );
  461. }
  462. // kate: space-indent on; indent-width 2; replace-tabs on;