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