src/gui/util/qcompleter.cpp
branchGCC_SURGE
changeset 31 5daf16870df6
parent 30 5dc02b23752f
child 33 3e2da88830cd
equal deleted inserted replaced
27:93b982ccede2 31:5daf16870df6
    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;
   525 void QCompletionEngine::saveInCache(QString part, const QModelIndex& parent, const QMatchData& m)
   530 void QCompletionEngine::saveInCache(QString part, const QModelIndex& parent, const QMatchData& m)
   526 {
   531 {
   527     QMatchData old = cache[parent].take(part);
   532     QMatchData old = cache[parent].take(part);
   528     cost = cost + m.indices.cost() - old.indices.cost();
   533     cost = cost + m.indices.cost() - old.indices.cost();
   529     if (cost * sizeof(int) > 1024 * 1024) {
   534     if (cost * sizeof(int) > 1024 * 1024) {
   530         QMap<QModelIndex, CacheItem>::iterator it1 ;
   535         QMap<QModelIndex, CacheItem>::iterator it1 = cache.begin();
   531         for (it1 = cache.begin(); it1 != cache.end(); ++it1) {
   536         while (it1 != cache.end()) {
   532             CacheItem& ci = it1.value();
   537             CacheItem& ci = it1.value();
   533             int sz = ci.count()/2;
   538             int sz = ci.count()/2;
   534             QMap<QString, QMatchData>::iterator it2 = ci.begin();
   539             QMap<QString, QMatchData>::iterator it2 = ci.begin();
   535             for (int i = 0; it2 != ci.end() && i < sz; i++, ++it2) {
   540             int i = 0;
       
   541             while (it2 != ci.end() && i < sz) {
   536                 cost -= it2.value().indices.cost();
   542                 cost -= it2.value().indices.cost();
   537                 ci.erase(it2);
   543                 it2 = ci.erase(it2);
       
   544                 i++;
   538             }
   545             }
   539             if (ci.count() == 0)
   546             if (ci.count() == 0) {
   540                 cache.erase(it1);
   547               it1 = cache.erase(it1);
       
   548             } else {
       
   549               ++it1;
       
   550             }
   541         }
   551         }
   542     }
   552     }
   543 
   553 
   544     if (c->cs == Qt::CaseInsensitive)
   554     if (c->cs == Qt::CaseInsensitive)
   545         part = part.toLower();
   555         part = part.toLower();
   836         if (mode == QCompleter::InlineCompletion) {
   846         if (mode == QCompleter::InlineCompletion) {
   837             if (qobject_cast<QDirModel *>(proxy->sourceModel()) && QFileInfo(completion).isDir())
   847             if (qobject_cast<QDirModel *>(proxy->sourceModel()) && QFileInfo(completion).isDir())
   838                 completion += QDir::separator();
   848                 completion += QDir::separator();
   839         }
   849         }
   840 #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
   841     }
   858     }
   842 
   859 
   843     if (highlighted) {
   860     if (highlighted) {
   844         emit q->highlighted(index);
   861         emit q->highlighted(index);
   845         emit q->highlighted(completion);
   862         emit q->highlighted(completion);
   859 void QCompleterPrivate::showPopup(const QRect& rect)
   876 void QCompleterPrivate::showPopup(const QRect& rect)
   860 {
   877 {
   861     const QRect screen = QApplication::desktop()->availableGeometry(widget);
   878     const QRect screen = QApplication::desktop()->availableGeometry(widget);
   862     Qt::LayoutDirection dir = widget->layoutDirection();
   879     Qt::LayoutDirection dir = widget->layoutDirection();
   863     QPoint pos;
   880     QPoint pos;
   864     int rw, rh, w;
   881     int rh, w;
   865     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;
   866     QScrollBar *hsb = popup->horizontalScrollBar();
   883     QScrollBar *hsb = popup->horizontalScrollBar();
   867     if (hsb && hsb->isVisible())
   884     if (hsb && hsb->isVisible())
   868         h += popup->horizontalScrollBar()->sizeHint().height();
   885         h += popup->horizontalScrollBar()->sizeHint().height();
   869 
   886 
   870     if (rect.isValid()) {
   887     if (rect.isValid()) {
   871         rh = rect.height();
   888         rh = rect.height();
   872         w = rw = rect.width();
   889         w = rect.width();
   873         pos = widget->mapToGlobal(dir == Qt::RightToLeft ? rect.bottomRight() : rect.bottomLeft());
   890         pos = widget->mapToGlobal(dir == Qt::RightToLeft ? rect.bottomRight() : rect.bottomLeft());
   874     } else {
   891     } else {
   875         rh = widget->height();
   892         rh = widget->height();
   876         rw = widget->width();
       
   877         pos = widget->mapToGlobal(QPoint(0, widget->height() - 2));
   893         pos = widget->mapToGlobal(QPoint(0, widget->height() - 2));
   878         w = widget->width();
   894         w = widget->width();
   879     }
   895     }
   880 
   896 
   881     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()))
   882         pos.setX(screen.x() + screen.width() - w);
   900         pos.setX(screen.x() + screen.width() - w);
   883     if (pos.x() < screen.x())
   901     if (pos.x() < screen.x())
   884         pos.setX(screen.x());
   902         pos.setX(screen.x());
   885     if (((pos.y() + rh) > (screen.y() + screen.height())) && ((pos.y() - h - rh) >= 0))
   903 
   886         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     }
   887 
   913 
   888     popup->setGeometry(pos.x(), pos.y(), w, h);
   914     popup->setGeometry(pos.x(), pos.y(), w, h);
   889 
   915 
   890     if (!popup->isVisible())
   916     if (!popup->isVisible())
   891         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();
   892 }
   926 }
   893 
   927 
   894 /*!
   928 /*!
   895     Constructs a completer object with the given \a parent.
   929     Constructs a completer object with the given \a parent.
   896 */
   930 */
   969 /*!
  1003 /*!
   970     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
   971     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
   972     and it has the QCompleter as its parent, it is deleted.
  1006     and it has the QCompleter as its parent, it is deleted.
   973 
  1007 
   974     For convenience, if \a model is a QDirModel, QCompleter switches its
  1008     For convenience, if \a model is a QFileSystemModel, QCompleter switches its
   975     caseSensitivity to Qt::CaseInsensitive on Windows and Qt::CaseSensitive
  1009     caseSensitivity to Qt::CaseInsensitive on Windows and Qt::CaseSensitive
   976     on other platforms.
  1010     on other platforms.
   977 
  1011 
   978     \sa completionModel(), modelSorting, {Handling Tree Models}
  1012     \sa completionModel(), modelSorting, {Handling Tree Models}
   979 */
  1013 */
   993 #else
  1027 #else
   994         setCaseSensitivity(Qt::CaseSensitive);
  1028         setCaseSensitivity(Qt::CaseSensitive);
   995 #endif
  1029 #endif
   996     }
  1030     }
   997 #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
   998 }
  1044 }
   999 
  1045 
  1000 /*!
  1046 /*!
  1001     Returns the model that provides completion strings.
  1047     Returns the model that provides completion strings.
  1002 
  1048 
  1076     }
  1122     }
  1077     if (d->popup != popup)
  1123     if (d->popup != popup)
  1078         delete d->popup;
  1124         delete d->popup;
  1079     if (popup->model() != d->proxy)
  1125     if (popup->model() != d->proxy)
  1080         popup->setModel(d->proxy);
  1126         popup->setModel(d->proxy);
  1081 #ifdef Q_OS_MAC
  1127 #if defined(Q_OS_MAC) && !defined(QT_MAC_USE_COCOA)
  1082      popup->show();
  1128      popup->show();
  1083 #else
  1129 #else
  1084      popup->hide();
  1130      popup->hide();
  1085 #endif
  1131 #endif
  1086 
  1132 
  1624     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
  1625     obtain the completion text from the underlying model.
  1671     obtain the completion text from the underlying model.
  1626 
  1672 
  1627     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
  1628     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
  1629     QDirModel.
  1675     QFileSystemModel.
  1630 
  1676 
  1631     \sa splitPath()
  1677     \sa splitPath()
  1632 */
  1678 */
       
  1679 
  1633 QString QCompleter::pathFromIndex(const QModelIndex& index) const
  1680 QString QCompleter::pathFromIndex(const QModelIndex& index) const
  1634 {
  1681 {
  1635     Q_D(const QCompleter);
  1682     Q_D(const QCompleter);
  1636     if (!index.isValid())
  1683     if (!index.isValid())
  1637         return QString();
  1684         return QString();
  1638 
  1685 
  1639     QAbstractItemModel *sourceModel = d->proxy->sourceModel();
  1686     QAbstractItemModel *sourceModel = d->proxy->sourceModel();
  1640     if (!sourceModel)
  1687     if (!sourceModel)
  1641         return QString();
  1688         return QString();
       
  1689     bool isDirModel = false;
       
  1690     bool isFsModel = false;
  1642 #ifndef QT_NO_DIRMODEL
  1691 #ifndef QT_NO_DIRMODEL
  1643     QDirModel *dirModel = qobject_cast<QDirModel *>(sourceModel);
  1692     isDirModel = qobject_cast<QDirModel *>(d->proxy->sourceModel()) != 0;
  1644     if (!dirModel)
  1693 #endif
  1645 #endif
  1694 #ifndef QT_NO_FILESYSTEMMODEL
       
  1695     isFsModel = qobject_cast<QFileSystemModel *>(d->proxy->sourceModel()) != 0;
       
  1696 #endif
       
  1697     if (!isDirModel && !isFsModel)
  1646         return sourceModel->data(index, d->role).toString();
  1698         return sourceModel->data(index, d->role).toString();
  1647 
  1699 
  1648     QModelIndex idx = index;
  1700     QModelIndex idx = index;
  1649     QStringList list;
  1701     QStringList list;
  1650     do {
  1702     do {
  1651         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
  1652         list.prepend(t);
  1710         list.prepend(t);
  1653         QModelIndex parent = idx.parent();
  1711         QModelIndex parent = idx.parent();
  1654         idx = parent.sibling(parent.row(), index.column());
  1712         idx = parent.sibling(parent.row(), index.column());
  1655     } while (idx.isValid());
  1713     } while (idx.isValid());
  1656 
  1714 
  1666 /*!
  1724 /*!
  1667     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
  1668     in the model().
  1726     in the model().
  1669 
  1727 
  1670     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
  1671     QDir::separator() when the sourceModel() is a QDirModel.
  1729     QDir::separator() when the sourceModel() is a QFileSystemModel.
  1672 
  1730 
  1673     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
  1674     matching.
  1732     matching.
  1675 
  1733 
  1676     \sa pathFromIndex(), {Handling Tree Models}
  1734     \sa pathFromIndex(), {Handling Tree Models}
  1677 */
  1735 */
  1678 QStringList QCompleter::splitPath(const QString& path) const
  1736 QStringList QCompleter::splitPath(const QString& path) const
  1679 {
  1737 {
  1680     bool isDirModel = false;
  1738     bool isDirModel = false;
       
  1739     bool isFsModel = false;
  1681 #ifndef QT_NO_DIRMODEL
  1740 #ifndef QT_NO_DIRMODEL
  1682     Q_D(const QCompleter);
  1741     Q_D(const QCompleter);
  1683     isDirModel = qobject_cast<QDirModel *>(d->proxy->sourceModel()) != 0;
  1742     isDirModel = qobject_cast<QDirModel *>(d->proxy->sourceModel()) != 0;
  1684 #endif
  1743 #endif
  1685 
  1744 #ifndef QT_NO_FILESYSTEMMODEL
  1686     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())
  1687         return QStringList(completionPrefix());
  1752         return QStringList(completionPrefix());
  1688 
  1753 
  1689     QString pathCopy = QDir::toNativeSeparators(path);
  1754     QString pathCopy = QDir::toNativeSeparators(path);
  1690     QString sep = QDir::separator();
  1755     QString sep = QDir::separator();
  1691 #if defined(Q_OS_SYMBIAN)
  1756 #if defined(Q_OS_SYMBIAN)
  1746 /*!
  1811 /*!
  1747     \fn void QCompleter::highlighted(const QString &text)
  1812     \fn void QCompleter::highlighted(const QString &text)
  1748 
  1813 
  1749     This signal is sent when an item in the popup() is highlighted by
  1814     This signal is sent when an item in the popup() is highlighted by
  1750     the user. It is also sent if complete() is called with the completionMode()
  1815     the user. It is also sent if complete() is called with the completionMode()
  1751     set to QCOmpleter::InlineCompletion. The item's \a text is given.
  1816     set to QCompleter::InlineCompletion. The item's \a text is given.
  1752 */
  1817 */
  1753 
  1818 
  1754 QT_END_NAMESPACE
  1819 QT_END_NAMESPACE
  1755 
  1820 
  1756 #include "moc_qcompleter.cpp"
  1821 #include "moc_qcompleter.cpp"