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

Kate

katecompletionmodel.cpp

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

Kate

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

kdelibs

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