src/gui/util/qcompleter.cpp
changeset 30 5dc02b23752f
parent 29 b72c6db6890b
child 33 3e2da88830cd
equal deleted inserted replaced
29:b72c6db6890b 30:5dc02b23752f
    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 
  1081     }
  1122     }
  1082     if (d->popup != popup)
  1123     if (d->popup != popup)
  1083         delete d->popup;
  1124         delete d->popup;
  1084     if (popup->model() != d->proxy)
  1125     if (popup->model() != d->proxy)
  1085         popup->setModel(d->proxy);
  1126         popup->setModel(d->proxy);
  1086 #ifdef Q_OS_MAC
  1127 #if defined(Q_OS_MAC) && !defined(QT_MAC_USE_COCOA)
  1087      popup->show();
  1128      popup->show();
  1088 #else
  1129 #else
  1089      popup->hide();
  1130      popup->hide();
  1090 #endif
  1131 #endif
  1091 
  1132 
  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)