00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027 #include "qeditorcodecompletion.h"
00028 #include "qeditor_settings.h"
00029 #include "qeditorcodecompletion.moc"
00030
00031
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
00046 QSize CCListBox::m_size = QSize(300,200);
00047
00048 static QColor getColor( const QString &type )
00049 {
00050
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 = "";
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
00235
00236
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
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
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