Update to 5.0.0-beta1
[profile/ivi/qtdeclarative.git] / src / quick / items / qquickanimatedimage.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 "qquickanimatedimage_p.h"
43 #include "qquickanimatedimage_p_p.h"
44
45 #ifndef QT_NO_MOVIE
46
47 #include <QtQml/qqmlinfo.h>
48 #include <QtQml/qqmlfile.h>
49 #include <QtQml/qqmlengine.h>
50 #include <QtGui/qmovie.h>
51 #include <QtNetwork/qnetworkrequest.h>
52 #include <QtNetwork/qnetworkreply.h>
53
54 QT_BEGIN_NAMESPACE
55 /*!
56     \qmltype AnimatedImage
57     \instantiates QQuickAnimatedImage
58     \inqmlmodule QtQuick 2
59     \inherits Image
60     \brief Plays animations stored as a series of images
61     \ingroup qtquick-visual
62
63     The AnimatedImage type extends the features of the \l Image type, providing
64     a way to play animations stored as images containing a series of frames,
65     such as those stored in GIF files.
66
67     Information about the current frame and total length of the animation can be
68     obtained using the \l currentFrame and \l frameCount properties. You can
69     start, pause and stop the animation by changing the values of the \l playing
70     and \l paused properties.
71
72     The full list of supported formats can be determined with QMovie::supportedFormats().
73
74     \section1 Example Usage
75
76     \beginfloatleft
77     \image animatedimageitem.gif
78     \endfloat
79
80     The following QML shows how to display an animated image and obtain information
81     about its state, such as the current frame and total number of frames.
82     The result is an animated image with a simple progress indicator underneath it.
83
84     \b Note: Unlike images, animated images are not cached or shared internally.
85
86     \clearfloat
87     \snippet qml/animatedimage.qml document
88
89     \sa BorderImage, Image
90 */
91
92 /*!
93     \qmlproperty url QtQuick2::AnimatedImage::source
94
95     This property holds the URL that refers to the source image.
96
97     AnimatedImage can handle any image format supported by Qt, loaded from any
98     URL scheme supported by Qt.
99
100     \sa QQuickImageProvider
101 */
102
103 QQuickAnimatedImage::QQuickAnimatedImage(QQuickItem *parent)
104     : QQuickImage(*(new QQuickAnimatedImagePrivate), parent)
105 {
106 }
107
108 QQuickAnimatedImage::~QQuickAnimatedImage()
109 {
110     Q_D(QQuickAnimatedImage);
111     if (d->reply)
112         d->reply->deleteLater();
113     delete d->_movie;
114 }
115
116 /*!
117   \qmlproperty bool QtQuick2::AnimatedImage::paused
118   This property holds whether the animated image is paused.
119
120   By default, this property is false. Set it to true when you want to pause
121   the animation.
122 */
123
124 bool QQuickAnimatedImage::isPaused() const
125 {
126     Q_D(const QQuickAnimatedImage);
127     if (!d->_movie)
128         return d->paused;
129     return d->_movie->state()==QMovie::Paused;
130 }
131
132 void QQuickAnimatedImage::setPaused(bool pause)
133 {
134     Q_D(QQuickAnimatedImage);
135     if (pause == d->paused)
136         return;
137     if (!d->_movie) {
138         d->paused = pause;
139         emit pausedChanged();
140     } else {
141         d->_movie->setPaused(pause);
142     }
143 }
144
145 /*!
146   \qmlproperty bool QtQuick2::AnimatedImage::playing
147   This property holds whether the animated image is playing.
148
149   By default, this property is true, meaning that the animation
150   will start playing immediately.
151
152   \b Note: this property is affected by changes to the actual playing
153   state of AnimatedImage. If non-animated images are used, \a playing
154   will need to be manually set to \a true in order to animate
155   following images.
156   \qml
157   AnimatedImage {
158       onStatusChanged: playing = (status == AnimatedImage.Ready)
159   }
160   \endqml
161 */
162
163 bool QQuickAnimatedImage::isPlaying() const
164 {
165     Q_D(const QQuickAnimatedImage);
166     if (!d->_movie)
167         return d->playing;
168     return d->_movie->state()!=QMovie::NotRunning;
169 }
170
171 void QQuickAnimatedImage::setPlaying(bool play)
172 {
173     Q_D(QQuickAnimatedImage);
174     if (play == d->playing)
175         return;
176     if (!d->_movie) {
177         d->playing = play;
178         emit playingChanged();
179         return;
180     }
181     if (play)
182         d->_movie->start();
183     else
184         d->_movie->stop();
185 }
186
187 /*!
188   \qmlproperty int QtQuick2::AnimatedImage::currentFrame
189   \qmlproperty int QtQuick2::AnimatedImage::frameCount
190
191   currentFrame is the frame that is currently visible. By monitoring this property
192   for changes, you can animate other items at the same time as the image.
193
194   frameCount is the number of frames in the animation. For some animation formats,
195   frameCount is unknown and has a value of zero.
196 */
197 int QQuickAnimatedImage::currentFrame() const
198 {
199     Q_D(const QQuickAnimatedImage);
200     if (!d->_movie)
201         return d->preset_currentframe;
202     return d->_movie->currentFrameNumber();
203 }
204
205 void QQuickAnimatedImage::setCurrentFrame(int frame)
206 {
207     Q_D(QQuickAnimatedImage);
208     if (!d->_movie) {
209         d->preset_currentframe = frame;
210         return;
211     }
212     d->_movie->jumpToFrame(frame);
213 }
214
215 int QQuickAnimatedImage::frameCount() const
216 {
217     Q_D(const QQuickAnimatedImage);
218     if (!d->_movie)
219         return 0;
220     return d->_movie->frameCount();
221 }
222
223 void QQuickAnimatedImage::setSource(const QUrl &url)
224 {
225     Q_D(QQuickAnimatedImage);
226     if (url == d->url)
227         return;
228
229     if (d->reply) {
230         d->reply->deleteLater();
231         d->reply = 0;
232     }
233
234     d->oldPlaying = isPlaying();
235     if (d->_movie) {
236         delete d->_movie;
237         d->_movie = 0;
238     }
239
240     d->url = url;
241     emit sourceChanged(d->url);
242
243     if (isComponentComplete())
244         load();
245 }
246
247 void QQuickAnimatedImage::load()
248 {
249     Q_D(QQuickAnimatedImage);
250
251     if (d->url.isEmpty()) {
252         if (d->progress != 0) {
253             d->progress = 0;
254             emit progressChanged(d->progress);
255         }
256
257         d->setImage(QImage());
258         d->status = Null;
259         emit statusChanged(d->status);
260
261         if (sourceSize() != d->oldSourceSize) {
262             d->oldSourceSize = sourceSize();
263             emit sourceSizeChanged();
264         }
265         if (isPlaying() != d->oldPlaying)
266             emit playingChanged();
267     } else {
268         QString lf = QQmlFile::urlToLocalFileOrQrc(d->url);
269         if (!lf.isEmpty()) {
270             d->_movie = new QMovie(lf);
271             movieRequestFinished();
272         } else {
273             if (d->status != Loading) {
274                 d->status = Loading;
275                 emit statusChanged(d->status);
276             }
277             if (d->progress != 0) {
278                 d->progress = 0;
279                 emit progressChanged(d->progress);
280             }
281             QNetworkRequest req(d->url);
282             req.setAttribute(QNetworkRequest::HttpPipeliningAllowedAttribute, true);
283
284             d->reply = qmlEngine(this)->networkAccessManager()->get(req);
285             QObject::connect(d->reply, SIGNAL(finished()),
286                             this, SLOT(movieRequestFinished()));
287             QObject::connect(d->reply, SIGNAL(downloadProgress(qint64,qint64)),
288                             this, SLOT(requestProgress(qint64,qint64)));
289         }
290     }
291 }
292
293 #define ANIMATEDIMAGE_MAXIMUM_REDIRECT_RECURSION 16
294
295 void QQuickAnimatedImage::movieRequestFinished()
296 {
297     Q_D(QQuickAnimatedImage);
298
299     if (d->reply) {
300         d->redirectCount++;
301         if (d->redirectCount < ANIMATEDIMAGE_MAXIMUM_REDIRECT_RECURSION) {
302             QVariant redirect = d->reply->attribute(QNetworkRequest::RedirectionTargetAttribute);
303             if (redirect.isValid()) {
304                 QUrl url = d->reply->url().resolved(redirect.toUrl());
305                 d->reply->deleteLater();
306                 setSource(url);
307                 return;
308             }
309         }
310
311         d->redirectCount=0;
312         d->_movie = new QMovie(d->reply);
313     }
314
315     if (!d->_movie->isValid()) {
316         qmlInfo(this) << "Error Reading Animated Image File " << d->url.toString();
317         delete d->_movie;
318         d->_movie = 0;
319         d->setImage(QImage());
320         if (d->progress != 0) {
321             d->progress = 0;
322             emit progressChanged(d->progress);
323         }
324         d->status = Error;
325         emit statusChanged(d->status);
326
327         if (sourceSize() != d->oldSourceSize) {
328             d->oldSourceSize = sourceSize();
329             emit sourceSizeChanged();
330         }
331         if (isPlaying() != d->oldPlaying)
332             emit playingChanged();
333         return;
334     }
335
336     connect(d->_movie, SIGNAL(stateChanged(QMovie::MovieState)),
337             this, SLOT(playingStatusChanged()));
338     connect(d->_movie, SIGNAL(frameChanged(int)),
339             this, SLOT(movieUpdate()));
340     d->_movie->setCacheMode(QMovie::CacheAll);
341
342     d->status = Ready;
343     emit statusChanged(d->status);
344
345     if (d->progress != 1.0) {
346         d->progress = 1.0;
347         emit progressChanged(d->progress);
348     }
349
350     bool pausedAtStart = d->paused;
351     if (d->playing) {
352         d->_movie->start();
353     }
354     if (pausedAtStart)
355         d->_movie->setPaused(true);
356     if (d->paused || !d->playing) {
357         d->_movie->jumpToFrame(d->preset_currentframe);
358         d->preset_currentframe = 0;
359     }
360     d->setImage(d->_movie->currentPixmap().toImage());
361
362     if (isPlaying() != d->oldPlaying)
363         emit playingChanged();
364     if (sourceSize() != d->oldSourceSize) {
365         d->oldSourceSize = sourceSize();
366         emit sourceSizeChanged();
367     }
368 }
369
370 void QQuickAnimatedImage::movieUpdate()
371 {
372     Q_D(QQuickAnimatedImage);
373
374     if (d->_movie) {
375         d->setImage(d->_movie->currentPixmap().toImage());
376         emit frameChanged();
377     }
378 }
379
380 void QQuickAnimatedImage::playingStatusChanged()
381 {
382     Q_D(QQuickAnimatedImage);
383
384     if ((d->_movie->state() != QMovie::NotRunning) != d->playing) {
385         d->playing = (d->_movie->state() != QMovie::NotRunning);
386         emit playingChanged();
387     }
388     if ((d->_movie->state() == QMovie::Paused) != d->paused) {
389         d->paused = (d->_movie->state() == QMovie::Paused);
390         emit pausedChanged();
391     }
392 }
393
394 QSize QQuickAnimatedImage::sourceSize()
395 {
396     Q_D(QQuickAnimatedImage);
397     if (!d->_movie)
398         return QSize(0, 0);
399     return QSize(d->_movie->currentPixmap().size());
400 }
401
402 void QQuickAnimatedImage::componentComplete()
403 {
404     QQuickItem::componentComplete(); // NOT QQuickImage
405     load();
406 }
407
408 QT_END_NAMESPACE
409
410 #endif // QT_NO_MOVIE