Debugger: Inspector code cleanup
[profile/ivi/qtdeclarative.git] / src / plugins / qmltooling / qmldbg_inspector / sgviewinspector.cpp
1 /****************************************************************************
2 **
3 ** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
4 ** All rights reserved.
5 ** Contact: Nokia Corporation (qt-info@nokia.com)
6 **
7 ** This file is part of the QtDeclarative module of the Qt Toolkit.
8 **
9 ** $QT_BEGIN_LICENSE:LGPL$
10 ** GNU Lesser General Public License Usage
11 ** This file may be used under the terms of the GNU Lesser General Public
12 ** License version 2.1 as published by the Free Software Foundation and
13 ** appearing in the file LICENSE.LGPL included in the packaging of this
14 ** file. Please review the following information to ensure the GNU Lesser
15 ** General Public License version 2.1 requirements will be met:
16 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
17 **
18 ** In addition, as a special exception, Nokia gives you certain additional
19 ** rights. These rights are described in the Nokia Qt LGPL Exception
20 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
21 **
22 ** GNU General Public License Usage
23 ** Alternatively, this file may be used under the terms of the GNU General
24 ** Public License version 3.0 as published by the Free Software Foundation
25 ** and appearing in the file LICENSE.GPL included in the packaging of this
26 ** file. Please review the following information to ensure the GNU General
27 ** Public License version 3.0 requirements will be met:
28 ** http://www.gnu.org/copyleft/gpl.html.
29 **
30 ** Other Usage
31 ** Alternatively, this file may be used in accordance with the terms and
32 ** conditions contained in a signed written agreement between you and Nokia.
33 **
34 **
35 **
36 **
37 **
38 ** $QT_END_LICENSE$
39 **
40 ****************************************************************************/
41
42 #include "sgviewinspector.h"
43
44 #include "qdeclarativeinspectorprotocol.h"
45 #include "sghighlight.h"
46 #include "sgselectiontool.h"
47
48 #include <QtDeclarative/private/qquickitem_p.h>
49
50 #include <QtDeclarative/QQuickView>
51 #include <QtDeclarative/QQuickItem>
52
53 #include <cfloat>
54
55 namespace QmlJSDebugger {
56
57 /*
58  * Collects all the items at the given position, from top to bottom.
59  */
60 static void collectItemsAt(QQuickItem *item, const QPointF &pos, QQuickItem *overlay,
61                            QList<QQuickItem *> &resultList)
62 {
63     if (item == overlay)
64         return;
65
66     if (item->flags() & QQuickItem::ItemClipsChildrenToShape) {
67         if (!QRectF(0, 0, item->width(), item->height()).contains(pos))
68             return;
69     }
70
71     QList<QQuickItem *> children = QQuickItemPrivate::get(item)->paintOrderChildItems();
72     for (int i = children.count() - 1; i >= 0; --i) {
73         QQuickItem *child = children.at(i);
74         collectItemsAt(child, item->mapToItem(child, pos), overlay, resultList);
75     }
76
77     if (!QRectF(0, 0, item->width(), item->height()).contains(pos))
78         return;
79
80     resultList.append(item);
81 }
82
83 /*
84  * Returns the first visible item at the given position, or 0 when no such
85  * child exists.
86  */
87 static QQuickItem *itemAt(QQuickItem *item, const QPointF &pos, QQuickItem *overlay)
88 {
89     if (item == overlay)
90         return 0;
91
92     if (!item->isVisible() || item->opacity() == 0.0)
93         return 0;
94
95     if (item->flags() & QQuickItem::ItemClipsChildrenToShape) {
96         if (!QRectF(0, 0, item->width(), item->height()).contains(pos))
97             return 0;
98     }
99
100     QList<QQuickItem *> children = QQuickItemPrivate::get(item)->paintOrderChildItems();
101     for (int i = children.count() - 1; i >= 0; --i) {
102         QQuickItem *child = children.at(i);
103         if (QQuickItem *betterCandidate = itemAt(child, item->mapToItem(child, pos), overlay))
104             return betterCandidate;
105     }
106
107     if (!(item->flags() & QQuickItem::ItemHasContents))
108         return 0;
109
110     if (!QRectF(0, 0, item->width(), item->height()).contains(pos))
111         return 0;
112
113     return item;
114 }
115
116
117 SGViewInspector::SGViewInspector(QQuickView *view, QObject *parent) :
118     AbstractViewInspector(parent),
119     m_view(view),
120     m_overlay(new QQuickItem),
121     m_selectionTool(new SGSelectionTool(this)),
122     m_designMode(true)
123 {
124     // Try to make sure the overlay is always on top
125     m_overlay->setZ(FLT_MAX);
126
127     if (QQuickItem *root = view->rootItem())
128         m_overlay->setParentItem(root);
129
130     view->installEventFilter(this);
131     setCurrentTool(m_selectionTool);
132 }
133
134 void SGViewInspector::changeCurrentObjects(const QList<QObject*> &objects)
135 {
136     QList<QQuickItem*> items;
137     foreach (QObject *obj, objects)
138         if (QQuickItem *item = qobject_cast<QQuickItem*>(obj))
139             items << item;
140
141     syncSelectedItems(items);
142 }
143
144 void SGViewInspector::reloadView()
145 {
146     // TODO
147     emit reloadRequested();
148 }
149
150 void SGViewInspector::reparentQmlObject(QObject *object, QObject *newParent)
151 {
152     if (!newParent)
153         return;
154
155     object->setParent(newParent);
156     QQuickItem *newParentItem = qobject_cast<QQuickItem*>(newParent);
157     QQuickItem *item = qobject_cast<QQuickItem*>(object);
158     if (newParentItem && item)
159         item->setParentItem(newParentItem);
160 }
161
162 void SGViewInspector::changeTool(InspectorProtocol::Tool tool)
163 {
164     switch (tool) {
165     case InspectorProtocol::ColorPickerTool:
166         // TODO
167         emit colorPickerActivated();
168         break;
169     case InspectorProtocol::SelectMarqueeTool:
170         // TODO
171         emit marqueeSelectToolActivated();
172         break;
173     case InspectorProtocol::SelectTool:
174         setCurrentTool(m_selectionTool);
175         emit selectToolActivated();
176         break;
177     case InspectorProtocol::ZoomTool:
178         // TODO
179         emit zoomToolActivated();
180         break;
181     }
182 }
183
184 QWindow *getMasterWindow(QWindow *w)
185 {
186     QWindow *p = w->parent();
187     while (p) {
188         w = p;
189         p = p->parent();
190     }
191     return w;
192 }
193
194 Qt::WindowFlags SGViewInspector::windowFlags() const
195 {
196     return getMasterWindow(m_view)->windowFlags();
197 }
198
199 void SGViewInspector::setWindowFlags(Qt::WindowFlags flags)
200 {
201     QWindow *w = getMasterWindow(m_view);
202     w->setWindowFlags(flags);
203     w->show();
204 }
205
206 QDeclarativeEngine *SGViewInspector::declarativeEngine() const
207 {
208     return m_view->engine();
209 }
210
211 QQuickItem *SGViewInspector::topVisibleItemAt(const QPointF &pos) const
212 {
213     QQuickItem *root = m_view->rootItem();
214     return itemAt(root, root->mapFromScene(pos), m_overlay);
215 }
216
217 QList<QQuickItem *> SGViewInspector::itemsAt(const QPointF &pos) const
218 {
219     QQuickItem *root = m_view->rootItem();
220     QList<QQuickItem *> resultList;
221     collectItemsAt(root, root->mapFromScene(pos), m_overlay, resultList);
222     return resultList;
223 }
224
225 QList<QQuickItem*> SGViewInspector::selectedItems() const
226 {
227     QList<QQuickItem *> selection;
228     foreach (const QWeakPointer<QQuickItem> &selectedItem, m_selectedItems) {
229         if (selectedItem)
230             selection << selectedItem.data();
231     }
232     return selection;
233 }
234
235 void SGViewInspector::setSelectedItems(const QList<QQuickItem *> &items)
236 {
237     if (!syncSelectedItems(items))
238         return;
239
240     QList<QObject*> objectList;
241     foreach (QQuickItem *item, items)
242         objectList << item;
243
244     sendCurrentObjects(objectList);
245 }
246
247 bool SGViewInspector::syncSelectedItems(const QList<QQuickItem *> &items)
248 {
249     bool selectionChanged = false;
250
251     // Disconnect and remove items that are no longer selected
252     foreach (const QWeakPointer<QQuickItem> &item, m_selectedItems) {
253         if (!item) // Don't see how this can happen due to handling of destroyed()
254             continue;
255         if (items.contains(item.data()))
256             continue;
257
258         selectionChanged = true;
259         item.data()->disconnect(this);
260         m_selectedItems.removeOne(item);
261         delete m_highlightItems.take(item.data());
262     }
263
264     // Connect and add newly selected items
265     foreach (QQuickItem *item, items) {
266         if (m_selectedItems.contains(item))
267             continue;
268
269         selectionChanged = true;
270         connect(item, SIGNAL(destroyed(QObject*)), this, SLOT(removeFromSelectedItems(QObject*)));
271         m_selectedItems.append(item);
272         m_highlightItems.insert(item, new SGSelectionHighlight(item, m_overlay));
273     }
274
275     return selectionChanged;
276 }
277
278 void SGViewInspector::removeFromSelectedItems(QObject *object)
279 {
280     if (QQuickItem *item = qobject_cast<QQuickItem*>(object)) {
281         if (m_selectedItems.removeOne(item))
282             delete m_highlightItems.take(item);
283     }
284 }
285
286 bool SGViewInspector::eventFilter(QObject *obj, QEvent *event)
287 {
288     if (obj != m_view)
289         return QObject::eventFilter(obj, event);
290
291     return AbstractViewInspector::eventFilter(obj, event);
292 }
293
294 bool SGViewInspector::mouseMoveEvent(QMouseEvent *event)
295 {
296     // TODO
297 //    if (QQuickItem *item = topVisibleItemAt(event->pos()))
298 //        m_view->setToolTip(titleForItem(item));
299 //    else
300 //        m_view->setToolTip(QString());
301
302     return AbstractViewInspector::mouseMoveEvent(event);
303 }
304
305 QString SGViewInspector::titleForItem(QQuickItem *item) const
306 {
307     QString className = QLatin1String(item->metaObject()->className());
308     QString objectStringId = idStringForObject(item);
309
310     className.remove(QRegExp(QLatin1String("_QMLTYPE_\\d+")));
311     className.remove(QRegExp(QLatin1String("_QML_\\d+")));
312     if (className.startsWith(QLatin1String("QQuick")))
313         className = className.mid(6);
314
315     QString constructedName;
316
317     if (!objectStringId.isEmpty()) {
318         constructedName = objectStringId + QLatin1String(" (") + className + QLatin1Char(')');
319     } else if (!item->objectName().isEmpty()) {
320         constructedName = item->objectName() + QLatin1String(" (") + className + QLatin1Char(')');
321     } else {
322         constructedName = className;
323     }
324
325     return constructedName;
326 }
327
328 } // namespace QmlJSDebugger