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#include <qpa/qplatformwindow.h>
126#include <qpa/qplatformwindow_p.h>
127#if QT_CONFIG(lineedit)
128#include "QtWidgets/qlineedit.h"
130#include "QtCore/qdir.h"
134using namespace Qt::StringLiterals;
137 : QAbstractProxyModel(*
new QCompletionModelPrivate, parent),
145 Q_D(
const QCompletionModel);
146 return d->model->columnCount();
151 bool hadModel = (sourceModel() !=
nullptr);
154 QObject::disconnect(sourceModel(),
nullptr,
this,
nullptr);
156 QAbstractProxyModel::setSourceModel(source);
160 connect(source, SIGNAL(modelReset()),
this, SLOT(invalidate()));
162 connect(source, SIGNAL(layoutChanged()),
this, SLOT(invalidate()));
163 connect(source, SIGNAL(rowsInserted(QModelIndex,
int,
int)),
this, SLOT(rowsInserted()));
164 connect(source, SIGNAL(rowsRemoved(QModelIndex,
int,
int)),
this, SLOT(invalidate()));
165 connect(source, SIGNAL(columnsInserted(QModelIndex,
int,
int)),
this, SLOT(invalidate()));
166 connect(source, SIGNAL(columnsRemoved(QModelIndex,
int,
int)),
this, SLOT(invalidate()));
167 connect(source, SIGNAL(dataChanged(QModelIndex,QModelIndex)),
this, SLOT(invalidate()));
175 bool sortedEngine =
false;
176 if (c->filterMode == Qt::MatchStartsWith) {
177 switch (
c->sorting) {
178 case QCompleter::UnsortedModel:
179 sortedEngine =
false;
181 case QCompleter::CaseSensitivelySortedModel:
182 sortedEngine = c->cs == Qt::CaseSensitive;
184 case QCompleter::CaseInsensitivelySortedModel:
185 sortedEngine = c->cs == Qt::CaseInsensitive;
191 engine.reset(
new QSortedModelEngine(c));
193 engine.reset(
new QUnsortedModelEngine(c));
198 Q_D(
const QCompletionModel);
199 if (!index.isValid())
200 return engine->curParent;
203 QModelIndex parent = engine->curParent;
205 if (!engine->matchCount())
206 return QModelIndex();
207 Q_ASSERT(index.row() < engine->matchCount());
208 QIndexMapper& rootIndices = engine->historyMatch.indices;
209 if (index.row() < rootIndices
.count()) {
210 row = rootIndices[index.row()];
211 parent = QModelIndex();
213 row = engine->curMatch.indices[index.row() - rootIndices.count()];
219 return d->model->index(row, index.column(), parent);
225 return QModelIndex();
229 if (!engine->matchCount())
230 return QModelIndex();
232 QIndexMapper& rootIndices = engine->historyMatch.indices;
233 if (idx.parent().isValid()) {
234 if (idx.parent() != engine->curParent)
235 return QModelIndex();
238 if (row == -1 && engine->curParent.isValid())
239 return QModelIndex();
244 engine->filterOnDemand(idx.row() - indices.last());
249 return QModelIndex();
251 if (idx.parent() != engine->curParent)
252 return QModelIndex();
256 return createIndex(row, idx.column());
261 if (row < 0 || !engine->matchCount())
264 if (row >= engine->matchCount())
265 engine->filterOnDemand(row + 1 - engine->matchCount());
267 if (row >= engine->matchCount())
270 engine->curRow = row;
276 if (!engine->matchCount())
277 return QModelIndex();
279 int row = engine->curRow;
281 row = engine->curMatch.indices[engine->curRow];
283 QModelIndex idx = createIndex(row,
c->column);
286 return mapToSource(idx);
291 Q_D(
const QCompletionModel);
292 if (row < 0 || column < 0 || column >= columnCount(parent) || parent.isValid())
293 return QModelIndex();
296 if (!engine->matchCount())
297 return QModelIndex();
298 if (row >= engine->historyMatch.indices.count()) {
299 int want = row + 1 - engine->matchCount();
301 engine->filterOnDemand(want);
302 if (row >= engine->matchCount())
303 return QModelIndex();
306 if (row >= d->model->rowCount(engine->curParent))
307 return QModelIndex();
310 return createIndex(row, column);
315 if (!engine->matchCount())
318 engine->filterOnDemand(INT_MAX);
319 return engine->matchCount();
324 Q_D(
const QCompletionModel);
325 if (parent.isValid())
330 if (engine->curParts.size() != 1 && !engine->matchCount()
331 && !engine->curParent.isValid())
333 return d->model->rowCount(engine->curParent);
350 Q_D(
const QCompletionModel);
351 if (parent.isValid())
355 return d->model->hasChildren(mapToSource(parent));
357 if (!engine->matchCount())
365 Q_D(
const QCompletionModel);
366 return d->model->data(mapToSource(index), role);
371 QAbstractProxyModel::setSourceModel(
nullptr);
383 engine->cache.clear();
384 filter(engine->curParts);
389 Q_D(QCompletionModel);
391 engine->filter(parts);
394 if (d->model->canFetchMore(engine->curParent))
395 d->model->fetchMore(engine->curParent);
401 const QAbstractItemModel *model =
c->proxy->sourceModel();
403 if (curParts.isEmpty())
404 curParts.append(QString());
407 curParent = QModelIndex();
408 curMatch = QMatchData();
409 historyMatch = filterHistory();
415 for (
int i = 0; i < curParts.size() - 1; i++) {
416 QString part = curParts.at(i);
417 int emi = filter(part, parent, -1).exactMatchIndex;
420 parent = model->index(emi,
c->column, parent);
426 if (curParts.constLast().isEmpty())
427 curMatch = QMatchData(QIndexMapper(0, model->rowCount(curParent) - 1), -1,
false);
429 curMatch = filter(curParts.constLast(), curParent, 1);
430 curRow = curMatch.isValid() ? 0 : -1;
435 QAbstractItemModel *source =
c->proxy->sourceModel();
436 if (curParts.size() <= 1 || c->proxy->showAll || !source)
439#if QT_CONFIG(filesystemmodel)
440 const bool isFsModel = (qobject_cast<QFileSystemModel *>(source) !=
nullptr);
442 const bool isFsModel =
false;
449 for (
int i = 0; i < source->rowCount(); i++) {
450 QString str = source->index(i,
c->column).data().toString();
451 if (str.startsWith(c->prefix, c->cs)
452#if !defined(Q_OS_WIN)
453 && (!isFsModel || QDir::toNativeSeparators(str) != QDir::separator())
467 const auto cit =
cache.find(parent);
468 if (cit ==
cache.end())
472 const auto mapEnd = map.end();
474 QString key = c->cs == Qt::CaseInsensitive ? part.toLower() : part;
476 while (!key.isEmpty()) {
478 const auto it = map.find(key);
493 const auto cit =
cache.find(parent);
494 if (cit ==
cache.end())
499 const QString key = c->cs == Qt::CaseInsensitive ? part.toLower() : part;
501 const auto it = map.find(key);
512 if (c->filterMode == Qt::MatchEndsWith)
515 cost =
cost + m.indices.cost() - old.indices.cost();
516 if (
cost *
sizeof(
int) > 1024 * 1024) {
517 QMap<QModelIndex, CacheItem>::iterator it1 =
cache.begin();
518 while (it1 !=
cache.end()) {
520 int sz = ci.size()/2;
521 QMap<QString, QMatchData>::iterator it2 = ci.begin();
523 while (it2 != ci.end() && i < sz) {
524 cost -= it2.value().indices.cost();
528 if (ci.size() == 0) {
529 it1 =
cache.erase(it1);
536 if (c->cs == Qt::CaseInsensitive)
537 part = std::move(part).toLower();
538 cache[parent][part] = m;
544 const QAbstractItemModel *model =
c->proxy->sourceModel();
546 if (c->cs == Qt::CaseInsensitive)
547 part = std::move(part).toLower();
552 int to = model->rowCount(parent) - 1;
554 const CacheItem::const_iterator it = map.lowerBound(part);
557 for (CacheItem::const_iterator it1 = it; it1 != map.constBegin();) {
561 if (order == Qt::AscendingOrder) {
562 from = value.indices.last() + 1;
564 to = value.indices.first() - 1;
571 for(CacheItem::const_iterator it2 = it; it2 != map.constEnd(); ++it2) {
573 if (value
.isValid() && !it2.key().startsWith(part)) {
574 if (order == Qt::AscendingOrder) {
575 to = value.indices.first() - 1;
577 from = value.indices.first() + 1;
588 const QAbstractItemModel *model =
c->proxy->sourceModel();
590 int rowCount = model->rowCount(parent);
592 return Qt::AscendingOrder;
593 QString first = model->data(model->index(0,
c->column, parent),
c->role).toString();
594 QString last = model->data(model->index(rowCount - 1,
c->column, parent),
c->role).toString();
595 return QString::compare(first, last, c->cs) <= 0 ? Qt::AscendingOrder : Qt::DescendingOrder;
600 const QAbstractItemModel *model =
c->proxy->sourceModel();
603 if (lookupCache(part, parent, &hint))
607 Qt::SortOrder order = sortOrder(parent);
609 if (matchHint(part, parent, &hint)) {
612 indices = hint.indices;
614 indices = indexHint(part, parent, order);
618 int high = indices
.to() + 1;
621 QModelIndex probeIndex;
624 while (high - low > 1)
626 probe = (high + low) / 2;
627 probeIndex = model->index(probe,
c->column, parent);
628 probeData = model->data(probeIndex,
c->role).toString();
629 const int cmp = QString::compare(probeData, part, c->cs);
630 if ((order == Qt::AscendingOrder && cmp >= 0)
631 || (order == Qt::DescendingOrder && cmp < 0)) {
638 if ((order == Qt::AscendingOrder && low == indices.to())
639 || (order == Qt::DescendingOrder && high == indices.from())) {
640 saveInCache(part, parent, QMatchData());
644 probeIndex = model->index(order == Qt::AscendingOrder ? low+1 : high-1, c->column, parent);
645 probeData = model->data(probeIndex,
c->role).toString();
646 if (!probeData.startsWith(part,
c->cs)) {
647 saveInCache(part, parent, QMatchData());
651 const bool exactMatch = QString::compare(probeData, part, c->cs) == 0;
652 int emi = exactMatch ? (order == Qt::AscendingOrder ? low+1 : high-1) : -1;
656 if (order == Qt::AscendingOrder) {
658 high = indices
.to() + 1;
666 while (high - low > 1)
668 probe = (high + low) / 2;
669 probeIndex = model->index(probe,
c->column, parent);
670 probeData = model->data(probeIndex,
c->role).toString();
671 const bool startsWith = probeData.startsWith(part,
c->cs);
672 if ((order == Qt::AscendingOrder && startsWith)
673 || (order == Qt::DescendingOrder && !startsWith)) {
680 QMatchData m(order == Qt::AscendingOrder ? QIndexMapper(from, high - 1) : QIndexMapper(low+1, to), emi,
false);
681 saveInCache(part, parent, m);
691 const QAbstractItemModel *model =
c->proxy->sourceModel();
694 for (i = 0; i < indices
.count() && count != n; ++i) {
695 QModelIndex idx = model->index(indices
[i
],
c->column, parent);
697 if (!(model->flags(idx) & Qt::ItemIsSelectable))
700 QString data = model->data(idx,
c->role).toString();
702 switch (
c->filterMode) {
703 case Qt::MatchStartsWith:
704 if (!data.startsWith(str,
c->cs))
707 case Qt::MatchContains:
708 if (!data.contains(str,
c->cs))
711 case Qt::MatchEndsWith:
712 if (!data.endsWith(str,
c->cs))
715 case Qt::MatchExactly:
716 case Qt::MatchFixedString:
717 case Qt::MatchCaseSensitive:
718 case Qt::MatchRegularExpression:
719 case Qt::MatchWildcard:
721 case Qt::MatchRecursive:
725 m->indices.append(indices
[i
]);
727 if (m->exactMatchIndex == -1 && QString::compare(data, str, c->cs) == 0) {
739 if (!curMatch.partial)
742 const QAbstractItemModel *model =
c->proxy->sourceModel();
743 int lastRow = model->rowCount(curParent) - 1;
745 int lastIndex = buildIndices(curParts.constLast(), curParent, n, im, &curMatch);
746 curMatch.partial = (lastRow != lastIndex);
747 saveInCache(curParts.constLast(), curParent, curMatch);
758 const QAbstractItemModel *model =
c->proxy->sourceModel();
759 bool foundInCache = lookupCache(part, parent, &m);
762 if (matchHint(part, parent, &hint) && !hint.isValid())
767 const int lastRow = model->rowCount(parent) - 1;
769 int lastIndex = buildIndices(part, parent, n, all, &m);
773 buildIndices(part, parent, INT_MAX, hint.indices, &m);
778 const int lastRow = model->rowCount(parent) - 1;
780 int want = n == -1 ? -1 : n - m.indices.count();
781 int lastIndex = buildIndices(part, parent, want, rest, &m);
786 saveInCache(part, parent, m);
795 filterMode(Qt::MatchStartsWith),
796 cs(Qt::CaseSensitive),
800 sorting(QCompleter::UnsortedModel),
810 proxy =
new QCompletionModel(
this, q);
811 QObject::connect(proxy, SIGNAL(rowsAdded()), q, SLOT(_q_autoResizePopup()));
813#if !QT_CONFIG(listview)
814 q->setCompletionMode(QCompleter::InlineCompletion);
816 q->setCompletionMode(QCompleter::PopupCompletion);
826 popup->selectionModel()->setCurrentIndex(index, QItemSelectionModel::NoUpdate);
828 if (!index.isValid())
829 popup->selectionModel()->clear();
831 popup->selectionModel()->setCurrentIndex(index, QItemSelectionModel::Select
832 | QItemSelectionModel::Rows);
834 index =
popup->selectionModel()->currentIndex();
835 if (!index.isValid())
836 popup->scrollToTop();
838 popup->scrollTo(index, QAbstractItemView::PositionAtTop);
844 if (
const auto indexes = selection.indexes(); !indexes.isEmpty())
845 index = indexes.first();
847 _q_complete(index,
true);
855 if (!index.isValid() || (!
proxy->showAll && (index.row() >=
proxy->engine->matchCount()))) {
857 index = QModelIndex();
859 if (!(index.flags() & Qt::ItemIsEnabled))
861 QModelIndex si =
proxy->mapToSource(index);
862 si = si.sibling(si.row(),
column);
863 completion = q->pathFromIndex(si);
864#if QT_CONFIG(filesystemmodel)
866 if (mode == QCompleter::InlineCompletion) {
867 if (qobject_cast<QFileSystemModel *>(proxy->sourceModel()) && QFileInfo(completion).isDir())
868 completion += QDir::separator();
874 emit q->highlighted(index);
875 emit q->highlighted(completion);
877 emit q->activated(index);
878 emit q->activated(completion);
886 showPopup(popupRect);
891 const QRect screen = widget->screen()->availableGeometry();
892 Qt::LayoutDirection dir = widget->layoutDirection();
896 QScrollBar *hsb =
popup->horizontalScrollBar();
897 if (hsb && hsb->isVisible())
898 h +=
popup->horizontalScrollBar()->sizeHint().height();
900 if (rect.isValid()) {
903 pos = widget->mapToGlobal(dir == Qt::RightToLeft ? rect.bottomRight() : rect.bottomLeft());
905 rh = widget->height();
906 pos = widget->mapToGlobal(QPoint(0, widget->height() - 2));
910 if (w > screen.width())
912 if ((pos.x() + w) > (screen.x() + screen.width()))
913 pos.setX(screen.x() + screen.width() - w);
914 if (pos.x() < screen.x())
915 pos.setX(screen.x());
917 int top = pos.y() - rh - screen.top() + 2;
918 int bottom = screen.bottom() - pos.y();
919 h = qMax(h,
popup->minimumHeight());
921 h = qMin(qMax(top, bottom), h);
924 pos.setY(pos.y() - h - rh + 2);
927 popup->setGeometry(pos.x(), pos.y(), w, h);
929 if (!
popup->isVisible()) {
930#if QT_CONFIG(wayland)
931 popup->createWinId();
932 if (
auto waylandWindow =
dynamic_cast<QNativeInterface::Private::QWaylandWindow*>(popup->windowHandle()->handle())) {
933 popup->windowHandle()->setTransientParent(widget->window()->windowHandle());
934 if (!rect.isValid()) {
935 const QRect controlGeometry = QRect(
936 widget->mapTo(widget->topLevelWidget(), QPoint(0, 0)), widget->size());
937 waylandWindow->setParentControlGeometry(controlGeometry);
938 waylandWindow->setExtendedWindowType(
939 QNativeInterface::Private::QWaylandWindow::ComboBox);
947#if QT_CONFIG(filesystemmodel)
948static bool isRoot(
const QFileSystemModel *model,
const QString &path)
950 const auto index = model->index(path);
951 return index.isValid() && model->fileInfo(index).isRoot();
954static bool completeOnLoaded(
const QFileSystemModel *model,
955 const QString &nativePrefix,
957 Qt::CaseSensitivity caseSensitivity)
959 const auto pathSize = path.size();
960 const auto prefixSize = nativePrefix.size();
961 if (prefixSize < pathSize)
963 const QString prefix = QDir::fromNativeSeparators(nativePrefix);
964 if (prefixSize == pathSize)
965 return path.compare(prefix, caseSensitivity) == 0 && isRoot(model, path);
967 const auto separator = u'/';
968 return prefix.startsWith(path, caseSensitivity) && prefix.at(pathSize) == separator
969 && !QStringView{prefix}.right(prefixSize - pathSize - 1).contains(separator);
972void QCompleterPrivate::_q_fileSystemModelDirectoryLoaded(
const QString &path)
979 if (hiddenBecauseNoMatch && widget) {
980 if (
auto model = qobject_cast<
const QFileSystemModel *>(proxy->sourceModel())) {
981 if (completeOnLoaded(model, prefix, path, cs))
991
992
993QCompleter::QCompleter(QObject *parent)
994: QObject(*
new QCompleterPrivate(), parent)
1001
1002
1003
1004QCompleter::QCompleter(QAbstractItemModel *model, QObject *parent)
1005 : QObject(*
new QCompleterPrivate(), parent)
1011#if QT_CONFIG(stringlistmodel)
1013
1014
1015
1016QCompleter::QCompleter(
const QStringList& list, QObject *parent)
1017: QObject(*
new QCompleterPrivate(), parent)
1020 d->init(
new QStringListModel(list,
this));
1025
1026
1027QCompleter::~QCompleter()
1032
1033
1034
1035
1036
1037
1038
1039
1040void QCompleter::setWidget(QWidget *widget)
1043 if (widget == d->widget)
1047 d->widget->removeEventFilter(
this);
1050 d->widget->installEventFilter(
this);
1054 d->popup->setFocusProxy(d->widget);
1059
1060
1061
1062
1063QWidget *QCompleter::widget()
const
1065 Q_D(
const QCompleter);
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080void QCompleter::setModel(QAbstractItemModel *model)
1083 QAbstractItemModel *oldModel = d->proxy->sourceModel();
1084 if (oldModel == model)
1086#if QT_CONFIG(filesystemmodel)
1087 if (qobject_cast<
const QFileSystemModel *>(oldModel))
1088 setCompletionRole(Qt::EditRole);
1090 d->proxy->setSourceModel(model);
1093 if (oldModel && oldModel->QObject::parent() ==
this)
1095#if QT_CONFIG(filesystemmodel)
1096 QFileSystemModel *fsModel = qobject_cast<QFileSystemModel *>(model);
1098#if defined(Q_OS_WIN)
1099 setCaseSensitivity(Qt::CaseInsensitive);
1101 setCaseSensitivity(Qt::CaseSensitive);
1103 setCompletionRole(QFileSystemModel::FileNameRole);
1104 connect(fsModel, SIGNAL(directoryLoaded(QString)),
this, SLOT(_q_fileSystemModelDirectoryLoaded(QString)));
1110
1111
1112
1113
1114QAbstractItemModel *QCompleter::model()
const
1116 Q_D(
const QCompleter);
1117 return d->proxy->sourceModel();
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1133
1134
1135
1136
1137
1138void QCompleter::setCompletionMode(QCompleter::CompletionMode mode)
1142 d->proxy->setFiltered(mode != QCompleter::UnfilteredPopupCompletion);
1144 if (mode == QCompleter::InlineCompletion) {
1146 d->widget->removeEventFilter(
this);
1148 d->popup->deleteLater();
1153 d->widget->installEventFilter(
this);
1157QCompleter::CompletionMode QCompleter::completionMode()
const
1159 Q_D(
const QCompleter);
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1183void QCompleter::setFilterMode(Qt::MatchFlags filterMode)
1187 if (d->filterMode == filterMode)
1190 if (Q_UNLIKELY(filterMode != Qt::MatchStartsWith &&
1191 filterMode != Qt::MatchContains &&
1192 filterMode != Qt::MatchEndsWith)) {
1193 qWarning(
"Unhandled QCompleter::filterMode flag is used.");
1197 d->filterMode = filterMode;
1198 d->proxy->createEngine();
1199 d->proxy->invalidate();
1202Qt::MatchFlags QCompleter::filterMode()
const
1204 Q_D(
const QCompleter);
1205 return d->filterMode;
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223void QCompleter::setPopup(QAbstractItemView *popup)
1227 if (popup == d->popup)
1231 const Qt::FocusPolicy origPolicy = d->widget ? d->widget->focusPolicy()
1236 QObject::disconnect(d->popup->selectionModel(),
nullptr,
this,
nullptr);
1237 QObject::disconnect(d->popup,
nullptr,
this,
nullptr);
1243 if (d->popup->model() != d->proxy)
1244 d->popup->setModel(d->proxy);
1252 d->popup->setParent(
nullptr);
1253 d->popup->setWindowFlag(Qt::Popup);
1254 d->popup->setFocusPolicy(Qt::NoFocus);
1256 d->widget->setFocusPolicy(origPolicy);
1258 d->popup->setFocusProxy(d->widget);
1259 d->popup->installEventFilter(
this);
1260 d->popup->setItemDelegate(
new QCompleterItemDelegate(d->popup));
1261#if QT_CONFIG(listview)
1262 if (QListView *listView = qobject_cast<QListView *>(d->popup)) {
1263 listView->setModelColumn(d->column);
1267 QObject::connect(d->popup, SIGNAL(clicked(QModelIndex)),
1268 this, SLOT(_q_complete(QModelIndex)));
1269 QObject::connect(
this, SIGNAL(activated(QModelIndex)),
1270 d->popup, SLOT(hide()));
1272 QObject::connect(d->popup->selectionModel(), SIGNAL(selectionChanged(QItemSelection,QItemSelection)),
1273 this, SLOT(_q_completionSelected(QItemSelection)));
1277
1278
1279
1280
1281QAbstractItemView *QCompleter::popup()
const
1283 Q_D(
const QCompleter);
1284#if QT_CONFIG(listview)
1285 if (!d->popup && completionMode() != QCompleter::InlineCompletion) {
1286 QListView *listView =
new QListView;
1287 listView->setEditTriggers(QAbstractItemView::NoEditTriggers);
1288 listView->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
1289 listView->setSelectionBehavior(QAbstractItemView::SelectRows);
1290 listView->setSelectionMode(QAbstractItemView::SingleSelection);
1291 listView->setModelColumn(d->column);
1292 QCompleter *that =
const_cast<QCompleter*>(
this);
1293 that->setPopup(listView);
1300
1301
1302bool QCompleter::event(QEvent *ev)
1304 return QObject::event(ev);
1308
1309
1310bool QCompleter::eventFilter(QObject *o, QEvent *e)
1314 if (o == d->widget) {
1315 switch (e->type()) {
1316 case QEvent::FocusOut:
1317 if (d->eatFocusOut) {
1318 d->hiddenBecauseNoMatch =
false;
1319 if (d->popup && d->popup->isVisible())
1333 return QObject::eventFilter(o, e);
1336 switch (e->type()) {
1337 case QEvent::KeyPress: {
1338 QKeyEvent *ke =
static_cast<QKeyEvent *>(e);
1340 QModelIndex curIndex = d->popup->currentIndex();
1341 QModelIndexList selList = d->popup->selectionModel()->selectedIndexes();
1343 const int key = ke->key();
1345 if ((key == Qt::Key_Up || key == Qt::Key_Down) && selList.isEmpty() && curIndex.isValid()
1346 && d->mode == QCompleter::UnfilteredPopupCompletion) {
1347 d->setCurrentIndex(curIndex);
1356 if (ke->modifiers() & Qt::ControlModifier)
1361 if (!curIndex.isValid()) {
1362 int rowCount = d->proxy->rowCount();
1363 QModelIndex lastIndex = d->proxy->index(rowCount - 1, d->column);
1364 d->setCurrentIndex(lastIndex);
1366 }
else if (curIndex.row() == 0) {
1368 d->setCurrentIndex(QModelIndex());
1374 if (!curIndex.isValid()) {
1375 QModelIndex firstIndex = d->proxy->index(0, d->column);
1376 d->setCurrentIndex(firstIndex);
1378 }
else if (curIndex.row() == d->proxy->rowCount() - 1) {
1380 d->setCurrentIndex(QModelIndex());
1385 case Qt::Key_PageUp:
1386 case Qt::Key_PageDown:
1393 d->eatFocusOut =
false;
1394 (
static_cast<QObject *>(d->widget))->event(ke);
1395 d->eatFocusOut =
true;
1397 if (!d->widget || e->isAccepted() || !d->popup->isVisible()) {
1399 if (d->widget && (!d->widget->hasFocus()
1400#ifdef QT_KEYPAD_NAVIGATION
1401 || (QApplicationPrivate::keypadNavigationEnabled() && !d->widget->hasEditFocus())
1405 if (e->isAccepted())
1410#if QT_CONFIG(shortcut)
1411 if (ke->matches(QKeySequence::Cancel)) {
1417#ifdef QT_KEYPAD_NAVIGATION
1418 case Qt::Key_Select:
1419 if (!QApplicationPrivate::keypadNavigationEnabled())
1422 case Qt::Key_Return:
1426 if (curIndex.isValid())
1427 d->_q_complete(curIndex);
1431 if (ke->modifiers() & Qt::AltModifier)
1435 case Qt::Key_Backtab:
1446#ifdef QT_KEYPAD_NAVIGATION
1447 case QEvent::KeyRelease: {
1449 QApplicationPrivate::keypadNavigationEnabled() && ke->key() == Qt::Key_Back) {
1450 QKeyEvent *ke =
static_cast<QKeyEvent *>(e);
1457 d->eatFocusOut =
false;
1458 static_cast<QObject *>(d->widget)->event(ke);
1459 d->eatFocusOut =
true;
1465 case QEvent::MouseButtonPress: {
1466#ifdef QT_KEYPAD_NAVIGATION
1468 && QApplicationPrivate::keypadNavigationEnabled()) {
1470 QWidget *source = qobject_cast<QWidget *>(o);
1472 QPoint pos = source->mapToGlobal((
static_cast<QMouseEvent *>(e))->pos());
1473 QWidget *target = QApplication::widgetAt(pos);
1474 if (target && (d->widget->isAncestorOf(target) ||
1475 target == d->widget)) {
1476 d->eatFocusOut =
false;
1477 static_cast<QObject *>(target)->event(e);
1478 d->eatFocusOut =
true;
1484 if (!d->popup->underMouse()) {
1485 if (!QGuiApplicationPrivate::maybeForwardEventToVirtualKeyboard(e))
1492 case QEvent::MouseButtonRelease:
1493 QGuiApplicationPrivate::maybeForwardEventToVirtualKeyboard(e);
1495 case QEvent::InputMethod:
1496 case QEvent::ShortcutOverride:
1498 QCoreApplication::sendEvent(d->widget, e);
1508
1509
1510
1511
1512
1513
1514
1515
1516
1517void QCompleter::complete(
const QRect& rect)
1520 QModelIndex idx = d->proxy->currentIndex(
false);
1521 d->hiddenBecauseNoMatch =
false;
1522 if (d->mode == QCompleter::InlineCompletion) {
1524 d->_q_complete(idx,
true);
1528 Q_ASSERT(d->widget);
1529 if ((d->mode == QCompleter::PopupCompletion && !idx.isValid())
1530 || (d->mode == QCompleter::UnfilteredPopupCompletion && d->proxy->rowCount() == 0)) {
1533 d->hiddenBecauseNoMatch =
true;
1538 if (d->mode == QCompleter::UnfilteredPopupCompletion)
1539 d->setCurrentIndex(idx,
false);
1542 d->popupRect = rect;
1546
1547
1548
1549
1550
1551
1552
1553
1554bool QCompleter::setCurrentRow(
int row)
1557 return d->proxy->setCurrentRow(row);
1561
1562
1563
1564
1565int QCompleter::currentRow()
const
1567 Q_D(
const QCompleter);
1568 return d->proxy->currentRow();
1572
1573
1574
1575
1576int QCompleter::completionCount()
const
1578 Q_D(
const QCompleter);
1579 return d->proxy->completionCount();
1583
1584
1585
1586
1587
1588
1589
1590
1591
1592
1595
1596
1597
1598
1599
1600
1601
1602
1603
1604
1605
1606
1607
1608
1609
1610
1611
1612
1613
1614
1615
1616void QCompleter::setModelSorting(QCompleter::ModelSorting sorting)
1619 if (d->sorting == sorting)
1621 d->sorting = sorting;
1622 d->proxy->createEngine();
1623 d->proxy->invalidate();
1626QCompleter::ModelSorting QCompleter::modelSorting()
const
1628 Q_D(
const QCompleter);
1633
1634
1635
1636
1637
1638
1639
1640
1641
1642
1643void QCompleter::setCompletionColumn(
int column)
1646 if (d->column == column)
1648#if QT_CONFIG(listview)
1649 if (QListView *listView = qobject_cast<QListView *>(d->popup))
1650 listView->setModelColumn(column);
1653 d->proxy->invalidate();
1656int QCompleter::completionColumn()
const
1658 Q_D(
const QCompleter);
1663
1664
1665
1666
1667
1668
1669
1670void QCompleter::setCompletionRole(
int role)
1673 if (d->role == role)
1676 d->proxy->invalidate();
1679int QCompleter::completionRole()
const
1681 Q_D(
const QCompleter);
1686
1687
1688
1689
1690
1691
1692void QCompleter::setWrapAround(
bool wrap)
1695 if (d->wrap == wrap)
1700bool QCompleter::wrapAround()
const
1702 Q_D(
const QCompleter);
1707
1708
1709
1710
1711
1712
1713int QCompleter::maxVisibleItems()
const
1715 Q_D(
const QCompleter);
1716 return d->maxVisibleItems;
1719void QCompleter::setMaxVisibleItems(
int maxItems)
1722 if (Q_UNLIKELY(maxItems < 0)) {
1723 qWarning(
"QCompleter::setMaxVisibleItems: "
1724 "Invalid max visible items (%d) must be >= 0", maxItems);
1727 d->maxVisibleItems = maxItems;
1731
1732
1733
1734
1735
1736
1737
1738void QCompleter::setCaseSensitivity(Qt::CaseSensitivity cs)
1744 d->proxy->createEngine();
1745 d->proxy->invalidate();
1748Qt::CaseSensitivity QCompleter::caseSensitivity()
const
1750 Q_D(
const QCompleter);
1755
1756
1757
1758
1759
1760
1761void QCompleter::setCompletionPrefix(
const QString &prefix)
1765 d->proxy->filter(splitPath(prefix));
1768QString QCompleter::completionPrefix()
const
1770 Q_D(
const QCompleter);
1775
1776
1777
1778
1779QModelIndex QCompleter::currentIndex()
const
1781 Q_D(
const QCompleter);
1782 return d->proxy->currentIndex(
false);
1786
1787
1788
1789
1790
1791
1792QString QCompleter::currentCompletion()
const
1794 Q_D(
const QCompleter);
1795 return pathFromIndex(d->proxy->currentIndex(
true));
1799
1800
1801
1802
1803
1804
1805
1806
1807
1808
1809QAbstractItemModel *QCompleter::completionModel()
const
1811 Q_D(
const QCompleter);
1816
1817
1818
1819
1820
1821
1822
1823
1824
1826QString QCompleter::pathFromIndex(
const QModelIndex& index)
const
1828 Q_D(
const QCompleter);
1829 if (!index.isValid())
1832 QAbstractItemModel *sourceModel = d->proxy->sourceModel();
1835 bool isFsModel =
false;
1836#if QT_CONFIG(filesystemmodel)
1837 isFsModel = qobject_cast<QFileSystemModel *>(d->proxy->sourceModel()) !=
nullptr;
1840 return sourceModel->data(index, d->role).toString();
1842 QModelIndex idx = index;
1846#if QT_CONFIG(filesystemmodel)
1847 t = sourceModel->data(idx, QFileSystemModel::FileNameRole).toString();
1850 QModelIndex parent = idx.parent();
1851 idx = parent.sibling(parent.row(), index.column());
1852 }
while (idx.isValid());
1854#if !defined(Q_OS_WIN)
1855 if (list.size() == 1)
1860 return list.join(QDir::separator());
1864
1865
1866
1867
1868
1869
1870
1871
1872
1873
1874
1875QStringList QCompleter::splitPath(
const QString& path)
const
1877 bool isFsModel =
false;
1878#if QT_CONFIG(filesystemmodel)
1879 Q_D(
const QCompleter);
1880 isFsModel = qobject_cast<QFileSystemModel *>(d->proxy->sourceModel()) !=
nullptr;
1883 if (!isFsModel || path.isEmpty())
1884 return QStringList(completionPrefix());
1886 QString pathCopy = QDir::toNativeSeparators(path);
1887#if defined(Q_OS_WIN)
1888 if (pathCopy ==
"\\"_L1 || pathCopy ==
"\\\\"_L1)
1889 return QStringList(pathCopy);
1890 const bool startsWithDoubleSlash = pathCopy.startsWith(
"\\\\"_L1);
1891 if (startsWithDoubleSlash)
1892 pathCopy = pathCopy.mid(2);
1895 const QChar sep = QDir::separator();
1896 QStringList parts = pathCopy.split(sep);
1898#if defined(Q_OS_WIN)
1899 if (startsWithDoubleSlash)
1900 parts[0].prepend(
"\\\\"_L1);
1902 if (pathCopy[0] == sep)
1910
1911
1912
1913
1914
1915
1916
1919
1920
1921
1922
1923
1924
1927
1928
1929
1930
1931
1932
1933
1936
1937
1938
1939
1940
1941
1945#include "moc_qcompleter.cpp"
1947#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
Combined button and popup list for selecting options.
QMatchData(const QIndexMapper &indices, int em, bool p)