• Skip to content
  • Skip to link menu
KDE 4.3 API Reference
  • KDE API Reference
  • kdelibs
  • Sitemap
  • Contact Us
 

Kate

katecompletionwidget.cpp

Go to the documentation of this file.
00001 /* This file is part of the KDE libraries
00002    Copyright (C) 2005-2006 Hamish Rodda <rodda@kde.org>
00003    Copyright (C) 2007-2008 David Nolden <david.nolden.kdevelop@art-master.de>
00004 
00005    This library is free software; you can redistribute it and/or
00006    modify it under the terms of the GNU Library General Public
00007    License version 2 as published by the Free Software Foundation.
00008 
00009    This library is distributed in the hope that it will be useful,
00010    but WITHOUT ANY WARRANTY; without even the implied warranty of
00011    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00012    Library General Public License for more details.
00013 
00014    You should have received a copy of the GNU Library General Public License
00015    along with this library; see the file COPYING.LIB.  If not, write to
00016    the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
00017    Boston, MA 02110-1301, USA.
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 //#include "modeltest.h"
00056 
00057 const bool hideAutomaticCompletionOnExactMatch = true;
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   , m_lastInvocationType(KTextEditor::CodeCompletionModel::AutomaticInvocation)
00088 {
00089   connect(parent, SIGNAL(navigateAccept()), SLOT(navigateAccept()));
00090   connect(parent, SIGNAL(navigateBack()), SLOT(navigateBack()));
00091   connect(parent, SIGNAL(navigateDown()), SLOT(navigateDown()));
00092   connect(parent, SIGNAL(navigateLeft()), SLOT(navigateLeft()));
00093   connect(parent, SIGNAL(navigateRight()), SLOT(navigateRight()));
00094   connect(parent, SIGNAL(navigateUp()), SLOT(navigateUp()));
00095 
00096   qRegisterMetaType<KTextEditor::Cursor>("KTextEditor::Cursor");
00097 
00098   setFrameStyle( QFrame::Box | QFrame::Plain );
00099   setLineWidth( 1 );
00100   //setWindowOpacity(0.8);
00101 
00102   m_entryList->setModel(m_presentationModel);
00103   m_entryList->setColumnWidth(0, 0); //These will be determined automatically in KateCompletionTree::resizeColumns
00104   m_entryList->setColumnWidth(1, 0);
00105   m_entryList->setColumnWidth(2, 0);
00106   
00107   m_entryList->setVerticalScrollMode(QAbstractItemView::ScrollPerItem);
00108 
00109   m_argumentHintTree->setParent(0, Qt::ToolTip);
00110   m_argumentHintTree->setModel(m_argumentHintModel);
00111 
00112   connect(m_entryList->verticalScrollBar(), SIGNAL(valueChanged(int)), m_presentationModel, SLOT(placeExpandingWidgets()));
00113   connect(m_argumentHintTree->verticalScrollBar(), SIGNAL(valueChanged(int)), m_argumentHintModel, SLOT(placeExpandingWidgets()));
00114   connect(view(), SIGNAL(focusOut(KTextEditor::View*)), this, SLOT(viewFocusOut()));
00115 
00116   m_automaticInvocationTimer = new QTimer(this);
00117   m_automaticInvocationTimer->setSingleShot(true);
00118   connect(m_automaticInvocationTimer, SIGNAL(timeout()), this, SLOT(automaticInvocation()));
00119 
00120 //   QVBoxLayout* vl = new QVBoxLayout(this);
00121 //   vl->addWidget(m_entryList);
00122 //   vl->setMargin(0);
00123 
00124   // Keep branches expanded
00125   connect(m_presentationModel, SIGNAL(modelReset()), this, SLOT(modelReset()));
00126   connect(m_presentationModel, SIGNAL(rowsInserted(const QModelIndex&, int, int)), SLOT(rowsInserted(const QModelIndex&, int, int)));
00127   connect(m_argumentHintModel, SIGNAL(contentStateChanged(bool)), this, SLOT(argumentHintsChanged(bool)));
00128 
00129   // These must be queued connections so that we're not holding the smart lock when we ask for the model to update.
00130   connect(view(), SIGNAL(cursorPositionChanged(KTextEditor::View*, const KTextEditor::Cursor&)), this, SLOT(cursorPositionChanged()), Qt::QueuedConnection);
00131   connect(view()->doc()->history(), SIGNAL(editDone(KateEditInfo*)), SLOT(editDone(KateEditInfo*)));
00132   connect(view(), SIGNAL(verticalScrollPositionChanged (KTextEditor::View*, const KTextEditor::Cursor&)), this, SLOT(updatePositionSlot()), Qt::QueuedConnection);
00133 
00134   // This is a non-focus widget, it is passed keyboard input from the view
00135 
00136   //We need to do this, because else the focus goes to nirvana without any control when the completion-widget is clicked.
00137   setFocusPolicy(Qt::ClickFocus);
00138   m_argumentHintTree->setFocusPolicy(Qt::ClickFocus);
00139 
00140   foreach (QWidget* childWidget, findChildren<QWidget*>())
00141     childWidget->setFocusPolicy(Qt::NoFocus);
00142 
00143   //Position the entry-list so a frame can be drawn around it
00144   m_entryList->move(frameWidth(), frameWidth());
00145 }
00146 
00147 KateCompletionWidget::~KateCompletionWidget() {
00148 }
00149 
00150 void KateCompletionWidget::viewFocusOut() {
00151   abortCompletion();
00152 }
00153 
00154 void KateCompletionWidget::modelContentChanged() {
00155   if(m_completionRanges.isEmpty()) {
00156     kDebug( 13035 ) << "content changed, but no completion active";
00157     abortCompletion();
00158     return;
00159   }
00160   
00161   if(!view()->hasFocus()) {
00162     kDebug( 13035 ) << "view does not have focus";
00163     return;
00164   }
00165   
00166   if(!m_waitingForReset.isEmpty()) {
00167     kDebug( 13035 ) << "waiting for" << m_waitingForReset.size() << "completion-models to reset";
00168     return;
00169   }
00170 
00171   int realItemCount = 0;
00172   foreach (KTextEditor::CodeCompletionModel* model, m_presentationModel->completionModels())
00173     realItemCount += model->rowCount();
00174   if( !m_isSuspended && ((isHidden() && m_argumentHintTree->isHidden()) || m_needShow) && realItemCount != 0 ) {
00175     m_needShow = false;
00176     updateAndShow();
00177   }
00178 
00179   if(m_argumentHintModel->rowCount(QModelIndex()) == 0)
00180     m_argumentHintTree->hide();
00181 
00182   if(m_presentationModel->rowCount(QModelIndex()) == 0)
00183     hide();
00184 
00185 
00186   //With each filtering items can be added or removed, so we have to reset the current index here so we always have a selected item
00187   m_entryList->setCurrentIndex(model()->index(0,0));
00188   if(!model()->indexIsItem(m_entryList->currentIndex())) {
00189     QModelIndex firstIndex = model()->index(0,0, m_entryList->currentIndex());
00190     m_entryList->setCurrentIndex(firstIndex);
00191     //m_entryList->scrollTo(firstIndex, QAbstractItemView::PositionAtTop);
00192   }
00193 
00194   updateHeight();
00195 
00196   //New items for the argument-hint tree may have arrived, so check whether it needs to be shown
00197   if( m_argumentHintTree->isHidden() && !m_dontShowArgumentHints && m_argumentHintModel->rowCount(QModelIndex()) != 0 )
00198     m_argumentHintTree->show();
00199 
00200   if(hideAutomaticCompletionOnExactMatch && !isHidden() &&
00201      m_lastInvocationType == KTextEditor::CodeCompletionModel::AutomaticInvocation &&
00202      m_presentationModel->shouldMatchHideCompletionList())
00203     hide();
00204   else if(isHidden() && !m_presentationModel->shouldMatchHideCompletionList() &&
00205                          m_presentationModel->rowCount(QModelIndex()))
00206     show();
00207 }
00208 
00209 KateArgumentHintTree* KateCompletionWidget::argumentHintTree() const {
00210   return m_argumentHintTree;
00211 }
00212 
00213 KateArgumentHintModel* KateCompletionWidget::argumentHintModel() const {
00214   return m_argumentHintModel;
00215 }
00216 
00217 const KateCompletionModel* KateCompletionWidget::model() const {
00218   return m_presentationModel;
00219 }
00220 
00221 KateCompletionModel* KateCompletionWidget::model() {
00222   return m_presentationModel;
00223 }
00224 
00225 void KateCompletionWidget::rowsInserted(const QModelIndex& parent, int rowFrom, int rowEnd)
00226 {
00227   m_entryList->setAnimated(false);
00228   if(!model()->isGroupingEnabled())
00229     return;
00230 
00231   if (!parent.isValid())
00232     for (int i = rowFrom; i <= rowEnd; ++i)
00233       m_entryList->expand(m_presentationModel->index(i, 0, parent));
00234 }
00235 
00236 KateView * KateCompletionWidget::view( ) const
00237 {
00238   return static_cast<KateView*>(const_cast<QObject*>(parent()));
00239 }
00240 
00241 void KateCompletionWidget::argumentHintsChanged(bool hasContent)
00242 {
00243   m_dontShowArgumentHints = !hasContent;
00244 
00245   if( m_dontShowArgumentHints )
00246     m_argumentHintTree->hide();
00247   else
00248     updateArgumentHintGeometry();
00249 }
00250 
00251 void KateCompletionWidget::startCompletion(KTextEditor::CodeCompletionModel::InvocationType invocationType)
00252 {
00253   if(invocationType == KTextEditor::CodeCompletionModel::UserInvocation)
00254     abortCompletion();
00255   startCompletion(KTextEditor::Range(KTextEditor::Cursor(-1, -1), KTextEditor::Cursor(-1, -1)), 0, invocationType);
00256 }
00257 
00258 
00259 void KateCompletionWidget::deleteCompletionRanges()
00260 {
00261 foreach(CompletionRange r, m_completionRanges)
00262       delete r.range;
00263     m_completionRanges.clear();
00264 }
00265 
00266 void KateCompletionWidget::startCompletion(const KTextEditor::Range& word, KTextEditor::CodeCompletionModel* model, KTextEditor::CodeCompletionModel::InvocationType invocationType)
00267 {
00268   m_isSuspended = false;
00269   m_inCompletionList = true; //Always start at the top of the completion-list
00270   m_needShow = true;
00271 
00272   m_lastInvocationType = invocationType;
00273 
00274   disconnect(this->model(), SIGNAL(contentGeometryChanged()), this, SLOT(modelContentChanged()));
00275 
00276   m_dontShowArgumentHints = true;
00277 
00278   QList<KTextEditor::CodeCompletionModel*> models;
00279   if (model) {
00280     models << model;
00281   } else {
00282     models = m_sourceModels;
00283   }
00284 
00285   foreach(KTextEditor::CodeCompletionModel* model, m_completionRanges.keys())
00286     if(!models.contains(model))
00287       models << model;
00288 
00289   if (!m_filterInstalled) {
00290     if (!QApplication::activeWindow()) {
00291       kWarning(13035) << "No active window to install event filter on!!";
00292       return;
00293     }
00294     // Enable the cc box to move when the editor window is moved
00295     QApplication::activeWindow()->installEventFilter(this);
00296     m_filterInstalled = true;
00297   }
00298 
00299   m_presentationModel->clearCompletionModels();
00300 
00301   if(invocationType == KTextEditor::CodeCompletionModel::UserInvocation) {
00302     QMutexLocker lock(view()->doc()->smartMutex());
00303     deleteCompletionRanges();
00304   }
00305 
00306   foreach (KTextEditor::CodeCompletionModel* model, models) {
00307     KTextEditor::Range range;
00308     if (word.isValid()) {
00309       range = word;
00310     } else {
00311       range = modelController(model)->completionRange(view(), view()->cursorPosition());
00312     }
00313     if(!range.isValid()) {
00314       if(m_completionRanges.contains(model)) {
00315         QMutexLocker lock(view()->doc()->smartMutex());
00316         KTextEditor::SmartRange *oldRange = m_completionRanges[model].range;
00317         m_completionRanges.remove(model);
00318         delete oldRange;
00319       }
00320       models.removeAll(model);
00321       continue;
00322     }
00323     if(m_completionRanges.contains(model)) {
00324       if(*m_completionRanges[model].range == range) {
00325         continue; //Leave it running as it is
00326       }
00327       else { // delete the range that was used previously
00328         QMutexLocker lock(view()->doc()->smartMutex());
00329         KTextEditor::SmartRange *oldRange = m_completionRanges[model].range;
00330         m_completionRanges.remove(model);
00331         delete oldRange;
00332       }
00333     }
00334     
00335     connect(model, SIGNAL(waitForReset()), this, SLOT(waitForModelReset()));
00336     
00337     model->completionInvoked(view(), range, invocationType);
00338     
00339     disconnect(model, SIGNAL(waitForReset()), this, SLOT(waitForModelReset()));
00340     
00341     QMutexLocker lock(view()->doc()->smartMutex());
00342     
00343     m_completionRanges[model] = view()->doc()->smartManager()->newSmartRange(range);
00344     m_completionRanges[model].range->setInsertBehavior(KTextEditor::SmartRange::ExpandRight | KTextEditor::SmartRange::ExpandLeft);
00345     
00346     //In automatic invocation mode, hide the completion widget as soon as the position where the completion was started is passed to the left
00347     m_completionRanges[model].leftBoundary = view()->cursorPosition();
00348     
00349     //In manual invocation mode, bound the activity either the the point from where completion was invoked, or to the start of the range
00350     if(invocationType != KTextEditor::CodeCompletionModel::AutomaticInvocation)
00351       if(range.start() < m_completionRanges[model].leftBoundary)
00352       m_completionRanges[model].leftBoundary = range.start();
00353     
00354     if(!m_completionRanges[model].range->isValid()) {
00355       kWarning(13035) << "Could not construct valid smart-range from" << range << "instead got" << *m_completionRanges[model].range;
00356       lock.unlock();
00357       abortCompletion();
00358       return;
00359     }
00360     
00361     lock.unlock();
00362     
00363     connect(m_completionRanges[model].range->smartStart().notifier(), SIGNAL(characterDeleted(KTextEditor::SmartCursor*, bool)),
00364               SLOT(startCharacterDeleted(KTextEditor::SmartCursor*, bool)));
00365   }
00366 
00367   m_presentationModel->setCompletionModels(models);
00368 
00369   cursorPositionChanged();
00370 
00371   if (!m_completionRanges.isEmpty()) {
00372     connect(this->model(), SIGNAL(contentGeometryChanged()), this, SLOT(modelContentChanged()));
00373     //Now that all models have been notified, check whether the widget should be displayed instantly
00374     modelContentChanged();
00375   }
00376   else {
00377     abortCompletion();
00378   }
00379 }
00380 
00381 void KateCompletionWidget::waitForModelReset()
00382 {
00383   KTextEditor::CodeCompletionModel* senderModel = qobject_cast<KTextEditor::CodeCompletionModel*>(sender());
00384   if(!senderModel) {
00385     kWarning() << "waitForReset signal from bad model";
00386     return;
00387   }
00388   m_waitingForReset.insert(senderModel);
00389 }
00390 
00391 void KateCompletionWidget::updateAndShow()
00392 {
00393   if(!view()->hasFocus()) {
00394     kDebug( 13035 ) << "view does not have focus";
00395     return;
00396   }
00397   
00398   setUpdatesEnabled(false);
00399 
00400   modelReset();
00401 
00402     m_argumentHintModel->buildRows();
00403     if( m_argumentHintModel->rowCount(QModelIndex()) != 0 )
00404       argumentHintsChanged(true);
00405 //   }
00406 
00407   //We do both actions twice here so they are stable, because they influence each other:
00408   //updatePosition updates the height, resizeColumns needs the correct height to decide over
00409   //how many rows it computs the column-width
00410   updatePosition(true);
00411   m_entryList->resizeColumns(false, true, true);
00412   updatePosition(true);
00413   m_entryList->resizeColumns(false, true, true);
00414 
00415   setUpdatesEnabled(true);
00416 
00417   if(m_argumentHintModel->rowCount(QModelIndex())) {
00418     updateArgumentHintGeometry();
00419     m_argumentHintTree->show();
00420   } else
00421     m_argumentHintTree->hide();
00422 
00423   if (m_presentationModel->rowCount() && (!m_presentationModel->shouldMatchHideCompletionList() ||
00424                                            !hideAutomaticCompletionOnExactMatch  ||
00425                                            m_lastInvocationType != KTextEditor::CodeCompletionModel::AutomaticInvocation) )
00426     show();
00427   else
00428     hide();
00429 }
00430 
00431 void KateCompletionWidget::updatePositionSlot()
00432 {
00433   updatePosition();
00434 }
00435 
00436 bool KateCompletionWidget::updatePosition(bool force)
00437 {
00438   if (!force && !isCompletionActive())
00439     return false;
00440 
00441   if (!completionRange()) {
00442     return false;
00443   }
00444   QPoint cursorPosition = view()->cursorToCoordinate(completionRange()->start());
00445   if (cursorPosition == QPoint(-1,-1)) {
00446     // Start of completion range is now off-screen -> abort
00447     abortCompletion();
00448     return false;
00449   }
00450 
00451   QPoint p = view()->mapToGlobal( cursorPosition );
00452   int x = p.x() - m_entryList->columnTextViewportPosition(m_presentationModel->translateColumn(KTextEditor::CodeCompletionModel::Name)) - 4 - (m_entryList->viewport()->pos().x());
00453   int y = p.y();
00454   //We do not need to move the widget up, because updateHeight will resize the widget to fit the screen
00455 /*  if ( y + height() + view()->renderer()->config()->fontMetrics().height() > QApplication::desktop()->screenGeometry(this).bottom() )
00456     y -= height();
00457   else*/
00458   y += view()->renderer()->config()->fontMetrics().height();
00459 
00460   bool borderHit = false;
00461 
00462   if (x + width() > QApplication::desktop()->screenGeometry(view()).right()) {
00463     x = QApplication::desktop()->screenGeometry(view()).right() - width();
00464     borderHit = true;
00465   }
00466 
00467   if( x < QApplication::desktop()->screenGeometry(view()).left() ) {
00468     x = QApplication::desktop()->screenGeometry(view()).left();
00469     borderHit = true;
00470   }
00471 
00472   move( QPoint(x,y) );
00473 
00474   updateHeight();
00475 
00476   updateArgumentHintGeometry();
00477 
00478 //   kDebug() << "updated to" << geometry() << m_entryList->geometry() << borderHit;
00479 
00480   return borderHit;
00481 }
00482 
00483 void KateCompletionWidget::updateArgumentHintGeometry()
00484 {
00485   if( !m_dontShowArgumentHints ) {
00486     //Now place the argument-hint widget
00487     QRect geom = m_argumentHintTree->geometry();
00488     geom.moveTo(pos());
00489     geom.setWidth(width());
00490     geom.moveBottom(pos().y() - view()->renderer()->config()->fontMetrics().height()*2);
00491     m_argumentHintTree->updateGeometry(geom);
00492   }
00493 }
00494 
00495 //Checks whether the given model has at least "rows" rows, also searching the second level of the tree.
00496 bool hasAtLeastNRows(int rows, QAbstractItemModel* model) {
00497   int count = 0;
00498   for(int row = 0; row < model->rowCount(); ++row) {
00499     ++count;
00500 
00501     QModelIndex index(model->index(row, 0));
00502     if(index.isValid())
00503       count += model->rowCount(index);
00504 
00505     if(count > rows)
00506       return true;
00507   }
00508   return false;
00509 }
00510 
00511 void KateCompletionWidget::updateHeight()
00512 {
00513   QRect geom = geometry();
00514 
00515   int minBaseHeight = 10;
00516   int maxBaseHeight = 300;
00517 
00518   int baseHeight = 0;
00519   int calculatedCustomHeight = 0;
00520 
00521   if(hasAtLeastNRows(15, m_presentationModel)) {
00522     //If we know there is enough rows, always use max-height, we don't need to calculate size-hints
00523     baseHeight = maxBaseHeight;
00524   }else{
00525     //Calculate size-hints to determine the best height
00526     for(int row = 0; row < m_presentationModel->rowCount(); ++row) {
00527       baseHeight += treeView()->sizeHintForRow(row);
00528 
00529       QModelIndex index(m_presentationModel->index(row, 0));
00530       if(index.isValid()) {
00531         for(int row2 = 0; row2 < m_presentationModel->rowCount(index); ++row2) {
00532           int h = 0;
00533           for(int a = 0; a < m_presentationModel->columnCount(index); ++a) {
00534             int localHeight = treeView()->sizeHintForIndex(index.child(row2, a)).height();
00535             if(localHeight > h)
00536               h = localHeight;
00537           }
00538           baseHeight += h;
00539           if(baseHeight > maxBaseHeight)
00540             break;
00541         }
00542 
00543         if(baseHeight > maxBaseHeight)
00544           break;
00545       }
00546     }
00547 
00548     calculatedCustomHeight = baseHeight;
00549   }
00550 
00551   baseHeight += 2*frameWidth();
00552 
00553   if(m_entryList->horizontalScrollBar()->isVisible())
00554     baseHeight += m_entryList->horizontalScrollBar()->height();
00555 
00556 
00557   if(baseHeight < minBaseHeight)
00558     baseHeight = minBaseHeight;
00559   if(baseHeight > maxBaseHeight) {
00560     baseHeight = maxBaseHeight;
00561     m_entryList->setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded);
00562   }else{
00563     //Somewhere there seems to be a bug that makes QTreeView add a scroll-bar
00564     //even if the content exactly fits in. So forcefully disable the scroll-bar in that case
00565     m_entryList->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
00566 }
00567 
00568   int newExpandingAddedHeight = 0;
00569 
00570   if(baseHeight == maxBaseHeight && model()->expandingWidgetsHeight()) {
00571     //Eventually add some more height
00572     if(calculatedCustomHeight && calculatedCustomHeight > baseHeight && calculatedCustomHeight < (maxBaseHeight + model()->expandingWidgetsHeight()))
00573       newExpandingAddedHeight = calculatedCustomHeight - baseHeight;
00574     else
00575       newExpandingAddedHeight = model()->expandingWidgetsHeight();
00576   }
00577 
00578   if( m_expandedAddedHeightBase != baseHeight && m_expandedAddedHeightBase - baseHeight > -2 && m_expandedAddedHeightBase - baseHeight < 2  )
00579   {
00580     //Re-use the stored base-height if it only slightly differs from the current one.
00581     //Reason: Qt seems to apply slightly wrong sizes when the completion-widget is moved out of the screen at the bottom,
00582     //        which completely breaks this algorithm. Solution: re-use the old base-size if it only slightly differs from the computed one.
00583     baseHeight = m_expandedAddedHeightBase;
00584   }
00585 
00586   int screenBottom = QApplication::desktop()->screenGeometry(view()).bottom();
00587 
00588   //Limit the height to the bottom of the screen
00589   int bottomPosition = baseHeight + newExpandingAddedHeight + geometry().top();
00590 
00591   if( bottomPosition > screenBottom ) {
00592     newExpandingAddedHeight -= bottomPosition - (screenBottom);
00593   }
00594 
00595   int finalHeight = baseHeight+newExpandingAddedHeight;
00596 
00597   if( finalHeight < 10 ) {
00598     m_entryList->resize(m_entryList->width(), height() - 2*frameWidth());
00599     return;
00600   }
00601 
00602   m_expandedAddedHeightBase = geometry().height();
00603 
00604   geom.setHeight(finalHeight);
00605 
00606   //Work around a crash deep within the Qt 4.5 raster engine
00607   m_entryList->setScrollingEnabled(false);
00608   
00609   if(geometry() != geom)
00610     setGeometry(geom);
00611 
00612   QSize entryListSize = QSize(m_entryList->width(), finalHeight - 2*frameWidth());
00613   if(m_entryList->size() != entryListSize)
00614     m_entryList->resize(entryListSize);
00615 
00616 
00617   m_entryList->setScrollingEnabled(true);
00618 }
00619 
00620 void KateCompletionWidget::cursorPositionChanged( )
00621 {
00622   if (m_completionRanges.isEmpty())
00623     return;
00624 
00625   KTextEditor::Cursor cursor = view()->cursorPosition();
00626 
00627   QList<KTextEditor::CodeCompletionModel*> checkCompletionRanges = m_completionRanges.keys();
00628   
00629   QMutexLocker lock(view()->doc()->smartMutex());
00630   lock.unlock();
00631 
00632   //Check the models and eventuall abort some
00633   for(QList<KTextEditor::CodeCompletionModel*>::iterator it = checkCompletionRanges.begin();
00634       it != checkCompletionRanges.end(); ++it) {
00635       if(!m_completionRanges.contains(*it))
00636         continue;
00637 
00638       KTextEditor::CodeCompletionModel *model = *it;
00639       KateSmartRange* range = m_completionRanges[*it].range;
00640 
00641       modelController(model)->updateCompletionRange(view(), *range);
00642       QString currentCompletion = modelController(model)->filterString(view(), *range, view()->cursorPosition());
00643       bool abort = modelController(model)->shouldAbortCompletion(view(), *range, currentCompletion);
00644 
00645       if(view()->cursorPosition() < m_completionRanges[*it].leftBoundary) {
00646         kDebug() << "aborting because of boundary";
00647         abort = true;
00648       }
00649       
00650       if(!m_completionRanges.contains(*it))
00651         continue;
00652 
00653       if (abort) {
00654         if (m_completionRanges.count() == 1) {
00655           //last model - abort whole completion
00656           abortCompletion();
00657           return;
00658         } else {
00659           {
00660             lock.relock();
00661             delete m_completionRanges[*it].range;
00662             m_completionRanges.remove(*it);
00663             lock.unlock();
00664           }
00665 
00666           modelController(model)->aborted(view());
00667           m_presentationModel->removeCompletionModel(model);
00668         }
00669       } else {
00670         m_presentationModel->setCurrentCompletion(model, currentCompletion);
00671       }
00672   }
00673   
00674   m_entryList->scheduleUpdate();
00675 }
00676 
00677 bool KateCompletionWidget::isCompletionActive( ) const
00678 {
00679   return !m_completionRanges.isEmpty() && ((!isHidden() && isVisible()) || (!m_argumentHintTree->isHidden() && m_argumentHintTree->isVisible()));
00680 }
00681 
00682 void KateCompletionWidget::abortCompletion( )
00683 {
00684   kDebug(13035) ;
00685 
00686   m_isSuspended = false;
00687 
00688   bool wasActive = isCompletionActive();
00689 
00690   clear();
00691 
00692   if(!isHidden())
00693     hide();
00694   if(!m_argumentHintTree->isHidden())
00695     m_argumentHintTree->hide();
00696 
00697   if (wasActive)
00698     view()->sendCompletionAborted();
00699 }
00700 
00701 void KateCompletionWidget::clear() {
00702   m_presentationModel->clearCompletionModels();
00703   m_argumentHintTree->clearCompletion();
00704   m_argumentHintModel->clear();
00705 
00706   foreach(KTextEditor::CodeCompletionModel* model, m_completionRanges.keys())
00707     modelController(model)->aborted(view());
00708 
00709   QMutexLocker lock(view()->doc()->smartMutex());
00710   
00711   deleteCompletionRanges();
00712 }
00713 
00714 bool KateCompletionWidget::navigateAccept() {
00715   m_hadCompletionNavigation = true;
00716 
00717   if(currentEmbeddedWidget())
00718     QMetaObject::invokeMethod(currentEmbeddedWidget(), "embeddedWidgetAccept");
00719 
00720   QModelIndex index = selectedIndex();
00721   if( index.isValid() ) {
00722     index.data(KTextEditor::CodeCompletionModel::AccessibilityAccept);
00723     return true;
00724   }
00725   return false;
00726 }
00727 
00728 void KateCompletionWidget::execute()
00729 {
00730   kDebug(13035) ;
00731 
00732   if (!isCompletionActive())
00733     return;
00734 
00735   QModelIndex index = selectedIndex();
00736 
00737   if (!index.isValid())
00738     return abortCompletion();
00739 
00740   QModelIndex toExecute;
00741 
00742   if(index.model() == m_presentationModel)
00743     toExecute = m_presentationModel->mapToSource(index);
00744   else
00745     toExecute = m_argumentHintModel->mapToSource(index);
00746 
00747   if (!toExecute.isValid()) {
00748     kWarning() << k_funcinfo << "Could not map index" << m_entryList->selectionModel()->currentIndex() << "to source index.";
00749     return abortCompletion();
00750   }
00751 
00752   // encapsulate all editing as being from the code completion, and undo-able in one step.
00753   view()->doc()->editStart(true, Kate::CodeCompletionEdit);
00754 
00755   KTextEditor::SmartCursor* oldPos = view()->doc()->smartManager()->newSmartCursor(view()->cursorPosition(), KTextEditor::SmartCursor::StayOnInsert);
00756 
00757   KTextEditor::CodeCompletionModel* model = static_cast<KTextEditor::CodeCompletionModel*>(const_cast<QAbstractItemModel*>(toExecute.model()));
00758   Q_ASSERT(model);
00759 
00760   KTextEditor::CodeCompletionModel2* model2 = qobject_cast<KTextEditor::CodeCompletionModel2*>(model);
00761 
00762   Q_ASSERT(m_completionRanges.contains(model));
00763   KTextEditor::Cursor start = m_completionRanges[model].range->start();
00764 
00765   //editStart locks the smart-mutex, but it must not be locked when calling external functions,
00766   //else we may get deadlock-issues.
00767   view()->doc()->smartMutex()->unlock();
00768 
00769   if(model2)
00770     model2->executeCompletionItem2(view()->document(), *m_completionRanges[model].range, toExecute);
00771   else if(toExecute.parent().isValid())
00772     //The normale CodeCompletionInterface cannot handle feedback for hierarchical models, so just do the replacement
00773     view()->document()->replaceText(*m_completionRanges[model].range, model->data(toExecute.sibling(toExecute.row(), KTextEditor::CodeCompletionModel::Name)).toString());
00774   else
00775     model->executeCompletionItem(view()->document(), *m_completionRanges[model].range, toExecute.row());
00776 
00777   //Relock, because editEnd expects it to be locked
00778   view()->doc()->smartMutex()->lock();
00779 
00780   view()->doc()->editEnd();
00781 
00782   abortCompletion();
00783 
00784   view()->sendCompletionExecuted(start, model, toExecute);
00785 
00786   KTextEditor::Cursor newPos = view()->cursorPosition();
00787 
00788   if(newPos > *oldPos) {
00789     m_automaticInvocationAt = newPos;
00790     m_automaticInvocationLine = view()->doc()->text(KTextEditor::Range(*oldPos, newPos));
00791     kDebug() << "executed, starting automatic invocation with line" << m_automaticInvocationLine;
00792     m_lastInsertionByUser = false;
00793     m_automaticInvocationTimer->start();
00794   }
00795 
00796   view()->doc()->smartMutex()->lock();
00797   delete oldPos;
00798   view()->doc()->smartMutex()->unlock();
00799 }
00800 
00801 void KateCompletionWidget::resizeEvent( QResizeEvent * event )
00802 {
00803   QFrame::resizeEvent(event);
00804 }
00805 
00806 void KateCompletionWidget::showEvent ( QShowEvent * event )
00807 {
00808   m_isSuspended = false;
00809 
00810   QFrame::showEvent(event);
00811 
00812   if( !m_dontShowArgumentHints && m_argumentHintModel->rowCount(QModelIndex()) != 0 )
00813     m_argumentHintTree->show();
00814 }
00815 
00816 KateSmartRange * KateCompletionWidget::completionRange(KTextEditor::CodeCompletionModel* model) const
00817 {
00818   if (!model) {
00819     if (m_completionRanges.isEmpty()) return 0;
00820 
00821     return m_completionRanges.begin()->range;
00822   }
00823   if(m_completionRanges.contains(model))
00824     return m_completionRanges[model].range;
00825   else
00826     return 0;
00827 }
00828 
00829 QMap<KTextEditor::CodeCompletionModel*, KateCompletionWidget::CompletionRange> KateCompletionWidget::completionRanges( ) const
00830 {
00831   return m_completionRanges;
00832 }
00833 
00834 void KateCompletionWidget::modelReset( )
00835 {
00836   setUpdatesEnabled(false);
00837   m_entryList->setAnimated(false);
00838   m_argumentHintTree->setAnimated(false);
00841   for(int row = 0; row < m_argumentHintModel->rowCount(QModelIndex()); ++row) {
00842     QModelIndex index(m_argumentHintModel->index(row, 0, QModelIndex()));
00843     if(!m_argumentHintTree->isExpanded(index)) {
00844       m_argumentHintTree->expand(index);
00845     }
00846   }
00847 
00848   for(int row = 0; row < m_entryList->model()->rowCount(QModelIndex()); ++row) {
00849     QModelIndex index(m_entryList->model()->index(row, 0, QModelIndex()));
00850     if(!m_entryList->isExpanded(index)) {
00851       m_entryList->expand(index);
00852     }
00853   }
00854   setUpdatesEnabled(true);
00855 }
00856 
00857 KateCompletionTree* KateCompletionWidget::treeView() const {
00858   return m_entryList;
00859 }
00860 
00861 QModelIndex KateCompletionWidget::selectedIndex() const {
00862   if(!isCompletionActive())
00863     return QModelIndex();
00864 
00865   if( m_inCompletionList )
00866     return m_entryList->currentIndex();
00867   else
00868     return m_argumentHintTree->currentIndex();
00869 }
00870 
00871 bool KateCompletionWidget::navigateLeft() {
00872   m_hadCompletionNavigation = true;
00873   if(currentEmbeddedWidget())
00874     QMetaObject::invokeMethod(currentEmbeddedWidget(), "embeddedWidgetLeft");
00875 
00876   QModelIndex index = selectedIndex();
00877 
00878   if( index.isValid() ) {
00879     index.data(KTextEditor::CodeCompletionModel::AccessibilityPrevious);
00880 
00881     return true;
00882   }
00883   return false;
00884 }
00885 
00886 bool KateCompletionWidget::navigateRight() {
00887   m_hadCompletionNavigation = true;
00888   if(currentEmbeddedWidget()) 
00889     QMetaObject::invokeMethod(currentEmbeddedWidget(), "embeddedWidgetRight");
00890 
00891   QModelIndex index = selectedIndex();
00892 
00893   if( index.isValid() ) {
00894     index.data(KTextEditor::CodeCompletionModel::AccessibilityNext);
00895     return true;
00896   }
00897 
00898   return false;
00899 }
00900 
00901 bool KateCompletionWidget::hadNavigation() const {
00902   return m_hadCompletionNavigation;
00903 }
00904 
00905 void KateCompletionWidget::resetHadNavigation() {
00906   m_hadCompletionNavigation = false;
00907 }
00908 
00909 
00910 bool KateCompletionWidget::navigateBack() {
00911   m_hadCompletionNavigation = true;
00912   if(currentEmbeddedWidget())
00913     QMetaObject::invokeMethod(currentEmbeddedWidget(), "embeddedWidgetBack");
00914   return false;
00915 }
00916 
00917 bool KateCompletionWidget::toggleExpanded(bool forceExpand, bool forceUnExpand) {
00918   if ( (canExpandCurrentItem() || forceExpand ) && !forceUnExpand) {
00919     bool ret = canExpandCurrentItem();
00920     setCurrentItemExpanded(true);
00921     return ret;
00922   } else if (canCollapseCurrentItem() || forceUnExpand) {
00923     bool ret = canCollapseCurrentItem();
00924     setCurrentItemExpanded(false);
00925     return ret;
00926   }
00927   return false;
00928 }
00929 
00930 bool KateCompletionWidget::canExpandCurrentItem() const {
00931   if( m_inCompletionList ) {
00932     if( !m_entryList->currentIndex().isValid() ) return false;
00933     return model()->isExpandable( m_entryList->currentIndex() ) && !model()->isExpanded( m_entryList->currentIndex() );
00934   } else {
00935     if( !m_argumentHintTree->currentIndex().isValid() ) return false;
00936     return argumentHintModel()->isExpandable( m_argumentHintTree->currentIndex() ) && !argumentHintModel()->isExpanded( m_argumentHintTree->currentIndex() );
00937   }
00938 }
00939 
00940 bool KateCompletionWidget::canCollapseCurrentItem() const {
00941   if( m_inCompletionList ) {
00942     if( !m_entryList->currentIndex().isValid() ) return false;
00943     return model()->isExpandable( m_entryList->currentIndex() ) && model()->isExpanded( m_entryList->currentIndex() );
00944   }else{
00945     if( !m_argumentHintTree->currentIndex().isValid() ) return false;
00946     return m_argumentHintModel->isExpandable( m_argumentHintTree->currentIndex() ) && m_argumentHintModel->isExpanded( m_argumentHintTree->currentIndex() );
00947   }
00948 }
00949 
00950 void KateCompletionWidget::setCurrentItemExpanded( bool expanded ) {
00951   if( m_inCompletionList ) {
00952     if( !m_entryList->currentIndex().isValid() ) return;
00953     model()->setExpanded(m_entryList->currentIndex(), expanded);
00954     updateHeight();
00955   }else{
00956     if( !m_argumentHintTree->currentIndex().isValid() ) return;
00957     m_argumentHintModel->setExpanded(m_argumentHintTree->currentIndex(), expanded);
00958   }
00959 }
00960 
00961 void KateCompletionWidget::startCharacterDeleted( KTextEditor::SmartCursor*, bool deletedBefore )
00962 {
00963   if (deletedBefore)
00964     // Cannot abort completion from this function, or the cursor will be deleted before returning
00965     QTimer::singleShot(0, this, SLOT(abortCompletion()));
00966 }
00967 
00968 bool KateCompletionWidget::eventFilter( QObject * watched, QEvent * event )
00969 {
00970   bool ret = QFrame::eventFilter(watched, event);
00971 
00972   if (watched != this)
00973     if (event->type() == QEvent::Move)
00974       updatePosition();
00975 
00976   return ret;
00977 }
00978 
00979 bool KateCompletionWidget::navigateDown() {
00980   m_hadCompletionNavigation = true;
00981   if(currentEmbeddedWidget()) {
00982     QMetaObject::invokeMethod(currentEmbeddedWidget(), "embeddedWidgetDown");
00983   }
00984   return false;
00985 }
00986 
00987 bool KateCompletionWidget::navigateUp() {
00988   m_hadCompletionNavigation = true;
00989   if(currentEmbeddedWidget())
00990     QMetaObject::invokeMethod(currentEmbeddedWidget(), "embeddedWidgetUp");
00991   return false;
00992 }
00993 
00994 QWidget* KateCompletionWidget::currentEmbeddedWidget() {
00995   QModelIndex index = selectedIndex();
00996   if(!index.isValid())
00997     return 0;
00998   if( qobject_cast<const ExpandingWidgetModel*>(index.model()) ) {
00999     const ExpandingWidgetModel* model = static_cast<const ExpandingWidgetModel*>(index.model());
01000     if( model->isExpanded(index) )
01001       return model->expandingWidget(index);
01002   }
01003   return 0;
01004 }
01005 
01006 void KateCompletionWidget::cursorDown()
01007 {
01008   bool wasPartiallyExpanded = model()->partiallyExpandedRow().isValid();
01009 
01010   if( m_inCompletionList )
01011     m_entryList->nextCompletion();
01012   else {
01013     if( !m_argumentHintTree->nextCompletion() )
01014       switchList();
01015   }
01016 
01017   if(wasPartiallyExpanded != model()->partiallyExpandedRow().isValid())
01018     updateHeight();
01019 }
01020 
01021 void KateCompletionWidget::cursorUp()
01022 {
01023   bool wasPartiallyExpanded = model()->partiallyExpandedRow().isValid();
01024 
01025   if( m_inCompletionList ) {
01026     if( !m_entryList->previousCompletion() )
01027       switchList();
01028   }else{
01029     m_argumentHintTree->previousCompletion();
01030   }
01031 
01032   if(wasPartiallyExpanded != model()->partiallyExpandedRow().isValid())
01033     updateHeight();
01034 }
01035 
01036 void KateCompletionWidget::pageDown( )
01037 {
01038   bool wasPartiallyExpanded = model()->partiallyExpandedRow().isValid();
01039 
01040   if( m_inCompletionList )
01041     m_entryList->pageDown();
01042   else {
01043     if( !m_argumentHintTree->pageDown() )
01044       switchList();
01045   }
01046 
01047   if(wasPartiallyExpanded != model()->partiallyExpandedRow().isValid())
01048     updateHeight();
01049 }
01050 
01051 void KateCompletionWidget::pageUp( )
01052 {
01053   bool wasPartiallyExpanded = model()->partiallyExpandedRow().isValid();
01054 
01055   if( m_inCompletionList ) {
01056     if( !m_entryList->pageUp() )
01057       switchList();
01058   }else{
01059     m_argumentHintTree->pageUp();
01060   }
01061 
01062   if(wasPartiallyExpanded != model()->partiallyExpandedRow().isValid())
01063     updateHeight();
01064 }
01065 
01066 void KateCompletionWidget::top( )
01067 {
01068   bool wasPartiallyExpanded = model()->partiallyExpandedRow().isValid();
01069 
01070   if( m_inCompletionList )
01071     m_entryList->top();
01072   else
01073     m_argumentHintTree->top();
01074 
01075   if(wasPartiallyExpanded != model()->partiallyExpandedRow().isValid())
01076     updateHeight();
01077 }
01078 
01079 void KateCompletionWidget::bottom( )
01080 {
01081   bool wasPartiallyExpanded = model()->partiallyExpandedRow().isValid();
01082 
01083   if( m_inCompletionList )
01084     m_entryList->bottom();
01085   else
01086     m_argumentHintTree->bottom();
01087 
01088   if(wasPartiallyExpanded != model()->partiallyExpandedRow().isValid())
01089     updateHeight();
01090 }
01091 
01092 void KateCompletionWidget::switchList() {
01093   if( m_inCompletionList ) {
01094       if( m_argumentHintModel->rowCount(QModelIndex()) != 0 ) {
01095         m_entryList->setCurrentIndex(QModelIndex());
01096         m_argumentHintTree->setCurrentIndex(m_argumentHintModel->index(m_argumentHintModel->rowCount(QModelIndex())-1, 0));
01097       }
01098   } else {
01099       if( m_presentationModel->rowCount(QModelIndex()) != 0 ) {
01100         m_argumentHintTree->setCurrentIndex(QModelIndex());
01101         m_entryList->setCurrentIndex(m_presentationModel->index(0, 0));
01102         if(model()->hasGroups()) //If we have groups we have to move on, because the first item is a label
01103           m_entryList->nextCompletion();
01104       }
01105   }
01106   m_inCompletionList = !m_inCompletionList;
01107 }
01108 
01109 void KateCompletionWidget::showConfig( )
01110 {
01111   abortCompletion();
01112 
01113   m_configWidget->exec();
01114 }
01115 
01116 void KateCompletionWidget::completionModelReset()
01117 {
01118   KTextEditor::CodeCompletionModel* model = qobject_cast<KTextEditor::CodeCompletionModel*>(sender());
01119   if(!model) {
01120     kWarning() << "bad sender";
01121     return;
01122   }
01123   
01124   if(!m_waitingForReset.contains(model))
01125     return;
01126   
01127   m_waitingForReset.remove(model);
01128   
01129   if(m_waitingForReset.isEmpty()) {
01130     if(!isCompletionActive()) {
01131       kDebug() << "all completion-models we waited for are ready. Last one: " << model->objectName();
01132       //Eventually show the completion-list if this was the last model we were waiting for      
01133       //Use a queued connection once again to make sure that KateCompletionModel is notified before we are
01134       QMetaObject::invokeMethod(this, "modelContentChanged", Qt::QueuedConnection);
01135     }
01136   }
01137 }
01138 
01139 void KateCompletionWidget::modelDestroyed(QObject* model) {
01140   unregisterCompletionModel(static_cast<KTextEditor::CodeCompletionModel*>(model));
01141 }
01142 
01143 void KateCompletionWidget::registerCompletionModel(KTextEditor::CodeCompletionModel* model)
01144 {
01145   connect(model, SIGNAL(destroyed(QObject*)), SLOT(modelDestroyed(QObject*)));
01146   //This connection must not be queued
01147   connect(model, SIGNAL(modelReset()), SLOT(completionModelReset()));
01148 
01149   m_sourceModels.append(model);
01150 
01151   if (isCompletionActive()) {
01152     m_presentationModel->addCompletionModel(model);
01153   }
01154 }
01155 
01156 void KateCompletionWidget::unregisterCompletionModel(KTextEditor::CodeCompletionModel* model)
01157 {
01158   disconnect(model, SIGNAL(destroyed(QObject*)), this, SLOT(modelDestroyed(QObject*)));
01159   disconnect(model, SIGNAL(modelReset()), this, SLOT(completionModelReset()));
01160 
01161   m_sourceModels.removeAll(model);
01162   abortCompletion();
01163 }
01164 
01165 int KateCompletionWidget::automaticInvocationDelay() const {
01166   return m_automaticInvocationDelay;
01167 }
01168 
01169 void KateCompletionWidget::setAutomaticInvocationDelay(int delay) {
01170   m_automaticInvocationDelay = delay;
01171 }
01172 
01173 void KateCompletionWidget::editDone(KateEditInfo * edit)
01174 {
01175   if(!edit->newText().join("\n").trimmed().isEmpty())
01176   m_lastInsertionByUser = edit->editSource() == Kate::UserInputEdit;
01177 
01178   if (!view()->config()->automaticCompletionInvocation()
01179        || (edit->editSource() != Kate::UserInputEdit)
01180        || edit->isRemoval()
01181        || (edit->editSource() != Kate::UserInputEdit && edit->editSource() != Kate::CodeCompletionEdit)
01182        || edit->newText().isEmpty() )
01183   {
01184     m_automaticInvocationLine.clear();
01185     m_automaticInvocationTimer->stop();
01186     return;
01187   }
01188 
01189   if(m_automaticInvocationAt != edit->newRange().start()) {
01190     m_automaticInvocationLine.clear();
01191     m_lastInsertionByUser = edit->editSource() == Kate::UserInputEdit;
01192   }
01193 
01194   m_automaticInvocationLine += edit->newText().last();
01195   m_automaticInvocationAt = edit->newRange().end();
01196 
01197   if (m_automaticInvocationLine.isEmpty()) {
01198     m_automaticInvocationTimer->stop();
01199     return;
01200   }
01201 
01202   m_automaticInvocationTimer->start(m_automaticInvocationDelay);
01203 }
01204 
01205 void KateCompletionWidget::automaticInvocation()
01206 {
01207   if(m_automaticInvocationAt != view()->cursorPosition())
01208     return;
01209   bool start = false;
01210 
01211   foreach (KTextEditor::CodeCompletionModel *model, m_sourceModels) {
01212       if(m_completionRanges.contains(model))
01213         continue;
01214       start = modelController(model)->shouldStartCompletion(view(), m_automaticInvocationLine, m_lastInsertionByUser, view()->cursorPosition());
01215       if (start) break;
01216   }
01217   if (start) {
01218     // Start automatic code completion
01219     startCompletion(KTextEditor::CodeCompletionModel::AutomaticInvocation);
01220   }
01221 }
01222 
01223 void KateCompletionWidget::userInvokedCompletion()
01224 {
01225   startCompletion(KTextEditor::CodeCompletionModel::UserInvocation);
01226 }
01227 
01228 #include "katecompletionwidget.moc"
01229 
01230 // kate: space-indent on; indent-width 2; replace-tabs on;

Kate

Skip menu "Kate"
  • Main Page
  • Namespace List
  • Class Hierarchy
  • Alphabetical List
  • Class List
  • File List
  • Namespace Members
  • Class Members
  • Related Pages

kdelibs

Skip menu "kdelibs"
  • DNSSD
  • Interfaces
  •   KHexEdit
  •   KMediaPlayer
  •   KSpeech
  •   KTextEditor
  • Kate
  • kconf_update
  • KDE3Support
  •   KUnitTest
  • KDECore
  • KDED
  • KDEsu
  • KDEUI
  • KDocTools
  • KFile
  • KHTML
  • KImgIO
  • KInit
  • kio
  • KIOSlave
  • KJS
  •   KJS-API
  •   WTF
  • kjsembed
  • KNewStuff
  • KParts
  • KPty
  • Kross
  • KUtils
  • Nepomuk
  • Plasma
  • Solid
  • Sonnet
  • ThreadWeaver
Generated for kdelibs by doxygen 1.6.1
This website is maintained by Adriaan de Groot and Allen Winter.
KDE® and the K Desktop Environment® logo are registered trademarks of KDE e.V. | Legal