• Skip to content
  • Skip to link menu
KDE 4.6 API Reference
  • KDE API Reference
  • kdelibs
  • KDE Home
  • Contact Us
 

Kate

katecompletionwidget.cpp

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