6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
109#include "QtWidgets/qscrollbar.h"
110#include "QtCore/qdir.h"
111#if QT_CONFIG(stringlistmodel)
112#include "QtCore/qstringlistmodel.h"
114#if QT_CONFIG(filesystemmodel)
115#include "QtGui/qfilesystemmodel.h"
117#include "QtWidgets/qheaderview.h"
118#if QT_CONFIG(listview)
119#include "QtWidgets/qlistview.h"
121#include "QtWidgets/qapplication.h"
122#include "QtGui/qevent.h"
123#include <private/qapplication_p.h>
124#include <private/qwidget_p.h>
125#if QT_CONFIG(lineedit)
126#include "QtWidgets/qlineedit.h"
128#include "QtCore/qdir.h"
132using namespace Qt::StringLiterals;
135 : QAbstractProxyModel(*
new QCompletionModelPrivate, parent),
143 Q_D(
const QCompletionModel);
144 return d->model->columnCount();
149 bool hadModel = (sourceModel() !=
nullptr);
152 QObject::disconnect(sourceModel(),
nullptr,
this,
nullptr);
154 QAbstractProxyModel::setSourceModel(source);
158 connect(source, SIGNAL(modelReset()),
this, SLOT(invalidate()));
160 connect(source, SIGNAL(layoutChanged()),
this, SLOT(invalidate()));
161 connect(source, SIGNAL(rowsInserted(QModelIndex,
int,
int)),
this, SLOT(rowsInserted()));
162 connect(source, SIGNAL(rowsRemoved(QModelIndex,
int,
int)),
this, SLOT(invalidate()));
163 connect(source, SIGNAL(columnsInserted(QModelIndex,
int,
int)),
this, SLOT(invalidate()));
164 connect(source, SIGNAL(columnsRemoved(QModelIndex,
int,
int)),
this, SLOT(invalidate()));
165 connect(source, SIGNAL(dataChanged(QModelIndex,QModelIndex)),
this, SLOT(invalidate()));
173 bool sortedEngine =
false;
174 if (c->filterMode == Qt::MatchStartsWith) {
175 switch (
c->sorting) {
176 case QCompleter::UnsortedModel:
177 sortedEngine =
false;
179 case QCompleter::CaseSensitivelySortedModel:
180 sortedEngine = c->cs == Qt::CaseSensitive;
182 case QCompleter::CaseInsensitivelySortedModel:
183 sortedEngine = c->cs == Qt::CaseInsensitive;
189 engine.reset(
new QSortedModelEngine(c));
191 engine.reset(
new QUnsortedModelEngine(c));
196 Q_D(
const QCompletionModel);
197 if (!index.isValid())
198 return engine->curParent;
201 QModelIndex parent = engine->curParent;
203 if (!engine->matchCount())
204 return QModelIndex();
205 Q_ASSERT(index.row() < engine->matchCount());
206 QIndexMapper& rootIndices = engine->historyMatch.indices;
207 if (index.row() < rootIndices
.count()) {
208 row = rootIndices[index.row()];
209 parent = QModelIndex();
211 row = engine->curMatch.indices[index.row() - rootIndices.count()];
217 return d->model->index(row, index.column(), parent);
223 return QModelIndex();
227 if (!engine->matchCount())
228 return QModelIndex();
230 QIndexMapper& rootIndices = engine->historyMatch.indices;
231 if (idx.parent().isValid()) {
232 if (idx.parent() != engine->curParent)
233 return QModelIndex();
236 if (row == -1 && engine->curParent.isValid())
237 return QModelIndex();
242 engine->filterOnDemand(idx.row() - indices.last());
247 return QModelIndex();
249 if (idx.parent() != engine->curParent)
250 return QModelIndex();
254 return createIndex(row, idx.column());
259 if (row < 0 || !engine->matchCount())
262 if (row >= engine->matchCount())
263 engine->filterOnDemand(row + 1 - engine->matchCount());
265 if (row >= engine->matchCount())
268 engine->curRow = row;
274 if (!engine->matchCount())
275 return QModelIndex();
277 int row = engine->curRow;
279 row = engine->curMatch.indices[engine->curRow];
281 QModelIndex idx = createIndex(row,
c->column);
284 return mapToSource(idx);
289 Q_D(
const QCompletionModel);
290 if (row < 0 || column < 0 || column >= columnCount(parent) || parent.isValid())
291 return QModelIndex();
294 if (!engine->matchCount())
295 return QModelIndex();
296 if (row >= engine->historyMatch.indices.count()) {
297 int want = row + 1 - engine->matchCount();
299 engine->filterOnDemand(want);
300 if (row >= engine->matchCount())
301 return QModelIndex();
304 if (row >= d->model->rowCount(engine->curParent))
305 return QModelIndex();
308 return createIndex(row, column);
313 if (!engine->matchCount())
316 engine->filterOnDemand(INT_MAX);
317 return engine->matchCount();
322 Q_D(
const QCompletionModel);
323 if (parent.isValid())
328 if (engine->curParts.size() != 1 && !engine->matchCount()
329 && !engine->curParent.isValid())
331 return d->model->rowCount(engine->curParent);
348 Q_D(
const QCompletionModel);
349 if (parent.isValid())
353 return d->model->hasChildren(mapToSource(parent));
355 if (!engine->matchCount())
363 Q_D(
const QCompletionModel);
364 return d->model->data(mapToSource(index), role);
369 QAbstractProxyModel::setSourceModel(
nullptr);
381 engine->cache.clear();
382 filter(engine->curParts);
387 Q_D(QCompletionModel);
389 engine->filter(parts);
392 if (d->model->canFetchMore(engine->curParent))
393 d->model->fetchMore(engine->curParent);
399 const QAbstractItemModel *model =
c->proxy->sourceModel();
401 if (curParts.isEmpty())
402 curParts.append(QString());
405 curParent = QModelIndex();
406 curMatch = QMatchData();
407 historyMatch = filterHistory();
413 for (
int i = 0; i < curParts.size() - 1; i++) {
414 QString part = curParts.at(i);
415 int emi = filter(part, parent, -1).exactMatchIndex;
418 parent = model->index(emi,
c->column, parent);
424 if (curParts.constLast().isEmpty())
425 curMatch = QMatchData(QIndexMapper(0, model->rowCount(curParent) - 1), -1,
false);
427 curMatch = filter(curParts.constLast(), curParent, 1);
428 curRow = curMatch.isValid() ? 0 : -1;
433 QAbstractItemModel *source =
c->proxy->sourceModel();
434 if (curParts.size() <= 1 || c->proxy->showAll || !source)
437#if QT_CONFIG(filesystemmodel)
438 const bool isFsModel = (qobject_cast<QFileSystemModel *>(source) !=
nullptr);
440 const bool isFsModel =
false;
447 for (
int i = 0; i < source->rowCount(); i++) {
448 QString str = source->index(i,
c->column).data().toString();
449 if (str.startsWith(c->prefix, c->cs)
450#if !defined(Q_OS_WIN)
451 && (!isFsModel || QDir::toNativeSeparators(str) != QDir::separator())
465 const auto cit =
cache.find(parent);
466 if (cit ==
cache.end())
470 const auto mapEnd = map.end();
472 QString key = c->cs == Qt::CaseInsensitive ? part.toLower() : part;
474 while (!key.isEmpty()) {
476 const auto it = map.find(key);
491 const auto cit =
cache.find(parent);
492 if (cit ==
cache.end())
497 const QString key = c->cs == Qt::CaseInsensitive ? part.toLower() : part;
499 const auto it = map.find(key);
510 if (c->filterMode == Qt::MatchEndsWith)
513 cost =
cost + m.indices.cost() - old.indices.cost();
514 if (
cost *
sizeof(
int) > 1024 * 1024) {
515 QMap<QModelIndex, CacheItem>::iterator it1 =
cache.begin();
516 while (it1 !=
cache.end()) {
518 int sz = ci.size()/2;
519 QMap<QString, QMatchData>::iterator it2 = ci.begin();
521 while (it2 != ci.end() && i < sz) {
522 cost -= it2.value().indices.cost();
526 if (ci.size() == 0) {
527 it1 =
cache.erase(it1);
534 if (c->cs == Qt::CaseInsensitive)
535 part = std::move(part).toLower();
536 cache[parent][part] = m;
542 const QAbstractItemModel *model =
c->proxy->sourceModel();
544 if (c->cs == Qt::CaseInsensitive)
545 part = std::move(part).toLower();
550 int to = model->rowCount(parent) - 1;
552 const CacheItem::const_iterator it = map.lowerBound(part);
555 for (CacheItem::const_iterator it1 = it; it1 != map.constBegin();) {
559 if (order == Qt::AscendingOrder) {
560 from = value.indices.last() + 1;
562 to = value.indices.first() - 1;
569 for(CacheItem::const_iterator it2 = it; it2 != map.constEnd(); ++it2) {
571 if (value
.isValid() && !it2.key().startsWith(part)) {
572 if (order == Qt::AscendingOrder) {
573 to = value.indices.first() - 1;
575 from = value.indices.first() + 1;
586 const QAbstractItemModel *model =
c->proxy->sourceModel();
588 int rowCount = model->rowCount(parent);
590 return Qt::AscendingOrder;
591 QString first = model->data(model->index(0,
c->column, parent),
c->role).toString();
592 QString last = model->data(model->index(rowCount - 1,
c->column, parent),
c->role).toString();
593 return QString::compare(first, last, c->cs) <= 0 ? Qt::AscendingOrder : Qt::DescendingOrder;
598 const QAbstractItemModel *model =
c->proxy->sourceModel();
601 if (lookupCache(part, parent, &hint))
605 Qt::SortOrder order = sortOrder(parent);
607 if (matchHint(part, parent, &hint)) {
610 indices = hint.indices;
612 indices = indexHint(part, parent, order);
616 int high = indices
.to() + 1;
619 QModelIndex probeIndex;
622 while (high - low > 1)
624 probe = (high + low) / 2;
625 probeIndex = model->index(probe,
c->column, parent);
626 probeData = model->data(probeIndex,
c->role).toString();
627 const int cmp = QString::compare(probeData, part, c->cs);
628 if ((order == Qt::AscendingOrder && cmp >= 0)
629 || (order == Qt::DescendingOrder && cmp < 0)) {
636 if ((order == Qt::AscendingOrder && low == indices.to())
637 || (order == Qt::DescendingOrder && high == indices.from())) {
638 saveInCache(part, parent, QMatchData());
642 probeIndex = model->index(order == Qt::AscendingOrder ? low+1 : high-1, c->column, parent);
643 probeData = model->data(probeIndex,
c->role).toString();
644 if (!probeData.startsWith(part,
c->cs)) {
645 saveInCache(part, parent, QMatchData());
649 const bool exactMatch = QString::compare(probeData, part, c->cs) == 0;
650 int emi = exactMatch ? (order == Qt::AscendingOrder ? low+1 : high-1) : -1;
654 if (order == Qt::AscendingOrder) {
656 high = indices
.to() + 1;
664 while (high - low > 1)
666 probe = (high + low) / 2;
667 probeIndex = model->index(probe,
c->column, parent);
668 probeData = model->data(probeIndex,
c->role).toString();
669 const bool startsWith = probeData.startsWith(part,
c->cs);
670 if ((order == Qt::AscendingOrder && startsWith)
671 || (order == Qt::DescendingOrder && !startsWith)) {
678 QMatchData m(order == Qt::AscendingOrder ? QIndexMapper(from, high - 1) : QIndexMapper(low+1, to), emi,
false);
679 saveInCache(part, parent, m);
689 const QAbstractItemModel *model =
c->proxy->sourceModel();
692 for (i = 0; i < indices
.count() && count != n; ++i) {
693 QModelIndex idx = model->index(indices
[i
],
c->column, parent);
695 if (!(model->flags(idx) & Qt::ItemIsSelectable))
698 QString data = model->data(idx,
c->role).toString();
700 switch (
c->filterMode) {
701 case Qt::MatchStartsWith:
702 if (!data.startsWith(str,
c->cs))
705 case Qt::MatchContains:
706 if (!data.contains(str,
c->cs))
709 case Qt::MatchEndsWith:
710 if (!data.endsWith(str,
c->cs))
713 case Qt::MatchExactly:
714 case Qt::MatchFixedString:
715 case Qt::MatchCaseSensitive:
716 case Qt::MatchRegularExpression:
717 case Qt::MatchWildcard:
719 case Qt::MatchRecursive:
723 m->indices.append(indices
[i
]);
725 if (m->exactMatchIndex == -1 && QString::compare(data, str, c->cs) == 0) {
737 if (!curMatch.partial)
740 const QAbstractItemModel *model =
c->proxy->sourceModel();
741 int lastRow = model->rowCount(curParent) - 1;
743 int lastIndex = buildIndices(curParts.constLast(), curParent, n, im, &curMatch);
744 curMatch.partial = (lastRow != lastIndex);
745 saveInCache(curParts.constLast(), curParent, curMatch);
756 const QAbstractItemModel *model =
c->proxy->sourceModel();
757 bool foundInCache = lookupCache(part, parent, &m);
760 if (matchHint(part, parent, &hint) && !hint.isValid())
765 const int lastRow = model->rowCount(parent) - 1;
767 int lastIndex = buildIndices(part, parent, n, all, &m);
771 buildIndices(part, parent, INT_MAX, hint.indices, &m);
776 const int lastRow = model->rowCount(parent) - 1;
778 int want = n == -1 ? -1 : n - m.indices.count();
779 int lastIndex = buildIndices(part, parent, want, rest, &m);
784 saveInCache(part, parent, m);
793 filterMode(Qt::MatchStartsWith),
794 cs(Qt::CaseSensitive),
798 sorting(QCompleter::UnsortedModel),
808 proxy =
new QCompletionModel(
this, q);
809 QObject::connect(proxy, SIGNAL(rowsAdded()), q, SLOT(_q_autoResizePopup()));
811#if !QT_CONFIG(listview)
812 q->setCompletionMode(QCompleter::InlineCompletion);
814 q->setCompletionMode(QCompleter::PopupCompletion);
824 popup->selectionModel()->setCurrentIndex(index, QItemSelectionModel::NoUpdate);
826 if (!index.isValid())
827 popup->selectionModel()->clear();
829 popup->selectionModel()->setCurrentIndex(index, QItemSelectionModel::Select
830 | QItemSelectionModel::Rows);
832 index =
popup->selectionModel()->currentIndex();
833 if (!index.isValid())
834 popup->scrollToTop();
836 popup->scrollTo(index, QAbstractItemView::PositionAtTop);
842 if (
const auto indexes = selection.indexes(); !indexes.isEmpty())
843 index = indexes.first();
845 _q_complete(index,
true);
853 if (!index.isValid() || (!
proxy->showAll && (index.row() >=
proxy->engine->matchCount()))) {
855 index = QModelIndex();
857 if (!(index.flags() & Qt::ItemIsEnabled))
859 QModelIndex si =
proxy->mapToSource(index);
860 si = si.sibling(si.row(),
column);
861 completion = q->pathFromIndex(si);
862#if QT_CONFIG(filesystemmodel)
864 if (mode == QCompleter::InlineCompletion) {
865 if (qobject_cast<QFileSystemModel *>(proxy->sourceModel()) && QFileInfo(completion).isDir())
866 completion += QDir::separator();
872 emit q->highlighted(index);
873 emit q->highlighted(completion);
875 emit q->activated(index);
876 emit q->activated(completion);
884 showPopup(popupRect);
889 const QRect screen = widget->screen()->availableGeometry();
890 Qt::LayoutDirection dir = widget->layoutDirection();
894 QScrollBar *hsb =
popup->horizontalScrollBar();
895 if (hsb && hsb->isVisible())
896 h +=
popup->horizontalScrollBar()->sizeHint().height();
898 if (rect.isValid()) {
901 pos = widget->mapToGlobal(dir == Qt::RightToLeft ? rect.bottomRight() : rect.bottomLeft());
903 rh = widget->height();
904 pos = widget->mapToGlobal(QPoint(0, widget->height() - 2));
908 if (w > screen.width())
910 if ((pos.x() + w) > (screen.x() + screen.width()))
911 pos.setX(screen.x() + screen.width() - w);
912 if (pos.x() < screen.x())
913 pos.setX(screen.x());
915 int top = pos.y() - rh - screen.top() + 2;
916 int bottom = screen.bottom() - pos.y();
917 h = qMax(h,
popup->minimumHeight());
919 h = qMin(qMax(top, bottom), h);
922 pos.setY(pos.y() - h - rh + 2);
925 popup->setGeometry(pos.x(), pos.y(), w, h);
927 if (!
popup->isVisible()) {
930 popup->windowHandle()->setTransientParent(widget->window()->windowHandle());
936#if QT_CONFIG(filesystemmodel)
937static bool isRoot(
const QFileSystemModel *model,
const QString &path)
939 const auto index = model->index(path);
940 return index.isValid() && model->fileInfo(index).isRoot();
943static bool completeOnLoaded(
const QFileSystemModel *model,
944 const QString &nativePrefix,
946 Qt::CaseSensitivity caseSensitivity)
948 const auto pathSize = path.size();
949 const auto prefixSize = nativePrefix.size();
950 if (prefixSize < pathSize)
952 const QString prefix = QDir::fromNativeSeparators(nativePrefix);
953 if (prefixSize == pathSize)
954 return path.compare(prefix, caseSensitivity) == 0 && isRoot(model, path);
956 const auto separator = u'/';
957 return prefix.startsWith(path, caseSensitivity) && prefix.at(pathSize) == separator
958 && !QStringView{prefix}.right(prefixSize - pathSize - 1).contains(separator);
961void QCompleterPrivate::_q_fileSystemModelDirectoryLoaded(
const QString &path)
968 if (hiddenBecauseNoMatch && widget) {
969 if (
auto model = qobject_cast<
const QFileSystemModel *>(proxy->sourceModel())) {
970 if (completeOnLoaded(model, prefix, path, cs))
980
981
982QCompleter::QCompleter(QObject *parent)
983: QObject(*
new QCompleterPrivate(), parent)
990
991
992
993QCompleter::QCompleter(QAbstractItemModel *model, QObject *parent)
994 : QObject(*
new QCompleterPrivate(), parent)
1000#if QT_CONFIG(stringlistmodel)
1002
1003
1004
1005QCompleter::QCompleter(
const QStringList& list, QObject *parent)
1006: QObject(*
new QCompleterPrivate(), parent)
1009 d->init(
new QStringListModel(list,
this));
1014
1015
1016QCompleter::~QCompleter()
1021
1022
1023
1024
1025
1026
1027
1028
1029void QCompleter::setWidget(QWidget *widget)
1032 if (widget == d->widget)
1036 d->widget->removeEventFilter(
this);
1039 d->widget->installEventFilter(
this);
1043 d->popup->setFocusProxy(d->widget);
1048
1049
1050
1051
1052QWidget *QCompleter::widget()
const
1054 Q_D(
const QCompleter);
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069void QCompleter::setModel(QAbstractItemModel *model)
1072 QAbstractItemModel *oldModel = d->proxy->sourceModel();
1073 if (oldModel == model)
1075#if QT_CONFIG(filesystemmodel)
1076 if (qobject_cast<
const QFileSystemModel *>(oldModel))
1077 setCompletionRole(Qt::EditRole);
1079 d->proxy->setSourceModel(model);
1082 if (oldModel && oldModel->QObject::parent() ==
this)
1084#if QT_CONFIG(filesystemmodel)
1085 QFileSystemModel *fsModel = qobject_cast<QFileSystemModel *>(model);
1087#if defined(Q_OS_WIN)
1088 setCaseSensitivity(Qt::CaseInsensitive);
1090 setCaseSensitivity(Qt::CaseSensitive);
1092 setCompletionRole(QFileSystemModel::FileNameRole);
1093 connect(fsModel, SIGNAL(directoryLoaded(QString)),
this, SLOT(_q_fileSystemModelDirectoryLoaded(QString)));
1099
1100
1101
1102
1103QAbstractItemModel *QCompleter::model()
const
1105 Q_D(
const QCompleter);
1106 return d->proxy->sourceModel();
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1122
1123
1124
1125
1126
1127void QCompleter::setCompletionMode(QCompleter::CompletionMode mode)
1131 d->proxy->setFiltered(mode != QCompleter::UnfilteredPopupCompletion);
1133 if (mode == QCompleter::InlineCompletion) {
1135 d->widget->removeEventFilter(
this);
1137 d->popup->deleteLater();
1142 d->widget->installEventFilter(
this);
1146QCompleter::CompletionMode QCompleter::completionMode()
const
1148 Q_D(
const QCompleter);
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1172void QCompleter::setFilterMode(Qt::MatchFlags filterMode)
1176 if (d->filterMode == filterMode)
1179 if (Q_UNLIKELY(filterMode != Qt::MatchStartsWith &&
1180 filterMode != Qt::MatchContains &&
1181 filterMode != Qt::MatchEndsWith)) {
1182 qWarning(
"Unhandled QCompleter::filterMode flag is used.");
1186 d->filterMode = filterMode;
1187 d->proxy->createEngine();
1188 d->proxy->invalidate();
1191Qt::MatchFlags QCompleter::filterMode()
const
1193 Q_D(
const QCompleter);
1194 return d->filterMode;
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212void QCompleter::setPopup(QAbstractItemView *popup)
1216 if (popup == d->popup)
1220 const Qt::FocusPolicy origPolicy = d->widget ? d->widget->focusPolicy()
1225 QObject::disconnect(d->popup->selectionModel(),
nullptr,
this,
nullptr);
1226 QObject::disconnect(d->popup,
nullptr,
this,
nullptr);
1232 if (d->popup->model() != d->proxy)
1233 d->popup->setModel(d->proxy);
1241 d->popup->setParent(
nullptr);
1242 d->popup->setWindowFlag(Qt::Popup);
1243 d->popup->setFocusPolicy(Qt::NoFocus);
1245 d->widget->setFocusPolicy(origPolicy);
1247 d->popup->setFocusProxy(d->widget);
1248 d->popup->installEventFilter(
this);
1249 d->popup->setItemDelegate(
new QCompleterItemDelegate(d->popup));
1250#if QT_CONFIG(listview)
1251 if (QListView *listView = qobject_cast<QListView *>(d->popup)) {
1252 listView->setModelColumn(d->column);
1256 QObject::connect(d->popup, SIGNAL(clicked(QModelIndex)),
1257 this, SLOT(_q_complete(QModelIndex)));
1258 QObject::connect(
this, SIGNAL(activated(QModelIndex)),
1259 d->popup, SLOT(hide()));
1261 QObject::connect(d->popup->selectionModel(), SIGNAL(selectionChanged(QItemSelection,QItemSelection)),
1262 this, SLOT(_q_completionSelected(QItemSelection)));
1266
1267
1268
1269
1270QAbstractItemView *QCompleter::popup()
const
1272 Q_D(
const QCompleter);
1273#if QT_CONFIG(listview)
1274 if (!d->popup && completionMode() != QCompleter::InlineCompletion) {
1275 QListView *listView =
new QListView;
1276 listView->setEditTriggers(QAbstractItemView::NoEditTriggers);
1277 listView->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
1278 listView->setSelectionBehavior(QAbstractItemView::SelectRows);
1279 listView->setSelectionMode(QAbstractItemView::SingleSelection);
1280 listView->setModelColumn(d->column);
1281 QCompleter *that =
const_cast<QCompleter*>(
this);
1282 that->setPopup(listView);
1289
1290
1291bool QCompleter::event(QEvent *ev)
1293 return QObject::event(ev);
1297
1298
1299bool QCompleter::eventFilter(QObject *o, QEvent *e)
1303 if (o == d->widget) {
1304 switch (e->type()) {
1305 case QEvent::FocusOut:
1306 if (d->eatFocusOut) {
1307 d->hiddenBecauseNoMatch =
false;
1308 if (d->popup && d->popup->isVisible())
1322 return QObject::eventFilter(o, e);
1325 switch (e->type()) {
1326 case QEvent::KeyPress: {
1327 QKeyEvent *ke =
static_cast<QKeyEvent *>(e);
1329 QModelIndex curIndex = d->popup->currentIndex();
1330 QModelIndexList selList = d->popup->selectionModel()->selectedIndexes();
1332 const int key = ke->key();
1334 if ((key == Qt::Key_Up || key == Qt::Key_Down) && selList.isEmpty() && curIndex.isValid()
1335 && d->mode == QCompleter::UnfilteredPopupCompletion) {
1336 d->setCurrentIndex(curIndex);
1345 if (ke->modifiers() & Qt::ControlModifier)
1350 if (!curIndex.isValid()) {
1351 int rowCount = d->proxy->rowCount();
1352 QModelIndex lastIndex = d->proxy->index(rowCount - 1, d->column);
1353 d->setCurrentIndex(lastIndex);
1355 }
else if (curIndex.row() == 0) {
1357 d->setCurrentIndex(QModelIndex());
1363 if (!curIndex.isValid()) {
1364 QModelIndex firstIndex = d->proxy->index(0, d->column);
1365 d->setCurrentIndex(firstIndex);
1367 }
else if (curIndex.row() == d->proxy->rowCount() - 1) {
1369 d->setCurrentIndex(QModelIndex());
1374 case Qt::Key_PageUp:
1375 case Qt::Key_PageDown:
1382 d->eatFocusOut =
false;
1383 (
static_cast<QObject *>(d->widget))->event(ke);
1384 d->eatFocusOut =
true;
1386 if (!d->widget || e->isAccepted() || !d->popup->isVisible()) {
1388 if (d->widget && (!d->widget->hasFocus()
1389#ifdef QT_KEYPAD_NAVIGATION
1390 || (QApplicationPrivate::keypadNavigationEnabled() && !d->widget->hasEditFocus())
1394 if (e->isAccepted())
1399#if QT_CONFIG(shortcut)
1400 if (ke->matches(QKeySequence::Cancel)) {
1406#ifdef QT_KEYPAD_NAVIGATION
1407 case Qt::Key_Select:
1408 if (!QApplicationPrivate::keypadNavigationEnabled())
1411 case Qt::Key_Return:
1415 if (curIndex.isValid())
1416 d->_q_complete(curIndex);
1420 if (ke->modifiers() & Qt::AltModifier)
1424 case Qt::Key_Backtab:
1435#ifdef QT_KEYPAD_NAVIGATION
1436 case QEvent::KeyRelease: {
1438 QApplicationPrivate::keypadNavigationEnabled() && ke->key() == Qt::Key_Back) {
1439 QKeyEvent *ke =
static_cast<QKeyEvent *>(e);
1446 d->eatFocusOut =
false;
1447 static_cast<QObject *>(d->widget)->event(ke);
1448 d->eatFocusOut =
true;
1454 case QEvent::MouseButtonPress: {
1455#ifdef QT_KEYPAD_NAVIGATION
1457 && QApplicationPrivate::keypadNavigationEnabled()) {
1459 QWidget *source = qobject_cast<QWidget *>(o);
1461 QPoint pos = source->mapToGlobal((
static_cast<QMouseEvent *>(e))->pos());
1462 QWidget *target = QApplication::widgetAt(pos);
1463 if (target && (d->widget->isAncestorOf(target) ||
1464 target == d->widget)) {
1465 d->eatFocusOut =
false;
1466 static_cast<QObject *>(target)->event(e);
1467 d->eatFocusOut =
true;
1473 if (!d->popup->underMouse()) {
1474 if (!QGuiApplicationPrivate::maybeForwardEventToVirtualKeyboard(e))
1481 case QEvent::MouseButtonRelease:
1482 QGuiApplicationPrivate::maybeForwardEventToVirtualKeyboard(e);
1484 case QEvent::InputMethod:
1485 case QEvent::ShortcutOverride:
1487 QCoreApplication::sendEvent(d->widget, e);
1497
1498
1499
1500
1501
1502
1503
1504
1505
1506void QCompleter::complete(
const QRect& rect)
1509 QModelIndex idx = d->proxy->currentIndex(
false);
1510 d->hiddenBecauseNoMatch =
false;
1511 if (d->mode == QCompleter::InlineCompletion) {
1513 d->_q_complete(idx,
true);
1517 Q_ASSERT(d->widget);
1518 if ((d->mode == QCompleter::PopupCompletion && !idx.isValid())
1519 || (d->mode == QCompleter::UnfilteredPopupCompletion && d->proxy->rowCount() == 0)) {
1522 d->hiddenBecauseNoMatch =
true;
1527 if (d->mode == QCompleter::UnfilteredPopupCompletion)
1528 d->setCurrentIndex(idx,
false);
1531 d->popupRect = rect;
1535
1536
1537
1538
1539
1540
1541
1542
1543bool QCompleter::setCurrentRow(
int row)
1546 return d->proxy->setCurrentRow(row);
1550
1551
1552
1553
1554int QCompleter::currentRow()
const
1556 Q_D(
const QCompleter);
1557 return d->proxy->currentRow();
1561
1562
1563
1564
1565int QCompleter::completionCount()
const
1567 Q_D(
const QCompleter);
1568 return d->proxy->completionCount();
1572
1573
1574
1575
1576
1577
1578
1579
1580
1581
1584
1585
1586
1587
1588
1589
1590
1591
1592
1593
1594
1595
1596
1597
1598
1599
1600
1601
1602
1603
1604
1605void QCompleter::setModelSorting(QCompleter::ModelSorting sorting)
1608 if (d->sorting == sorting)
1610 d->sorting = sorting;
1611 d->proxy->createEngine();
1612 d->proxy->invalidate();
1615QCompleter::ModelSorting QCompleter::modelSorting()
const
1617 Q_D(
const QCompleter);
1622
1623
1624
1625
1626
1627
1628
1629
1630
1631
1632void QCompleter::setCompletionColumn(
int column)
1635 if (d->column == column)
1637#if QT_CONFIG(listview)
1638 if (QListView *listView = qobject_cast<QListView *>(d->popup))
1639 listView->setModelColumn(column);
1642 d->proxy->invalidate();
1645int QCompleter::completionColumn()
const
1647 Q_D(
const QCompleter);
1652
1653
1654
1655
1656
1657
1658
1659void QCompleter::setCompletionRole(
int role)
1662 if (d->role == role)
1665 d->proxy->invalidate();
1668int QCompleter::completionRole()
const
1670 Q_D(
const QCompleter);
1675
1676
1677
1678
1679
1680
1681void QCompleter::setWrapAround(
bool wrap)
1684 if (d->wrap == wrap)
1689bool QCompleter::wrapAround()
const
1691 Q_D(
const QCompleter);
1696
1697
1698
1699
1700
1701
1702int QCompleter::maxVisibleItems()
const
1704 Q_D(
const QCompleter);
1705 return d->maxVisibleItems;
1708void QCompleter::setMaxVisibleItems(
int maxItems)
1711 if (Q_UNLIKELY(maxItems < 0)) {
1712 qWarning(
"QCompleter::setMaxVisibleItems: "
1713 "Invalid max visible items (%d) must be >= 0", maxItems);
1716 d->maxVisibleItems = maxItems;
1720
1721
1722
1723
1724
1725
1726
1727void QCompleter::setCaseSensitivity(Qt::CaseSensitivity cs)
1733 d->proxy->createEngine();
1734 d->proxy->invalidate();
1737Qt::CaseSensitivity QCompleter::caseSensitivity()
const
1739 Q_D(
const QCompleter);
1744
1745
1746
1747
1748
1749
1750void QCompleter::setCompletionPrefix(
const QString &prefix)
1754 d->proxy->filter(splitPath(prefix));
1757QString QCompleter::completionPrefix()
const
1759 Q_D(
const QCompleter);
1764
1765
1766
1767
1768QModelIndex QCompleter::currentIndex()
const
1770 Q_D(
const QCompleter);
1771 return d->proxy->currentIndex(
false);
1775
1776
1777
1778
1779
1780
1781QString QCompleter::currentCompletion()
const
1783 Q_D(
const QCompleter);
1784 return pathFromIndex(d->proxy->currentIndex(
true));
1788
1789
1790
1791
1792
1793
1794
1795
1796
1797
1798QAbstractItemModel *QCompleter::completionModel()
const
1800 Q_D(
const QCompleter);
1805
1806
1807
1808
1809
1810
1811
1812
1813
1815QString QCompleter::pathFromIndex(
const QModelIndex& index)
const
1817 Q_D(
const QCompleter);
1818 if (!index.isValid())
1821 QAbstractItemModel *sourceModel = d->proxy->sourceModel();
1824 bool isFsModel =
false;
1825#if QT_CONFIG(filesystemmodel)
1826 isFsModel = qobject_cast<QFileSystemModel *>(d->proxy->sourceModel()) !=
nullptr;
1829 return sourceModel->data(index, d->role).toString();
1831 QModelIndex idx = index;
1835#if QT_CONFIG(filesystemmodel)
1836 t = sourceModel->data(idx, QFileSystemModel::FileNameRole).toString();
1839 QModelIndex parent = idx.parent();
1840 idx = parent.sibling(parent.row(), index.column());
1841 }
while (idx.isValid());
1843#if !defined(Q_OS_WIN)
1844 if (list.size() == 1)
1849 return list.join(QDir::separator());
1853
1854
1855
1856
1857
1858
1859
1860
1861
1862
1863
1864QStringList QCompleter::splitPath(
const QString& path)
const
1866 bool isFsModel =
false;
1867#if QT_CONFIG(filesystemmodel)
1868 Q_D(
const QCompleter);
1869 isFsModel = qobject_cast<QFileSystemModel *>(d->proxy->sourceModel()) !=
nullptr;
1872 if (!isFsModel || path.isEmpty())
1873 return QStringList(completionPrefix());
1875 QString pathCopy = QDir::toNativeSeparators(path);
1876#if defined(Q_OS_WIN)
1877 if (pathCopy ==
"\\"_L1 || pathCopy ==
"\\\\"_L1)
1878 return QStringList(pathCopy);
1879 const bool startsWithDoubleSlash = pathCopy.startsWith(
"\\\\"_L1);
1880 if (startsWithDoubleSlash)
1881 pathCopy = pathCopy.mid(2);
1884 const QChar sep = QDir::separator();
1885 QStringList parts = pathCopy.split(sep);
1887#if defined(Q_OS_WIN)
1888 if (startsWithDoubleSlash)
1889 parts[0].prepend(
"\\\\"_L1);
1891 if (pathCopy[0] == sep)
1899
1900
1901
1902
1903
1904
1905
1908
1909
1910
1911
1912
1913
1916
1917
1918
1919
1920
1921
1922
1925
1926
1927
1928
1929
1930
1934#include "moc_qcompleter.cpp"
1936#include "moc_qcompleter_p.cpp"
void setCurrentIndex(QModelIndex, bool=true)
void _q_fileSystemModelDirectoryLoaded(const QString &path)
bool hiddenBecauseNoMatch
void _q_completionSelected(const QItemSelection &)
void _q_autoResizePopup()
void init(QAbstractItemModel *model=nullptr)
QAbstractItemView * popup
void _q_complete(QModelIndex, bool=false)
void showPopup(const QRect &)
void saveInCache(QString, const QModelIndex &, const QMatchData &)
void filter(const QStringList &parts)
QMap< QString, QMatchData > CacheItem
bool matchHint(const QString &part, const QModelIndex &parent, QMatchData *m) const
bool lookupCache(const QString &part, const QModelIndex &parent, QMatchData *m) const
QMatchData filterHistory()
QModelIndex index(int row, int column, const QModelIndex &=QModelIndex()) const override
Returns the index of the item in the model specified by the given row, column and parent index.
int rowCount(const QModelIndex &index=QModelIndex()) const override
Returns the number of rows under the given parent.
QVariant data(const QModelIndex &index, int role=Qt::DisplayRole) const override
\reimp
int completionCount() const
int columnCount(const QModelIndex &index=QModelIndex()) const override
Returns the number of columns for the children of the given parent.
QModelIndex mapFromSource(const QModelIndex &sourceIndex) const override
Reimplement this function to return the model index in the proxy model that corresponds to the source...
bool hasChildren(const QModelIndex &parent=QModelIndex()) const override
\reimp
QModelIndex currentIndex(bool) const
void filter(const QStringList &parts)
QModelIndex mapToSource(const QModelIndex &proxyIndex) const override
Reimplement this function to return the model index in the source model that corresponds to the proxy...
void setSourceModel(QAbstractItemModel *sourceModel) override
Sets the given sourceModel to be processed by the proxy model.
bool setCurrentRow(int row)
int operator[](int index) const
QIndexMapper(int f, int t)
QMatchData filter(const QString &, const QModelIndex &, int) override
Qt::SortOrder sortOrder(const QModelIndex &) const
QIndexMapper indexHint(QString, const QModelIndex &, Qt::SortOrder)
void filterOnDemand(int) override
QMatchData filter(const QString &, const QModelIndex &, int) override
QMatchData(const QIndexMapper &indices, int em, bool p)