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