|
1 /* |
|
2 * Copyright (c) 2010 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: |
|
15 * |
|
16 */ |
|
17 |
|
18 #include <QGesture> |
|
19 #include <QPainter> |
|
20 #include <QTimer> |
|
21 #include <hbgridviewitem> |
|
22 #include <hbmainwindow> |
|
23 #include "hgcontainer.h" |
|
24 #include "hgmediawallrenderer.h" |
|
25 #include "hgquad.h" |
|
26 #include "hgvgquadrenderer.h" |
|
27 #include "hgvgimage.h" |
|
28 #include "hgwidgetitem.h" |
|
29 #include "trace.h" |
|
30 |
|
31 #include <hbgridviewitem> |
|
32 #include <hbgridview> |
|
33 #include <hbiconitem> |
|
34 #include <qabstractitemmodel> |
|
35 #include "hglongpressvisualizer.h" |
|
36 |
|
37 static const qreal KSpringKScrolling(50.0); |
|
38 static const qreal KSpringKScrollBar(10.0); |
|
39 static const qreal KSpringDampingScrolling(20.0); |
|
40 static const qreal KSpringDampingScrollBar(5.0); |
|
41 static const qreal KFramesToZeroVelocity(60.0); |
|
42 static const int KLongTapDuration(400); |
|
43 |
|
44 |
|
45 HgContainer::HgContainer(QGraphicsItem* parent) : |
|
46 HbWidget(parent), |
|
47 mQuadRenderer(0), |
|
48 mRenderer(0), |
|
49 mTapCount(0), |
|
50 mAnimateUsingScrollBar(false), |
|
51 mSelectionMode(HgWidget::NoSelection), |
|
52 mSelectionModel(0), |
|
53 mMarkImage(0), |
|
54 mSpringVelAtDragStart(0), |
|
55 mDragged(false), |
|
56 mFramesDragged(0), |
|
57 mHitItemView(NULL), |
|
58 mLongPressVisualizer(NULL), |
|
59 mLongPressTimer(NULL), |
|
60 mHitItemIndex(NULL) |
|
61 { |
|
62 FUNC_LOG; |
|
63 |
|
64 grabGesture(Qt::PanGesture); |
|
65 grabGesture(Qt::TapGesture); |
|
66 grabGesture(Qt::TapAndHoldGesture); |
|
67 } |
|
68 |
|
69 HgContainer::~HgContainer() |
|
70 { |
|
71 FUNC_LOG; |
|
72 |
|
73 for (QList<HgWidgetItem*>::iterator i = mItems.begin(); i != mItems.end(); ++i) { |
|
74 delete (*i); |
|
75 } |
|
76 mItems.clear(); |
|
77 delete mMarkImage; |
|
78 delete mRenderer; |
|
79 } |
|
80 |
|
81 void HgContainer::setItemCount(int itemCount) |
|
82 { |
|
83 FUNC_LOG; |
|
84 |
|
85 mItems.clear(); |
|
86 for (int i=0; i<itemCount; i++) { |
|
87 HgWidgetItem* item = new HgWidgetItem(mQuadRenderer); |
|
88 mItems.append(item); |
|
89 } |
|
90 } |
|
91 |
|
92 int HgContainer::itemCount() const |
|
93 { |
|
94 return mItems.count(); |
|
95 } |
|
96 |
|
97 int HgContainer::rowCount() const |
|
98 { |
|
99 return mRenderer ? mRenderer->getRowCount() : 0; |
|
100 } |
|
101 |
|
102 QList<HgWidgetItem*> HgContainer::items() const |
|
103 { |
|
104 return mItems; |
|
105 } |
|
106 |
|
107 HgWidgetItem* HgContainer::itemByIndex(const QModelIndex& index) const |
|
108 { |
|
109 foreach (HgWidgetItem* item, mItems) { |
|
110 if (item->modelIndex() == index) |
|
111 return item; |
|
112 } |
|
113 |
|
114 return 0; |
|
115 } |
|
116 |
|
117 HgWidgetItem* HgContainer::itemByIndex(const int& index) const |
|
118 { |
|
119 if (mItems.count() > index && index >= 0) |
|
120 return mItems.at(index); |
|
121 else |
|
122 return 0; |
|
123 } |
|
124 |
|
125 /*! |
|
126 Changes the selection model of the container. |
|
127 Ownership is not transferred. |
|
128 Widget is redrawn to make new selection visible. |
|
129 */ |
|
130 void HgContainer::setSelectionModel(QItemSelectionModel *selectionModel) |
|
131 { |
|
132 FUNC_LOG; |
|
133 HANDLE_ERROR_NULL(selectionModel); // Parameter is always a valid QItemSelectionModel |
|
134 |
|
135 if (mSelectionModel != selectionModel) { |
|
136 if (mSelectionModel) { // mSelectionModel is 0 when called first time |
|
137 mSelectionModel->disconnect(SIGNAL(currentChanged(QModelIndex,QModelIndex)), this); |
|
138 |
|
139 if (mSelectionModel->currentIndex().isValid() && |
|
140 !(selectionModel->currentIndex().isValid())) { |
|
141 selectionModel->setCurrentIndex(mSelectionModel->currentIndex(), |
|
142 QItemSelectionModel::Current); |
|
143 } |
|
144 } |
|
145 mSelectionModel = selectionModel; |
|
146 connect(mSelectionModel, SIGNAL(currentChanged(QModelIndex,QModelIndex)), |
|
147 SLOT(updateByCurrentIndex(QModelIndex))); |
|
148 update(); |
|
149 } |
|
150 } |
|
151 |
|
152 /*! |
|
153 Returns the selection model of the container. |
|
154 Ownership is not transferred. |
|
155 */ |
|
156 QItemSelectionModel *HgContainer::selectionModel() const |
|
157 { |
|
158 FUNC_LOG; |
|
159 |
|
160 return mSelectionModel; |
|
161 } |
|
162 |
|
163 /*! |
|
164 Changes the selection mode of the container (no selection/multiselection). |
|
165 */ |
|
166 void HgContainer::setSelectionMode(HgWidget::SelectionMode mode, bool resetSelection) |
|
167 { |
|
168 FUNC_LOG; |
|
169 |
|
170 if (mSelectionMode != mode) { |
|
171 mSelectionMode = mode; |
|
172 |
|
173 if (mSelectionModel && resetSelection) { |
|
174 mSelectionModel->clearSelection(); |
|
175 update(); |
|
176 } |
|
177 } |
|
178 } |
|
179 |
|
180 /*! |
|
181 Returns the selection mode of the container (no selection/multiselection). |
|
182 */ |
|
183 HgWidget::SelectionMode HgContainer::selectionMode() const |
|
184 { |
|
185 FUNC_LOG; |
|
186 |
|
187 return mSelectionMode; |
|
188 } |
|
189 |
|
190 void HgContainer::dimensions(qreal &screenSize, qreal &worldSize) |
|
191 { |
|
192 const QRectF containerRect(rect()); |
|
193 |
|
194 // TODO, fix logic |
|
195 if (containerRect.height() > containerRect.width()) { |
|
196 // assume we are in portrait mode, ofcource this might not be the case |
|
197 screenSize = containerRect.height()/(mRenderer->getImageSize().height() + mRenderer->getSpacing().height()); |
|
198 worldSize = worldWidth(); |
|
199 } |
|
200 else{ |
|
201 screenSize = containerRect.width()/(mRenderer->getImageSize().width() + mRenderer->getSpacing().width()); |
|
202 worldSize = worldWidth(); |
|
203 } |
|
204 } |
|
205 |
|
206 Qt::Orientation HgContainer::orientation() const |
|
207 { |
|
208 FUNC_LOG; |
|
209 |
|
210 return mRenderer->getOrientation(); |
|
211 } |
|
212 |
|
213 void HgContainer::setOrientation(Qt::Orientation orientation, bool animate) |
|
214 { |
|
215 FUNC_LOG; |
|
216 |
|
217 mRenderer->setOrientation(orientation, animate); |
|
218 } |
|
219 |
|
220 void HgContainer::scrollToPosition(qreal value, bool animate) |
|
221 { |
|
222 FUNC_LOG; |
|
223 |
|
224 scrollToPosition(QPointF(value*worldWidth(), 0), animate); |
|
225 } |
|
226 |
|
227 void HgContainer::scrollToPosition(const QPointF& pos, bool animate) |
|
228 { |
|
229 FUNC_LOG; |
|
230 |
|
231 mAnimateUsingScrollBar = animate; |
|
232 initSpringForScrollBar(); |
|
233 |
|
234 if (animate) |
|
235 mSpring.animateToPos(pos); |
|
236 else |
|
237 mSpring.gotoPos(pos); |
|
238 } |
|
239 |
|
240 void HgContainer::scrollTo(const QModelIndex &index) |
|
241 { |
|
242 FUNC_LOG; |
|
243 |
|
244 if (index.isValid()) { |
|
245 scrollToPosition(QPointF(index.row(), index.column()), false); |
|
246 } |
|
247 } |
|
248 |
|
249 void HgContainer::itemDataChanged(const QModelIndex &firstIndex, const QModelIndex &lastIndex) |
|
250 { |
|
251 FUNC_LOG; |
|
252 |
|
253 // TODO, fix this |
|
254 int columns = firstIndex.model()->columnCount(QModelIndex()); |
|
255 |
|
256 // Check this!! |
|
257 int index = columns*firstIndex.row()+firstIndex.column(); |
|
258 int index2 = columns*lastIndex.row()+lastIndex.column(); |
|
259 |
|
260 // convert indeces to match one dimensional item array |
|
261 itemDataChanged( index, index2 ); |
|
262 } |
|
263 |
|
264 void HgContainer::addItems(int start, int end) |
|
265 { |
|
266 FUNC_LOG; |
|
267 |
|
268 int first = qBound(0, start, mItems.count()-1); |
|
269 int last = qBound(0, end, mItems.count()-1); |
|
270 for (int i = 0; i <= end-start; i++) { |
|
271 HgWidgetItem* item = new HgWidgetItem(mQuadRenderer); |
|
272 mItems.insert(start, item); |
|
273 } |
|
274 } |
|
275 |
|
276 void HgContainer::removeItems(int start, int end) |
|
277 { |
|
278 FUNC_LOG; |
|
279 |
|
280 int first = qBound(0, start, mItems.count()-1); |
|
281 int last = qBound(0, end, mItems.count()-1); |
|
282 for (int i = last; i >= first; i--) { |
|
283 delete mItems.at(i); |
|
284 mItems.removeAt(i); |
|
285 } |
|
286 } |
|
287 |
|
288 void HgContainer::moveItems(int start, int end, int destination) |
|
289 { |
|
290 FUNC_LOG; |
|
291 |
|
292 int first = qBound(0, start, mItems.count()-1); |
|
293 int last = qBound(0, end, mItems.count()-1); |
|
294 int target = qBound(0, destination, mItems.count()-1); |
|
295 |
|
296 if (target < first) { |
|
297 for (int i = 0; i <= last-first; i++) { |
|
298 mItems.move(first+i, target+i); |
|
299 } |
|
300 } |
|
301 else if (target > last) { |
|
302 for (int i = 0; i <= last-first; i++) { |
|
303 mItems.move(last-i, target); |
|
304 } |
|
305 } |
|
306 // else do nothing |
|
307 } |
|
308 |
|
309 int HgContainer::imageCount() const |
|
310 { |
|
311 return mItems.count(); |
|
312 } |
|
313 |
|
314 const HgImage *HgContainer::image(int index) const |
|
315 { |
|
316 return mItems[index]->image(); |
|
317 } |
|
318 |
|
319 int HgContainer::flags(int index) const |
|
320 { |
|
321 if (index >= 0 && index < itemCount()) { |
|
322 if (mSelectionModel && mSelectionModel->isSelected(mSelectionModel->model()->index(index, 0))) { |
|
323 return 1; // TODO: Assign flag to mark indicator |
|
324 } |
|
325 } |
|
326 return 0; |
|
327 } |
|
328 |
|
329 const HgImage *HgContainer::indicator(int flags) const |
|
330 { |
|
331 if (flags & 1) { |
|
332 return mMarkImage; |
|
333 } |
|
334 |
|
335 return 0; |
|
336 } |
|
337 |
|
338 void HgContainer::updateBySpringPosition() |
|
339 { |
|
340 update(); |
|
341 |
|
342 // spring works always in one dimension, that is, x coord. |
|
343 qreal pos = mSpring.pos().x(); |
|
344 |
|
345 onScrollPositionChanged(pos); |
|
346 |
|
347 emit scrollPositionChanged(pos, mAnimateUsingScrollBar); |
|
348 } |
|
349 |
|
350 void HgContainer::redraw() |
|
351 { |
|
352 update(); |
|
353 } |
|
354 |
|
355 void HgContainer::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) |
|
356 { |
|
357 Q_UNUSED(option) |
|
358 Q_UNUSED(widget) |
|
359 |
|
360 if (mSpring.updatePositionIfNeeded()) |
|
361 { |
|
362 // spring works always in one dimension, that is, x coord. |
|
363 qreal pos = mSpring.pos().x(); |
|
364 onScrollPositionChanged(pos); |
|
365 emit scrollPositionChanged(pos, mAnimateUsingScrollBar); |
|
366 } |
|
367 |
|
368 QRectF vp = painter->viewport(); |
|
369 QRectF rts = mapRectToScene(rect()); |
|
370 QRectF r; |
|
371 |
|
372 // transform rectangle to vg space & |
|
373 // rotate rendering according to orientation |
|
374 if (mainWindow()->orientation() == Qt::Horizontal) { |
|
375 r = QRectF(vp.width()-(rts.height()+rts.top()), rts.left(), rts.height(), rts.width()); |
|
376 mRenderer->setCameraRotationZ(-90); |
|
377 } |
|
378 else { |
|
379 r = QRectF(rts.left(), vp.height()-(rts.height()+rts.top()), rts.width(), rts.height()); |
|
380 mRenderer->setCameraRotationZ(0); |
|
381 } |
|
382 |
|
383 // interpolate spring velocity towards zero, this is done |
|
384 // so that spring velocity for rendering doesn't drop directly to |
|
385 // zero when dragging starts |
|
386 qreal springVel = mSpring.velocity().x(); |
|
387 if (mDragged) { |
|
388 qreal t = qBound(mFramesDragged / KFramesToZeroVelocity, 0.0f, 1.0f); |
|
389 springVel = mSpringVelAtDragStart * (1.0f - t); |
|
390 mFramesDragged++; |
|
391 } |
|
392 |
|
393 // setup rendering and draw the current view |
|
394 mRenderer->setCameraDistance(getCameraDistance(springVel)); |
|
395 mRenderer->setCameraRotationY(getCameraRotationY(springVel)); |
|
396 mRenderer->setRect(r); |
|
397 mRenderer->draw(mSpring.startPos(), mSpring.pos(), mSpring.endPos(), |
|
398 springVel, painter); |
|
399 |
|
400 } |
|
401 |
|
402 void HgContainer::resizeEvent(QGraphicsSceneResizeEvent *event) |
|
403 { |
|
404 FUNC_LOG; |
|
405 |
|
406 HbWidget::resizeEvent(event); |
|
407 } |
|
408 |
|
409 // this needs to be implemented for gesture framework to work |
|
410 void HgContainer::mousePressEvent(QGraphicsSceneMouseEvent *event) |
|
411 { |
|
412 Q_UNUSED(event); |
|
413 } |
|
414 |
|
415 void HgContainer::gestureEvent(QGestureEvent *event) |
|
416 { |
|
417 FUNC_LOG; |
|
418 |
|
419 // Event may contain more than one gesture type |
|
420 if (QGesture *gesture = event->gesture(Qt::TapAndHoldGesture)) { |
|
421 QTapAndHoldGesture *tapAndHold = static_cast<QTapAndHoldGesture *>(gesture); |
|
422 handleLongTap(tapAndHold->state(), |
|
423 mapFromScene(event->mapToGraphicsScene(tapAndHold->position()))); |
|
424 } |
|
425 else if (QGesture *gesture = event->gesture(Qt::TapGesture)) { |
|
426 // Tap and hold is not working yet in HW, tap gesture is delivered instead |
|
427 QTapGesture *tap = static_cast<QTapGesture *>(gesture); |
|
428 handleTap(tap->state(), |
|
429 mapFromScene(event->mapToGraphicsScene(tap->position()))); |
|
430 } |
|
431 else if (QGesture *pan = event->gesture(Qt::PanGesture)) { |
|
432 handlePanning(static_cast<QPanGesture*>(pan)); |
|
433 } |
|
434 } |
|
435 |
|
436 void HgContainer::init(Qt::Orientation scrollDirection) |
|
437 { |
|
438 FUNC_LOG; |
|
439 |
|
440 mRenderer = createRenderer(); |
|
441 if (mRenderer->coverflowModeEnabled()) |
|
442 mRenderer->setOrientation(Qt::Horizontal, false); |
|
443 else |
|
444 mRenderer->setOrientation(scrollDirection, false); |
|
445 |
|
446 mQuadRenderer = mRenderer->getRenderer(); |
|
447 |
|
448 QImage markImage(":/images/mark.svg"); |
|
449 if (markImage.isNull()) { |
|
450 ERROR("Failed to load :/images/mark.svg"); |
|
451 } |
|
452 mMarkImage = mQuadRenderer->createNativeImage(); |
|
453 HANDLE_ERROR_NULL(mMarkImage); |
|
454 if (mMarkImage) { |
|
455 mMarkImage->setImage(markImage); |
|
456 } |
|
457 |
|
458 connect(&mSpring, SIGNAL(updated()), SLOT(updateBySpringPosition())); |
|
459 connect(&mSpring, SIGNAL(ended()), SLOT(onScrollingEnded())); |
|
460 connect(&mSpring, SIGNAL(ended()), SIGNAL(scrollingEnded())); |
|
461 connect(&mSpring, SIGNAL(started()), SIGNAL(scrollingStarted())); |
|
462 connect(mRenderer, SIGNAL(renderingNeeded()), SLOT(redraw())); |
|
463 |
|
464 } |
|
465 |
|
466 qreal HgContainer::worldWidth() const |
|
467 { |
|
468 return (qreal)mRenderer->getWorldWidth(); |
|
469 } |
|
470 |
|
471 void HgContainer::initSpringForScrollBar() |
|
472 { |
|
473 FUNC_LOG; |
|
474 |
|
475 mSpring.setDamping(KSpringDampingScrollBar); |
|
476 mSpring.setK(KSpringKScrollBar); |
|
477 } |
|
478 |
|
479 void HgContainer::initSpringForScrolling() |
|
480 { |
|
481 FUNC_LOG; |
|
482 |
|
483 mSpring.setDamping(KSpringDampingScrolling); |
|
484 mSpring.setK(KSpringKScrolling); |
|
485 } |
|
486 |
|
487 void HgContainer::boundSpring() |
|
488 { |
|
489 FUNC_LOG; |
|
490 |
|
491 qreal x = mSpring.endPos().x(); |
|
492 x = qBound(qreal(0), x, worldWidth()); |
|
493 if (mRenderer->coverflowModeEnabled()) { |
|
494 qreal i = floorf(x); |
|
495 x = (x - i > 0.5f) ? ceilf(x) : i; |
|
496 mSpring.animateToPos(QPointF(x, 0)); |
|
497 } |
|
498 |
|
499 mSpring.animateToPos(QPointF(x, 0)); |
|
500 |
|
501 } |
|
502 |
|
503 void HgContainer::handlePanning(QPanGesture *gesture) |
|
504 { |
|
505 mAnimateUsingScrollBar = false; |
|
506 initSpringForScrolling(); |
|
507 |
|
508 qreal pos = mSpring.pos().x(); |
|
509 qreal delta(0); |
|
510 qreal itemSide(0); |
|
511 |
|
512 if (mRenderer->getOrientation() == mainWindow()->orientation()) { |
|
513 delta = gesture->delta().y(); |
|
514 } |
|
515 else { |
|
516 delta = gesture->delta().x(); |
|
517 } |
|
518 |
|
519 if (mRenderer->getOrientation() == Qt::Vertical) |
|
520 itemSide = mRenderer->getImageSize().height()+mRenderer->getSpacing().height(); |
|
521 else |
|
522 itemSide = mRenderer->getImageSize().width()+mRenderer->getSpacing().width(); |
|
523 |
|
524 if (gesture->state() == Qt::GestureStarted) { |
|
525 mOffsetAtDragStart = gesture->offset(); |
|
526 } |
|
527 else if (gesture->state() == Qt::GestureUpdated) { |
|
528 QPointF offset = gesture->offset(); |
|
529 QPointF offsetDelta = offset - mOffsetAtDragStart; |
|
530 if (!mDragged && (qAbs(offsetDelta.x()) > 8 || |
|
531 qAbs(offsetDelta.y()) > 8)) { |
|
532 mDragged = true; |
|
533 mDrag.reset(delta, mSpring.pos().x()); |
|
534 mDragged = true; |
|
535 mSpringVelAtDragStart = mSpring.velocity().x(); |
|
536 mFramesDragged = 0; |
|
537 } |
|
538 |
|
539 if (mDragged) |
|
540 { |
|
541 emit scrollingStarted(); |
|
542 |
|
543 qreal newPosition = mDrag.update(delta, pos, itemSide); |
|
544 if (qAbs(newPosition - mSpring.pos().x()) > 0.01f) |
|
545 { |
|
546 mSpring.gotoPos(QPointF(newPosition, 0)); |
|
547 update(); |
|
548 } |
|
549 } |
|
550 } |
|
551 else if (mDragged && gesture->state() == Qt::GestureFinished) { |
|
552 mDrag.update(delta, pos, itemSide); |
|
553 mDragged = false; |
|
554 qreal newPos(0); |
|
555 if (mDrag.finish(pos, mRenderer->coverflowModeEnabled(), newPos)) { |
|
556 mSpring.animateToPos(QPointF(qBound(qreal(0), newPos, worldWidth()), 0)); |
|
557 HgWidgetItem* item = itemByIndex(newPos); |
|
558 if (item && item->modelIndex() != mSelectionModel->currentIndex()) { |
|
559 // mSelectionModel->setCurrentIndex(item->modelIndex(), QItemSelectionModel::Current); |
|
560 } |
|
561 } |
|
562 else { |
|
563 boundSpring(); |
|
564 } |
|
565 } |
|
566 else if (gesture->state() == Qt::GestureCanceled) { |
|
567 boundSpring(); |
|
568 } |
|
569 } |
|
570 |
|
571 |
|
572 |
|
573 void HgContainer::handleTap(Qt::GestureState state, const QPointF &pos) |
|
574 { |
|
575 FUNC_LOG; |
|
576 |
|
577 if (state == Qt::GestureStarted) { |
|
578 mTapDuration.start(); |
|
579 |
|
580 startLongPressWatcher(pos); |
|
581 } |
|
582 else if (state == Qt::GestureCanceled) |
|
583 { |
|
584 stopLongPressWatcher(); |
|
585 } |
|
586 else if (state == Qt::GestureFinished) { |
|
587 |
|
588 stopLongPressWatcher(); |
|
589 handleItemAction(pos, mTapDuration.elapsed() > KLongTapDuration ? LongTap : NormalTap); |
|
590 } |
|
591 } |
|
592 |
|
593 void HgContainer::handleLongTap(Qt::GestureState state, const QPointF &pos) |
|
594 { |
|
595 FUNC_LOG; |
|
596 |
|
597 mAnimateUsingScrollBar = false; |
|
598 initSpringForScrolling(); |
|
599 |
|
600 if (state == Qt::GestureFinished) { |
|
601 handleItemAction(pos, LongTap); |
|
602 } |
|
603 } |
|
604 |
|
605 /*! |
|
606 Handle tap, lang tap and double tap action. |
|
607 Finds out the item in the tap position and sends out suitable signal, |
|
608 Sets the item as the current item and in multiselection mode toggles the |
|
609 item selection status. |
|
610 */ |
|
611 void HgContainer::handleItemAction(const QPointF &pos, ItemActionType action) |
|
612 { |
|
613 FUNC_LOG; |
|
614 |
|
615 // If there is content, mSelectionModel must always exist - either default or client-provided |
|
616 if (!mSelectionModel) return; |
|
617 |
|
618 mHitItem = getItemAt(pos, mHitItemIndex); |
|
619 if (mHitItem) |
|
620 { |
|
621 int index = mHitItemIndex; |
|
622 |
|
623 |
|
624 HgWidgetItem* item = itemByIndex(index); |
|
625 if (item && action != DoubleTap) { |
|
626 mSelectionModel->setCurrentIndex(item->modelIndex(), QItemSelectionModel::Current); |
|
627 |
|
628 if (action == LongTap) { |
|
629 INFO("Long tap:" << item->modelIndex().row()); |
|
630 |
|
631 if (!mRenderer->coverflowModeEnabled()) |
|
632 selectItem(); |
|
633 |
|
634 emit longPressed(item->modelIndex(), pos); |
|
635 } |
|
636 else if (mSelectionMode == HgWidget::MultiSelection) { |
|
637 INFO("Select:" << item->modelIndex().row()); |
|
638 mSelectionModel->select(item->modelIndex(), QItemSelectionModel::Toggle); |
|
639 update(); // It would be enough to update the item |
|
640 } |
|
641 else if (mSelectionMode == HgWidget::SingleSelection) { |
|
642 INFO("Select:" << item->modelIndex().row()); |
|
643 mSelectionModel->select(item->modelIndex(), QItemSelectionModel::ClearAndSelect); |
|
644 update(); // It would be enough to update the item |
|
645 } |
|
646 else if (mSelectionMode == HgWidget::ContiguousSelection) { |
|
647 QModelIndex newSelected = item->modelIndex(); |
|
648 QModelIndexList oldSelection = mSelectionModel->selectedIndexes(); |
|
649 INFO("Select:" << newSelected.row()); |
|
650 if (oldSelection.count() > 0 && !mSelectionModel->isSelected(newSelected)) { |
|
651 if (newSelected.row() < oldSelection.front().row()) { |
|
652 mSelectionModel->select(QItemSelection(newSelected, oldSelection.back()), |
|
653 QItemSelectionModel::Select); |
|
654 } |
|
655 else { // newSelected.row() > oldSelection.back().row() |
|
656 mSelectionModel->select(QItemSelection(oldSelection.front(), newSelected), |
|
657 QItemSelectionModel::Select); |
|
658 } |
|
659 } |
|
660 else { |
|
661 mSelectionModel->select(newSelected, QItemSelectionModel::Select); |
|
662 } |
|
663 update(); // It would be enough to update the item |
|
664 } |
|
665 else { |
|
666 INFO("Tap:" << item->modelIndex().row()); |
|
667 |
|
668 if (mRenderer->coverflowModeEnabled()) |
|
669 { |
|
670 if (qAbs(qreal(index) - mSpring.pos().x()) < 0.01f) |
|
671 { |
|
672 emit activated(item->modelIndex()); |
|
673 } |
|
674 else |
|
675 { |
|
676 mSpring.animateToPos(QPointF(index, 0)); |
|
677 } |
|
678 } |
|
679 else |
|
680 { |
|
681 selectItem(); |
|
682 emit activated(item->modelIndex()); |
|
683 } |
|
684 |
|
685 } |
|
686 } |
|
687 } |
|
688 else { |
|
689 INFO("No quad at pos:" << pos); |
|
690 |
|
691 unselectItem(); |
|
692 } |
|
693 } |
|
694 |
|
695 bool HgContainer::getItemPoints(int index, QPolygonF& points) |
|
696 { |
|
697 QPolygonF poly; |
|
698 if (!mRenderer->getItemPoints(index, poly)) |
|
699 return false; |
|
700 |
|
701 bool invertible; |
|
702 QTransform t = qtToVgTransform().inverted(&invertible); |
|
703 |
|
704 points = t.map(poly); |
|
705 return true; |
|
706 } |
|
707 |
|
708 QList<QModelIndex> HgContainer::getVisibleItemIndices() const |
|
709 { |
|
710 QList<HgQuad*> quads = mRenderer->getVisibleQuads(); |
|
711 QList<QModelIndex> result; |
|
712 for (int i = 0; i < quads.count(); i++) { |
|
713 bool ok; |
|
714 int index = quads.at(i)->userData().toInt(&ok); |
|
715 result.append(itemByIndex(index)->modelIndex()); |
|
716 } |
|
717 qSort(result); |
|
718 return result; |
|
719 } |
|
720 |
|
721 void HgContainer::itemDataChanged(const int &firstIndex, const int &lastIndex) |
|
722 { |
|
723 FUNC_LOG; |
|
724 |
|
725 // TODO FIX THIS FUNCTION!!!!!!!!!!!!!!!!!!!!!! |
|
726 |
|
727 int firstItemOnScreen = 0, lastItemOnScreen = 0; |
|
728 firstItemOnScreen = mSpring.pos().x(); |
|
729 firstItemOnScreen *= rowCount(); |
|
730 |
|
731 // This code doesnt take into count if there is some empty space at the |
|
732 // beginning or at the end of the widget |
|
733 |
|
734 int itemsOnScreen = 0; |
|
735 if (mRenderer->getOrientation() == Qt::Vertical) { |
|
736 itemsOnScreen = this->rect().height()/mRenderer->getImageSize().height(); |
|
737 itemsOnScreen += rowCount(); |
|
738 } |
|
739 else { |
|
740 // Doesnt work here. Use some magic for now. |
|
741 itemsOnScreen = this->rect().width()/mRenderer->getImageSize().width(); |
|
742 itemsOnScreen += 4; |
|
743 } |
|
744 itemsOnScreen *= rowCount(); |
|
745 lastItemOnScreen = firstItemOnScreen+itemsOnScreen; |
|
746 |
|
747 if ((firstIndex >= firstItemOnScreen && firstIndex <= lastItemOnScreen) || |
|
748 (lastIndex >= firstItemOnScreen && lastIndex <= lastItemOnScreen)) { |
|
749 update( this->rect() ); |
|
750 } |
|
751 } |
|
752 |
|
753 void HgContainer::selectItem() |
|
754 { |
|
755 // TODO: replace this with own selection implementation |
|
756 |
|
757 if (mHitItemView) |
|
758 { |
|
759 delete mHitItemView; |
|
760 mHitItemView = NULL; |
|
761 } |
|
762 |
|
763 mHitItemView = new HbGridViewItem(this); |
|
764 mHitItemView->setVisible(false); |
|
765 |
|
766 QModelIndex modelIndex = mItems[mHitItemIndex]->modelIndex(); |
|
767 const QAbstractItemModel* model = modelIndex.model(); |
|
768 mHitItemView->resize(mRenderer->getImageSize().width() + 10, |
|
769 mRenderer->getImageSize().height() + 10); |
|
770 |
|
771 QVariant iconVariant = model->data(modelIndex, Qt::DecorationRole); |
|
772 mHitItemPixmap = iconVariant.value<QPixmap>(); |
|
773 HbIcon icon(mHitItemPixmap); |
|
774 |
|
775 QGraphicsItem* item = mHitItemView->style()->createPrimitive(HbStyle::P_GridViewItem_icon, mHitItemView); |
|
776 HbIconItem *iconItem = static_cast<HbIconItem*>(item); |
|
777 iconItem->setIcon(icon); |
|
778 iconItem->setAlignment(Qt::AlignCenter); |
|
779 iconItem->setAspectRatioMode(Qt::KeepAspectRatio); |
|
780 |
|
781 mHitItemView->setModelIndex(modelIndex); |
|
782 mHitItemView->setPos(QPointF(-10,-10)); |
|
783 mHitItemView->setPressed(true, false); |
|
784 mHitItemView->updatePrimitives(); |
|
785 |
|
786 } |
|
787 |
|
788 void HgContainer::updateSelectedItem() |
|
789 { |
|
790 if (!mHitItemView || mHitItemIndex == -1) |
|
791 return; |
|
792 |
|
793 QPolygonF points; |
|
794 if (!getItemPoints(mHitItemIndex, points)) |
|
795 { |
|
796 // the item was not rendered, we must hide |
|
797 // our qt item |
|
798 mHitItemView->setVisible(false); |
|
799 } |
|
800 |
|
801 if (mHitItemPixmap.isNull()) |
|
802 { |
|
803 mHitItemView->setVisible(false); |
|
804 return; |
|
805 } |
|
806 |
|
807 QPolygonF img; |
|
808 img.append(QPointF(0,mHitItemPixmap.height())); |
|
809 img.append(QPointF(mHitItemPixmap.width(),mHitItemPixmap.height())); |
|
810 img.append(QPointF(mHitItemPixmap.width(),0)); |
|
811 img.append(QPointF(0,0)); |
|
812 |
|
813 QTransform t; |
|
814 QTransform::quadToQuad(img, points, t); |
|
815 |
|
816 mHitItemView->setTransform(t); |
|
817 mHitItemView->setVisible(true); |
|
818 } |
|
819 |
|
820 void HgContainer::unselectItem() |
|
821 { |
|
822 mHitItemIndex = -1; |
|
823 if (mHitItemView) |
|
824 { |
|
825 mHitItemView->setPressed(false, false); |
|
826 mHitItemView->setVisible(false); |
|
827 } |
|
828 } |
|
829 |
|
830 void HgContainer::updateLongPressVisualizer() |
|
831 { |
|
832 int elapsed = mLongTapDuration.elapsed(); |
|
833 |
|
834 if (elapsed > 80) |
|
835 { |
|
836 int frame = 100.0f * qreal(elapsed - 80) / qreal(KLongTapDuration - 80); |
|
837 mLongPressVisualizer->setFrame(frame); |
|
838 } |
|
839 } |
|
840 |
|
841 void HgContainer::updateByCurrentIndex(const QModelIndex ¤t) |
|
842 { |
|
843 handleCurrentChanged(current); |
|
844 } |
|
845 |
|
846 HgWidgetItem* HgContainer::getItemAt(const QPointF& pos, int& index) |
|
847 { |
|
848 QPointF p = mapQtToVg(pos); |
|
849 HgQuad* quad = mRenderer->getQuadAt(p); |
|
850 if (quad) |
|
851 { |
|
852 bool ok; |
|
853 index = quad->userData().toInt(&ok); |
|
854 |
|
855 HgWidgetItem* item = itemByIndex(index); |
|
856 return item; |
|
857 } |
|
858 return NULL; |
|
859 } |
|
860 |
|
861 void HgContainer::startLongPressWatcher(const QPointF& pos) |
|
862 { |
|
863 if (!mLongPressVisualizer) |
|
864 { |
|
865 mLongPressVisualizer = new HgLongPressVisualizer(this); |
|
866 mLongPressVisualizer->setZValue(zValue()+1); |
|
867 } |
|
868 |
|
869 mLongPressVisualizer->start(pos); |
|
870 |
|
871 if (!mLongPressTimer) |
|
872 { |
|
873 mLongPressTimer = new QTimer(this); |
|
874 QObject::connect(mLongPressTimer, SIGNAL(timeout()), this, SLOT(updateLongPressVisualizer())); |
|
875 } |
|
876 |
|
877 mLongPressTimer->start(20); |
|
878 |
|
879 mLongTapDuration.start(); |
|
880 } |
|
881 |
|
882 void HgContainer::stopLongPressWatcher() |
|
883 { |
|
884 if (mLongPressTimer && mLongPressVisualizer) |
|
885 { |
|
886 mLongPressTimer->stop(); |
|
887 mLongPressVisualizer->stop(); |
|
888 } |
|
889 } |
|
890 |
|
891 QTransform HgContainer::qtToVgTransform() const |
|
892 { |
|
893 QTransform t; |
|
894 if (mainWindow()->orientation() == Qt::Vertical) |
|
895 { |
|
896 t.translate(0, rect().height()); |
|
897 t.scale(1, -1); |
|
898 } |
|
899 else // horizontal |
|
900 { |
|
901 t.translate(rect().height(), 0); |
|
902 t.scale(-1, 1); |
|
903 t.translate(0, rect().width()); |
|
904 t.rotate(-90, Qt::ZAxis); |
|
905 } |
|
906 return t; |
|
907 } |
|
908 |
|
909 QPointF HgContainer::mapQtToVg(const QPointF& p) const |
|
910 { |
|
911 QTransform t = qtToVgTransform(); |
|
912 return t.map(p); |
|
913 } |
|
914 |
|
915 qreal HgContainer::getCameraDistance(qreal springVelocity) |
|
916 { |
|
917 Q_UNUSED(springVelocity) |
|
918 return 0; |
|
919 } |
|
920 |
|
921 qreal HgContainer::getCameraRotationY(qreal springVelocity) |
|
922 { |
|
923 Q_UNUSED(springVelocity) |
|
924 return 0; |
|
925 } |
|
926 |
|
927 void HgContainer::handleTapAction(const QPointF& pos, HgWidgetItem* hitItem, int hitItemIndex) |
|
928 { |
|
929 Q_UNUSED(pos) |
|
930 Q_UNUSED(hitItem) |
|
931 Q_UNUSED(hitItemIndex) |
|
932 } |
|
933 |
|
934 void HgContainer::handleLongTapAction(const QPointF& pos, HgWidgetItem* hitItem, int hitItemIndex) |
|
935 { |
|
936 Q_UNUSED(pos) |
|
937 Q_UNUSED(hitItem) |
|
938 Q_UNUSED(hitItemIndex) |
|
939 } |
|
940 |
|
941 void HgContainer::onScrollPositionChanged(qreal pos) |
|
942 { |
|
943 Q_UNUSED(pos) |
|
944 } |
|
945 |
|
946 void HgContainer::handleCurrentChanged(const QModelIndex ¤t) |
|
947 { |
|
948 Q_UNUSED(current) |
|
949 // By default do nothing |
|
950 } |
|
951 |
|
952 void HgContainer::onScrollingEnded() |
|
953 { |
|
954 /* int index; |
|
955 HgWidgetItem* item = getItemAt(rect().center(), index); |
|
956 if (item && item->modelIndex() != mSelectionModel->currentIndex()) { |
|
957 mSelectionModel->setCurrentIndex(item->modelIndex(), QItemSelectionModel::Current); |
|
958 }*/ |
|
959 } |
|
960 |