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

Kate

katecompletionmodel.cpp

Go to the documentation of this file.
00001 /* This file is part of the KDE libraries
00002    Copyright (C) 2005-2007 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 "katecompletionmodel.h"
00021 
00022 #include <QTextEdit>
00023 #include <QMultiMap>
00024 #include <QTimer>
00025 
00026 #include <klocale.h>
00027 #include <kiconloader.h>
00028 #include <kapplication.h>
00029 
00030 #include "katecompletionwidget.h"
00031 #include "katecompletiontree.h"
00032 #include "katecompletiondelegate.h"
00033 #include "kateargumenthintmodel.h"
00034 #include "kateview.h"
00035 #include "katerenderer.h"
00036 #include "kateconfig.h"
00037 #include <ktexteditor/codecompletionmodelcontrollerinterface.h>
00038 
00039 using namespace KTextEditor;
00040 
00042 class HierarchicalModelHandler {
00043 public:
00044   HierarchicalModelHandler(CodeCompletionModel* model);
00045   void addValue(CodeCompletionModel::ExtraItemDataRoles role, const QVariant& value);
00046   //Walks the index upwards and collects all defined completion-roles on the way
00047   void collectRoles(const QModelIndex& index);
00048   void takeRole(const QModelIndex& index);
00049 
00050   CodeCompletionModel* model() const;
00051 
00052   //Assumes that index is a sub-index of the indices where role-values were taken
00053   QVariant getData(CodeCompletionModel::ExtraItemDataRoles role, const QModelIndex& index) const;
00054   
00055   bool hasHierarchicalRoles() const;
00056 
00057   int inheritanceDepth(const QModelIndex& i) const;
00058   
00059   QString customGroup() const {
00060     return m_customGroup;
00061   }
00062   
00063   int customGroupingKey() const {
00064     return m_groupSortingKey;
00065   }
00066 private:
00067   typedef QMap<CodeCompletionModel::ExtraItemDataRoles, QVariant> RoleMap;
00068   RoleMap m_roleValues;
00069   QString m_customGroup;
00070   int m_groupSortingKey;
00071   CodeCompletionModel* m_model;
00072 };
00073 
00074 CodeCompletionModel* HierarchicalModelHandler::model() const {
00075   return m_model;
00076 }
00077 
00078 bool HierarchicalModelHandler::hasHierarchicalRoles() const {
00079   return !m_roleValues.isEmpty();
00080 }
00081 
00082 void HierarchicalModelHandler::collectRoles(const QModelIndex& index) {
00083   if( index.parent().isValid() )
00084     collectRoles(index.parent());
00085   if(m_model->rowCount(index) != 0)
00086     takeRole(index);
00087 }
00088 
00089 int HierarchicalModelHandler::inheritanceDepth(const QModelIndex& i) const {
00090   return getData(CodeCompletionModel::InheritanceDepth, i).toInt();
00091 }
00092 
00093 void HierarchicalModelHandler::takeRole(const QModelIndex& index) {
00094   QVariant v = index.data(CodeCompletionModel::GroupRole);
00095   if( v.isValid() && v.canConvert(QVariant::Int) ) {
00096     QVariant value = index.data(v.toInt());
00097     if(v.toInt() == Qt::DisplayRole) {
00098       m_customGroup = index.data(Qt::DisplayRole).toString();
00099       QVariant sortingKey = index.data(CodeCompletionModel::InheritanceDepth);
00100       if(sortingKey.canConvert(QVariant::Int))
00101         m_groupSortingKey = sortingKey.toInt();
00102     }else{
00103       m_roleValues[(CodeCompletionModel::ExtraItemDataRoles)v.toInt()] = value;
00104     }
00105   }else{
00106     kDebug( 13035 ) << "Did not return valid GroupRole in hierarchical completion-model";
00107   }
00108 }
00109 
00110 QVariant HierarchicalModelHandler::getData(CodeCompletionModel::ExtraItemDataRoles role, const QModelIndex& index) const {
00111   RoleMap::const_iterator it = m_roleValues.find(role);
00112   if( it != m_roleValues.end() )
00113     return *it;
00114   else
00115     return index.data(role);
00116 }
00117 
00118 HierarchicalModelHandler::HierarchicalModelHandler(CodeCompletionModel* model) : m_groupSortingKey(-1),  m_model(model)  {
00119 }
00120 
00121 void HierarchicalModelHandler::addValue(CodeCompletionModel::ExtraItemDataRoles role, const QVariant& value) {
00122   m_roleValues[role] = value;
00123 }
00124 
00125 KateCompletionModel::KateCompletionModel(KateCompletionWidget* parent)
00126   : ExpandingWidgetModel(parent)
00127   , m_hasGroups(false)
00128   , m_matchCaseSensitivity(Qt::CaseInsensitive)
00129   , m_ungrouped(new Group(this))
00130   , m_argumentHints(new Group(this))
00131   , m_bestMatches(new Group(this))
00132   , m_sortingEnabled(false)
00133   , m_sortingAlphabetical(false)
00134   , m_isSortingByInheritance(false)
00135   , m_sortingCaseSensitivity(Qt::CaseInsensitive)
00136   , m_filteringEnabled(false)
00137   , m_filterContextMatchesOnly(false)
00138   , m_filterByAttribute(false)
00139   , m_filterAttributes(KTextEditor::CodeCompletionModel::NoProperty)
00140   , m_maximumInheritanceDepth(0)
00141   , m_groupingEnabled(false)
00142   , m_accessConst(false)
00143   , m_accessStatic(false)
00144   , m_accesSignalSlot(false)
00145   , m_columnMergingEnabled(false)
00146 //   , m_haveExactMatch(false)
00147 {
00148 
00149   m_ungrouped->attribute = 0;
00150   m_argumentHints->attribute = -1;
00151   m_bestMatches->attribute = BestMatchesProperty;
00152 
00153   m_argumentHints->title = i18n("Argument-hints");
00154   m_bestMatches->title = i18n("Best matches");
00155 
00156   m_emptyGroups.append(m_ungrouped);
00157   m_emptyGroups.append(m_argumentHints);
00158   m_emptyGroups.append(m_bestMatches);
00159 
00160   m_updateBestMatchesTimer = new QTimer(this);
00161   m_updateBestMatchesTimer->setSingleShot(true);
00162   connect(m_updateBestMatchesTimer, SIGNAL(timeout()), this, SLOT(updateBestMatches()));
00163 
00164   m_groupHash.insert(0, m_ungrouped);
00165   m_groupHash.insert(-1, m_argumentHints);
00166   m_groupHash.insert(BestMatchesProperty, m_argumentHints);
00167 }
00168 
00169 KateCompletionModel::~KateCompletionModel() {
00170   clearCompletionModels();
00171   delete m_argumentHints;
00172   delete m_ungrouped;
00173   delete m_bestMatches;
00174 }
00175 
00176 QTreeView* KateCompletionModel::treeView() const {
00177   return view()->completionWidget()->treeView();
00178 }
00179 
00180 QVariant KateCompletionModel::data( const QModelIndex & index, int role ) const
00181 {
00182   if (!hasCompletionModel() || !index.isValid())
00183     return QVariant();
00184 
00185   if( role == Qt::DecorationRole && index.column() == KTextEditor::CodeCompletionModel::Prefix && isExpandable(index) )
00186   {
00187     cacheIcons();
00188 
00189     if( !isExpanded(index ) )
00190       return QVariant( m_collapsedIcon );
00191     else
00192       return QVariant( m_expandedIcon );
00193   }
00194 
00195   //groupOfParent returns a group when the index is a member of that group, but not the group head/label.
00196   if (!hasGroups() || groupOfParent(index)) {
00197     switch (role) {
00198       case Qt::TextAlignmentRole:
00199         if (isColumnMergingEnabled() && m_columnMerges.count()) {
00200           int c = 0;
00201           foreach (const QList<int>& list, m_columnMerges) {
00202             foreach (int column, list) {
00203               if (c++ == index.column()) {
00204                 if (column == CodeCompletionModel::Scope)
00205                   if (list.count() == 1)
00206                     return Qt::AlignRight;
00207 
00208                 goto dontalign;
00209               }
00210             }
00211           }
00212 
00213         } else if ((!isColumnMergingEnabled() || m_columnMerges.isEmpty()) && index.column() == CodeCompletionModel::Scope) {
00214           return Qt::AlignRight;
00215         }
00216 
00217         dontalign:
00218         break;
00219     }
00220 
00221     // Merge text for column merging
00222     if (role == Qt::DisplayRole && m_columnMerges.count() && isColumnMergingEnabled()) {
00223       QString text;
00224       foreach (int column, m_columnMerges[index.column()]) {
00225         QModelIndex sourceIndex = mapToSource(createIndex(index.row(), column, index.internalPointer()));
00226         text.append(sourceIndex.data(role).toString());
00227       }
00228 
00229       return text;
00230     }
00231 
00232     if(role == CodeCompletionModel::HighlightingMethod)
00233     {
00234       //Return that we are doing custom-highlighting of one of the sub-strings does it. Unfortunately internal highlighting does not work for the other substrings.
00235       foreach (int column, m_columnMerges[index.column()]) {
00236     QModelIndex sourceIndex = mapToSource(createIndex(index.row(), column, index.internalPointer()));
00237     QVariant method = sourceIndex.data(CodeCompletionModel::HighlightingMethod);
00238     if( method.type() == QVariant::Int && method.toInt() ==  CodeCompletionModel::CustomHighlighting)
00239       return QVariant(CodeCompletionModel::CustomHighlighting);
00240       }
00241       return QVariant();
00242     }
00243     if(role == CodeCompletionModel::CustomHighlight)
00244     {
00245       //Merge custom highlighting if multiple columns were merged
00246       QStringList strings;
00247 
00248       //Collect strings
00249       foreach (int column, m_columnMerges[index.column()])
00250           strings << mapToSource(createIndex(index.row(), column, index.internalPointer())).data(Qt::DisplayRole).toString();
00251 
00252       QList<QVariantList> highlights;
00253 
00254       //Collect custom-highlightings
00255       foreach (int column, m_columnMerges[index.column()])
00256           highlights << mapToSource(createIndex(index.row(), column, index.internalPointer())).data(CodeCompletionModel::CustomHighlight).toList();
00257 
00258       return mergeCustomHighlighting( strings, highlights, 0 );
00259     }
00260 
00261     QVariant v = mapToSource(index).data(role);
00262     if( v.isValid() )
00263       return v;
00264     else
00265       return ExpandingWidgetModel::data(index, role);
00266   }
00267 
00268   //Returns a nonzero group if this index is the head of a group(A Label in the list)
00269   Group* g = groupForIndex(index);
00270   
00271   if (g && (!g->isEmpty)) {
00272     switch (role) {
00273       case Qt::DisplayRole:
00274           //We return the group-header for all columns, ExpandingDelegate will paint them properly over the whole space
00275           return ' ' + g->title;
00276         break;
00277 
00278       case Qt::FontRole:
00279         if (!index.column()) {
00280           QFont f = view()->renderer()->config()->font();
00281           f.setBold(true);
00282           return f;
00283         }
00284         break;
00285 
00286       case Qt::ForegroundRole:
00287         return KApplication::kApplication()->palette().toolTipText().color();
00288       case Qt::BackgroundRole:
00289         return KApplication::kApplication()->palette().toolTipBase().color();
00290     }
00291   }
00292 
00293   return QVariant();
00294 }
00295 
00296 int KateCompletionModel::contextMatchQuality(const QModelIndex& index) const {
00297   if(!index.isValid())
00298     return 0;
00299   Group* g = groupOfParent(index);
00300   if(!g || g->filtered.size() < index.row())
00301     return 0;
00302   
00303   return contextMatchQuality(g->filtered[index.row()].sourceRow());
00304 }
00305 
00306 int KateCompletionModel::contextMatchQuality(const ModelRow& source) const {
00307   QModelIndex realIndex = source.second;
00308 
00309   int bestMatch = -1;
00310   //Iterate through all argument-hints and find the best match-quality
00311   foreach( const Item& item, m_argumentHints->filtered )
00312   {
00313     const ModelRow& row(item.sourceRow());
00314     if( realIndex.model() != row.first )
00315       continue; //We can only match within the same source-model
00316 
00317     QModelIndex hintIndex = row.second;
00318 
00319     QVariant depth = hintIndex.data(CodeCompletionModel::ArgumentHintDepth);
00320     if( !depth.isValid() || depth.type() != QVariant::Int || depth.toInt() != 1 )
00321       continue; //Only match completion-items to argument-hints of depth 1(the ones the item will be given to as argument)
00322 
00323     hintIndex.data(CodeCompletionModel::SetMatchContext);
00324 
00325     QVariant matchQuality = realIndex.data(CodeCompletionModel::MatchQuality);
00326     if( matchQuality.isValid() && matchQuality.type() == QVariant::Int ) {
00327       int m = matchQuality.toInt();
00328       if( m > bestMatch )
00329         bestMatch = m;
00330     }
00331   }
00332   
00333   if(m_argumentHints->filtered.isEmpty()) {
00334     QVariant matchQuality = realIndex.data(CodeCompletionModel::MatchQuality);
00335     if( matchQuality.isValid() && matchQuality.type() == QVariant::Int ) {
00336       int m = matchQuality.toInt();
00337       if( m > bestMatch )
00338         bestMatch = m;
00339     }
00340   }
00341 
00342   return bestMatch;
00343 }
00344 
00345 Qt::ItemFlags KateCompletionModel::flags( const QModelIndex & index ) const
00346 {
00347   if (!hasCompletionModel() || !index.isValid())
00348     return 0;
00349 
00350   if (!hasGroups() || groupOfParent(index))
00351     return Qt::ItemIsSelectable | Qt::ItemIsEnabled;
00352 
00353   return Qt::ItemIsEnabled;
00354 }
00355 
00356 KateCompletionWidget* KateCompletionModel::widget() const {
00357   return static_cast<KateCompletionWidget*>(QObject::parent());
00358 }
00359 
00360 KateView * KateCompletionModel::view( ) const
00361 {
00362   return widget()->view();
00363 }
00364 
00365 void KateCompletionModel::setMatchCaseSensitivity( Qt::CaseSensitivity cs )
00366 {
00367   m_matchCaseSensitivity = cs;
00368 }
00369 
00370 int KateCompletionModel::columnCount( const QModelIndex& ) const
00371 {
00372   return isColumnMergingEnabled() && !m_columnMerges.isEmpty() ? m_columnMerges.count() : KTextEditor::CodeCompletionModel::ColumnCount;
00373 }
00374 
00375 KateCompletionModel::ModelRow KateCompletionModel::modelRowPair(const QModelIndex& index) const
00376 {
00377   return qMakePair(static_cast<CodeCompletionModel*>(const_cast<QAbstractItemModel*>(index.model())), index);
00378 }
00379 
00380 bool KateCompletionModel::hasChildren( const QModelIndex & parent ) const
00381 {
00382   if (!hasCompletionModel())
00383     return false;
00384 
00385   if (!parent.isValid()) {
00386     if (hasGroups())
00387       return true;
00388 
00389     return !m_ungrouped->filtered.isEmpty();
00390   }
00391 
00392   if (parent.column() != 0)
00393     return false;
00394 
00395   if (!hasGroups())
00396     return false;
00397 
00398   if (Group* g = groupForIndex(parent))
00399     return !g->filtered.isEmpty();
00400 
00401   return false;
00402 }
00403 
00404 QModelIndex KateCompletionModel::index( int row, int column, const QModelIndex & parent ) const
00405 {
00406   if (row < 0 || column < 0 || column >= columnCount(QModelIndex()))
00407     return QModelIndex();
00408 
00409   if (parent.isValid() || !hasGroups()) {
00410     if (parent.isValid() && parent.column() != 0)
00411       return QModelIndex();
00412 
00413     Group* g = groupForIndex(parent);
00414 
00415     if (!g)
00416       return QModelIndex();
00417 
00418     if (row >= g->filtered.count()) {
00419       //kWarning() << "Invalid index requested: row " << row << " beyond indivdual range in group " << g;
00420       return QModelIndex();
00421     }
00422 
00423     //kDebug( 13035 ) << "Returning index for child " << row << " of group " << g;
00424     return createIndex(row, column, g);
00425   }
00426 
00427   if (row >= m_rowTable.count()) {
00428     //kWarning() << "Invalid index requested: row " << row << " beyond group range.";
00429     return QModelIndex();
00430   }
00431 
00432   //kDebug( 13035 ) << "Returning index for group " << m_rowTable[row];
00433   return createIndex(row, column, 0);
00434 }
00435 
00436 /*QModelIndex KateCompletionModel::sibling( int row, int column, const QModelIndex & index ) const
00437 {
00438   if (row < 0 || column < 0 || column >= columnCount(QModelIndex()))
00439     return QModelIndex();
00440 
00441   if (!index.isValid()) {
00442   }
00443 
00444   if (Group* g = groupOfParent(index)) {
00445     if (row >= g->filtered.count())
00446       return QModelIndex();
00447 
00448     return createIndex(row, column, g);
00449   }
00450 
00451   if (hasGroups())
00452     return QModelIndex();
00453 
00454   if (row >= m_ungrouped->filtered.count())
00455     return QModelIndex();
00456 
00457   return createIndex(row, column, m_ungrouped);
00458 }*/
00459 
00460 bool KateCompletionModel::hasIndex( int row, int column, const QModelIndex & parent ) const
00461 {
00462   if (row < 0 || column < 0 || column >= columnCount(QModelIndex()))
00463     return false;
00464 
00465   if (parent.isValid() || !hasGroups()) {
00466     if (parent.isValid() && parent.column() != 0)
00467       return false;
00468 
00469     Group* g = groupForIndex(parent);
00470 
00471     if (row >= g->filtered.count())
00472       return false;
00473 
00474     return true;
00475   }
00476 
00477   if (row >= m_rowTable.count())
00478     return false;
00479 
00480   return true;
00481 }
00482 
00483 QModelIndex KateCompletionModel::indexForRow( Group * g, int row ) const
00484 {
00485   if (row < 0 || row >= g->filtered.count())
00486     return QModelIndex();
00487 
00488   return createIndex(row, 0, g);
00489 }
00490 
00491 QModelIndex KateCompletionModel::indexForGroup( Group * g ) const
00492 {
00493   if (!hasGroups())
00494     return QModelIndex();
00495 
00496   int row = m_rowTable.indexOf(g);
00497 
00498   if (row == -1)
00499     return QModelIndex();
00500 
00501   return createIndex(row, 0, 0);
00502 }
00503 
00504 void KateCompletionModel::clearGroups( bool shouldReset )
00505 {
00506   clearExpanding();
00507   m_ungrouped->clear();
00508   m_argumentHints->clear();
00509   m_bestMatches->clear();
00510 
00511   // Don't bother trying to work out where it is
00512   m_rowTable.removeAll(m_ungrouped);
00513   m_emptyGroups.removeAll(m_ungrouped);
00514 
00515   m_rowTable.removeAll(m_argumentHints);
00516   m_emptyGroups.removeAll(m_argumentHints);
00517 
00518   m_rowTable.removeAll(m_bestMatches);
00519   m_emptyGroups.removeAll(m_bestMatches);
00520 
00521   qDeleteAll(m_rowTable);
00522   qDeleteAll(m_emptyGroups);
00523   m_rowTable.clear();
00524   m_emptyGroups.clear();
00525   m_groupHash.clear();
00526   m_customGroupHash.clear();
00527 
00528   m_emptyGroups.append(m_ungrouped);
00529   m_groupHash.insert(0, m_ungrouped);
00530 
00531   m_emptyGroups.append(m_argumentHints);
00532   m_groupHash.insert(-1, m_argumentHints);
00533 
00534   m_emptyGroups.append(m_bestMatches);
00535   m_groupHash.insert(BestMatchesProperty, m_bestMatches);
00536 
00537   if(shouldReset)
00538     reset();
00539 }
00540 
00541 QSet<KateCompletionModel::Group*> KateCompletionModel::createItems(const HierarchicalModelHandler& _handler, const QModelIndex& i, bool notifyModel) {
00542   HierarchicalModelHandler handler(_handler);
00543   QSet<Group*> ret;
00544 
00545   if( handler.model()->rowCount(i) == 0 ) {
00546     //Leaf node, create an item
00547     ret.insert( createItem(handler, i, notifyModel) );
00548   } else {
00549     //Non-leaf node, take the role from the node, and recurse to the sub-nodes
00550     handler.takeRole(i);
00551     for(int a = 0; a < handler.model()->rowCount(i); a++)
00552       ret += createItems(handler, i.child(a, 0), notifyModel);
00553   }
00554 
00555   return ret;
00556 }
00557 
00558 QSet<KateCompletionModel::Group*> KateCompletionModel::deleteItems(const QModelIndex& i) {
00559   QSet<Group*> ret;
00560 
00561   if( i.model()->rowCount(i) == 0 ) {
00562     //Leaf node, delete the item
00563     Group* g = groupForIndex(mapFromSource(i));
00564     ret.insert(g);
00565     g->removeItem(ModelRow(const_cast<CodeCompletionModel*>(static_cast<const CodeCompletionModel*>(i.model())), i));
00566   } else {
00567     //Non-leaf node
00568     for(int a = 0; a < i.model()->rowCount(i); a++)
00569       ret += deleteItems(i.child(a, 0));
00570   }
00571 
00572   return ret;
00573 }
00574 
00575 void KateCompletionModel::createGroups()
00576 {
00577   //After clearing the model, it has to be reset, else we will be in an invalid state while inserting
00578   //new groups.
00579   clearGroups(true);
00580 
00581   bool has_groups=false;
00582   foreach (CodeCompletionModel* sourceModel, m_completionModels) {
00583     has_groups|=sourceModel->hasGroups();
00584     for (int i = 0; i < sourceModel->rowCount(); ++i)
00585       createItems(HierarchicalModelHandler(sourceModel), sourceModel->index(i, 0));
00586   }
00587   m_hasGroups=has_groups;
00588 
00589   //debugStats();
00590 
00591   foreach (Group* g, m_rowTable)
00592     hideOrShowGroup(g);
00593 
00594   foreach (Group* g, m_emptyGroups)
00595     hideOrShowGroup(g);
00596 
00597   updateBestMatches();
00598   
00599   reset();
00600 
00601   emit contentGeometryChanged();
00602 }
00603 
00604 KateCompletionModel::Group* KateCompletionModel::createItem(const HierarchicalModelHandler& handler, const QModelIndex& sourceIndex, bool notifyModel)
00605 {
00606   //QModelIndex sourceIndex = sourceModel->index(row, CodeCompletionModel::Name, QModelIndex());
00607 
00608   int completionFlags = handler.getData(CodeCompletionModel::CompletionRole, sourceIndex).toInt();
00609 
00610   //Scope is expensive, should not be used with big models
00611   QString scopeIfNeeded = (groupingMethod() & Scope) ? sourceIndex.sibling(sourceIndex.row(), CodeCompletionModel::Scope).data(Qt::DisplayRole).toString() : QString();
00612 
00613   int argumentHintDepth = handler.getData(CodeCompletionModel::ArgumentHintDepth, sourceIndex).toInt();
00614 
00615   Group* g;
00616   if( argumentHintDepth ) {
00617     g = m_argumentHints;
00618   } else{
00619     QString customGroup = handler.customGroup();
00620     if(!customGroup.isNull() && m_hasGroups) {
00621       if(m_customGroupHash.contains(customGroup)) {
00622         g = m_customGroupHash[customGroup];
00623       }else{
00624         g = new Group(this);
00625         g->title = customGroup;
00626         g->customSortingKey = handler.customGroupingKey();
00627         m_emptyGroups.append(g);
00628         m_customGroupHash.insert(customGroup, g);
00629       }
00630     }else{
00631       g = fetchGroup(completionFlags, scopeIfNeeded, handler.hasHierarchicalRoles());
00632     }
00633   }
00634 
00635   Item item = Item(g != m_argumentHints, this, handler, ModelRow(handler.model(), sourceIndex));
00636 
00637   if(g != m_argumentHints)
00638     item.match();
00639 
00640   g->addItem(item, notifyModel);
00641 
00642   return g;
00643 }
00644 
00645 void KateCompletionModel::slotRowsInserted( const QModelIndex & parent, int start, int end )
00646 {
00647   QSet<Group*> affectedGroups;
00648 
00649   HierarchicalModelHandler handler(static_cast<CodeCompletionModel*>(sender()));
00650   if(parent.isValid())
00651     handler.collectRoles(parent);
00652 
00653 
00654   for (int i = start; i <= end; ++i)
00655     affectedGroups += createItems(handler, parent.isValid() ? parent.child(i, 0) :  handler.model()->index(i, 0), true);
00656 
00657   foreach (Group* g, affectedGroups)
00658       hideOrShowGroup(g);
00659 
00660     emit contentGeometryChanged();
00661 }
00662 
00663 void KateCompletionModel::slotRowsRemoved( const QModelIndex & parent, int start, int end )
00664 {
00665   CodeCompletionModel* source = static_cast<CodeCompletionModel*>(sender());
00666 
00667   QSet<Group*> affectedGroups;
00668 
00669   for (int i = start; i <= end; ++i) {
00670     QModelIndex index = parent.isValid() ? parent.child(i, 0) :  source->index(i, 0);
00671 
00672     affectedGroups += deleteItems(index);
00673   }
00674 
00675   foreach (Group* g, affectedGroups)
00676     hideOrShowGroup(g);
00677 
00678   emit contentGeometryChanged();
00679 }
00680 
00681 KateCompletionModel::Group* KateCompletionModel::fetchGroup( int attribute, const QString& scope, bool forceGrouping )
00682 {
00683   Q_UNUSED(forceGrouping);
00684 
00686   if (!hasGroups())
00687     return m_ungrouped;
00688 
00689   int groupingAttribute = groupingAttributes(attribute);
00690   //kDebug( 13035 ) << attribute << " " << groupingAttribute;
00691 
00692   if (m_groupHash.contains(groupingAttribute)) {
00693     if (groupingMethod() & Scope) {
00694       for (QHash<int, Group*>::ConstIterator it = m_groupHash.constFind(groupingAttribute); it != m_groupHash.constEnd() && it.key() == groupingAttribute; ++it)
00695         if (it.value()->scope == scope)
00696           return it.value();
00697     } else {
00698       return m_groupHash.value(groupingAttribute);
00699     }
00700   }
00701   Group* ret = new Group(this);
00702 
00703   ret->attribute = attribute;
00704   ret->scope = scope;
00705 
00706   QString st, at, it;
00707 
00708   if (groupingMethod() & ScopeType) {
00709     if (attribute & KTextEditor::CodeCompletionModel::GlobalScope)
00710       st = "Global";
00711     else if (attribute & KTextEditor::CodeCompletionModel::NamespaceScope)
00712       st = "Namespace";
00713     else if (attribute & KTextEditor::CodeCompletionModel::LocalScope)
00714       st = "Local";
00715 
00716     ret->title = st;
00717   }
00718 
00719   if (groupingMethod() & Scope) {
00720     if (!ret->title.isEmpty())
00721       ret->title.append(" ");
00722 
00723     ret->title.append(scope);
00724   }
00725 
00726   if (groupingMethod() & AccessType) {
00727     if (attribute & KTextEditor::CodeCompletionModel::Public)
00728       at = "Public";
00729     else if (attribute & KTextEditor::CodeCompletionModel::Protected)
00730       at = "Protected";
00731     else if (attribute & KTextEditor::CodeCompletionModel::Private)
00732       at = "Private";
00733 
00734     if (accessIncludeStatic() && attribute & KTextEditor::CodeCompletionModel::Static)
00735       at.append(" Static");
00736 
00737     if (accessIncludeConst() && attribute & KTextEditor::CodeCompletionModel::Const)
00738       at.append(" Const");
00739 
00740     if( !at.isEmpty() ) {
00741       if (!ret->title.isEmpty())
00742         ret->title.append(", ");
00743 
00744       ret->title.append(at);
00745     }
00746   }
00747 
00748   if (groupingMethod() & ItemType) {
00749     if (attribute & CodeCompletionModel::Namespace)
00750       it = i18n("Namespaces");
00751     else if (attribute & CodeCompletionModel::Class)
00752       it = i18n("Classes");
00753     else if (attribute & CodeCompletionModel::Struct)
00754       it = i18n("Structs");
00755     else if (attribute & CodeCompletionModel::Union)
00756       it = i18n("Unions");
00757     else if (attribute & CodeCompletionModel::Function)
00758       it = i18n("Functions");
00759     else if (attribute & CodeCompletionModel::Variable)
00760       it = i18n("Variables");
00761     else if (attribute & CodeCompletionModel::Enum)
00762       it = i18n("Enumerations");
00763 
00764     if( !it.isEmpty() ) {
00765       if (!ret->title.isEmpty())
00766         ret->title.append(" ");
00767 
00768       ret->title.append(it);
00769     }
00770   }
00771 
00772   m_emptyGroups.append(ret);
00773   m_groupHash.insert(groupingAttribute, ret);
00774 
00775   return ret;
00776 }
00777 
00778 bool KateCompletionModel::hasGroups( ) const
00779 {
00780   //kDebug( 13035 ) << "m_groupHash.size()"<<m_groupHash.size();
00781   //kDebug( 13035 ) << "m_rowTable.count()"<<m_rowTable.count();
00782   // We cannot decide whether there is groups easily. The problem: The code-model can
00783   // be populated with a delay from within a background-thread.
00784   // Proper solution: Ask all attached code-models(Through a new interface) whether they want to use grouping,
00785   // and if at least one wants to, return true, else return false.
00786   return m_groupingEnabled && m_hasGroups;
00787 }
00788 
00789 KateCompletionModel::Group* KateCompletionModel::groupForIndex( const QModelIndex & index ) const
00790 {
00791   if (!index.isValid()) {
00792     if (!hasGroups())
00793       return m_ungrouped;
00794     else
00795       return 0L;
00796   }
00797 
00798   if (groupOfParent(index))
00799     return 0L;
00800 
00801   if (index.row() < 0 || index.row() >= m_rowTable.count())
00802     return m_ungrouped;
00803 
00804   return m_rowTable[index.row()];
00805 }
00806 
00807 /*QMap< int, QVariant > KateCompletionModel::itemData( const QModelIndex & index ) const
00808 {
00809   if (!hasGroups() || groupOfParent(index)) {
00810     QModelIndex index = mapToSource(index);
00811     if (index.isValid())
00812       return index.model()->itemData(index);
00813   }
00814 
00815   return QAbstractItemModel::itemData(index);
00816 }*/
00817 
00818 QModelIndex KateCompletionModel::parent( const QModelIndex & index ) const
00819 {
00820   if (!index.isValid())
00821     return QModelIndex();
00822 
00823   if (Group* g = groupOfParent(index)) {
00824     if (!hasGroups()) {
00825       Q_ASSERT(g == m_ungrouped);
00826       return QModelIndex();
00827     }
00828 
00829     int row = m_rowTable.indexOf(g);
00830 
00831     if (row == -1) {
00832       kWarning() << "Couldn't find parent for index" << index;
00833       return QModelIndex();
00834     }
00835 
00836     return createIndex(row, 0, 0);
00837   }
00838 
00839   return QModelIndex();
00840 }
00841 
00842 int KateCompletionModel::rowCount( const QModelIndex & parent ) const
00843 {
00844   if (!parent.isValid()) {
00845     if (hasGroups()) {
00846       //kDebug( 13035 ) << "Returning row count for toplevel " << m_rowTable.count();
00847       return m_rowTable.count();
00848     } else {
00849       //kDebug( 13035 ) << "Returning ungrouped row count for toplevel " << m_ungrouped->filtered.count();
00850       return m_ungrouped->filtered.count();
00851     }
00852   }
00853 
00854   Group* g = groupForIndex(parent);
00855 
00856   // This is not an error, seems you don't have to check hasChildren()
00857   if (!g)
00858     return 0;
00859 
00860   //kDebug( 13035 ) << "Returning row count for group " << g << " as " << g->filtered.count();
00861   return g->filtered.count();
00862 }
00863 
00864 void KateCompletionModel::sort( int column, Qt::SortOrder order )
00865 {
00866   Q_UNUSED(column)
00867   Q_UNUSED(order)
00868 }
00869 
00870 QModelIndex KateCompletionModel::mapToSource( const QModelIndex & proxyIndex ) const
00871 {
00872   if (!proxyIndex.isValid())
00873     return QModelIndex();
00874 
00875   if (Group* g = groupOfParent(proxyIndex)) {
00876     if( proxyIndex.row() >= 0 && proxyIndex.row() < g->filtered.count() ) {
00877       ModelRow source = g->filtered[proxyIndex.row()].sourceRow();
00878       return source.second.sibling(source.second.row(), proxyIndex.column());
00879     }else{
00880       kDebug("Invalid proxy-index");
00881     }
00882   }
00883 
00884   return QModelIndex();
00885 }
00886 
00887 QModelIndex KateCompletionModel::mapFromSource( const QModelIndex & sourceIndex ) const
00888 {
00889   if (!sourceIndex.isValid())
00890     return QModelIndex();
00891 
00892   if (!hasGroups())
00893     return index(m_ungrouped->rowOf(modelRowPair(sourceIndex)), sourceIndex.column(), QModelIndex());
00894 
00895   foreach (Group* g, m_rowTable) {
00896     int row = g->rowOf(modelRowPair(sourceIndex));
00897     if (row != -1)
00898       return index(row, sourceIndex.column(), QModelIndex());
00899   }
00900 
00901   // Copied from above
00902   foreach (Group* g, m_emptyGroups) {
00903     int row = g->rowOf(modelRowPair(sourceIndex));
00904     if (row != -1)
00905       return index(row, sourceIndex.column(), QModelIndex());
00906   }
00907 
00908   return QModelIndex();
00909 }
00910 
00911 void KateCompletionModel::setCurrentCompletion( KTextEditor::CodeCompletionModel* model, const QString & completion )
00912 {
00913   if (m_currentMatch[model] == completion)
00914     return;
00915 
00916   if (!hasCompletionModel()) {
00917     m_currentMatch[model] = completion;
00918     return;
00919   }
00920 
00921   changeTypes changeType = Change;
00922 
00923   if (m_currentMatch[model].length() > completion.length() && m_currentMatch[model].startsWith(completion, m_matchCaseSensitivity)) {
00924     // Filter has been broadened
00925     changeType = Broaden;
00926 
00927   } else if (m_currentMatch[model].length() < completion.length() && completion.startsWith(m_currentMatch[model], m_matchCaseSensitivity)) {
00928     // Filter has been narrowed
00929     changeType = Narrow;
00930   }
00931 
00932   kDebug( 13035 ) << model << "Old match: " << m_currentMatch[model] << ", new: " << completion << ", type: " << changeType;
00933 
00934   m_currentMatch[model] = completion;
00935 
00936   bool needsReset = false;
00937   
00938   if (!hasGroups()) {
00939     needsReset |= changeCompletions(m_ungrouped, changeType);
00940   } else {
00941     foreach (Group* g, m_rowTable) {
00942       if(g != m_argumentHints)
00943         needsReset |= changeCompletions(g, changeType);
00944     }
00945     foreach (Group* g, m_emptyGroups) {
00946       if(g != m_argumentHints)
00947         needsReset |= changeCompletions(g, changeType);
00948     }
00949 
00950     updateBestMatches();
00951   }
00952 
00953   if(needsReset)
00954     reset();
00955 
00956   clearExpanding(); //We need to do this, or be aware of expanding-widgets while filtering.
00957   emit contentGeometryChanged();
00958 }
00959 
00960 bool KateCompletionModel::changeCompletions( Group * g, changeTypes changeType )
00961 {
00962   bool notifyModel = true;
00963   if(changeType != Narrow) {
00964     notifyModel = false;
00965     g->filtered = g->prefilter;
00966     //In the "Broaden" or "Change" case, just re-filter everything,
00967     //and don't notify the model. The model is notified afterwards through a reset().
00968   }
00969   //This code determines what of the filtered items still fit, and computes the ranges that were removed, giving
00970   //them to beginRemoveRows(..) in batches
00971   
00972   QList <KateCompletionModel::Item > newFiltered;
00973   int deleteUntil = -1; //In each state, the range [currentRow+1, deleteUntil] needs to be deleted
00974   for(int currentRow = g->filtered.count()-1; currentRow >= 0; --currentRow) {
00975     if(g->filtered[currentRow].match()) {
00976       //This row does not need to be deleted, which means that currentRow+1 to deleteUntil need to be deleted now
00977       if(deleteUntil != -1 && notifyModel) {
00978         beginRemoveRows(indexForGroup(g), currentRow+1, deleteUntil);
00979         endRemoveRows();
00980       }
00981       deleteUntil = -1;
00982       
00983       newFiltered.prepend(g->filtered[currentRow]);
00984     }else{
00985       if(deleteUntil == -1)
00986         deleteUntil = currentRow; //Mark that this row needs to be deleted
00987     }
00988   }
00989   
00990   if(deleteUntil != -1) {
00991     beginRemoveRows(indexForGroup(g), 0, deleteUntil);
00992     endRemoveRows();
00993   }
00994   
00995   g->filtered = newFiltered;
00996   hideOrShowGroup(g, notifyModel);
00997   return !notifyModel;
00998 }
00999 
01000 int KateCompletionModel::Group::orderNumber() const {
01001     if( this == model->m_ungrouped )
01002       return 700;
01003 
01004     if(customSortingKey != -1)
01005       return customSortingKey;
01006     
01007     if( attribute & BestMatchesProperty )
01008       return 1;
01009     
01010     if (attribute & KTextEditor::CodeCompletionModel::LocalScope)
01011       return 100;
01012     else if (attribute & KTextEditor::CodeCompletionModel::Public)
01013       return 200;
01014     else if (attribute & KTextEditor::CodeCompletionModel::Protected)
01015       return 300;
01016     else if (attribute & KTextEditor::CodeCompletionModel::Private)
01017       return 400;
01018     else if (attribute & KTextEditor::CodeCompletionModel::NamespaceScope)
01019       return 500;
01020     else if (attribute & KTextEditor::CodeCompletionModel::GlobalScope)
01021       return 600;
01022 
01023 
01024     return 700;
01025 }
01026 
01027 bool KateCompletionModel::Group::orderBefore(Group* other) const {
01028     return orderNumber() < other->orderNumber();
01029 }
01030 
01031 void KateCompletionModel::hideOrShowGroup(Group* g, bool notifyModel)
01032 {
01033   if( g == m_argumentHints ) {
01034     emit argumentHintsChanged();
01035     m_updateBestMatchesTimer->start(200); //We have new argument-hints, so we have new best matches
01036     return; //Never show argument-hints in the normal completion-list
01037   }
01038 
01039   if (!g->isEmpty) {
01040     if (g->filtered.isEmpty()) {
01041       // Move to empty group list
01042       g->isEmpty = true;
01043       int row = m_rowTable.indexOf(g);
01044       if (row != -1) {
01045         if (hasGroups() && notifyModel)
01046           beginRemoveRows(QModelIndex(), row, row);
01047         m_rowTable.removeAt(row);
01048         if (hasGroups() && notifyModel)
01049           endRemoveRows();
01050         m_emptyGroups.append(g);
01051       } else {
01052         kWarning() << "Group " << g << " not found in row table!!";
01053       }
01054     }
01055 
01056   } else {
01057 
01058     if (!g->filtered.isEmpty()) {
01059       // Move off empty group list
01060       g->isEmpty = false;
01061 
01062       int row = 0; //Find row where to insert
01063       for( int a = 0; a < m_rowTable.count(); a++ ) {
01064         if( g->orderBefore(m_rowTable[a]) ) {
01065         row = a;
01066         break;
01067         }
01068         row = a+1;
01069       }
01070 
01071       if(notifyModel) {
01072         if (hasGroups())
01073           beginInsertRows(QModelIndex(), row, row);
01074         else
01075           beginInsertRows(QModelIndex(), 0, g->filtered.count());
01076       }
01077       m_rowTable.insert(row, g);
01078       if(notifyModel)
01079         endInsertRows();
01080       m_emptyGroups.removeAll(g);
01081     }
01082   }
01083 }
01084 
01085 bool KateCompletionModel::indexIsItem( const QModelIndex & index ) const
01086 {
01087   if (!hasGroups())
01088     return true;
01089 
01090   if (groupOfParent(index))
01091     return true;
01092 
01093   return false;
01094 }
01095 
01096 void KateCompletionModel::slotModelReset()
01097 {
01098   createGroups();
01099 
01100   //debugStats();
01101 }
01102 
01103 void KateCompletionModel::debugStats()
01104 {
01105   if (!hasGroups())
01106     kDebug( 13035 ) << "Model groupless, " << m_ungrouped->filtered.count() << " items.";
01107   else {
01108     kDebug( 13035 ) << "Model grouped (" << m_rowTable.count() << " groups):";
01109     foreach (Group* g, m_rowTable)
01110       kDebug( 13035 ) << "Group" << g << "count" << g->filtered.count();
01111   }
01112 }
01113 
01114 bool KateCompletionModel::hasCompletionModel( ) const
01115 {
01116   return !m_completionModels.isEmpty();
01117 }
01118 
01119 void KateCompletionModel::setFilteringEnabled( bool enable )
01120 {
01121   if (m_filteringEnabled != enable)
01122     m_filteringEnabled = enable;
01123 }
01124 
01125 void KateCompletionModel::setSortingEnabled( bool enable )
01126 {
01127   if (m_sortingEnabled != enable) {
01128     m_sortingEnabled = enable;
01129     resort();
01130   }
01131 }
01132 
01133 void KateCompletionModel::setGroupingEnabled(bool enable)
01134 {
01135   if (m_groupingEnabled != enable)
01136     m_groupingEnabled = enable;
01137 }
01138 
01139 void KateCompletionModel::setColumnMergingEnabled(bool enable)
01140 {
01141   if (m_columnMergingEnabled != enable)
01142     m_columnMergingEnabled = enable;
01143 }
01144 
01145 bool KateCompletionModel::isColumnMergingEnabled( ) const
01146 {
01147   return m_columnMergingEnabled;
01148 }
01149 
01150 bool KateCompletionModel::isGroupingEnabled( ) const
01151 {
01152   return m_groupingEnabled;
01153 }
01154 
01155 bool KateCompletionModel::isFilteringEnabled( ) const
01156 {
01157   return m_filteringEnabled;
01158 }
01159 
01160 bool KateCompletionModel::isSortingEnabled( ) const
01161 {
01162   return m_sortingEnabled;
01163 }
01164 
01165 QString KateCompletionModel::columnName( int column )
01166 {
01167   switch (column) {
01168     case KTextEditor::CodeCompletionModel::Prefix:
01169       return i18n("Prefix");
01170     case KTextEditor::CodeCompletionModel::Icon:
01171       return i18n("Icon");
01172     case KTextEditor::CodeCompletionModel::Scope:
01173       return i18n("Scope");
01174     case KTextEditor::CodeCompletionModel::Name:
01175       return i18n("Name");
01176     case KTextEditor::CodeCompletionModel::Arguments:
01177       return i18n("Arguments");
01178     case KTextEditor::CodeCompletionModel::Postfix:
01179       return i18n("Postfix");
01180   }
01181 
01182   return QString();
01183 }
01184 
01185 const QList< QList < int > > & KateCompletionModel::columnMerges( ) const
01186 {
01187   return m_columnMerges;
01188 }
01189 
01190 void KateCompletionModel::setColumnMerges( const QList< QList < int > > & columnMerges )
01191 {
01192   m_columnMerges = columnMerges;
01193   reset();
01194 }
01195 
01196 int KateCompletionModel::translateColumn( int sourceColumn ) const
01197 {
01198   if (m_columnMerges.isEmpty())
01199     return sourceColumn;
01200 
01201   /* Debugging - dump column merge list
01202 
01203   QString columnMerge;
01204   foreach (const QList<int>& list, m_columnMerges) {
01205     columnMerge += '[';
01206     foreach (int column, list) {
01207       columnMerge += QString::number(column) + " ";
01208     }
01209     columnMerge += "] ";
01210   }
01211 
01212   kDebug( 13035 ) << k_funcinfo << columnMerge;*/
01213 
01214   int c = 0;
01215   foreach (const QList<int>& list, m_columnMerges) {
01216     foreach (int column, list) {
01217       if (column == sourceColumn)
01218         return c;
01219     }
01220     c++;
01221   }
01222   return -1;
01223 }
01224 
01225 int KateCompletionModel::groupingAttributes( int attribute ) const
01226 {
01227   int ret = 0;
01228 
01229   if (m_groupingMethod & ScopeType) {
01230     if (countBits(attribute & ScopeTypeMask) > 1)
01231       kWarning() << "Invalid completion model metadata: more than one scope type modifier provided.";
01232 
01233     if (attribute & KTextEditor::CodeCompletionModel::GlobalScope)
01234       ret |= KTextEditor::CodeCompletionModel::GlobalScope;
01235     else if (attribute & KTextEditor::CodeCompletionModel::NamespaceScope)
01236       ret |= KTextEditor::CodeCompletionModel::NamespaceScope;
01237     else if (attribute & KTextEditor::CodeCompletionModel::LocalScope)
01238       ret |= KTextEditor::CodeCompletionModel::LocalScope;
01239   }
01240 
01241   if (m_groupingMethod & AccessType) {
01242     if (countBits(attribute & AccessTypeMask) > 1)
01243       kWarning() << "Invalid completion model metadata: more than one access type modifier provided.";
01244 
01245     if (attribute & KTextEditor::CodeCompletionModel::Public)
01246       ret |= KTextEditor::CodeCompletionModel::Public;
01247     else if (attribute & KTextEditor::CodeCompletionModel::Protected)
01248       ret |= KTextEditor::CodeCompletionModel::Protected;
01249     else if (attribute & KTextEditor::CodeCompletionModel::Private)
01250       ret |= KTextEditor::CodeCompletionModel::Private;
01251 
01252     if (accessIncludeStatic() && attribute & KTextEditor::CodeCompletionModel::Static)
01253       ret |= KTextEditor::CodeCompletionModel::Static;
01254 
01255     if (accessIncludeConst() && attribute & KTextEditor::CodeCompletionModel::Const)
01256       ret |= KTextEditor::CodeCompletionModel::Const;
01257   }
01258 
01259   if (m_groupingMethod & ItemType) {
01260     if (countBits(attribute & ItemTypeMask) > 1)
01261       kWarning() << "Invalid completion model metadata: more than one item type modifier provided.";
01262 
01263     if (attribute & KTextEditor::CodeCompletionModel::Namespace)
01264       ret |= KTextEditor::CodeCompletionModel::Namespace;
01265     else if (attribute & KTextEditor::CodeCompletionModel::Class)
01266       ret |= KTextEditor::CodeCompletionModel::Class;
01267     else if (attribute & KTextEditor::CodeCompletionModel::Struct)
01268       ret |= KTextEditor::CodeCompletionModel::Struct;
01269     else if (attribute & KTextEditor::CodeCompletionModel::Union)
01270       ret |= KTextEditor::CodeCompletionModel::Union;
01271     else if (attribute & KTextEditor::CodeCompletionModel::Function)
01272       ret |= KTextEditor::CodeCompletionModel::Function;
01273     else if (attribute & KTextEditor::CodeCompletionModel::Variable)
01274       ret |= KTextEditor::CodeCompletionModel::Variable;
01275     else if (attribute & KTextEditor::CodeCompletionModel::Enum)
01276       ret |= KTextEditor::CodeCompletionModel::Enum;
01277 
01278     /*
01279     if (itemIncludeTemplate() && attribute & KTextEditor::CodeCompletionModel::Template)
01280       ret |= KTextEditor::CodeCompletionModel::Template;*/
01281   }
01282 
01283   return ret;
01284 }
01285 
01286 void KateCompletionModel::setGroupingMethod( GroupingMethods m )
01287 {
01288   m_groupingMethod = m;
01289 
01290   createGroups();
01291 }
01292 
01293 bool KateCompletionModel::accessIncludeConst( ) const
01294 {
01295   return m_accessConst;
01296 }
01297 
01298 void KateCompletionModel::setAccessIncludeConst( bool include )
01299 {
01300   if (m_accessConst != include) {
01301     m_accessConst = include;
01302 
01303     if (groupingMethod() & AccessType)
01304       createGroups();
01305   }
01306 }
01307 
01308 bool KateCompletionModel::accessIncludeStatic( ) const
01309 {
01310   return m_accessStatic;
01311 }
01312 
01313 void KateCompletionModel::setAccessIncludeStatic( bool include )
01314 {
01315   if (m_accessStatic != include) {
01316     m_accessStatic = include;
01317 
01318     if (groupingMethod() & AccessType)
01319       createGroups();
01320   }
01321 }
01322 
01323 bool KateCompletionModel::accessIncludeSignalSlot( ) const
01324 {
01325   return m_accesSignalSlot;
01326 }
01327 
01328 void KateCompletionModel::setAccessIncludeSignalSlot( bool include )
01329 {
01330   if (m_accesSignalSlot != include) {
01331     m_accesSignalSlot = include;
01332 
01333     if (groupingMethod() & AccessType)
01334       createGroups();
01335   }
01336 }
01337 
01338 int KateCompletionModel::countBits( int value ) const
01339 {
01340   int count = 0;
01341   for (int i = 1; i; i <<= 1)
01342     if (i & value)
01343       count++;
01344 
01345   return count;
01346 }
01347 
01348 KateCompletionModel::GroupingMethods KateCompletionModel::groupingMethod( ) const
01349 {
01350   return m_groupingMethod;
01351 }
01352 
01353 bool KateCompletionModel::isSortingByInheritanceDepth() const {
01354   return m_isSortingByInheritance;
01355 }
01356 void KateCompletionModel::setSortingByInheritanceDepth(bool byInheritance) {
01357   m_isSortingByInheritance = byInheritance;
01358 }
01359 
01360 bool KateCompletionModel::isSortingAlphabetical( ) const
01361 {
01362   return m_sortingAlphabetical;
01363 }
01364 
01365 Qt::CaseSensitivity KateCompletionModel::sortingCaseSensitivity( ) const
01366 {
01367   return m_sortingCaseSensitivity;
01368 }
01369 
01370 KateCompletionModel::Item::Item( bool doInitialMatch, KateCompletionModel* m, const HierarchicalModelHandler& handler, ModelRow sr )
01371   : model(m)
01372   , m_sourceRow(sr)
01373   , matchCompletion(StartsWithMatch)
01374   , matchFilters(true)
01375   , m_haveExactMatch(false)
01376 {
01377   
01378   inheritanceDepth = handler.getData(CodeCompletionModel::InheritanceDepth, m_sourceRow.second).toInt();
01379 
01380   QModelIndex nameSibling = sr.second.sibling(sr.second.row(), CodeCompletionModel::Name);
01381   m_nameColumn = nameSibling.data(Qt::DisplayRole).toString();
01382 
01383   if(doInitialMatch) {
01384     filter();
01385     match();
01386   }
01387 }
01388 
01389 bool KateCompletionModel::Item::operator <( const Item & rhs ) const
01390 {
01391   int ret = 0;
01392 
01393     //kDebug( 13035 ) << c1 << " c/w " << c2 << " -> " << (model->isSortingReverse() ? ret > 0 : ret < 0) << " (" << ret << ")";
01394 
01395   if( model->isSortingByInheritanceDepth() )
01396     ret = inheritanceDepth - rhs.inheritanceDepth;
01397 
01398   if (ret == 0 && model->isSortingAlphabetical()) {
01399     if(!m_completionSortingName.isEmpty() && !rhs.m_completionSortingName.isEmpty())
01400       //Shortcut, plays a role in this tight loop
01401       ret = QString::compare(m_completionSortingName, rhs.m_completionSortingName);
01402     else
01403       ret = QString::compare(completionSortingName(), rhs.completionSortingName()); //Do not use localeAwareCompare, because it is simply too slow for a list of about 1000 items
01404   }
01405 
01406   if( ret == 0 ) {
01407     // FIXME need to define a better default ordering for multiple model display
01408     ret = m_sourceRow.second.row() - rhs.m_sourceRow.second.row();
01409   }
01410 
01411   return ret < 0;
01412 }
01413 
01414 QString KateCompletionModel::Item::completionSortingName( ) const
01415 {
01416   if(m_completionSortingName.isEmpty()) {
01417     m_completionSortingName = m_nameColumn;
01418     if (model->sortingCaseSensitivity() == Qt::CaseInsensitive)
01419       m_completionSortingName = m_completionSortingName.toLower();
01420   }
01421 
01422   return m_completionSortingName;
01423 }
01424 
01425 void KateCompletionModel::Group::addItem( Item i, bool notifyModel )
01426 {
01427   if (isEmpty)
01428     notifyModel = false;
01429 
01430   QModelIndex groupIndex;
01431   if (notifyModel)
01432     groupIndex = model->indexForGroup(this);
01433 
01434   if (model->isSortingEnabled()) {
01435     
01436     prefilter.insert(qUpperBound(prefilter.begin(), prefilter.end(), i), i);
01437     if(i.isVisible()) {
01438       QList<Item>::iterator it = qUpperBound(filtered.begin(), filtered.end(), i);
01439       uint rowNumber = it - filtered.begin();
01440       
01441       if(notifyModel)
01442         model->beginInsertRows(groupIndex, rowNumber, rowNumber);
01443       
01444       filtered.insert(it, i);
01445     }
01446   } else {
01447     if(notifyModel)
01448       model->beginInsertRows(groupIndex, prefilter.size(), prefilter.size());
01449     if (i.isVisible())
01450       prefilter.append(i);
01451   }
01452   
01453   if(notifyModel)
01454     model->endInsertRows();
01455 }
01456 
01457 bool KateCompletionModel::Group::removeItem(const ModelRow& row)
01458 {
01459   for (int pi = 0; pi < prefilter.count(); ++pi)
01460     if (prefilter[pi].sourceRow() == row) {
01461       int index = rowOf(row);
01462       if (index != -1)
01463         model->beginRemoveRows(model->indexForGroup(this), index, index);
01464 
01465       filtered.removeAt(index);
01466       prefilter.removeAt(pi);
01467 
01468       if (index != -1)
01469         model->endRemoveRows();
01470 
01471       return index != -1;
01472     }
01473 
01474   Q_ASSERT(false);
01475   return false;
01476 }
01477 
01478 KateCompletionModel::Group::Group( KateCompletionModel * m )
01479   : model(m)
01480   , isEmpty(true)
01481   , customSortingKey(-1)
01482 {
01483   Q_ASSERT(model);
01484 }
01485 
01486 void KateCompletionModel::setSortingAlphabetical( bool alphabetical )
01487 {
01488   if (m_sortingAlphabetical != alphabetical) {
01489     m_sortingAlphabetical = alphabetical;
01490     resort();
01491   }
01492 }
01493 
01494 void KateCompletionModel::Group::resort( )
01495 {
01496   qStableSort(prefilter.begin(), prefilter.end());
01497   //int oldRowCount = filtered.count();
01498   filtered.clear();
01499   foreach (const Item& i, prefilter)
01500     if (i.isVisible())
01501       filtered.append(i);
01502 
01503   model->hideOrShowGroup(this);
01504   //Q_ASSERT(filtered.count() == oldRowCount);
01505 }
01506 
01507 void KateCompletionModel::setSortingCaseSensitivity( Qt::CaseSensitivity cs )
01508 {
01509   if (m_sortingCaseSensitivity != cs) {
01510     m_sortingCaseSensitivity = cs;
01511     resort();
01512   }
01513 }
01514 
01515 void KateCompletionModel::resort( )
01516 {
01517   foreach (Group* g, m_rowTable)
01518     g->resort();
01519 
01520   foreach (Group* g, m_emptyGroups)
01521     g->resort();
01522 
01523   emit contentGeometryChanged();
01524 }
01525 
01526 bool KateCompletionModel::Item::isValid( ) const
01527 {
01528   return model && m_sourceRow.first && m_sourceRow.second.row() >= 0;
01529 }
01530 
01531 void KateCompletionModel::Group::clear( )
01532 {
01533   prefilter.clear();
01534   filtered.clear();
01535   isEmpty = true;
01536 }
01537 
01538 bool KateCompletionModel::filterContextMatchesOnly( ) const
01539 {
01540   return m_filterContextMatchesOnly;
01541 }
01542 
01543 void KateCompletionModel::setFilterContextMatchesOnly( bool filter )
01544 {
01545   if (m_filterContextMatchesOnly != filter) {
01546     m_filterContextMatchesOnly = filter;
01547     refilter();
01548   }
01549 }
01550 
01551 bool KateCompletionModel::filterByAttribute( ) const
01552 {
01553   return m_filterByAttribute;
01554 }
01555 
01556 void KateCompletionModel::setFilterByAttribute( bool filter )
01557 {
01558   if (m_filterByAttribute == filter) {
01559     m_filterByAttribute = filter;
01560     refilter();
01561   }
01562 }
01563 
01564 KTextEditor::CodeCompletionModel::CompletionProperties KateCompletionModel::filterAttributes( ) const
01565 {
01566   return m_filterAttributes;
01567 }
01568 
01569 void KateCompletionModel::setFilterAttributes( KTextEditor::CodeCompletionModel::CompletionProperties attributes )
01570 {
01571   if (m_filterAttributes == attributes) {
01572     m_filterAttributes = attributes;
01573     refilter();
01574   }
01575 }
01576 
01577 int KateCompletionModel::maximumInheritanceDepth( ) const
01578 {
01579   return m_maximumInheritanceDepth;
01580 }
01581 
01582 void KateCompletionModel::setMaximumInheritanceDepth( int maxDepth )
01583 {
01584   if (m_maximumInheritanceDepth != maxDepth) {
01585     m_maximumInheritanceDepth = maxDepth;
01586     refilter();
01587   }
01588 }
01589 
01590 void KateCompletionModel::refilter( )
01591 {
01592   m_ungrouped->refilter();
01593 
01594   foreach (Group* g, m_rowTable)
01595     if(g != m_argumentHints)
01596       g->refilter();
01597 
01598   foreach (Group* g, m_emptyGroups)
01599     if(g != m_argumentHints)
01600       g->refilter();
01601 
01602   updateBestMatches();
01603 
01604   clearExpanding(); //We need to do this, or be aware of expanding-widgets while filtering.
01605 }
01606 
01607 void KateCompletionModel::Group::refilter( )
01608 {
01609   filtered.clear();
01610   foreach (const Item& i, prefilter)
01611     if (!i.isFiltered())
01612       filtered.append(i);
01613 }
01614 
01615 bool KateCompletionModel::Item::filter( )
01616 {
01617   matchFilters = false;
01618 
01619   if (model->isFilteringEnabled()) {
01620     QModelIndex sourceIndex = m_sourceRow.second.sibling(m_sourceRow.second.row(), CodeCompletionModel::Name);
01621 
01622     if (model->filterContextMatchesOnly()) {
01623       QVariant contextMatch = sourceIndex.data(CodeCompletionModel::MatchQuality);
01624       if (contextMatch.canConvert(QVariant::Int) && !contextMatch.toInt())
01625         goto filter;
01626     }
01627 
01628     if (model->filterByAttribute()) {
01629       int completionFlags = sourceIndex.data(CodeCompletionModel::CompletionRole).toInt();
01630       if (model->filterAttributes() & completionFlags)
01631         goto filter;
01632     }
01633 
01634     if (model->maximumInheritanceDepth() > 0) {
01635       int inheritanceDepth = sourceIndex.data(CodeCompletionModel::InheritanceDepth).toInt();
01636       if (inheritanceDepth > model->maximumInheritanceDepth())
01637         goto filter;
01638     }
01639   }
01640 
01641   matchFilters = true;
01642 
01643   filter:
01644   return matchFilters;
01645 }
01646 
01647 bool KateCompletionModel::shouldMatchHideCompletionList() const {
01648 //   return m_haveExactMatch;
01650   foreach(Group* group, m_rowTable)
01651     foreach(const Item& item, group->filtered)
01652       if(item.haveExactMatch()) {
01653         KTextEditor::CodeCompletionModelControllerInterface2* iface2 = dynamic_cast<KTextEditor::CodeCompletionModelControllerInterface2*>(item.sourceRow().first);
01654         if(!iface2 || iface2->matchingItem(item.sourceRow().second) == KTextEditor::CodeCompletionModelControllerInterface2::HideListIfAutomaticInvocation)
01655           return true;
01656       }
01657   return false;
01658 }
01659 
01660 KateCompletionModel::Item::MatchType KateCompletionModel::Item::match()
01661 {
01662   // Check to see if the item is matched by the current completion string
01663   QModelIndex sourceIndex = m_sourceRow.second.sibling(m_sourceRow.second.row(), CodeCompletionModel::Name);
01664 
01665   QString match = model->currentCompletion(m_sourceRow.first);
01666 
01667   m_haveExactMatch = false;
01668   
01669    // Hehe, everything matches nothing! (ie. everything matches a blank string)
01670    if (match.isEmpty())
01671      return PerfectMatch;
01672   
01673   matchCompletion = (m_nameColumn.startsWith(match, model->matchCaseSensitivity()) ? StartsWithMatch : NoMatch);
01674 
01675   if(matchCompletion && match.length() == m_nameColumn.length()) {
01676     matchCompletion = PerfectMatch;
01677     m_haveExactMatch = true;
01678   }
01679   
01680   return matchCompletion;
01681 }
01682 
01683 QString KateCompletionModel::propertyName( KTextEditor::CodeCompletionModel::CompletionProperty property )
01684 {
01685   switch (property) {
01686     case CodeCompletionModel::Public:
01687       return i18n("Public");
01688 
01689     case CodeCompletionModel::Protected:
01690       return i18n("Protected");
01691 
01692     case CodeCompletionModel::Private:
01693       return i18n("Private");
01694 
01695     case CodeCompletionModel::Static:
01696       return i18n("Static");
01697 
01698     case CodeCompletionModel::Const:
01699       return i18n("Constant");
01700 
01701     case CodeCompletionModel::Namespace:
01702       return i18n("Namespace");
01703 
01704     case CodeCompletionModel::Class:
01705       return i18n("Class");
01706 
01707     case CodeCompletionModel::Struct:
01708       return i18n("Struct");
01709 
01710     case CodeCompletionModel::Union:
01711       return i18n("Union");
01712 
01713     case CodeCompletionModel::Function:
01714       return i18n("Function");
01715 
01716     case CodeCompletionModel::Variable:
01717       return i18n("Variable");
01718 
01719     case CodeCompletionModel::Enum:
01720       return i18n("Enumeration");
01721 
01722     case CodeCompletionModel::Template:
01723       return i18n("Template");
01724 
01725     case CodeCompletionModel::Virtual:
01726       return i18n("Virtual");
01727 
01728     case CodeCompletionModel::Override:
01729       return i18n("Override");
01730 
01731     case CodeCompletionModel::Inline:
01732       return i18n("Inline");
01733 
01734     case CodeCompletionModel::Friend:
01735       return i18n("Friend");
01736 
01737     case CodeCompletionModel::Signal:
01738       return i18n("Signal");
01739 
01740     case CodeCompletionModel::Slot:
01741       return i18n("Slot");
01742 
01743     case CodeCompletionModel::LocalScope:
01744       return i18n("Local Scope");
01745 
01746     case CodeCompletionModel::NamespaceScope:
01747       return i18n("Namespace Scope");
01748 
01749     case CodeCompletionModel::GlobalScope:
01750       return i18n("Global Scope");
01751 
01752     default:
01753       return i18n("Unknown Property");
01754   }
01755 }
01756 
01757 bool KateCompletionModel::Item::isVisible( ) const
01758 {
01759   return matchCompletion && matchFilters;
01760 }
01761 
01762 bool KateCompletionModel::Item::isFiltered( ) const
01763 {
01764   return !matchFilters;
01765 }
01766 
01767 bool KateCompletionModel::Item::isMatching( ) const
01768 {
01769   return matchFilters;
01770 }
01771 
01772 const KateCompletionModel::ModelRow& KateCompletionModel::Item::sourceRow( ) const
01773 {
01774   return m_sourceRow;
01775 }
01776 
01777 QString KateCompletionModel::currentCompletion( KTextEditor::CodeCompletionModel* model ) const
01778 {
01779   return m_currentMatch.value(model);
01780 }
01781 
01782 Qt::CaseSensitivity KateCompletionModel::matchCaseSensitivity( ) const
01783 {
01784   return m_matchCaseSensitivity;
01785 }
01786 
01787 void KateCompletionModel::addCompletionModel(KTextEditor::CodeCompletionModel * model)
01788 {
01789   if (m_completionModels.contains(model))
01790     return;
01791 
01792   m_completionModels.append(model);
01793 
01794   connect(model, SIGNAL(rowsInserted(const QModelIndex&, int, int)), SLOT(slotRowsInserted(const QModelIndex&, int, int)));
01795   connect(model, SIGNAL(rowsRemoved(const QModelIndex&, int, int)), SLOT(slotRowsRemoved(const QModelIndex&, int, int)));
01796   connect(model, SIGNAL(modelReset()), SLOT(slotModelReset()));
01797 
01798   // This performs the reset
01799   createGroups();
01800 }
01801 
01802 void KateCompletionModel::setCompletionModel(KTextEditor::CodeCompletionModel* model)
01803 {
01804   clearCompletionModels();
01805   addCompletionModel(model);
01806 }
01807 
01808 void KateCompletionModel::setCompletionModels(const QList<KTextEditor::CodeCompletionModel*>& models)
01809 {
01810   //if (m_completionModels == models)
01811     //return;
01812 
01813   clearCompletionModels();
01814 
01815   m_completionModels = models;
01816 
01817   foreach (KTextEditor::CodeCompletionModel* model, models) {
01818     connect(model, SIGNAL(rowsInserted(const QModelIndex&, int, int)), SLOT(slotRowsInserted(const QModelIndex&, int, int)));
01819     connect(model, SIGNAL(rowsRemoved(const QModelIndex&, int, int)), SLOT(slotRowsRemoved(const QModelIndex&, int, int)));
01820     connect(model, SIGNAL(modelReset()), SLOT(slotModelReset()));
01821   }
01822 
01823   // This performs the reset
01824   createGroups();
01825 }
01826 
01827 QList< KTextEditor::CodeCompletionModel * > KateCompletionModel::completionModels() const
01828 {
01829   return m_completionModels;
01830 }
01831 
01832 void KateCompletionModel::removeCompletionModel(CodeCompletionModel * model)
01833 {
01834   if (!model || !m_completionModels.contains(model))
01835     return;
01836 
01837   m_currentMatch.remove(model);
01838 
01839   clearGroups(false);
01840 
01841   model->disconnect(this);
01842 
01843   m_completionModels.removeAll(model);
01844 
01845   if (!m_completionModels.isEmpty()) {
01846     // This performs the reset
01847     createGroups();
01848   }else{
01849     emit contentGeometryChanged();
01850     reset();
01851   }
01852 }
01853 
01854 //Updates the best-matches group
01855 void KateCompletionModel::updateBestMatches() {
01856   int maxMatches = 300; //We cannot do too many operations here, because they are all executed whenever a character is added. Would be nice if we could split the operations up somewhat using a timer.
01857 
01858   m_updateBestMatchesTimer->stop();
01859   //Maps match-qualities to ModelRows paired together with the BestMatchesCount returned by the items.
01860   typedef QMultiMap<int, QPair<int, ModelRow> > BestMatchMap;
01861   BestMatchMap matches;
01862   
01863   if(!hasGroups()) {
01864     //If there is no grouping, just change the order of the items, moving the best matching ones to the front
01865     QMultiMap<int, int> rowsForQuality;
01866     
01867     int row = 0;
01868     foreach(const Item& item, m_ungrouped->filtered) {
01869       ModelRow source = item.sourceRow();
01870       
01871       QVariant v = source.second.data(CodeCompletionModel::BestMatchesCount);
01872 
01873       if( v.type() == QVariant::Int && v.toInt() > 0 ) {
01874         int quality = contextMatchQuality(source);
01875         if(quality > 0)
01876           rowsForQuality.insert(quality, row);
01877       }
01878       
01879       ++row;
01880       --maxMatches;
01881       if(maxMatches < 0)
01882         break;
01883     }
01884     
01885     if(!rowsForQuality.isEmpty()) {
01886       //Rewrite m_ungrouped->filtered in a new order
01887       QSet<int> movedToFront;
01888       QList<Item> newFiltered;
01889       for(QMultiMap<int, int>::const_iterator it = rowsForQuality.constBegin(); it != rowsForQuality.constEnd(); ++it) {
01890         newFiltered.prepend(m_ungrouped->filtered[it.value()]);
01891         movedToFront.insert(it.value());
01892       }
01893       
01894       {
01895         uint size = m_ungrouped->filtered.size();
01896         for(uint a = 0; a < size; ++a)
01897           if(!movedToFront.contains(a))
01898             newFiltered.append(m_ungrouped->filtered[a]);
01899       }
01900       m_ungrouped->filtered = newFiltered;
01901     }
01902     return;
01903   }
01904   
01906   foreach (Group* g, m_rowTable) {
01907     if( g == m_bestMatches )
01908       continue;
01909     for( int a = 0; a < g->filtered.size(); a++ )
01910     {
01911       ModelRow source = g->filtered[a].sourceRow();
01912 
01913       QVariant v = source.second.data(CodeCompletionModel::BestMatchesCount);
01914 
01915       if( v.type() == QVariant::Int && v.toInt() > 0 ) {
01916         //Return the best match with any of the argument-hints
01917 
01918         int quality = contextMatchQuality(source);
01919         if( quality > 0 )
01920           matches.insert(quality, qMakePair(v.toInt(), g->filtered[a].sourceRow()));
01921         --maxMatches;
01922       }
01923 
01924       if( maxMatches < 0 )
01925         break;
01926     }
01927     if( maxMatches < 0 )
01928       break;
01929   }
01930 
01931   //Now choose how many of the matches will be taken. This is done with the rule:
01932   //The count of shown best-matches should equal the average count of their BestMatchesCounts
01933   int cnt = 0;
01934   int matchesSum = 0;
01935   BestMatchMap::const_iterator it = matches.constEnd();
01936   while( it != matches.constBegin() )
01937   {
01938     --it;
01939     ++cnt;
01940     matchesSum += (*it).first;
01941     if( cnt > matchesSum / cnt )
01942       break;
01943   }
01944 
01945   m_bestMatches->filtered.clear();
01946   
01947   it = matches.constEnd();
01948 
01949   while( it != matches.constBegin() && cnt > 0 )
01950   {
01951     --it;
01952     --cnt;
01953 
01954     m_bestMatches->filtered.append( Item( true, this, HierarchicalModelHandler((*it).second.first), (*it).second) );
01955   }
01956 
01957   hideOrShowGroup(m_bestMatches);
01958 }
01959 
01960 void KateCompletionModel::rowSelected(const QModelIndex& row) {
01961   ExpandingWidgetModel::rowSelected(row);
01963   int rc = widget()->argumentHintModel()->rowCount(QModelIndex());
01964   if( rc == 0 ) return;
01965 
01966   //For now, simply update the whole column 0
01967   QModelIndex start = widget()->argumentHintModel()->index(0,0);
01968   QModelIndex end = widget()->argumentHintModel()->index(rc-1,0);
01969 
01970   widget()->argumentHintModel()->emitDataChanged(start, end);
01971 }
01972 
01973 void KateCompletionModel::clearCompletionModels()
01974 {
01975   foreach (CodeCompletionModel * model, m_completionModels)
01976     model->disconnect(this);
01977 
01978   m_completionModels.clear();
01979 
01980   m_currentMatch.clear();
01981 
01982   clearGroups();
01983 }
01984 
01985 #include "katecompletionmodel.moc"
01986 // kate: space-indent on; indent-width 2; replace-tabs on;

Kate

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

kdelibs

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