diff options
author | Lars Knoll <lars.knoll@nokia.com> | 2009-03-23 09:18:55 (GMT) |
---|---|---|
committer | Simon Hausmann <simon.hausmann@nokia.com> | 2009-03-23 09:18:55 (GMT) |
commit | e5fcad302d86d316390c6b0f62759a067313e8a9 (patch) | |
tree | c2afbf6f1066b6ce261f14341cf6d310e5595bc1 /tools/shared/findwidget | |
download | Qt-e5fcad302d86d316390c6b0f62759a067313e8a9.zip Qt-e5fcad302d86d316390c6b0f62759a067313e8a9.tar.gz Qt-e5fcad302d86d316390c6b0f62759a067313e8a9.tar.bz2 |
Long live Qt 4.5!
Diffstat (limited to 'tools/shared/findwidget')
17 files changed, 1065 insertions, 0 deletions
diff --git a/tools/shared/findwidget/abstractfindwidget.cpp b/tools/shared/findwidget/abstractfindwidget.cpp new file mode 100644 index 0000000..e52722c --- /dev/null +++ b/tools/shared/findwidget/abstractfindwidget.cpp @@ -0,0 +1,295 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the tools applications of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 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 the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +/*! \class AbstractFindWidget + + \brief A search bar that is commonly added below a searchable widget. + + \internal + + This widget implements a search bar which becomes visible when the user + wants to start searching. It is a modern replacement for the commonly used + search dialog. It is usually placed below the target widget using a QVBoxLayout. + + The search is incremental and can be set to case sensitive or whole words + using buttons available on the search bar. + */ + +#include "abstractfindwidget.h" + +#include <QtCore/QEvent> +#include <QtCore/QFile> +#include <QtCore/QTimer> + +#include <QtGui/QCheckBox> +#include <QtGui/QKeyEvent> +#include <QtGui/QLabel> +#include <QtGui/QLayout> +#include <QtGui/QLineEdit> +#include <QtGui/QSpacerItem> +#include <QtGui/QToolButton> + +QT_BEGIN_NAMESPACE + +static QIcon createIconSet(const QString &name) +{ + QStringList candidates = QStringList() + << (QString::fromUtf8(":/trolltech/shared/images/") + name) +#ifdef Q_WS_MAC + << (QString::fromUtf8(":/trolltech/shared/images/mac/") + name); +#else + << (QString::fromUtf8(":/trolltech/shared/images/win/") + name); +#endif + + foreach (const QString &f, candidates) { + if (QFile::exists(f)) + return QIcon(f); + } + + return QIcon(); +} + +/*! + Constructs an AbstractFindWidget. + + \a flags can change the layout and turn off certain features. + \a parent is passed to the QWidget constructor. + */ +AbstractFindWidget::AbstractFindWidget(FindFlags flags, QWidget *parent) + : QWidget(parent) +{ + QBoxLayout *topLayOut; + QBoxLayout *layOut; + if (flags & NarrowLayout) { + topLayOut = new QVBoxLayout(this); + layOut = new QHBoxLayout; + topLayOut->addLayout(layOut); + } else { + topLayOut = layOut = new QHBoxLayout(this); + } +#ifndef Q_OS_MAC + topLayOut->setSpacing(6); + topLayOut->setMargin(0); +#endif + + m_toolClose = new QToolButton(this); + m_toolClose->setIcon(createIconSet(QLatin1String("closetab.png"))); + m_toolClose->setAutoRaise(true); + layOut->addWidget(m_toolClose); + connect(m_toolClose, SIGNAL(clicked()), SLOT(deactivate())); + + m_editFind = new QLineEdit(this); + layOut->addWidget(m_editFind); + connect(m_editFind, SIGNAL(returnPressed()), SLOT(findNext())); + connect(m_editFind, SIGNAL(textChanged(QString)), SLOT(findCurrentText())); + connect(m_editFind, SIGNAL(textChanged(QString)), SLOT(updateButtons())); + + m_toolPrevious = new QToolButton(this); + m_toolPrevious->setAutoRaise(true); + m_toolPrevious->setText(tr("&Previous")); + m_toolPrevious->setToolButtonStyle(Qt::ToolButtonTextBesideIcon); + m_toolPrevious->setIcon(createIconSet(QLatin1String("previous.png"))); + layOut->addWidget(m_toolPrevious); + connect(m_toolPrevious, SIGNAL(clicked()), SLOT(findPrevious())); + + m_toolNext = new QToolButton(this); + m_toolNext->setAutoRaise(true); + m_toolNext->setText(tr("&Next")); + m_toolNext->setToolButtonStyle(Qt::ToolButtonTextBesideIcon); + m_toolNext->setIcon(createIconSet(QLatin1String("next.png"))); + layOut->addWidget(m_toolNext); + connect(m_toolNext, SIGNAL(clicked()), SLOT(findNext())); + + if (flags & NarrowLayout) { + QSizePolicy sp(QSizePolicy::Preferred, QSizePolicy::Fixed); + m_toolPrevious->setSizePolicy(sp); + m_toolPrevious->setMinimumWidth(m_toolPrevious->minimumSizeHint().height()); + m_toolNext->setSizePolicy(sp); + m_toolNext->setMinimumWidth(m_toolNext->minimumSizeHint().height()); + + QSpacerItem *spacerItem = + new QSpacerItem(1, 1, QSizePolicy::Expanding, QSizePolicy::Minimum); + layOut->addItem(spacerItem); + + layOut = new QHBoxLayout; + topLayOut->addLayout(layOut); + } else { + m_editFind->setMinimumWidth(150); + } + + if (!(flags & NoCaseSensitive)) { + m_checkCase = new QCheckBox(tr("&Case sensitive"), this); + layOut->addWidget(m_checkCase); + connect(m_checkCase, SIGNAL(toggled(bool)), SLOT(findCurrentText())); + } else { + m_checkCase = 0; + } + + if (!(flags & NoWholeWords)) { + m_checkWholeWords = new QCheckBox(tr("Whole &words"), this); + layOut->addWidget(m_checkWholeWords); + connect(m_checkWholeWords, SIGNAL(toggled(bool)), SLOT(findCurrentText())); + } else { + m_checkWholeWords = 0; + } + + m_labelWrapped = new QLabel(this); + m_labelWrapped->setTextFormat(Qt::RichText); + m_labelWrapped->setAlignment( + Qt::AlignLeading | Qt::AlignLeft | Qt::AlignVCenter); + m_labelWrapped->setText( + tr("<img src=\":/trolltech/shared/images/wrap.png\">" + " Search wrapped")); + m_labelWrapped->hide(); + layOut->addWidget(m_labelWrapped); + + QSpacerItem *spacerItem = + new QSpacerItem(1, 1, QSizePolicy::Expanding, QSizePolicy::Minimum); + layOut->addItem(spacerItem); + + setMinimumWidth(minimumSizeHint().width()); + + updateButtons(); + hide(); +} + +/*! + Destroys the AbstractFindWidget. + */ +AbstractFindWidget::~AbstractFindWidget() +{ +} + +/*! + Returns the icon set to be used for the action that initiates a search. + */ +QIcon AbstractFindWidget::findIconSet() +{ + return createIconSet(QLatin1String("searchfind.png")); +} + +/*! + Activates the find widget, making it visible and having focus on its input + field. + */ +void AbstractFindWidget::activate() +{ + show(); + m_editFind->selectAll(); + m_editFind->setFocus(Qt::ShortcutFocusReason); +} + +/*! + Deactivates the find widget, making it invisible and handing focus to any + associated QTextEdit. + */ +void AbstractFindWidget::deactivate() +{ + hide(); +} + +void AbstractFindWidget::findNext() +{ + findInternal(m_editFind->text(), true, false); +} + +void AbstractFindWidget::findPrevious() +{ + findInternal(m_editFind->text(), true, true); +} + +void AbstractFindWidget::findCurrentText() +{ + findInternal(m_editFind->text(), false, false); +} + +void AbstractFindWidget::keyPressEvent(QKeyEvent *event) +{ + if (event->key() == Qt::Key_Escape) { + deactivate(); + return; + } + + QWidget::keyPressEvent(event); +} + +void AbstractFindWidget::updateButtons() +{ + const bool en = !m_editFind->text().isEmpty(); + m_toolPrevious->setEnabled(en); + m_toolNext->setEnabled(en); +} + +void AbstractFindWidget::findInternal(const QString &ttf, bool skipCurrent, bool backward) +{ + bool found = false; + bool wrapped = false; + find(ttf, skipCurrent, backward, &found, &wrapped); + QPalette p; + p.setColor(QPalette::Active, QPalette::Base, found ? Qt::white : QColor(255, 102, 102)); + m_editFind->setPalette(p); + m_labelWrapped->setVisible(wrapped); +} + +bool AbstractFindWidget::caseSensitive() const +{ + return m_checkCase && m_checkCase->isChecked(); +} + +bool AbstractFindWidget::wholeWords() const +{ + return m_checkWholeWords && m_checkWholeWords->isChecked(); +} + +bool AbstractFindWidget::eventFilter(QObject *object, QEvent *e) +{ + if (isVisible() && e->type() == QEvent::KeyPress) { + QKeyEvent *ke = static_cast<QKeyEvent*>(e); + if (ke->key() == Qt::Key_Escape) { + hide(); + return true; + } + } + + return QWidget::eventFilter(object, e); +} + +QT_END_NAMESPACE diff --git a/tools/shared/findwidget/abstractfindwidget.h b/tools/shared/findwidget/abstractfindwidget.h new file mode 100644 index 0000000..fe0c932 --- /dev/null +++ b/tools/shared/findwidget/abstractfindwidget.h @@ -0,0 +1,115 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the tools applications of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 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 the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef ABSTRACTFINDWIDGET_H +#define ABSTRACTFINDWIDGET_H + +#include <QtGui/QIcon> +#include <QtGui/QWidget> + +QT_BEGIN_NAMESPACE + +class QCheckBox; +class QEvent; +class QKeyEvent; +class QLabel; +class QLineEdit; +class QObject; +class QToolButton; + +class AbstractFindWidget : public QWidget +{ + Q_OBJECT + +public: + enum FindFlag { + /// Use a layout that is roughly half as wide and twice as high as the regular one. + NarrowLayout = 1, + /// Do not show the "Whole words" checkbox. + NoWholeWords = 2, + /// Do not show the "Case sensitive" checkbox. + NoCaseSensitive = 4 + }; + Q_DECLARE_FLAGS(FindFlags, FindFlag) + + AbstractFindWidget(FindFlags flags = FindFlags(), QWidget *parent = 0); + virtual ~AbstractFindWidget(); + + bool eventFilter(QObject *object, QEvent *e); + + static QIcon findIconSet(); + +public slots: + void activate(); + virtual void deactivate(); + void findNext(); + void findPrevious(); + void findCurrentText(); + +protected: + void keyPressEvent(QKeyEvent *event); + +private slots: + void updateButtons(); + +protected: + virtual void find(const QString &textToFind, bool skipCurrent, bool backward, bool *found, bool *wrapped) = 0; + + bool caseSensitive() const; + bool wholeWords() const; + +private: + void findInternal(const QString &textToFind, bool skipCurrent, bool backward); + + QLineEdit *m_editFind; + QLabel *m_labelWrapped; + QToolButton *m_toolNext; + QToolButton *m_toolClose; + QToolButton *m_toolPrevious; + QCheckBox *m_checkCase; + QCheckBox *m_checkWholeWords; +}; + +Q_DECLARE_OPERATORS_FOR_FLAGS(AbstractFindWidget::FindFlags) + +QT_END_NAMESPACE + +#endif // ABSTRACTFINDWIDGET_H diff --git a/tools/shared/findwidget/findwidget.pri b/tools/shared/findwidget/findwidget.pri new file mode 100644 index 0000000..6e0f58a --- /dev/null +++ b/tools/shared/findwidget/findwidget.pri @@ -0,0 +1,4 @@ +INCLUDEPATH += $$PWD +HEADERS += $$PWD/abstractfindwidget.h $$PWD/texteditfindwidget.h $$PWD/itemviewfindwidget.h +SOURCES += $$PWD/abstractfindwidget.cpp $$PWD/texteditfindwidget.cpp $$PWD/itemviewfindwidget.cpp +RESOURCES += $$PWD/findwidget.qrc diff --git a/tools/shared/findwidget/findwidget.qrc b/tools/shared/findwidget/findwidget.qrc new file mode 100644 index 0000000..1d45b25 --- /dev/null +++ b/tools/shared/findwidget/findwidget.qrc @@ -0,0 +1,14 @@ +<RCC> + <qresource prefix="/trolltech/shared"> + <file>images/mac/closetab.png</file> + <file>images/mac/next.png</file> + <file>images/mac/previous.png</file> + <file>images/mac/searchfind.png</file> + <file>images/win/closetab.png</file> + <file>images/win/next.png</file> + <file>images/win/previous.png</file> + <file>images/win/searchfind.png</file> + <file>images/wrap.png</file> + </qresource> +</RCC> + diff --git a/tools/shared/findwidget/images/mac/closetab.png b/tools/shared/findwidget/images/mac/closetab.png Binary files differnew file mode 100644 index 0000000..ab9d669 --- /dev/null +++ b/tools/shared/findwidget/images/mac/closetab.png diff --git a/tools/shared/findwidget/images/mac/next.png b/tools/shared/findwidget/images/mac/next.png Binary files differnew file mode 100644 index 0000000..a585cab --- /dev/null +++ b/tools/shared/findwidget/images/mac/next.png diff --git a/tools/shared/findwidget/images/mac/previous.png b/tools/shared/findwidget/images/mac/previous.png Binary files differnew file mode 100644 index 0000000..612fb34 --- /dev/null +++ b/tools/shared/findwidget/images/mac/previous.png diff --git a/tools/shared/findwidget/images/mac/searchfind.png b/tools/shared/findwidget/images/mac/searchfind.png Binary files differnew file mode 100644 index 0000000..3561745 --- /dev/null +++ b/tools/shared/findwidget/images/mac/searchfind.png diff --git a/tools/shared/findwidget/images/win/closetab.png b/tools/shared/findwidget/images/win/closetab.png Binary files differnew file mode 100644 index 0000000..ef9e020 --- /dev/null +++ b/tools/shared/findwidget/images/win/closetab.png diff --git a/tools/shared/findwidget/images/win/next.png b/tools/shared/findwidget/images/win/next.png Binary files differnew file mode 100644 index 0000000..8df4127 --- /dev/null +++ b/tools/shared/findwidget/images/win/next.png diff --git a/tools/shared/findwidget/images/win/previous.png b/tools/shared/findwidget/images/win/previous.png Binary files differnew file mode 100644 index 0000000..0780bc2 --- /dev/null +++ b/tools/shared/findwidget/images/win/previous.png diff --git a/tools/shared/findwidget/images/win/searchfind.png b/tools/shared/findwidget/images/win/searchfind.png Binary files differnew file mode 100644 index 0000000..6ea35e9 --- /dev/null +++ b/tools/shared/findwidget/images/win/searchfind.png diff --git a/tools/shared/findwidget/images/wrap.png b/tools/shared/findwidget/images/wrap.png Binary files differnew file mode 100644 index 0000000..90f18d9 --- /dev/null +++ b/tools/shared/findwidget/images/wrap.png diff --git a/tools/shared/findwidget/itemviewfindwidget.cpp b/tools/shared/findwidget/itemviewfindwidget.cpp new file mode 100644 index 0000000..e1f8468 --- /dev/null +++ b/tools/shared/findwidget/itemviewfindwidget.cpp @@ -0,0 +1,317 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the tools applications of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 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 the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +/*! \class ItemViewFindWidget + + \brief A search bar that is commonly added below the searchable item view. + + \internal + + This widget implements a search bar which becomes visible when the user + wants to start searching. It is a modern replacement for the commonly used + search dialog. It is usually placed below a QAbstractItemView using a QVBoxLayout. + + The QAbstractItemView instance will need to be associated with this class using + setItemView(). + + The search is incremental and can be set to case sensitive or whole words + using buttons available on the search bar. + + The item traversal order should fit QTreeView, QTableView and QListView alike. + More complex tree structures will work as well, assuming the branch structure + is painted left to the items, without crossing lines. + + \sa QAbstractItemView + */ + +#include "itemviewfindwidget.h" + +#include <QtGui/QAbstractItemView> +#include <QtGui/QCheckBox> +#include <QtGui/QTreeView> + +QT_BEGIN_NAMESPACE + +/*! + Constructs a ItemViewFindWidget. + + \a flags is passed to the AbstractFindWidget constructor. + \a parent is passed to the QWidget constructor. + */ +ItemViewFindWidget::ItemViewFindWidget(FindFlags flags, QWidget *parent) + : AbstractFindWidget(flags, parent) + , m_itemView(0) +{ +} + +/*! + Associates a QAbstractItemView with this find widget. Searches done using this find + widget will then apply to the given QAbstractItemView. + + An event filter is set on the QAbstractItemView which intercepts the ESC key while + the find widget is active, and uses it to deactivate the find widget. + + If the find widget is already associated with a QAbstractItemView, the event filter + is removed from this QAbstractItemView first. + + \a itemView may be NULL. + */ +void ItemViewFindWidget::setItemView(QAbstractItemView *itemView) +{ + if (m_itemView) + m_itemView->removeEventFilter(this); + + m_itemView = itemView; + + if (m_itemView) + m_itemView->installEventFilter(this); +} + +/*! + \reimp + */ +void ItemViewFindWidget::deactivate() +{ + if (m_itemView) + m_itemView->setFocus(); + + AbstractFindWidget::deactivate(); +} + +// Sorting is needed to find the start/end of the selection. +// This is utter black magic. And it is damn slow. +static bool indexLessThan(const QModelIndex &a, const QModelIndex &b) +{ + // First determine the nesting of each index in the tree. + QModelIndex aa = a; + int aDepth = 0; + while (aa.parent() != QModelIndex()) { + // As a side effect, check if one of the items is the parent of the other. + // Children are always displayed below their parents, so sort them further down. + if (aa.parent() == b) + return true; + aa = aa.parent(); + aDepth++; + } + QModelIndex ba = b; + int bDepth = 0; + while (ba.parent() != QModelIndex()) { + if (ba.parent() == a) + return false; + ba = ba.parent(); + bDepth++; + } + // Now find indices at comparable depth. + for (aa = a; aDepth > bDepth; aDepth--) + aa = aa.parent(); + for (ba = b; aDepth < bDepth; bDepth--) + ba = ba.parent(); + // If they have the same parent, sort them within a top-to-bottom, left-to-right rectangle. + if (aa.parent() == ba.parent()) { + if (aa.row() < ba.row()) + return true; + if (aa.row() > ba.row()) + return false; + return aa.column() < ba.column(); + } + // Now try to find indices that have the same grandparent. This ends latest at the root node. + while (aa.parent().parent() != ba.parent().parent()) { + aa = aa.parent(); + ba = ba.parent(); + } + // A bigger row is always displayed further down. + if (aa.parent().row() < ba.parent().row()) + return true; + if (aa.parent().row() > ba.parent().row()) + return false; + // Here's the trick: a child spawned from a bigger column is displayed further *up*. + // That's because the tree lines are on the left and are supposed not to cross each other. + // This case is mostly academical, as "all" models spawn children from the first column. + return aa.parent().column() > ba.parent().column(); +} + +/*! + \reimp + */ +void ItemViewFindWidget::find(const QString &ttf, bool skipCurrent, bool backward, bool *found, bool *wrapped) +{ + if (!m_itemView || !m_itemView->model()->hasChildren()) + return; + + QModelIndex idx; + if (skipCurrent && m_itemView->selectionModel()->hasSelection()) { + QModelIndexList il = m_itemView->selectionModel()->selectedIndexes(); + qSort(il.begin(), il.end(), indexLessThan); + idx = backward ? il.first() : il.last(); + } else { + idx = m_itemView->currentIndex(); + } + + *found = true; + QModelIndex newIdx = idx; + + if (!ttf.isEmpty()) { + if (newIdx.isValid()) { + int column = newIdx.column(); + if (skipCurrent) + if (QTreeView *tv = qobject_cast<QTreeView *>(m_itemView)) + if (tv->allColumnsShowFocus()) + column = backward ? 0 : m_itemView->model()->columnCount(newIdx.parent()) - 1; + newIdx = findHelper(ttf, skipCurrent, backward, + newIdx.parent(), newIdx.row(), column); + } + if (!newIdx.isValid()) { + int row = backward ? m_itemView->model()->rowCount() : 0; + int column = backward ? 0 : -1; + newIdx = findHelper(ttf, true, backward, m_itemView->rootIndex(), row, column); + if (!newIdx.isValid()) { + *found = false; + newIdx = idx; + } else { + *wrapped = true; + } + } + } + + if (!isVisible()) + show(); + + m_itemView->setCurrentIndex(newIdx); +} + +// You are not expected to understand the following two functions. +// The traversal order is described in the indexLessThan() comments above. + +static inline bool skipForward(const QAbstractItemModel *model, QModelIndex &parent, int &row, int &column) +{ + forever { + column++; + if (column < model->columnCount(parent)) + return true; + forever { + while (--column >= 0) { + QModelIndex nIdx = model->index(row, column, parent); + if (nIdx.isValid()) { + if (model->hasChildren(nIdx)) { + row = 0; + column = 0; + parent = nIdx; + return true; + } + } + } + if (++row < model->rowCount(parent)) + break; + if (!parent.isValid()) + return false; + row = parent.row(); + column = parent.column(); + parent = parent.parent(); + } + } +} + +static inline bool skipBackward(const QAbstractItemModel *model, QModelIndex &parent, int &row, int &column) +{ + column--; + if (column == -1) { + if (--row < 0) { + if (!parent.isValid()) + return false; + row = parent.row(); + column = parent.column(); + parent = parent.parent(); + } + while (++column < model->columnCount(parent)) { + QModelIndex nIdx = model->index(row, column, parent); + if (nIdx.isValid()) { + if (model->hasChildren(nIdx)) { + row = model->rowCount(nIdx) - 1; + column = -1; + parent = nIdx; + } + } + } + column--; + } + return true; +} + +// QAbstractItemModel::match() does not support backwards searching. Still using it would +// be just a bit inefficient (not much worse than when no match is found). +// The bigger problem is that QAbstractItemView does not provide a method to sort a +// set of indices in traversal order (to find the start and end of the selection). +// Consequently, we do everything by ourselves to be consistent. Of course, this puts +// constraints on the allowable visualizations. +QModelIndex ItemViewFindWidget::findHelper(const QString &textToFind, bool skipCurrent, bool backward, + QModelIndex parent, int row, int column) +{ + const QAbstractItemModel *model = m_itemView->model(); + forever { + if (skipCurrent) { + if (backward) { + if (!skipBackward(model, parent, row, column)) + return QModelIndex(); + } else { + if (!skipForward(model, parent, row, column)) + return QModelIndex(); + } + } + + QModelIndex idx = model->index(row, column, parent); + if (idx.isValid()) { + Qt::CaseSensitivity cs = caseSensitive() ? Qt::CaseSensitive : Qt::CaseInsensitive; + + if (wholeWords()) { + QString rx = QLatin1String("\\b") + QRegExp::escape(textToFind) + QLatin1String("\\b"); + if (idx.data().toString().indexOf(QRegExp(rx, cs)) >= 0) + return idx; + } else { + if (idx.data().toString().indexOf(textToFind, 0, cs) >= 0) + return idx; + } + } + + skipCurrent = true; + } +} + +QT_END_NAMESPACE diff --git a/tools/shared/findwidget/itemviewfindwidget.h b/tools/shared/findwidget/itemviewfindwidget.h new file mode 100644 index 0000000..71bb8f6 --- /dev/null +++ b/tools/shared/findwidget/itemviewfindwidget.h @@ -0,0 +1,78 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the tools applications of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 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 the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef ITEMVIEWFINDWIDGET_H +#define ITEMVIEWFINDWIDGET_H + +#include "abstractfindwidget.h" + +#include <QModelIndex> + +QT_BEGIN_NAMESPACE + +class QAbstractItemView; + +class ItemViewFindWidget : public AbstractFindWidget +{ + Q_OBJECT + +public: + ItemViewFindWidget(FindFlags flags = FindFlags(), QWidget *parent = 0); + + QAbstractItemView *itemView() const + { return m_itemView; } + + void setItemView(QAbstractItemView *itemView); + +protected: + virtual void deactivate(); + virtual void find(const QString &textToFind, bool skipCurrent, bool backward, bool *found, bool *wrapped); + +private: + QModelIndex findHelper(const QString &textToFind, bool skipCurrent, bool backward, + QModelIndex parent, int row, int column); + + QAbstractItemView *m_itemView; +}; + +QT_END_NAMESPACE + +#endif // ITEMVIEWFINDWIDGET_H diff --git a/tools/shared/findwidget/texteditfindwidget.cpp b/tools/shared/findwidget/texteditfindwidget.cpp new file mode 100644 index 0000000..73100f5 --- /dev/null +++ b/tools/shared/findwidget/texteditfindwidget.cpp @@ -0,0 +1,169 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the tools applications of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 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 the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +/*! \class TextEditFindWidget + + \brief A search bar that is commonly added below the searchable text. + + \internal + + This widget implements a search bar which becomes visible when the user + wants to start searching. It is a modern replacement for the commonly used + search dialog. It is usually placed below a QTextEdit using a QVBoxLayout. + + The QTextEdit instance will need to be associated with this class using + setTextEdit(). + + The search is incremental and can be set to case sensitive or whole words + using buttons available on the search bar. + + \sa QTextEdit + */ + +#include "texteditfindwidget.h" + +#include <QtGui/QCheckBox> +#include <QtGui/QTextCursor> +#include <QtGui/QTextEdit> + +QT_BEGIN_NAMESPACE + +/*! + Constructs a TextEditFindWidget. + + \a flags is passed to the AbstractFindWidget constructor. + \a parent is passed to the QWidget constructor. + */ +TextEditFindWidget::TextEditFindWidget(FindFlags flags, QWidget *parent) + : AbstractFindWidget(flags, parent) + , m_textEdit(0) +{ +} + +/*! + Associates a QTextEdit with this find widget. Searches done using this find + widget will then apply to the given QTextEdit. + + An event filter is set on the QTextEdit which intercepts the ESC key while + the find widget is active, and uses it to deactivate the find widget. + + If the find widget is already associated with a QTextEdit, the event filter + is removed from this QTextEdit first. + + \a textEdit may be NULL. + */ +void TextEditFindWidget::setTextEdit(QTextEdit *textEdit) +{ + if (m_textEdit) + m_textEdit->removeEventFilter(this); + + m_textEdit = textEdit; + + if (m_textEdit) + m_textEdit->installEventFilter(this); +} + +/*! + \reimp + */ +void TextEditFindWidget::deactivate() +{ + // Pass focus to the text edit + if (m_textEdit) + m_textEdit->setFocus(); + + AbstractFindWidget::deactivate(); +} + +/*! + \reimp + */ +void TextEditFindWidget::find(const QString &ttf, bool skipCurrent, bool backward, bool *found, bool *wrapped) +{ + if (!m_textEdit) + return; + + QTextCursor cursor = m_textEdit->textCursor(); + QTextDocument *doc = m_textEdit->document(); + + if (!doc || cursor.isNull()) + return; + + if (cursor.hasSelection()) + cursor.setPosition((skipCurrent && !backward) ? cursor.position() : cursor.anchor()); + + *found = true; + QTextCursor newCursor = cursor; + + if (!ttf.isEmpty()) { + QTextDocument::FindFlags options; + + if (backward) + options |= QTextDocument::FindBackward; + + if (caseSensitive()) + options |= QTextDocument::FindCaseSensitively; + + if (wholeWords()) + options |= QTextDocument::FindWholeWords; + + newCursor = doc->find(ttf, cursor, options); + if (newCursor.isNull()) { + QTextCursor ac(doc); + ac.movePosition(options & QTextDocument::FindBackward + ? QTextCursor::End : QTextCursor::Start); + newCursor = doc->find(ttf, ac, options); + if (newCursor.isNull()) { + *found = false; + newCursor = cursor; + } else { + *wrapped = true; + } + } + } + + if (!isVisible()) + show(); + + m_textEdit->setTextCursor(newCursor); +} + +QT_END_NAMESPACE diff --git a/tools/shared/findwidget/texteditfindwidget.h b/tools/shared/findwidget/texteditfindwidget.h new file mode 100644 index 0000000..4074ff0 --- /dev/null +++ b/tools/shared/findwidget/texteditfindwidget.h @@ -0,0 +1,73 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the tools applications of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 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 the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef TEXTEDITFINDWIDGET_H +#define TEXTEDITFINDWIDGET_H + +#include "abstractfindwidget.h" + +QT_BEGIN_NAMESPACE + +class QTextEdit; + +class TextEditFindWidget : public AbstractFindWidget +{ + Q_OBJECT + +public: + TextEditFindWidget(FindFlags flags = FindFlags(), QWidget *parent = 0); + + QTextEdit *textEdit() const + { return m_textEdit; } + + void setTextEdit(QTextEdit *textEdit); + +protected: + virtual void deactivate(); + virtual void find(const QString &textToFind, bool skipCurrent, bool backward, bool *found, bool *wrapped); + +private: + QTextEdit *m_textEdit; +}; + +QT_END_NAMESPACE + +#endif // TEXTEDITFINDWIDGET_H |