|
1 /* |
|
2 * Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). |
|
3 * All rights reserved. |
|
4 * This component and the accompanying materials are made available |
|
5 * under the terms of "Eclipse Public License v1.0" |
|
6 * which accompanies this distribution, and is available |
|
7 * at the URL "http://www.eclipse.org/legal/epl-v10.html". |
|
8 * |
|
9 * Initial Contributors: |
|
10 * Nokia Corporation - initial contribution. |
|
11 * |
|
12 * Contributors: |
|
13 * |
|
14 * Description: hssearchview.cpp |
|
15 * |
|
16 */ |
|
17 |
|
18 #include <QApplication> |
|
19 #include <QSortFilterProxyModel> |
|
20 #include <HbGroupBox> |
|
21 #include <HbLineEdit> |
|
22 #include <HbListView> |
|
23 #include <HbAbstractViewItem> |
|
24 #include <HbView> |
|
25 #include <HbSearchPanel> |
|
26 |
|
27 #include <caitemmodel.h> |
|
28 |
|
29 #include "hslistviewitem.h" |
|
30 #include "hsmainwindow.h" |
|
31 #include "hssearchview.h" |
|
32 |
|
33 /*! |
|
34 * \fn void activated(const QModelIndex &index) |
|
35 * This signal is emitted when item is tapped. |
|
36 * \param index of an item that was tapped. |
|
37 */ |
|
38 |
|
39 /*! |
|
40 * \fn void longPressed(HbAbstractViewItem *item, const QPointF &coords) |
|
41 * This singal is emitted when view is long pressed. |
|
42 * \param item View item that was long pressed. |
|
43 * \param coords View coordinates of the long press. |
|
44 */ |
|
45 |
|
46 /*! |
|
47 * \fn void searchComplete(const QModelIndex &firstMatching) |
|
48 * This signal is emitted when search action has been finished |
|
49 * and search view is about to quit. |
|
50 * \param firstMatching of first item in search filtered view. |
|
51 */ |
|
52 |
|
53 |
|
54 /*! |
|
55 Set up proxy model for search. |
|
56 \param builder Retrieves UI widgets. |
|
57 \param stateContext Identifies the state where search starts from. |
|
58 \param mainWindow Enables switching between originating and search view. |
|
59 */ |
|
60 HsSearchView::HsSearchView(HsMenuViewBuilder &builder, |
|
61 HsStateContext stateContext, HsMainWindow &mainWindow) : |
|
62 mProxyModel(new QSortFilterProxyModel(this)), mSearchView(NULL), |
|
63 mSearchListView(NULL), mSearchPanel(NULL), mStateContext(stateContext), |
|
64 mBuilder(builder), mMainWindow(mainWindow), mListView(NULL), |
|
65 mVkbHost(NULL), mSearchViewBuilder(), mEmptyResultText(true) |
|
66 { |
|
67 mProxyModel->setFilterRole(CaItemModel::TextRole); |
|
68 mProxyModel->setFilterKeyColumn(0); |
|
69 mProxyModel->setSortRole(CaItemModel::TextRole); |
|
70 mProxyModel->setDynamicSortFilter(true); |
|
71 } |
|
72 |
|
73 /*! |
|
74 Empty. |
|
75 */ |
|
76 HsSearchView::~HsSearchView() |
|
77 { |
|
78 } |
|
79 |
|
80 /*! |
|
81 Makes the UI to show or hide view search panel. |
|
82 When search panel is shown the view toolbar and status pane |
|
83 are hidden until search panel is hidden. |
|
84 \param visible When true search panel will be shown, |
|
85 otherwise it will be hidden. |
|
86 */ |
|
87 void HsSearchView::setSearchPanelVisible(bool visible) |
|
88 { |
|
89 HSMENUTEST_FUNC_ENTRY("HsMenuView::setSearchPanelVisible"); |
|
90 |
|
91 if (visible && !isActive()) { |
|
92 searchBegins(); |
|
93 connectSearchItemViewsSignals(); |
|
94 connectSearchPanelSignals(); |
|
95 } else if (isActive()) { |
|
96 disconnectSearchPanelSignals(); |
|
97 disconnectSearchItemViewsSignals(); |
|
98 searchFinished(); |
|
99 } |
|
100 HSMENUTEST_FUNC_EXIT("HsMenuView::setSearchPanelVisible"); |
|
101 } |
|
102 |
|
103 /*! |
|
104 \param view List view to operate on. |
|
105 \return Index of F=first visible item of \a view if any |
|
106 or default QModelIndex otherwise. |
|
107 */ |
|
108 QModelIndex HsSearchView::firstVisibleItemIndex(const HbListView *view) const |
|
109 { |
|
110 const QList<HbAbstractViewItem *> array = view->visibleItems(); |
|
111 if (array.count()) { |
|
112 return array[0]->modelIndex(); |
|
113 } |
|
114 return QModelIndex(); |
|
115 } |
|
116 |
|
117 /*! |
|
118 Displays search panel with view representing search results. |
|
119 */ |
|
120 void HsSearchView::searchBegins() |
|
121 { |
|
122 HSMENUTEST_FUNC_ENTRY("HsSearchView::searchBegins"); |
|
123 |
|
124 setOriginatingContext(); |
|
125 |
|
126 mListView = mBuilder.currentListView(); |
|
127 mSearchViewInitialIndex = firstVisibleItemIndex(mListView); |
|
128 |
|
129 mProxyModel->invalidate(); |
|
130 mProxyModel->setSourceModel(mListView->model()); |
|
131 mProxyModel->setFilterRegExp( |
|
132 QRegExp(QString(".*"), Qt::CaseInsensitive, QRegExp::RegExp)); |
|
133 |
|
134 mSearchView = mSearchViewBuilder.searchView(); |
|
135 mSearchListView = mSearchViewBuilder.searchListView(); |
|
136 mSearchPanel = mSearchViewBuilder.searchPanel(); |
|
137 initSearchPanel(*mSearchPanel); |
|
138 mVkbHost.reset(new HbShrinkingVkbHost(mSearchView)); |
|
139 |
|
140 if (mBuilder.currentViewLabel()) { |
|
141 mSearchViewBuilder.searchViewLabel()->setHeading( |
|
142 mBuilder.currentViewLabel()->heading()); |
|
143 mSearchViewBuilder.setSearchLabledContext(); |
|
144 } |
|
145 |
|
146 mSearchView->hideItems(Hb::AllItems); |
|
147 mSearchListView->setModel(mProxyModel, new HsListViewItem()); |
|
148 |
|
149 mMainWindow.setCurrentView(mSearchView); |
|
150 |
|
151 openVkb(); |
|
152 |
|
153 mSearchListView->scrollTo( |
|
154 mProxyModel->mapFromSource(mSearchViewInitialIndex), |
|
155 HbAbstractItemView::PositionAtTop); |
|
156 |
|
157 setNoResultsVisibility(); |
|
158 HSMENUTEST_FUNC_EXIT("HsSearchView::searchBegins"); |
|
159 } |
|
160 |
|
161 /*! |
|
162 Handles button visibility. |
|
163 \param visibility indicates whether show or not to show 'empty' label. |
|
164 */ |
|
165 void HsSearchView::setNoResultsVisibility() |
|
166 { |
|
167 if (mProxyModel->rowCount() && mEmptyResultText) { |
|
168 mSearchViewBuilder.loadViewListSection(); |
|
169 mEmptyResultText = false; |
|
170 } else if (!mProxyModel->rowCount() && !mEmptyResultText) { |
|
171 mSearchViewBuilder.loadViewEmptySection(); |
|
172 mEmptyResultText = true; |
|
173 } |
|
174 } |
|
175 |
|
176 /* |
|
177 Connects \a activated and \a longPressed signals coming from search list |
|
178 view to emit corresponding signal of this object with translated model index. |
|
179 */ |
|
180 void HsSearchView::connectSearchItemViewsSignals() |
|
181 { |
|
182 connect(mSearchListView, SIGNAL(pressed(QModelIndex)), |
|
183 this, SLOT(hideVkb()), Qt::UniqueConnection); |
|
184 connect(mSearchListView, SIGNAL(activated(QModelIndex)), |
|
185 this, SLOT(activatedProxySlot(QModelIndex)), Qt::UniqueConnection); |
|
186 connect(mSearchListView, SIGNAL(longPressed(HbAbstractViewItem *, QPointF)), |
|
187 this, SLOT(longPressedProxySlot(HbAbstractViewItem *, QPointF)), |
|
188 Qt::UniqueConnection); |
|
189 |
|
190 } |
|
191 |
|
192 /*! |
|
193 Disconnects \a activated and \a longPressed signals coming from list |
|
194 view from to emit corresponding signal |
|
195 of this object with translated model index. |
|
196 */ |
|
197 void HsSearchView::disconnectSearchItemViewsSignals() |
|
198 { |
|
199 disconnect(mSearchListView, SIGNAL(pressed(QModelIndex)), |
|
200 this, SLOT(hideVkb())); |
|
201 disconnect(mSearchListView, SIGNAL(activated(QModelIndex)), |
|
202 this, SLOT(activatedProxySlot(QModelIndex))); |
|
203 disconnect(mSearchListView, |
|
204 SIGNAL(longPressed(HbAbstractViewItem *, QPointF)), |
|
205 this, SLOT(longPressedProxySlot(HbAbstractViewItem *, QPointF))); |
|
206 } |
|
207 |
|
208 |
|
209 /*! |
|
210 Makes search panel invisible. |
|
211 Equivalent to \a setSearchPanelVisible(false). |
|
212 */ |
|
213 void HsSearchView::hideSearchPanel() |
|
214 { |
|
215 HSMENUTEST_FUNC_ENTRY("HsMenuView::hideSearchPanel"); |
|
216 setSearchPanelVisible(false); |
|
217 HSMENUTEST_FUNC_EXIT("HsMenuView::hideSearchPanel"); |
|
218 } |
|
219 |
|
220 /*! |
|
221 Connects signals \a exitClicked and \a criteriaChanged emitted |
|
222 by search panel with handling slots of the object or its members. |
|
223 */ |
|
224 void HsSearchView::connectSearchPanelSignals() |
|
225 { |
|
226 connect(mProxyModel, SIGNAL(dataChanged(QModelIndex, QModelIndex)), |
|
227 this, SLOT(setNoResultsVisibility()), Qt::UniqueConnection); |
|
228 connect(mProxyModel, SIGNAL(rowsInserted(QModelIndex, int, int)), |
|
229 this, SLOT(setNoResultsVisibility())); |
|
230 connect(mProxyModel, SIGNAL(rowsRemoved(QModelIndex, int, int)), |
|
231 this, SLOT(setNoResultsVisibility())); |
|
232 connect(mSearchPanel, SIGNAL(exitClicked()), |
|
233 this, SLOT(hideSearchPanel())); |
|
234 connect(mSearchPanel, SIGNAL(criteriaChanged(QString)), |
|
235 this, SLOT(findItem(QString)), Qt::UniqueConnection); |
|
236 } |
|
237 |
|
238 /*! |
|
239 Disconnects signals \a exitClicked and \a criteriaChanged emitted |
|
240 by search panel from handling slots of the object or its members |
|
241 Scrolls view to state before connections. |
|
242 */ |
|
243 void HsSearchView::disconnectSearchPanelSignals() |
|
244 { |
|
245 mProxyModel->disconnect(this); |
|
246 mSearchPanel->disconnect(this); |
|
247 } |
|
248 |
|
249 /*! |
|
250 Scrolls originating view to first item matching search criteria. |
|
251 */ |
|
252 void HsSearchView::searchFinished() |
|
253 { |
|
254 HSMENUTEST_FUNC_ENTRY("HsSearchView::searchFinished"); |
|
255 |
|
256 mIndexToScrollAfterSearchDone = firstVisibleItemIndex(mSearchListView); |
|
257 |
|
258 // emiting searchComplete must be done |
|
259 // after this->isActive() returns false |
|
260 mSearchListView = NULL; |
|
261 mSearchPanel = NULL; |
|
262 |
|
263 emit searchComplete(mProxyModel->mapToSource( |
|
264 mIndexToScrollAfterSearchDone)); |
|
265 |
|
266 HbVkbHost::detachHost(mSearchView); |
|
267 mVkbHost.reset(NULL); |
|
268 |
|
269 mProxyModel->setSourceModel(NULL); |
|
270 HSMENUTEST_FUNC_EXIT("HsSearchView::searchFinished"); |
|
271 } |
|
272 |
|
273 /*! |
|
274 Looks up for item and if found scrolls to it. |
|
275 \param criteriaStr The item name to find. |
|
276 */ |
|
277 void HsSearchView::findItem(QString criteriaStr) |
|
278 { |
|
279 qDebug() << "HsSearchView::findItem: " + criteriaStr; |
|
280 HSMENUTEST_FUNC_ENTRY("HsSearchView::findItem"); |
|
281 |
|
282 mProxyModel->setFilterRegExp( |
|
283 QRegExp("(^|\\b)" + criteriaStr, Qt::CaseInsensitive)); |
|
284 |
|
285 mSearchListView->scrollTo( |
|
286 mProxyModel->index(0, 0), HbAbstractItemView::PositionAtTop); |
|
287 |
|
288 HSMENUTEST_FUNC_EXIT("HsSearchView::findItem"); |
|
289 } |
|
290 |
|
291 /*! |
|
292 Slot used to translate activated signal from proxy model to normal model. |
|
293 \param index Represents an item activated in search list view. |
|
294 */ |
|
295 void HsSearchView::activatedProxySlot(const QModelIndex &index) |
|
296 { |
|
297 emit activated(mProxyModel->mapToSource(index)); |
|
298 } |
|
299 |
|
300 /*! |
|
301 Slot used to forward 'long pressed' signal with item description transladed |
|
302 from search view context to context of the view search was requested from. |
|
303 \param item Long pressed item. |
|
304 \param coords Coordinates of the long press action. |
|
305 */ |
|
306 void HsSearchView::longPressedProxySlot( |
|
307 HbAbstractViewItem *item, const QPointF &coords) |
|
308 { |
|
309 /* |
|
310 this is a kind of hack, introduced for reasons: |
|
311 item object should be reusable later, but orbit (or qt) prevents setting |
|
312 its index model to previous state |
|
313 */ |
|
314 mSearchViewLongPressedIndex = mProxyModel->mapToSource( |
|
315 item->modelIndex()); |
|
316 QScopedPointer<HbAbstractViewItem> itemNew(item->createItem()); |
|
317 itemNew->setModelIndex(mSearchViewLongPressedIndex); |
|
318 emit longPressed(itemNew.data(), coords); |
|
319 } |
|
320 |
|
321 /*! |
|
322 Sets up search panel. |
|
323 |
|
324 \param searchPanel Search panel to initialize. |
|
325 */ |
|
326 void HsSearchView::initSearchPanel(HbSearchPanel &searchPanel) |
|
327 { |
|
328 HbLineEdit *const lineEdit(searchPanelLineEdit(searchPanel)); |
|
329 |
|
330 lineEdit->setText(""); |
|
331 lineEdit->setFocus(); |
|
332 lineEdit->setInputMethodHints( |
|
333 Qt::ImhNoPredictiveText | Qt::ImhNoAutoUppercase); |
|
334 } |
|
335 |
|
336 /*! |
|
337 \param searchPanel Search panel to operate on. |
|
338 \return Line edit of the searchPanel on success, NULL otherwise. |
|
339 */ |
|
340 HbLineEdit *HsSearchView::searchPanelLineEdit( |
|
341 HbSearchPanel &searchPanel) const |
|
342 { |
|
343 HSMENUTEST_FUNC_ENTRY("HsSearchView::searchPanelLineEdit"); |
|
344 |
|
345 HbLineEdit *result(0); |
|
346 foreach(QGraphicsItem *obj, searchPanel.childItems()) { |
|
347 QGraphicsWidget *const widget = static_cast<QGraphicsWidget *>(obj); |
|
348 if (widget != NULL) { |
|
349 HbLineEdit *const lineEdit = qobject_cast<HbLineEdit *>(widget); |
|
350 if (lineEdit != NULL) { |
|
351 result = lineEdit; |
|
352 break; |
|
353 } |
|
354 } |
|
355 } |
|
356 HSMENUTEST_FUNC_EXIT("HsSearchView::searchPanelLineEdit"); |
|
357 |
|
358 return result; |
|
359 } |
|
360 |
|
361 /*! |
|
362 Sets the builder context to the one determined by \a mStateContext and |
|
363 \a HsItemViewContext. |
|
364 */ |
|
365 void HsSearchView::setOriginatingContext() |
|
366 { |
|
367 mBuilder.setStateContext(mStateContext); |
|
368 mBuilder.setOperationalContext(HsItemViewContext); |
|
369 } |
|
370 |
|
371 /*! |
|
372 \retval true when search view is actually responsible for view management, |
|
373 \a false otherwise. |
|
374 */ |
|
375 bool HsSearchView::isActive() const |
|
376 { |
|
377 return mSearchListView != NULL; |
|
378 } |
|
379 |
|
380 /*! |
|
381 Slot to close virtual keyboard. |
|
382 */ |
|
383 void HsSearchView::hideVkb() |
|
384 { |
|
385 sendEvent(QEvent::CloseSoftwareInputPanel); |
|
386 } |
|
387 |
|
388 /*! |
|
389 Makes Vkb open provided there is an editor visible in the graphics scene. |
|
390 */ |
|
391 void HsSearchView::openVkb() |
|
392 { |
|
393 sendEvent(QEvent::RequestSoftwareInputPanel); |
|
394 } |
|
395 |
|
396 /*! |
|
397 Sends QEvent. |
|
398 \param eventType Identifies event to be sent. |
|
399 */ |
|
400 void HsSearchView::sendEvent(const QEvent::Type eventType) |
|
401 { |
|
402 QInputContext *const ic = qApp->inputContext(); |
|
403 if (ic != NULL) { |
|
404 QScopedPointer<QEvent> event( |
|
405 new QEvent(eventType)); |
|
406 |
|
407 ic->filterEvent(event.data()); |
|
408 } |
|
409 |
|
410 } |
|
411 |