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