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

Kate

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

kdelibs

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