source: trunk/tools/shared/findwidget/itemviewfindwidget.cpp@ 987

Last change on this file since 987 was 846, checked in by Dmitry A. Kuminov, 14 years ago

trunk: Merged in qt 4.7.2 sources from branches/vendor/nokia/qt.

File size: 10.9 KB
Line 
1/****************************************************************************
2**
3** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
4** All rights reserved.
5** Contact: Nokia Corporation ([email protected])
6**
7** This file is part of the tools applications of the Qt Toolkit.
8**
9** $QT_BEGIN_LICENSE:LGPL$
10** Commercial Usage
11** Licensees holding valid Qt Commercial licenses may use this file in
12** accordance with the Qt Commercial License Agreement provided with the
13** Software or, alternatively, in accordance with the terms contained in
14** a written agreement between you and Nokia.
15**
16** GNU Lesser General Public License Usage
17** Alternatively, this file may be used under the terms of the GNU Lesser
18** General Public License version 2.1 as published by the Free Software
19** Foundation and appearing in the file LICENSE.LGPL included in the
20** packaging of this file. Please review the following information to
21** ensure the GNU Lesser General Public License version 2.1 requirements
22** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
23**
24** In addition, as a special exception, Nokia gives you certain additional
25** rights. These rights are described in the Nokia Qt LGPL Exception
26** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
27**
28** GNU General Public License Usage
29** Alternatively, this file may be used under the terms of the GNU
30** General Public License version 3.0 as published by the Free Software
31** Foundation and appearing in the file LICENSE.GPL included in the
32** packaging of this file. Please review the following information to
33** ensure the GNU General Public License version 3.0 requirements will be
34** met: http://www.gnu.org/copyleft/gpl.html.
35**
36** If you have questions regarding the use of this file, please contact
37** Nokia at [email protected].
38** $QT_END_LICENSE$
39**
40****************************************************************************/
41
42/*! \class ItemViewFindWidget
43
44 \brief A search bar that is commonly added below the searchable item view.
45
46 \internal
47
48 This widget implements a search bar which becomes visible when the user
49 wants to start searching. It is a modern replacement for the commonly used
50 search dialog. It is usually placed below a QAbstractItemView using a QVBoxLayout.
51
52 The QAbstractItemView instance will need to be associated with this class using
53 setItemView().
54
55 The search is incremental and can be set to case sensitive or whole words
56 using buttons available on the search bar.
57
58 The item traversal order should fit QTreeView, QTableView and QListView alike.
59 More complex tree structures will work as well, assuming the branch structure
60 is painted left to the items, without crossing lines.
61
62 \sa QAbstractItemView
63 */
64
65#include "itemviewfindwidget.h"
66
67#include <QtGui/QAbstractItemView>
68#include <QtGui/QCheckBox>
69#include <QtGui/QTreeView>
70
71QT_BEGIN_NAMESPACE
72
73/*!
74 Constructs a ItemViewFindWidget.
75
76 \a flags is passed to the AbstractFindWidget constructor.
77 \a parent is passed to the QWidget constructor.
78 */
79ItemViewFindWidget::ItemViewFindWidget(FindFlags flags, QWidget *parent)
80 : AbstractFindWidget(flags, parent)
81 , m_itemView(0)
82{
83}
84
85/*!
86 Associates a QAbstractItemView with this find widget. Searches done using this find
87 widget will then apply to the given QAbstractItemView.
88
89 An event filter is set on the QAbstractItemView which intercepts the ESC key while
90 the find widget is active, and uses it to deactivate the find widget.
91
92 If the find widget is already associated with a QAbstractItemView, the event filter
93 is removed from this QAbstractItemView first.
94
95 \a itemView may be NULL.
96 */
97void ItemViewFindWidget::setItemView(QAbstractItemView *itemView)
98{
99 if (m_itemView)
100 m_itemView->removeEventFilter(this);
101
102 m_itemView = itemView;
103
104 if (m_itemView)
105 m_itemView->installEventFilter(this);
106}
107
108/*!
109 \reimp
110 */
111void ItemViewFindWidget::deactivate()
112{
113 if (m_itemView)
114 m_itemView->setFocus();
115
116 AbstractFindWidget::deactivate();
117}
118
119// Sorting is needed to find the start/end of the selection.
120// This is utter black magic. And it is damn slow.
121static bool indexLessThan(const QModelIndex &a, const QModelIndex &b)
122{
123 // First determine the nesting of each index in the tree.
124 QModelIndex aa = a;
125 int aDepth = 0;
126 while (aa.parent() != QModelIndex()) {
127 // As a side effect, check if one of the items is the parent of the other.
128 // Children are always displayed below their parents, so sort them further down.
129 if (aa.parent() == b)
130 return true;
131 aa = aa.parent();
132 aDepth++;
133 }
134 QModelIndex ba = b;
135 int bDepth = 0;
136 while (ba.parent() != QModelIndex()) {
137 if (ba.parent() == a)
138 return false;
139 ba = ba.parent();
140 bDepth++;
141 }
142 // Now find indices at comparable depth.
143 for (aa = a; aDepth > bDepth; aDepth--)
144 aa = aa.parent();
145 for (ba = b; aDepth < bDepth; bDepth--)
146 ba = ba.parent();
147 // If they have the same parent, sort them within a top-to-bottom, left-to-right rectangle.
148 if (aa.parent() == ba.parent()) {
149 if (aa.row() < ba.row())
150 return true;
151 if (aa.row() > ba.row())
152 return false;
153 return aa.column() < ba.column();
154 }
155 // Now try to find indices that have the same grandparent. This ends latest at the root node.
156 while (aa.parent().parent() != ba.parent().parent()) {
157 aa = aa.parent();
158 ba = ba.parent();
159 }
160 // A bigger row is always displayed further down.
161 if (aa.parent().row() < ba.parent().row())
162 return true;
163 if (aa.parent().row() > ba.parent().row())
164 return false;
165 // Here's the trick: a child spawned from a bigger column is displayed further *up*.
166 // That's because the tree lines are on the left and are supposed not to cross each other.
167 // This case is mostly academical, as "all" models spawn children from the first column.
168 return aa.parent().column() > ba.parent().column();
169}
170
171/*!
172 \reimp
173 */
174void ItemViewFindWidget::find(const QString &ttf, bool skipCurrent, bool backward, bool *found, bool *wrapped)
175{
176 if (!m_itemView || !m_itemView->model()->hasChildren())
177 return;
178
179 QModelIndex idx;
180 if (skipCurrent && m_itemView->selectionModel()->hasSelection()) {
181 QModelIndexList il = m_itemView->selectionModel()->selectedIndexes();
182 qSort(il.begin(), il.end(), indexLessThan);
183 idx = backward ? il.first() : il.last();
184 } else {
185 idx = m_itemView->currentIndex();
186 }
187
188 *found = true;
189 QModelIndex newIdx = idx;
190
191 if (!ttf.isEmpty()) {
192 if (newIdx.isValid()) {
193 int column = newIdx.column();
194 if (skipCurrent)
195 if (QTreeView *tv = qobject_cast<QTreeView *>(m_itemView))
196 if (tv->allColumnsShowFocus())
197 column = backward ? 0 : m_itemView->model()->columnCount(newIdx.parent()) - 1;
198 newIdx = findHelper(ttf, skipCurrent, backward,
199 newIdx.parent(), newIdx.row(), column);
200 }
201 if (!newIdx.isValid()) {
202 int row = backward ? m_itemView->model()->rowCount() : 0;
203 int column = backward ? 0 : -1;
204 newIdx = findHelper(ttf, true, backward, m_itemView->rootIndex(), row, column);