1
/****************************************************************************
3
** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
4
** All rights reserved.
5
** Contact: Nokia Corporation (qt-info@nokia.com)
7
** This file is part of the QtGui module of the Qt Toolkit.
9
** $QT_BEGIN_LICENSE:LGPL$
10
** GNU Lesser General Public License Usage
11
** This file may be used under the terms of the GNU Lesser General Public
12
** License version 2.1 as published by the Free Software Foundation and
13
** appearing in the file LICENSE.LGPL included in the packaging of this
14
** file. Please review the following information to ensure the GNU Lesser
15
** General Public License version 2.1 requirements will be met:
16
** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
18
** In addition, as a special exception, Nokia gives you certain additional
19
** rights. These rights are described in the Nokia Qt LGPL Exception
20
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
22
** GNU General Public License Usage
23
** Alternatively, this file may be used under the terms of the GNU General
24
** Public License version 3.0 as published by the Free Software Foundation
25
** and appearing in the file LICENSE.GPL included in the packaging of this
26
** file. Please review the following information to ensure the GNU General
27
** Public License version 3.0 requirements will be met:
28
** http://www.gnu.org/copyleft/gpl.html.
31
** Alternatively, this file may be used in accordance with the terms and
32
** conditions contained in a signed written agreement between you and Nokia.
40
****************************************************************************/
44
\brief The KexiCompleter class provides completions based on an item model.
47
You can use KexiCompleter to provide auto completions in any Qt
48
widget, such as QLineEdit and QComboBox.
49
When the user starts typing a word, KexiCompleter suggests possible ways of
50
completing the word, based on a word list. The word list is
51
provided as a QAbstractItemModel. (For simple applications, where
52
the word list is static, you can pass a QStringList to
53
KexiCompleter's constructor.)
59
A KexiCompleter is used typically with a QLineEdit or QComboBox.
60
For example, here's how to provide auto completions from a simple
61
word list in a QLineEdit:
63
\snippet doc/src/snippets/code/src_gui_util_qcompleter.cpp 0
65
A QFileSystemModel can be used to provide auto completion of file names.
68
\snippet doc/src/snippets/code/src_gui_util_qcompleter.cpp 1
70
To set the model on which KexiCompleter should operate, call
71
setModel(). By default, KexiCompleter will attempt to match the \l
72
{completionPrefix}{completion prefix} (i.e., the word that the
73
user has started typing) against the Qt::EditRole data stored in
74
column 0 in the model case sensitively. This can be changed
75
using setCompletionRole(), setCompletionColumn(), and
78
If the model is sorted on the column and role that are used for completion,
79
you can call setModelSorting() with either
80
KexiCompleter::CaseSensitivelySortedModel or
81
KexiCompleter::CaseInsensitivelySortedModel as the argument. On large models,
82
this can lead to significant performance improvements, because KexiCompleter
83
can then use binary search instead of linear search.
85
The model can be a \l{QAbstractListModel}{list model},
86
a \l{QAbstractTableModel}{table model}, or a
87
\l{QAbstractItemModel}{tree model}. Completion on tree models
88
is slightly more involved and is covered in the \l{Handling
89
Tree Models} section below.
91
The completionMode() determines the mode used to provide completions to
94
\section1 Iterating Through Completions
96
To retrieve a single candidate string, call setCompletionPrefix()
97
with the text that needs to be completed and call
98
currentCompletion(). You can iterate through the list of
101
\snippet doc/src/snippets/code/src_gui_util_qcompleter.cpp 2
103
completionCount() returns the total number of completions for the
104
current prefix. completionCount() should be avoided when possible,
105
since it requires a scan of the entire model.
107
\section1 The Completion Model
109
completionModel() return a list model that contains all possible
110
completions for the current completion prefix, in the order in which
111
they appear in the model. This model can be used to display the current
112
completions in a custom view. Calling setCompletionPrefix() automatically
113
refreshes the completion model.
115
\section1 Handling Tree Models
117
KexiCompleter can look for completions in tree models, assuming
118
that any item (or sub-item or sub-sub-item) can be unambiguously
119
represented as a string by specifying the path to the item. The
120
completion is then performed one level at a time.
122
Let's take the example of a user typing in a file system path.
123
The model is a (hierarchical) QFileSystemModel. The completion
124
occurs for every element in the path. For example, if the current
125
text is \c C:\Wind, KexiCompleter might suggest \c Windows to
126
complete the current path element. Similarly, if the current text
127
is \c C:\Windows\Sy, KexiCompleter might suggest \c System.
129
For this kind of completion to work, KexiCompleter needs to be able to
130
split the path into a list of strings that are matched at each level.
131
For \c C:\Windows\Sy, it needs to be split as "C:", "Windows" and "Sy".
132
The default implementation of splitPath(), splits the completionPrefix
133
using QDir::separator() if the model is a QFileSystemModel.
135
To provide completions, KexiCompleter needs to know the path from an index.
136
This is provided by pathFromIndex(). The default implementation of
137
pathFromIndex(), returns the data for the \l{Qt::EditRole}{edit role}
138
for list models and the absolute file path if the mode is a QFileSystemModel.
140
\sa QAbstractItemModel, QLineEdit, QComboBox, {Completer Example}
143
#include "KexiCompleter_p.h"
145
#ifndef QT_NO_COMPLETER
147
#include <QScrollBar>
148
#include <QStringListModel>
150
#include <QFileSystemModel>
152
#include <QApplication>
154
#include <QDesktopWidget>
160
class KexiEmptyItemModel : public QAbstractItemModel
164
explicit KexiEmptyItemModel(QObject *parent = 0) : QAbstractItemModel(parent) {}
165
QModelIndex index(int, int, const QModelIndex &) const { return QModelIndex(); }
166
QModelIndex parent(const QModelIndex &) const { return QModelIndex(); }
167
int rowCount(const QModelIndex &) const { return 0; }
168
int columnCount(const QModelIndex &) const { return 0; }
169
bool hasChildren(const QModelIndex &) const { return false; }
170
QVariant data(const QModelIndex &, int) const { return QVariant(); }
173
Q_GLOBAL_STATIC(KexiEmptyItemModel, kexiEmptyModel)
175
QAbstractItemModel *KexiAbstractItemModelPrivate::staticEmptyModel()
177
return kexiEmptyModel();
181
struct DefaultRoleNames : public QHash<int, QByteArray>
184
(*this)[Qt::DisplayRole] = "display";
185
(*this)[Qt::DecorationRole] = "decoration";
186
(*this)[Qt::EditRole] = "edit";
187
(*this)[Qt::ToolTipRole] = "toolTip";
188
(*this)[Qt::StatusTipRole] = "statusTip";
189
(*this)[Qt::WhatsThisRole] = "whatsThis";
194
Q_GLOBAL_STATIC(DefaultRoleNames, qDefaultRoleNames)
196
const QHash<int,QByteArray> &KexiAbstractItemModelPrivate::defaultRoleNames()
198
return *qDefaultRoleNames();
202
KexiCompletionModel::KexiCompletionModel(KexiCompleterPrivate *c, QObject *parent)
203
: QAbstractProxyModel(parent),
204
c(c), showAll(false), d(new KexiCompletionModelPrivate(this))
206
QAbstractProxyModel::setSourceModel(KexiAbstractItemModelPrivate::staticEmptyModel());
210
KexiCompletionModel::~KexiCompletionModel()
215
int KexiCompletionModel::columnCount(const QModelIndex &) const
217
return sourceModel()->columnCount();
220
void KexiCompletionModel::setSourceModel(QAbstractItemModel *source)
222
bool hadModel = (sourceModel() != 0);
225
QObject::disconnect(sourceModel(), 0, this, 0);
227
QAbstractProxyModel::setSourceModel(source ? source : KexiAbstractItemModelPrivate::staticEmptyModel());
230
// TODO: Optimize updates in the source model
231
connect(source, SIGNAL(modelReset()), this, SLOT(invalidate()));
232
connect(source, SIGNAL(destroyed()), this, SLOT(modelDestroyed()));
233
connect(source, SIGNAL(layoutChanged()), this, SLOT(invalidate()));
234
connect(source, SIGNAL(rowsInserted(QModelIndex,int,int)), this, SLOT(rowsInserted()));
235
connect(source, SIGNAL(rowsRemoved(QModelIndex,int,int)), this, SLOT(invalidate()));
236
connect(source, SIGNAL(columnsInserted(QModelIndex,int,int)), this, SLOT(invalidate()));
237
connect(source, SIGNAL(columnsRemoved(QModelIndex,int,int)), this, SLOT(invalidate()));
238
connect(source, SIGNAL(dataChanged(QModelIndex,QModelIndex)), this, SLOT(invalidate()));
244
void KexiCompletionModel::createEngine()
246
bool sortedEngine = false;
247
switch (c->sorting) {
248
case KexiCompleter::UnsortedModel:
249
sortedEngine = false;
251
case KexiCompleter::CaseSensitivelySortedModel:
252
sortedEngine = c->cs == Qt::CaseSensitive;
254
case KexiCompleter::CaseInsensitivelySortedModel:
255
sortedEngine = c->cs == Qt::CaseInsensitive;
260
engine.reset(new QSortedModelEngine(c));
262
engine.reset(new QUnsortedModelEngine(c));
265
QModelIndex KexiCompletionModel::mapToSource(const QModelIndex& index) const
267
if (!index.isValid())
268
return engine->curParent;
271
QModelIndex parent = engine->curParent;
273
if (!engine->matchCount())
274
return QModelIndex();
275
Q_ASSERT(index.row() < engine->matchCount());
276
KexiIndexMapper& rootIndices = engine->historyMatch.indices;
277
if (index.row() < rootIndices.count()) {
278
row = rootIndices[index.row()];
279
parent = QModelIndex();
281
row = engine->curMatch.indices[index.row() - rootIndices.count()];
287
return sourceModel()->index(row, index.column(), parent);
290
QModelIndex KexiCompletionModel::mapFromSource(const QModelIndex& idx) const
293
return QModelIndex();
297
if (!engine->matchCount())
298
return QModelIndex();
300
KexiIndexMapper& rootIndices = engine->historyMatch.indices;
301
if (idx.parent().isValid()) {
302
if (idx.parent() != engine->curParent)
303
return QModelIndex();
305
row = rootIndices.indexOf(idx.row());
306
if (row == -1 && engine->curParent.isValid())
307
return QModelIndex(); // source parent and our parent don't match
311
KexiIndexMapper& indices = engine->curMatch.indices;
312
engine->filterOnDemand(idx.row() - indices.last());
313
row = indices.indexOf(idx.row()) + rootIndices.count();
317
return QModelIndex();
319
if (idx.parent() != engine->curParent)
320
return QModelIndex();
324
return createIndex(row, idx.column());
327
bool KexiCompletionModel::setCurrentRow(int row)
329
if (row < 0 || !engine->matchCount())
332
if (row >= engine->matchCount())
333
engine->filterOnDemand(row + 1 - engine->matchCount());
335
if (row >= engine->matchCount()) // invalid row
338
engine->curRow = row;
342
QModelIndex KexiCompletionModel::currentIndex(bool sourceIndex) const
344
if (!engine->matchCount())
345
return QModelIndex();
347
int row = engine->curRow;
349
row = engine->curMatch.indices[engine->curRow];
351
QModelIndex idx = createIndex(row, c->column);
354
return mapToSource(idx);
357
QModelIndex KexiCompletionModel::index(int row, int column, const QModelIndex& parent) const
359
if (row < 0 || column < 0 || column >= columnCount(parent) || parent.isValid())
360
return QModelIndex();
363
if (!engine->matchCount())
364
return QModelIndex();
365
if (row >= engine->historyMatch.indices.count()) {
366
int want = row + 1 - engine->matchCount();
368
engine->filterOnDemand(want);
369
if (row >= engine->matchCount())
370
return QModelIndex();
373
if (row >= sourceModel()->rowCount(engine->curParent))
374
return QModelIndex();
377
return createIndex(row, column);
380
int KexiCompletionModel::completionCount() const
382
if (!engine->matchCount())
385
engine->filterOnDemand(INT_MAX);
386
return engine->matchCount();
389
int KexiCompletionModel::rowCount(const QModelIndex &parent) const
391
if (parent.isValid())
395
// Show all items below current parent, even if we have no valid matches
396
if (engine->curParts.count() != 1 && !engine->matchCount()
397
&& !engine->curParent.isValid())
399
return sourceModel()->rowCount(engine->curParent);
402
return completionCount();
405
void KexiCompletionModel::setFiltered(bool filtered)
407
if (showAll == !filtered)
413
bool KexiCompletionModel::hasChildren(const QModelIndex &parent) const
415
if (parent.isValid())
419
return sourceModel()->hasChildren(mapToSource(parent));
421
if (!engine->matchCount())
427
QVariant KexiCompletionModel::data(const QModelIndex& index, int role) const
429
return sourceModel()->data(mapToSource(index), role);
432
void KexiCompletionModel::modelDestroyed()
434
QAbstractProxyModel::setSourceModel(0); // switch to static empty model
438
void KexiCompletionModel::rowsInserted()
444
void KexiCompletionModel::invalidate()
446
engine->cache.clear();
447
filter(engine->curParts);
450
void KexiCompletionModel::filter(const QStringList& parts)
452
engine->filter(parts);
455
if (sourceModel()->canFetchMore(engine->curParent))
456
sourceModel()->fetchMore(engine->curParent);
459
void KexiCompletionModel::resetModel()
461
if (rowCount() == 0) {
467
emit layoutAboutToBeChanged();
468
QModelIndexList piList = persistentIndexList();
469
QModelIndexList empty;
470
for (int i = 0; i < piList.size(); i++)
471
empty.append(QModelIndex());
472
changePersistentIndexList(piList, empty);
473
emit layoutChanged();
476
//////////////////////////////////////////////////////////////////////////////
477
void KexiCompletionEngine::filter(const QStringList& parts)
479
const QAbstractItemModel *model = c->proxy->sourceModel();
481
if (curParts.isEmpty())
482
curParts.append(QString());
485
curParent = QModelIndex();
486
curMatch = KexiMatchData();
487
historyMatch = filterHistory();
493
for (int i = 0; i < curParts.count() - 1; i++) {
494
QString part = curParts[i];
495
int emi = filter(part, parent, -1).exactMatchIndex;
498
parent = model->index(emi, c->column, parent);
501
// Note that we set the curParent to a valid parent, even if we have no matches
502
// When filtering is disabled, we show all the items under this parent
504
if (curParts.last().isEmpty())
505
curMatch = KexiMatchData(KexiIndexMapper(0, model->rowCount(curParent) - 1), -1, false);
507
curMatch = filter(curParts.last(), curParent, 1); // build at least one
508
curRow = curMatch.isValid() ? 0 : -1;
511
inline bool matchPrefix(const QString& s1, const QString& s2, Qt::CaseSensitivity cs)
513
return s1.startsWith(s2, cs);
516
inline bool matchSubstring(const QString& s1, const QString& s2, Qt::CaseSensitivity cs)
518
return s1.contains(s2, cs);
521
typedef bool (*MatchFunction)(const QString&, const QString&, Qt::CaseSensitivity);
523
KexiMatchData KexiCompletionEngine::filterHistory()
525
QAbstractItemModel *source = c->proxy->sourceModel();
526
if (curParts.count() <= 1 || c->proxy->showAll || !source)
527
return KexiMatchData();
528
#ifdef QT_NO_DIRMODEL
529
bool isDirModel = false;
531
bool isDirModel = qobject_cast<QDirModel *>(source) != 0;
533
#ifdef QT_NO_FILESYSTEMMODEL
534
bool isFsModel = false;
536
bool isFsModel = qobject_cast<QFileSystemModel *>(source) != 0;
539
KexiIndexMapper im(v);
540
KexiMatchData m(im, -1, true);
542
MatchFunction matchFunction = c->substringCompletion ? &matchSubstring : &matchPrefix;
543
for (int i = 0; i < source->rowCount(); i++) {
544
QString str = source->index(i, c->column).data().toString();
545
if (matchFunction(str, c->prefix, c->cs)
546
#if (!defined(Q_OS_WIN) || defined(Q_OS_WINCE)) && !defined(Q_OS_SYMBIAN)
547
&& ((!isFsModel && !isDirModel) || QDir::toNativeSeparators(str) != QDir::separator())
555
// Returns a match hint from the cache by chopping the search string
556
bool KexiCompletionEngine::matchHint(QString part, const QModelIndex& parent, KexiMatchData *hint)
558
if (c->cs == Qt::CaseInsensitive)
559
part = part.toLower();
561
const CacheItem& map = cache[parent];
564
while (!key.isEmpty()) {
566
if (map.contains(key)) {
575
bool KexiCompletionEngine::lookupCache(QString part, const QModelIndex& parent, KexiMatchData *m)
577
if (c->cs == Qt::CaseInsensitive)
578
part = part.toLower();
579
const CacheItem& map = cache[parent];
580
if (!map.contains(part))
586
// When the cache size exceeds 1MB, it clears out about 1/2 of the cache.
587
void KexiCompletionEngine::saveInCache(QString part, const QModelIndex& parent, const KexiMatchData& m)
589
KexiMatchData old = cache[parent].take(part);
590
cost = cost + m.indices.cost() - old.indices.cost();
591
if (cost * sizeof(int) > 1024 * 1024) {
592
QMap<QModelIndex, CacheItem>::iterator it1 = cache.begin();
593
while (it1 != cache.end()) {
594
CacheItem& ci = it1.value();
595
int sz = ci.count()/2;
596
QMap<QString, KexiMatchData>::iterator it2 = ci.begin();
598
while (it2 != ci.end() && i < sz) {
599
cost -= it2.value().indices.cost();
603
if (ci.count() == 0) {
604
it1 = cache.erase(it1);
611
if (c->cs == Qt::CaseInsensitive)
612
part = part.toLower();
613
cache[parent][part] = m;
616
///////////////////////////////////////////////////////////////////////////////////
617
KexiIndexMapper QSortedModelEngine::indexHint(QString part, const QModelIndex& parent, Qt::SortOrder order)
619
const QAbstractItemModel *model = c->proxy->sourceModel();
621
if (c->cs == Qt::CaseInsensitive)
622
part = part.toLower();
624
const CacheItem& map = cache[parent];
626
// Try to find a lower and upper bound for the search from previous results
627
int to = model->rowCount(parent) - 1;
629
const CacheItem::const_iterator it = map.lowerBound(part);
631
// look backward for first valid hint
632
for(CacheItem::const_iterator it1 = it; it1-- != map.constBegin();) {
633
const KexiMatchData& value = it1.value();
634
if (value.isValid()) {
635
if (order == Qt::AscendingOrder) {
636
from = value.indices.last() + 1;
638
to = value.indices.first() - 1;
644
// look forward for first valid hint
645
for(CacheItem::const_iterator it2 = it; it2 != map.constEnd(); ++it2) {
646
const KexiMatchData& value = it2.value();
647
if (value.isValid() && !it2.key().startsWith(part)) {
648
if (order == Qt::AscendingOrder) {
649
to = value.indices.first() - 1;
651
from = value.indices.first() + 1;
657
return KexiIndexMapper(from, to);
660
Qt::SortOrder QSortedModelEngine::sortOrder(const QModelIndex &parent) const
662
const QAbstractItemModel *model = c->proxy->sourceModel();
664
int rowCount = model->rowCount(parent);
666
return Qt::AscendingOrder;
667
QString first = model->data(model->index(0, c->column, parent), c->role).toString();
668
QString last = model->data(model->index(rowCount - 1, c->column, parent), c->role).toString();
669
return QString::compare(first, last, c->cs) <= 0 ? Qt::AscendingOrder : Qt::DescendingOrder;
672
KexiMatchData QSortedModelEngine::filter(const QString& part, const QModelIndex& parent, int)
674
const QAbstractItemModel *model = c->proxy->sourceModel();
677
if (lookupCache(part, parent, &hint))
680
KexiIndexMapper indices;
681
Qt::SortOrder order = sortOrder(parent);
683
if (matchHint(part, parent, &hint)) {
685
return KexiMatchData();
686
indices = hint.indices;
688
indices = indexHint(part, parent, order);
691
// binary search the model within 'indices' for 'part' under 'parent'
692
int high = indices.to() + 1;
693
int low = indices.from() - 1;
695
QModelIndex probeIndex;
698
while (high - low > 1)
700
probe = (high + low) / 2;
701
probeIndex = model->index(probe, c->column, parent);
702
probeData = model->data(probeIndex, c->role).toString();
703
const int cmp = QString::compare(probeData, part, c->cs);
704
if ((order == Qt::AscendingOrder && cmp >= 0)
705
|| (order == Qt::DescendingOrder && cmp < 0)) {
712
if ((order == Qt::AscendingOrder && low == indices.to())
713
|| (order == Qt::DescendingOrder && high == indices.from())) { // not found
714
saveInCache(part, parent, KexiMatchData());
715
return KexiMatchData();
718
probeIndex = model->index(order == Qt::AscendingOrder ? low+1 : high-1, c->column, parent);
719
probeData = model->data(probeIndex, c->role).toString();
720
if (!probeData.startsWith(part, c->cs)) {
721
saveInCache(part, parent, KexiMatchData());
722
return KexiMatchData();
725
const bool exactMatch = QString::compare(probeData, part, c->cs) == 0;
726
int emi = exactMatch ? (order == Qt::AscendingOrder ? low+1 : high-1) : -1;
730
if (order == Qt::AscendingOrder) {
732
high = indices.to() + 1;
736
low = indices.from() - 1;
740
while (high - low > 1)
742
probe = (high + low) / 2;
743
probeIndex = model->index(probe, c->column, parent);
744
probeData = model->data(probeIndex, c->role).toString();
745
const bool startsWith = probeData.startsWith(part, c->cs);
746
if ((order == Qt::AscendingOrder && startsWith)
747
|| (order == Qt::DescendingOrder && !startsWith)) {
754
KexiMatchData m(order == Qt::AscendingOrder ? KexiIndexMapper(from, high - 1) : KexiIndexMapper(low+1, to), emi, false);
755
saveInCache(part, parent, m);
759
////////////////////////////////////////////////////////////////////////////////////////
760
int QUnsortedModelEngine::buildIndices(const QString& str, const QModelIndex& parent, int n,
761
const KexiIndexMapper& indices, KexiMatchData* m)
763
Q_ASSERT(m->partial);
764
Q_ASSERT(n != -1 || m->exactMatchIndex == -1);
765
const QAbstractItemModel *model = c->proxy->sourceModel();
768
MatchFunction matchFunction = c->substringCompletion ? &matchSubstring : &matchPrefix;
769
for (i = 0; i < indices.count() && count != n; ++i) {
770
QModelIndex idx = model->index(indices[i], c->column, parent);
771
QString data = model->data(idx, c->role).toString();
772
if (!matchFunction(data, str, c->cs) || !(model->flags(idx) & Qt::ItemIsSelectable))
774
m->indices.append(indices[i]);
776
if (m->exactMatchIndex == -1 && QString::compare(data, str, c->cs) == 0) {
777
m->exactMatchIndex = indices[i];
785
void QUnsortedModelEngine::filterOnDemand(int n)
787
Q_ASSERT(matchCount());
788
if (!curMatch.partial)
791
const QAbstractItemModel *model = c->proxy->sourceModel();
792
int lastRow = model->rowCount(curParent) - 1;
793
KexiIndexMapper im(curMatch.indices.last() + 1, lastRow);
794
int lastIndex = buildIndices(curParts.last(), curParent, n, im, &curMatch);
795
curMatch.partial = (lastRow != lastIndex);
796
saveInCache(curParts.last(), curParent, curMatch);
799
KexiMatchData QUnsortedModelEngine::filter(const QString& part, const QModelIndex& parent, int n)
804
KexiIndexMapper im(v);
805
KexiMatchData m(im, -1, true);
807
const QAbstractItemModel *model = c->proxy->sourceModel();
808
bool foundInCache = lookupCache(part, parent, &m);
811
if (matchHint(part, parent, &hint) && !hint.isValid())
812
return KexiMatchData();
815
if (!foundInCache && !hint.isValid()) {
816
const int lastRow = model->rowCount(parent) - 1;
817
KexiIndexMapper all(0, lastRow);
818
int lastIndex = buildIndices(part, parent, n, all, &m);
819
m.partial = (lastIndex != lastRow);
821
if (!foundInCache) { // build from hint as much as we can
822
buildIndices(part, parent, INT_MAX, hint.indices, &m);
823
m.partial = hint.partial;
825
if (m.partial && ((n == -1 && m.exactMatchIndex == -1) || (m.indices.count() < n))) {
826
// need more and have more
827
const int lastRow = model->rowCount(parent) - 1;
828
KexiIndexMapper rest(hint.indices.last() + 1, lastRow);
829
int want = n == -1 ? -1 : n - m.indices.count();
830
int lastIndex = buildIndices(part, parent, want, rest, &m);
831
m.partial = (lastRow != lastIndex);
835
saveInCache(part, parent, m);
839
///////////////////////////////////////////////////////////////////////////////
840
KexiCompleterPrivate::KexiCompleterPrivate(KexiCompleter *qq)
841
: widget(0), proxy(0), popup(0), cs(Qt::CaseSensitive), substringCompletion(false),
842
role(Qt::EditRole), column(0), maxVisibleItems(7), sorting(KexiCompleter::UnsortedModel),
843
wrap(true), eatFocusOut(true),
844
hiddenBecauseNoMatch(false), q(qq)
848
void KexiCompleterPrivate::init(QAbstractItemModel *m)
850
proxy = new KexiCompletionModel(this, q);
851
QObject::connect(proxy, SIGNAL(rowsAdded()), q, SLOT(_q_autoResizePopup()));
853
#ifdef QT_NO_LISTVIEW
854
q->setCompletionMode(KexiCompleter::InlineCompletion);
856
q->setCompletionMode(KexiCompleter::PopupCompletion);
857
#endif // QT_NO_LISTVIEW
860
void KexiCompleterPrivate::setCurrentIndex(QModelIndex index, bool select)
865
popup->selectionModel()->setCurrentIndex(index, QItemSelectionModel::NoUpdate);
867
if (!index.isValid())
868
popup->selectionModel()->clear();
870
popup->selectionModel()->setCurrentIndex(index, QItemSelectionModel::Select
871
| QItemSelectionModel::Rows);
873
index = popup->selectionModel()->currentIndex();
874
if (!index.isValid())
875
popup->scrollToTop();
877
popup->scrollTo(index, QAbstractItemView::PositionAtTop);
880
void KexiCompleterPrivate::_q_completionSelected(const QItemSelection& selection)
883
if (!selection.indexes().isEmpty())
884
index = selection.indexes().first();
886
_q_complete(index, true);
889
void KexiCompleterPrivate::_q_complete(QModelIndex index, bool highlighted)
893
if (!index.isValid() || (!proxy->showAll && (index.row() >= proxy->engine->matchCount()))) {
896
if (!(index.flags() & Qt::ItemIsEnabled))
898
QModelIndex si = proxy->mapToSource(index);
899
si = si.sibling(si.row(), column); // for clicked()
900
completion = q->pathFromIndex(si);
901
#ifndef QT_NO_DIRMODEL
902
// add a trailing separator in inline
903
if (mode == KexiCompleter::InlineCompletion) {
904
if (qobject_cast<QDirModel *>(proxy->sourceModel()) && QFileInfo(completion).isDir())
905
completion += QDir::separator();
908
#ifndef QT_NO_FILESYSTEMMODEL
909
// add a trailing separator in inline
910
if (mode == KexiCompleter::InlineCompletion) {
911
if (qobject_cast<QFileSystemModel *>(proxy->sourceModel()) && QFileInfo(completion).isDir())
912
completion += QDir::separator();
918
emit q->highlighted(index);
919
emit q->highlighted(completion);
921
emit q->activated(index);
922
emit q->activated(completion);
926
void KexiCompleterPrivate::_q_autoResizePopup()
928
if (!popup || !popup->isVisible())
930
showPopup(popupRect);
933
static void adjustPopupGeometry(QWidget *popupWidget, QWidget *widget, int widthHint,
934
int heightHint, const QRect ¤tRect)
936
const QRect screen = QApplication::desktop()->availableGeometry(widget);
937
const Qt::LayoutDirection dir = widget->layoutDirection();
942
if (currentRect.isValid()) {
943
rh = currentRect.height();
944
w = currentRect.width();
945
pos = widget->mapToGlobal(dir == Qt::RightToLeft ? currentRect.bottomRight() : currentRect.bottomLeft());
947
rh = widget->height();
948
pos = widget->mapToGlobal(QPoint(0, widget->height() - 2));
955
if (w > screen.width())
957
if ((pos.x() + w) > (screen.x() + screen.width()))
958
pos.setX(screen.x() + screen.width() - w);
959
if (pos.x() < screen.x())
960
pos.setX(screen.x());
962
int top = pos.y() - rh - screen.top() + 2;
963
int bottom = screen.bottom() - pos.y();
964
h = qMax(h, popupWidget->minimumHeight());
966
h = qMin(qMax(top, bottom), h);
969
pos.setY(pos.y() - h - rh + 2);
972
popupWidget->setGeometry(pos.x(), pos.y(), w, h);
975
void KexiCompleterPrivate::showPopup(const QRect& rect)
977
int widthHint = popup->sizeHintForColumn(0);
978
QScrollBar *vsb = popup->verticalScrollBar();
980
widthHint += vsb->sizeHint().width() + 3 + 3;
982
int heightHint = (popup->sizeHintForRow(0) * qMin(maxVisibleItems, popup->model()->rowCount()) + 3) + 3;
983
QScrollBar *hsb = popup->horizontalScrollBar();
984
if (hsb && hsb->isVisible()) {
985
heightHint += hsb->sizeHint().height();
987
adjustPopupGeometry(popup, widget, widthHint, heightHint, rect);
988
if (!popup->isVisible())
992
void KexiCompleterPrivate::_q_fileSystemModelDirectoryLoaded(const QString &path)
994
// Slot called when QFileSystemModel has finished loading.
995
// If we hide the popup because there was no match because the model was not loaded yet,
996
// we re-start the completion when we get the results
997
if (hiddenBecauseNoMatch
998
&& prefix.startsWith(path) && prefix != (path + QLatin1Char('/'))
1005
Constructs a completer object with the given \a parent.
1007
KexiCompleter::KexiCompleter(QObject *parent)
1008
: QObject(parent), d(new KexiCompleterPrivate(this))
1014
Constructs a completer object with the given \a parent that provides completions
1015
from the specified \a model.
1017
KexiCompleter::KexiCompleter(QAbstractItemModel *model, QObject *parent)
1018
: QObject(parent), d(new KexiCompleterPrivate(this))
1023
#ifndef QT_NO_STRINGLISTMODEL
1025
Constructs a KexiCompleter object with the given \a parent that uses the specified
1026
\a list as a source of possible completions.
1028
KexiCompleter::KexiCompleter(const QStringList& list, QObject *parent)
1029
: QObject(parent), d(new KexiCompleterPrivate(this))
1031
d->init(new QStringListModel(list, this));
1033
#endif // QT_NO_STRINGLISTMODEL
1036
Destroys the completer object.
1038
KexiCompleter::~KexiCompleter()
1044
Sets the widget for which completion are provided for to \a widget. This
1045
function is automatically called when a KexiCompleter is set on a QLineEdit
1046
using QLineEdit::setCompleter() or on a QComboBox using
1047
QComboBox::setCompleter(). The widget needs to be set explicitly when
1048
providing completions for custom widgets.
1050
\sa widget(), setModel(), setPopup()
1052
void KexiCompleter::setWidget(QWidget *widget)
1054
if (widget && d->widget == widget)
1057
d->widget->removeEventFilter(this);
1060
d->widget->installEventFilter(this);
1063
d->popup->setFocusProxy(d->widget);
1068
Returns the widget for which the completer object is providing completions.
1072
QWidget *KexiCompleter::widget() const
1078
Sets the model which provides completions to \a model. The \a model can
1079
be list model or a tree model. If a model has been already previously set
1080
and it has the KexiCompleter as its parent, it is deleted.
1082
For convenience, if \a model is a QFileSystemModel, KexiCompleter switches its
1083
caseSensitivity to Qt::CaseInsensitive on Windows and Qt::CaseSensitive
1086
\sa completionModel(), modelSorting, {Handling Tree Models}
1088
void KexiCompleter::setModel(QAbstractItemModel *model)
1090
QAbstractItemModel *oldModel = d->proxy->sourceModel();
1091
d->proxy->setSourceModel(model);
1093
setPopup(d->popup); // set the model and make new connections
1094
if (oldModel && oldModel->QObject::parent() == this)
1096
#ifndef QT_NO_DIRMODEL
1097
if (qobject_cast<QDirModel *>(model)) {
1098
#if (defined(Q_OS_WIN) && !defined(Q_OS_WINCE)) || defined(Q_OS_SYMBIAN)
1099
setCaseSensitivity(Qt::CaseInsensitive);
1101
setCaseSensitivity(Qt::CaseSensitive);
1104
#endif // QT_NO_DIRMODEL
1105
#ifndef QT_NO_FILESYSTEMMODEL
1106
QFileSystemModel *fsModel = qobject_cast<QFileSystemModel *>(model);
1108
#if (defined(Q_OS_WIN) && !defined(Q_OS_WINCE)) || defined(Q_OS_SYMBIAN)
1109
setCaseSensitivity(Qt::CaseInsensitive);
1111
setCaseSensitivity(Qt::CaseSensitive);
1113
setCompletionRole(QFileSystemModel::FileNameRole);
1114
connect(fsModel, SIGNAL(directoryLoaded(QString)), this, SLOT(_q_fileSystemModelDirectoryLoaded(QString)));
1116
#endif // QT_NO_FILESYSTEMMODEL
1120
Returns the model that provides completion strings.
1122
\sa completionModel()
1124
QAbstractItemModel *KexiCompleter::model() const
1126
return d->proxy->sourceModel();
1130
\enum KexiCompleter::CompletionMode
1132
This enum specifies how completions are provided to the user.
1134
\value PopupCompletion Current completions are displayed in a popup window.
1135
\value InlineCompletion Completions appear inline (as selected text).
1136
\value UnfilteredPopupCompletion All possible completions are displayed in a popup window with the most likely suggestion indicated as current.
1138
\sa setCompletionMode()
1142
\property KexiCompleter::completionMode
1143
\brief how the completions are provided to the user
1145
The default value is KexiCompleter::PopupCompletion.
1147
void KexiCompleter::setCompletionMode(KexiCompleter::CompletionMode mode)
1150
d->proxy->setFiltered(mode != KexiCompleter::UnfilteredPopupCompletion);
1152
if (mode == KexiCompleter::InlineCompletion) {
1154
d->widget->removeEventFilter(this);
1156
d->popup->deleteLater();
1161
d->widget->installEventFilter(this);
1165
KexiCompleter::CompletionMode KexiCompleter::completionMode() const
1171
Sets the popup used to display completions to \a popup. KexiCompleter takes
1172
ownership of the view.
1174
A QListView is automatically created when the completionMode() is set to
1175
KexiCompleter::PopupCompletion or KexiCompleter::UnfilteredPopupCompletion. The
1176
default popup displays the completionColumn().
1178
Ensure that this function is called before the view settings are modified.
1179
This is required since view's properties may require that a model has been
1180
set on the view (for example, hiding columns in the view requires a model
1181
to be set on the view).
1185
void KexiCompleter::setPopup(QAbstractItemView *popup)
1187
Q_ASSERT(popup != 0);
1189
QObject::disconnect(d->popup->selectionModel(), 0, this, 0);
1190
QObject::disconnect(d->popup, 0, this, 0);
1192
if (d->popup != popup)
1194
if (popup->model() != d->proxy)
1195
popup->setModel(d->proxy);
1196
#if defined(Q_OS_MAC) && !defined(QT_MAC_USE_COCOA)
1202
Qt::FocusPolicy origPolicy = Qt::NoFocus;
1204
origPolicy = d->widget->focusPolicy();
1205
popup->setParent(0, Qt::Popup);
1206
popup->setFocusPolicy(Qt::NoFocus);
1208
d->widget->setFocusPolicy(origPolicy);
1210
popup->setFocusProxy(d->widget);
1211
popup->installEventFilter(this);
1212
popup->setItemDelegate(new KexiCompleterItemDelegate(popup));
1213
#ifndef QT_NO_LISTVIEW
1214
if (QListView *listView = qobject_cast<QListView *>(popup)) {
1215
listView->setModelColumn(d->column);
1219
QObject::connect(popup, SIGNAL(clicked(QModelIndex)),
1220
this, SLOT(_q_complete(QModelIndex)));
1221
QObject::connect(this, SIGNAL(activated(QModelIndex)),
1222
popup, SLOT(hide()));
1224
QObject::connect(popup->selectionModel(), SIGNAL(selectionChanged(QItemSelection,QItemSelection)),
1225
this, SLOT(_q_completionSelected(QItemSelection)));
1230
Returns the popup used to display completions.
1234
QAbstractItemView *KexiCompleter::popup() const
1236
#ifndef QT_NO_LISTVIEW
1237
if (!d->popup && completionMode() != KexiCompleter::InlineCompletion) {
1238
QListView *listView = new QListView;
1239
listView->setEditTriggers(QAbstractItemView::NoEditTriggers);
1240
listView->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
1241
listView->setSelectionBehavior(QAbstractItemView::SelectRows);
1242
listView->setSelectionMode(QAbstractItemView::SingleSelection);
1243
listView->setModelColumn(d->column);
1244
KexiCompleter *that = const_cast<KexiCompleter*>(this);
1245
that->setPopup(listView);
1247
#endif // QT_NO_LISTVIEW
1254
bool KexiCompleter::event(QEvent *ev)
1256
return QObject::event(ev);
1262
bool KexiCompleter::eventFilter(QObject *o, QEvent *e)
1264
if (d->eatFocusOut && o == d->widget && e->type() == QEvent::FocusOut) {
1265
d->hiddenBecauseNoMatch = false;
1266
if (d->popup && d->popup->isVisible())
1271
return QObject::eventFilter(o, e);
1273
switch (e->type()) {
1274
case QEvent::KeyPress: {
1275
QKeyEvent *ke = static_cast<QKeyEvent *>(e);
1277
QModelIndex curIndex = d->popup->currentIndex();
1278
QModelIndexList selList = d->popup->selectionModel()->selectedIndexes();
1280
const int key = ke->key();
1281
// In UnFilteredPopup mode, select the current item
1282
if ((key == Qt::Key_Up || key == Qt::Key_Down) && selList.isEmpty() && curIndex.isValid()
1283
&& d->mode == KexiCompleter::UnfilteredPopupCompletion) {
1284
d->setCurrentIndex(curIndex);
1288
// Handle popup navigation keys. These are hardcoded because up/down might make the
1289
// widget do something else (lineedit cursor moves to home/end on mac, for instance)
1293
if (ke->modifiers() & Qt::ControlModifier)
1298
if (!curIndex.isValid()) {
1299
int rowCount = d->proxy->rowCount();
1300
QModelIndex lastIndex = d->proxy->index(rowCount - 1, d->column);
1301
d->setCurrentIndex(lastIndex);
1303
} else if (curIndex.row() == 0) {
1305
d->setCurrentIndex(QModelIndex());
1311
if (!curIndex.isValid()) {
1312
QModelIndex firstIndex = d->proxy->index(0, d->column);
1313
d->setCurrentIndex(firstIndex);
1315
} else if (curIndex.row() == d->proxy->rowCount() - 1) {
1317
d->setCurrentIndex(QModelIndex());
1322
case Qt::Key_PageUp:
1323
case Qt::Key_PageDown:
1327
// Send the event to the widget. If the widget accepted the event, do nothing
1328
// If the widget did not accept the event, provide a default implementation
1329
d->eatFocusOut = false;
1330
(static_cast<QObject *>(d->widget))->event(ke);
1331
d->eatFocusOut = true;
1332
if (!d->widget || e->isAccepted() || !d->popup->isVisible()) {
1333
// widget lost focus, hide the popup
1334
if (d->widget && (!d->widget->hasFocus()
1335
#ifdef QT_KEYPAD_NAVIGATION
1336
|| (QApplication::keypadNavigationEnabled() && !d->widget->hasEditFocus())
1340
if (e->isAccepted())
1344
// default implementation for keys not handled by the widget when popup is open
1346
#ifdef QT_KEYPAD_NAVIGATION
1347
case Qt::Key_Select:
1348
if (!QApplication::keypadNavigationEnabled())
1351
case Qt::Key_Return:
1355
if (curIndex.isValid())
1356
d->_q_complete(curIndex);
1360
if (ke->modifiers() & Qt::AltModifier)
1364
case Qt::Key_Backtab:
1365
case Qt::Key_Escape:
1376
#ifdef QT_KEYPAD_NAVIGATION
1377
case QEvent::KeyRelease: {
1378
QKeyEvent *ke = static_cast<QKeyEvent *>(e);
1379
if (QApplication::keypadNavigationEnabled() && ke->key() == Qt::Key_Back) {
1380
// Send the event to the 'widget'. This is what we did for KeyPress, so we need
1381
// to do the same for KeyRelease, in case the widget's KeyPress event set
1382
// up something (such as a timer) that is relying on also receiving the
1383
// key release. I see this as a bug in Qt, and should really set it up for all
1384
// the affected keys. However, it is difficult to tell how this will affect
1385
// existing code, and I can't test for every combination!
1386
d->eatFocusOut = false;
1387
static_cast<QObject *>(d->widget)->event(ke);
1388
d->eatFocusOut = true;
1394
case QEvent::MouseButtonPress: {
1395
#ifdef QT_KEYPAD_NAVIGATION
1396
if (QApplication::keypadNavigationEnabled()) {
1397
// if we've clicked in the widget (or its descendant), let it handle the click
1398
QWidget *source = qobject_cast<QWidget *>(o);
1400
QPoint pos = source->mapToGlobal((static_cast<QMouseEvent *>(e))->pos());
1401
QWidget *target = QApplication::widgetAt(pos);
1402
if (target && (d->widget->isAncestorOf(target) ||
1403
target == d->widget)) {
1404
d->eatFocusOut = false;
1405
static_cast<QObject *>(target)->event(e);
1406
d->eatFocusOut = true;
1412
if (!d->popup->underMouse()) {
1419
case QEvent::InputMethod:
1420
case QEvent::ShortcutOverride:
1421
QApplication::sendEvent(d->widget, e);
1431
For KexiCompleter::PopupCompletion and KexiCompleter::UnfilteredPopupCompletion
1432
modes, calling this function displays the popup displaying the current
1433
completions. By default, if \a rect is not specified, the popup is displayed
1434
on the bottom of the widget(). If \a rect is specified the popup is
1435
displayed on the left edge of the rectangle.
1437
For KexiCompleter::InlineCompletion mode, the highlighted() signal is fired
1438
with the current completion.
1440
void KexiCompleter::complete(const QRect& rect)
1442
QModelIndex idx = d->proxy->currentIndex(false);
1443
d->hiddenBecauseNoMatch = false;
1444
if (d->mode == KexiCompleter::InlineCompletion) {
1446
d->_q_complete(idx, true);
1450
Q_ASSERT(d->widget != 0);
1451
if ((d->mode == KexiCompleter::PopupCompletion && !idx.isValid())
1452
|| (d->mode == KexiCompleter::UnfilteredPopupCompletion && d->proxy->rowCount() == 0)) {
1454
d->popup->hide(); // no suggestion, hide
1455
d->hiddenBecauseNoMatch = true;
1460
if (d->mode == KexiCompleter::UnfilteredPopupCompletion)
1461
d->setCurrentIndex(idx, false);
1464
d->popupRect = rect;
1468
Sets the current row to the \a row specified. Returns true if successful;
1469
otherwise returns false.
1471
This function may be used along with currentCompletion() to iterate
1472
through all the possible completions.
1474
\sa currentCompletion(), completionCount()
1476
bool KexiCompleter::setCurrentRow(int row)
1478
return d->proxy->setCurrentRow(row);
1482
Returns the current row.
1486
int KexiCompleter::currentRow() const
1488
return d->proxy->currentRow();
1492
Returns the number of completions for the current prefix. For an unsorted
1493
model with a large number of items this can be expensive. Use setCurrentRow()
1494
and currentCompletion() to iterate through all the completions.
1496
int KexiCompleter::completionCount() const
1498
return d->proxy->completionCount();
1502
\enum KexiCompleter::ModelSorting
1504
This enum specifies how the items in the model are sorted.
1506
\value UnsortedModel The model is unsorted.
1507
\value CaseSensitivelySortedModel The model is sorted case sensitively.
1508
\value CaseInsensitivelySortedModel The model is sorted case insensitively.
1510
\sa setModelSorting()
1514
\property KexiCompleter::modelSorting
1515
\brief the way the model is sorted
1517
By default, no assumptions are made about the order of the items
1518
in the model that provides the completions.
1520
If the model's data for the completionColumn() and completionRole() is sorted in
1521
ascending order, you can set this property to \l CaseSensitivelySortedModel
1522
or \l CaseInsensitivelySortedModel. On large models, this can lead to
1523
significant performance improvements because the completer object can
1524
then use a binary search algorithm instead of linear search algorithm.
1526
The sort order (i.e ascending or descending order) of the model is determined
1527
dynamically by inspecting the contents of the model.
1529
\bold{Note:} The performance improvements described above cannot take place
1530
when the completer's \l caseSensitivity is different to the case sensitivity
1531
used by the model's when sorting.
1533
\sa setCaseSensitivity(), KexiCompleter::ModelSorting
1535
void KexiCompleter::setModelSorting(KexiCompleter::ModelSorting sorting)
1537
if (d->sorting == sorting)
1539
d->sorting = sorting;
1540
d->proxy->createEngine();
1541
d->proxy->invalidate();
1544
KexiCompleter::ModelSorting KexiCompleter::modelSorting() const
1550
\property KexiCompleter::completionColumn
1551
\brief the column in the model in which completions are searched for.
1553
If the popup() is a QListView, it is automatically setup to display
1556
By default, the match column is 0.
1558
\sa completionRole, caseSensitivity
1560
void KexiCompleter::setCompletionColumn(int column)
1562
if (d->column == column)
1564
#ifndef QT_NO_LISTVIEW
1565
if (QListView *listView = qobject_cast<QListView *>(d->popup))
1566
listView->setModelColumn(column);
1569
d->proxy->invalidate();
1572
int KexiCompleter::completionColumn() const
1578
\property KexiCompleter::completionRole
1579
\brief the item role to be used to query the contents of items for matching.
1581
The default role is Qt::EditRole.
1583
\sa completionColumn, caseSensitivity
1585
void KexiCompleter::setCompletionRole(int role)
1587
if (d->role == role)
1590
d->proxy->invalidate();
1593
int KexiCompleter::completionRole() const
1599
\property KexiCompleter::wrapAround
1600
\brief the completions wrap around when navigating through items
1603
The default is true.
1605
void KexiCompleter::setWrapAround(bool wrap)
1607
if (d->wrap == wrap)
1612
bool KexiCompleter::wrapAround() const
1618
\property KexiCompleter::maxVisibleItems
1619
\brief the maximum allowed size on screen of the completer, measured in items
1622
By default, this property has a value of 7.
1624
int KexiCompleter::maxVisibleItems() const
1626
return d->maxVisibleItems;
1629
void KexiCompleter::setMaxVisibleItems(int maxItems)
1632
qWarning("KexiCompleter::setMaxVisibleItems: "
1633
"Invalid max visible items (%d) must be >= 0", maxItems);
1636
d->maxVisibleItems = maxItems;
1640
\property KexiCompleter::caseSensitivity
1641
\brief the case sensitivity of the matching
1643
The default is Qt::CaseSensitive.
1645
\sa substringCompletion, completionColumn, completionRole, modelSorting
1647
void KexiCompleter::setCaseSensitivity(Qt::CaseSensitivity cs)
1652
d->proxy->createEngine();
1653
d->proxy->invalidate();
1656
Qt::CaseSensitivity KexiCompleter::caseSensitivity() const
1662
\property KexiCompleter::substringCompletion
1663
\brief the completion uses any substring matching
1665
If true the completion uses any substring matching.
1666
If false prefix matching is used. The default is false.
1670
void KexiCompleter::setSubstringCompletion(bool substringCompletion)
1672
if (d->substringCompletion == substringCompletion)
1674
d->substringCompletion = substringCompletion;
1675
d->proxy->invalidate();
1678
bool KexiCompleter::substringCompletion() const
1680
return d->substringCompletion;
1684
\property KexiCompleter::completionPrefix
1685
\brief the completion prefix used to provide completions.
1687
The completionModel() is updated to reflect the list of possible
1688
matches for \a prefix.
1690
void KexiCompleter::setCompletionPrefix(const QString &prefix)
1693
d->proxy->filter(splitPath(prefix));
1696
QString KexiCompleter::completionPrefix() const
1702
Returns the model index of the current completion in the completionModel().
1704
\sa setCurrentRow(), currentCompletion(), model()
1706
QModelIndex KexiCompleter::currentIndex() const
1708
return d->proxy->currentIndex(false);
1712
Returns the current completion string. This includes the \l completionPrefix.
1713
When used alongside setCurrentRow(), it can be used to iterate through
1716
\sa setCurrentRow(), currentIndex()
1718
QString KexiCompleter::currentCompletion() const
1720
return pathFromIndex(d->proxy->currentIndex(true));
1724
Returns the completion model. The completion model is a read-only list model
1725
that contains all the possible matches for the current completion prefix.
1726
The completion model is auto-updated to reflect the current completions.
1728
\note The return value of this function is defined to be an QAbstractItemModel
1729
purely for generality. This actual kind of model returned is an instance of an
1730
QAbstractProxyModel subclass.
1732
\sa completionPrefix, model()
1734
QAbstractItemModel *KexiCompleter::completionModel() const
1740
Returns the path for the given \a index. The completer object uses this to
1741
obtain the completion text from the underlying model.
1743
The default implementation returns the \l{Qt::EditRole}{edit role} of the
1744
item for list models. It returns the absolute file path if the model is a
1750
QString KexiCompleter::pathFromIndex(const QModelIndex& index) const
1752
if (!index.isValid())
1755
QAbstractItemModel *sourceModel = d->proxy->sourceModel();
1758
bool isDirModel = false;
1759
bool isFsModel = false;
1760
#ifndef QT_NO_DIRMODEL
1761
isDirModel = qobject_cast<QDirModel *>(d->proxy->sourceModel()) != 0;
1763
#ifndef QT_NO_FILESYSTEMMODEL
1764
isFsModel = qobject_cast<QFileSystemModel *>(d->proxy->sourceModel()) != 0;
1766
if (!isDirModel && !isFsModel)
1767
return sourceModel->data(index, d->role).toString();
1769
QModelIndex idx = index;
1774
t = sourceModel->data(idx, Qt::EditRole).toString();
1775
#ifndef QT_NO_FILESYSTEMMODEL
1777
t = sourceModel->data(idx, QFileSystemModel::FileNameRole).toString();
1780
QModelIndex parent = idx.parent();
1781
idx = parent.sibling(parent.row(), index.column());
1782
} while (idx.isValid());
1784
#if (!defined(Q_OS_WIN) || defined(Q_OS_WINCE)) && !defined(Q_OS_SYMBIAN)
1785
if (list.count() == 1) // only the separator or some other text
1787
list[0].clear(); // the join below will provide the separator
1790
return list.join(QDir::separator());
1794
Splits the given \a path into strings that are used to match at each level
1797
The default implementation of splitPath() splits a file system path based on
1798
QDir::separator() when the sourceModel() is a QFileSystemModel.
1800
When used with list models, the first item in the returned list is used for
1803
\sa pathFromIndex(), {Handling Tree Models}
1805
QStringList KexiCompleter::splitPath(const QString& path) const
1807
bool isDirModel = false;
1808
bool isFsModel = false;
1809
#ifndef QT_NO_DIRMODEL
1810
isDirModel = qobject_cast<QDirModel *>(d->proxy->sourceModel()) != 0;
1812
#ifndef QT_NO_FILESYSTEMMODEL
1813
#ifdef QT_NO_DIRMODEL
1815
isFsModel = qobject_cast<QFileSystemModel *>(d->proxy->sourceModel()) != 0;
1818
if ((!isDirModel && !isFsModel) || path.isEmpty())
1819
return QStringList(completionPrefix());
1821
QString pathCopy = QDir::toNativeSeparators(path);
1822
QString sep = QDir::separator();
1823
#if defined(Q_OS_SYMBIAN)
1824
if (pathCopy == QLatin1String("\\"))
1825
return QStringList(pathCopy);
1826
#elif defined(Q_OS_WIN) && !defined(Q_OS_WINCE)
1827
if (pathCopy == QLatin1String("\\") || pathCopy == QLatin1String("\\\\"))
1828
return QStringList(pathCopy);
1829
QString doubleSlash(QLatin1String("\\\\"));
1830
if (pathCopy.startsWith(doubleSlash))
1831
pathCopy.remove(0, 2);
1833
doubleSlash.clear();
1836
QRegularExpression re(QLatin1Char('[') + QRegularExpression::escape(sep) + QLatin1Char(']'));
1837
QStringList parts = pathCopy.split(re);
1839
#if defined(Q_OS_SYMBIAN)
1841
#elif defined(Q_OS_WIN) && !defined(Q_OS_WINCE)
1842
if (!doubleSlash.isEmpty())
1843
parts[0].prepend(doubleSlash);
1845
if (pathCopy[0] == sep[0]) // readd the "/" at the beginning as the split removed it
1846
parts[0] = QDir::fromNativeSeparators(QString(sep[0]));
1853
\fn void KexiCompleter::activated(const QModelIndex& index)
1855
This signal is sent when an item in the popup() is activated by the user.
1856
(by clicking or pressing return). The item's \a index in the completionModel()
1862
\fn void KexiCompleter::activated(const QString &text)
1864
This signal is sent when an item in the popup() is activated by the user (by
1865
clicking or pressing return). The item's \a text is given.
1870
\fn void KexiCompleter::highlighted(const QModelIndex& index)
1872
This signal is sent when an item in the popup() is highlighted by
1873
the user. It is also sent if complete() is called with the completionMode()
1874
set to KexiCompleter::InlineCompletion. The item's \a index in the completionModel()
1879
\fn void KexiCompleter::highlighted(const QString &text)
1881
This signal is sent when an item in the popup() is highlighted by
1882
the user. It is also sent if complete() is called with the completionMode()
1883
set to KexiCompleter::InlineCompletion. The item's \a text is given.
1886
void KexiCompleter::_q_complete(const QModelIndex& index)
1888
d->_q_complete(index);
1891
void KexiCompleter::_q_completionSelected(const QItemSelection& selection)
1893
d->_q_completionSelected(selection);
1896
void KexiCompleter::_q_autoResizePopup()
1898
d->_q_autoResizePopup();
1901
void KexiCompleter::_q_fileSystemModelDirectoryLoaded(const QString& dir)
1903
d->_q_fileSystemModelDirectoryLoaded(dir);
1906
KexiCompletionModelPrivate::KexiCompletionModelPrivate(KexiCompletionModel *q)
1911
void KexiCompletionModelPrivate::_q_sourceModelDestroyed()
1913
q->setSourceModel(KexiAbstractItemModelPrivate::staticEmptyModel());
1916
#include "KexiCompleter.moc"
1918
#endif // QT_NO_COMPLETER