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);
397#if QT_CONFIG(filesystemmodel)
401 if (engine->matchCount() == 0 && parts.size() > 1) {
402 if (
auto *fsModel = qobject_cast<QFileSystemModel *>(d->model)) {
403 const QModelIndex dirIndex = fsModel->index(QFileInfo(c->prefix).path());
404 if (dirIndex.isValid() && fsModel->canFetchMore(dirIndex))
405 fsModel->fetchMore(dirIndex);
414 const QAbstractItemModel *model =
c->proxy->sourceModel();
416 if (curParts.isEmpty())
417 curParts.append(QString());
420 curParent = QModelIndex();
421 curMatch = QMatchData();
422 historyMatch = filterHistory();
428 for (
int i = 0; i < curParts.size() - 1; i++) {
429 QString part = curParts.at(i);
430 int emi = filter(part, parent, -1).exactMatchIndex;
433 parent = model->index(emi,
c->column, parent);
439 if (curParts.constLast().isEmpty())
440 curMatch = QMatchData(QIndexMapper(0, model->rowCount(curParent) - 1), -1,
false);
442 curMatch = filter(curParts.constLast(), curParent, 1);
443 curRow = curMatch.isValid() ? 0 : -1;
448 QAbstractItemModel *source =
c->proxy->sourceModel();
449 if (curParts.size() <= 1 || c->proxy->showAll || !source)
452#if QT_CONFIG(filesystemmodel)
453 const bool isFsModel = (qobject_cast<QFileSystemModel *>(source) !=
nullptr);
455 const bool isFsModel =
false;
462 for (
int i = 0; i < source->rowCount(); i++) {
463 QString str = source->index(i,
c->column).data().toString();
464 if (str.startsWith(c->prefix, c->cs)
465#if !defined(Q_OS_WIN)
466 && (!isFsModel || QDir::toNativeSeparators(str) != QDir::separator())
480 const auto cit =
cache.find(parent);
481 if (cit ==
cache.end())
485 const auto mapEnd = map.end();
487 QString key = c->cs == Qt::CaseInsensitive ? part.toLower() : part;
489 while (!key.isEmpty()) {
491 const auto it = map.find(key);
506 const auto cit =
cache.find(parent);
507 if (cit ==
cache.end())
512 const QString key = c->cs == Qt::CaseInsensitive ? part.toLower() : part;
514 const auto it = map.find(key);
525 if (c->filterMode == Qt::MatchEndsWith)
528 cost =
cost + m.indices.cost() - old.indices.cost();
529 if (
cost *
sizeof(
int) > 1024 * 1024) {
530 QMap<QModelIndex, CacheItem>::iterator it1 =
cache.begin();
531 while (it1 !=
cache.end()) {
533 int sz = ci.size()/2;
534 QMap<QString, QMatchData>::iterator it2 = ci.begin();
536 while (it2 != ci.end() && i < sz) {
537 cost -= it2.value().indices.cost();
541 if (ci.size() == 0) {
542 it1 =
cache.erase(it1);
549 if (c->cs == Qt::CaseInsensitive)
550 part = std::move(part).toLower();
551 cache[parent][part] = m;
557 const QAbstractItemModel *model = c->proxy->sourceModel();
559 if (c->cs == Qt::CaseInsensitive)
560 part = std::move(part).toLower();
565 int to = model->rowCount(parent) - 1;
567 const CacheItem::const_iterator it = map.lowerBound(part);
570 for (CacheItem::const_iterator it1 = it; it1 != map.constBegin();) {
574 if (order == Qt::AscendingOrder) {
575 from = value.indices.last() + 1;
577 to = value.indices.first() - 1;
584 for(CacheItem::const_iterator it2 = it; it2 != map.constEnd(); ++it2) {
586 if (value
.isValid() && !it2.key().startsWith(part)) {
587 if (order == Qt::AscendingOrder) {
588 to = value.indices.first() - 1;
590 from = value.indices.first() + 1;
601 const QAbstractItemModel *model = c->proxy->sourceModel();
603 int rowCount = model->rowCount(parent);
605 return Qt::AscendingOrder;
606 QString first = model->data(model->index(0, c->column, parent), c->role).toString();
607 QString last = model->data(model->index(rowCount - 1, c->column, parent), c->role).toString();
608 return QString::compare(first, last, c->cs) <= 0 ? Qt::AscendingOrder : Qt::DescendingOrder;
613 const QAbstractItemModel *model = c->proxy->sourceModel();
616 if (lookupCache(part, parent, &hint))
620 Qt::SortOrder order = sortOrder(parent);
622 if (matchHint(part, parent, &hint)) {
625 indices = hint.indices;
627 indices = indexHint(part, parent, order);
631 int high = indices
.to() + 1;
634 QModelIndex probeIndex;
637 while (high - low > 1)
639 probe = (high + low) / 2;
640 probeIndex = model->index(probe, c->column, parent);
641 probeData = model->data(probeIndex, c->role).toString();
642 const int cmp = QString::compare(probeData, part, c->cs);
643 if ((order == Qt::AscendingOrder && cmp >= 0)
644 || (order == Qt::DescendingOrder && cmp < 0)) {
651 if ((order == Qt::AscendingOrder && low == indices.to())
652 || (order == Qt::DescendingOrder && high == indices.from())) {
653 saveInCache(part, parent, QMatchData());
657 probeIndex = model->index(order == Qt::AscendingOrder ? low+1 : high-1, c->column, parent);
658 probeData = model->data(probeIndex, c->role).toString();
659 if (!probeData.startsWith(part, c->cs)) {
660 saveInCache(part, parent, QMatchData());
664 const bool exactMatch = QString::compare(probeData, part, c->cs) == 0;
665 int emi = exactMatch ? (order == Qt::AscendingOrder ? low+1 : high-1) : -1;
669 if (order == Qt::AscendingOrder) {
671 high = indices
.to() + 1;
679 while (high - low > 1)
681 probe = (high + low) / 2;
682 probeIndex = model->index(probe, c->column, parent);
683 probeData = model->data(probeIndex, c->role).toString();
684 const bool startsWith = probeData.startsWith(part, c->cs);
685 if ((order == Qt::AscendingOrder && startsWith)
686 || (order == Qt::DescendingOrder && !startsWith)) {
693 QMatchData m(order == Qt::AscendingOrder ? QIndexMapper(from, high - 1) : QIndexMapper(low+1, to), emi,
false);
694 saveInCache(part, parent, m);
704 const QAbstractItemModel *model = c->proxy->sourceModel();
707 for (i = 0; i < indices
.count() && count != n; ++i) {
708 QModelIndex idx = model->index(indices
[i
], c->column, parent);
710 if (!(model->flags(idx) & Qt::ItemIsSelectable))
713 QString data = model->data(idx, c->role).toString();
715 switch (c->filterMode) {
716 case Qt::MatchStartsWith:
717 if (!data.startsWith(str, c->cs))
720 case Qt::MatchContains:
721 if (!data.contains(str, c->cs))
724 case Qt::MatchEndsWith:
725 if (!data.endsWith(str, c->cs))
728 case Qt::MatchExactly:
729 case Qt::MatchFixedString:
730 case Qt::MatchCaseSensitive:
731 case Qt::MatchRegularExpression:
732 case Qt::MatchWildcard:
734 case Qt::MatchRecursive:
738 m->indices.append(indices
[i
]);
740 if (m->exactMatchIndex == -1 && QString::compare(data, str, c->cs) == 0) {
752 if (!curMatch.partial)
755 const QAbstractItemModel *model = c->proxy->sourceModel();
756 int lastRow = model->rowCount(curParent) - 1;
758 int lastIndex = buildIndices(curParts.constLast(), curParent, n, im, &curMatch);
759 curMatch.partial = (lastRow != lastIndex);
760 saveInCache(curParts.constLast(), curParent, curMatch);
771 const QAbstractItemModel *model = c->proxy->sourceModel();
772 bool foundInCache = lookupCache(part, parent, &m);
775 if (matchHint(part, parent, &hint) && !hint.isValid())
780 const int lastRow = model->rowCount(parent) - 1;
782 int lastIndex = buildIndices(part, parent, n, all, &m);
786 buildIndices(part, parent, INT_MAX, hint.indices, &m);
791 const int lastRow = model->rowCount(parent) - 1;
793 int want = n == -1 ? -1 : n - m.indices.count();
794 int lastIndex = buildIndices(part, parent, want, rest, &m);
799 saveInCache(part, parent, m);
808 filterMode(Qt::MatchStartsWith),
809 cs(Qt::CaseSensitive),
813 sorting(QCompleter::UnsortedModel),
823 proxy =
new QCompletionModel(
this, q);
824 QObject::connect(proxy, SIGNAL(rowsAdded()), q, SLOT(_q_autoResizePopup()));
826#if !QT_CONFIG(listview)
827 q->setCompletionMode(QCompleter::InlineCompletion);
829 q->setCompletionMode(QCompleter::PopupCompletion);
839 popup->selectionModel()->setCurrentIndex(index, QItemSelectionModel::NoUpdate);
841 if (!index.isValid())
842 popup->selectionModel()->clear();
844 popup->selectionModel()->setCurrentIndex(index, QItemSelectionModel::Select
845 | QItemSelectionModel::Rows);
847 index =
popup->selectionModel()->currentIndex();
848 if (!index.isValid())
849 popup->scrollToTop();
851 popup->scrollTo(index, QAbstractItemView::PositionAtTop);
857 if (
const auto indexes = selection.indexes(); !indexes.isEmpty())
858 index = indexes.first();
860 _q_complete(index,
true);
868 if (!index.isValid() || (!
proxy->showAll && (index.row() >=
proxy->engine->matchCount()))) {
870 index = QModelIndex();
872 if (!(index.flags() & Qt::ItemIsEnabled))
874 QModelIndex si =
proxy->mapToSource(index);
875 si = si.sibling(si.row(),
column);
876 completion = q->pathFromIndex(si);
877#if QT_CONFIG(filesystemmodel)
879 if (mode == QCompleter::InlineCompletion) {
880 if (qobject_cast<QFileSystemModel *>(proxy->sourceModel()) && QFileInfo(completion).isDir())
881 completion += QDir::separator();
887 emit q->highlighted(index);
888 emit q->highlighted(completion);
890 emit q->activated(index);
891 emit q->activated(completion);
899 showPopup(popupRect);
904 const QRect screen = widget->screen()->availableGeometry();
908 QScrollBar *hsb =
popup->horizontalScrollBar();
909 if (hsb && hsb->isVisible())
910 h +=
popup->horizontalScrollBar()->sizeHint().height();
912 if (rect.isValid()) {
915 pos = widget->mapToGlobal(rect.bottomLeft());
917 rh = widget->height();
918 pos = widget->mapToGlobal(QPoint(0, widget->height() - 2));
922 if (w > screen.width())
924 if ((pos.x() + w) > (screen.x() + screen.width()))
925 pos.setX(screen.x() + screen.width() - w);
926 if (pos.x() < screen.x())
927 pos.setX(screen.x());
929 int top = pos.y() - rh - screen.top() + 2;
930 int bottom = screen.bottom() - pos.y();
931 h = qMax(h,
popup->minimumHeight());
933 h = qMin(qMax(top, bottom), h);
936 pos.setY(pos.y() - h - rh + 2);
939 popup->setGeometry(pos.x(), pos.y(), w, h);
941 if (!
popup->isVisible()) {
942#if QT_CONFIG(wayland)
943 popup->createWinId();
944 if (
auto waylandWindow =
dynamic_cast<QNativeInterface::Private::QWaylandWindow*>(popup->windowHandle()->handle())) {
945 popup->windowHandle()->setTransientParent(widget->window()->windowHandle());
946 if (!rect.isValid()) {
947 const QRect controlGeometry = QRect(
948 widget->mapTo(widget->topLevelWidget(), QPoint(0, 0)), widget->size());
949 waylandWindow->setParentControlGeometry(controlGeometry);
950 waylandWindow->setExtendedWindowType(
951 QNativeInterface::Private::QWaylandWindow::ComboBox);
959#if QT_CONFIG(filesystemmodel)
960static bool isRoot(
const QFileSystemModel *model,
const QString &path)
962 const auto index = model->index(path);
963 return index.isValid() && model->fileInfo(index).isRoot();
966static bool completeOnLoaded(
const QFileSystemModel *model,
967 const QString &nativePrefix,
969 Qt::CaseSensitivity caseSensitivity)
971 const auto pathSize = path.size();
972 const auto prefixSize = nativePrefix.size();
973 if (prefixSize < pathSize)
975 const QString prefix = QDir::fromNativeSeparators(nativePrefix);
976 if (prefixSize == pathSize)
977 return path.compare(prefix, caseSensitivity) == 0 && isRoot(model, path);
979 const auto separator = u'/';
980 return prefix.startsWith(path, caseSensitivity) && prefix.at(pathSize) == separator
981 && !QStringView{prefix}.right(prefixSize - pathSize - 1).contains(separator);
984void QCompleterPrivate::_q_fileSystemModelDirectoryLoaded(
const QString &path)
991 if (hiddenBecauseNoMatch && widget) {
992 if (
auto model = qobject_cast<
const QFileSystemModel *>(proxy->sourceModel())) {
993 if (completeOnLoaded(model, prefix, path, cs))
1003
1004
1005QCompleter::QCompleter(QObject *parent)
1006: QObject(*
new QCompleterPrivate(), parent)
1013
1014
1015
1016QCompleter::QCompleter(QAbstractItemModel *model, QObject *parent)
1017 : QObject(*
new QCompleterPrivate(), parent)
1023#if QT_CONFIG(stringlistmodel)
1025
1026
1027
1028QCompleter::QCompleter(
const QStringList& list, QObject *parent)
1029: QObject(*
new QCompleterPrivate(), parent)
1032 d->init(
new QStringListModel(list,
this));
1037
1038
1039QCompleter::~QCompleter()
1044
1045
1046
1047
1048
1049
1050
1051
1052void QCompleter::setWidget(QWidget *widget)
1055 if (widget == d->widget)
1059 d->widget->removeEventFilter(
this);
1062 d->widget->installEventFilter(
this);
1066 d->popup->setFocusProxy(d->widget);
1071
1072
1073
1074
1075QWidget *QCompleter::widget()
const
1077 Q_D(
const QCompleter);
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092void QCompleter::setModel(QAbstractItemModel *model)
1095 QAbstractItemModel *oldModel = d->proxy->sourceModel();
1096 if (oldModel == model)
1098#if QT_CONFIG(filesystemmodel)
1099 if (qobject_cast<
const QFileSystemModel *>(oldModel))
1100 setCompletionRole(Qt::EditRole);
1102 d->proxy->setSourceModel(model);
1105 if (oldModel && oldModel->QObject::parent() ==
this)
1107#if QT_CONFIG(filesystemmodel)
1108 QFileSystemModel *fsModel = qobject_cast<QFileSystemModel *>(model);
1110#if defined(Q_OS_WIN)
1111 setCaseSensitivity(Qt::CaseInsensitive);
1113 setCaseSensitivity(Qt::CaseSensitive);
1115 setCompletionRole(QFileSystemModel::FileNameRole);
1116 connect(fsModel, SIGNAL(directoryLoaded(QString)),
this, SLOT(_q_fileSystemModelDirectoryLoaded(QString)));
1122
1123
1124
1125
1126QAbstractItemModel *QCompleter::model()
const
1128 Q_D(
const QCompleter);
1129 return d->proxy->sourceModel();
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1145
1146
1147
1148
1149
1150void QCompleter::setCompletionMode(QCompleter::CompletionMode mode)
1154 d->proxy->setFiltered(mode != QCompleter::UnfilteredPopupCompletion);
1156 if (mode == QCompleter::InlineCompletion) {
1158 d->widget->removeEventFilter(
this);
1160 d->popup->deleteLater();
1165 d->widget->installEventFilter(
this);
1169QCompleter::CompletionMode QCompleter::completionMode()
const
1171 Q_D(
const QCompleter);
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1195void QCompleter::setFilterMode(Qt::MatchFlags filterMode)
1199 if (d->filterMode == filterMode)
1202 if (Q_UNLIKELY(filterMode != Qt::MatchStartsWith &&
1203 filterMode != Qt::MatchContains &&
1204 filterMode != Qt::MatchEndsWith)) {
1205 qWarning(
"Unhandled QCompleter::filterMode flag is used.");
1209 d->filterMode = filterMode;
1210 d->proxy->createEngine();
1211 d->proxy->invalidate();
1214Qt::MatchFlags QCompleter::filterMode()
const
1216 Q_D(
const QCompleter);
1217 return d->filterMode;
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235void QCompleter::setPopup(QAbstractItemView *popup)
1239 if (popup == d->popup)
1243 const Qt::FocusPolicy origPolicy = d->widget ? d->widget->focusPolicy()
1248 QObject::disconnect(d->popup->selectionModel(),
nullptr,
this,
nullptr);
1249 QObject::disconnect(d->popup,
nullptr,
this,
nullptr);
1255 if (d->popup->model() != d->proxy)
1256 d->popup->setModel(d->proxy);
1264 d->popup->setParent(
nullptr);
1265 d->popup->setWindowFlag(Qt::Popup);
1266 d->popup->setFocusPolicy(Qt::NoFocus);
1268 d->widget->setFocusPolicy(origPolicy);
1270 d->popup->setFocusProxy(d->widget);
1271 d->popup->installEventFilter(
this);
1272 d->popup->setItemDelegate(
new QCompleterItemDelegate(d->popup));
1273#if QT_CONFIG(listview)
1274 if (QListView *listView = qobject_cast<QListView *>(d->popup)) {
1275 listView->setModelColumn(d->column);
1279 QObject::connect(d->popup, SIGNAL(clicked(QModelIndex)),
1280 this, SLOT(_q_complete(QModelIndex)));
1281 QObject::connect(
this, SIGNAL(activated(QModelIndex)),
1282 d->popup, SLOT(hide()));
1284 QObject::connect(d->popup->selectionModel(), SIGNAL(selectionChanged(QItemSelection,QItemSelection)),
1285 this, SLOT(_q_completionSelected(QItemSelection)));
1289
1290
1291
1292
1293QAbstractItemView *QCompleter::popup()
const
1295 Q_D(
const QCompleter);
1296#if QT_CONFIG(listview)
1297 if (!d->popup && completionMode() != QCompleter::InlineCompletion) {
1298 QListView *listView =
new QListView;
1299 listView->setEditTriggers(QAbstractItemView::NoEditTriggers);
1300 listView->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
1301 listView->setSelectionBehavior(QAbstractItemView::SelectRows);
1302 listView->setSelectionMode(QAbstractItemView::SingleSelection);
1303 listView->setModelColumn(d->column);
1304 QCompleter *that =
const_cast<QCompleter*>(
this);
1305 that->setPopup(listView);
1312
1313
1314bool QCompleter::event(QEvent *ev)
1316 return QObject::event(ev);
1320
1321
1322bool QCompleter::eventFilter(QObject *o, QEvent *e)
1326 if (o == d->widget) {
1327 switch (e->type()) {
1328 case QEvent::FocusOut:
1329 if (d->eatFocusOut) {
1330 d->hiddenBecauseNoMatch =
false;
1331 if (d->popup && d->popup->isVisible())
1345 return QObject::eventFilter(o, e);
1348 switch (e->type()) {
1349 case QEvent::KeyPress: {
1350 QKeyEvent *ke =
static_cast<QKeyEvent *>(e);
1352 QModelIndex curIndex = d->popup->currentIndex();
1353 QModelIndexList selList = d->popup->selectionModel()->selectedIndexes();
1355 const int key = ke->key();
1357 if ((key == Qt::Key_Up || key == Qt::Key_Down) && selList.isEmpty() && curIndex.isValid()
1358 && d->mode == QCompleter::UnfilteredPopupCompletion) {
1359 d->setCurrentIndex(curIndex);
1368 if (ke->modifiers() & Qt::ControlModifier)
1373 if (!curIndex.isValid()) {
1374 int rowCount = d->proxy->rowCount();
1375 QModelIndex lastIndex = d->proxy->index(rowCount - 1, d->column);
1376 d->setCurrentIndex(lastIndex);
1378 }
else if (curIndex.row() == 0) {
1380 d->setCurrentIndex(QModelIndex());
1386 if (!curIndex.isValid()) {
1387 QModelIndex firstIndex = d->proxy->index(0, d->column);
1388 d->setCurrentIndex(firstIndex);
1390 }
else if (curIndex.row() == d->proxy->rowCount() - 1) {
1392 d->setCurrentIndex(QModelIndex());
1397 case Qt::Key_PageUp:
1398 case Qt::Key_PageDown:
1405 d->eatFocusOut =
false;
1406 (
static_cast<QObject *>(d->widget))->event(ke);
1407 d->eatFocusOut =
true;
1409 if (!d->widget || e->isAccepted() || !d->popup->isVisible()) {
1411 if (d->widget && !d->widget->hasFocus())
1413 if (e->isAccepted())
1418#if QT_CONFIG(shortcut)
1419 if (ke->matches(QKeySequence::Cancel)) {
1425 case Qt::Key_Return:
1429 if (curIndex.isValid())
1430 d->_q_complete(curIndex);
1434 if (ke->modifiers() & Qt::AltModifier)
1438 case Qt::Key_Backtab:
1449 case QEvent::MouseButtonPress:
1450 if (!d->popup->underMouse()) {
1451 if (!QGuiApplicationPrivate::maybeForwardEventToVirtualKeyboard(e))
1457 case QEvent::MouseButtonRelease:
1458 QGuiApplicationPrivate::maybeForwardEventToVirtualKeyboard(e);
1460 case QEvent::InputMethod:
1461 case QEvent::ShortcutOverride:
1463 QCoreApplication::sendEvent(d->widget, e);
1473
1474
1475
1476
1477
1478
1479
1480
1481
1482void QCompleter::complete(
const QRect& rect)
1485 QModelIndex idx = d->proxy->currentIndex(
false);
1486 d->hiddenBecauseNoMatch =
false;
1487 if (d->mode == QCompleter::InlineCompletion) {
1489 d->_q_complete(idx,
true);
1493 Q_ASSERT(d->widget);
1494 if ((d->mode == QCompleter::PopupCompletion && !idx.isValid())
1495 || (d->mode == QCompleter::UnfilteredPopupCompletion && d->proxy->rowCount() == 0)) {
1498 d->hiddenBecauseNoMatch =
true;
1503 if (d->mode == QCompleter::UnfilteredPopupCompletion)
1504 d->setCurrentIndex(idx,
false);
1507 d->popupRect = rect;
1511
1512
1513
1514
1515
1516
1517
1518
1519bool QCompleter::setCurrentRow(
int row)
1522 return d->proxy->setCurrentRow(row);
1526
1527
1528
1529
1530int QCompleter::currentRow()
const
1532 Q_D(
const QCompleter);
1533 return d->proxy->currentRow();
1537
1538
1539
1540
1541int QCompleter::completionCount()
const
1543 Q_D(
const QCompleter);
1544 return d->proxy->completionCount();
1548
1549
1550
1551
1552
1553
1554
1555
1556
1557
1560
1561
1562
1563
1564
1565
1566
1567
1568
1569
1570
1571
1572
1573
1574
1575
1576
1577
1578
1579
1580
1581void QCompleter::setModelSorting(QCompleter::ModelSorting sorting)
1584 if (d->sorting == sorting)
1586 d->sorting = sorting;
1587 d->proxy->createEngine();
1588 d->proxy->invalidate();
1591QCompleter::ModelSorting QCompleter::modelSorting()
const
1593 Q_D(
const QCompleter);
1598
1599
1600
1601
1602
1603
1604
1605
1606
1607
1608void QCompleter::setCompletionColumn(
int column)
1611 if (d->column == column)
1613#if QT_CONFIG(listview)
1614 if (QListView *listView = qobject_cast<QListView *>(d->popup))
1615 listView->setModelColumn(column);
1618 d->proxy->invalidate();
1621int QCompleter::completionColumn()
const
1623 Q_D(
const QCompleter);
1628
1629
1630
1631
1632
1633
1634
1635void QCompleter::setCompletionRole(
int role)
1638 if (d->role == role)
1641 d->proxy->invalidate();
1644int QCompleter::completionRole()
const
1646 Q_D(
const QCompleter);
1651
1652
1653
1654
1655
1656
1657void QCompleter::setWrapAround(
bool wrap)
1660 if (d->wrap == wrap)
1665bool QCompleter::wrapAround()
const
1667 Q_D(
const QCompleter);
1672
1673
1674
1675
1676
1677
1678int QCompleter::maxVisibleItems()
const
1680 Q_D(
const QCompleter);
1681 return d->maxVisibleItems;
1684void QCompleter::setMaxVisibleItems(
int maxItems)
1687 if (Q_UNLIKELY(maxItems < 0)) {
1688 qWarning(
"QCompleter::setMaxVisibleItems: "
1689 "Invalid max visible items (%d) must be >= 0", maxItems);
1692 d->maxVisibleItems = maxItems;
1696
1697
1698
1699
1700
1701
1702
1703void QCompleter::setCaseSensitivity(Qt::CaseSensitivity cs)
1709 d->proxy->createEngine();
1710 d->proxy->invalidate();
1713Qt::CaseSensitivity QCompleter::caseSensitivity()
const
1715 Q_D(
const QCompleter);
1720
1721
1722
1723
1724
1725
1726void QCompleter::setCompletionPrefix(
const QString &prefix)
1730 d->proxy->filter(splitPath(prefix));
1733QString QCompleter::completionPrefix()
const
1735 Q_D(
const QCompleter);
1740
1741
1742
1743
1744QModelIndex QCompleter::currentIndex()
const
1746 Q_D(
const QCompleter);
1747 return d->proxy->currentIndex(
false);
1751
1752
1753
1754
1755
1756
1757QString QCompleter::currentCompletion()
const
1759 Q_D(
const QCompleter);
1760 return pathFromIndex(d->proxy->currentIndex(
true));
1764
1765
1766
1767
1768
1769
1770
1771
1772
1773
1774QAbstractItemModel *QCompleter::completionModel()
const
1776 Q_D(
const QCompleter);
1781
1782
1783
1784
1785
1786
1787
1788
1789
1791QString QCompleter::pathFromIndex(
const QModelIndex& index)
const
1793 Q_D(
const QCompleter);
1794 if (!index.isValid())
1797 QAbstractItemModel *sourceModel = d->proxy->sourceModel();
1800 bool isFsModel =
false;
1801#if QT_CONFIG(filesystemmodel)
1802 isFsModel = qobject_cast<QFileSystemModel *>(d->proxy->sourceModel()) !=
nullptr;
1805 return sourceModel->data(index, d->role).toString();
1807 QModelIndex idx = index;
1811#if QT_CONFIG(filesystemmodel)
1812 t = sourceModel->data(idx, QFileSystemModel::FileNameRole).toString();
1815 QModelIndex parent = idx.parent();
1816 idx = parent.sibling(parent.row(), index.column());
1817 }
while (idx.isValid());
1819#if !defined(Q_OS_WIN)
1820 if (list.size() == 1)
1825 return list.join(QDir::separator());
1829
1830
1831
1832
1833
1834
1835
1836
1837
1838
1839
1840QStringList QCompleter::splitPath(
const QString& path)
const
1842 bool isFsModel =
false;
1843#if QT_CONFIG(filesystemmodel)
1844 Q_D(
const QCompleter);
1845 isFsModel = qobject_cast<QFileSystemModel *>(d->proxy->sourceModel()) !=
nullptr;
1848 if (!isFsModel || path.isEmpty())
1849 return QStringList(completionPrefix());
1851 QString pathCopy = QDir::toNativeSeparators(path);
1852#if defined(Q_OS_WIN)
1853 if (pathCopy ==
"\\"_L1 || pathCopy ==
"\\\\"_L1)
1854 return QStringList(pathCopy);
1855 const bool startsWithDoubleSlash = pathCopy.startsWith(
"\\\\"_L1);
1856 if (startsWithDoubleSlash)
1857 pathCopy = pathCopy.mid(2);
1860 const QChar sep = QDir::separator();
1861 QStringList parts = pathCopy.split(sep);
1863#if defined(Q_OS_WIN)
1864 if (startsWithDoubleSlash)
1865 parts[0].prepend(
"\\\\"_L1);
1867 if (pathCopy[0] == sep)
1875
1876
1877
1878
1879
1880
1881
1884
1885
1886
1887
1888
1889
1892
1893
1894
1895
1896
1897
1898
1901
1902
1903
1904
1905
1906
1910#include "moc_qcompleter.cpp"
1912#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)