Initial import from the monolithic Qt.
[profile/ivi/qtdeclarative.git] / src / declarative / graphicsitems / qdeclarativeanimatedimage.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 ** No Commercial Usage
11 ** This file contains pre-release code and may not be distributed.
12 ** You may use this file in accordance with the terms and conditions
13 ** contained in the Technology Preview License Agreement accompanying
14 ** this package.
15 **
16 ** GNU Lesser General Public License Usage
17 ** Alternatively, this file may be used under the terms of the GNU Lesser
18 ** General Public License version 2.1 as published by the Free Software
19 ** Foundation and appearing in the file LICENSE.LGPL included in the
20 ** packaging of this file.  Please review the following information to
21 ** ensure the GNU Lesser General Public License version 2.1 requirements
22 ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
23 **
24 ** In addition, as a special exception, Nokia gives you certain additional
25 ** rights.  These rights are described in the Nokia Qt LGPL Exception
26 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
27 **
28 ** If you have questions regarding the use of this file, please contact
29 ** Nokia at qt-info@nokia.com.
30 **
31 **
32 **
33 **
34 **
35 **
36 **
37 **
38 ** $QT_END_LICENSE$
39 **
40 ****************************************************************************/
41
42 #include "private/qdeclarativeanimatedimage_p.h"
43 #include "private/qdeclarativeanimatedimage_p_p.h"
44
45 #ifndef QT_NO_MOVIE
46
47 #include <qdeclarativeinfo.h>
48 #include <private/qdeclarativeengine_p.h>
49
50 #include <QMovie>
51 #include <QNetworkRequest>
52 #include <QNetworkReply>
53
54 QT_BEGIN_NAMESPACE
55
56 /*!
57     \qmlclass AnimatedImage QDeclarativeAnimatedImage
58     \inherits Image
59     \since 4.7
60     \ingroup basic-visual-elements
61
62     The AnimatedImage element extends the features of the \l Image element, providing
63     a way to play animations stored as images containing a series of frames,
64     such as those stored in GIF files.
65
66     Information about the current frame and totla length of the animation can be
67     obtained using the \l currentFrame and \l frameCount properties. You can
68     start, pause and stop the animation by changing the values of the \l playing
69     and \l paused properties.
70
71     The full list of supported formats can be determined with QMovie::supportedFormats().
72
73     \section1 Example Usage
74
75     \beginfloatleft
76     \image animatedimageitem.gif
77     \endfloat
78
79     The following QML shows how to display an animated image and obtain information
80     about its state, such as the current frame and total number of frames.
81     The result is an animated image with a simple progress indicator underneath it.
82
83     \clearfloat
84     \snippet doc/src/snippets/declarative/animatedimage.qml document
85
86     \sa BorderImage, Image
87 */
88
89 /*!
90     \qmlproperty bool AnimatedImage::cache
91     \since Quick 1.1
92
93     Specifies whether the image should be cached. The default value is
94     true. Setting \a cache to false is useful when dealing with large images,
95     to make sure that they aren't cached at the expense of small 'ui element' images.
96 */
97
98 /*!
99     \qmlproperty bool AnimatedImage::mirror
100     \since Quick 1.1
101
102     This property holds whether the image should be horizontally inverted
103     (effectively displaying a mirrored image).
104
105     The default value is false.
106 */
107
108 QDeclarativeAnimatedImage::QDeclarativeAnimatedImage(QDeclarativeItem *parent)
109     : QDeclarativeImage(*(new QDeclarativeAnimatedImagePrivate), parent)
110 {
111 }
112
113 QDeclarativeAnimatedImage::~QDeclarativeAnimatedImage()
114 {
115     Q_D(QDeclarativeAnimatedImage);
116     delete d->_movie;
117 }
118
119 /*!
120   \qmlproperty bool AnimatedImage::paused
121   This property holds whether the animated image is paused.
122
123   By default, this property is false. Set it to true when you want to pause
124   the animation.
125 */
126 bool QDeclarativeAnimatedImage::isPaused() const
127 {
128     Q_D(const QDeclarativeAnimatedImage);
129     if(!d->_movie)
130         return false;
131     return d->_movie->state()==QMovie::Paused;
132 }
133
134 void QDeclarativeAnimatedImage::setPaused(bool pause)
135 {
136     Q_D(QDeclarativeAnimatedImage);
137     if(pause == d->paused)
138         return;
139     d->paused = pause;
140     if(!d->_movie)
141         return;
142     d->_movie->setPaused(pause);
143 }
144 /*!
145   \qmlproperty bool AnimatedImage::playing
146   This property holds whether the animated image is playing.
147
148   By default, this property is true, meaning that the animation
149   will start playing immediately.
150 */
151 bool QDeclarativeAnimatedImage::isPlaying() const
152 {
153     Q_D(const QDeclarativeAnimatedImage);
154     if (!d->_movie)
155         return false;
156     return d->_movie->state()!=QMovie::NotRunning;
157 }
158
159 void QDeclarativeAnimatedImage::setPlaying(bool play)
160 {
161     Q_D(QDeclarativeAnimatedImage);
162     if(play == d->playing)
163         return;
164     d->playing = play;
165     if (!d->_movie)
166         return;
167     if (play)
168         d->_movie->start();
169     else
170         d->_movie->stop();
171 }
172
173 /*!
174   \qmlproperty int AnimatedImage::currentFrame
175   \qmlproperty int AnimatedImage::frameCount
176
177   currentFrame is the frame that is currently visible. By monitoring this property
178   for changes, you can animate other items at the same time as the image.
179
180   frameCount is the number of frames in the animation. For some animation formats,
181   frameCount is unknown and has a value of zero.
182 */
183 int QDeclarativeAnimatedImage::currentFrame() const
184 {
185     Q_D(const QDeclarativeAnimatedImage);
186     if (!d->_movie)
187         return d->preset_currentframe;
188     return d->_movie->currentFrameNumber();
189 }
190
191 void QDeclarativeAnimatedImage::setCurrentFrame(int frame)
192 {
193     Q_D(QDeclarativeAnimatedImage);
194     if (!d->_movie) {
195         d->preset_currentframe = frame;
196         return;
197     }
198     d->_movie->jumpToFrame(frame);
199 }
200
201 int QDeclarativeAnimatedImage::frameCount() const
202 {
203     Q_D(const QDeclarativeAnimatedImage);
204     if (!d->_movie)
205         return 0;
206     return d->_movie->frameCount();
207 }
208
209 void QDeclarativeAnimatedImage::setSource(const QUrl &url)
210 {
211     Q_D(QDeclarativeAnimatedImage);
212     if (url == d->url)
213         return;
214
215     delete d->_movie;
216     d->_movie = 0;
217
218     if (d->reply) {
219         d->reply->deleteLater();
220         d->reply = 0;
221     }
222
223     d->url = url;
224     emit sourceChanged(d->url);
225
226     if (isComponentComplete())
227         load();
228 }
229
230 void QDeclarativeAnimatedImage::load()
231 {
232     Q_D(QDeclarativeAnimatedImage);
233
234     QDeclarativeImageBase::Status oldStatus = d->status;
235     qreal oldProgress = d->progress;
236
237     if (d->url.isEmpty()) {
238         delete d->_movie;
239         d->setPixmap(QPixmap());
240         d->progress = 0;
241         d->status = Null;
242         if (d->status != oldStatus)
243             emit statusChanged(d->status);
244         if (d->progress != oldProgress)
245             emit progressChanged(d->progress);
246     } else {
247 #ifndef QT_NO_LOCALFILE_OPTIMIZED_QML
248         QString lf = QDeclarativeEnginePrivate::urlToLocalFileOrQrc(d->url);
249         if (!lf.isEmpty()) {
250             //### should be unified with movieRequestFinished
251             d->_movie = new QMovie(lf);
252             if (!d->_movie->isValid()){
253                 qmlInfo(this) << "Error Reading Animated Image File " << d->url.toString();
254                 delete d->_movie;
255                 d->_movie = 0;
256                 d->status = Error;
257                 if (d->status != oldStatus)
258                     emit statusChanged(d->status);
259                 return;
260             }
261             connect(d->_movie, SIGNAL(stateChanged(QMovie::MovieState)),
262                     this, SLOT(playingStatusChanged()));
263             connect(d->_movie, SIGNAL(frameChanged(int)),
264                     this, SLOT(movieUpdate()));
265             d->_movie->setCacheMode(QMovie::CacheAll);
266             if(d->playing)
267                 d->_movie->start();
268             else
269                 d->_movie->jumpToFrame(0);
270             if(d->paused)
271                 d->_movie->setPaused(true);
272             d->setPixmap(d->_movie->currentPixmap());
273             d->status = Ready;
274             d->progress = 1.0;
275             if (d->status != oldStatus)
276                 emit statusChanged(d->status);
277             if (d->progress != oldProgress)
278                 emit progressChanged(d->progress);
279             return;
280         }
281 #endif
282         d->status = Loading;
283         d->progress = 0;
284         emit statusChanged(d->status);
285         emit progressChanged(d->progress);
286         QNetworkRequest req(d->url);
287         req.setAttribute(QNetworkRequest::HttpPipeliningAllowedAttribute, true);
288         d->reply = qmlEngine(this)->networkAccessManager()->get(req);
289         QObject::connect(d->reply, SIGNAL(finished()),
290                          this, SLOT(movieRequestFinished()));
291         QObject::connect(d->reply, SIGNAL(downloadProgress(qint64,qint64)),
292                          this, SLOT(requestProgress(qint64,qint64)));
293     }
294 }
295
296 #define ANIMATEDIMAGE_MAXIMUM_REDIRECT_RECURSION 16
297
298 void QDeclarativeAnimatedImage::movieRequestFinished()
299 {
300     Q_D(QDeclarativeAnimatedImage);
301
302     d->redirectCount++;
303     if (d->redirectCount < ANIMATEDIMAGE_MAXIMUM_REDIRECT_RECURSION) {
304         QVariant redirect = d->reply->attribute(QNetworkRequest::RedirectionTargetAttribute);
305         if (redirect.isValid()) {
306             QUrl url = d->reply->url().resolved(redirect.toUrl());
307             d->reply->deleteLater();
308             d->reply = 0;
309             setSource(url);
310             return;
311         }
312     }
313     d->redirectCount=0;
314
315     d->_movie = new QMovie(d->reply);
316     if (!d->_movie->isValid()){
317 #ifndef QT_NO_DEBUG_STREAM
318         qmlInfo(this) << "Error Reading Animated Image File " << d->url;
319 #endif
320         delete d->_movie;
321         d->_movie = 0;
322         d->status = Error;
323         emit statusChanged(d->status);
324         return;
325     }
326     connect(d->_movie, SIGNAL(stateChanged(QMovie::MovieState)),
327             this, SLOT(playingStatusChanged()));
328     connect(d->_movie, SIGNAL(frameChanged(int)),
329             this, SLOT(movieUpdate()));
330     d->_movie->setCacheMode(QMovie::CacheAll);
331     if(d->playing)
332         d->_movie->start();
333     if (d->paused || !d->playing) {
334         d->_movie->jumpToFrame(d->preset_currentframe);
335         d->preset_currentframe = 0;
336     }
337     if(d->paused)
338         d->_movie->setPaused(true);
339     d->setPixmap(d->_movie->currentPixmap());
340     d->status = Ready;
341     emit statusChanged(d->status);
342 }
343
344 void QDeclarativeAnimatedImage::movieUpdate()
345 {
346     Q_D(QDeclarativeAnimatedImage);
347     d->setPixmap(d->_movie->currentPixmap());
348     emit frameChanged();
349 }
350
351 void QDeclarativeAnimatedImage::playingStatusChanged()
352 {
353     Q_D(QDeclarativeAnimatedImage);
354     if((d->_movie->state() != QMovie::NotRunning) != d->playing){
355         d->playing = (d->_movie->state() != QMovie::NotRunning);
356         emit playingChanged();
357     }
358     if((d->_movie->state() == QMovie::Paused) != d->paused){
359         d->playing = (d->_movie->state() == QMovie::Paused);
360         emit pausedChanged();
361     }
362 }
363
364 void QDeclarativeAnimatedImage::componentComplete()
365 {
366     Q_D(QDeclarativeAnimatedImage);
367     QDeclarativeItem::componentComplete(); // NOT QDeclarativeImage
368     if (d->url.isValid())
369         load();
370     if (!d->reply) {
371         setCurrentFrame(d->preset_currentframe);
372         d->preset_currentframe = 0;
373     }
374 }
375
376 QT_END_NAMESPACE
377
378 #endif // QT_NO_MOVIE