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