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 #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
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 == "()" ) {
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
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
00174
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
00198 if ((e->key() == Key_Up) && (m_completionListBox->currentItem() == 0))
00199 {
00200 abortCompletion();
00201 m_view->setFocus();
00202 return;
00203 }
00204
00205
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
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
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
00276
00277
00278
00279
00280
00281
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 &&
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
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