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