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;
KDE 4.6 API Reference