00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00141 #include "qcompleter_p.h"
00142
00143 #ifndef QT_NO_COMPLETER
00144
00145 #include "QtGui/qscrollbar.h"
00146 #include "QtGui/qstringlistmodel.h"
00147 #include "QtGui/qdirmodel.h"
00148 #include "QtGui/qheaderview.h"
00149 #include "QtGui/qlistview.h"
00150 #include "QtGui/qapplication.h"
00151 #include "QtGui/qevent.h"
00152 #include "QtGui/qheaderview.h"
00153 #include "QtGui/qdesktopwidget.h"
00154
00155 void QCompletionModel::setSourceModel(QAbstractItemModel *source)
00156 {
00157 if (model)
00158 QObject::disconnect(model, 0, this, 0);
00159
00160 QAbstractProxyModel::setSourceModel(source);
00161 model = sourceModel();
00162
00163
00164 connect(model, SIGNAL(modelReset()), this, SLOT(invalidate()));
00165 connect(model, SIGNAL(destroyed()), this, SLOT(modelDestroyed()));
00166 connect(model, SIGNAL(layoutChanged()), this, SLOT(invalidate()));
00167 connect(model, SIGNAL(rowsInserted(QModelIndex,int,int)), this, SLOT(invalidate()));
00168 connect(model, SIGNAL(rowsRemoved(QModelIndex,int,int)), this, SLOT(invalidate()));
00169 connect(model, SIGNAL(columnsInserted(QModelIndex,int,int)), this, SLOT(invalidate()));
00170 connect(model, SIGNAL(columnsRemoved(QModelIndex,int,int)), this, SLOT(invalidate()));
00171 connect(model, SIGNAL(dataChanged(QModelIndex,QModelIndex)), this, SLOT(invalidate()));
00172
00173 invalidate();
00174 }
00175
00176 void QCompletionModel::createEngine()
00177 {
00178 bool sortedEngine = false;
00179 switch (c->sorting) {
00180 case QCompleter::UnsortedModel:
00181 sortedEngine = false;
00182 break;
00183 case QCompleter::CaseSensitivelySortedModel:
00184 sortedEngine = c->cs == Qt::CaseSensitive;
00185 break;
00186 case QCompleter::CaseInsensitivelySortedModel:
00187 sortedEngine = c->cs == Qt::CaseInsensitive;
00188 break;
00189 }
00190
00191 delete engine;
00192 if (sortedEngine)
00193 engine = new QSortedModelEngine(c);
00194 else
00195 engine = new QUnsortedModelEngine(c);
00196 }
00197
00198 QModelIndex QCompletionModel::mapToSource(const QModelIndex& index) const
00199 {
00200 if (!index.isValid())
00201 return QModelIndex();
00202
00203 int row;
00204 QModelIndex parent = engine->curParent;
00205 if (!showAll) {
00206 if (!engine->matchCount())
00207 return QModelIndex();
00208 Q_ASSERT(index.row() < engine->matchCount());
00209 QIndexMapper& rootIndices = engine->historyMatch.indices;
00210 if (index.row() < rootIndices.count()) {
00211 row = rootIndices[index.row()];
00212 parent = QModelIndex();
00213 } else {
00214 row = engine->curMatch.indices[index.row() - rootIndices.count()];
00215 }
00216 } else {
00217 row = index.row();
00218 }
00219
00220 return model->index(row, index.column(), parent);
00221 }
00222
00223 QModelIndex QCompletionModel::mapFromSource(const QModelIndex& idx) const
00224 {
00225 if (!idx.isValid())
00226 return QModelIndex();
00227
00228 int row = -1;
00229 if (!showAll) {
00230 if (!engine->matchCount())
00231 return QModelIndex();
00232
00233 QIndexMapper& rootIndices = engine->historyMatch.indices;
00234 if (idx.parent().isValid()) {
00235 if (idx.parent() != engine->curParent)
00236 return QModelIndex();
00237 } else {
00238 row = rootIndices.indexOf(idx.row());
00239 if (row == -1 && engine->curParent.isValid())
00240 return QModelIndex();
00241 }
00242
00243 if (row == -1) {
00244 QIndexMapper& indices = engine->curMatch.indices;
00245 engine->filterOnDemand(idx.row() - indices.last());
00246 row = indices.indexOf(idx.row()) + rootIndices.count();
00247 }
00248
00249 if (row == -1)
00250 return QModelIndex();
00251 } else {
00252 if (idx.parent() != engine->curParent)
00253 return QModelIndex();
00254 row = idx.row();
00255 }
00256
00257 return createIndex(row, idx.column());
00258 }
00259
00260 bool QCompletionModel::setCurrentRow(int row)
00261 {
00262 if (row < 0 || !engine->matchCount())
00263 return false;
00264
00265 if (row >= engine->matchCount())
00266 engine->filterOnDemand(row + 1 - engine->matchCount());
00267
00268 if (row >= engine->matchCount())
00269 return false;
00270
00271 engine->curRow = row;
00272 return true;
00273 }
00274
00275 QModelIndex QCompletionModel::currentIndex(bool sourceIndex) const
00276 {
00277 if (!engine->matchCount())
00278 return QModelIndex();
00279
00280 int row = engine->curRow;
00281 if (showAll)
00282 row = engine->curMatch.indices[engine->curRow];
00283
00284 QModelIndex idx = createIndex(row, c->column);
00285 if (!sourceIndex)
00286 return idx;
00287 return mapToSource(idx);
00288 }
00289
00290 QModelIndex QCompletionModel::index(int row, int column, const QModelIndex& parent) const
00291 {
00292 if (row < 0 || column < 0 || column >= columnCount(parent) || parent.isValid())
00293 return QModelIndex();
00294
00295 if (!showAll) {
00296 if (!engine->matchCount())
00297 return QModelIndex();
00298 if (row >= engine->historyMatch.indices.count()) {
00299 int want = row + 1 - engine->matchCount();
00300 if (want > 0)
00301 engine->filterOnDemand(want);
00302 if (row >= engine->matchCount())
00303 return QModelIndex();
00304 }
00305 } else {
00306 if (row >= model->rowCount(engine->curParent))
00307 return QModelIndex();
00308 }
00309
00310 return createIndex(row, column);
00311 }
00312
00313 int QCompletionModel::completionCount() const
00314 {
00315 if (!engine->matchCount())
00316 return 0;
00317
00318 engine->filterOnDemand(INT_MAX);
00319 return engine->matchCount();
00320 }
00321
00322 int QCompletionModel::rowCount(const QModelIndex &parent) const
00323 {
00324 if (parent.isValid())
00325 return 0;
00326
00327 if (showAll) {
00328
00329 if (engine->curParts.count() != 1 && !engine->matchCount()
00330 && !engine->curParent.isValid())
00331 return 0;
00332 return model->rowCount(engine->curParent);
00333 }
00334
00335 return completionCount();
00336 }
00337
00338 void QCompletionModel::setFiltered(bool filtered)
00339 {
00340 if (showAll == !filtered)
00341 return;
00342 showAll = !filtered;
00343 reset();
00344 }
00345
00346 bool QCompletionModel::hasChildren(const QModelIndex &parent) const
00347 {
00348 if (parent.isValid())
00349 return false;
00350
00351 if (showAll)
00352 return model->hasChildren(mapToSource(parent));
00353
00354 if (!engine->matchCount())
00355 return false;
00356
00357 return true;
00358 }
00359
00360 QVariant QCompletionModel::data(const QModelIndex& index, int role) const
00361 {
00362 return model->data(mapToSource(index), role);
00363 }
00364
00365 void QCompletionModel::modelDestroyed()
00366 {
00367 QAbstractProxyModel::setSourceModel(0);
00368 model = sourceModel();
00369 invalidate();
00370 }
00371
00372 void QCompletionModel::invalidate()
00373 {
00374 engine->cache.clear();
00375 filter(engine->curParts);
00376 }
00377
00378 void QCompletionModel::filter(const QStringList& parts)
00379 {
00380 engine->filter(parts);
00381 reset();
00382 }
00383
00385 void QCompletionEngine::filter(const QStringList& parts)
00386 {
00387 const QAbstractItemModel *model = c->proxy->sourceModel();
00388 curParts = parts;
00389 if (curParts.isEmpty())
00390 curParts.append(QString());
00391
00392 curRow = -1;
00393 curParent = QModelIndex();
00394 curMatch = QMatchData();
00395 historyMatch = filterHistory();
00396
00397 QModelIndex parent;
00398 for (int i = 0; i < curParts.count() - 1; i++) {
00399 QString part = curParts[i];
00400 int emi = filter(part, parent, -1).exactMatchIndex;
00401 if (emi == -1)
00402 return;
00403 parent = model->index(emi, c->column, parent);
00404 }
00405
00406
00407
00408 curParent = parent;
00409 if (curParts.last().isEmpty())
00410 curMatch = QMatchData(QIndexMapper(0, model->rowCount(curParent) - 1), -1, false);
00411 else
00412 curMatch = filter(curParts.last(), curParent, 1);
00413 curRow = curMatch.isValid() ? 0 : -1;
00414 }
00415
00416 QMatchData QCompletionEngine::filterHistory()
00417 {
00418 if (curParts.count() <= 1 || c->proxy->showAll)
00419 return QMatchData();
00420 QAbstractItemModel *source = c->proxy->model;
00421 bool dirModel = false;
00422 #ifndef QT_NO_DIRMODEL
00423 dirModel = (qobject_cast<QDirModel *>(source) != 0);
00424 #endif
00425 QVector<int> v;
00426 QIndexMapper im(v);
00427 QMatchData m(im, -1, true);
00428
00429 for (int i = 0; i < source->rowCount(); i++) {
00430 QString str = source->index(i, c->column).data().toString();
00431 if (str.startsWith(c->prefix, c->cs)
00432 #ifndef Q_OS_WIN
00433 && (!dirModel || str != QDir::separator())
00434 #endif
00435 )
00436 m.indices.append(i);
00437 }
00438 return m;
00439 }
00440
00441
00442 bool QCompletionEngine::matchHint(QString part, const QModelIndex& parent, QMatchData *hint)
00443 {
00444 if (c->cs == Qt::CaseInsensitive)
00445 part = part.toLower();
00446
00447 const CacheItem& map = cache[parent];
00448
00449 QString key = part;
00450 while (!key.isEmpty()) {
00451 key.chop(1);
00452 if (map.contains(key)) {
00453 *hint = map[key];
00454 return true;
00455 }
00456 }
00457
00458 return false;
00459 }
00460
00461 bool QCompletionEngine::lookupCache(QString part, const QModelIndex& parent, QMatchData *m)
00462 {
00463 if (c->cs == Qt::CaseInsensitive)
00464 part = part.toLower();
00465 const CacheItem& map = cache[parent];
00466 if (!map.contains(part))
00467 return false;
00468 *m = map[part];
00469 return true;
00470 }
00471
00472
00473 void QCompletionEngine::saveInCache(QString part, const QModelIndex& parent, const QMatchData& m)
00474 {
00475 QMatchData old = cache[parent].take(part);
00476 cost = cost + m.indices.cost() - old.indices.cost();
00477 if (cost * sizeof(int) > 1024 * 1024) {
00478 QMap<QModelIndex, CacheItem>::iterator it1 ;
00479 for (it1 = cache.begin(); it1 != cache.end(); it1++) {
00480 CacheItem& ci = it1.value();
00481 int sz = ci.count()/2;
00482 QMap<QString, QMatchData>::iterator it2 = ci.begin();
00483 for (int i = 0; it2 != ci.end() && i < sz; i++, ++it2) {
00484 cost -= it2.value().indices.cost();
00485 ci.erase(it2);
00486 }
00487 if (ci.count() == 0)
00488 cache.erase(it1);
00489 }
00490 }
00491
00492 if (c->cs == Qt::CaseInsensitive)
00493 part = part.toLower();
00494 cache[parent][part] = m;
00495 }
00496
00498 QIndexMapper QSortedModelEngine::indexHint(QString part, const QModelIndex& parent)
00499 {
00500 const QAbstractItemModel *model = c->proxy->sourceModel();
00501
00502 if (c->cs == Qt::CaseInsensitive)
00503 part = part.toLower();
00504
00505 const CacheItem& map = cache[parent];
00506
00507
00508 int to = model->rowCount(parent) - 1;
00509 int from = 0;
00510 const CacheItem::const_iterator it = map.lowerBound(part);
00511
00512
00513 for(CacheItem::const_iterator it1 = it; it1-- != map.constBegin();) {
00514 const QMatchData& value = it1.value();
00515 if (value.isValid()) {
00516 from = value.indices.last() + 1;
00517 break;
00518 }
00519 }
00520
00521
00522 for(CacheItem::const_iterator it2 = it; it2 != map.constEnd(); ++it2) {
00523 const QMatchData& value = it2.value();
00524 if (value.isValid() && !it2.key().startsWith(part)) {
00525 to = value.indices[0] - 1;
00526 break;
00527 }
00528 }
00529
00530 return QIndexMapper(from, to);
00531 }
00532
00533 QMatchData QSortedModelEngine::filter(const QString& part, const QModelIndex& parent, int)
00534 {
00535 const QAbstractItemModel *model = c->proxy->sourceModel();
00536
00537 QMatchData hint;
00538 if (lookupCache(part, parent, &hint))
00539 return hint;
00540
00541 QIndexMapper indices;
00542
00543 if (matchHint(part, parent, &hint)) {
00544 if (!hint.isValid())
00545 return QMatchData();
00546 indices = hint.indices;
00547 } else {
00548 indices = indexHint(part, parent);
00549 }
00550
00551
00552 int high = indices.to() + 1;
00553 int low = indices.from() - 1;
00554 int probe;
00555 QModelIndex probeIndex;
00556 QString probeData;
00557
00558 while (high - low > 1)
00559 {
00560 probe = (high + low) / 2;
00561 probeIndex = model->index(probe, c->column, parent);
00562 probeData = model->data(probeIndex, c->role).toString();
00563 if (QString::compare(probeData, part, c->cs) >= 0)
00564 high = probe;
00565 else
00566 low = probe;
00567 }
00568
00569 if (low == indices.to()) {
00570 saveInCache(part, parent, QMatchData());
00571 return QMatchData();
00572 }
00573
00574 probeIndex = model->index(low + 1, c->column, parent);
00575 probeData = model->data(probeIndex, c->role).toString();
00576 if (!probeData.startsWith(part, c->cs)) {
00577 saveInCache(part, parent, QMatchData());
00578 return QMatchData();
00579 }
00580
00581 int emi = QString::compare(probeData, part, c->cs) == 0 ? low+1 : -1;
00582
00583 int from = low + 1;
00584 high = indices.to() + 1;
00585 low = from;
00586
00587 while (high - low > 1)
00588 {
00589 probe = (high + low) / 2;
00590 probeIndex = model->index(probe, c->column, parent);
00591 probeData = model->data(probeIndex, c->role).toString();
00592 if (probeData.startsWith(part, c->cs))
00593 low = probe;
00594 else
00595 high = probe;
00596 }
00597
00598 QMatchData m(QIndexMapper(from, high - 1), emi, false);
00599 saveInCache(part, parent, m);
00600 return m;
00601 }
00602
00604 int QUnsortedModelEngine::buildIndices(const QString& str, const QModelIndex& parent, int n,
00605 const QIndexMapper& indices, QMatchData* m)
00606 {
00607 Q_ASSERT(m->partial);
00608 Q_ASSERT(n != -1 || m->exactMatchIndex == -1);
00609 const QAbstractItemModel *model = c->proxy->sourceModel();
00610 int i, count = 0;
00611
00612 for (i = 0; i < indices.count() && count != n; ++i) {
00613 QModelIndex idx = model->index(indices[i], c->column, parent);
00614 QString data = model->data(idx, c->role).toString();
00615 if (!data.startsWith(str, c->cs))
00616 continue;
00617 m->indices.append(indices[i]);
00618 ++count;
00619 if (m->exactMatchIndex == -1 && QString::compare(data, str, c->cs) == 0) {
00620 m->exactMatchIndex = indices[i];
00621 if (n == -1)
00622 return indices[i];
00623 }
00624 }
00625 return indices[i-1];
00626 }
00627
00628 void QUnsortedModelEngine::filterOnDemand(int n)
00629 {
00630 Q_ASSERT(matchCount());
00631 if (!curMatch.partial)
00632 return;
00633 Q_ASSERT(n >= -1);
00634 const QAbstractItemModel *model = c->proxy->sourceModel();
00635 int lastRow = model->rowCount(curParent) - 1;
00636 QIndexMapper im(curMatch.indices.last() + 1, lastRow);
00637 int lastIndex = buildIndices(curParts.last(), curParent, n, im, &curMatch);
00638 curMatch.partial = (lastRow != lastIndex);
00639 saveInCache(curParts.last(), curParent, curMatch);
00640 }
00641
00642 QMatchData QUnsortedModelEngine::filter(const QString& part, const QModelIndex& parent, int n)
00643 {
00644 QMatchData hint;
00645
00646 QVector<int> v;
00647 QIndexMapper im(v);
00648 QMatchData m(im, -1, true);
00649
00650 const QAbstractItemModel *model = c->proxy->sourceModel();
00651 bool foundInCache = lookupCache(part, parent, &m);
00652
00653 if (!foundInCache) {
00654 if (matchHint(part, parent, &hint) && !hint.isValid())
00655 return QMatchData();
00656 }
00657
00658 if (!foundInCache && !hint.isValid()) {
00659 const int lastRow = model->rowCount(parent) - 1;
00660 QIndexMapper all(0, lastRow);
00661 int lastIndex = buildIndices(part, parent, n, all, &m);
00662 m.partial = (lastIndex != lastRow);
00663 } else {
00664 if (!foundInCache) {
00665 buildIndices(part, parent, INT_MAX, hint.indices, &m);
00666 m.partial = hint.partial;
00667 }
00668 if (m.partial && ((n == -1 && m.exactMatchIndex == -1) || (m.indices.count() < n))) {
00669
00670 const int lastRow = model->rowCount(parent) - 1;
00671 QIndexMapper rest(hint.indices.last() + 1, lastRow);
00672 int want = n == -1 ? -1 : n - m.indices.count();
00673 int lastIndex = buildIndices(part, parent, want, rest, &m);
00674 m.partial = (lastRow != lastIndex);
00675 }
00676 }
00677
00678 saveInCache(part, parent, m);
00679 return m;
00680 }
00681
00683 QCompleterPrivate::QCompleterPrivate()
00684 : widget(0), proxy(0), popup(0), cs(Qt::CaseSensitive), role(Qt::EditRole), column(0),
00685 sorting(QCompleter::UnsortedModel), eatFocusOut(true)
00686 {
00687 }
00688
00689 void QCompleterPrivate::init(QAbstractItemModel *m)
00690 {
00691 Q_Q(QCompleter);
00692 proxy = new QCompletionModel(this, q);
00693 q->setModel(m);
00694 #ifdef QT_NO_LISTVIEW
00695 q->setCompletionMode(QCompleter::InlineCompletion);
00696 #else
00697 QListView *listView = new QListView;
00698 listView->setEditTriggers(QAbstractItemView::NoEditTriggers);
00699 listView->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
00700 listView->setSelectionBehavior(QAbstractItemView::SelectRows);
00701 listView->setSelectionMode(QAbstractItemView::SingleSelection);
00702 listView->setModelColumn(column);
00703 q->setPopup(listView);
00704 q->setCompletionMode(QCompleter::PopupCompletion);
00705 #endif // QT_NO_LISTVIEW
00706 }
00707
00708 void QCompleterPrivate::setCurrentIndex(QModelIndex index, bool select)
00709 {
00710 if (!select) {
00711 popup->selectionModel()->setCurrentIndex(index, QItemSelectionModel::NoUpdate);
00712 } else {
00713 if (!index.isValid())
00714 popup->selectionModel()->clear();
00715 else
00716 popup->selectionModel()->setCurrentIndex(index, QItemSelectionModel::Select
00717 | QItemSelectionModel::Rows);
00718 }
00719 index = popup->selectionModel()->currentIndex();
00720 if (!index.isValid())
00721 popup->scrollToTop();
00722 else
00723 popup->scrollTo(index, QAbstractItemView::PositionAtTop);
00724 }
00725
00726 void QCompleterPrivate::_q_completionSelected(const QItemSelection& selection)
00727 {
00728 QModelIndex index;
00729 if (!selection.indexes().isEmpty())
00730 index = selection.indexes().first();
00731
00732 _q_complete(index, true);
00733 }
00734
00735 void QCompleterPrivate::_q_complete(QModelIndex index, bool highlighted)
00736 {
00737 Q_Q(QCompleter);
00738 QString completion;
00739
00740 if (!index.isValid())
00741 completion = prefix;
00742 else {
00743 QModelIndex si = proxy->mapToSource(index);
00744 si = si.sibling(si.row(), column);
00745 completion = q->pathFromIndex(si);
00746 #ifndef QT_NO_DIRMODEL
00747
00748 if (mode == QCompleter::InlineCompletion) {
00749 if (qobject_cast<QDirModel *>(proxy->sourceModel()) && QFileInfo(completion).isDir())
00750 completion += QDir::separator();
00751 }
00752 #endif
00753 }
00754
00755 if (highlighted) {
00756 emit q->highlighted(index);
00757 emit q->highlighted(completion);
00758 } else {
00759 emit q->activated(index);
00760 emit q->activated(completion);
00761 }
00762 }
00763
00764 void QCompleterPrivate::showPopup(const QRect& rect)
00765 {
00766 const QRect screen = QApplication::desktop()->availableGeometry(widget);
00767 Qt::LayoutDirection dir = widget->layoutDirection();
00768 QPoint pos;
00769 int rw, rh, w;
00770 int h = (popup->sizeHintForRow(0) * qMin(7, popup->model()->rowCount()) + 3) + 3;
00771 QScrollBar *hsb = popup->horizontalScrollBar();
00772 if (hsb && hsb->isVisible())
00773 h += popup->horizontalScrollBar()->sizeHint().height();
00774
00775 if (rect.isValid()) {
00776 rh = rect.height();
00777 w = rw = rect.width();
00778 pos = widget->mapToGlobal(dir == Qt::RightToLeft ? rect.bottomRight() : rect.bottomLeft());
00779 } else {
00780 rh = widget->height();
00781 rw = widget->width();
00782 pos = widget->mapToGlobal(QPoint(0, widget->height() - 2));
00783 w = widget->width();
00784 }
00785
00786 if ((pos.x() + rw) > (screen.x() + screen.width()))
00787 pos.setX(screen.x() + screen.width() - w);
00788 if (pos.x() < screen.x())
00789 pos.setX(screen.x());
00790 if (((pos.y() + rh) > (screen.y() + screen.height())) && ((pos.y() - h - rh) >= 0))
00791 pos.setY(pos.y() - qMax(h, popup->minimumHeight()) - rh + 2);
00792
00793 popup->setGeometry(pos.x(), pos.y(), w, h);
00794
00795 if (!popup->isVisible())
00796 popup->show();
00797 }
00798
00802 QCompleter::QCompleter(QObject *parent)
00803 : QObject(*new QCompleterPrivate(), parent)
00804 {
00805 Q_D(QCompleter);
00806 d->init();
00807 }
00808
00813 QCompleter::QCompleter(QAbstractItemModel *model, QObject *parent)
00814 : QObject(*new QCompleterPrivate(), parent)
00815 {
00816 Q_D(QCompleter);
00817 d->init(model);
00818 }
00819
00820 #ifndef QT_NO_STRINGLISTMODEL
00821
00825 QCompleter::QCompleter(const QStringList& list, QObject *parent)
00826 : QObject(*new QCompleterPrivate(), parent)
00827 {
00828 Q_D(QCompleter);
00829 d->init(new QStringListModel(list, this));
00830 }
00831 #endif // QT_NO_STRINGLISTMODEL
00832
00836 QCompleter::~QCompleter()
00837 {
00838 }
00839
00849 void QCompleter::setWidget(QWidget *widget)
00850 {
00851 Q_D(QCompleter);
00852 d->widget = widget;
00853 if (d->popup)
00854 d->popup->setFocusProxy(d->widget);
00855 setCompletionMode(d->mode);
00856 }
00857
00863 QWidget *QCompleter::widget() const
00864 {
00865 Q_D(const QCompleter);
00866 return d->widget;
00867 }
00868
00880 void QCompleter::setModel(QAbstractItemModel *model)
00881 {
00882 Q_D(QCompleter);
00883 QAbstractItemModel *oldModel = d->proxy->model;
00884 d->proxy->setSourceModel(model);
00885 if (d->popup)
00886 setPopup(d->popup);
00887 if (oldModel && oldModel->QObject::parent() == this)
00888 delete oldModel;
00889 #ifndef QT_NO_DIRMODEL
00890 if (qobject_cast<QDirModel *>(model)) {
00891 #ifdef Q_OS_WIN
00892 setCaseSensitivity(Qt::CaseInsensitive);
00893 #else
00894 setCaseSensitivity(Qt::CaseSensitive);
00895 #endif
00896 }
00897 #endif // QT_NO_DIRMODEL
00898 }
00899
00905 QAbstractItemModel *QCompleter::model() const
00906 {
00907 Q_D(const QCompleter);
00908 return d->proxy->sourceModel();
00909 }
00910
00929 void QCompleter::setCompletionMode(QCompleter::CompletionMode mode)
00930 {
00931 Q_D(QCompleter);
00932
00933 d->mode = mode;
00934 d->proxy->setFiltered(mode != QCompleter::UnfilteredPopupCompletion);
00935
00936 if (mode == QCompleter::InlineCompletion) {
00937 if (d->widget)
00938 d->widget->removeEventFilter(this);
00939 return;
00940 }
00941
00942 if (d->widget)
00943 d->widget->installEventFilter(this);
00944 }
00945
00946 QCompleter::CompletionMode QCompleter::completionMode() const
00947 {
00948 Q_D(const QCompleter);
00949 return d->mode;
00950 }
00951
00967 void QCompleter::setPopup(QAbstractItemView *popup)
00968 {
00969 Q_D(QCompleter);
00970 Q_ASSERT(popup != 0);
00971 if (d->popup) {
00972 QObject::disconnect(d->popup->selectionModel(), 0, this, 0);
00973 QObject::disconnect(d->popup, 0, this, 0);
00974 }
00975 if (d->popup != popup)
00976 delete d->popup;
00977 if (popup->model() != d->proxy)
00978 popup->setModel(d->proxy);
00979 popup->hide();
00980 popup->setParent(0, Qt::Popup);
00981 popup->setFocusPolicy(Qt::NoFocus);
00982 popup->setFocusProxy(d->widget);
00983 popup->installEventFilter(this);
00984 popup->setItemDelegate(new QCompleterItemDelegate(popup));
00985
00986 QObject::connect(popup, SIGNAL(clicked(QModelIndex)),
00987 this, SLOT(_q_complete(QModelIndex)));
00988 QObject::connect(popup, SIGNAL(clicked(QModelIndex)), popup, SLOT(hide()));
00989
00990 QObject::connect(popup->selectionModel(), SIGNAL(selectionChanged(QItemSelection,QItemSelection)),
00991 this, SLOT(_q_completionSelected(QItemSelection)));
00992 d->popup = popup;
00993 }
00994
01000 QAbstractItemView *QCompleter::popup() const
01001 {
01002 Q_D(const QCompleter);
01003 return d->popup;
01004 }
01005
01009 bool QCompleter::event(QEvent *ev)
01010 {
01011 return QObject::event(ev);
01012 }
01013
01017 bool QCompleter::eventFilter(QObject *o, QEvent *e)
01018 {
01019 Q_D(QCompleter);
01020
01021 if (d->eatFocusOut && o == d->widget && e->type() == QEvent::FocusOut) {
01022 if (d->popup && d->popup->isVisible())
01023 return true;
01024 }
01025
01026 if (o != d->popup)
01027 return QObject::eventFilter(o, e);
01028
01029 switch (e->type()) {
01030 case QEvent::KeyPress: {
01031 QKeyEvent *ke = static_cast<QKeyEvent *>(e);
01032
01033 QModelIndex curIndex = d->popup->currentIndex();
01034 QModelIndexList selList = d->popup->selectionModel()->selectedIndexes();
01035
01036 const int key = ke->key();
01037
01038 if ((key == Qt::Key_Up || key == Qt::Key_Down) && selList.isEmpty() && curIndex.isValid()
01039 && d->mode == QCompleter::UnfilteredPopupCompletion) {
01040 d->setCurrentIndex(curIndex);
01041 return true;
01042 }
01043
01044
01045
01046 switch (key) {
01047 case Qt::Key_End:
01048 case Qt::Key_Home:
01049 if (ke->modifiers() & Qt::ControlModifier)
01050 return false;
01051 break;
01052
01053 case Qt::Key_Up:
01054 if (!curIndex.isValid()) {
01055 int rowCount = d->proxy->rowCount();
01056 QModelIndex lastIndex = d->proxy->index(rowCount - 1, 0);
01057 d->setCurrentIndex(lastIndex);
01058 return true;
01059 } else if (curIndex.row() == 0) {
01060 d->setCurrentIndex(QModelIndex());
01061 return true;
01062 }
01063 return false;
01064
01065 case Qt::Key_Down:
01066 if (!curIndex.isValid()) {
01067 QModelIndex firstIndex = d->proxy->index(0, 0);
01068 d->setCurrentIndex(firstIndex);
01069 return true;
01070 } else if (curIndex.row() == d->proxy->rowCount() - 1) {
01071 d->setCurrentIndex(QModelIndex());
01072 return true;
01073 }
01074 return false;
01075
01076 case Qt::Key_PageUp:
01077 case Qt::Key_PageDown:
01078 return false;
01079 }
01080
01081
01082
01083 d->eatFocusOut = false;
01084 (static_cast<QObject *>(d->widget))->event(ke);
01085 d->eatFocusOut = true;
01086 if (!d->widget || e->isAccepted() || !d->popup->isVisible()) {
01087
01088 if (d->widget && !d->widget->hasFocus())
01089 d->popup->hide();
01090 return true;
01091 }
01092
01093
01094 switch (key) {
01095 case Qt::Key_Return:
01096 case Qt::Key_Enter:
01097 case Qt::Key_Tab:
01098 d->popup->hide();
01099 if (curIndex.isValid())
01100 d->_q_complete(curIndex);
01101 break;
01102
01103 case Qt::Key_F4:
01104 if (ke->modifiers() & Qt::AltModifier)
01105 d->popup->hide();
01106 break;
01107
01108 case Qt::Key_Backtab:
01109 case Qt::Key_Escape:
01110 d->popup->hide();
01111 break;
01112
01113 default:
01114 break;
01115 }
01116
01117 return true;
01118 }
01119
01120 case QEvent::MouseButtonPress:
01121 if (!d->popup->underMouse()) {
01122 d->popup->hide();
01123 return true;
01124 }
01125 return false;
01126
01127 default:
01128 return false;
01129 }
01130 }
01131
01142 void QCompleter::complete(const QRect& rect)
01143 {
01144 Q_D(QCompleter);
01145 QModelIndex idx = d->proxy->currentIndex(false);
01146 if (d->mode == QCompleter::InlineCompletion) {
01147 if (idx.isValid())
01148 d->_q_complete(idx, true);
01149 return;
01150 }
01151
01152 Q_ASSERT(d->widget != 0);
01153 if ((d->mode == QCompleter::PopupCompletion && !idx.isValid())
01154 || (d->mode == QCompleter::UnfilteredPopupCompletion && d->proxy->rowCount() == 0)) {
01155 d->popup->hide();
01156 return;
01157 }
01158
01159 if (d->mode == QCompleter::UnfilteredPopupCompletion)
01160 d->setCurrentIndex(idx, false);
01161
01162 d->showPopup(rect);
01163 }
01164
01174 bool QCompleter::setCurrentRow(int row)
01175 {
01176 Q_D(QCompleter);
01177 return d->proxy->setCurrentRow(row);
01178 }
01179
01185 int QCompleter::currentRow() const
01186 {
01187 Q_D(const QCompleter);
01188 return d->proxy->currentRow();
01189 }
01190
01196 int QCompleter::completionCount() const
01197 {
01198 Q_D(const QCompleter);
01199 return d->proxy->completionCount();
01200 }
01201
01233 void QCompleter::setModelSorting(QCompleter::ModelSorting sorting)
01234 {
01235 Q_D(QCompleter);
01236 if (d->sorting == sorting)
01237 return;
01238 d->sorting = sorting;
01239 d->proxy->createEngine();
01240 d->proxy->invalidate();
01241 }
01242
01243 QCompleter::ModelSorting QCompleter::modelSorting() const
01244 {
01245 Q_D(const QCompleter);
01246 return d->sorting;
01247 }
01248
01260 void QCompleter::setCompletionColumn(int column)
01261 {
01262 Q_D(QCompleter);
01263 if (d->column == column)
01264 return;
01265 #ifndef QT_NO_LISTVIEW
01266 if (QListView *listView = qobject_cast<QListView *>(d->popup))
01267 listView->setModelColumn(column);
01268 #endif
01269 d->column = column;
01270 d->proxy->invalidate();
01271 }
01272
01273 int QCompleter::completionColumn() const
01274 {
01275 Q_D(const QCompleter);
01276 return d->column;
01277 }
01278
01287 void QCompleter::setCompletionRole(int role)
01288 {
01289 Q_D(QCompleter);
01290 if (d->role == role)
01291 return;
01292 d->role = role;
01293 d->proxy->invalidate();
01294 }
01295
01296 int QCompleter::completionRole() const
01297 {
01298 Q_D(const QCompleter);
01299 return d->role;
01300 }
01301
01310 void QCompleter::setCaseSensitivity(Qt::CaseSensitivity cs)
01311 {
01312 Q_D(QCompleter);
01313 if (d->cs == cs)
01314 return;
01315 d->cs = cs;
01316 d->proxy->createEngine();
01317 d->proxy->invalidate();
01318 }
01319
01320 Qt::CaseSensitivity QCompleter::caseSensitivity() const
01321 {
01322 Q_D(const QCompleter);
01323 return d->cs;
01324 }
01325
01333 void QCompleter::setCompletionPrefix(const QString &prefix)
01334 {
01335 Q_D(QCompleter);
01336 d->prefix = prefix;
01337 d->proxy->filter(splitPath(prefix));
01338 }
01339
01340 QString QCompleter::completionPrefix() const
01341 {
01342 Q_D(const QCompleter);
01343 return d->prefix;
01344 }
01345
01351 QModelIndex QCompleter::currentIndex() const
01352 {
01353 Q_D(const QCompleter);
01354 return d->proxy->currentIndex(false);
01355 }
01356
01364 QString QCompleter::currentCompletion() const
01365 {
01366 Q_D(const QCompleter);
01367 return pathFromIndex(d->proxy->currentIndex(true));
01368 }
01369
01377 QAbstractItemModel *QCompleter::completionModel() const
01378 {
01379 Q_D(const QCompleter);
01380 return d->proxy;
01381 }
01382
01393 QString QCompleter::pathFromIndex(const QModelIndex& index) const
01394 {
01395 Q_D(const QCompleter);
01396 if (!index.isValid())
01397 return QString();
01398
01399 QAbstractItemModel *sourceModel = d->proxy->sourceModel();
01400 #ifndef QT_NO_DIRMODEL
01401 QDirModel *dirModel = qobject_cast<QDirModel *>(sourceModel);
01402 if (!dirModel)
01403 #endif
01404 return sourceModel->data(index, d->role).toString();
01405
01406 QModelIndex idx = index;
01407 QStringList list;
01408 do {
01409 QString t = sourceModel->data(idx, Qt::EditRole).toString();
01410 list.prepend(t);
01411 QModelIndex parent = idx.parent();
01412 idx = parent.sibling(parent.row(), index.column());
01413 } while (idx.isValid());
01414
01415 #ifndef Q_OS_WIN
01416 if (list.count() == 1)
01417 return list[0];
01418 list[0].clear() ;
01419 #endif
01420
01421 return list.join(QDir::separator());
01422 }
01423
01436 QStringList QCompleter::splitPath(const QString& path) const
01437 {
01438 Q_D(const QCompleter);
01439 bool isDirModel = false;
01440 #ifndef QT_NO_DIRMODEL
01441 isDirModel = qobject_cast<QDirModel *>(d->proxy->sourceModel()) != 0;
01442 #endif
01443
01444 if (!isDirModel || path.isEmpty())
01445 return QStringList(completionPrefix());
01446
01447 QString pathCopy = QDir::toNativeSeparators(path);
01448 QString sep = QDir::separator();
01449 #ifdef Q_OS_WIN
01450 if (pathCopy == QLatin1String("\\") || pathCopy == QLatin1String("\\\\"))
01451 return QStringList(pathCopy);
01452 QString doubleSlash(QLatin1String("\\\\"));
01453 if (pathCopy.startsWith(doubleSlash))
01454 pathCopy = pathCopy.mid(2);
01455 else
01456 doubleSlash.clear();
01457 #endif
01458
01459 QRegExp re(QLatin1String("[") + QRegExp::escape(sep) + QLatin1String("]"));
01460 QStringList parts = pathCopy.split(re);
01461
01462 #ifdef Q_OS_WIN
01463 if (!doubleSlash.isEmpty())
01464 parts[0].prepend(doubleSlash);
01465 #else
01466 if (path[0] == sep[0])
01467 parts[0] = sep[0];
01468 #endif
01469
01470 return parts;
01471 }
01472
01507 #include "moc_qcompleter.cpp"
01508
01509 #endif // QT_NO_COMPLETER