KDevelop API Documentation

qeditorcodecompletion.cpp

Go to the documentation of this file.
00001 /***************************************************************************
00002                           qeditorcodecompletion.cpp -  description
00003                              -------------------
00004 
00005 
00006   begin      : Sun Nov 18 20:00 CET 2001
00007   copyright  : (C) 2001 Joseph Wenninger <jowenn@kde.org>
00008                (C) 2002 John Firebaugh <jfirebaugh@kde.org>
00009                (C) 2002 Roberto Raggi <roberto@kdevelop.org>
00010 
00011   taken from KDEVELOP:
00012   begin   : Sam Jul 14 18:20:00 CEST 2001
00013   copyright : (C) 2001 by Victor Röder <Victor_Roeder@GMX.de>
00014  ***************************************************************************/
00015 
00016 /******** Partly based on the ArgHintWidget of Qt3 by Trolltech AS *********/
00017 
00018 /***************************************************************************
00019  *                                                                         *
00020  *   This program is free software; you can redistribute it and/or modify  *
00021  *   it under the terms of the GNU General Public License as published by  *
00022  *   the Free Software Foundation; either version 2 of the License, or     *
00023  *   (at your option) any later version.                                   *
00024  *                                                                         *
00025  ***************************************************************************/
00026 
00027 #include "qeditorcodecompletion.h"
00028 #include "qeditor_settings.h"
00029 #include "qeditorcodecompletion.moc"
00030 
00031 // #include "qeditorcodecompletion_arghint.h"
00032 #include "qeditor_arghint.h"
00033 #include "qeditor_part.h"
00034 #include "qeditor_view.h"
00035 #include "qeditor.h"
00036 
00037 #include <qwhatsthis.h>
00038 #include <qtimer.h>
00039 #include <qtooltip.h>
00040 #include <qsizegrip.h>
00041 #include <qapplication.h>
00042 #include <private/qrichtext_p.h>
00043 
00044 #include <kdebug.h>
00045 //default size for codecompletionlistbox, value may not be ideal, change later
00046 QSize CCListBox::m_size = QSize(300,200);
00047 
00048 static QColor getColor( const QString &type )
00049 {
00050     // #### should be configurable
00051     if ( type == "function" || type == "slot" )
00052     return Qt::blue;
00053     if ( type == "variable" )
00054     return Qt::darkRed;
00055     if ( type == "property" )
00056     return Qt::darkGreen;
00057     if ( type == "type" )
00058     return Qt::darkBlue;
00059     return Qt::black;
00060 }
00061 
00062 class CompletionItem : public QListBoxItem
00063 {
00064 public:
00065     CompletionItem( QListBox *lb, const KTextEditor::CompletionEntry& entry )
00066     : QListBoxItem( lb ), parag( 0 ), lastState( FALSE ), m_entry( entry )
00067         {
00068             m_entry.type = "";  // #### at the moment cppcodecompletion don't fill type in the right way (robe)
00069             setText( m_entry.text );
00070         }
00071 
00072     ~CompletionItem() { delete parag; }
00073 
00074     void paint( QPainter *painter ) {
00075     if ( lastState != isSelected() ) {
00076         delete parag;
00077         parag = 0;
00078     }
00079     lastState = isSelected();
00080     if ( !parag )
00081         setupParag();
00082     parag->paint( *painter, listBox()->colorGroup() );
00083     }
00084 
00085     int height( const QListBox * ) const {
00086     if ( !parag )
00087         ( (CompletionItem*)this )->setupParag();
00088     return parag->rect().height();
00089     }
00090 
00091     int width( const QListBox * ) const {
00092     if ( !parag )
00093         ( (CompletionItem*)this )->setupParag();
00094     return parag->rect().width() - 2;
00095     }
00096 
00097     QString text() const { return QListBoxItem::text() + m_entry.postfix; }
00098 
00099 private:
00100     void setupParag() {
00101     if ( !parag ) {
00102         QTextFormatter *formatter;
00103         formatter = new QTextFormatterBreakWords;
00104         formatter->setWrapEnabled( FALSE );
00105         parag = new QTextParagraph( 0 );
00106         parag->pseudoDocument()->pFormatter = formatter;
00107         parag->insert( 0, " " + m_entry.type + ( m_entry.type.isEmpty() ? " " : "\t" ) + m_entry.prefix + " "+
00108                QListBoxItem::text() + m_entry.postfix );
00109         bool selCol = isSelected() && listBox()->colorGroup().highlightedText() != listBox()->colorGroup().text();
00110         QColor sc = listBox()->colorGroup().highlightedText();
00111         QTextFormat *f1 = parag->formatCollection()->format( listBox()->font(), selCol ? sc : getColor( m_entry.type ) );
00112         QTextFormat *f3 = parag->formatCollection()->format( listBox()->font(), isSelected() ?
00113                                  listBox()->colorGroup().highlightedText() :
00114                                  listBox()->colorGroup().text() );
00115         QFont f( listBox()->font() );
00116         f.setBold( TRUE );
00117         QTextFormat *f2 =
00118         parag->formatCollection()->format( f, isSelected() ? listBox()->colorGroup().highlightedText() :
00119                                                    listBox()->colorGroup().text() );
00120 
00121         parag->setFormat( 1, m_entry.type.length() + 1, f1 );
00122             if( m_entry.text.endsWith("(") ){
00123                 parag->setFormat( m_entry.type.length() + 2,
00124                                   m_entry.prefix.length() + 1 + QListBoxItem::text().length() - 1,
00125                                   f2 );
00126             } else {
00127                 parag->setFormat( m_entry.type.length() + 2,
00128                                   m_entry.prefix.length() + 1 + QListBoxItem::text().length(),
00129                                   f2 );
00130             }
00131 
00132         if ( !m_entry.postfix.isEmpty() )
00133         parag->setFormat( m_entry.type.length() + 2 + m_entry.prefix.length() + 1 + QListBoxItem::text().length(),
00134                   m_entry.postfix.length(), f3 );
00135 
00136         f1->removeRef();
00137         f2->removeRef();
00138         f3->removeRef();
00139         parag->format();
00140     }
00141     }
00142 
00143 public:
00144     QTextParagraph *parag;
00145     bool lastState;
00146     KTextEditor::CompletionEntry m_entry;
00147 };
00148 
00149 
00150 QEditorCodeCompletion::QEditorCodeCompletion( QEditorView* view )
00151     : QObject( view, "QEditor Code Completion" )
00152     , m_view( view )
00153     , m_commentLabel( 0 )
00154 {
00155     m_completionPopup = new QVBox( 0, 0, WType_Popup );
00156     m_completionPopup->setFrameStyle( QFrame::Box | QFrame::Plain );
00157     m_completionPopup->setLineWidth( 1 );
00158 
00159     m_completionListBox = new CCListBox( m_completionPopup );
00160     m_completionListBox->setFrameStyle( QFrame::NoFrame );
00161     m_completionListBox->installEventFilter( this );
00162     m_completionListBox->setHScrollBarMode( QScrollView::AlwaysOn );
00163     m_completionListBox->setVScrollBarMode( QScrollView::AlwaysOn );
00164     m_completionListBox->setCornerWidget( new QSizeGrip(m_completionListBox) );
00165 
00166     m_completionPopup->installEventFilter( this );
00167     m_completionPopup->setFocusProxy( m_completionListBox );
00168 
00169     m_pArgHint = new QEditorArgHint( m_view );
00170     m_view->editor()->installEventFilter( m_pArgHint );
00171     connect( m_pArgHint, SIGNAL(argHintHidden()),
00172              this, SIGNAL(argHintHidden()) );
00173 
00174     connect( m_view, SIGNAL(cursorPositionChanged()),
00175              this, SLOT(slotCursorPosChanged()) );
00176 }
00177 
00178 void QEditorCodeCompletion::showCompletionBox(
00179     QValueList<KTextEditor::CompletionEntry> complList, int offset, bool casesensitive )
00180 {
00181     kdDebug(9032) << "showCompletionBox " << endl;
00182 
00183     m_caseSensitive = casesensitive;
00184     m_complList = complList;
00185     m_offset = offset;
00186     m_view->cursorPositionReal( &m_lineCursor, &m_colCursor );
00187     m_colCursor -= offset;
00188 
00189     updateBox( true );
00190 }
00191 
00192 bool QEditorCodeCompletion::eventFilter( QObject *o, QEvent *e )
00193 {
00194     if ( o != m_completionPopup &&
00195          o != m_completionListBox &&
00196          o != m_completionListBox->viewport() )
00197         return false;
00198 
00199     if ( e->type() == QEvent::KeyPress ) {
00200         QKeyEvent *ke = (QKeyEvent*)e;
00201         if( (ke->key() == Key_Left)  || (ke->key() == Key_Right) ||
00202             (ke->key() == Key_Up)    || (ke->key() == Key_Down ) ||
00203             (ke->key() == Key_Home ) || (ke->key() == Key_End)   ||
00204             (ke->key() == Key_Prior) || (ke->key() == Key_Next )) {
00205             QTimer::singleShot(0,this,SLOT(showComment()));
00206             return false;
00207         }
00208         if( ke->key() == Key_Enter || ke->key() == Key_Return ||
00209             (QEditorSettings::self()->completeWordWithSpace() && (ke->key() == Key_Space || ke->key() == Key_Tab)) ) {
00210             CompletionItem* item = static_cast<CompletionItem*>(
00211                 m_completionListBox->item(m_completionListBox->currentItem()));
00212 
00213             if( item == 0 )
00214                 return false;
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             if( QEditorSettings::self()->completeWordWithSpace() ){
00228                 if( ke->key() == Key_Space )
00229                     m_view->insertText( " " );
00230                 else if( ke->key() == Key_Tab )
00231                     m_view->insertText( "\t" );
00232             }
00233 
00234             // HACK: move cursor. This needs to be handled in a clean way
00235             // by the doc/view.
00236             //m_view->setCursorPositionReal( m_lineCursor, m_view->cursorColumnReal() + add.length() );
00237 
00238             complete( item->m_entry );
00239             m_view->setFocus();
00240             return false;
00241         }
00242 
00243         if( ke->key() == Key_Escape ) {
00244             abortCompletion();
00245             m_view->setFocus();
00246             return false;
00247         }
00248 
00249         // redirect the event to the editor
00250         QApplication::sendEvent( m_view->editor(), e );
00251 
00252         QString currentLine = m_view->currentTextLine();
00253         int len = m_view->cursorColumnReal() - m_colCursor;
00254         QString currentComplText = currentLine.mid( m_colCursor, len );
00255     
00256         if( m_colCursor + m_offset > m_view->cursorColumnReal() ||
00257         (m_completionListBox->count() == 1 && m_completionListBox->currentText() == currentComplText) ) {
00258             // the cursor is too far left
00259             kdDebug(9032) << "Aborting Codecompletion after sendEvent" << endl;
00260             kdDebug(9032) << m_view->cursorColumnReal() << endl;
00261             abortCompletion();
00262             m_view->setFocus();
00263             return true;
00264         }
00265         updateBox();
00266         return true;
00267     }
00268 
00269     if( e->type() == QEvent::FocusOut )
00270         abortCompletion();
00271     return false;
00272 }
00273 
00274 void QEditorCodeCompletion::abortCompletion()
00275 {
00276     m_completionPopup->hide();
00277     delete m_commentLabel;
00278     m_commentLabel = 0;
00279     emit completionAborted();
00280 }
00281 
00282 void QEditorCodeCompletion::complete( KTextEditor::CompletionEntry entry )
00283 {
00284     m_completionPopup->hide();
00285     delete m_commentLabel;
00286     m_commentLabel = 0;
00287     emit completionDone( entry );
00288     emit completionDone();
00289 }
00290 
00291 void QEditorCodeCompletion::updateBox( bool newCoordinate )
00292 {
00293     m_completionListBox->clear();
00294 
00295     QString currentLine = m_view->currentTextLine();
00296     int len = m_view->cursorColumnReal() - m_colCursor;
00297     QString currentComplText = currentLine.mid(m_colCursor,len);
00298 
00299     kdDebug(9032) << "Column: " << m_colCursor << endl;
00300     kdDebug(9032) << "Line: " << currentLine << endl;
00301     kdDebug(9032) << "CurrentColumn: " << m_view->cursorColumnReal() << endl;
00302     kdDebug(9032) << "Len: " << len << endl;
00303     kdDebug(9032) << "Text: " << currentComplText << endl;
00304     kdDebug(9032) << "Count: " << m_complList.count() << endl;
00305 
00306     QValueList<KTextEditor::CompletionEntry>::Iterator it;
00307     if( m_caseSensitive ) {
00308         for( it = m_complList.begin(); it != m_complList.end(); ++it ) {
00309             if( (*it).text.startsWith(currentComplText) ) {
00310                 new CompletionItem(m_completionListBox,*it);
00311             }
00312         }
00313     } else {
00314         currentComplText = currentComplText.upper();
00315         for( it = m_complList.begin(); it != m_complList.end(); ++it ) {
00316             if( (*it).text.upper().startsWith(currentComplText) ) {
00317                 new CompletionItem(m_completionListBox,*it);
00318             }
00319         }
00320     }
00321 
00322     if( m_completionListBox->count() == 0 ) {
00323         abortCompletion();
00324         m_view->setFocus();
00325         return;
00326     }
00327 
00328     if( newCoordinate ) {
00329         QEditor* curEditor = m_view->editor();
00330         QTextCursor* cursor = curEditor->textCursor();
00331         QTextStringChar *chr = cursor->paragraph()->at( cursor->index() );
00332         int x = cursor->paragraph()->rect().x() + chr->x;
00333         int y, dummy;
00334         int h = cursor->paragraph()->lineHeightOfChar( cursor->index(), &dummy, &y );
00335         y += cursor->paragraph()->rect().y();
00336 
00337         m_completionPopup->resize( m_completionListBox->sizeHint()+ QSize(2,2));
00338 
00339         QPoint pt = curEditor->contentsToViewport( QPoint(x, y) );
00340         int yy = curEditor->mapToGlobal( pt ).y() + h + m_completionListBox->height();
00341         if ( yy < QApplication::desktop()->height() )
00342             m_completionPopup->move( curEditor->mapToGlobal( curEditor->
00343                                                              contentsToViewport( QPoint( x, y + h ) ) ) );
00344         else
00345             m_completionPopup->move( curEditor->mapToGlobal( curEditor->
00346                                                              contentsToViewport( QPoint( x, y - m_completionPopup->height() ) ) ) );
00347     }
00348 
00349     m_completionListBox->setCurrentItem( 0 );
00350     m_completionListBox->setSelected( 0, true );
00351     m_completionListBox->setFocus();
00352     m_completionPopup->show();
00353     QTimer::singleShot( 0, this, SLOT(showComment()) );
00354 }
00355 
00356 void QEditorCodeCompletion::showArgHint ( QStringList functionList,
00357                                           const QString& strWrapping,
00358                                           const QString& strDelimiter )
00359 {
00360     unsigned int line, col;
00361     m_view->cursorPositionReal( &line, &col );
00362     m_pArgHint->reset( line, col );
00363 
00364     m_pArgHint->setArgMarkInfos( "()", "," );
00365 
00366     int nNum = 0;
00367     for( QStringList::Iterator it = functionList.begin(); it != functionList.end(); it++ )
00368     {
00369         kdDebug(9032) << "Insert function text: " << *it << endl;
00370 
00371         m_pArgHint->addFunction ( nNum, ( *it ) );
00372 
00373         nNum++;
00374     }
00375 
00376     m_pArgHint->move(m_view->mapToGlobal(m_view->cursorCoordinates()));
00377     m_pArgHint->show();
00378 }
00379 
00380 void QEditorCodeCompletion::slotCursorPosChanged()
00381 {
00382     unsigned int line, col;
00383     m_view->cursorPositionReal( &line, &col );
00384     m_pArgHint->cursorPositionChanged( m_view, line, col );
00385 }
00386 
00387 void QEditorCodeCompletion::showComment()
00388 {
00389     CompletionItem* item = static_cast<CompletionItem*>(m_completionListBox->item(m_completionListBox->currentItem()));
00390 
00391     if( !item )
00392         return;
00393     if( item->m_entry.comment.isEmpty() )
00394         return;
00395 
00396     delete m_commentLabel;
00397     m_commentLabel = new QEditorCodeCompletionCommentLabel( 0, item->m_entry.comment );
00398     m_commentLabel->setFont(QToolTip::font());
00399     m_commentLabel->setPalette(QToolTip::palette());
00400 
00401     QPoint rightPoint = m_completionPopup->mapToGlobal(QPoint(m_completionPopup->width(),0));
00402     QPoint leftPoint = m_completionPopup->mapToGlobal(QPoint(0,0));
00403     QDesktopWidget *desktop = QApplication::desktop();
00404     QRect screen = desktop->screenGeometry( desktop->screenNumber(m_commentLabel) );
00405     QPoint finalPoint;
00406     if (rightPoint.x()+m_commentLabel->width() > screen.x() + screen.width())
00407         finalPoint.setX(leftPoint.x()-m_commentLabel->width());
00408     else
00409         finalPoint.setX(rightPoint.x());
00410 
00411     m_completionListBox->ensureCurrentVisible();
00412 
00413     finalPoint.setY(
00414         m_completionListBox->viewport()->mapToGlobal(m_completionListBox->itemRect(
00415                                                          m_completionListBox->item(m_completionListBox->currentItem())).topLeft()).y());
00416 
00417     m_commentLabel->move(finalPoint);
00418     m_commentLabel->show();
00419 }
00420 
00421 #undef kdDebug
00422 
KDE Logo
This file is part of the documentation for KDevelop Version 3.1.2.
Documentation copyright © 1996-2004 the KDE developers.
Generated on Wed Mar 23 00:03:42 2005 by doxygen 1.3.9.1 written by Dimitri van Heesch, © 1997-2003