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()));
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();
895 QScrollBar *hsb =
popup->horizontalScrollBar();
896 if (hsb && hsb->isVisible())
897 h +=
popup->horizontalScrollBar()->sizeHint().height();
899 if (rect.isValid()) {
902 pos = widget->mapToGlobal(rect.bottomLeft());
904 rh = widget->height();
905 pos = widget->mapToGlobal(QPoint(0, widget->height() - 2));
909 if (w > screen.width())
911 if ((pos.x() + w) > (screen.x() + screen.width()))
912 pos.setX(screen.x() + screen.width() - w);
913 if (pos.x() < screen.x())
914 pos.setX(screen.x());
916 int top = pos.y() - rh - screen.top() + 2;
917 int bottom = screen.bottom() - pos.y();
918 h = qMax(h,
popup->minimumHeight());
920 h = qMin(qMax(top, bottom), h);
923 pos.setY(pos.y() - h - rh + 2);
926 popup->setGeometry(pos.x(), pos.y(), w, h);
928 if (!
popup->isVisible()) {
929#if QT_CONFIG(wayland)
930 popup->createWinId();
931 if (
auto waylandWindow =
dynamic_cast<QNativeInterface::Private::QWaylandWindow*>(popup->windowHandle()->handle())) {
932 popup->windowHandle()->setTransientParent(widget->window()->windowHandle());
933 if (!rect.isValid()) {
934 const QRect controlGeometry = QRect(
935 widget->mapTo(widget->topLevelWidget(), QPoint(0, 0)), widget->size());
936 waylandWindow->setParentControlGeometry(controlGeometry);
937 waylandWindow->setExtendedWindowType(
938 QNativeInterface::Private::QWaylandWindow::ComboBox);
946#if QT_CONFIG(filesystemmodel)
947static bool isRoot(
const QFileSystemModel *model,
const QString &path)
949 const auto index = model->index(path);
950 return index.isValid() && model->fileInfo(index).isRoot();
953static bool completeOnLoaded(
const QFileSystemModel *model,
954 const QString &nativePrefix,
956 Qt::CaseSensitivity caseSensitivity)
958 const auto pathSize = path.size();
959 const auto prefixSize = nativePrefix.size();
960 if (prefixSize < pathSize)
962 const QString prefix = QDir::fromNativeSeparators(nativePrefix);
963 if (prefixSize == pathSize)
964 return path.compare(prefix, caseSensitivity) == 0 && isRoot(model, path);
966 const auto separator = u'/';
967 return prefix.startsWith(path, caseSensitivity) && prefix.at(pathSize) == separator
968 && !QStringView{prefix}.right(prefixSize - pathSize - 1).contains(separator);
971void QCompleterPrivate::_q_fileSystemModelDirectoryLoaded(
const QString &path)
978 if (hiddenBecauseNoMatch && widget) {
979 if (
auto model = qobject_cast<
const QFileSystemModel *>(proxy->sourceModel())) {
980 if (completeOnLoaded(model, prefix, path, cs))
990
991
992QCompleter::QCompleter(QObject *parent)
993: QObject(*
new QCompleterPrivate(), parent)
1000
1001
1002
1003QCompleter::QCompleter(QAbstractItemModel *model, QObject *parent)
1004 : QObject(*
new QCompleterPrivate(), parent)
1010#if QT_CONFIG(stringlistmodel)
1012
1013
1014
1015QCompleter::QCompleter(
const QStringList& list, QObject *parent)
1016: QObject(*
new QCompleterPrivate(), parent)
1019 d->init(
new QStringListModel(list,
this));
1024
1025
1026QCompleter::~QCompleter()
1031
1032
1033
1034
1035
1036
1037
1038
1039void QCompleter::setWidget(QWidget *widget)
1042 if (widget == d->widget)
1046 d->widget->removeEventFilter(
this);
1049 d->widget->installEventFilter(
this);
1053 d->popup->setFocusProxy(d->widget);
1058
1059
1060
1061
1062QWidget *QCompleter::widget()
const
1064 Q_D(
const QCompleter);
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079void QCompleter::setModel(QAbstractItemModel *model)
1082 QAbstractItemModel *oldModel = d->proxy->sourceModel();
1083 if (oldModel == model)
1085#if QT_CONFIG(filesystemmodel)
1086 if (qobject_cast<
const QFileSystemModel *>(oldModel))
1087 setCompletionRole(Qt::EditRole);
1089 d->proxy->setSourceModel(model);
1092 if (oldModel && oldModel->QObject::parent() ==
this)
1094#if QT_CONFIG(filesystemmodel)
1095 QFileSystemModel *fsModel = qobject_cast<QFileSystemModel *>(model);
1097#if defined(Q_OS_WIN)
1098 setCaseSensitivity(Qt::CaseInsensitive);
1100 setCaseSensitivity(Qt::CaseSensitive);
1102 setCompletionRole(QFileSystemModel::FileNameRole);
1103 connect(fsModel, SIGNAL(directoryLoaded(QString)),
this, SLOT(_q_fileSystemModelDirectoryLoaded(QString)));
1109
1110
1111
1112
1113QAbstractItemModel *QCompleter::model()
const
1115 Q_D(
const QCompleter);
1116 return d->proxy->sourceModel();
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1132
1133
1134
1135
1136
1137void QCompleter::setCompletionMode(QCompleter::CompletionMode mode)
1141 d->proxy->setFiltered(mode != QCompleter::UnfilteredPopupCompletion);
1143 if (mode == QCompleter::InlineCompletion) {
1145 d->widget->removeEventFilter(
this);
1147 d->popup->deleteLater();
1152 d->widget->installEventFilter(
this);
1156QCompleter::CompletionMode QCompleter::completionMode()
const
1158 Q_D(
const QCompleter);
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1182void QCompleter::setFilterMode(Qt::MatchFlags filterMode)
1186 if (d->filterMode == filterMode)
1189 if (Q_UNLIKELY(filterMode != Qt::MatchStartsWith &&
1190 filterMode != Qt::MatchContains &&
1191 filterMode != Qt::MatchEndsWith)) {
1192 qWarning(
"Unhandled QCompleter::filterMode flag is used.");
1196 d->filterMode = filterMode;
1197 d->proxy->createEngine();
1198 d->proxy->invalidate();
1201Qt::MatchFlags QCompleter::filterMode()
const
1203 Q_D(
const QCompleter);
1204 return d->filterMode;
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222void QCompleter::setPopup(QAbstractItemView *popup)
1226 if (popup == d->popup)
1230 const Qt::FocusPolicy origPolicy = d->widget ? d->widget->focusPolicy()
1235 QObject::disconnect(d->popup->selectionModel(),
nullptr,
this,
nullptr);
1236 QObject::disconnect(d->popup,
nullptr,
this,
nullptr);
1242 if (d->popup->model() != d->proxy)
1243 d->popup->setModel(d->proxy);
1251 d->popup->setParent(
nullptr);
1252 d->popup->setWindowFlag(Qt::Popup);
1253 d->popup->setFocusPolicy(Qt::NoFocus);
1255 d->widget->setFocusPolicy(origPolicy);
1257 d->popup->setFocusProxy(d->widget);
1258 d->popup->installEventFilter(
this);
1259 d->popup->setItemDelegate(
new QCompleterItemDelegate(d->popup));
1260#if QT_CONFIG(listview)
1261 if (QListView *listView = qobject_cast<QListView *>(d->popup)) {
1262 listView->setModelColumn(d->column);
1266 QObject::connect(d->popup, SIGNAL(clicked(QModelIndex)),
1267 this, SLOT(_q_complete(QModelIndex)));
1268 QObject::connect(
this, SIGNAL(activated(QModelIndex)),
1269 d->popup, SLOT(hide()));
1271 QObject::connect(d->popup->selectionModel(), SIGNAL(selectionChanged(QItemSelection,QItemSelection)),
1272 this, SLOT(_q_completionSelected(QItemSelection)));
1276
1277
1278
1279
1280QAbstractItemView *QCompleter::popup()
const
1282 Q_D(
const QCompleter);
1283#if QT_CONFIG(listview)
1284 if (!d->popup && completionMode() != QCompleter::InlineCompletion) {
1285 QListView *listView =
new QListView;
1286 listView->setEditTriggers(QAbstractItemView::NoEditTriggers);
1287 listView->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
1288 listView->setSelectionBehavior(QAbstractItemView::SelectRows);
1289 listView->setSelectionMode(QAbstractItemView::SingleSelection);
1290 listView->setModelColumn(d->column);
1291 QCompleter *that =
const_cast<QCompleter*>(
this);
1292 that->setPopup(listView);
1299
1300
1301bool QCompleter::event(QEvent *ev)
1303 return QObject::event(ev);
1307
1308
1309bool QCompleter::eventFilter(QObject *o, QEvent *e)
1313 if (o == d->widget) {
1314 switch (e->type()) {
1315 case QEvent::FocusOut:
1316 if (d->eatFocusOut) {
1317 d->hiddenBecauseNoMatch =
false;
1318 if (d->popup && d->popup->isVisible())
1332 return QObject::eventFilter(o, e);
1335 switch (e->type()) {
1336 case QEvent::KeyPress: {
1337 QKeyEvent *ke =
static_cast<QKeyEvent *>(e);
1339 QModelIndex curIndex = d->popup->currentIndex();
1340 QModelIndexList selList = d->popup->selectionModel()->selectedIndexes();
1342 const int key = ke->key();
1344 if ((key == Qt::Key_Up || key == Qt::Key_Down) && selList.isEmpty() && curIndex.isValid()
1345 && d->mode == QCompleter::UnfilteredPopupCompletion) {
1346 d->setCurrentIndex(curIndex);
1355 if (ke->modifiers() & Qt::ControlModifier)
1360 if (!curIndex.isValid()) {
1361 int rowCount = d->proxy->rowCount();
1362 QModelIndex lastIndex = d->proxy->index(rowCount - 1, d->column);
1363 d->setCurrentIndex(lastIndex);
1365 }
else if (curIndex.row() == 0) {
1367 d->setCurrentIndex(QModelIndex());
1373 if (!curIndex.isValid()) {
1374 QModelIndex firstIndex = d->proxy->index(0, d->column);
1375 d->setCurrentIndex(firstIndex);
1377 }
else if (curIndex.row() == d->proxy->rowCount() - 1) {
1379 d->setCurrentIndex(QModelIndex());
1384 case Qt::Key_PageUp:
1385 case Qt::Key_PageDown:
1392 d->eatFocusOut =
false;
1393 (
static_cast<QObject *>(d->widget))->event(ke);
1394 d->eatFocusOut =
true;
1396 if (!d->widget || e->isAccepted() || !d->popup->isVisible()) {
1398 if (d->widget && !d->widget->hasFocus())
1400 if (e->isAccepted())
1405#if QT_CONFIG(shortcut)
1406 if (ke->matches(QKeySequence::Cancel)) {
1412 case Qt::Key_Return:
1416 if (curIndex.isValid())
1417 d->_q_complete(curIndex);
1421 if (ke->modifiers() & Qt::AltModifier)
1425 case Qt::Key_Backtab:
1436 case QEvent::MouseButtonPress:
1437 if (!d->popup->underMouse()) {
1438 if (!QGuiApplicationPrivate::maybeForwardEventToVirtualKeyboard(e))
1444 case QEvent::MouseButtonRelease:
1445 QGuiApplicationPrivate::maybeForwardEventToVirtualKeyboard(e);
1447 case QEvent::InputMethod:
1448 case QEvent::ShortcutOverride:
1450 QCoreApplication::sendEvent(d->widget, e);
1460
1461
1462
1463
1464
1465
1466
1467
1468
1469void QCompleter::complete(
const QRect& rect)
1472 QModelIndex idx = d->proxy->currentIndex(
false);
1473 d->hiddenBecauseNoMatch =
false;
1474 if (d->mode == QCompleter::InlineCompletion) {
1476 d->_q_complete(idx,
true);
1480 Q_ASSERT(d->widget);
1481 if ((d->mode == QCompleter::PopupCompletion && !idx.isValid())
1482 || (d->mode == QCompleter::UnfilteredPopupCompletion && d->proxy->rowCount() == 0)) {
1485 d->hiddenBecauseNoMatch =
true;
1490 if (d->mode == QCompleter::UnfilteredPopupCompletion)
1491 d->setCurrentIndex(idx,
false);
1494 d->popupRect = rect;
1498
1499
1500
1501
1502
1503
1504
1505
1506bool QCompleter::setCurrentRow(
int row)
1509 return d->proxy->setCurrentRow(row);
1513
1514
1515
1516
1517int QCompleter::currentRow()
const
1519 Q_D(
const QCompleter);
1520 return d->proxy->currentRow();
1524
1525
1526
1527
1528int QCompleter::completionCount()
const
1530 Q_D(
const QCompleter);
1531 return d->proxy->completionCount();
1535
1536
1537
1538
1539
1540
1541
1542
1543
1544
1547
1548
1549
1550
1551
1552
1553
1554
1555
1556
1557
1558
1559
1560
1561
1562
1563
1564
1565
1566
1567
1568void QCompleter::setModelSorting(QCompleter::ModelSorting sorting)
1571 if (d->sorting == sorting)
1573 d->sorting = sorting;
1574 d->proxy->createEngine();
1575 d->proxy->invalidate();
1578QCompleter::ModelSorting QCompleter::modelSorting()
const
1580 Q_D(
const QCompleter);
1585
1586
1587
1588
1589
1590
1591
1592
1593
1594
1595void QCompleter::setCompletionColumn(
int column)
1598 if (d->column == column)
1600#if QT_CONFIG(listview)
1601 if (QListView *listView = qobject_cast<QListView *>(d->popup))
1602 listView->setModelColumn(column);
1605 d->proxy->invalidate();
1608int QCompleter::completionColumn()
const
1610 Q_D(
const QCompleter);
1615
1616
1617
1618
1619
1620
1621
1622void QCompleter::setCompletionRole(
int role)
1625 if (d->role == role)
1628 d->proxy->invalidate();
1631int QCompleter::completionRole()
const
1633 Q_D(
const QCompleter);
1638
1639
1640
1641
1642
1643
1644void QCompleter::setWrapAround(
bool wrap)
1647 if (d->wrap == wrap)
1652bool QCompleter::wrapAround()
const
1654 Q_D(
const QCompleter);
1659
1660
1661
1662
1663
1664
1665int QCompleter::maxVisibleItems()
const
1667 Q_D(
const QCompleter);
1668 return d->maxVisibleItems;
1671void QCompleter::setMaxVisibleItems(
int maxItems)
1674 if (Q_UNLIKELY(maxItems < 0)) {
1675 qWarning(
"QCompleter::setMaxVisibleItems: "
1676 "Invalid max visible items (%d) must be >= 0", maxItems);
1679 d->maxVisibleItems = maxItems;
1683
1684
1685
1686
1687
1688
1689
1690void QCompleter::setCaseSensitivity(Qt::CaseSensitivity cs)
1696 d->proxy->createEngine();
1697 d->proxy->invalidate();
1700Qt::CaseSensitivity QCompleter::caseSensitivity()
const
1702 Q_D(
const QCompleter);
1707
1708
1709
1710
1711
1712
1713void QCompleter::setCompletionPrefix(
const QString &prefix)
1717 d->proxy->filter(splitPath(prefix));
1720QString QCompleter::completionPrefix()
const
1722 Q_D(
const QCompleter);
1727
1728
1729
1730
1731QModelIndex QCompleter::currentIndex()
const
1733 Q_D(
const QCompleter);
1734 return d->proxy->currentIndex(
false);
1738
1739
1740
1741
1742
1743
1744QString QCompleter::currentCompletion()
const
1746 Q_D(
const QCompleter);
1747 return pathFromIndex(d->proxy->currentIndex(
true));
1751
1752
1753
1754
1755
1756
1757
1758
1759
1760
1761QAbstractItemModel *QCompleter::completionModel()
const
1763 Q_D(
const QCompleter);
1768
1769
1770
1771
1772
1773
1774
1775
1776
1778QString QCompleter::pathFromIndex(
const QModelIndex& index)
const
1780 Q_D(
const QCompleter);
1781 if (!index.isValid())
1784 QAbstractItemModel *sourceModel = d->proxy->sourceModel();
1787 bool isFsModel =
false;
1788#if QT_CONFIG(filesystemmodel)
1789 isFsModel = qobject_cast<QFileSystemModel *>(d->proxy->sourceModel()) !=
nullptr;
1792 return sourceModel->data(index, d->role).toString();
1794 QModelIndex idx = index;
1798#if QT_CONFIG(filesystemmodel)
1799 t = sourceModel->data(idx, QFileSystemModel::FileNameRole).toString();
1802 QModelIndex parent = idx.parent();
1803 idx = parent.sibling(parent.row(), index.column());
1804 }
while (idx.isValid());
1806#if !defined(Q_OS_WIN)
1807 if (list.size() == 1)
1812 return list.join(QDir::separator());
1816
1817
1818
1819
1820
1821
1822
1823
1824
1825
1826
1827QStringList QCompleter::splitPath(
const QString& path)
const
1829 bool isFsModel =
false;
1830#if QT_CONFIG(filesystemmodel)
1831 Q_D(
const QCompleter);
1832 isFsModel = qobject_cast<QFileSystemModel *>(d->proxy->sourceModel()) !=
nullptr;
1835 if (!isFsModel || path.isEmpty())
1836 return QStringList(completionPrefix());
1838 QString pathCopy = QDir::toNativeSeparators(path);
1839#if defined(Q_OS_WIN)
1840 if (pathCopy ==
"\\"_L1 || pathCopy ==
"\\\\"_L1)
1841 return QStringList(pathCopy);
1842 const bool startsWithDoubleSlash = pathCopy.startsWith(
"\\\\"_L1);
1843 if (startsWithDoubleSlash)
1844 pathCopy = pathCopy.mid(2);
1847 const QChar sep = QDir::separator();
1848 QStringList parts = pathCopy.split(sep);
1850#if defined(Q_OS_WIN)
1851 if (startsWithDoubleSlash)
1852 parts[0].prepend(
"\\\\"_L1);
1854 if (pathCopy[0] == sep)
1862
1863
1864
1865
1866
1867
1868
1871
1872
1873
1874
1875
1876
1879
1880
1881
1882
1883
1884
1885
1888
1889
1890
1891
1892
1893
1897#include "moc_qcompleter.cpp"
1899#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)