00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020 #include "katecompletionwidget.h"
00021
00022 #include <QtGui/QBoxLayout>
00023 #include <QtGui/QApplication>
00024 #include <QtGui/QDesktopWidget>
00025 #include <QtGui/QHeaderView>
00026 #include <QtCore/QTimer>
00027 #include <QtGui/QLabel>
00028 #include <QtGui/QToolButton>
00029 #include <QtGui/QSizeGrip>
00030 #include <QtGui/QPushButton>
00031 #include <QtGui/QAbstractScrollArea>
00032 #include <QtGui/QScrollBar>
00033 #include <QtCore/QMutex>
00034
00035 #include <kicon.h>
00036 #include <kdialog.h>
00037
00038 #include <ktexteditor/cursorfeedback.h>
00039 #include <ktexteditor/codecompletionmodelcontrollerinterface.h>
00040
00041 #include "kateview.h"
00042 #include "katesmartmanager.h"
00043 #include "katerenderer.h"
00044 #include "kateconfig.h"
00045 #include "katedocument.h"
00046 #include "katesmartrange.h"
00047 #include "kateedit.h"
00048
00049 #include "katecompletionmodel.h"
00050 #include "katecompletiontree.h"
00051 #include "katecompletionconfig.h"
00052 #include "kateargumenthinttree.h"
00053 #include "kateargumenthintmodel.h"
00054
00055
00056
00057 Q_DECLARE_METATYPE(KTextEditor::Cursor)
00058
00059 KTextEditor::CodeCompletionModelControllerInterface* modelController(KTextEditor::CodeCompletionModel *model)
00060 {
00061 static KTextEditor::CodeCompletionModelControllerInterface defaultIf;
00062 KTextEditor::CodeCompletionModelControllerInterface* ret =
00063 qobject_cast<KTextEditor::CodeCompletionModelControllerInterface*>(model);
00064 if (!ret) {
00065 ret = &defaultIf;
00066 }
00067 return ret;
00068 }
00069
00070
00071 KateCompletionWidget::KateCompletionWidget(KateView* parent)
00072 : QFrame(parent, Qt::ToolTip)
00073 , m_presentationModel(new KateCompletionModel(this))
00074 , m_entryList(new KateCompletionTree(this))
00075 , m_argumentHintModel(new KateArgumentHintModel(this))
00076 , m_argumentHintTree(new KateArgumentHintTree(this))
00077 , m_automaticInvocationDelay(300)
00078 , m_filterInstalled(false)
00079 , m_configWidget(new KateCompletionConfig(m_presentationModel, view()))
00080 , m_lastInsertionByUser(false)
00081 , m_inCompletionList(false)
00082 , m_isSuspended(false)
00083 , m_dontShowArgumentHints(false)
00084 , m_needShow(false)
00085 , m_hadCompletionNavigation(false)
00086 , m_expandedAddedHeightBase(0)
00087 {
00088 connect(parent, SIGNAL(navigateAccept()), SLOT(navigateAccept()));
00089 connect(parent, SIGNAL(navigateBack()), SLOT(navigateBack()));
00090 connect(parent, SIGNAL(navigateDown()), SLOT(navigateDown()));
00091 connect(parent, SIGNAL(navigateLeft()), SLOT(navigateLeft()));
00092 connect(parent, SIGNAL(navigateRight()), SLOT(navigateRight()));
00093 connect(parent, SIGNAL(navigateUp()), SLOT(navigateUp()));
00094
00095 qRegisterMetaType<KTextEditor::Cursor>("KTextEditor::Cursor");
00096
00097 setFrameStyle( QFrame::Box | QFrame::Plain );
00098 setLineWidth( 1 );
00099
00100
00101 m_entryList->setModel(m_presentationModel);
00102 m_entryList->setColumnWidth(0, 0);
00103 m_entryList->setColumnWidth(1, 0);
00104 m_entryList->setColumnWidth(2, 0);
00105
00106 m_argumentHintTree->setParent(0, Qt::ToolTip);
00107 m_argumentHintTree->setModel(m_argumentHintModel);
00108
00109 connect(m_entryList->verticalScrollBar(), SIGNAL(valueChanged(int)), m_presentationModel, SLOT(placeExpandingWidgets()));
00110 connect(m_argumentHintTree->verticalScrollBar(), SIGNAL(valueChanged(int)), m_argumentHintModel, SLOT(placeExpandingWidgets()));
00111 connect(view(), SIGNAL(focusOut(KTextEditor::View*)), this, SLOT(viewFocusOut()));
00112
00113 m_automaticInvocationTimer = new QTimer(this);
00114 m_automaticInvocationTimer->setSingleShot(true);
00115 connect(m_automaticInvocationTimer, SIGNAL(timeout()), this, SLOT(automaticInvocation()));
00116
00117
00118
00119
00120
00121
00122 connect(m_presentationModel, SIGNAL(modelReset()), this, SLOT(modelReset()));
00123 connect(m_presentationModel, SIGNAL(rowsInserted(const QModelIndex&, int, int)), SLOT(rowsInserted(const QModelIndex&, int, int)));
00124 connect(m_argumentHintModel, SIGNAL(contentStateChanged(bool)), this, SLOT(argumentHintsChanged(bool)));
00125
00126
00127 connect(view(), SIGNAL(cursorPositionChanged(KTextEditor::View*, const KTextEditor::Cursor&)), this, SLOT(cursorPositionChanged()), Qt::QueuedConnection);
00128 connect(view()->doc()->history(), SIGNAL(editDone(KateEditInfo*)), SLOT(editDone(KateEditInfo*)));
00129 connect(view(), SIGNAL(verticalScrollPositionChanged (KTextEditor::View*, const KTextEditor::Cursor&)), this, SLOT(updatePositionSlot()), Qt::QueuedConnection);
00130
00131
00132
00133
00134 setFocusPolicy(Qt::ClickFocus);
00135 m_argumentHintTree->setFocusPolicy(Qt::ClickFocus);
00136
00137 foreach (QWidget* childWidget, findChildren<QWidget*>())
00138 childWidget->setFocusPolicy(Qt::NoFocus);
00139
00140
00141 m_entryList->move(frameWidth(), frameWidth());
00142 }
00143
00144 KateCompletionWidget::~KateCompletionWidget() {
00145 }
00146
00147 void KateCompletionWidget::viewFocusOut() {
00148 abortCompletion();
00149 }
00150
00151 void KateCompletionWidget::modelContentChanged() {
00152 if(m_completionRanges.isEmpty()) {
00153 kDebug( 13035 ) << "content changed, but no completion active";
00154 hide();
00155 return;
00156 }
00157
00158 int realItemCount = 0;
00159 foreach (KTextEditor::CodeCompletionModel* model, m_presentationModel->completionModels())
00160 realItemCount += model->rowCount();
00161 if( !m_isSuspended && (isHidden() || m_needShow) && realItemCount != 0 ) {
00162 m_needShow = false;
00163 updateAndShow();
00164 }
00165
00166 if(m_presentationModel->rowCount(QModelIndex()) == 0 && m_argumentHintModel->rowCount(QModelIndex()) == 0) {
00167 kDebug( 13035 ) << "hiding because no content";
00168 hide();
00169 return;
00170 }
00171
00172
00173 m_entryList->setCurrentIndex(model()->index(0,0));
00174 if(!model()->indexIsItem(m_entryList->currentIndex())) {
00175 QModelIndex firstIndex = model()->index(0,0, m_entryList->currentIndex());
00176 m_entryList->setCurrentIndex(firstIndex);
00177
00178 }
00179
00180 updateHeight();
00181
00182
00183 if( m_argumentHintTree->isHidden() && !m_dontShowArgumentHints && m_argumentHintModel->rowCount(QModelIndex()) != 0 )
00184 m_argumentHintTree->show();
00185 }
00186
00187 KateArgumentHintTree* KateCompletionWidget::argumentHintTree() const {
00188 return m_argumentHintTree;
00189 }
00190
00191 KateArgumentHintModel* KateCompletionWidget::argumentHintModel() const {
00192 return m_argumentHintModel;
00193 }
00194
00195 const KateCompletionModel* KateCompletionWidget::model() const {
00196 return m_presentationModel;
00197 }
00198
00199 KateCompletionModel* KateCompletionWidget::model() {
00200 return m_presentationModel;
00201 }
00202
00203 void KateCompletionWidget::rowsInserted(const QModelIndex& parent, int rowFrom, int rowEnd)
00204 {
00205 m_entryList->setAnimated(false);
00206 if (!parent.isValid())
00207 for (int i = rowFrom; i <= rowEnd; ++i)
00208 m_entryList->expand(m_presentationModel->index(i, 0, parent));
00209 }
00210
00211 KateView * KateCompletionWidget::view( ) const
00212 {
00213 return static_cast<KateView*>(const_cast<QObject*>(parent()));
00214 }
00215
00216 void KateCompletionWidget::argumentHintsChanged(bool hasContent)
00217 {
00218 m_dontShowArgumentHints = !hasContent;
00219
00220 if( m_dontShowArgumentHints )
00221 m_argumentHintTree->hide();
00222 else
00223 updateArgumentHintGeometry();
00224 }
00225
00226 void KateCompletionWidget::startCompletion(KTextEditor::CodeCompletionModel::InvocationType invocationType)
00227 {
00228 startCompletion(KTextEditor::Range(KTextEditor::Cursor(-1, -1), KTextEditor::Cursor(-1, -1)), 0, invocationType);
00229 }
00230
00231 void KateCompletionWidget::startCompletion(const KTextEditor::Range& word, KTextEditor::CodeCompletionModel* model, KTextEditor::CodeCompletionModel::InvocationType invocationType)
00232 {
00233 m_isSuspended = false;
00234 m_inCompletionList = true;
00235 m_needShow = true;
00236
00237 disconnect(this->model(), SIGNAL(contentGeometryChanged()), this, SLOT(modelContentChanged()));
00238
00239 m_dontShowArgumentHints = true;
00240
00241 QList<KTextEditor::CodeCompletionModel*> models;
00242 if (model) {
00243 models << model;
00244 } else {
00245 models = m_sourceModels;
00246 }
00247
00248 foreach(KTextEditor::CodeCompletionModel* model, m_completionRanges.keys())
00249 if(!models.contains(model))
00250 models << model;
00251
00252 if (!m_filterInstalled) {
00253 if (!QApplication::activeWindow()) {
00254 kWarning(13035) << "No active window to install event filter on!!";
00255 return;
00256 }
00257
00258 QApplication::activeWindow()->installEventFilter(this);
00259 m_filterInstalled = true;
00260 }
00261
00262 m_presentationModel->clearCompletionModels();
00263
00264 if(invocationType == KTextEditor::CodeCompletionModel::UserInvocation) {
00265 qDeleteAll(m_completionRanges);
00266 m_completionRanges.clear();
00267 }
00268
00269 foreach (KTextEditor::CodeCompletionModel* model, models) {
00270 KTextEditor::Range range;
00271 if (word.isValid()) {
00272 range = word;
00273 } else {
00274 range = modelController(model)->completionRange(view(), view()->cursorPosition());
00275 }
00276 if(!range.isValid()) {
00277 if(m_completionRanges.contains(model)) {
00278 KTextEditor::SmartRange *oldRange = m_completionRanges[model];
00279 m_completionRanges.remove(model);
00280 delete oldRange;
00281 }
00282 models.removeAll(model);
00283 continue;
00284 }
00285 if(m_completionRanges.contains(model)) {
00286 if(*m_completionRanges[model] == range) {
00287 continue;
00288 }
00289 else {
00290 KTextEditor::SmartRange *oldRange = m_completionRanges[model];
00291 m_completionRanges.remove(model);
00292 delete oldRange;
00293 }
00294 }
00295 model->completionInvoked(view(), range, invocationType);
00296 m_completionRanges[model] = view()->doc()->smartManager()->newSmartRange(range);
00297 m_completionRanges[model]->setInsertBehavior(KTextEditor::SmartRange::ExpandRight | KTextEditor::SmartRange::ExpandLeft);
00298 if(!m_completionRanges[model]->isValid()) {
00299 kWarning(13035) << "Could not construct valid smart-range from" << range << "instead got" << *m_completionRanges[model];
00300 abortCompletion();
00301 return;
00302 }
00303 connect(m_completionRanges[model]->smartStart().notifier(), SIGNAL(characterDeleted(KTextEditor::SmartCursor*, bool)),
00304 SLOT(startCharacterDeleted(KTextEditor::SmartCursor*, bool)));
00305 }
00306
00307 m_presentationModel->setCompletionModels(models);
00308
00309 cursorPositionChanged();
00310
00311 if (!m_completionRanges.isEmpty()) {
00312 connect(this->model(), SIGNAL(contentGeometryChanged()), this, SLOT(modelContentChanged()));
00313
00314 modelContentChanged();
00315 }
00316 else {
00317 abortCompletion();
00318 }
00319 }
00320
00321 void KateCompletionWidget::updateAndShow()
00322 {
00323 setUpdatesEnabled(false);
00324
00325 modelReset();
00326
00327 m_argumentHintModel->buildRows();
00328 if( m_argumentHintModel->rowCount(QModelIndex()) != 0 )
00329 argumentHintsChanged(true);
00330
00331
00332
00333
00334
00335 updatePosition(true);
00336 m_entryList->resizeColumns(false, true, true);
00337 updatePosition(true);
00338 m_entryList->resizeColumns(false, true, true);
00339
00340 setUpdatesEnabled(true);
00341 if (m_presentationModel->rowCount() || m_argumentHintModel->rowCount(QModelIndex()))
00342 show();
00343 }
00344
00345 void KateCompletionWidget::updatePositionSlot()
00346 {
00347 updatePosition();
00348 }
00349
00350 bool KateCompletionWidget::updatePosition(bool force)
00351 {
00352 if (!force && !isCompletionActive())
00353 return false;
00354
00355 if (!completionRange()) {
00356 return false;
00357 }
00358 QPoint cursorPosition = view()->cursorToCoordinate(completionRange()->start());
00359 if (cursorPosition == QPoint(-1,-1)) {
00360
00361 abortCompletion();
00362 return false;
00363 }
00364
00365 QPoint p = view()->mapToGlobal( cursorPosition );
00366 int x = p.x() - m_entryList->columnViewportPosition(m_presentationModel->translateColumn(KTextEditor::CodeCompletionModel::Name)) - 2;
00367 int y = p.y();
00368
00369
00370
00371
00372 y += view()->renderer()->config()->fontMetrics().height();
00373
00374 bool borderHit = false;
00375
00376 if (x + width() > QApplication::desktop()->screenGeometry(view()).right()) {
00377 x = QApplication::desktop()->screenGeometry(view()).right() - width();
00378 borderHit = true;
00379 }
00380
00381 if( x < QApplication::desktop()->screenGeometry(view()).left() ) {
00382 x = QApplication::desktop()->screenGeometry(view()).left();
00383 borderHit = true;
00384 }
00385
00386 move( QPoint(x,y) );
00387
00388 updateHeight();
00389
00390 updateArgumentHintGeometry();
00391
00392
00393
00394 return borderHit;
00395 }
00396
00397 void KateCompletionWidget::updateArgumentHintGeometry()
00398 {
00399 if( !m_dontShowArgumentHints ) {
00400
00401 QRect geom = m_argumentHintTree->geometry();
00402 geom.moveTo(pos());
00403 geom.setWidth(width());
00404 geom.moveBottom(pos().y() - view()->renderer()->config()->fontMetrics().height()*2);
00405 m_argumentHintTree->updateGeometry(geom);
00406 }
00407 }
00408
00409
00410 bool hasAtLeastNRows(int rows, QAbstractItemModel* model) {
00411 int count = 0;
00412 for(int row = 0; row < model->rowCount(); ++row) {
00413 ++count;
00414
00415 QModelIndex index(model->index(row, 0));
00416 if(index.isValid())
00417 count += model->rowCount(index);
00418
00419 if(count > rows)
00420 return true;
00421 }
00422 return false;
00423 }
00424
00425 void KateCompletionWidget::updateHeight()
00426 {
00427 QRect geom = geometry();
00428
00429 int minBaseHeight = 10;
00430 int maxBaseHeight = 300;
00431
00432 int baseHeight = 0;
00433 int calculatedCustomHeight = 0;
00434
00435 if(hasAtLeastNRows(15, m_presentationModel)) {
00436
00437 baseHeight = maxBaseHeight;
00438 }else{
00439
00440 for(int row = 0; row < m_presentationModel->rowCount(); ++row) {
00441 baseHeight += treeView()->sizeHintForRow(row);
00442
00443 QModelIndex index(m_presentationModel->index(row, 0));
00444 if(index.isValid()) {
00445 for(int row2 = 0; row2 < m_presentationModel->rowCount(index); ++row2) {
00446 int h = 0;
00447 for(int a = 0; a < m_presentationModel->columnCount(index); ++a) {
00448 int localHeight = treeView()->sizeHintForIndex(index.child(row2, a)).height();
00449 if(localHeight > 0)
00450 h = localHeight;
00451 }
00452 baseHeight += h;
00453 }
00454 }
00455 }
00456
00457 calculatedCustomHeight = baseHeight;
00458 }
00459
00460 baseHeight += 2*frameWidth();
00461
00462 if(m_entryList->horizontalScrollBar()->isVisible())
00463 baseHeight += m_entryList->horizontalScrollBar()->height();
00464
00465 if(baseHeight < minBaseHeight)
00466 baseHeight = minBaseHeight;
00467 if(baseHeight > maxBaseHeight)
00468 baseHeight = maxBaseHeight;
00469
00470 int newExpandingAddedHeight = 0;
00471
00472 if(baseHeight == maxBaseHeight && model()->expandingWidgetsHeight()) {
00473
00474 if(calculatedCustomHeight && calculatedCustomHeight > baseHeight && calculatedCustomHeight < (maxBaseHeight + model()->expandingWidgetsHeight()))
00475 newExpandingAddedHeight = calculatedCustomHeight - baseHeight;
00476 else
00477 newExpandingAddedHeight = model()->expandingWidgetsHeight();
00478 }
00479
00480 if( m_expandedAddedHeightBase != baseHeight && m_expandedAddedHeightBase - baseHeight > -2 && m_expandedAddedHeightBase - baseHeight < 2 )
00481 {
00482
00483
00484
00485 baseHeight = m_expandedAddedHeightBase;
00486 }
00487
00488 int screenBottom = QApplication::desktop()->screenGeometry(view()).bottom();
00489
00490
00491 int bottomPosition = baseHeight + newExpandingAddedHeight + geometry().top();
00492
00493 if( bottomPosition > screenBottom-50 ) {
00494 newExpandingAddedHeight -= bottomPosition - (screenBottom-50);
00495 }
00496
00497 int finalHeight = baseHeight+newExpandingAddedHeight;
00498
00499 if( finalHeight < 10 ) {
00500 m_entryList->resize(m_entryList->width(), height() - 2*frameWidth());
00501 return;
00502 }
00503
00504 m_expandedAddedHeightBase = geometry().height();
00505
00506 geom.setHeight(finalHeight);
00507
00508 setGeometry(geom);
00509 m_entryList->resize(m_entryList->width(), height() - 2*frameWidth());
00510 }
00511
00512
00513 void KateCompletionWidget::cursorPositionChanged( )
00514 {
00515 if (m_completionRanges.isEmpty())
00516 return;
00517
00518 KTextEditor::Cursor cursor = view()->cursorPosition();
00519
00520
00521 QMutableMapIterator<KTextEditor::CodeCompletionModel*, KateSmartRange*> i(m_completionRanges);
00522 while (i.hasNext()) {
00523 i.next();
00524 KTextEditor::CodeCompletionModel *model = i.key();
00525 KateSmartRange* range = i.value();
00526 modelController(model)->updateCompletionRange(view(), *range);
00527 QString currentCompletion = modelController(model)->filterString(view(), *range, view()->cursorPosition());
00528 bool abort = modelController(model)->shouldAbortCompletion(view(), *range, currentCompletion);
00529
00530 if (abort) {
00531 if (m_completionRanges.count() == 1) {
00532
00533 abortCompletion();
00534 return;
00535 } else {
00536 modelController(model)->aborted(view());
00537 i.remove();
00538 delete range;
00539 m_presentationModel->removeCompletionModel(model);
00540 }
00541 } else {
00542 m_presentationModel->setCurrentCompletion(model, currentCompletion);
00543 }
00544 }
00545
00546 m_entryList->scheduleUpdate();
00547 }
00548
00549 bool KateCompletionWidget::isCompletionActive( ) const
00550 {
00551 return !m_completionRanges.isEmpty() && !isHidden() && isVisible();
00552 }
00553
00554 void KateCompletionWidget::abortCompletion( )
00555 {
00556 kDebug(13035) ;
00557
00558 m_isSuspended = false;
00559
00560 bool wasActive = isCompletionActive();
00561
00562 clear();
00563
00564 if(!isHidden())
00565 hide();
00566
00567 if (wasActive)
00568 view()->sendCompletionAborted();
00569 }
00570
00571 void KateCompletionWidget::clear() {
00572 m_presentationModel->clearCompletionModels();
00573 m_argumentHintTree->clearCompletion();
00574 m_argumentHintModel->clear();
00575
00576 foreach(KTextEditor::CodeCompletionModel* model, m_completionRanges.keys())
00577 modelController(model)->aborted(view());
00578
00579 qDeleteAll(m_completionRanges);
00580 m_completionRanges.clear();
00581 }
00582
00583 bool KateCompletionWidget::navigateAccept() {
00584 m_hadCompletionNavigation = true;
00585
00586 if(currentEmbeddedWidget())
00587 QMetaObject::invokeMethod(currentEmbeddedWidget(), "embeddedWidgetAccept");
00588
00589 QModelIndex index = selectedIndex();
00590 if( index.isValid() ) {
00591 view()->doc()->smartMutex()->unlock();
00592 index.data(KTextEditor::CodeCompletionModel::AccessibilityAccept);
00593 view()->doc()->smartMutex()->lock();
00594 return true;
00595 }
00596 return false;
00597 }
00598
00599 void KateCompletionWidget::execute()
00600 {
00601 kDebug(13035) ;
00602
00603 if (!isCompletionActive())
00604 return;
00605
00606 QModelIndex index = selectedIndex();
00607
00608 if (!index.isValid())
00609 return abortCompletion();
00610
00611 QModelIndex toExecute;
00612
00613 KTextEditor::Cursor oldPos = view()->cursorPosition();
00614
00615 if(index.model() == m_presentationModel)
00616 toExecute = m_presentationModel->mapToSource(index);
00617 else
00618 toExecute = m_argumentHintModel->mapToSource(index);
00619
00620 if (!toExecute.isValid()) {
00621 kWarning() << k_funcinfo << "Could not map index" << m_entryList->selectionModel()->currentIndex() << "to source index.";
00622 return abortCompletion();
00623 }
00624
00625
00626 view()->doc()->editStart(true, Kate::CodeCompletionEdit);
00627
00628 KTextEditor::CodeCompletionModel* model = static_cast<KTextEditor::CodeCompletionModel*>(const_cast<QAbstractItemModel*>(toExecute.model()));
00629 Q_ASSERT(model);
00630
00631 KTextEditor::CodeCompletionModel2* model2 = qobject_cast<KTextEditor::CodeCompletionModel2*>(model);
00632
00633 Q_ASSERT(m_completionRanges.contains(model));
00634 KTextEditor::Cursor start = m_completionRanges[model]->start();
00635
00636
00637
00638 view()->doc()->smartMutex()->unlock();
00639
00640 if(model2)
00641 model2->executeCompletionItem2(view()->document(), *m_completionRanges[model], toExecute);
00642 else if(toExecute.parent().isValid())
00643
00644 view()->document()->replaceText(*m_completionRanges[model], model->data(toExecute.sibling(toExecute.row(), KTextEditor::CodeCompletionModel::Name)).toString());
00645 else
00646 model->executeCompletionItem(view()->document(), *m_completionRanges[model], toExecute.row());
00647
00648
00649 view()->doc()->smartMutex()->lock();
00650
00651 view()->doc()->editEnd();
00652
00653 abortCompletion();
00654
00655 view()->sendCompletionExecuted(start, model, toExecute);
00656
00657 KTextEditor::Cursor newPos = view()->cursorPosition();
00658 if(newPos > oldPos) {
00659 m_automaticInvocationAt = newPos;
00660 m_automaticInvocationLine = view()->doc()->text(KTextEditor::Range(oldPos, newPos));
00661 kDebug() << "executed, starting automatic invocation with line" << m_automaticInvocationLine;
00662 m_lastInsertionByUser = false;
00663 m_automaticInvocationTimer->start();
00664 }
00665 }
00666
00667 void KateCompletionWidget::resizeEvent( QResizeEvent * event )
00668 {
00669 QFrame::resizeEvent(event);
00670 }
00671
00672 void KateCompletionWidget::showEvent ( QShowEvent * event )
00673 {
00674 m_isSuspended = false;
00675
00676 QFrame::showEvent(event);
00677
00678 if( !m_dontShowArgumentHints && m_argumentHintModel->rowCount(QModelIndex()) != 0 )
00679 m_argumentHintTree->show();
00680 }
00681
00682 void KateCompletionWidget::hideEvent( QHideEvent * event )
00683 {
00684 QFrame::hideEvent(event);
00685 m_argumentHintTree->hide();
00686 }
00687
00688 KateSmartRange * KateCompletionWidget::completionRange(KTextEditor::CodeCompletionModel* model) const
00689 {
00690 if (!model) {
00691 if (m_completionRanges.isEmpty()) return 0;
00692
00693 return *m_completionRanges.begin();
00694 }
00695 if(m_completionRanges.contains(model))
00696 return m_completionRanges[model];
00697 else
00698 return 0;
00699 }
00700
00701 QMap<KTextEditor::CodeCompletionModel*, KateSmartRange*> KateCompletionWidget::completionRanges( ) const
00702 {
00703 return m_completionRanges;
00704 }
00705
00706 void KateCompletionWidget::modelReset( )
00707 {
00708 setUpdatesEnabled(false);
00709 m_entryList->setAnimated(false);
00710 m_argumentHintTree->setAnimated(false);
00713 for(int row = 0; row < m_argumentHintModel->rowCount(QModelIndex()); ++row) {
00714 QModelIndex index(m_argumentHintModel->index(row, 0, QModelIndex()));
00715 if(!m_argumentHintTree->isExpanded(index)) {
00716 m_argumentHintTree->expand(index);
00717 }
00718 }
00719
00720 for(int row = 0; row < m_entryList->model()->rowCount(QModelIndex()); ++row) {
00721 QModelIndex index(m_entryList->model()->index(row, 0, QModelIndex()));
00722 if(!m_entryList->isExpanded(index)) {
00723 m_entryList->expand(index);
00724 }
00725 }
00726 setUpdatesEnabled(true);
00727 }
00728
00729 KateCompletionTree* KateCompletionWidget::treeView() const {
00730 return m_entryList;
00731 }
00732
00733 QModelIndex KateCompletionWidget::selectedIndex() const {
00734 if(!isCompletionActive())
00735 return QModelIndex();
00736
00737 if( m_inCompletionList )
00738 return m_entryList->currentIndex();
00739 else
00740 return m_argumentHintTree->currentIndex();
00741 }
00742
00743 bool KateCompletionWidget::navigateLeft() {
00744 m_hadCompletionNavigation = true;
00745 if(currentEmbeddedWidget())
00746 QMetaObject::invokeMethod(currentEmbeddedWidget(), "embeddedWidgetLeft");
00747
00748 QModelIndex index = selectedIndex();
00749
00750 if( index.isValid() ) {
00751 index.data(KTextEditor::CodeCompletionModel::AccessibilityPrevious);
00752
00753 return true;
00754 }
00755 return false;
00756 }
00757
00758 bool KateCompletionWidget::navigateRight() {
00759 m_hadCompletionNavigation = true;
00760 if(currentEmbeddedWidget())
00761 QMetaObject::invokeMethod(currentEmbeddedWidget(), "embeddedWidgetRight");
00762
00763 QModelIndex index = selectedIndex();
00764
00765 if( index.isValid() ) {
00766 index.data(KTextEditor::CodeCompletionModel::AccessibilityNext);
00767 return true;
00768 }
00769
00770 return false;
00771 }
00772
00773 bool KateCompletionWidget::hadNavigation() const {
00774 return m_hadCompletionNavigation;
00775 }
00776
00777 void KateCompletionWidget::resetHadNavigation() {
00778 m_hadCompletionNavigation = false;
00779 }
00780
00781
00782 bool KateCompletionWidget::navigateBack() {
00783 m_hadCompletionNavigation = true;
00784 if(currentEmbeddedWidget())
00785 QMetaObject::invokeMethod(currentEmbeddedWidget(), "embeddedWidgetBack");
00786 return false;
00787 }
00788
00789 bool KateCompletionWidget::toggleExpanded(bool forceExpand, bool forceUnExpand) {
00790 if ( (canExpandCurrentItem() || forceExpand ) && !forceUnExpand) {
00791 bool ret = canExpandCurrentItem();
00792 setCurrentItemExpanded(true);
00793 return ret;
00794 } else if (canCollapseCurrentItem() || forceUnExpand) {
00795 bool ret = canCollapseCurrentItem();
00796 setCurrentItemExpanded(false);
00797 return ret;
00798 }
00799 return false;
00800 }
00801
00802 bool KateCompletionWidget::canExpandCurrentItem() const {
00803 if( m_inCompletionList ) {
00804 if( !m_entryList->currentIndex().isValid() ) return false;
00805 return model()->isExpandable( m_entryList->currentIndex() ) && !model()->isExpanded( m_entryList->currentIndex() );
00806 } else {
00807 if( !m_argumentHintTree->currentIndex().isValid() ) return false;
00808 return argumentHintModel()->isExpandable( m_argumentHintTree->currentIndex() ) && !argumentHintModel()->isExpanded( m_argumentHintTree->currentIndex() );
00809 }
00810 }
00811
00812 bool KateCompletionWidget::canCollapseCurrentItem() const {
00813 if( m_inCompletionList ) {
00814 if( !m_entryList->currentIndex().isValid() ) return false;
00815 return model()->isExpandable( m_entryList->currentIndex() ) && model()->isExpanded( m_entryList->currentIndex() );
00816 }else{
00817 if( !m_argumentHintTree->currentIndex().isValid() ) return false;
00818 return m_argumentHintModel->isExpandable( m_argumentHintTree->currentIndex() ) && m_argumentHintModel->isExpanded( m_argumentHintTree->currentIndex() );
00819 }
00820 }
00821
00822 void KateCompletionWidget::setCurrentItemExpanded( bool expanded ) {
00823 if( m_inCompletionList ) {
00824 if( !m_entryList->currentIndex().isValid() ) return;
00825 model()->setExpanded(m_entryList->currentIndex(), expanded);
00826 updateHeight();
00827 }else{
00828 if( !m_argumentHintTree->currentIndex().isValid() ) return;
00829 m_argumentHintModel->setExpanded(m_argumentHintTree->currentIndex(), expanded);
00830 }
00831 }
00832
00833 void KateCompletionWidget::startCharacterDeleted( KTextEditor::SmartCursor*, bool deletedBefore )
00834 {
00835 if (deletedBefore)
00836
00837 QTimer::singleShot(0, this, SLOT(abortCompletion()));
00838 }
00839
00840 bool KateCompletionWidget::eventFilter( QObject * watched, QEvent * event )
00841 {
00842 bool ret = QFrame::eventFilter(watched, event);
00843
00844 if (watched != this)
00845 if (event->type() == QEvent::Move)
00846 updatePosition();
00847
00848 return ret;
00849 }
00850
00851 bool KateCompletionWidget::navigateDown() {
00852 m_hadCompletionNavigation = true;
00853 if(currentEmbeddedWidget()) {
00854 QMetaObject::invokeMethod(currentEmbeddedWidget(), "embeddedWidgetDown");
00855 }
00856 return false;
00857 }
00858
00859 bool KateCompletionWidget::navigateUp() {
00860 m_hadCompletionNavigation = true;
00861 if(currentEmbeddedWidget())
00862 QMetaObject::invokeMethod(currentEmbeddedWidget(), "embeddedWidgetUp");
00863 return false;
00864 }
00865
00866 QWidget* KateCompletionWidget::currentEmbeddedWidget() {
00867 QModelIndex index = selectedIndex();
00868 if(!index.isValid())
00869 return 0;
00870 if( qobject_cast<const ExpandingWidgetModel*>(index.model()) ) {
00871 const ExpandingWidgetModel* model = static_cast<const ExpandingWidgetModel*>(index.model());
00872 if( model->isExpanded(index) )
00873 return model->expandingWidget(index);
00874 }
00875 return 0;
00876 }
00877
00878 void KateCompletionWidget::cursorDown()
00879 {
00880 if( m_inCompletionList )
00881 m_entryList->nextCompletion();
00882 else {
00883 if( !m_argumentHintTree->nextCompletion() )
00884 switchList();
00885 }
00886 }
00887
00888 void KateCompletionWidget::cursorUp()
00889 {
00890 if( m_inCompletionList ) {
00891 if( !m_entryList->previousCompletion() )
00892 switchList();
00893 }else{
00894 m_argumentHintTree->previousCompletion();
00895 }
00896 }
00897
00898 void KateCompletionWidget::pageDown( )
00899 {
00900 if( m_inCompletionList )
00901 m_entryList->pageDown();
00902 else {
00903 if( !m_argumentHintTree->pageDown() )
00904 switchList();
00905 }
00906 }
00907
00908 void KateCompletionWidget::pageUp( )
00909 {
00910 if( m_inCompletionList ) {
00911 if( !m_entryList->pageUp() )
00912 switchList();
00913 }else{
00914 m_argumentHintTree->pageUp();
00915 }
00916 }
00917
00918 void KateCompletionWidget::top( )
00919 {
00920 if( m_inCompletionList )
00921 m_entryList->top();
00922 else
00923 m_argumentHintTree->top();
00924 }
00925
00926 void KateCompletionWidget::bottom( )
00927 {
00928 if( m_inCompletionList )
00929 m_entryList->bottom();
00930 else
00931 m_argumentHintTree->bottom();
00932 }
00933
00934 void KateCompletionWidget::switchList() {
00935 if( m_inCompletionList ) {
00936 m_entryList->setCurrentIndex(QModelIndex());
00937 if( m_argumentHintModel->rowCount(QModelIndex()) != 0 )
00938 m_argumentHintTree->setCurrentIndex(m_argumentHintModel->index(m_argumentHintModel->rowCount(QModelIndex())-1, 0));
00939 } else {
00940 m_argumentHintTree->setCurrentIndex(QModelIndex());
00941 if( m_presentationModel->rowCount(QModelIndex()) != 0 )
00942 m_entryList->setCurrentIndex(m_presentationModel->index(0, 0));
00943 }
00944 m_inCompletionList = !m_inCompletionList;
00945 }
00946
00947 void KateCompletionWidget::showConfig( )
00948 {
00949 abortCompletion();
00950
00951 m_configWidget->exec();
00952 }
00953
00954 void KateCompletionWidget::registerCompletionModel(KTextEditor::CodeCompletionModel* model)
00955 {
00956 m_sourceModels.append(model);
00957
00958 if (isCompletionActive()) {
00959 m_presentationModel->addCompletionModel(model);
00960 }
00961 }
00962
00963 void KateCompletionWidget::unregisterCompletionModel(KTextEditor::CodeCompletionModel* model)
00964 {
00965 m_sourceModels.removeAll(model);
00966 }
00967
00968 int KateCompletionWidget::automaticInvocationDelay() const {
00969 return m_automaticInvocationDelay;
00970 }
00971
00972 void KateCompletionWidget::setAutomaticInvocationDelay(int delay) {
00973 m_automaticInvocationDelay = delay;
00974 }
00975
00976
00977 void KateCompletionWidget::editDone(KateEditInfo * edit)
00978 {
00979 if(!edit->newText().join("\n").trimmed().isEmpty())
00980 m_lastInsertionByUser = edit->editSource() == Kate::UserInputEdit;
00981
00982 if (!view()->config()->automaticCompletionInvocation()
00983 || (edit->editSource() != Kate::UserInputEdit)
00984 || edit->isRemoval()
00985 || (edit->editSource() != Kate::UserInputEdit && edit->editSource() != Kate::CodeCompletionEdit)
00986 || edit->newText().isEmpty() )
00987 {
00988 m_automaticInvocationLine.clear();
00989 m_automaticInvocationTimer->stop();
00990 return;
00991 }
00992
00993 if(m_automaticInvocationAt != edit->newRange().start()) {
00994 m_automaticInvocationLine.clear();
00995 m_lastInsertionByUser = edit->editSource() == Kate::UserInputEdit;
00996 }
00997
00998 m_automaticInvocationLine += edit->newText().last();
00999 m_automaticInvocationAt = edit->newRange().end();
01000
01001 if (m_automaticInvocationLine.isEmpty()) {
01002 m_automaticInvocationTimer->stop();
01003 return;
01004 }
01005
01006 m_automaticInvocationTimer->start(m_automaticInvocationDelay);
01007 }
01008
01009 void KateCompletionWidget::automaticInvocation()
01010 {
01011 if(m_automaticInvocationAt != view()->cursorPosition())
01012 return;
01013 bool start = false;
01014
01015 foreach (KTextEditor::CodeCompletionModel *model, m_sourceModels) {
01016 if(m_completionRanges.contains(model))
01017 continue;
01018 start = modelController(model)->shouldStartCompletion(view(), m_automaticInvocationLine, m_lastInsertionByUser, view()->cursorPosition());
01019 if (start) break;
01020 }
01021 if (start) {
01022
01023 startCompletion(KTextEditor::CodeCompletionModel::AutomaticInvocation);
01024 }
01025 }
01026
01027 void KateCompletionWidget::userInvokedCompletion()
01028 {
01029 startCompletion(KTextEditor::CodeCompletionModel::UserInvocation);
01030 }
01031
01032 #include "katecompletionwidget.moc"
01033
01034