| # kate Library API Documentation
Overview | Class Hierarchy | Classes | Classes (annotated) | Members | Namespaces | Source Files

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

This file is part of the documentation for kate Library Version 3.4.1.
Documentation copyright © 1996-2004 the KDE developers.
Generated on Sat Jun 11 20:36:30 2005 by doxygen 1.4.1 written by Dimitri van Heesch, © 1997-2003