37a8997927eafca57e657e33161f7e7d54c5b9d8
[profile/ivi/qtdeclarative.git] / src / plugins / qmltooling / qmldbg_qtquick2 / qquickviewinspector.cpp
1 /****************************************************************************
2 **
3 ** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
4 ** Contact: http://www.qt-project.org/
5 **
6 ** This file is part of the QtQml module of the Qt Toolkit.
7 **
8 ** $QT_BEGIN_LICENSE:LGPL$
9 ** GNU Lesser General Public License Usage
10 ** This file may be used under the terms of the GNU Lesser General Public
11 ** License version 2.1 as published by the Free Software Foundation and
12 ** appearing in the file LICENSE.LGPL included in the packaging of this
13 ** file. Please review the following information to ensure the GNU Lesser
14 ** General Public License version 2.1 requirements will be met:
15 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
16 **
17 ** In addition, as a special exception, Nokia gives you certain additional
18 ** rights. These rights are described in the Nokia Qt LGPL Exception
19 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
20 **
21 ** GNU General Public License Usage
22 ** Alternatively, this file may be used under the terms of the GNU General
23 ** Public License version 3.0 as published by the Free Software Foundation
24 ** and appearing in the file LICENSE.GPL included in the packaging of this
25 ** file. Please review the following information to ensure the GNU General
26 ** Public License version 3.0 requirements will be met:
27 ** http://www.gnu.org/copyleft/gpl.html.
28 **
29 ** Other Usage
30 ** Alternatively, this file may be used in accordance with the terms and
31 ** conditions contained in a signed written agreement between you and Nokia.
32 **
33 **
34 **
35 **
36 **
37 **
38 ** $QT_END_LICENSE$
39 **
40 ****************************************************************************/
41
42 #include "qquickviewinspector.h"
43
44 #include "highlight.h"
45 #include "inspecttool.h"
46
47 #include <QtQml/private/qqmlengine_p.h>
48 #include <QtQml/private/qqmldebugservice_p.h>
49 #include <QtQuick/private/qquickitem_p.h>
50
51 #include <QtQuick/QQuickView>
52 #include <QtQuick/QQuickItem>
53
54 #include <cfloat>
55
56 namespace QmlJSDebugger {
57 namespace QtQuick2 {
58
59 /*
60  * Collects all the items at the given position, from top to bottom.
61  */
62 static void collectItemsAt(QQuickItem *item, const QPointF &pos,
63                            QQuickItem *overlay, QList<QQuickItem *> &resultList)
64 {
65     if (item == overlay)
66         return;
67
68     if (item->flags() & QQuickItem::ItemClipsChildrenToShape) {
69         if (!QRectF(0, 0, item->width(), item->height()).contains(pos))
70             return;
71     }
72
73     QList<QQuickItem *> children = QQuickItemPrivate::get(item)->paintOrderChildItems();
74     for (int i = children.count() - 1; i >= 0; --i) {
75         QQuickItem *child = children.at(i);
76         collectItemsAt(child, item->mapToItem(child, pos), overlay, resultList);
77     }
78
79     if (!QRectF(0, 0, item->width(), item->height()).contains(pos))
80         return;
81
82     resultList.append(item);
83 }
84
85 /*
86  * Returns the first visible item at the given position, or 0 when no such
87  * child exists.
88  */
89 static QQuickItem *itemAt(QQuickItem *item, const QPointF &pos,
90                           QQuickItem *overlay)
91 {
92     if (item == overlay)
93         return 0;
94
95     if (!item->isVisible() || item->opacity() == 0.0)
96         return 0;
97
98     if (item->flags() & QQuickItem::ItemClipsChildrenToShape) {
99         if (!QRectF(0, 0, item->width(), item->height()).contains(pos))
100             return 0;
101     }
102
103     QList<QQuickItem *> children = QQuickItemPrivate::get(item)->paintOrderChildItems();
104     for (int i = children.count() - 1; i >= 0; --i) {
105         QQuickItem *child = children.at(i);
106         if (QQuickItem *betterCandidate = itemAt(child, item->mapToItem(child, pos),
107                                                  overlay))
108             return betterCandidate;
109     }
110
111     if (!(item->flags() & QQuickItem::ItemHasContents))
112         return 0;
113
114     if (!QRectF(0, 0, item->width(), item->height()).contains(pos))
115         return 0;
116
117     return item;
118 }
119
120
121 QQuickViewInspector::QQuickViewInspector(QQuickView *view, QObject *parent) :
122     AbstractViewInspector(parent),
123     m_view(view),
124     m_overlay(new QQuickItem),
125     m_inspectTool(new InspectTool(this, view)),
126     m_sendQmlReloadedMessage(false)
127 {
128     // Try to make sure the overlay is always on top
129     m_overlay->setZ(FLT_MAX);
130
131     if (QQuickItem *root = view->rootItem())
132         m_overlay->setParentItem(root);
133
134     view->installEventFilter(this);
135     appendTool(m_inspectTool);
136     connect(view, SIGNAL(statusChanged(QQuickView::Status)),
137             this, SLOT(onViewStatus(QQuickView::Status)));
138 }
139
140 void QQuickViewInspector::changeCurrentObjects(const QList<QObject*> &objects)
141 {
142     QList<QQuickItem*> items;
143     foreach (QObject *obj, objects)
144         if (QQuickItem *item = qobject_cast<QQuickItem*>(obj))
145             items << item;
146
147     syncSelectedItems(items);
148 }
149
150 void QQuickViewInspector::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 QWindow *getMasterWindow(QWindow *w)
163 {
164     QWindow *p = w->parent();
165     while (p) {
166         w = p;
167         p = p->parent();
168     }
169     return w;
170 }
171
172 Qt::WindowFlags QQuickViewInspector::windowFlags() const
173 {
174     return getMasterWindow(m_view)->windowFlags();
175 }
176
177 void QQuickViewInspector::setWindowFlags(Qt::WindowFlags flags)
178 {
179     QWindow *w = getMasterWindow(m_view);
180     w->setWindowFlags(flags);
181     // make flags are applied
182     w->setVisible(false);
183     w->setVisible(true);
184 }
185
186 QQmlEngine *QQuickViewInspector::declarativeEngine() const
187 {
188     return m_view->engine();
189 }
190
191 QQuickItem *QQuickViewInspector::topVisibleItemAt(const QPointF &pos) const
192 {
193     QQuickItem *root = m_view->rootItem();
194     return itemAt(root, root->mapFromScene(pos), m_overlay);
195 }
196
197 QList<QQuickItem *> QQuickViewInspector::itemsAt(const QPointF &pos) const
198 {
199     QQuickItem *root = m_view->rootItem();
200     QList<QQuickItem *> resultList;
201     collectItemsAt(root, root->mapFromScene(pos), m_overlay,
202                    resultList);
203     return resultList;
204 }
205
206 QList<QQuickItem*> QQuickViewInspector::selectedItems() const
207 {
208     QList<QQuickItem *> selection;
209     foreach (const QPointer<QQuickItem> &selectedItem, m_selectedItems) {
210         if (selectedItem)
211             selection << selectedItem;
212     }
213     return selection;
214 }
215
216 void QQuickViewInspector::setSelectedItems(const QList<QQuickItem *> &items)
217 {
218     if (!syncSelectedItems(items))
219         return;
220
221     QList<QObject*> objectList;
222     foreach (QQuickItem *item, items)
223         objectList << item;
224
225     sendCurrentObjects(objectList);
226 }
227
228 bool QQuickViewInspector::syncSelectedItems(const QList<QQuickItem *> &items)
229 {
230     bool selectionChanged = false;
231
232     // Disconnect and remove items that are no longer selected
233     foreach (const QPointer<QQuickItem> &item, m_selectedItems) {
234         if (!item) // Don't see how this can happen due to handling of destroyed()
235             continue;
236         if (items.contains(item))
237             continue;
238
239         selectionChanged = true;
240         item->disconnect(this);
241         m_selectedItems.removeOne(item);
242         delete m_highlightItems.take(item);
243     }
244
245     // Connect and add newly selected items
246     foreach (QQuickItem *item, items) {
247         if (m_selectedItems.contains(item))
248             continue;
249
250         selectionChanged = true;
251         connect(item, SIGNAL(destroyed(QObject*)), this, SLOT(removeFromSelectedItems(QObject*)));
252         m_selectedItems.append(item);
253         SelectionHighlight *selectionHighlightItem;
254         selectionHighlightItem = new SelectionHighlight(titleForItem(item), item, m_overlay);
255         m_highlightItems.insert(item, selectionHighlightItem);
256     }
257
258     return selectionChanged;
259 }
260
261 void QQuickViewInspector::showSelectedItemName(QQuickItem *item, const QPointF &point)
262 {
263     SelectionHighlight *highlightItem = m_highlightItems.value(item, 0);
264     if (highlightItem)
265         highlightItem->showName(point);
266 }
267
268 void QQuickViewInspector::removeFromSelectedItems(QObject *object)
269 {
270     if (QQuickItem *item = qobject_cast<QQuickItem*>(object)) {
271         if (m_selectedItems.removeOne(item))
272             delete m_highlightItems.take(item);
273     }
274 }
275
276 bool QQuickViewInspector::eventFilter(QObject *obj, QEvent *event)
277 {
278     if (obj != m_view)
279         return QObject::eventFilter(obj, event);
280
281     return AbstractViewInspector::eventFilter(obj, event);
282 }
283
284 bool QQuickViewInspector::mouseMoveEvent(QMouseEvent *event)
285 {
286     // TODO
287 //    if (QQuickItem *item = topVisibleItemAt(event->pos()))
288 //        m_view->setToolTip(titleForItem(item));
289 //    else
290 //        m_view->setToolTip(QString());
291
292     return AbstractViewInspector::mouseMoveEvent(event);
293 }
294
295 QString QQuickViewInspector::titleForItem(QQuickItem *item) const
296 {
297     QString className = QLatin1String(item->metaObject()->className());
298     QString objectStringId = idStringForObject(item);
299
300     className.remove(QRegExp(QLatin1String("_QMLTYPE_\\d+")));
301     className.remove(QRegExp(QLatin1String("_QML_\\d+")));
302     if (className.startsWith(QLatin1String("QQuick")))
303         className = className.mid(6);
304
305     QString constructedName;
306
307     if (!objectStringId.isEmpty()) {
308         constructedName = objectStringId + QLatin1String(" (") + className + QLatin1Char(')');
309     } else if (!item->objectName().isEmpty()) {
310         constructedName = item->objectName() + QLatin1String(" (") + className + QLatin1Char(')');
311     } else {
312         constructedName = className;
313     }
314
315     return constructedName;
316 }
317
318 void QQuickViewInspector::reloadQmlFile(const QHash<QString, QByteArray> &changesHash)
319 {
320     clearComponentCache();
321
322     // Reset the selection since we are reloading the main qml
323     setSelectedItems(QList<QQuickItem *>());
324
325     QQmlDebugService::clearObjectsFromHash();
326
327     QHash<QUrl, QByteArray> debugCache;
328
329     foreach (const QString &str, changesHash.keys())
330         debugCache.insert(QUrl(str), changesHash.value(str, QByteArray()));
331
332     // Updating the cache in engine private such that the QML Data loader
333     // gets the changes from the cache.
334     QQmlEnginePrivate::get(declarativeEngine())->setDebugChangesCache(debugCache);
335
336     m_sendQmlReloadedMessage = true;
337     // reloading the view such that the changes done for the files are
338     // reflected in view
339     view()->setSource(view()->source());
340 }
341
342 void QQuickViewInspector::setShowAppOnTop(bool appOnTop)
343 {
344     m_appOnTop = appOnTop;
345     // Hack for QTCREATORBUG-6295.
346     // TODO: The root cause to be identified and fixed later.
347     QTimer::singleShot(100, this, SLOT(applyAppOnTop()));
348 }
349
350 void QQuickViewInspector::onViewStatus(QQuickView::Status status)
351 {
352     bool success = false;
353     switch (status) {
354     case QQuickView::Loading:
355         return;
356     case QQuickView::Ready:
357         if (view()->errors().count())
358             break;
359         success = true;
360         break;
361     case QQuickView::Null:
362     case QQuickView::Error:
363         break;
364     default:
365         break;
366     }
367     if (m_sendQmlReloadedMessage) {
368         m_sendQmlReloadedMessage = false;
369         sendQmlFileReloaded(success);
370     }
371 }
372
373 void QQuickViewInspector::applyAppOnTop()
374 {
375     Qt::WindowFlags flags = windowFlags();
376     if (m_appOnTop)
377         flags |= Qt::WindowStaysOnTopHint;
378     else
379         flags &= ~Qt::WindowStaysOnTopHint;
380
381     setWindowFlags(flags);
382 }
383
384 } // namespace QtQuick2
385 } // namespace QmlJSDebugger