• 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 
00004    This library is free software; you can redistribute it and/or
00005    modify it under the terms of the GNU Library General Public
00006    License version 2 as published by the Free Software Foundation.
00007 
00008    This library is distributed in the hope that it will be useful,
00009    but WITHOUT ANY WARRANTY; without even the implied warranty of
00010    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00011    Library General Public License for more details.
00012 
00013    You should have received a copy of the GNU Library General Public License
00014    along with this library; see the file COPYING.LIB.  If not, write to
00015    the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
00016    Boston, MA 02110-1301, USA.
00017 */
00018 
00019 #include "katecompletionwidget.h"
00020 
00021 #include <QtGui/QBoxLayout>
00022 #include <QtGui/QApplication>
00023 #include <QtGui/QDesktopWidget>
00024 #include <QtGui/QHeaderView>
00025 #include <QtCore/QTimer>
00026 #include <QtGui/QLabel>
00027 #include <QtGui/QToolButton>
00028 #include <QtGui/QSizeGrip>
00029 #include <QtGui/QPushButton>
00030 #include <QtGui/QAbstractScrollArea>
00031 #include <QtGui/QScrollBar>
00032 #include <QtCore/QMutex>
00033 
00034 #include <kicon.h>
00035 #include <kdialog.h>
00036 
00037 #include <ktexteditor/cursorfeedback.h>
00038 
00039 #include "kateview.h"
00040 #include "katesmartmanager.h"
00041 #include "katerenderer.h"
00042 #include "kateconfig.h"
00043 #include "katedocument.h"
00044 #include "katesmartrange.h"
00045 #include "kateedit.h"
00046 
00047 #include "katecompletionmodel.h"
00048 #include "katecompletiontree.h"
00049 #include "katecompletionconfig.h"
00050 #include "kateargumenthinttree.h"
00051 #include "kateargumenthintmodel.h"
00052 
00053 //#include "modeltest.h"
00054 
00055 KateCompletionWidget::KateCompletionWidget(KateView* parent)
00056   : QFrame(parent, Qt::ToolTip)
00057   , m_presentationModel(new KateCompletionModel(this))
00058   , m_completionRange(0L)
00059   , m_entryList(new KateCompletionTree(this))
00060   , m_argumentHintModel(new KateArgumentHintModel(this))
00061   , m_argumentHintTree(new KateArgumentHintTree(this))
00062   , m_automaticInvocationDelay(300)
00063   , m_filterInstalled(false)
00064   , m_configWidget(new KateCompletionConfig(m_presentationModel, view()))
00065   , m_inCompletionList(false)
00066   , m_isSuspended(false)
00067   , m_dontShowArgumentHints(false)
00068   , m_needShow(false)
00069   , m_expandedAddedHeightBase(0)
00070   , m_expandingAddedHeight(0)
00071 {
00072 
00073   setFrameStyle( QFrame::Box | QFrame::Plain );
00074   setLineWidth( 1 );
00075   //setWindowOpacity(0.8);
00076 
00077   m_entryList->setModel(m_presentationModel);
00078   m_entryList->setColumnWidth(0, 0); //These will be determined automatically in KateCompletionTree::resizeColumns
00079   m_entryList->setColumnWidth(1, 0);
00080   m_entryList->setColumnWidth(2, 0);
00081 
00082   m_argumentHintTree->setParent(0, Qt::ToolTip);
00083   m_argumentHintTree->setModel(m_argumentHintModel);
00084 
00085   connect(m_entryList->verticalScrollBar(), SIGNAL(valueChanged(int)), m_presentationModel, SLOT(placeExpandingWidgets()));
00086   connect(m_argumentHintTree->verticalScrollBar(), SIGNAL(valueChanged(int)), m_argumentHintModel, SLOT(placeExpandingWidgets()));
00087   connect(view(), SIGNAL(focusOut(KTextEditor::View*)), this, SLOT(viewFocusOut()));
00088 
00089   m_automaticInvocationTimer = new QTimer(this);
00090   m_automaticInvocationTimer->setSingleShot(true);
00091   connect(m_automaticInvocationTimer, SIGNAL(timeout()), this, SLOT(automaticInvocation()));
00092 
00093   QVBoxLayout* vl = new QVBoxLayout(this);
00094   vl->addWidget(m_entryList);
00095   vl->setMargin(0);
00096 
00097   // Keep branches expanded
00098   connect(m_presentationModel, SIGNAL(modelReset()), this, SLOT(modelReset()), Qt::QueuedConnection);
00099   connect(m_presentationModel, SIGNAL(rowsInserted(const QModelIndex&, int, int)), SLOT(rowsInserted(const QModelIndex&, int, int)));
00100   connect(m_argumentHintModel, SIGNAL(contentStateChanged(bool)), this, SLOT(argumentHintsChanged(bool)));
00101 
00102   // These must be queued connections so that we're not holding the smart lock when we ask for the model to update.
00103   connect(view(), SIGNAL(cursorPositionChanged(KTextEditor::View*, const KTextEditor::Cursor&)), this, SLOT(cursorPositionChanged()), Qt::QueuedConnection);
00104   connect(view()->doc()->history(), SIGNAL(editDone(KateEditInfo*)), SLOT(editDone(KateEditInfo*)), Qt::QueuedConnection);
00105   connect(view(), SIGNAL(verticalScrollPositionChanged (KTextEditor::View*, const KTextEditor::Cursor&)), this, SLOT(updatePositionSlot()), Qt::QueuedConnection);
00106 
00107   // This is a non-focus widget, it is passed keyboard input from the view
00108 
00109   //We need to do this, because else the focus goes to nirvana without any control when the completion-widget is clicked.
00110   setFocusPolicy(Qt::ClickFocus);
00111   m_argumentHintTree->setFocusPolicy(Qt::ClickFocus);
00112 
00113   foreach (QWidget* childWidget, findChildren<QWidget*>())
00114     childWidget->setFocusPolicy(Qt::NoFocus);
00115 }
00116 
00117 KateCompletionWidget::~KateCompletionWidget() {
00118 }
00119 
00120 void KateCompletionWidget::viewFocusOut() {
00121   abortCompletion();
00122 }
00123 
00124 void KateCompletionWidget::modelContentChanged() {
00125   int realItemCount = 0;
00126   foreach (KTextEditor::CodeCompletionModel* model, m_presentationModel->completionModels())
00127     realItemCount += model->rowCount();
00128   if( !m_isSuspended && (!isVisible() || m_needShow) && realItemCount != 0 ) {
00129     m_needShow = false;
00130     updateAndShow();
00131   }
00132 
00133   if(m_presentationModel->rowCount(QModelIndex()) == 0 && m_argumentHintModel->rowCount(QModelIndex()) == 0) {
00134     kDebug( 13035 ) << "hiding because no content";
00135     hide();
00136     return;
00137   }
00138 
00139   //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
00140   m_entryList->setCurrentIndex(model()->index(0,0));
00141   if(!model()->indexIsItem(m_entryList->currentIndex())) {
00142     QModelIndex firstIndex = model()->index(0,0, m_entryList->currentIndex());
00143     m_entryList->setCurrentIndex(firstIndex);
00144     //m_entryList->scrollTo(firstIndex, QAbstractItemView::PositionAtTop);
00145   }
00146 }
00147 
00148 KateArgumentHintTree* KateCompletionWidget::argumentHintTree() const {
00149   return m_argumentHintTree;
00150 }
00151 
00152 KateArgumentHintModel* KateCompletionWidget::argumentHintModel() const {
00153   return m_argumentHintModel;
00154 }
00155 
00156 const KateCompletionModel* KateCompletionWidget::model() const {
00157   return m_presentationModel;
00158 }
00159 
00160 KateCompletionModel* KateCompletionWidget::model() {
00161   return m_presentationModel;
00162 }
00163 
00164 void KateCompletionWidget::rowsInserted(const QModelIndex& parent, int rowFrom, int rowEnd)
00165 {
00166   if (!parent.isValid())
00167     for (int i = rowFrom; i <= rowEnd; ++i)
00168       m_entryList->expand(m_presentationModel->index(i, 0, parent));
00169 }
00170 
00171 KateView * KateCompletionWidget::view( ) const
00172 {
00173   return static_cast<KateView*>(const_cast<QObject*>(parent()));
00174 }
00175 
00176 void KateCompletionWidget::argumentHintsChanged(bool hasContent)
00177 {
00178   m_dontShowArgumentHints = !hasContent;
00179 
00180   if( m_dontShowArgumentHints )
00181     m_argumentHintTree->hide();
00182   else
00183     updateArgumentHintGeometry();
00184 }
00185 
00186 void KateCompletionWidget::startCompletion( const KTextEditor::Range & word, KTextEditor::CodeCompletionModel * model, KTextEditor::CodeCompletionModel::InvocationType invocationType)
00187 {
00188   m_isSuspended = false;
00189   m_inCompletionList = true; //Always start at the top of the completion-list
00190   m_needShow = true;
00191 
00192   disconnect(this->model(), SIGNAL(contentGeometryChanged()), this, SLOT(modelContentChanged()));
00193 
00194   m_dontShowArgumentHints = true;
00195 
00196   if (!word.isValid()) {
00197     kWarning(13035) << "Invalid range given to start code completion!";
00198     return;
00199   }
00200 
00201   kDebug(13035) << word << " " << model;
00202 
00203   if (!m_filterInstalled) {
00204     if (!QApplication::activeWindow()) {
00205       kWarning(13035) << "No active window to install event filter on!!";
00206       return;
00207     }
00208     // Enable the cc box to move when the editor window is moved
00209     QApplication::activeWindow()->installEventFilter(this);
00210     m_filterInstalled = true;
00211   }
00212 
00213   if (isCompletionActive())
00214     abortCompletion();
00215 
00216   m_completionRange = view()->doc()->smartManager()->newSmartRange(word);
00217   m_completionRange->setInsertBehavior(KTextEditor::SmartRange::ExpandRight | KTextEditor::SmartRange::ExpandLeft);
00218   if(!m_completionRange->isValid()) {
00219     kWarning(13035) << "Could not construct valid smart-range from" << word << "instead got" << *m_completionRange;
00220     abortCompletion();
00221     return;
00222   }
00223 
00224   connect(m_completionRange->smartStart().notifier(), SIGNAL(characterDeleted(KTextEditor::SmartCursor*, bool)), SLOT(startCharacterDeleted(KTextEditor::SmartCursor*, bool)));
00225 
00226   cursorPositionChanged();
00227 
00228   if (model)
00229     model->completionInvoked(view(), word, invocationType);
00230   else
00231     foreach (KTextEditor::CodeCompletionModel* model, m_sourceModels)
00232       model->completionInvoked(view(), word, invocationType);
00233 
00234   kDebug( 13035 ) << "msdofjdsoifdsflkdsjf";
00235   if (model)
00236     m_presentationModel->setCompletionModel(model);
00237   else
00238     m_presentationModel->setCompletionModels(m_sourceModels);
00239 
00240   if (!m_presentationModel->completionModels().isEmpty()) {
00241     m_presentationModel->setCurrentCompletion(view()->doc()->text(KTextEditor::Range(m_completionRange->start(), view()->cursorPosition())));
00242   }
00243 
00244   connect(this->model(), SIGNAL(contentGeometryChanged()), this, SLOT(modelContentChanged()));
00245   //Now that all models have been notified, check whether the widget should be displayed instantly
00246   modelContentChanged();
00247 }
00248 
00249 void KateCompletionWidget::updateAndShow()
00250 {
00251   setUpdatesEnabled(false);
00252 
00253   updatePosition(true);
00254 
00255   if (!m_presentationModel->completionModels().isEmpty()) {
00256     show();
00257     m_entryList->resizeColumns(false, true);
00258 
00259     m_argumentHintModel->buildRows();
00260     if( m_argumentHintModel->rowCount(QModelIndex()) != 0 )
00261       argumentHintsChanged(true);
00262   }
00263 
00264   setUpdatesEnabled(true);
00265 }
00266 
00267 void KateCompletionWidget::updatePositionSlot()
00268 {
00269   updatePosition();
00270 }
00271 
00272 void KateCompletionWidget::updatePosition(bool force)
00273 {
00274   if (!force && !isCompletionActive())
00275     return;
00276 
00277   QPoint cursorPosition = view()->cursorToCoordinate(m_completionRange->start());
00278   if (cursorPosition == QPoint(-1,-1))
00279     // Start of completion range is now off-screen -> abort
00280     return abortCompletion();
00281 
00282   QPoint p = view()->mapToGlobal( cursorPosition );
00283   int x = p.x() - m_entryList->columnViewportPosition(m_presentationModel->translateColumn(KTextEditor::CodeCompletionModel::Name)) - 2;
00284   int y = p.y();
00285   //We do not need to move the widget up, because updateHeight will resize the widget to fit the screen
00286 /*  if ( y + height() + view()->renderer()->config()->fontMetrics().height() > QApplication::desktop()->screenGeometry(this).bottom() )
00287     y -= height();
00288   else*/
00289   y += view()->renderer()->config()->fontMetrics().height();
00290 
00291   if (x + width() > QApplication::desktop()->screenGeometry(view()).right())
00292     x = QApplication::desktop()->screenGeometry(view()).right() - width();
00293 
00294   if( x < QApplication::desktop()->screenGeometry(view()).left() )
00295     x = QApplication::desktop()->screenGeometry(view()).left();
00296 
00297   move( QPoint(x,y) );
00298 
00299   updateHeight();
00300 
00301   updateArgumentHintGeometry();
00302 }
00303 
00304 void KateCompletionWidget::updateArgumentHintGeometry()
00305 {
00306   if( !m_dontShowArgumentHints ) {
00307     //Now place the argument-hint widget
00308     QRect geom = m_argumentHintTree->geometry();
00309     geom.moveTo(pos());
00310     geom.setWidth(width());
00311     geom.moveBottom(pos().y() - view()->renderer()->config()->fontMetrics().height()*2);
00312     m_argumentHintTree->updateGeometry(geom);
00313   }
00314 }
00315 
00316 void KateCompletionWidget::updateHeight()
00317 {
00318   QRect geom = geometry();
00319 
00320   int baseHeight = geom.height() - m_expandingAddedHeight;
00321 
00322   if( m_expandedAddedHeightBase != baseHeight && m_expandedAddedHeightBase - baseHeight > -2 && m_expandedAddedHeightBase - baseHeight < 2  )
00323   {
00324     //Re-use the stored base-height if it only slightly differs from the current one.
00325     //Reason: Qt seems to apply slightly wrong sizes when the completion-widget is moved out of the screen at the bottom,
00326     //        which completely breaks this algorithm. Solution: re-use the old base-size if it only slightly differs from the computed one.
00327     baseHeight = m_expandedAddedHeightBase;
00328   }
00329 
00330   if( baseHeight < 300 ) {
00331     baseHeight = 300; //Here we enforce a minimum desirable height
00332     m_expandingAddedHeight = 0;
00333 //     kDebug( 13035 ) << "Resetting baseHeight and m_expandingAddedHeight";
00334   }
00335 
00336   int newExpandingAddedHeight = 0;
00337 
00338 //   kDebug( 13035 ) << "baseHeight: " << baseHeight;
00339 
00340   newExpandingAddedHeight = model()->expandingWidgetsHeight();
00341 
00342 //   kDebug( 13035 ) << "new newExpandingAddedHeight: " << newExpandingAddedHeight;
00343 
00344   int screenBottom = QApplication::desktop()->screenGeometry(view()).bottom();
00345 
00346   int bottomPosition = baseHeight + newExpandingAddedHeight + geometry().top();
00347 //  int targetHeight = baseHeight + newExpandingAddedHeight;
00348 //   kDebug( 13035 ) << "targetHeight: " << targetHeight;
00349 
00350 //   kDebug( 13035 ) << "screen-bottom: " << screenBottom << " bottomPosition: " << bottomPosition;
00351 
00352   if( bottomPosition > screenBottom-50 ) {
00353     newExpandingAddedHeight -= bottomPosition - (screenBottom-50);
00354 //     kDebug( 13035 ) << "Too high, moved bottomPosition to: " << baseHeight + newExpandingAddedHeight + geometry().top() << " changed newExpandingAddedHeight to " << newExpandingAddedHeight;
00355   }
00356 
00357   int finalHeight = baseHeight+newExpandingAddedHeight;
00358 //   kDebug( 13035 ) << "finalHeight: " << finalHeight;
00359   if( finalHeight < 50 ) {
00360     return;
00361   }
00362 
00363   m_expandingAddedHeight = baseHeight;
00364   m_expandedAddedHeightBase = geometry().height();
00365 
00366   geom.setHeight(finalHeight);
00367 
00368   setGeometry(geom);
00369 }
00370 
00371 
00372 void KateCompletionWidget::cursorPositionChanged( )
00373 {
00374   if (!isCompletionActive())  {
00375 //    m_presentationModel->setCurrentCompletion(QString());
00376     return;
00377   }
00378 
00379   KTextEditor::Cursor cursor = view()->cursorPosition();
00380 
00381   if (m_completionRange->start() > cursor || m_completionRange->end() < cursor)
00382     return abortCompletion();
00383 
00384   QString currentCompletion = view()->doc()->text(KTextEditor::Range(m_completionRange->start(), view()->cursorPosition()));
00385 
00386   // FIXME - allow client to specify this?
00387   static QRegExp allowedText("^(\\w*)");
00388   if (!allowedText.exactMatch(currentCompletion))
00389     return abortCompletion();
00390 
00391   m_presentationModel->setCurrentCompletion(currentCompletion);
00392 }
00393 
00394 bool KateCompletionWidget::isCompletionActive( ) const
00395 {
00396   return m_completionRange && isVisible();
00397 }
00398 
00399 void KateCompletionWidget::abortCompletion( )
00400 {
00401   kDebug(13035) ;
00402 
00403   m_isSuspended = false;
00404 
00405   bool wasActive = isCompletionActive();
00406 
00407   clear();
00408 
00409   if(isVisible())
00410     hide();
00411 
00412   delete m_completionRange;
00413   m_completionRange = 0L;
00414 
00415   if (wasActive)
00416     view()->sendCompletionAborted();
00417 }
00418 
00419 void KateCompletionWidget::clear() {
00420   m_presentationModel->clearCompletionModels();
00421   m_argumentHintTree->clearCompletion();
00422   m_argumentHintModel->clear();
00423 }
00424 
00425 void KateCompletionWidget::execute(bool shift)
00426 {
00427   kDebug(13035) ;
00428 
00429   if (!isCompletionActive())
00430     return;
00431 
00432   QModelIndex index = selectedIndex();
00433   
00434   if( shift ) {
00435     if( index.isValid() )
00436       index.data(KTextEditor::CodeCompletionModel::AccessibilityAccept);
00437 
00438     return;
00439   }
00440 
00441   if (!index.isValid())
00442     return abortCompletion();
00443 
00444   QModelIndex toExecute;
00445   
00446   if(index.model() == m_presentationModel)
00447     toExecute = m_presentationModel->mapToSource(index);
00448   else
00449     toExecute = m_argumentHintModel->mapToSource(index);
00450 
00451   if (!toExecute.isValid()) {
00452     kWarning() << k_funcinfo << "Could not map index" << m_entryList->selectionModel()->currentIndex() << "to source index.";
00453     return abortCompletion();
00454   }
00455 
00456   KTextEditor::Cursor start = m_completionRange->start();
00457 
00458   // encapsulate all editing as being from the code completion, and undo-able in one step.
00459   view()->doc()->editStart(true, Kate::CodeCompletionEdit);
00460 
00461   KTextEditor::CodeCompletionModel* model = static_cast<KTextEditor::CodeCompletionModel*>(const_cast<QAbstractItemModel*>(toExecute.model()));
00462   Q_ASSERT(model);
00463 
00464   KTextEditor::CodeCompletionModel2* model2 = dynamic_cast<KTextEditor::CodeCompletionModel2*>(model);
00465 
00466   //editStart locks the smart-mutex, but it must not be locked when calling external functions,
00467   //else we may get deadlock-issues.
00468   view()->doc()->smartMutex()->unlock();
00469   
00470   if(model2)
00471     model2->executeCompletionItem2(view()->document(), *m_completionRange, toExecute);
00472   else if(toExecute.parent().isValid())
00473     //The normale CodeCompletionInterface cannot handle feedback for hierarchical models, so just do the replacement
00474     view()->document()->replaceText(*m_completionRange, model->data(toExecute.sibling(toExecute.row(), KTextEditor::CodeCompletionModel::Name)).toString());
00475   else
00476     model->executeCompletionItem(view()->document(), *m_completionRange, toExecute.row());
00477   
00478   //Relock, because editEnd expects it to be locked
00479   view()->doc()->smartMutex()->lock();
00480 
00481   view()->doc()->editEnd();
00482 
00483   hide();
00484 
00485   view()->sendCompletionExecuted(start, model, toExecute);
00486 }
00487 
00488 void KateCompletionWidget::resizeEvent( QResizeEvent * event )
00489 {
00490   QWidget::resizeEvent(event);
00491 
00492   m_entryList->resizeColumns(true);
00493 }
00494 
00495 void KateCompletionWidget::showEvent ( QShowEvent * event )
00496 {
00497   m_isSuspended = false;
00498 
00499   QWidget::showEvent(event);
00500 
00501   if( !m_dontShowArgumentHints && m_argumentHintModel->rowCount(QModelIndex()) != 0 )
00502     m_argumentHintTree->show();
00503 }
00504 
00505 void KateCompletionWidget::hideEvent( QHideEvent * event )
00506 {
00507   QWidget::hideEvent(event);
00508   m_argumentHintTree->hide();
00509 
00510   if (isCompletionActive())
00511     abortCompletion();
00512 }
00513 
00514 KateSmartRange * KateCompletionWidget::completionRange( ) const
00515 {
00516   return m_completionRange;
00517 }
00518 
00519 void KateCompletionWidget::modelReset( )
00520 {
00521   m_argumentHintTree->expandAll();
00522   m_entryList->expandAll();
00523 }
00524 
00525 KateCompletionTree* KateCompletionWidget::treeView() const {
00526   return m_entryList;
00527 }
00528 
00529 QModelIndex KateCompletionWidget::selectedIndex() const {
00530   if( m_inCompletionList )
00531     return m_entryList->currentIndex();
00532   else
00533     return m_argumentHintTree->currentIndex();
00534 }
00535 
00536 bool KateCompletionWidget::cursorLeft( bool shift ) {
00537   if( shift ) {
00538     QModelIndex index = selectedIndex();
00539 
00540     if( index.isValid() )
00541       index.data(KTextEditor::CodeCompletionModel::AccessibilityPrevious);
00542 
00543     return true;
00544   }
00545 
00546   if (canCollapseCurrentItem() ) {
00547     setCurrentItemExpanded(false);
00548     return true;
00549   }
00550   return false;
00551 }
00552 
00553 bool KateCompletionWidget::cursorRight( bool shift ) {
00554   if( shift ) {
00555     QModelIndex index = selectedIndex();
00556 
00557     if( index.isValid() )
00558       index.data(KTextEditor::CodeCompletionModel::AccessibilityNext);
00559 
00560     return true;
00561   }
00562 
00563   if ( canExpandCurrentItem() ) {
00564     setCurrentItemExpanded(true);
00565     return true;
00566   }
00567   return false;
00568 }
00569 
00570 bool KateCompletionWidget::canExpandCurrentItem() const {
00571   if( m_inCompletionList ) {
00572     if( !m_entryList->currentIndex().isValid() ) return false;
00573     return model()->isExpandable( m_entryList->currentIndex() ) && !model()->isExpanded( m_entryList->currentIndex() );
00574   } else {
00575     if( !m_argumentHintTree->currentIndex().isValid() ) return false;
00576     return argumentHintModel()->isExpandable( m_argumentHintTree->currentIndex() ) && !argumentHintModel()->isExpanded( m_argumentHintTree->currentIndex() );
00577   }
00578 }
00579 
00580 bool KateCompletionWidget::canCollapseCurrentItem() const {
00581   if( m_inCompletionList ) {
00582     if( !m_entryList->currentIndex().isValid() ) return false;
00583     return model()->isExpandable( m_entryList->currentIndex() ) && model()->isExpanded( m_entryList->currentIndex() );
00584   }else{
00585     if( !m_argumentHintTree->currentIndex().isValid() ) return false;
00586     return m_argumentHintModel->isExpandable( m_argumentHintTree->currentIndex() ) && m_argumentHintModel->isExpanded( m_argumentHintTree->currentIndex() );
00587   }
00588 }
00589 
00590 void KateCompletionWidget::setCurrentItemExpanded( bool expanded ) {
00591   if( m_inCompletionList ) {
00592     if( !m_entryList->currentIndex().isValid() ) return;
00593     model()->setExpanded(m_entryList->currentIndex(), expanded);
00594     updateHeight();
00595   }else{
00596     if( !m_argumentHintTree->currentIndex().isValid() ) return;
00597     m_argumentHintModel->setExpanded(m_argumentHintTree->currentIndex(), expanded);
00598   }
00599 }
00600 
00601 void KateCompletionWidget::startCharacterDeleted( KTextEditor::SmartCursor*, bool deletedBefore )
00602 {
00603   if (deletedBefore)
00604     // Cannot abort completion from this function, or the cursor will be deleted before returning
00605     QTimer::singleShot(0, this, SLOT(abortCompletion()));
00606 }
00607 
00608 bool KateCompletionWidget::eventFilter( QObject * watched, QEvent * event )
00609 {
00610   bool ret = QFrame::eventFilter(watched, event);
00611 
00612   if (watched != this)
00613     if (event->type() == QEvent::Move)
00614       updatePosition();
00615 
00616   return ret;
00617 }
00618 
00619 void KateCompletionWidget::cursorDown( bool shift )
00620 {
00621   if( shift ) {
00622     QModelIndex index = selectedIndex();
00623     if( dynamic_cast<const ExpandingWidgetModel*>(index.model()) ) {
00624       const ExpandingWidgetModel* model = static_cast<const ExpandingWidgetModel*>(index.model());
00625       if( model->isExpanded(index) ) {
00626         QWidget* widget = model->expandingWidget(index);
00627         QAbstractScrollArea* area = qobject_cast<QAbstractScrollArea*>(widget);
00628         if( area && area->verticalScrollBar() )
00629           area->verticalScrollBar()->setSliderPosition( area->verticalScrollBar()->sliderPosition() + area->verticalScrollBar()->singleStep() );
00630       }
00631     }
00632     return;
00633   }
00634 
00635   if( m_inCompletionList )
00636     m_entryList->nextCompletion();
00637   else {
00638     if( !m_argumentHintTree->nextCompletion() )
00639       switchList();
00640   }
00641 }
00642 
00643 void KateCompletionWidget::cursorUp( bool shift )
00644 {
00645   if( shift ) {
00646     QModelIndex index = selectedIndex();
00647     if( dynamic_cast<const ExpandingWidgetModel*>(index.model()) ) {
00648       const ExpandingWidgetModel* model = static_cast<const ExpandingWidgetModel*>(index.model());
00649       if( model->isExpanded(index) ) {
00650         QWidget* widget = model->expandingWidget(index);
00651         QAbstractScrollArea* area = qobject_cast<QAbstractScrollArea*>(widget);
00652         if( area && area->verticalScrollBar() )
00653           area->verticalScrollBar()->setSliderPosition( area->verticalScrollBar()->sliderPosition() - area->verticalScrollBar()->singleStep() );
00654       }
00655     }
00656     return;
00657   }
00658 
00659   if( m_inCompletionList ) {
00660     if( !m_entryList->previousCompletion() )
00661       switchList();
00662   }else{
00663     m_argumentHintTree->previousCompletion();
00664   }
00665 }
00666 
00667 void KateCompletionWidget::pageDown( )
00668 {
00669   if( m_inCompletionList )
00670     m_entryList->pageDown();
00671   else {
00672     if( !m_argumentHintTree->pageDown() )
00673       switchList();
00674   }
00675 }
00676 
00677 void KateCompletionWidget::pageUp( )
00678 {
00679   if( m_inCompletionList ) {
00680     if( !m_entryList->pageUp() )
00681       switchList();
00682   }else{
00683     m_argumentHintTree->pageUp();
00684   }
00685 }
00686 
00687 void KateCompletionWidget::top( )
00688 {
00689   if( m_inCompletionList )
00690     m_entryList->top();
00691   else
00692     m_argumentHintTree->top();
00693 }
00694 
00695 void KateCompletionWidget::bottom( )
00696 {
00697   if( m_inCompletionList )
00698     m_entryList->bottom();
00699   else
00700     m_argumentHintTree->bottom();
00701 }
00702 
00703 void KateCompletionWidget::switchList() {
00704   if( m_inCompletionList ) {
00705       m_entryList->setCurrentIndex(QModelIndex());
00706       if( m_argumentHintModel->rowCount(QModelIndex()) != 0 )
00707         m_argumentHintTree->setCurrentIndex(m_argumentHintModel->index(m_argumentHintModel->rowCount(QModelIndex())-1, 0));
00708   } else {
00709       m_argumentHintTree->setCurrentIndex(QModelIndex());
00710       if( m_presentationModel->rowCount(QModelIndex()) != 0 )
00711         m_entryList->setCurrentIndex(m_presentationModel->index(0, 0));
00712   }
00713   m_inCompletionList = !m_inCompletionList;
00714 }
00715 
00716 void KateCompletionWidget::showConfig( )
00717 {
00718   abortCompletion();
00719 
00720   m_configWidget->exec();
00721 }
00722 
00723 void KateCompletionWidget::registerCompletionModel(KTextEditor::CodeCompletionModel* model)
00724 {
00725   m_sourceModels.append(model);
00726 
00727   if (isCompletionActive()) {
00728     m_presentationModel->addCompletionModel(model);
00729   }
00730 }
00731 
00732 void KateCompletionWidget::unregisterCompletionModel(KTextEditor::CodeCompletionModel* model)
00733 {
00734   m_sourceModels.removeAll(model);
00735 }
00736 
00737 int KateCompletionWidget::automaticInvocationDelay() const {
00738   return m_automaticInvocationDelay;
00739 }
00740 
00741 void KateCompletionWidget::setAutomaticInvocationDelay(int delay) {
00742   m_automaticInvocationDelay = delay;
00743 }
00744 
00745 
00746 void KateCompletionWidget::editDone(KateEditInfo * edit)
00747 {
00748   if (!view()->config()->automaticCompletionInvocation()
00749        || (edit->editSource() != Kate::UserInputEdit)
00750        || edit->isRemoval()
00751        || isCompletionActive()
00752        || edit->newText().isEmpty() )
00753   {
00754     m_automaticInvocationTimer->stop();
00755     return;
00756   }
00757 
00758   m_automaticInvocationLine = edit->newText().last();
00759 
00760   if (m_automaticInvocationLine.isEmpty()) {
00761     m_automaticInvocationTimer->stop();
00762     return;
00763   }
00764 
00765   m_automaticInvocationTimer->start(m_automaticInvocationDelay);
00766 }
00767 
00768 void KateCompletionWidget::automaticInvocation()
00769 {
00770   if(m_automaticInvocationLine.isEmpty())
00771     return;
00772 
00773   QString lastLine = m_automaticInvocationLine;
00774   QChar lastChar = lastLine.at(lastLine.count() - 1);
00775 
00776   if (lastChar.isLetter() || lastChar.isNumber() || lastChar == '.' || lastChar == '_' || lastChar == '>') {
00777     // Start automatic code completion
00778     KTextEditor::Range range = determineRange();
00779     if (range.isValid())
00780       startCompletion(range, 0, KTextEditor::CodeCompletionModel::AutomaticInvocation);
00781     else
00782       kWarning(13035) << "Completion range was invalid even though it was expected to be valid.";
00783   }
00784 }
00785 
00786 void KateCompletionWidget::userInvokedCompletion()
00787 {
00788   startCompletion(determineRange(), 0, KTextEditor::CodeCompletionModel::UserInvocation);
00789 }
00790 
00791 KTextEditor::Range KateCompletionWidget::determineRange() const
00792 {
00793   KTextEditor::Cursor end = view()->cursorPosition();
00794 
00795   // the end cursor should always be valid, otherwise things go wrong
00796   // Assumption: view()->cursorPosition() is always valid.
00797   Q_ASSERT(end.isValid());
00798 
00799   QString text = view()->document()->line(end.line());
00800 
00801   static QRegExp findWordStart( "\\b([_\\w]+)$" );
00802   static QRegExp findWordEnd( "^([_\\w]*)\\b" );
00803 
00804   KTextEditor::Cursor start = end;
00805 
00806   if (findWordStart.lastIndexIn(text.left(end.column())) >= 0)
00807     start.setColumn(findWordStart.pos(1));
00808 
00809   if (findWordEnd.indexIn(text.mid(end.column())) >= 0)
00810     end.setColumn(end.column() + findWordEnd.cap(1).length());
00811 
00812   return KTextEditor::Range(start, end);
00813 }
00814 
00815 #include "katecompletionwidget.moc"
00816 
00817 // 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