60 For example, here's how to provide auto completions from a simple |
60 For example, here's how to provide auto completions from a simple |
61 word list in a QLineEdit: |
61 word list in a QLineEdit: |
62 |
62 |
63 \snippet doc/src/snippets/code/src_gui_util_qcompleter.cpp 0 |
63 \snippet doc/src/snippets/code/src_gui_util_qcompleter.cpp 0 |
64 |
64 |
65 A QDirModel can be used to provide auto completion of file names. |
65 A QFileSystemModel can be used to provide auto completion of file names. |
66 For example: |
66 For example: |
67 |
67 |
68 \snippet doc/src/snippets/code/src_gui_util_qcompleter.cpp 1 |
68 \snippet doc/src/snippets/code/src_gui_util_qcompleter.cpp 1 |
69 |
69 |
70 To set the model on which QCompleter should operate, call |
70 To set the model on which QCompleter should operate, call |
118 that any item (or sub-item or sub-sub-item) can be unambiguously |
118 that any item (or sub-item or sub-sub-item) can be unambiguously |
119 represented as a string by specifying the path to the item. The |
119 represented as a string by specifying the path to the item. The |
120 completion is then performed one level at a time. |
120 completion is then performed one level at a time. |
121 |
121 |
122 Let's take the example of a user typing in a file system path. |
122 Let's take the example of a user typing in a file system path. |
123 The model is a (hierarchical) QDirModel. The completion |
123 The model is a (hierarchical) QFileSystemModel. The completion |
124 occurs for every element in the path. For example, if the current |
124 occurs for every element in the path. For example, if the current |
125 text is \c C:\Wind, QCompleter might suggest \c Windows to |
125 text is \c C:\Wind, QCompleter might suggest \c Windows to |
126 complete the current path element. Similarly, if the current text |
126 complete the current path element. Similarly, if the current text |
127 is \c C:\Windows\Sy, QCompleter might suggest \c System. |
127 is \c C:\Windows\Sy, QCompleter might suggest \c System. |
128 |
128 |
129 For this kind of completion to work, QCompleter needs to be able to |
129 For this kind of completion to work, QCompleter needs to be able to |
130 split the path into a list of strings that are matched at each level. |
130 split the path into a list of strings that are matched at each level. |
131 For \c C:\Windows\Sy, it needs to be split as "C:", "Windows" and "Sy". |
131 For \c C:\Windows\Sy, it needs to be split as "C:", "Windows" and "Sy". |
132 The default implementation of splitPath(), splits the completionPrefix |
132 The default implementation of splitPath(), splits the completionPrefix |
133 using QDir::separator() if the model is a QDirModel. |
133 using QDir::separator() if the model is a QFileSystemModel. |
134 |
134 |
135 To provide completions, QCompleter needs to know the path from an index. |
135 To provide completions, QCompleter needs to know the path from an index. |
136 This is provided by pathFromIndex(). The default implementation of |
136 This is provided by pathFromIndex(). The default implementation of |
137 pathFromIndex(), returns the data for the \l{Qt::EditRole}{edit role} |
137 pathFromIndex(), returns the data for the \l{Qt::EditRole}{edit role} |
138 for list models and the absolute file path if the mode is a QDirModel. |
138 for list models and the absolute file path if the mode is a QFileSystemModel. |
139 |
139 |
140 \sa QAbstractItemModel, QLineEdit, QComboBox, {Completer Example} |
140 \sa QAbstractItemModel, QLineEdit, QComboBox, {Completer Example} |
141 */ |
141 */ |
142 |
142 |
143 #include "qcompleter_p.h" |
143 #include "qcompleter_p.h" |
145 #ifndef QT_NO_COMPLETER |
145 #ifndef QT_NO_COMPLETER |
146 |
146 |
147 #include "QtGui/qscrollbar.h" |
147 #include "QtGui/qscrollbar.h" |
148 #include "QtGui/qstringlistmodel.h" |
148 #include "QtGui/qstringlistmodel.h" |
149 #include "QtGui/qdirmodel.h" |
149 #include "QtGui/qdirmodel.h" |
|
150 #include "QtGui/qfilesystemmodel.h" |
150 #include "QtGui/qheaderview.h" |
151 #include "QtGui/qheaderview.h" |
151 #include "QtGui/qlistview.h" |
152 #include "QtGui/qlistview.h" |
152 #include "QtGui/qapplication.h" |
153 #include "QtGui/qapplication.h" |
153 #include "QtGui/qevent.h" |
154 #include "QtGui/qevent.h" |
154 #include "QtGui/qheaderview.h" |
155 #include "QtGui/qheaderview.h" |
468 QMatchData QCompletionEngine::filterHistory() |
469 QMatchData QCompletionEngine::filterHistory() |
469 { |
470 { |
470 QAbstractItemModel *source = c->proxy->sourceModel(); |
471 QAbstractItemModel *source = c->proxy->sourceModel(); |
471 if (curParts.count() <= 1 || c->proxy->showAll || !source) |
472 if (curParts.count() <= 1 || c->proxy->showAll || !source) |
472 return QMatchData(); |
473 return QMatchData(); |
473 bool dirModel = false; |
474 bool isDirModel = false; |
|
475 bool isFsModel = false; |
474 #ifndef QT_NO_DIRMODEL |
476 #ifndef QT_NO_DIRMODEL |
475 dirModel = (qobject_cast<QDirModel *>(source) != 0); |
477 isDirModel = (qobject_cast<QDirModel *>(source) != 0); |
|
478 #endif |
|
479 #ifndef QT_NO_FILESYSTEMMODEL |
|
480 isFsModel = (qobject_cast<QFileSystemModel *>(source) != 0); |
476 #endif |
481 #endif |
477 QVector<int> v; |
482 QVector<int> v; |
478 QIndexMapper im(v); |
483 QIndexMapper im(v); |
479 QMatchData m(im, -1, true); |
484 QMatchData m(im, -1, true); |
480 |
485 |
481 for (int i = 0; i < source->rowCount(); i++) { |
486 for (int i = 0; i < source->rowCount(); i++) { |
482 QString str = source->index(i, c->column).data().toString(); |
487 QString str = source->index(i, c->column).data().toString(); |
483 if (str.startsWith(c->prefix, c->cs) |
488 if (str.startsWith(c->prefix, c->cs) |
484 #if (!defined(Q_OS_WIN) || defined(Q_OS_WINCE)) && !defined(Q_OS_SYMBIAN) |
489 #if (!defined(Q_OS_WIN) || defined(Q_OS_WINCE)) && !defined(Q_OS_SYMBIAN) |
485 && (!dirModel || QDir::toNativeSeparators(str) != QDir::separator()) |
490 && ((!isFsModel && !isDirModel) || QDir::toNativeSeparators(str) != QDir::separator()) |
486 #endif |
491 #endif |
487 ) |
492 ) |
488 m.indices.append(i); |
493 m.indices.append(i); |
489 } |
494 } |
490 return m; |
495 return m; |
841 if (mode == QCompleter::InlineCompletion) { |
846 if (mode == QCompleter::InlineCompletion) { |
842 if (qobject_cast<QDirModel *>(proxy->sourceModel()) && QFileInfo(completion).isDir()) |
847 if (qobject_cast<QDirModel *>(proxy->sourceModel()) && QFileInfo(completion).isDir()) |
843 completion += QDir::separator(); |
848 completion += QDir::separator(); |
844 } |
849 } |
845 #endif |
850 #endif |
|
851 #ifndef QT_NO_FILESYSTEMMODEL |
|
852 // add a trailing separator in inline |
|
853 if (mode == QCompleter::InlineCompletion) { |
|
854 if (qobject_cast<QFileSystemModel *>(proxy->sourceModel()) && QFileInfo(completion).isDir()) |
|
855 completion += QDir::separator(); |
|
856 } |
|
857 #endif |
846 } |
858 } |
847 |
859 |
848 if (highlighted) { |
860 if (highlighted) { |
849 emit q->highlighted(index); |
861 emit q->highlighted(index); |
850 emit q->highlighted(completion); |
862 emit q->highlighted(completion); |
864 void QCompleterPrivate::showPopup(const QRect& rect) |
876 void QCompleterPrivate::showPopup(const QRect& rect) |
865 { |
877 { |
866 const QRect screen = QApplication::desktop()->availableGeometry(widget); |
878 const QRect screen = QApplication::desktop()->availableGeometry(widget); |
867 Qt::LayoutDirection dir = widget->layoutDirection(); |
879 Qt::LayoutDirection dir = widget->layoutDirection(); |
868 QPoint pos; |
880 QPoint pos; |
869 int rw, rh, w; |
881 int rh, w; |
870 int h = (popup->sizeHintForRow(0) * qMin(maxVisibleItems, popup->model()->rowCount()) + 3) + 3; |
882 int h = (popup->sizeHintForRow(0) * qMin(maxVisibleItems, popup->model()->rowCount()) + 3) + 3; |
871 QScrollBar *hsb = popup->horizontalScrollBar(); |
883 QScrollBar *hsb = popup->horizontalScrollBar(); |
872 if (hsb && hsb->isVisible()) |
884 if (hsb && hsb->isVisible()) |
873 h += popup->horizontalScrollBar()->sizeHint().height(); |
885 h += popup->horizontalScrollBar()->sizeHint().height(); |
874 |
886 |
875 if (rect.isValid()) { |
887 if (rect.isValid()) { |
876 rh = rect.height(); |
888 rh = rect.height(); |
877 w = rw = rect.width(); |
889 w = rect.width(); |
878 pos = widget->mapToGlobal(dir == Qt::RightToLeft ? rect.bottomRight() : rect.bottomLeft()); |
890 pos = widget->mapToGlobal(dir == Qt::RightToLeft ? rect.bottomRight() : rect.bottomLeft()); |
879 } else { |
891 } else { |
880 rh = widget->height(); |
892 rh = widget->height(); |
881 rw = widget->width(); |
|
882 pos = widget->mapToGlobal(QPoint(0, widget->height() - 2)); |
893 pos = widget->mapToGlobal(QPoint(0, widget->height() - 2)); |
883 w = widget->width(); |
894 w = widget->width(); |
884 } |
895 } |
885 |
896 |
886 if ((pos.x() + rw) > (screen.x() + screen.width())) |
897 if (w > screen.width()) |
|
898 w = screen.width(); |
|
899 if ((pos.x() + w) > (screen.x() + screen.width())) |
887 pos.setX(screen.x() + screen.width() - w); |
900 pos.setX(screen.x() + screen.width() - w); |
888 if (pos.x() < screen.x()) |
901 if (pos.x() < screen.x()) |
889 pos.setX(screen.x()); |
902 pos.setX(screen.x()); |
890 if (((pos.y() + rh) > (screen.y() + screen.height())) && ((pos.y() - h - rh) >= 0)) |
903 |
891 pos.setY(pos.y() - qMax(h, popup->minimumHeight()) - rh + 2); |
904 int top = pos.y() - rh - screen.top() + 2; |
|
905 int bottom = screen.bottom() - pos.y(); |
|
906 h = qMax(h, popup->minimumHeight()); |
|
907 if (h > bottom) { |
|
908 h = qMin(qMax(top, bottom), h); |
|
909 |
|
910 if (top > bottom) |
|
911 pos.setY(pos.y() - h - rh + 2); |
|
912 } |
892 |
913 |
893 popup->setGeometry(pos.x(), pos.y(), w, h); |
914 popup->setGeometry(pos.x(), pos.y(), w, h); |
894 |
915 |
895 if (!popup->isVisible()) |
916 if (!popup->isVisible()) |
896 popup->show(); |
917 popup->show(); |
|
918 } |
|
919 |
|
920 void QCompleterPrivate::_q_fileSystemModelDirectoryLoaded(const QString &path) |
|
921 { |
|
922 Q_Q(QCompleter); |
|
923 //the path given by QFileSystemModel does not end with / |
|
924 if (!q->completionPrefix().isEmpty() && q->completionPrefix() != path + QLatin1Char('/')) |
|
925 q->complete(); |
897 } |
926 } |
898 |
927 |
899 /*! |
928 /*! |
900 Constructs a completer object with the given \a parent. |
929 Constructs a completer object with the given \a parent. |
901 */ |
930 */ |
974 /*! |
1003 /*! |
975 Sets the model which provides completions to \a model. The \a model can |
1004 Sets the model which provides completions to \a model. The \a model can |
976 be list model or a tree model. If a model has been already previously set |
1005 be list model or a tree model. If a model has been already previously set |
977 and it has the QCompleter as its parent, it is deleted. |
1006 and it has the QCompleter as its parent, it is deleted. |
978 |
1007 |
979 For convenience, if \a model is a QDirModel, QCompleter switches its |
1008 For convenience, if \a model is a QFileSystemModel, QCompleter switches its |
980 caseSensitivity to Qt::CaseInsensitive on Windows and Qt::CaseSensitive |
1009 caseSensitivity to Qt::CaseInsensitive on Windows and Qt::CaseSensitive |
981 on other platforms. |
1010 on other platforms. |
982 |
1011 |
983 \sa completionModel(), modelSorting, {Handling Tree Models} |
1012 \sa completionModel(), modelSorting, {Handling Tree Models} |
984 */ |
1013 */ |
998 #else |
1027 #else |
999 setCaseSensitivity(Qt::CaseSensitive); |
1028 setCaseSensitivity(Qt::CaseSensitive); |
1000 #endif |
1029 #endif |
1001 } |
1030 } |
1002 #endif // QT_NO_DIRMODEL |
1031 #endif // QT_NO_DIRMODEL |
|
1032 #ifndef QT_NO_FILESYSTEMMODEL |
|
1033 QFileSystemModel *fsModel = qobject_cast<QFileSystemModel *>(model); |
|
1034 if (fsModel) { |
|
1035 #if (defined(Q_OS_WIN) && !defined(Q_OS_WINCE)) || defined(Q_OS_SYMBIAN) |
|
1036 setCaseSensitivity(Qt::CaseInsensitive); |
|
1037 #else |
|
1038 setCaseSensitivity(Qt::CaseSensitive); |
|
1039 #endif |
|
1040 setCompletionRole(QFileSystemModel::FileNameRole); |
|
1041 connect(fsModel, SIGNAL(directoryLoaded(QString)), this, SLOT(_q_fileSystemModelDirectoryLoaded(QString))); |
|
1042 } |
|
1043 #endif // QT_NO_FILESYSTEMMODEL |
1003 } |
1044 } |
1004 |
1045 |
1005 /*! |
1046 /*! |
1006 Returns the model that provides completion strings. |
1047 Returns the model that provides completion strings. |
1007 |
1048 |
1629 Returns the path for the given \a index. The completer object uses this to |
1670 Returns the path for the given \a index. The completer object uses this to |
1630 obtain the completion text from the underlying model. |
1671 obtain the completion text from the underlying model. |
1631 |
1672 |
1632 The default implementation returns the \l{Qt::EditRole}{edit role} of the |
1673 The default implementation returns the \l{Qt::EditRole}{edit role} of the |
1633 item for list models. It returns the absolute file path if the model is a |
1674 item for list models. It returns the absolute file path if the model is a |
1634 QDirModel. |
1675 QFileSystemModel. |
1635 |
1676 |
1636 \sa splitPath() |
1677 \sa splitPath() |
1637 */ |
1678 */ |
|
1679 |
1638 QString QCompleter::pathFromIndex(const QModelIndex& index) const |
1680 QString QCompleter::pathFromIndex(const QModelIndex& index) const |
1639 { |
1681 { |
1640 Q_D(const QCompleter); |
1682 Q_D(const QCompleter); |
1641 if (!index.isValid()) |
1683 if (!index.isValid()) |
1642 return QString(); |
1684 return QString(); |
1643 |
1685 |
1644 QAbstractItemModel *sourceModel = d->proxy->sourceModel(); |
1686 QAbstractItemModel *sourceModel = d->proxy->sourceModel(); |
1645 if (!sourceModel) |
1687 if (!sourceModel) |
1646 return QString(); |
1688 return QString(); |
|
1689 bool isDirModel = false; |
|
1690 bool isFsModel = false; |
1647 #ifndef QT_NO_DIRMODEL |
1691 #ifndef QT_NO_DIRMODEL |
1648 QDirModel *dirModel = qobject_cast<QDirModel *>(sourceModel); |
1692 isDirModel = qobject_cast<QDirModel *>(d->proxy->sourceModel()) != 0; |
1649 if (!dirModel) |
1693 #endif |
1650 #endif |
1694 #ifndef QT_NO_FILESYSTEMMODEL |
|
1695 isFsModel = qobject_cast<QFileSystemModel *>(d->proxy->sourceModel()) != 0; |
|
1696 #endif |
|
1697 if (!isDirModel && !isFsModel) |
1651 return sourceModel->data(index, d->role).toString(); |
1698 return sourceModel->data(index, d->role).toString(); |
1652 |
1699 |
1653 QModelIndex idx = index; |
1700 QModelIndex idx = index; |
1654 QStringList list; |
1701 QStringList list; |
1655 do { |
1702 do { |
1656 QString t = sourceModel->data(idx, Qt::EditRole).toString(); |
1703 QString t; |
|
1704 if (isDirModel) |
|
1705 t = sourceModel->data(idx, Qt::EditRole).toString(); |
|
1706 #ifndef QT_NO_FILESYSTEMMODEL |
|
1707 else |
|
1708 t = sourceModel->data(idx, QFileSystemModel::FileNameRole).toString(); |
|
1709 #endif |
1657 list.prepend(t); |
1710 list.prepend(t); |
1658 QModelIndex parent = idx.parent(); |
1711 QModelIndex parent = idx.parent(); |
1659 idx = parent.sibling(parent.row(), index.column()); |
1712 idx = parent.sibling(parent.row(), index.column()); |
1660 } while (idx.isValid()); |
1713 } while (idx.isValid()); |
1661 |
1714 |
1671 /*! |
1724 /*! |
1672 Splits the given \a path into strings that are used to match at each level |
1725 Splits the given \a path into strings that are used to match at each level |
1673 in the model(). |
1726 in the model(). |
1674 |
1727 |
1675 The default implementation of splitPath() splits a file system path based on |
1728 The default implementation of splitPath() splits a file system path based on |
1676 QDir::separator() when the sourceModel() is a QDirModel. |
1729 QDir::separator() when the sourceModel() is a QFileSystemModel. |
1677 |
1730 |
1678 When used with list models, the first item in the returned list is used for |
1731 When used with list models, the first item in the returned list is used for |
1679 matching. |
1732 matching. |
1680 |
1733 |
1681 \sa pathFromIndex(), {Handling Tree Models} |
1734 \sa pathFromIndex(), {Handling Tree Models} |
1682 */ |
1735 */ |
1683 QStringList QCompleter::splitPath(const QString& path) const |
1736 QStringList QCompleter::splitPath(const QString& path) const |
1684 { |
1737 { |
1685 bool isDirModel = false; |
1738 bool isDirModel = false; |
|
1739 bool isFsModel = false; |
1686 #ifndef QT_NO_DIRMODEL |
1740 #ifndef QT_NO_DIRMODEL |
1687 Q_D(const QCompleter); |
1741 Q_D(const QCompleter); |
1688 isDirModel = qobject_cast<QDirModel *>(d->proxy->sourceModel()) != 0; |
1742 isDirModel = qobject_cast<QDirModel *>(d->proxy->sourceModel()) != 0; |
1689 #endif |
1743 #endif |
1690 |
1744 #ifndef QT_NO_FILESYSTEMMODEL |
1691 if (!isDirModel || path.isEmpty()) |
1745 #ifdef QT_NO_DIRMODEL |
|
1746 Q_D(const QCompleter); |
|
1747 #endif |
|
1748 isFsModel = qobject_cast<QFileSystemModel *>(d->proxy->sourceModel()) != 0; |
|
1749 #endif |
|
1750 |
|
1751 if ((!isDirModel && !isFsModel) || path.isEmpty()) |
1692 return QStringList(completionPrefix()); |
1752 return QStringList(completionPrefix()); |
1693 |
1753 |
1694 QString pathCopy = QDir::toNativeSeparators(path); |
1754 QString pathCopy = QDir::toNativeSeparators(path); |
1695 QString sep = QDir::separator(); |
1755 QString sep = QDir::separator(); |
1696 #if defined(Q_OS_SYMBIAN) |
1756 #if defined(Q_OS_SYMBIAN) |