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::FocusOut ) 00166 { 00167 abortCompletion(); 00168 m_view->setFocus(); 00169 return false; 00170 } 00171 00172 if ( e->type() == QEvent::MouseButtonDblClick ) { 00173 doComplete(); 00174 return false; 00175 } 00176 00177 if ( e->type() == QEvent::MouseButtonPress ) { 00178 QTimer::singleShot(0, this, SLOT(showComment())); 00179 return false; 00180 } 00181 00182 return false; 00183 } 00184 00185 void KateCodeCompletion::handleKey (QKeyEvent *e) 00186 { 00187 // close completion if you move out of range 00188 if ((e->key() == Key_Up) && (m_completionListBox->currentItem() == 0)) 00189 { 00190 abortCompletion(); 00191 m_view->setFocus(); 00192 return; 00193 } 00194 00195 // keyboard movement 00196 if( (e->key() == Key_Up) || (e->key() == Key_Down ) || 00197 (e->key() == Key_Home ) || (e->key() == Key_End) || 00198 (e->key() == Key_Prior) || (e->key() == Key_Next )) 00199 { 00200 QTimer::singleShot(0,this,SLOT(showComment())); 00201 QApplication::sendEvent( m_completionListBox, (QEvent*)e ); 00202 return; 00203 } 00204 00205 // update the box 00206 updateBox(); 00207 } 00208 00209 void KateCodeCompletion::doComplete() 00210 { 00211 KateCompletionItem* item = static_cast<KateCompletionItem*>( 00212 m_completionListBox->item(m_completionListBox->currentItem())); 00213 00214 if( item == 0 ) 00215 return; 00216 00217 QString text = item->m_entry.text; 00218 QString currentLine = m_view->currentTextLine(); 00219 int len = m_view->cursorColumnReal() - m_colCursor; 00220 QString currentComplText = currentLine.mid(m_colCursor,len); 00221 QString add = text.mid(currentComplText.length()); 00222 if( item->m_entry.postfix == "()" ) 00223 add += "("; 00224 00225 emit filterInsertString(&(item->m_entry),&add); 00226 m_view->insertText(add); 00227 00228 complete( item->m_entry ); 00229 m_view->setFocus(); 00230 } 00231 00232 void KateCodeCompletion::abortCompletion() 00233 { 00234 m_completionPopup->hide(); 00235 delete m_commentLabel; 00236 m_commentLabel = 0; 00237 emit completionAborted(); 00238 } 00239 00240 void KateCodeCompletion::complete( KTextEditor::CompletionEntry entry ) 00241 { 00242 m_completionPopup->hide(); 00243 delete m_commentLabel; 00244 m_commentLabel = 0; 00245 emit completionDone( entry ); 00246 emit completionDone(); 00247 } 00248 00249 void KateCodeCompletion::updateBox( bool ) 00250 { 00251 if( m_colCursor > m_view->cursorColumnReal() ) { 00252 // the cursor is too far left 00253 kdDebug(13035) << "Aborting Codecompletion after sendEvent" << endl; 00254 kdDebug(13035) << m_view->cursorColumnReal() << endl; 00255 abortCompletion(); 00256 m_view->setFocus(); 00257 return; 00258 } 00259 00260 m_completionListBox->clear(); 00261 00262 QString currentLine = m_view->currentTextLine(); 00263 int len = m_view->cursorColumnReal() - m_colCursor; 00264 QString currentComplText = currentLine.mid(m_colCursor,len); 00265 /* No-one really badly wants those, or? 00266 kdDebug(13035) << "Column: " << m_colCursor << endl; 00267 kdDebug(13035) << "Line: " << currentLine << endl; 00268 kdDebug(13035) << "CurrentColumn: " << m_view->cursorColumnReal() << endl; 00269 kdDebug(13035) << "Len: " << len << endl; 00270 kdDebug(13035) << "Text: '" << currentComplText << "'" << endl; 00271 kdDebug(13035) << "Count: " << m_complList.count() << endl; 00272 */ 00273 QValueList<KTextEditor::CompletionEntry>::Iterator it; 00274 if( m_caseSensitive ) { 00275 for( it = m_complList.begin(); it != m_complList.end(); ++it ) { 00276 if( (*it).text.startsWith(currentComplText) ) { 00277 new KateCompletionItem(m_completionListBox,*it); 00278 } 00279 } 00280 } else { 00281 currentComplText = currentComplText.upper(); 00282 for( it = m_complList.begin(); it != m_complList.end(); ++it ) { 00283 if( (*it).text.upper().startsWith(currentComplText) ) { 00284 new KateCompletionItem(m_completionListBox,*it); 00285 } 00286 } 00287 } 00288 00289 if( m_completionListBox->count() == 0 || 00290 ( m_completionListBox->count() == 1 && // abort if we equaled the last item 00291 currentComplText == m_completionListBox->text(0).stripWhiteSpace() ) ) { 00292 abortCompletion(); 00293 m_view->setFocus(); 00294 return; 00295 } 00296 00297 kdDebug(13035)<<"KateCodeCompletion::updateBox: Resizing widget"<<endl; 00298 m_completionPopup->resize(m_completionListBox->sizeHint() + QSize(2,2)); 00299 QPoint p = m_view->mapToGlobal( m_view->cursorCoordinates() ); 00300 int x = p.x(); 00301 int y = p.y() ; 00302 if ( y + m_completionPopup->height() + m_view->renderer()->config()->fontMetrics( )->height() > QApplication::desktop()->height() ) 00303 y -= (m_completionPopup->height() ); 00304 else 00305 y += m_view->renderer()->config()->fontMetrics( )->height(); 00306 00307 if (x + m_completionPopup->width() > QApplication::desktop()->width()) 00308 x = QApplication::desktop()->width() - m_completionPopup->width(); 00309 00310 m_completionPopup->move( QPoint(x,y) ); 00311 00312 m_completionListBox->setCurrentItem( 0 ); 00313 m_completionListBox->setSelected( 0, true ); 00314 m_completionListBox->setFocus(); 00315 m_completionPopup->show(); 00316 00317 QTimer::singleShot(0,this,SLOT(showComment())); 00318 } 00319 00320 void KateCodeCompletion::showArgHint ( QStringList functionList, const QString& strWrapping, const QString& strDelimiter ) 00321 { 00322 unsigned int line, col; 00323 m_view->cursorPositionReal( &line, &col ); 00324 m_pArgHint->reset( line, col ); 00325 m_pArgHint->setArgMarkInfos( strWrapping, strDelimiter ); 00326 00327 int nNum = 0; 00328 for( QStringList::Iterator it = functionList.begin(); it != functionList.end(); it++ ) 00329 { 00330 kdDebug(13035) << "Insert function text: " << *it << endl; 00331 00332 m_pArgHint->addFunction( nNum, ( *it ) ); 00333 00334 nNum++; 00335 } 00336 00337 m_pArgHint->move(m_view->mapToGlobal(m_view->cursorCoordinates() + QPoint(0,m_view->renderer()->config()->fontMetrics( )->height())) ); 00338 m_pArgHint->show(); 00339 } 00340 00341 void KateCodeCompletion::slotCursorPosChanged() 00342 { 00343 m_pArgHint->cursorPositionChanged ( m_view, m_view->cursorLine(), m_view->cursorColumnReal() ); 00344 } 00345 00346 void KateCodeCompletion::showComment() 00347 { 00348 if (!m_completionPopup->isVisible()) 00349 return; 00350 00351 KateCompletionItem* item = static_cast<KateCompletionItem*>(m_completionListBox->item(m_completionListBox->currentItem())); 00352 00353 if( !item ) 00354 return; 00355 00356 if( item->m_entry.comment.isEmpty() ) 00357 return; 00358 00359 delete m_commentLabel; 00360 m_commentLabel = new KateCodeCompletionCommentLabel( 0, item->m_entry.comment ); 00361 m_commentLabel->setFont(QToolTip::font()); 00362 m_commentLabel->setPalette(QToolTip::palette()); 00363 00364 QPoint rightPoint = m_completionPopup->mapToGlobal(QPoint(m_completionPopup->width(),0)); 00365 QPoint leftPoint = m_completionPopup->mapToGlobal(QPoint(0,0)); 00366 QRect screen = QApplication::desktop()->screenGeometry ( m_commentLabel ); 00367 QPoint finalPoint; 00368 if (rightPoint.x()+m_commentLabel->width() > screen.x() + screen.width()) 00369 finalPoint.setX(leftPoint.x()-m_commentLabel->width()); 00370 else 00371 finalPoint.setX(rightPoint.x()); 00372 00373 m_completionListBox->ensureCurrentVisible(); 00374 00375 finalPoint.setY( 00376 m_completionListBox->viewport()->mapToGlobal(m_completionListBox->itemRect( 00377 m_completionListBox->item(m_completionListBox->currentItem())).topLeft()).y()); 00378 00379 m_commentLabel->move(finalPoint); 00380 m_commentLabel->show(); 00381 } 00382 00383 KateArgHint::KateArgHint( KateView* parent, const char* name ) 00384 : QFrame( parent, name, WType_Popup ) 00385 { 00386 setBackgroundColor( black ); 00387 setPaletteForegroundColor( Qt::black ); 00388 00389 labelDict.setAutoDelete( true ); 00390 layout = new QVBoxLayout( this, 1, 2 ); 00391 layout->setAutoAdd( true ); 00392 editorView = parent; 00393 00394 m_markCurrentFunction = true; 00395 00396 setFocusPolicy( StrongFocus ); 00397 setFocusProxy( parent ); 00398 00399 reset( -1, -1 ); 00400 } 00401 00402 KateArgHint::~KateArgHint() 00403 { 00404 } 00405 00406 void KateArgHint::setArgMarkInfos( const QString& wrapping, const QString& delimiter ) 00407 { 00408 m_wrapping = wrapping; 00409 m_delimiter = delimiter; 00410 m_markCurrentFunction = true; 00411 } 00412 00413 void KateArgHint::reset( int line, int col ) 00414 { 00415 m_functionMap.clear(); 00416 m_currentFunction = -1; 00417 labelDict.clear(); 00418 00419 m_currentLine = line; 00420 m_currentCol = col - 1; 00421 } 00422 00423 void KateArgHint::slotDone(bool completed) 00424 { 00425 hide(); 00426 00427 m_currentLine = m_currentCol = -1; 00428 00429 emit argHintHidden(); 00430 if (completed) 00431 emit argHintCompleted(); 00432 else 00433 emit argHintAborted(); 00434 } 00435 00436 void KateArgHint::cursorPositionChanged( KateView* view, int line, int col ) 00437 { 00438 if( m_currentCol == -1 || m_currentLine == -1 ){ 00439 slotDone(false); 00440 return; 00441 } 00442 00443 int nCountDelimiter = 0; 00444 int count = 0; 00445 00446 QString currentTextLine = view->doc()->textLine( line ); 00447 QString text = currentTextLine.mid( m_currentCol, col - m_currentCol ); 00448 QRegExp strconst_rx( "\"[^\"]*\"" ); 00449 QRegExp chrconst_rx( "'[^']*'" ); 00450 00451 text = text 00452 .replace( strconst_rx, "\"\"" ) 00453 .replace( chrconst_rx, "''" ); 00454 00455 int index = 0; 00456 while( index < (int)text.length() ){ 00457 if( text[index] == m_wrapping[0] ){ 00458 ++count; 00459 } else if( text[index] == m_wrapping[1] ){ 00460 --count; 00461 } else if( count > 0 && text[index] == m_delimiter[0] ){ 00462 ++nCountDelimiter; 00463 } 00464 ++index; 00465 } 00466 00467 if( (m_currentLine > 0 && m_currentLine != line) || (m_currentLine < col) || (count == 0) ){ 00468 slotDone(count == 0); 00469 return; 00470 } 00471 00472 // setCurArg ( nCountDelimiter + 1 ); 00473 00474 } 00475 00476 void KateArgHint::addFunction( int id, const QString& prot ) 00477 { 00478 m_functionMap[ id ] = prot; 00479 QLabel* label = new QLabel( prot.stripWhiteSpace().simplifyWhiteSpace(), this ); 00480 label->setBackgroundColor( QColor(255, 255, 238) ); 00481 label->show(); 00482 labelDict.insert( id, label ); 00483 00484 if( m_currentFunction < 0 ) 00485 setCurrentFunction( id ); 00486 } 00487 00488 void KateArgHint::setCurrentFunction( int currentFunction ) 00489 { 00490 if( m_currentFunction != currentFunction ){ 00491 00492 if( currentFunction < 0 ) 00493 currentFunction = (int)m_functionMap.size() - 1; 00494 00495 if( currentFunction > (int)m_functionMap.size()-1 ) 00496 currentFunction = 0; 00497 00498 if( m_markCurrentFunction && m_currentFunction >= 0 ){ 00499 QLabel* label = labelDict[ m_currentFunction ]; 00500 label->setFont( font() ); 00501 } 00502 00503 m_currentFunction = currentFunction; 00504 00505 if( m_markCurrentFunction ){ 00506 QLabel* label = labelDict[ currentFunction ]; 00507 QFont fnt( font() ); 00508 fnt.setBold( true ); 00509 label->setFont( fnt ); 00510 } 00511 00512 adjustSize(); 00513 } 00514 } 00515 00516 void KateArgHint::show() 00517 { 00518 QFrame::show(); 00519 adjustSize(); 00520 } 00521 00522 bool KateArgHint::eventFilter( QObject*, QEvent* e ) 00523 { 00524 if( isVisible() && e->type() == QEvent::KeyPress ){ 00525 QKeyEvent* ke = static_cast<QKeyEvent*>( e ); 00526 if( (ke->state() & ControlButton) && ke->key() == Key_Left ){ 00527 setCurrentFunction( currentFunction() - 1 ); 00528 ke->accept(); 00529 return true; 00530 } else if( ke->key() == Key_Escape ){ 00531 slotDone(false); 00532 return false; 00533 } else if( (ke->state() & ControlButton) && ke->key() == Key_Right ){ 00534 setCurrentFunction( currentFunction() + 1 ); 00535 ke->accept(); 00536 return true; 00537 } 00538 } 00539 00540 return false; 00541 } 00542 00543 void KateArgHint::adjustSize( ) 00544 { 00545 QRect screen = QApplication::desktop()->screenGeometry( pos() ); 00546 00547 QFrame::adjustSize(); 00548 if( width() > screen.width() ) 00549 resize( screen.width(), height() ); 00550 00551 if( x() + width() > screen.width() ) 00552 move( screen.width() - width(), y() ); 00553 } 00554 00555 // kate: space-indent on; indent-width 2; replace-tabs on;
KDE Logo
This file is part of the documentation for kate Library Version 3.3.1.
Documentation copyright © 1996-2004 the KDE developers.
Generated on Sun Oct 17 11:35:09 2004 by doxygen 1.3.8 written by Dimitri van Heesch, © 1997-2003