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