69b2dd4e215370d0e1f242397ef5f196f6180624
[profile/ivi/qtdeclarative.git] / src / plugins / qmltooling / qmldbg_qtquick2 / inspecttool.cpp
1 /****************************************************************************
2 **
3 ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies).
4 ** Contact: http://www.qt-project.org/legal
5 **
6 ** This file is part of the QtQml module of the Qt Toolkit.
7 **
8 ** $QT_BEGIN_LICENSE:LGPL$
9 ** Commercial License Usage
10 ** Licensees holding valid commercial Qt licenses may use this file in
11 ** accordance with the commercial license agreement provided with the
12 ** Software or, alternatively, in accordance with the terms contained in
13 ** a written agreement between you and Digia.  For licensing terms and
14 ** conditions see http://qt.digia.com/licensing.  For further information
15 ** use the contact form at http://qt.digia.com/contact-us.
16 **
17 ** GNU Lesser General Public License Usage
18 ** Alternatively, this file may be used under the terms of the GNU Lesser
19 ** General Public License version 2.1 as published by the Free Software
20 ** Foundation and appearing in the file LICENSE.LGPL included in the
21 ** packaging of this file.  Please review the following information to
22 ** ensure the GNU Lesser General Public License version 2.1 requirements
23 ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
24 **
25 ** In addition, as a special exception, Digia gives you certain additional
26 ** rights.  These rights are described in the Digia Qt LGPL Exception
27 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
28 **
29 ** GNU General Public License Usage
30 ** Alternatively, this file may be used under the terms of the GNU
31 ** General Public License version 3.0 as published by the Free Software
32 ** Foundation and appearing in the file LICENSE.GPL included in the
33 ** packaging of this file.  Please review the following information to
34 ** ensure the GNU General Public License version 3.0 requirements will be
35 ** met: http://www.gnu.org/copyleft/gpl.html.
36 **
37 **
38 ** $QT_END_LICENSE$
39 **
40 ****************************************************************************/
41
42 #include "inspecttool.h"
43
44 #include "highlight.h"
45 #include "qquickviewinspector.h"
46
47 #include <QtCore/QLineF>
48
49 #include <QtGui/QMouseEvent>
50 #include <QtGui/QWheelEvent>
51 #include <QtGui/QTouchEvent>
52 #include <QtGui/QKeyEvent>
53 #include <QtGui/QGuiApplication>
54 #include <QtGui/QStyleHints>
55
56 #include <QtQuick/QQuickView>
57 #include <QtQuick/QQuickItem>
58
59 namespace QmlJSDebugger {
60 namespace QtQuick2 {
61
62 InspectTool::InspectTool(QQuickViewInspector *inspector, QQuickView *view) :
63     AbstractTool(inspector),
64     m_originalSmooth(view->rootItem()->smooth()),
65     m_dragStarted(false),
66     m_pinchStarted(false),
67     m_didPressAndHold(false),
68     m_tapEvent(false),
69     m_rootItem(view->rootItem()),
70     m_originalPosition(view->rootItem()->pos()),
71     m_smoothScaleFactor(Constants::ZoomSnapDelta),
72     m_minScale(0.125f),
73     m_maxScale(48.0f),
74     m_originalScale(view->rootItem()->scale()),
75     m_touchTimestamp(0),
76     m_hoverHighlight(new HoverHighlight(inspector->overlay())),
77     m_lastItem(0),
78     m_lastClickedItem(0)
79 {
80     //Press and Hold Timer
81     m_pressAndHoldTimer.setSingleShot(true);
82     m_pressAndHoldTimer.setInterval(Constants::PressAndHoldTimeout);
83     connect(&m_pressAndHoldTimer, SIGNAL(timeout()), SLOT(zoomTo100()));
84     //Timer to display selected item's name
85     m_nameDisplayTimer.setSingleShot(true);
86     m_nameDisplayTimer.setInterval(qApp->styleHints()->mouseDoubleClickInterval());
87     connect(&m_nameDisplayTimer, SIGNAL(timeout()), SLOT(showSelectedItemName()));
88     enable(true);
89 }
90
91 InspectTool::~InspectTool()
92 {
93     enable(false);
94 }
95
96 void InspectTool::enable(bool enable)
97 {
98     if (!enable) {
99         inspector()->setSelectedItems(QList<QQuickItem*>());
100         // restoring the original states.
101         if (m_rootItem) {
102             m_rootItem->setScale(m_originalScale);
103             m_rootItem->setPos(m_originalPosition);
104             m_rootItem->setSmooth(m_originalSmooth);
105         }
106     } else {
107         if (m_rootItem) {
108             m_originalSmooth = m_rootItem->smooth();
109             m_originalScale = m_rootItem->scale();
110             m_originalPosition = m_rootItem->pos();
111             m_rootItem->setSmooth(true);
112         }
113     }
114 }
115
116 void InspectTool::leaveEvent(QEvent *)
117 {
118     m_hoverHighlight->setVisible(false);
119 }
120
121 void InspectTool::mousePressEvent(QMouseEvent *event)
122 {
123     m_mousePosition = event->localPos();
124     if (event->button() == Qt::LeftButton) {
125         m_pressAndHoldTimer.start();
126         initializeDrag(event->localPos());
127     }
128 }
129
130 void InspectTool::mouseReleaseEvent(QMouseEvent *event)
131 {
132     m_mousePosition = event->localPos();
133     m_pressAndHoldTimer.stop();
134     if (event->button() == Qt::LeftButton && !m_dragStarted) {
135         selectItem();
136         m_hoverHighlight->setVisible(false);
137     }
138 }
139
140 void InspectTool::mouseDoubleClickEvent(QMouseEvent *event)
141 {
142     m_mousePosition = event->localPos();
143     m_pressAndHoldTimer.stop();
144     if (event->button() == Qt::LeftButton) {
145         selectNextItem();
146         m_hoverHighlight->setVisible(false);
147     }
148 }
149
150 void InspectTool::mouseMoveEvent(QMouseEvent *event)
151 {
152     m_mousePosition = event->localPos();
153     moveItem(event->buttons() & Qt::LeftButton);
154 }
155
156 void InspectTool::hoverMoveEvent(QMouseEvent *event)
157 {
158     m_mousePosition = event->localPos();
159     m_pressAndHoldTimer.stop();
160     QQuickItem *item = inspector()->topVisibleItemAt(event->pos());
161     if (!item || item == m_lastClickedItem) {
162         m_hoverHighlight->setVisible(false);
163     } else {
164         m_hoverHighlight->setItem(item);
165         m_hoverHighlight->setVisible(true);
166     }
167 }
168
169 void InspectTool::wheelEvent(QWheelEvent *event)
170 {
171     if (event->orientation() != Qt::Vertical)
172         return;
173
174     Qt::KeyboardModifier smoothZoomModifier = Qt::ControlModifier;
175     if (event->modifiers() & smoothZoomModifier) {
176         int numDegrees = event->delta() / 8;
177         qreal newScale = m_rootItem->scale() + m_smoothScaleFactor * (numDegrees / 15.0f);
178         scaleView(newScale / m_rootItem->scale(), m_mousePosition, m_mousePosition);
179     } else if (!event->modifiers()) {
180         if (event->delta() > 0) {
181             zoomIn();
182         } else if (event->delta() < 0) {
183             zoomOut();
184         }
185     }
186 }
187
188 void InspectTool::keyReleaseEvent(QKeyEvent *event)
189 {
190     switch (event->key()) {
191     case Qt::Key_Plus:
192         zoomIn();
193         break;
194     case Qt::Key_Minus:
195         zoomOut();
196         break;
197     case Qt::Key_1:
198     case Qt::Key_2:
199     case Qt::Key_3:
200     case Qt::Key_4:
201     case Qt::Key_5:
202     case Qt::Key_6:
203     case Qt::Key_7:
204     case Qt::Key_8:
205     case Qt::Key_9: {
206         qreal newScale = ((event->key() - Qt::Key_0) * 1.0f);
207         scaleView(newScale / m_rootItem->scale(), m_mousePosition, m_mousePosition);
208         break;
209     }
210     default:
211         break;
212     }
213 }
214
215 void InspectTool::touchEvent(QTouchEvent *event)
216 {
217     QList<QTouchEvent::TouchPoint> touchPoints = event->touchPoints();
218
219     switch (event->type()) {
220     case QEvent::TouchBegin:
221         if (touchPoints.count() == 1 && (event->touchPointStates() & Qt::TouchPointPressed)) {
222             if (!m_pressAndHoldTimer.isActive())
223                 m_pressAndHoldTimer.start();
224             m_mousePosition = touchPoints.first().pos();
225             initializeDrag(touchPoints.first().pos());
226             m_tapEvent = true;
227         } else {
228             m_tapEvent = false;
229         }
230         break;
231     case QEvent::TouchUpdate: {
232         if (touchPoints.count() > 1)
233             m_tapEvent = false;
234         if ((touchPoints.count() == 1)
235                 && (event->touchPointStates() & Qt::TouchPointMoved)) {
236             m_mousePosition = touchPoints.first().pos();
237             moveItem(true);
238         } else if ((touchPoints.count() == 2)
239                    && (!(event->touchPointStates() & Qt::TouchPointReleased))) {
240             // determine scale factor
241             const QTouchEvent::TouchPoint &touchPoint0 = touchPoints.first();
242             const QTouchEvent::TouchPoint &touchPoint1 = touchPoints.last();
243
244             qreal touchScaleFactor =
245                     QLineF(touchPoint0.pos(), touchPoint1.pos()).length()
246                     / QLineF(touchPoint0.lastPos(), touchPoint1.lastPos()).length();
247
248             QPointF oldcenter = (touchPoint0.lastPos() + touchPoint1.lastPos()) / 2;
249             QPointF newcenter = (touchPoint0.pos() + touchPoint1.pos()) / 2;
250
251             m_pinchStarted = true;
252             scaleView(touchScaleFactor, newcenter, oldcenter);
253         }
254         break;
255     }
256     case QEvent::TouchEnd: {
257         m_pressAndHoldTimer.stop();
258         if (m_pinchStarted) {
259             m_pinchStarted = false;
260         }
261         if (touchPoints.count() == 1 && !m_dragStarted &&
262                 !m_didPressAndHold && m_tapEvent) {
263             m_tapEvent = false;
264             bool doubleTap = event->timestamp() - m_touchTimestamp
265                     < static_cast<ulong>(qApp->styleHints()->mouseDoubleClickInterval());
266             if (doubleTap) {
267                 m_nameDisplayTimer.stop();
268                 selectNextItem();
269             }
270             else {
271                 selectItem();
272             }
273             m_touchTimestamp = event->timestamp();
274         }
275         m_didPressAndHold = false;
276         break;
277     }
278     default:
279         break;
280     }
281 }
282
283 void InspectTool::scaleView(const qreal &factor, const QPointF &newcenter, const QPointF &oldcenter)
284 {
285     m_pressAndHoldTimer.stop();
286     if (((m_rootItem->scale() * factor) > m_maxScale)
287             || ((m_rootItem->scale() * factor) < m_minScale)) {
288         return;
289     }
290     //New position = new center + scalefactor * (oldposition - oldcenter)
291     QPointF newPosition = newcenter + (factor * (m_rootItem->pos() - oldcenter));
292     m_rootItem->setScale(m_rootItem->scale() * factor);
293     m_rootItem->setPos(newPosition);
294 }
295
296 void InspectTool::zoomIn()
297 {
298     qreal newScale = nextZoomScale(ZoomIn);
299     scaleView(newScale / m_rootItem->scale(), m_mousePosition, m_mousePosition);
300 }
301
302 void InspectTool::zoomOut()
303 {
304     qreal newScale = nextZoomScale(ZoomOut);
305     scaleView(newScale / m_rootItem->scale(), m_mousePosition, m_mousePosition);
306 }
307
308 void InspectTool::zoomTo100()
309 {
310     m_didPressAndHold = true;
311
312     m_rootItem->setPos(QPointF(0, 0));
313     m_rootItem->setScale(1.0);
314 }
315
316 qreal InspectTool::nextZoomScale(ZoomDirection direction)
317 {
318     static QList<qreal> zoomScales =
319             QList<qreal>()
320             << 0.125f
321             << 1.0f / 6.0f
322             << 0.25f
323             << 1.0f / 3.0f
324             << 0.5f
325             << 2.0f / 3.0f
326             << 1.0f
327             << 2.0f
328             << 3.0f
329             << 4.0f
330             << 5.0f
331             << 6.0f
332             << 7.0f
333             << 8.0f
334             << 12.0f
335             << 16.0f
336             << 32.0f
337             << 48.0f;
338
339     if (direction == ZoomIn) {
340         for (int i = 0; i < zoomScales.length(); ++i) {
341             if (zoomScales[i] > m_rootItem->scale())
342                 return zoomScales[i];
343         }
344         return zoomScales.last();
345     } else {
346         for (int i = zoomScales.length() - 1; i >= 0; --i) {
347             if (zoomScales[i] < m_rootItem->scale())
348                 return zoomScales[i];
349         }
350         return zoomScales.first();
351     }
352
353     return 1.0f;
354 }
355
356 void InspectTool::initializeDrag(const QPointF &pos)
357 {
358     m_dragStartPosition = pos;
359     m_dragStarted = false;
360 }
361
362 void InspectTool::dragItemToPosition()
363 {
364     QPointF newPosition = m_rootItem->pos() + m_mousePosition - m_dragStartPosition;
365     m_dragStartPosition = m_mousePosition;
366     m_rootItem->setPos(newPosition);
367 }
368
369 void InspectTool::moveItem(bool valid)
370 {
371     if (m_pinchStarted)
372         return;
373
374     if (!m_dragStarted
375             && valid
376             && ((m_dragStartPosition - m_mousePosition).manhattanLength()
377                 > qApp->styleHints()->startDragDistance())) {
378         m_pressAndHoldTimer.stop();
379         m_dragStarted = true;
380     }
381     if (m_dragStarted)
382         dragItemToPosition();
383 }
384
385 void InspectTool::selectNextItem()
386 {
387     if (m_lastClickedItem != inspector()->topVisibleItemAt(m_mousePosition))
388         return;
389     QList<QQuickItem*> items = inspector()->itemsAt(m_mousePosition);
390     for (int i = 0; i < items.count(); i++) {
391         if (m_lastItem == items[i]) {
392             if (i + 1 < items.count())
393                 m_lastItem = items[i+1];
394             else
395                 m_lastItem = items[0];
396             inspector()->setSelectedItems(QList<QQuickItem*>() << m_lastItem);
397             showSelectedItemName();
398             break;
399         }
400     }
401 }
402
403 void InspectTool::selectItem()
404 {
405     if (!inspector()->topVisibleItemAt(m_mousePosition))
406         return;
407     if (m_lastClickedItem == inspector()->topVisibleItemAt(m_mousePosition)) {
408         m_nameDisplayTimer.start();
409         return;
410     }
411     m_lastClickedItem = inspector()->topVisibleItemAt(m_mousePosition);
412     m_lastItem = m_lastClickedItem;
413     inspector()->setSelectedItems(QList<QQuickItem*>() << m_lastClickedItem);
414     showSelectedItemName();
415 }
416
417 QQuickViewInspector *InspectTool::inspector() const
418 {
419     return static_cast<QQuickViewInspector*>(AbstractTool::inspector());
420 }
421
422 void InspectTool::showSelectedItemName()
423 {
424     inspector()->showSelectedItemName(m_lastItem, m_mousePosition);
425 }
426
427 } // namespace QtQuick2
428 } // namespace QmlJSDebugger