diff options
author | con <qtc-commiter@nokia.com> | 2008-12-02 12:01:29 +0100 |
---|---|---|
committer | con <qtc-commiter@nokia.com> | 2008-12-02 12:01:29 +0100 |
commit | 05c35356abc31549c5db6eba31fb608c0365c2a0 (patch) | |
tree | be044530104267afaff13f8943889cb97f8c8bad /src/plugins/quickopen/quickopentoolwindow.cpp | |
download | qt-creator-05c35356abc31549c5db6eba31fb608c0365c2a0.tar.gz |
Initial import
Diffstat (limited to 'src/plugins/quickopen/quickopentoolwindow.cpp')
-rw-r--r-- | src/plugins/quickopen/quickopentoolwindow.cpp | 484 |
1 files changed, 484 insertions, 0 deletions
diff --git a/src/plugins/quickopen/quickopentoolwindow.cpp b/src/plugins/quickopen/quickopentoolwindow.cpp new file mode 100644 index 0000000000..f6ca212c82 --- /dev/null +++ b/src/plugins/quickopen/quickopentoolwindow.cpp @@ -0,0 +1,484 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information (qt-info@nokia.com) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ + +#include <qglobal.h> + +namespace QuickOpen { +struct FilterEntry; +} + +QT_BEGIN_NAMESPACE +unsigned int qHash(const QuickOpen::FilterEntry &entry); +QT_END_NAMESPACE + +#include "quickopentoolwindow.h" +#include "quickopenplugin.h" +#include "quickopenconstants.h" + +#include <extensionsystem/pluginmanager.h> +#include <coreplugin/icore.h> +#include <coreplugin/modemanager.h> +#include <coreplugin/coreconstants.h> +#include <coreplugin/fileiconprovider.h> +#include <utils/fancylineedit.h> + +#include <QtCore/QFileInfo> +#include <QtCore/QFile> +#include <QtCore/QTimer> +#include <QtCore/QRegExp> +#include <QtCore/QSettings> +#include <QtCore/QDebug> +#include <QtGui/QAction> +#include <QtGui/QApplication> +#include <QtGui/QContextMenuEvent> +#include <QtGui/QHBoxLayout> +#include <QtGui/QHeaderView> +#include <QtGui/QKeyEvent> +#include <QtGui/QLabel> +#include <QtGui/QLineEdit> +#include <QtGui/QMenu> +#include <QtGui/QMouseEvent> +#include <QtGui/QPushButton> +#include <QtGui/QScrollBar> +#include <QtGui/QTreeView> + +Q_DECLARE_METATYPE(QuickOpen::IQuickOpenFilter*); +Q_DECLARE_METATYPE(QuickOpen::FilterEntry); + +namespace QuickOpen { +namespace Internal { + +/*! A model to represent the QuickOpen results. */ +class QuickOpenModel : public QAbstractListModel +{ +public: + QuickOpenModel(QObject *parent = 0) + : QAbstractListModel(parent) +// , mDisplayCount(64) + {} + + int rowCount(const QModelIndex &parent = QModelIndex()) const; + int columnCount(const QModelIndex &parent = QModelIndex()) const; + QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const; + + void setEntries(const QList<FilterEntry> &entries); + //void setDisplayCount(int count); + +private: + mutable QList<FilterEntry> mEntries; + //int mDisplayCount; +}; + +class CompletionList : public QTreeView +{ +public: + CompletionList(QWidget *parent = 0); + + void updatePreferredSize(); + QSize preferredSize() const { return m_preferredSize; } + +private: + QSize m_preferredSize; +}; + +} // namespace Internal +} // namespace QuickOpen + +using namespace QuickOpen; +using namespace QuickOpen::Internal; + +QT_BEGIN_NAMESPACE +uint qHash(const FilterEntry &entry) +{ + if (entry.internalData.canConvert(QVariant::String)) + return qHash(entry.internalData.toString()); + return qHash(entry.internalData.constData()); +} +QT_END_NAMESPACE + + +// =========== QuickOpenModel =========== + +int QuickOpenModel::rowCount(const QModelIndex & /* parent */) const +{ + return mEntries.size(); +} + +int QuickOpenModel::columnCount(const QModelIndex &parent) const +{ + return parent.isValid() ? 0 : 2; +} + +/*! + * When asked for the icon via Qt::DecorationRole, the QuickOpenModel lazily + * resolves and caches the Greehouse-specific file icon when + * FilterEntry::resolveFileIcon is true. FilterEntry::internalData is assumed + * to be the filename. + */ +QVariant QuickOpenModel::data(const QModelIndex &index, int role) const +{ + if (!index.isValid() || index.row() >= mEntries.size()) + return QVariant(); + + if (role == Qt::DisplayRole) { + if (index.column() == 0) { + return mEntries.at(index.row()).displayName; + } else if (index.column() == 1) { + return mEntries.at(index.row()).extraInfo; + } + } else if (role == Qt::DecorationRole && index.column() == 0) { + FilterEntry &entry = mEntries[index.row()]; + if (entry.resolveFileIcon && entry.displayIcon.isNull()) { + entry.resolveFileIcon = false; + entry.displayIcon = + Core::FileIconProvider::instance()->icon(QFileInfo(entry.internalData.toString())); + } + return entry.displayIcon; + } else if (role == Qt::ForegroundRole && index.column() == 1) { + return Qt::darkGray; + } else if (role == Qt::UserRole) { + return qVariantFromValue(mEntries.at(index.row())); + } + + return QVariant(); +} + +void QuickOpenModel::setEntries(const QList<FilterEntry> &entries) +{ + mEntries = entries; + reset(); +} +#if 0 +void QuickOpenModel::setDisplayCount(int count) +{ + // TODO: This method is meant to be used for increasing the number of items displayed at the + // user's request. There is however no way yet for the user to request this. + if (count == mDisplayCount) + return; + + const int displayedOld = qMin(mDisplayCount, mEntries.size()); + const int displayedNew = qMin(count, mEntries.size()); + + if (displayedNew < displayedOld) + beginRemoveRows(QModelIndex(), displayedNew - 1, displayedOld - 1); + else if (displayedNew > displayedOld) + beginInsertRows(QModelIndex(), displayedOld - 1, displayedNew - 1); + + mDisplayCount = count; + + if (displayedNew < displayedOld) + endRemoveRows(); + else if (displayedNew > displayedOld) + endInsertRows(); +} +#endif + +// =========== CompletionList =========== + +CompletionList::CompletionList(QWidget *parent) + : QTreeView(parent) +{ + setRootIsDecorated(false); + setUniformRowHeights(true); + setMaximumWidth(900); + header()->hide(); + header()->setStretchLastSection(true); + // This is too slow when done on all results + //header()->setResizeMode(QHeaderView::ResizeToContents); + setWindowFlags(Qt::ToolTip); +} + +void CompletionList::updatePreferredSize() +{ + //header()->setStretchLastSection(false); + //updateGeometries(); + + const QStyleOptionViewItem &option = viewOptions(); + const QSize shint = itemDelegate()->sizeHint(option, model()->index(0, 0)); +#if 0 + const int visibleItems = model()->rowCount(); + + // TODO: Look into enabling preferred height as well. Current problem is that this doesn't + // work nicely from the user perspective if the list is popping up instead of down. + //const int h = shint.height() * visibleItems; + + const QScrollBar *vscroll = verticalScrollBar(); + int preferredWidth = header()->length() + frameWidth() * 2 + + (vscroll->isVisibleTo(this) ? vscroll->width() : 0); + const int diff = preferredWidth - width(); + + // Avoid resizing the list widget when there are no results or when the preferred size is + // only a little smaller than our current size + if (visibleItems == 0 || (diff > -100 && diff < 0)) + preferredWidth = width(); +#endif + + m_preferredSize = QSize(600, //qMax(600, preferredWidth), + shint.height() * 17 + frameWidth() * 2); + //header()->setStretchLastSection(true); +} + + +// =========== QuickOpenToolWindow =========== + +QuickOpenToolWindow::QuickOpenToolWindow(QuickOpenPlugin *qop) : + m_quickOpenPlugin(qop), + m_quickOpenModel(new QuickOpenModel(this)), + m_completionList(new CompletionList(this)), + m_filterMenu(new QMenu(this)), + m_refreshAction(new QAction(tr("Refresh"), this)), + m_configureAction(new QAction(tr("Configure..."), this)), + m_fileLineEdit(new Core::Utils::FancyLineEdit) +{ + // Explcitly hide the completion list popup. + m_completionList->hide(); + + setWindowTitle("Quick Open"); + resize(200, 90); + QSizePolicy sizePolicy(QSizePolicy::Fixed, QSizePolicy::Preferred); + sizePolicy.setHorizontalStretch(0); + sizePolicy.setVerticalStretch(0); + setSizePolicy(sizePolicy); + setMinimumSize(QSize(200, 0)); + + QHBoxLayout *layout = new QHBoxLayout(this); + setLayout(layout); + layout->setMargin(0); + layout->addWidget(m_fileLineEdit); + + setWindowIcon(QIcon(":/quickopen/images/quickopen.png")); + QPixmap image(Core::Constants::ICON_MAGNIFIER); + m_fileLineEdit->setPixmap(image); + m_fileLineEdit->setUseLayoutDirection(true); + m_fileLineEdit->setHintText(tr("Type to QuickOpen")); + m_fileLineEdit->setFocusPolicy(Qt::ClickFocus); + + m_fileLineEdit->installEventFilter(this); + this->installEventFilter(this); + + m_completionList->setModel(m_quickOpenModel); + m_completionList->header()->resizeSection(0, 300); + m_completionList->updatePreferredSize(); + m_completionList->resize(m_completionList->preferredSize()); + + m_filterMenu->addAction(m_refreshAction); + m_filterMenu->addAction(m_configureAction); + + m_fileLineEdit->setMenu( m_filterMenu); + + connect(m_refreshAction, SIGNAL(triggered()), m_quickOpenPlugin, SLOT(refresh())); + connect(m_configureAction, SIGNAL(triggered()), this, SLOT(showConfigureDialog())); + connect(m_fileLineEdit, SIGNAL(textEdited(const QString&)), + this, SLOT(textEdited(const QString&))); + connect(m_completionList, SIGNAL(activated(QModelIndex)), + this, SLOT(acceptCurrentEntry())); +} + +bool QuickOpenToolWindow::isShowingTypeHereMessage() const +{ + return m_fileLineEdit->isShowingHintText(); +} + +void QuickOpenToolWindow::updateFilterList() +{ + m_filterMenu->clear(); + foreach (IQuickOpenFilter *filter, m_quickOpenPlugin->filter()) { + if (!filter->shortcutString().isEmpty()) { + QAction *action = m_filterMenu->addAction(filter->trName(), this, SLOT(filterSelected())); + action->setData(qVariantFromValue(filter)); + } + } + m_filterMenu->addSeparator(); + m_filterMenu->addAction(m_refreshAction); + m_filterMenu->addAction(m_configureAction); +} + +bool QuickOpenToolWindow::eventFilter(QObject *obj, QEvent *event) +{ + if (obj == m_fileLineEdit && event->type() == QEvent::KeyPress) { + QKeyEvent *keyEvent = static_cast<QKeyEvent *>(event); + switch (keyEvent->key()) { + case Qt::Key_Up: + case Qt::Key_Down: + case Qt::Key_PageUp: + case Qt::Key_PageDown: + showCompletionList(); + QApplication::sendEvent(m_completionList, event); + return true; + case Qt::Key_Enter: + case Qt::Key_Return: + acceptCurrentEntry(); + return true; + case Qt::Key_Escape: + m_completionList->hide(); + return true; + default: + break; + } + } else if (obj == m_fileLineEdit && event->type() == QEvent::FocusOut) { + m_completionList->hide(); + } else if (obj == m_fileLineEdit && event->type() == QEvent::FocusIn) { + updateCompletionList(m_fileLineEdit->typedText()); + showCompletionList(); + } else if (obj == this && event->type() == QEvent::ShortcutOverride) { + QKeyEvent *ke = static_cast<QKeyEvent *>(event); + if (ke->key() == Qt::Key_Escape && !ke->modifiers()) { + event->accept(); + QTimer::singleShot(0, Core::ModeManager::instance(), SLOT(setFocusToCurrentMode())); + return true; + } + } + return QWidget::eventFilter(obj, event); +} + +void QuickOpenToolWindow::showCompletionList() +{ + const int border = m_completionList->frameWidth(); + const QSize size = m_completionList->preferredSize(); + const QRect rect(mapToGlobal(QPoint(-border, -size.height() - border)), size); + m_completionList->setGeometry(rect); + m_completionList->show(); +} + +void QuickOpenToolWindow::textEdited(const QString &text) +{ + updateCompletionList(text); + showCompletionList(); +} + +QList<IQuickOpenFilter*> QuickOpenToolWindow::filtersFor(const QString &text, QString &searchText) +{ + QList<IQuickOpenFilter*> filters = m_quickOpenPlugin->filter(); + int whiteSpace = text.indexOf(" "); + QString prefix; + if (whiteSpace >= 0) + prefix = text.left(whiteSpace); + if (!prefix.isEmpty()) { + prefix = prefix.toLower(); + foreach (IQuickOpenFilter *filter, filters) { + if (prefix == filter->shortcutString()) { + searchText = text.mid(whiteSpace+1); + return QList<IQuickOpenFilter*>() << filter; + } + } + } + searchText = text; + QList<IQuickOpenFilter*> activeFilters; + foreach (IQuickOpenFilter *filter, filters) + if (filter->defaultActiveState()) + activeFilters << filter; + return activeFilters; +} + +void QuickOpenToolWindow::updateCompletionList(const QString &text) +{ + QString searchText; + const QList<IQuickOpenFilter*> filters = filtersFor(text, searchText); + QSet<FilterEntry> alreadyAdded; + const bool checkDuplicates = (filters.size() > 1); + QList<FilterEntry> entries; + foreach (IQuickOpenFilter *filter, filters) { + foreach (const FilterEntry &entry, filter->matchesFor(searchText)) { + if (checkDuplicates && alreadyAdded.contains(entry)) + continue; + entries.append(entry); + if (checkDuplicates) + alreadyAdded.insert(entry); + } + } + m_quickOpenModel->setEntries(entries); + if (m_quickOpenModel->rowCount() > 0) { + m_completionList->setCurrentIndex(m_quickOpenModel->index(0, 0)); + } +#if 0 + m_completionList->updatePreferredSize(); +#endif +} + +void QuickOpenToolWindow::acceptCurrentEntry() +{ + if (!m_completionList->isVisible()) + return; + const QModelIndex index = m_completionList->currentIndex(); + if (!index.isValid()) + return; + const FilterEntry entry = m_quickOpenModel->data(index, Qt::UserRole).value<FilterEntry>(); + m_completionList->hide(); + entry.filter->accept(entry); +} + +void QuickOpenToolWindow::show(const QString &text, int selectionStart, int selectionLength) +{ + m_fileLineEdit->hideHintText(); + m_fileLineEdit->setText(text); + setFocus(); + if (selectionStart >= 0) + m_fileLineEdit->setSelection(selectionStart, selectionLength); + else + m_fileLineEdit->deselect(); +} + +void QuickOpenToolWindow::filterSelected() +{ + const char * const TEXT = "<type here>"; + QAction *action = qobject_cast<QAction*>(sender()); + Q_ASSERT(action); + IQuickOpenFilter *filter = action->data().value<IQuickOpenFilter*>(); + Q_ASSERT(filter); + show(filter->shortcutString() + " " + TEXT, + filter->shortcutString().length() + 1, + QString(TEXT).length()); + updateCompletionList(m_fileLineEdit->text()); + m_fileLineEdit->setFocus(); +} + +void QuickOpenToolWindow::focusInEvent(QFocusEvent *e) +{ + m_fileLineEdit->setFocus(e->reason()); + if (e->reason() != Qt::MouseFocusReason) { + m_fileLineEdit->selectAll(); + } + QWidget::focusInEvent(e); +} + +void QuickOpenToolWindow::showEvent(QShowEvent *event) +{ + QWidget::showEvent(event); +} + +void QuickOpenToolWindow::showConfigureDialog() +{ + ExtensionSystem::PluginManager::instance() + ->getObject<Core::ICore>()->showOptionsDialog(Constants::QUICKOPEN_CATEGORY, + Constants::FILTER_OPTIONS_PAGE); +} |