kate Library API Documentation

katecodecompletion.cpp

00001 /* This file is part of the KDE libraries 00002 Copyright (C) 2001 Joseph Wenninger <jowenn@kde.org> 00003 Copyright (C) 2002 John Firebaugh <jfirebaugh@kde.org> 00004 Copyright (C) 2001 by Victor Röder <Victor_Roeder@GMX.de> 00005 Copyright (C) 2002 by Roberto Raggi <roberto@kdevelop.org> 00006 00007 This library is free software; you can redistribute it and/or 00008 modify it under the terms of the GNU Library General Public 00009 License version 2 as published by the Free Software Foundation. 00010 00011 This library is distributed in the hope that it will be useful, 00012 but WITHOUT ANY WARRANTY; without even the implied warranty of 00013 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 00014 Library General Public License for more details. 00015 00016 You should have received a copy of the GNU Library General Public License 00017 along with this library; see the file COPYING.LIB. If not, write to 00018 the Free Software Foundation, Inc., 59 Temple Place - Suite 330, 00019 Boston, MA 02111-1307, USA. 00020 */ 00021 00022 /******** Partly based on the ArgHintWidget of Qt3 by Trolltech AS *********/ 00023 /* Trolltech doesn't mind, if we license that piece of code as LGPL, because there isn't much 00024 * left from the desigener code */ 00025 00026 #include "katecodecompletion.h" 00027 #include "katecodecompletion.moc" 00028 00029 #include "katedocument.h" 00030 #include "kateview.h" 00031 #include "katerenderer.h" 00032 #include "kateconfig.h" 00033 #include "katefont.h" 00034 00035 #include <kdebug.h> 00036 00037 #include <qwhatsthis.h> 00038 #include <qvbox.h> 00039 #include <qlistbox.h> 00040 #include <qtimer.h> 00041 #include <qtooltip.h> 00042 #include <qapplication.h> 00043 #include <qsizegrip.h> 00044 #include <qfontmetrics.h> 00045 #include <qlayout.h> 00046 #include <qregexp.h> 00047 00054 class KateCCListBox : public QListBox 00055 { 00056 public: 00061 KateCCListBox (QWidget* parent = 0, const char* name = 0, WFlags f = 0):QListBox(parent, name, f) 00062 { 00063 } 00064 00065 QSize sizeHint() const 00066 { 00067 int count = this->count(); 00068 int height = 20; 00069 int tmpwidth = 8; 00070 //FIXME the height is for some reasons at least 3 items heigh, even if there is only one item in the list 00071 if (count > 0) 00072 if(count < 11) 00073 height = count * itemHeight(0); 00074 else { 00075 height = 10 * itemHeight(0); 00076 tmpwidth += verticalScrollBar()->width(); 00077 } 00078 00079 int maxcount = 0, tmpcount = 0; 00080 for (int i = 0; i < count; ++i) 00081 if ( (tmpcount = fontMetrics().width(text(i)) ) > maxcount) 00082 maxcount = tmpcount; 00083 00084 if (maxcount > QApplication::desktop()->width()){ 00085 tmpwidth = QApplication::desktop()->width() - 5; 00086 height += horizontalScrollBar()->height(); 00087 } else 00088 tmpwidth += maxcount; 00089 return QSize(tmpwidth,height); 00090 00091 } 00092 }; 00093 00094 class KateCompletionItem : public QListBoxText 00095 { 00096 public: 00097 KateCompletionItem( QListBox* lb, KTextEditor::CompletionEntry entry ) 00098 : QListBoxText( lb ) 00099 , m_entry( entry ) 00100 { 00101 if( entry.postfix == "()" ) { // should be configurable 00102 setText( entry.prefix + " " + entry.text + entry.postfix ); 00103 } else { 00104 setText( entry.prefix + " " + entry.text + " " + entry.postfix); 00105 } 00106 } 00107 00108 KTextEditor::CompletionEntry m_entry; 00109 }; 00110 00111 00112 KateCodeCompletion::KateCodeCompletion( KateView* view ) 00113 : QObject( view, "Kate Code Completion" ) 00114 , m_view( view ) 00115 , m_commentLabel( 0 ) 00116 { 00117 m_completionPopup = new QVBox( 0, 0, WType_Popup ); 00118 m_completionPopup->setFrameStyle( QFrame::Box | QFrame::Plain ); 00119 m_completionPopup->setLineWidth( 1 ); 00120 00121 m_completionListBox = new KateCCListBox( m_completionPopup ); 00122 m_completionListBox->setFrameStyle( QFrame::NoFrame ); 00123 //m_completionListBox->setCornerWidget( new QSizeGrip( m_completionListBox) ); 00124 m_completionListBox->setFocusProxy( m_view->m_viewInternal ); 00125 00126 m_completionListBox->installEventFilter( this ); 00127 00128 m_completionPopup->resize(m_completionListBox->sizeHint() + QSize(2,2)); 00129 m_completionPopup->installEventFilter( this ); 00130 m_completionPopup->setFocusProxy( m_view->m_viewInternal ); 00131 00132 m_pArgHint = new KateArgHint( m_view ); 00133 connect( m_pArgHint, SIGNAL(argHintHidden()), 00134 this, SIGNAL(argHintHidden()) ); 00135 00136 connect( m_view, SIGNAL(cursorPositionChanged()), 00137 this, SLOT(slotCursorPosChanged()) ); 00138 } 00139 00140 bool KateCodeCompletion::codeCompletionVisible () { 00141 return m_completionPopup->isVisible(); 00142 } 00143 00144 void KateCodeCompletion::showCompletionBox( 00145 QValueList<KTextEditor::CompletionEntry> complList, int offset, bool casesensitive ) 00146 { 00147 kdDebug(13035) << "showCompletionBox " << endl; 00148 00149 m_caseSensitive = casesensitive; 00150 m_complList = complList; 00151 m_offset = offset; 00152 m_view->cursorPositionReal( &m_lineCursor, &m_colCursor ); 00153 m_colCursor -= offset; 00154 00155 updateBox( true ); 00156 } 00157 00158 bool KateCodeCompletion::eventFilter( QObject *o, QEvent *e ) 00159 { 00160 if ( o != m_completionPopup && 00161 o != m_completionListBox && 00162 o != m_completionListBox->viewport() ) 00163 return false; 00164 00165 if( e->type() == QEvent::Hide ) 00166 { 00167 abortCompletion(); 00168 return false; 00169 } 00170 00171 if ( e->type() == QEvent::MouseButtonDblClick ) { 00172 doComplete(); 00173 return false; 00174 } 00175 00176 if ( e->type() == QEvent::MouseButtonPress ) { 00177 QTimer::singleShot(0, this, SLOT(showComment())); 00178 return false; 00179 } 00180 00181 return false; 00182 } 00183 00184 void KateCodeCompletion::handleKey (QKeyEvent *e) 00185 { 00186 // close completion if you move out of range 00187 if ((e->key() == Key_Up) && (m_completionListBox->currentItem() == 0)) 00188 { 00189 abortCompletion(); 00190 m_view->setFocus(); 00191 return; 00192 } 00193 00194 // keyboard movement 00195 if( (e->key() == Key_Up) || (e->key() == Key_Down ) || 00196 (e->key() == Key_Home ) || (e->key() == Key_End) || 00197 (e->key() == Key_Prior) || (e->key() == Key_Next )) 00198 { 00199 QTimer::singleShot(0,this,SLOT(showComment())); 00200 QApplication::sendEvent( m_completionListBox, (QEvent*)e ); 00201 return; 00202 } 00203 00204 // update the box 00205 updateBox(); 00206 } 00207 00208 void KateCodeCompletion::doComplete() 00209 { 00210 KateCompletionItem* item = static_cast<KateCompletionItem*>( 00211 m_completionListBox->item(m_completionListBox->currentItem())); 00212 00213 if( item == 0 ) 00214 return; 00215 00216 QString text = item->m_entry.text; 00217 QString currentLine = m_view->currentTextLine(); 00218 int len = m_view->cursorColumnReal() - m_colCursor; 00219 QString currentComplText = currentLine.mid(m_colCursor,len); 00220 QString add = text.mid(currentComplText.length()); 00221 if( item->m_entry.postfix == "()" ) 00222 add += "("; 00223 00224 emit filterInsertString(&(item->m_entry),&add); 00225 m_view->insertText(add); 00226 00227 complete( item->m_entry ); 00228 m_view->setFocus(); 00229 } 00230 00231 void KateCodeCompletion::abortCompletion() 00232 { 00233 m_completionPopup->hide(); 00234 delete m_commentLabel; 00235 m_commentLabel = 0; 00236 emit completionAborted(); 00237 } 00238 00239 void KateCodeCompletion::complete( KTextEditor::CompletionEntry entry ) 00240 { 00241 m_completionPopup->hide(); 00242 delete m_commentLabel; 00243 m_commentLabel = 0; 00244 emit completionDone( entry ); 00245 emit completionDone(); 00246 } 00247 00248 void KateCodeCompletion::updateBox( bool ) 00249 { 00250 if( m_colCursor > m_view->cursorColumnReal() ) { 00251 // the cursor is too far left 00252 kdDebug(13035) << "Aborting Codecompletion after sendEvent" << endl; 00253 kdDebug(13035) << m_view->cursorColumnReal() << endl; 00254 abortCompletion(); 00255 m_view->setFocus(); 00256 return; 00257 } 00258 00259 m_completionListBox->clear(); 00260 00261 QString currentLine = m_view->currentTextLine(); 00262 int len = m_view->cursorColumnReal() - m_colCursor; 00263 QString currentComplText = currentLine.mid(m_colCursor,len); 00264 /* No-one really badly wants those, or? 00265 kdDebug(13035) << "Column: " << m_colCursor << endl; 00266 kdDebug(13035) << "Line: " << currentLine << endl; 00267 kdDebug(13035) << "CurrentColumn: " << m_view->cursorColumnReal() << endl; 00268 kdDebug(13035) << "Len: " << len << endl; 00269 kdDebug(13035) << "Text: '" << currentComplText << "'" << endl; 00270 kdDebug(13035) << "Count: " << m_complList.count() << endl; 00271 */ 00272 QValueList<KTextEditor::CompletionEntry>::Iterator it; 00273 if( m_caseSensitive ) { 00274 for( it = m_complList.begin(); it != m_complList.end(); ++it ) { 00275 if( (*it).text.startsWith(currentComplText) ) { 00276 new KateCompletionItem(m_completionListBox,*it); 00277 } 00278 } 00279 } else { 00280 currentComplText = currentComplText.upper(); 00281 for( it = m_complList.begin(); it != m_complList.end(); ++it ) { 00282 if( (*it).text.upper().startsWith(currentComplText) ) { 00283 new KateCompletionItem(m_completionListBox,*it); 00284 } 00285 } 00286 } 00287 00288 if( m_completionListBox->count() == 0 || 00289 ( m_completionListBox->count() == 1 && // abort if we equaled the last item 00290 currentComplText == m_completionListBox->text(0).stripWhiteSpace() ) ) { 00291 abortCompletion(); 00292 m_view->setFocus(); 00293 return; 00294 } 00295 00296 kdDebug(13035)<<"KateCodeCompletion::updateBox: Resizing widget"<<endl; 00297 m_completionPopup->resize(m_completionListBox->sizeHint() + QSize(2,2)); 00298 QPoint p = m_view->mapToGlobal( m_view->cursorCoordinates() ); 00299 int x = p.x(); 00300 int y = p.y() ; 00301 if ( y + m_completionPopup->height() + m_view->renderer()->config()->fontMetrics( )->height() > QApplication::desktop()->height() ) 00302 y -= (m_completionPopup->height() ); 00303 else 00304 y += m_view->renderer()->config()->fontMetrics( )->height(); 00305 00306 if (x + m_completionPopup->width() > QApplication::desktop()->width()) 00307 x = QApplication::desktop()->width() - m_completionPopup->width(); 00308 00309 m_completionPopup->move( QPoint(x,y) ); 00310 00311 m_completionListBox->setCurrentItem( 0 ); 00312 m_completionListBox->setSelected( 0, true ); 00313 m_completionListBox->setFocus(); 00314 m_completionPopup->show(); 00315 00316 QTimer::singleShot(0,this,SLOT(showComment())); 00317 } 00318 00319 void KateCodeCompletion::showArgHint ( QStringList functionList, const QString& strWrapping, const QString& strDelimiter ) 00320 { 00321 unsigned int line, col; 00322 m_view->cursorPositionReal( &line, &col ); 00323 m_pArgHint->reset( line, col ); 00324 m_pArgHint->setArgMarkInfos( strWrapping, strDelimiter ); 00325 00326 int nNum = 0; 00327 for( QStringList::Iterator it = functionList.begin(); it != functionList.end(); it++ ) 00328 { 00329 kdDebug(13035) << "Insert function text: " << *it << endl; 00330 00331 m_pArgHint->addFunction( nNum, ( *it ) ); 00332 00333 nNum++; 00334 } 00335 00336 m_pArgHint->move(m_view->mapToGlobal(m_view->cursorCoordinates() + QPoint(0,m_view->renderer()->config()->fontMetrics( )->height())) ); 00337 m_pArgHint->show(); 00338 } 00339 00340 void KateCodeCompletion::slotCursorPosChanged() 00341 { 00342 m_pArgHint->cursorPositionChanged ( m_view, m_view->cursorLine(), m_view->cursorColumnReal() ); 00343 } 00344 00345 void KateCodeCompletion::showComment() 00346 { 00347 if (!m_completionPopup->isVisible()) 00348 return; 00349 00350 KateCompletionItem* item = static_cast<KateCompletionItem*>(m_completionListBox->item(m_completionListBox->currentItem())); 00351 00352 if( !item ) 00353 return; 00354 00355 if( item->m_entry.comment.isEmpty() ) 00356 return; 00357 00358 delete m_commentLabel; 00359 m_commentLabel = new KateCodeCompletionCommentLabel( 0, item->m_entry.comment ); 00360 m_commentLabel->setFont(QToolTip::font()); 00361 m_commentLabel->setPalette(QToolTip::palette()); 00362 00363 QPoint rightPoint = m_completionPopup->mapToGlobal(QPoint(m_completionPopup->width(),0)); 00364 QPoint leftPoint = m_completionPopup->mapToGlobal(QPoint(0,0)); 00365 QRect screen = QApplication::desktop()->screenGeometry ( m_commentLabel ); 00366 QPoint finalPoint; 00367 if (rightPoint.x()+m_commentLabel->width() > screen.x() + screen.width()) 00368 finalPoint.setX(leftPoint.x()-m_commentLabel->width()); 00369 else 00370 finalPoint.setX(rightPoint.x()); 00371 00372 m_completionListBox->ensureCurrentVisible(); 00373 00374 finalPoint.setY( 00375 m_completionListBox->viewport()->mapToGlobal(m_completionListBox->itemRect( 00376 m_completionListBox->item(m_completionListBox->currentItem())).topLeft()).y()); 00377 00378 m_commentLabel->move(finalPoint); 00379 m_commentLabel->show(); 00380 } 00381 00382 KateArgHint::KateArgHint( KateView* parent, const char* name ) 00383 : QFrame( parent, name, WType_Popup ) 00384 { 00385 setBackgroundColor( black ); 00386 setPaletteForegroundColor( Qt::black ); 00387 00388 labelDict.setAutoDelete( true ); 00389 layout = new QVBoxLayout( this, 1, 2 ); 00390 layout->setAutoAdd( true ); 00391 editorView = parent; 00392 00393 m_markCurrentFunction = true; 00394 00395 setFocusPolicy( StrongFocus ); 00396 setFocusProxy( parent ); 00397 00398 reset( -1, -1 ); 00399 } 00400 00401 KateArgHint::~KateArgHint() 00402 { 00403 } 00404 00405 void KateArgHint::setArgMarkInfos( const QString& wrapping, const QString& delimiter ) 00406 { 00407 m_wrapping = wrapping; 00408 m_delimiter = delimiter; 00409 m_markCurrentFunction = true; 00410 } 00411 00412 void KateArgHint::reset( int line, int col ) 00413 { 00414 m_functionMap.clear(); 00415 m_currentFunction = -1; 00416 labelDict.clear(); 00417 00418 m_currentLine = line; 00419 m_currentCol = col - 1; 00420 } 00421 00422 void KateArgHint::slotDone(bool completed) 00423 { 00424 hide(); 00425 00426 m_currentLine = m_currentCol = -1; 00427 00428 emit argHintHidden(); 00429 if (completed) 00430 emit argHintCompleted(); 00431 else 00432 emit argHintAborted(); 00433 } 00434 00435 void KateArgHint::cursorPositionChanged( KateView* view, int line, int col ) 00436 { 00437 if( m_currentCol == -1 || m_currentLine == -1 ){ 00438 slotDone(false); 00439 return; 00440 } 00441 00442 int nCountDelimiter = 0; 00443 int count = 0; 00444 00445 QString currentTextLine = view->doc()->textLine( line ); 00446 QString text = currentTextLine.mid( m_currentCol, col - m_currentCol ); 00447 QRegExp strconst_rx( "\"[^\"]*\"" ); 00448 QRegExp chrconst_rx( "'[^']*'" ); 00449 00450 text = text 00451 .replace( strconst_rx, "\"\"" ) 00452 .replace( chrconst_rx, "''" ); 00453 00454 int index = 0; 00455 while( index < (int)text.length() ){ 00456 if( text[index] == m_wrapping[0] ){ 00457 ++count; 00458 } else if( text[index] == m_wrapping[1] ){ 00459 --count; 00460 } else if( count > 0 && text[index] == m_delimiter[0] ){ 00461 ++nCountDelimiter; 00462 } 00463 ++index; 00464 } 00465 00466 if( (m_currentLine > 0 && m_currentLine != line) || (m_currentLine < col) || (count == 0) ){ 00467 slotDone(count == 0); 00468 return; 00469 } 00470 00471 // setCurArg ( nCountDelimiter + 1 ); 00472 00473 } 00474 00475 void KateArgHint::addFunction( int id, const QString& prot ) 00476 { 00477 m_functionMap[ id ] = prot; 00478 QLabel* label = new QLabel( prot.stripWhiteSpace().simplifyWhiteSpace(), this ); 00479 label->setBackgroundColor( QColor(255, 255, 238) ); 00480 label->show(); 00481 labelDict.insert( id, label ); 00482 00483 if( m_currentFunction < 0 ) 00484 setCurrentFunction( id ); 00485 } 00486 00487 void KateArgHint::setCurrentFunction( int currentFunction ) 00488 { 00489 if( m_currentFunction != currentFunction ){ 00490 00491 if( currentFunction < 0 ) 00492 currentFunction = (int)m_functionMap.size() - 1; 00493 00494 if( currentFunction > (int)m_functionMap.size()-1 ) 00495 currentFunction = 0; 00496 00497 if( m_markCurrentFunction && m_currentFunction >= 0 ){ 00498 QLabel* label = labelDict[ m_currentFunction ]; 00499 label->setFont( font() ); 00500 } 00501 00502 m_currentFunction = currentFunction; 00503 00504 if( m_markCurrentFunction ){ 00505 QLabel* label = labelDict[ currentFunction ]; 00506 QFont fnt( font() ); 00507 fnt.setBold( true ); 00508 label->setFont( fnt ); 00509 } 00510 00511 adjustSize(); 00512 } 00513 } 00514 00515 void KateArgHint::show() 00516 { 00517 QFrame::show(); 00518 adjustSize(); 00519 } 00520 00521 bool KateArgHint::eventFilter( QObject*, QEvent* e ) 00522 { 00523 if( isVisible() && e->type() == QEvent::KeyPress ){ 00524 QKeyEvent* ke = static_cast<QKeyEvent*>( e ); 00525 if( (ke->state() & ControlButton) && ke->key() == Key_Left ){ 00526 setCurrentFunction( currentFunction() - 1 ); 00527 ke->accept(); 00528 return true; 00529 } else if( ke->key() == Key_Escape ){ 00530 slotDone(false); 00531 return false; 00532 } else if( (ke->state() & ControlButton) && ke->key() == Key_Right ){ 00533 setCurrentFunction( currentFunction() + 1 ); 00534 ke->accept(); 00535 return true; 00536 } 00537 } 00538 00539 return false; 00540 } 00541 00542 void KateArgHint::adjustSize( ) 00543 { 00544 QRect screen = QApplication::desktop()->screenGeometry( pos() ); 00545 00546 QFrame::adjustSize(); 00547 if( width() > screen.width() ) 00548 resize( screen.width(), height() ); 00549 00550 if( x() + width() > screen.width() ) 00551 move( screen.width() - width(), y() ); 00552 } 00553 00554 // kate: space-indent on; indent-width 2; replace-tabs on;
KDE Logo
This file is part of the documentation for kate Library Version 3.4.0.
Documentation copyright © 1996-2004 the KDE developers.
Generated on Thu Apr 14 00:46:03 2005 by doxygen 1.3.7 written by Dimitri van Heesch, © 1997-2003