Remove "All rights reserved" line from license headers.
[profile/ivi/qtdeclarative.git] / src / qtquick1 / graphicsitems / qdeclarativeloader.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 QtDeclarative 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 "QtQuick1/private/qdeclarativeloader_p_p.h"
43
44 #include <QtDeclarative/qdeclarativeinfo.h>
45 #include <QtDeclarative/private/qdeclarativeengine_p.h>
46 #include <QtDeclarative/private/qdeclarativeglobal_p.h>
47
48 QT_BEGIN_NAMESPACE
49
50
51
52 QDeclarative1LoaderPrivate::QDeclarative1LoaderPrivate()
53     : item(0), component(0), ownComponent(false), updatingSize(false),
54       itemWidthValid(false), itemHeightValid(false)
55 {
56 }
57
58 QDeclarative1LoaderPrivate::~QDeclarative1LoaderPrivate()
59 {
60 }
61
62 void QDeclarative1LoaderPrivate::itemGeometryChanged(QDeclarativeItem *resizeItem, const QRectF &newGeometry, const QRectF &oldGeometry)
63 {
64     if (resizeItem == item) {
65         if (!updatingSize && newGeometry.width() != oldGeometry.width())
66             itemWidthValid = true;
67         if (!updatingSize && newGeometry.height() != oldGeometry.height())
68             itemHeightValid = true;
69         _q_updateSize(false);
70     }
71     QDeclarativeItemChangeListener::itemGeometryChanged(resizeItem, newGeometry, oldGeometry);
72 }
73
74 void QDeclarative1LoaderPrivate::clear()
75 {
76     if (ownComponent) {
77         component->deleteLater();
78         component = 0;
79         ownComponent = false;
80     }
81     source = QUrl();
82
83     if (item) {
84         if (QDeclarativeItem *qmlItem = qobject_cast<QDeclarativeItem*>(item)) {
85             QDeclarativeItemPrivate *p =
86                     static_cast<QDeclarativeItemPrivate *>(QGraphicsItemPrivate::get(qmlItem));
87             p->removeItemChangeListener(this, QDeclarativeItemPrivate::Geometry);
88         }
89
90         // We can't delete immediately because our item may have triggered
91         // the Loader to load a different item.
92         if (item->scene()) {
93             item->scene()->removeItem(item);
94         } else {
95             item->setParentItem(0);
96             item->setVisible(false);
97         }
98         item->deleteLater();
99         item = 0;
100     }
101 }
102
103 void QDeclarative1LoaderPrivate::initResize()
104 {
105     Q_Q(QDeclarative1Loader);
106     if (QDeclarativeItem *qmlItem = qobject_cast<QDeclarativeItem*>(item)) {
107         QDeclarativeItemPrivate *p =
108                 static_cast<QDeclarativeItemPrivate *>(QGraphicsItemPrivate::get(qmlItem));
109         p->addItemChangeListener(this, QDeclarativeItemPrivate::Geometry);
110         // We may override the item's size, so we need to remember
111         // whether the item provided its own valid size.
112         itemWidthValid = p->widthValid;
113         itemHeightValid = p->heightValid;
114     } else if (item && item->isWidget()) {
115         QGraphicsWidget *widget = static_cast<QGraphicsWidget*>(item);
116         widget->installEventFilter(q);
117     }
118     _q_updateSize();
119 }
120
121 /*!
122     \qmlclass Loader QDeclarative1Loader
123     \inqmlmodule QtQuick 1
124     \ingroup qml-utility-elements
125     \since QtQuick 1.0
126     \inherits Item
127
128     \brief The Loader item allows dynamically loading an Item-based
129     subtree from a URL or Component.
130
131     Loader is used to dynamically load visual QML components. It can load a
132     QML file (using the \l source property) or a \l Component object (using 
133     the \l sourceComponent property). It is useful for delaying the creation 
134     of a component until it is required: for example, when a component should 
135     be created on demand, or when a component should not be created 
136     unnecessarily for performance reasons.
137
138     Here is a Loader that loads "Page1.qml" as a component when the 
139     \l MouseArea is clicked:
140
141     \snippet doc/src/snippets/qtquick1/loader/simple.qml 0
142
143     The loaded item can be accessed using the \l item property.
144
145     If the \l source or \l sourceComponent changes, any previously instantiated
146     items are destroyed. Setting \l source to an empty string or setting
147     \l sourceComponent to \c undefined destroys the currently loaded item,
148     freeing resources and leaving the Loader empty.
149
150     \section2 Loader sizing behavior
151
152     Loader is like any other visual item and must be positioned and sized
153     accordingly to become visible.
154
155     \list
156     \o If an explicit size is not specified for the Loader, the Loader
157     is automatically resized to the size of the loaded item once the
158     component is loaded.
159     \o If the size of the Loader is specified explicitly by setting
160     the width, height or by anchoring, the loaded item will be resized
161     to the size of the Loader.
162     \endlist
163
164     In both scenarios the size of the item and the Loader are identical.
165     This ensures that anchoring to the Loader is equivalent to anchoring
166     to the loaded item.
167
168     \table
169     \row
170     \o sizeloader.qml
171     \o sizeitem.qml
172     \row
173     \o \snippet doc/src/snippets/qtquick1/loader/sizeloader.qml 0
174     \o \snippet doc/src/snippets/qtquick1/loader/sizeitem.qml 0
175     \row
176     \o The red rectangle will be sized to the size of the root item.
177     \o The red rectangle will be 50x50, centered in the root item.
178     \endtable
179
180
181     \section2 Receiving signals from loaded items
182
183     Any signals emitted from the loaded item can be received using the 
184     \l Connections element. For example, the following \c application.qml
185     loads \c MyItem.qml, and is able to receive the \c message signal from
186     the loaded item through a \l Connections object:
187
188     \table
189     \row 
190     \o application.qml
191     \o MyItem.qml
192     \row
193     \o \snippet doc/src/snippets/qtquick1/loader/connections.qml 0
194     \o \snippet doc/src/snippets/qtquick1/loader/MyItem.qml 0
195     \endtable
196
197     Alternatively, since \c MyItem.qml is loaded within the scope of the
198     Loader, it could also directly call any function defined in the Loader or
199     its parent \l Item.
200
201
202     \section2 Focus and key events
203
204     Loader is a focus scope. Its \l {Item::}{focus} property must be set to 
205     \c true for any of its children to get the \e {active focus}. (See 
206     \l{qmlfocus#Acquiring Focus and Focus Scopes}{the focus documentation page} 
207     for more details.) Any key events received in the loaded item should likely
208     also be \l {KeyEvent::}{accepted} so they are not propagated to the Loader.
209
210     For example, the following \c application.qml loads \c KeyReader.qml when
211     the \l MouseArea is clicked.  Notice the \l {Item::}{focus} property is 
212     set to \c true for the Loader as well as the \l Item in the dynamically 
213     loaded object:
214
215     \table
216     \row 
217     \o application.qml
218     \o KeyReader.qml
219     \row
220     \o \snippet doc/src/snippets/qtquick1/loader/focus.qml 0
221     \o \snippet doc/src/snippets/qtquick1/loader/KeyReader.qml 0
222     \endtable
223
224     Once \c KeyReader.qml is loaded, it accepts key events and sets 
225     \c event.accepted to \c true so that the event is not propagated to the
226     parent \l Rectangle.
227
228     \sa {dynamic-object-creation}{Dynamic Object Creation}
229 */
230
231 QDeclarative1Loader::QDeclarative1Loader(QDeclarativeItem *parent)
232   : QDeclarative1ImplicitSizeItem(*(new QDeclarative1LoaderPrivate), parent)
233 {
234     Q_D(QDeclarative1Loader);
235     d->flags |= QGraphicsItem::ItemIsFocusScope;
236 }
237
238 QDeclarative1Loader::~QDeclarative1Loader()
239 {
240     Q_D(QDeclarative1Loader);
241     if (d->item) {
242         if (QDeclarativeItem *qmlItem = qobject_cast<QDeclarativeItem*>(d->item)) {
243             QDeclarativeItemPrivate *p =
244                     static_cast<QDeclarativeItemPrivate *>(QGraphicsItemPrivate::get(qmlItem));
245             p->removeItemChangeListener(d, QDeclarativeItemPrivate::Geometry);
246         }
247     }
248 }
249
250 /*!
251     \qmlproperty url QtQuick1::Loader::source
252     This property holds the URL of the QML component to instantiate.
253
254     Note the QML component must be an \l{Item}-based component. The loader
255     cannot load non-visual components.
256
257     To unload the currently loaded item, set this property to an empty string,
258     or set \l sourceComponent to \c undefined. Setting \c source to a
259     new URL will also cause the item created by the previous URL to be unloaded.
260
261     \sa sourceComponent, status, progress
262 */
263 QUrl QDeclarative1Loader::source() const
264 {
265     Q_D(const QDeclarative1Loader);
266     return d->source;
267 }
268
269 void QDeclarative1Loader::setSource(const QUrl &url)
270 {
271     Q_D(QDeclarative1Loader);
272     if (d->source == url)
273         return;
274
275     d->clear();
276
277     d->source = url;
278
279     if (d->source.isEmpty()) {
280         emit sourceChanged();
281         emit statusChanged();
282         emit progressChanged();
283         emit itemChanged();
284         return;
285     }
286
287     d->component = new QDeclarativeComponent(qmlEngine(this), d->source, this);
288     d->ownComponent = true;
289
290     if (isComponentComplete())
291         d->load();
292 }
293
294 /*!
295     \qmlproperty Component QtQuick1::Loader::sourceComponent
296     This property holds the \l{Component} to instantiate.
297
298     \qml
299     Item {
300         Component {
301             id: redSquare
302             Rectangle { color: "red"; width: 10; height: 10 }
303         }
304
305         Loader { sourceComponent: redSquare }
306         Loader { sourceComponent: redSquare; x: 10 }
307     }
308     \endqml
309
310     To unload the currently loaded item, set this property to an empty string
311     or \c undefined.
312
313     \sa source, progress
314 */
315
316 QDeclarativeComponent *QDeclarative1Loader::sourceComponent() const
317 {
318     Q_D(const QDeclarative1Loader);
319     return d->component;
320 }
321
322 void QDeclarative1Loader::setSourceComponent(QDeclarativeComponent *comp)
323 {
324     Q_D(QDeclarative1Loader);
325     if (comp == d->component)
326         return;
327
328     d->clear();
329
330     d->component = comp;
331     d->ownComponent = false;
332
333     if (!d->component) {
334         emit sourceChanged();
335         emit statusChanged();
336         emit progressChanged();
337         emit itemChanged();
338         return;
339     }
340
341     if (isComponentComplete())
342         d->load();
343 }
344
345 void QDeclarative1Loader::resetSourceComponent()
346 {
347     setSourceComponent(0);
348 }
349
350 void QDeclarative1LoaderPrivate::load()
351 {
352     Q_Q(QDeclarative1Loader);
353
354     if (!q->isComponentComplete() || !component)
355         return;
356
357     if (!component->isLoading()) {
358         _q_sourceLoaded();
359     } else {
360         QObject::connect(component, SIGNAL(statusChanged(QDeclarativeComponent::Status)),
361                 q, SLOT(_q_sourceLoaded()));
362         QObject::connect(component, SIGNAL(progressChanged(qreal)),
363                 q, SIGNAL(progressChanged()));
364         emit q->statusChanged();
365         emit q->progressChanged();
366         emit q->sourceChanged();
367         emit q->itemChanged();
368     }
369 }
370
371 void QDeclarative1LoaderPrivate::_q_sourceLoaded()
372 {
373     Q_Q(QDeclarative1Loader);
374
375     if (component) {
376         if (!component->errors().isEmpty()) {
377             QDeclarativeEnginePrivate::warning(qmlEngine(q), component->errors());
378             emit q->sourceChanged();
379             emit q->statusChanged();
380             emit q->progressChanged();
381             return;
382         }
383
384         QDeclarativeContext *creationContext = component->creationContext();
385         if (!creationContext) creationContext = qmlContext(q);
386         QDeclarativeContext *ctxt = new QDeclarativeContext(creationContext);
387         ctxt->setContextObject(q);
388
389         QDeclarativeGuard<QDeclarativeComponent> c = component;
390         QObject *obj = component->beginCreate(ctxt);
391         if (component != c) {
392             // component->create could trigger a change in source that causes
393             // component to be set to something else. In that case we just
394             // need to cleanup.
395             if (c)
396                 c->completeCreate();
397             delete obj;
398             delete ctxt;
399             return;
400         }
401         if (obj) {
402             item = qobject_cast<QGraphicsObject *>(obj);
403             if (item) {
404                 QDeclarative_setParent_noEvent(ctxt, obj);
405                 QDeclarative_setParent_noEvent(item, q);
406                 item->setParentItem(q);
407 //                item->setFocus(true);
408                 initResize();
409             } else {
410                 qmlInfo(q) << QDeclarative1Loader::tr("Loader does not support loading non-visual elements.");
411                 delete obj;
412                 delete ctxt;
413             }
414         } else {
415             if (!component->errors().isEmpty())
416                 QDeclarativeEnginePrivate::warning(qmlEngine(q), component->errors());
417             delete obj;
418             delete ctxt;
419             source = QUrl();
420         }
421         component->completeCreate();
422         emit q->sourceChanged();
423         emit q->statusChanged();
424         emit q->progressChanged();
425         emit q->itemChanged();
426         emit q->loaded();
427     }
428 }
429
430 /*!
431     \qmlproperty enumeration QtQuick1::Loader::status
432
433     This property holds the status of QML loading.  It can be one of:
434     \list
435     \o Loader.Null - no QML source has been set
436     \o Loader.Ready - the QML source has been loaded
437     \o Loader.Loading - the QML source is currently being loaded
438     \o Loader.Error - an error occurred while loading the QML source
439     \endlist
440
441     Use this status to provide an update or respond to the status change in some way.
442     For example, you could:
443
444     \list
445     \o Trigger a state change:
446     \qml
447         State { name: 'loaded'; when: loader.status == Loader.Ready }
448     \endqml
449
450     \o Implement an \c onStatusChanged signal handler:
451     \qml
452         Loader {
453             id: loader
454             onStatusChanged: if (loader.status == Loader.Ready) console.log('Loaded')
455         }
456     \endqml
457
458     \o Bind to the status value:
459     \qml
460         Text { text: loader.status == Loader.Ready ? 'Loaded' : 'Not loaded' }
461     \endqml
462     \endlist
463
464     Note that if the source is a local file, the status will initially be Ready (or Error). While
465     there will be no onStatusChanged signal in that case, the onLoaded will still be invoked.
466
467     \sa progress
468 */
469
470 QDeclarative1Loader::Status QDeclarative1Loader::status() const
471 {
472     Q_D(const QDeclarative1Loader);
473
474     if (d->component)
475         return static_cast<QDeclarative1Loader::Status>(d->component->status());
476
477     if (d->item)
478         return Ready;
479
480     return d->source.isEmpty() ? Null : Error;
481 }
482
483 void QDeclarative1Loader::componentComplete()
484 {
485     Q_D(QDeclarative1Loader);
486
487     QDeclarativeItem::componentComplete();
488     d->load();
489 }
490
491
492 /*!
493     \qmlsignal QtQuick1::Loader::onLoaded()
494
495     This handler is called when the \l status becomes \c Loader.Ready, or on successful
496     initial load.
497 */
498
499
500 /*!
501 \qmlproperty real QtQuick1::Loader::progress
502
503 This property holds the progress of loading QML data from the network, from
504 0.0 (nothing loaded) to 1.0 (finished).  Most QML files are quite small, so
505 this value will rapidly change from 0 to 1.
506
507 \sa status
508 */
509 qreal QDeclarative1Loader::progress() const
510 {
511     Q_D(const QDeclarative1Loader);
512
513     if (d->item)
514         return 1.0;
515
516     if (d->component)
517         return d->component->progress();
518
519     return 0.0;
520 }
521
522 void QDeclarative1LoaderPrivate::_q_updateSize(bool loaderGeometryChanged)
523 {
524     Q_Q(QDeclarative1Loader);
525     if (!item || updatingSize)
526         return;
527
528     updatingSize = true;
529     if (QDeclarativeItem *qmlItem = qobject_cast<QDeclarativeItem*>(item)) {
530         if (!itemWidthValid)
531             q->setImplicitWidth(qmlItem->implicitWidth());
532         else
533             q->setImplicitWidth(qmlItem->width());
534         if (loaderGeometryChanged && q->widthValid())
535             qmlItem->setWidth(q->width());
536         if (!itemHeightValid)
537             q->setImplicitHeight(qmlItem->implicitHeight());
538         else
539             q->setImplicitHeight(qmlItem->height());
540         if (loaderGeometryChanged && q->heightValid())
541             qmlItem->setHeight(q->height());
542     } else if (item && item->isWidget()) {
543         QGraphicsWidget *widget = static_cast<QGraphicsWidget*>(item);
544         QSizeF widgetSize = widget->size();
545         q->setImplicitWidth(widgetSize.width());
546         if (loaderGeometryChanged && q->widthValid())
547             widgetSize.setWidth(q->width());
548         q->setImplicitHeight(widgetSize.height());
549         if (loaderGeometryChanged && q->heightValid())
550             widgetSize.setHeight(q->height());
551         if (widget->size() != widgetSize)
552             widget->resize(widgetSize);
553     }
554     updatingSize = false;
555 }
556
557 /*!
558     \qmlproperty Item QtQuick1::Loader::item
559     This property holds the top-level item that is currently loaded.
560 */
561 QGraphicsObject *QDeclarative1Loader::item() const
562 {
563     Q_D(const QDeclarative1Loader);
564     return d->item;
565 }
566
567 void QDeclarative1Loader::geometryChanged(const QRectF &newGeometry, const QRectF &oldGeometry)
568 {
569     Q_D(QDeclarative1Loader);
570     if (newGeometry != oldGeometry) {
571         d->_q_updateSize();
572     }
573     QDeclarativeItem::geometryChanged(newGeometry, oldGeometry);
574 }
575
576 QVariant QDeclarative1Loader::itemChange(GraphicsItemChange change, const QVariant &value)
577 {
578     Q_D(QDeclarative1Loader);
579     if (change == ItemSceneHasChanged) {
580         if (d->item && d->item->isWidget()) {
581             d->item->removeEventFilter(this);
582             d->item->installEventFilter(this);
583         }
584     }
585     return QDeclarativeItem::itemChange(change, value);
586 }
587
588 bool QDeclarative1Loader::eventFilter(QObject *watched, QEvent *e)
589 {
590     Q_D(QDeclarative1Loader);
591     if (watched == d->item && e->type() == QEvent::GraphicsSceneResize) {
592         if (d->item && d->item->isWidget())
593             d->_q_updateSize(false);
594     }
595     return QDeclarativeItem::eventFilter(watched, e);
596 }
597
598 #include <moc_qdeclarativeloader_p.cpp>
599
600
601
602 QT_END_NAMESPACE