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 m_completionListBox->setCornerWidget(
new QSizeGrip( m_completionListBox) );
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_completionListBox );
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
bool KateCodeCompletion::codeCompletionVisible () {
00140
return m_completionPopup->isVisible();
00141 }
00142
00143
void KateCodeCompletion::showCompletionBox(
00144
QValueList<KTextEditor::CompletionEntry> complList,
int offset,
bool casesensitive )
00145 {
00146
kdDebug(13035) <<
"showCompletionBox " <<
endl;
00147
00148 m_caseSensitive = casesensitive;
00149 m_complList = complList;
00150 m_offset = offset;
00151 m_view->cursorPositionReal( &m_lineCursor, &m_colCursor );
00152 m_colCursor -= offset;
00153
00154 updateBox(
true );
00155 }
00156
00157
bool KateCodeCompletion::eventFilter(
QObject *o,
QEvent *e )
00158 {
00159
if ( o != m_completionPopup &&
00160 o != m_completionListBox &&
00161 o != m_completionListBox->viewport() )
00162
return false;
00163
00164
if( e->type() == QEvent::FocusOut )
00165 {
00166 abortCompletion();
00167 m_view->setFocus();
00168
return false;
00169 }
00170
00171
if ( e->type() == QEvent::MouseButtonDblClick ) {
00172 doComplete();
00173
return false;
00174 }
00175
00176
if ( e->type() == QEvent::MouseButtonPress ) {
00177 QTimer::singleShot(0,
this, SLOT(showComment()));
00178
return false;
00179 }
00180
00181
if ( e->type() == QEvent::KeyPress ) {
00182
QKeyEvent *ke = (
QKeyEvent*)e;
00183
if(
00184 (ke->key() == Key_Up) || (ke->key() == Key_Down ) ||
00185 (ke->key() == Key_Home ) || (ke->key() == Key_End) ||
00186 (ke->key() == Key_Prior) || (ke->key() == Key_Next )) {
00187 QTimer::singleShot(0,
this,SLOT(showComment()));
00188
return false;
00189 }
00190
if( ke->key() == Key_Enter || ke->key() == Key_Return ) {
00191 doComplete();
00192
return false;
00193 }
00194
00195
if( ke->key() == Key_Escape ) {
00196 abortCompletion();
00197 m_view->setFocus();
00198
return false;
00199 }
00200
00201
int qtKeyCode = ke->key() | ((ke->state() & Qt::ShiftButton) ? Qt::SHIFT : 0) | ((ke->state() & Qt::ControlButton) ? Qt::CTRL : 0) | ((ke->state() & Qt::AltButton) ? Qt::ALT : 0) | ((ke->state() & Qt::MetaButton) ? Qt::META : 0);
00202
00203
00204
if( ke->key() == Key_Backspace) {
00205 m_view->backspace();
00206 }
else if (qtKeyCode == m_view->m_editUndo->shortcut().keyCodeQt()) {
00207 m_view->m_editUndo->activate();
00208 }
else {
00209 QApplication::sendEvent( m_view->m_viewInternal, e );
00210 }
00211
00212
if( m_colCursor > m_view->cursorColumnReal() ) {
00213
00214
kdDebug(13035) <<
"Aborting Codecompletion after sendEvent" <<
endl;
00215
kdDebug(13035) << m_view->cursorColumnReal() <<
endl;
00216 abortCompletion();
00217 m_view->setFocus();
00218
return true;
00219 }
00220
00221 updateBox();
00222
return true;
00223 }
00224
00225
return false;
00226 }
00227
00228
void KateCodeCompletion::doComplete()
00229 {
00230 KateCompletionItem* item = static_cast<KateCompletionItem*>(
00231 m_completionListBox->item(m_completionListBox->currentItem()));
00232
00233
if( item == 0 )
00234
return;
00235
00236
QString text = item->m_entry.text;
00237
QString currentLine = m_view->currentTextLine();
00238
int len = m_view->cursorColumnReal() - m_colCursor;
00239
QString currentComplText = currentLine.mid(m_colCursor,len);
00240
QString add = text.mid(currentComplText.length());
00241
if( item->m_entry.postfix ==
"()" )
00242 add +=
"(";
00243
00244 emit filterInsertString(&(item->m_entry),&add);
00245 m_view->insertText(add);
00246
00247 complete( item->m_entry );
00248 m_view->setFocus();
00249 }
00250
00251
void KateCodeCompletion::abortCompletion()
00252 {
00253 m_completionPopup->hide();
00254
delete m_commentLabel;
00255 m_commentLabel = 0;
00256 emit completionAborted();
00257 }
00258
00259
void KateCodeCompletion::complete( KTextEditor::CompletionEntry entry )
00260 {
00261 m_completionPopup->hide();
00262
delete m_commentLabel;
00263 m_commentLabel = 0;
00264 emit completionDone( entry );
00265 emit completionDone();
00266 }
00267
00268
void KateCodeCompletion::updateBox(
bool )
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
for( QStringList::Iterator it = functionList.begin(); it != functionList.end(); it++ )
00339 {
00340
kdDebug(13035) <<
"Insert function text: " << *it <<
endl;
00341
00342 m_pArgHint->addFunction( nNum, ( *it ) );
00343
00344 nNum++;
00345 }
00346
00347 m_pArgHint->move(m_view->mapToGlobal(m_view->cursorCoordinates() +
QPoint(0,m_view->renderer()->config()->fontMetrics( )->height())) );
00348 m_pArgHint->show();
00349 }
00350
00351
void KateCodeCompletion::slotCursorPosChanged()
00352 {
00353 m_pArgHint->cursorPositionChanged ( m_view, m_view->cursorLine(), m_view->cursorColumnReal() );
00354 }
00355
00356
void KateCodeCompletion::showComment()
00357 {
00358
if (!m_completionPopup->isVisible())
00359
return;
00360
00361 KateCompletionItem* item = static_cast<KateCompletionItem*>(m_completionListBox->item(m_completionListBox->currentItem()));
00362
00363
if( !item )
00364
return;
00365
00366
if( item->m_entry.comment.isEmpty() )
00367
return;
00368
00369
delete m_commentLabel;
00370 m_commentLabel =
new KateCodeCompletionCommentLabel( 0, item->m_entry.comment );
00371 m_commentLabel->setFont(QToolTip::font());
00372 m_commentLabel->setPalette(QToolTip::palette());
00373
00374
QPoint rightPoint = m_completionPopup->mapToGlobal(
QPoint(m_completionPopup->width(),0));
00375
QPoint leftPoint = m_completionPopup->mapToGlobal(
QPoint(0,0));
00376
QRect screen = QApplication::desktop()->screenGeometry ( m_commentLabel );
00377
QPoint finalPoint;
00378
if (rightPoint.x()+m_commentLabel->width() > screen.x() + screen.width())
00379 finalPoint.setX(leftPoint.x()-m_commentLabel->width());
00380
else
00381 finalPoint.setX(rightPoint.x());
00382
00383 m_completionListBox->ensureCurrentVisible();
00384
00385 finalPoint.setY(
00386 m_completionListBox->viewport()->mapToGlobal(m_completionListBox->itemRect(
00387 m_completionListBox->item(m_completionListBox->currentItem())).topLeft()).y());
00388
00389 m_commentLabel->move(finalPoint);
00390 m_commentLabel->show();
00391 }
00392
00393 KateArgHint::KateArgHint( KateView* parent,
const char* name )
00394 :
QFrame( parent,
name, WType_Popup )
00395 {
00396 setBackgroundColor( black );
00397
00398 labelDict.setAutoDelete(
true );
00399 layout =
new QVBoxLayout(
this, 1, 2 );
00400 layout->setAutoAdd(
true );
00401 editorView = parent;
00402
00403 m_markCurrentFunction =
true;
00404
00405 setFocusPolicy( StrongFocus );
00406 setFocusProxy( parent );
00407
00408 reset( -1, -1 );
00409 }
00410
00411 KateArgHint::~KateArgHint()
00412 {
00413 }
00414
00415
void KateArgHint::setArgMarkInfos(
const QString& wrapping,
const QString& delimiter )
00416 {
00417 m_wrapping = wrapping;
00418 m_delimiter = delimiter;
00419 m_markCurrentFunction =
true;
00420 }
00421
00422
void KateArgHint::reset(
int line,
int col )
00423 {
00424 m_functionMap.clear();
00425 m_currentFunction = -1;
00426 labelDict.clear();
00427
00428 m_currentLine = line;
00429 m_currentCol = col - 1;
00430 }
00431
00432
void KateArgHint::slotDone(
bool completed)
00433 {
00434 hide();
00435
00436 m_currentLine = m_currentCol = -1;
00437
00438 emit argHintHidden();
00439
if (completed)
00440 emit argHintCompleted();
00441
else
00442 emit argHintAborted();
00443 }
00444
00445
void KateArgHint::cursorPositionChanged( KateView* view,
int line,
int col )
00446 {
00447
if( m_currentCol == -1 || m_currentLine == -1 ){
00448 slotDone(
false);
00449
return;
00450 }
00451
00452
int nCountDelimiter = 0;
00453
int count = 0;
00454
00455
QString currentTextLine = view->doc()->textLine( line );
00456
QString text = currentTextLine.mid( m_currentCol, col - m_currentCol );
00457
QRegExp strconst_rx(
"\"[^\"]*\"" );
00458
QRegExp chrconst_rx(
"'[^']*'" );
00459
00460 text = text
00461 .replace( strconst_rx,
"\"\"" )
00462 .replace( chrconst_rx,
"''" );
00463
00464
int index = 0;
00465
while( index < (
int)text.length() ){
00466
if( text[index] == m_wrapping[0] ){
00467 ++count;
00468 }
else if( text[index] == m_wrapping[1] ){
00469 --count;
00470 }
else if( count > 0 && text[index] == m_delimiter[0] ){
00471 ++nCountDelimiter;
00472 }
00473 ++index;
00474 }
00475
00476
if( (m_currentLine > 0 && m_currentLine != line) || (m_currentLine < col) || (count == 0) ){
00477 slotDone(count == 0);
00478
return;
00479 }
00480
00481
00482
00483 }
00484
00485
void KateArgHint::addFunction(
int id,
const QString& prot )
00486 {
00487 m_functionMap[
id ] = prot;
00488
QLabel*
label =
new QLabel( prot.stripWhiteSpace().simplifyWhiteSpace(),
this );
00489
label->setBackgroundColor(
QColor(255, 255, 238) );
00490
label->show();
00491 labelDict.insert(
id, label );
00492
00493
if( m_currentFunction < 0 )
00494 setCurrentFunction(
id );
00495 }
00496
00497
void KateArgHint::setCurrentFunction(
int currentFunction )
00498 {
00499
if( m_currentFunction != currentFunction ){
00500
00501
if( currentFunction < 0 )
00502 currentFunction = (
int)m_functionMap.size() - 1;
00503
00504
if( currentFunction > (
int)m_functionMap.size()-1 )
00505 currentFunction = 0;
00506
00507
if( m_markCurrentFunction && m_currentFunction >= 0 ){
00508
QLabel*
label = labelDict[ m_currentFunction ];
00509
label->setFont( font() );
00510 }
00511
00512 m_currentFunction = currentFunction;
00513
00514
if( m_markCurrentFunction ){
00515
QLabel*
label = labelDict[ currentFunction ];
00516
QFont fnt( font() );
00517 fnt.setBold(
true );
00518
label->setFont( fnt );
00519 }
00520
00521 adjustSize();
00522 }
00523 }
00524
00525
void KateArgHint::show()
00526 {
00527 QFrame::show();
00528 adjustSize();
00529 }
00530
00531
bool KateArgHint::eventFilter(
QObject*,
QEvent* e )
00532 {
00533
if( isVisible() && e->type() == QEvent::KeyPress ){
00534
QKeyEvent* ke = static_cast<QKeyEvent*>( e );
00535
if( (ke->state() & ControlButton) && ke->key() == Key_Left ){
00536 setCurrentFunction( currentFunction() - 1 );
00537 ke->accept();
00538
return true;
00539 }
else if( ke->key() == Key_Escape ){
00540 slotDone(
false);
00541
return false;
00542 }
else if( (ke->state() & ControlButton) && ke->key() == Key_Right ){
00543 setCurrentFunction( currentFunction() + 1 );
00544 ke->accept();
00545
return true;
00546 }
00547 }
00548
00549
return false;
00550 }
00551
00552
void KateArgHint::adjustSize( )
00553 {
00554
QRect screen = QApplication::desktop()->screenGeometry( pos() );
00555
00556 QFrame::adjustSize();
00557
if( width() > screen.width() )
00558 resize( screen.width(), height() );
00559
00560
if( x() + width() > screen.width() )
00561
move( screen.width() - width(), y() );
00562 }
00563
00564