• Skip to content
  • Skip to link menu
KDE 3.5 API Reference
  • KDE API Reference
  • API Reference
  • Sitemap
  • Contact Us
 

Kate

katecodecompletion.cpp

Go to the documentation of this file.
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., 51 Franklin Street, Fifth Floor,
00019    Boston, MA 02110-1301, 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:
00060     KateCCListBox (QWidget* parent = 0, const char* name = 0, WFlags f = 0):QListBox(parent, name, f)
00061     {
00062     }
00063 
00064     QSize sizeHint()  const
00065     {
00066         int count = this->count();
00067         int height = 20;
00068         int tmpwidth = 8;
00069         //FIXME the height is for some reasons at least 3 items heigh, even if there is only one item in the list
00070         if (count > 0)
00071             if(count < 11)
00072                 height =  count * itemHeight(0);
00073             else  {
00074                 height = 10 * itemHeight(0);
00075                 tmpwidth += verticalScrollBar()->width();
00076             }
00077 
00078         int maxcount = 0, tmpcount = 0;
00079         for (int i = 0; i < count; ++i)
00080             if ( (tmpcount = fontMetrics().width(text(i)) ) > maxcount)
00081                     maxcount = tmpcount;
00082 
00083         if (maxcount > QApplication::desktop()->width()){
00084             tmpwidth = QApplication::desktop()->width() - 5;
00085             height += horizontalScrollBar()->height();
00086         } else
00087             tmpwidth += maxcount;
00088         return QSize(tmpwidth,height);
00089 
00090     }
00091 };
00092 
00093 class KateCompletionItem : public QListBoxText
00094 {
00095   public:
00096     KateCompletionItem( QListBox* lb, KTextEditor::CompletionEntry entry )
00097       : QListBoxText( lb )
00098       , m_entry( entry )
00099     {
00100       if( entry.postfix == "()" ) { // should be configurable
00101         setText( entry.prefix + " " + entry.text + entry.postfix );
00102       } else {
00103         setText( entry.prefix + " " + entry.text + " " + entry.postfix);
00104       }
00105     }
00106 
00107     KTextEditor::CompletionEntry m_entry;
00108 };
00109 
00110 
00111 KateCodeCompletion::KateCodeCompletion( KateView* view )
00112   : QObject( view, "Kate Code Completion" )
00113   , m_view( view )
00114   , m_commentLabel( 0 )
00115 {
00116   m_completionPopup = new QVBox( 0, 0, WType_Popup );
00117   m_completionPopup->setFrameStyle( QFrame::Box | QFrame::Plain );
00118   m_completionPopup->setLineWidth( 1 );
00119 
00120   m_completionListBox = new KateCCListBox( m_completionPopup );
00121   m_completionListBox->setFrameStyle( QFrame::NoFrame );
00122   //m_completionListBox->setCornerWidget( new QSizeGrip( m_completionListBox) );
00123   m_completionListBox->setFocusProxy( m_view->m_viewInternal );
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_view->m_viewInternal );
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 KateCodeCompletion::~KateCodeCompletion()
00140 {
00141   delete m_completionPopup;
00142 }
00143 
00144 bool KateCodeCompletion::codeCompletionVisible () {
00145   return m_completionPopup->isVisible();
00146 }
00147 
00148 void KateCodeCompletion::showCompletionBox(
00149     QValueList<KTextEditor::CompletionEntry> complList, int offset, bool casesensitive )
00150 {
00151   kdDebug(13035) << "showCompletionBox " << endl;
00152 
00153   if ( codeCompletionVisible() ) return;
00154 
00155   m_caseSensitive = casesensitive;
00156   m_complList = complList;
00157   m_offset = offset;
00158   m_view->cursorPositionReal( &m_lineCursor, &m_colCursor );
00159   m_colCursor -= offset;
00160 
00161   updateBox( true );
00162 }
00163 
00164 bool KateCodeCompletion::eventFilter( QObject *o, QEvent *e )
00165 {
00166   if ( o != m_completionPopup &&
00167        o != m_completionListBox &&
00168        o != m_completionListBox->viewport() )
00169     return false;
00170 
00171    if( e->type() == QEvent::Hide )
00172    { 
00173      //don't use abortCompletion() as aborting here again will send abort signal
00174      //even on successfull completion we will emit completionAborted() twice...
00175      m_completionPopup->hide();
00176      delete m_commentLabel;
00177      m_commentLabel = 0;
00178      return false;
00179    }
00180 
00181 
00182    if ( e->type() == QEvent::MouseButtonDblClick  ) {
00183     doComplete();
00184     return false;
00185    }
00186 
00187    if ( e->type() == QEvent::MouseButtonPress ) {
00188     QTimer::singleShot(0, this, SLOT(showComment()));
00189     return false;
00190    }
00191 
00192   return false;
00193 }
00194 
00195 void KateCodeCompletion::handleKey (QKeyEvent *e)
00196 {
00197   // close completion if you move out of range
00198   if ((e->key() == Key_Up) && (m_completionListBox->currentItem() == 0))
00199   {
00200     abortCompletion();
00201     m_view->setFocus();
00202     return;
00203   }
00204 
00205   // keyboard movement
00206   if( (e->key() == Key_Up)    || (e->key() == Key_Down ) ||
00207         (e->key() == Key_Home ) || (e->key() == Key_End)   ||
00208         (e->key() == Key_Prior) || (e->key() == Key_Next ))
00209   {
00210     QTimer::singleShot(0,this,SLOT(showComment()));
00211     QApplication::sendEvent( m_completionListBox, (QEvent*)e );
00212     return;
00213   }
00214 
00215   // update the box
00216   updateBox();
00217 }
00218 
00219 void KateCodeCompletion::doComplete()
00220 {
00221   KateCompletionItem* item = static_cast<KateCompletionItem*>(
00222      m_completionListBox->item(m_completionListBox->currentItem()));
00223 
00224   if( item == 0 )
00225     return;
00226 
00227   QString text = item->m_entry.text;
00228   QString currentLine = m_view->currentTextLine();
00229   int len = m_view->cursorColumnReal() - m_colCursor;
00230   QString currentComplText = currentLine.mid(m_colCursor,len);
00231   QString add = text.mid(currentComplText.length());
00232   if( item->m_entry.postfix == "()" )
00233     add += "(";
00234 
00235   emit filterInsertString(&(item->m_entry),&add);
00236   m_view->insertText(add);
00237 
00238   complete( item->m_entry );
00239   m_view->setFocus();
00240 }
00241 
00242 void KateCodeCompletion::abortCompletion()
00243 {
00244   m_completionPopup->hide();
00245   delete m_commentLabel;
00246   m_commentLabel = 0;
00247   emit completionAborted();
00248 }
00249 
00250 void KateCodeCompletion::complete( KTextEditor::CompletionEntry entry )
00251 {
00252   m_completionPopup->hide();
00253   delete m_commentLabel;
00254   m_commentLabel = 0;
00255   emit completionDone( entry );
00256   emit completionDone();
00257 }
00258 
00259 void KateCodeCompletion::updateBox( bool )
00260 {
00261   if( m_colCursor > m_view->cursorColumnReal() ) {
00262     // the cursor is too far left
00263     kdDebug(13035) << "Aborting Codecompletion after sendEvent" << endl;
00264     kdDebug(13035) << m_view->cursorColumnReal() << endl;
00265     abortCompletion();
00266     m_view->setFocus();
00267     return;
00268   }
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   QStringList::Iterator end(functionList.end());
00339   for( QStringList::Iterator it = functionList.begin(); it != end; ++it )
00340   {
00341     kdDebug(13035) << "Insert function text: " << *it << endl;
00342 
00343     m_pArgHint->addFunction( nNum, ( *it ) );
00344 
00345     nNum++;
00346   }
00347 
00348   m_pArgHint->move(m_view->mapToGlobal(m_view->cursorCoordinates() + QPoint(0,m_view->renderer()->config()->fontMetrics( )->height())) );
00349   m_pArgHint->show();
00350 }
00351 
00352 void KateCodeCompletion::slotCursorPosChanged()
00353 {
00354   m_pArgHint->cursorPositionChanged ( m_view, m_view->cursorLine(), m_view->cursorColumnReal() );
00355 }
00356 
00357 void KateCodeCompletion::showComment()
00358 {
00359   if (!m_completionPopup->isVisible())
00360     return;
00361 
00362   KateCompletionItem* item = static_cast<KateCompletionItem*>(m_completionListBox->item(m_completionListBox->currentItem()));
00363 
00364   if( !item )
00365     return;
00366 
00367   if( item->m_entry.comment.isEmpty() )
00368     return;
00369 
00370   delete m_commentLabel;
00371   m_commentLabel = new KateCodeCompletionCommentLabel( 0, item->m_entry.comment );
00372   m_commentLabel->setFont(QToolTip::font());
00373   m_commentLabel->setPalette(QToolTip::palette());
00374 
00375   QPoint rightPoint = m_completionPopup->mapToGlobal(QPoint(m_completionPopup->width(),0));
00376   QPoint leftPoint = m_completionPopup->mapToGlobal(QPoint(0,0));
00377   QRect screen = QApplication::desktop()->screenGeometry ( m_commentLabel );
00378   QPoint finalPoint;
00379   if (rightPoint.x()+m_commentLabel->width() > screen.x() + screen.width())
00380     finalPoint.setX(leftPoint.x()-m_commentLabel->width());
00381   else
00382     finalPoint.setX(rightPoint.x());
00383 
00384   m_completionListBox->ensureCurrentVisible();
00385 
00386   finalPoint.setY(
00387     m_completionListBox->viewport()->mapToGlobal(m_completionListBox->itemRect(
00388       m_completionListBox->item(m_completionListBox->currentItem())).topLeft()).y());
00389 
00390   m_commentLabel->move(finalPoint);
00391   m_commentLabel->show();
00392 }
00393 
00394 KateArgHint::KateArgHint( KateView* parent, const char* name )
00395     : QFrame( parent, name, WType_Popup )
00396 {
00397     setBackgroundColor( black );
00398     setPaletteForegroundColor( Qt::black );
00399 
00400     labelDict.setAutoDelete( true );
00401     layout = new QVBoxLayout( this, 1, 2 );
00402     layout->setAutoAdd( true );
00403     editorView = parent;
00404 
00405     m_markCurrentFunction = true;
00406 
00407     setFocusPolicy( StrongFocus );
00408     setFocusProxy( parent );
00409 
00410     reset( -1, -1 );
00411 }
00412 
00413 KateArgHint::~KateArgHint()
00414 {
00415 }
00416 
00417 void KateArgHint::setArgMarkInfos( const QString& wrapping, const QString& delimiter )
00418 {
00419     m_wrapping = wrapping;
00420     m_delimiter = delimiter;
00421     m_markCurrentFunction = true;
00422 }
00423 
00424 void KateArgHint::reset( int line, int col )
00425 {
00426     m_functionMap.clear();
00427     m_currentFunction = -1;
00428     labelDict.clear();
00429 
00430     m_currentLine = line;
00431     m_currentCol = col - 1;
00432 }
00433 
00434 void KateArgHint::slotDone(bool completed)
00435 {
00436     hide();
00437 
00438     m_currentLine = m_currentCol = -1;
00439 
00440     emit argHintHidden();
00441     if (completed)
00442         emit argHintCompleted();
00443     else
00444         emit argHintAborted();
00445 }
00446 
00447 void KateArgHint::cursorPositionChanged( KateView* view, int line, int col )
00448 {
00449     if( m_currentCol == -1 || m_currentLine == -1 ){
00450         slotDone(false);
00451         return;
00452     }
00453 
00454     int nCountDelimiter = 0;
00455     int count = 0;
00456 
00457     QString currentTextLine = view->doc()->textLine( line );
00458     QString text = currentTextLine.mid( m_currentCol, col - m_currentCol );
00459     QRegExp strconst_rx( "\"[^\"]*\"" );
00460     QRegExp chrconst_rx( "'[^']*'" );
00461 
00462     text = text
00463         .replace( strconst_rx, "\"\"" )
00464         .replace( chrconst_rx, "''" );
00465 
00466     int index = 0;
00467     while( index < (int)text.length() ){
00468         if( text[index] == m_wrapping[0] ){
00469             ++count;
00470         } else if( text[index] == m_wrapping[1] ){
00471             --count;
00472         } else if( count > 0 && text[index] == m_delimiter[0] ){
00473             ++nCountDelimiter;
00474         }
00475         ++index;
00476     }
00477 
00478     if( (m_currentLine > 0 && m_currentLine != line) || (m_currentLine < col) || (count == 0) ){
00479         slotDone(count == 0);
00480         return;
00481     }
00482 
00483     // setCurArg ( nCountDelimiter + 1 );
00484 
00485 }
00486 
00487 void KateArgHint::addFunction( int id, const QString& prot )
00488 {
00489     m_functionMap[ id ] = prot;
00490     QLabel* label = new QLabel( prot.stripWhiteSpace().simplifyWhiteSpace(), this );
00491     label->setBackgroundColor( QColor(255, 255, 238) );
00492     label->show();
00493     labelDict.insert( id, label );
00494 
00495     if( m_currentFunction < 0 )
00496         setCurrentFunction( id );
00497 }
00498 
00499 void KateArgHint::setCurrentFunction( int currentFunction )
00500 {
00501     if( m_currentFunction != currentFunction ){
00502 
00503         if( currentFunction < 0 )
00504             currentFunction = (int)m_functionMap.size() - 1;
00505 
00506         if( currentFunction > (int)m_functionMap.size()-1 )
00507             currentFunction = 0;
00508 
00509         if( m_markCurrentFunction && m_currentFunction >= 0 ){
00510             QLabel* label = labelDict[ m_currentFunction ];
00511             label->setFont( font() );
00512         }
00513 
00514         m_currentFunction = currentFunction;
00515 
00516         if( m_markCurrentFunction ){
00517             QLabel* label = labelDict[ currentFunction ];
00518             QFont fnt( font() );
00519             fnt.setBold( true );
00520             label->setFont( fnt );
00521         }
00522 
00523         adjustSize();
00524     }
00525 }
00526 
00527 void KateArgHint::show()
00528 {
00529     QFrame::show();
00530     adjustSize();
00531 }
00532 
00533 bool KateArgHint::eventFilter( QObject*, QEvent* e )
00534 {
00535     if( isVisible() && e->type() == QEvent::KeyPress ){
00536         QKeyEvent* ke = static_cast<QKeyEvent*>( e );
00537         if( (ke->state() & ControlButton) && ke->key() == Key_Left ){
00538             setCurrentFunction( currentFunction() - 1 );
00539             ke->accept();
00540             return true;
00541         } else if( ke->key() == Key_Escape ){
00542             slotDone(false);
00543             return false;
00544         } else if( (ke->state() & ControlButton) && ke->key() == Key_Right ){
00545             setCurrentFunction( currentFunction() + 1 );
00546             ke->accept();
00547             return true;
00548         }
00549     }
00550 
00551     return false;
00552 }
00553 
00554 void KateArgHint::adjustSize( )
00555 {
00556     QRect screen = QApplication::desktop()->screenGeometry( pos() );
00557 
00558     QFrame::adjustSize();
00559     if( width() > screen.width() )
00560         resize( screen.width(), height() );
00561 
00562     if( x() + width() > screen.x() + screen.width() )
00563         move( screen.x() + screen.width() - width(), y() );
00564 }
00565 
00566 // kate: space-indent on; indent-width 2; replace-tabs on;

Kate

Skip menu "Kate"
  • Main Page
  • Namespace List
  • Class Hierarchy
  • Alphabetical List
  • Class List
  • File List
  • Namespace Members
  • Class Members
  • Related Pages

API Reference

Skip menu "API Reference"
  • dcop
  • DNSSD
  • interfaces
  • Kate
  • kconf_update
  • KDECore
  • KDED
  • kdefx
  • KDEsu
  • kdeui
  • KDocTools
  • KHTML
  • KImgIO
  • KInit
  • kio
  • kioslave
  • KJS
  • KNewStuff
  • KParts
  • KUtils
Generated for API Reference by doxygen 1.5.9
This website is maintained by Adriaan de Groot and Allen Winter.
KDE® and the K Desktop Environment® logo are registered trademarks of KDE e.V. | Legal