1/****************************************************************************
2**
3** Copyright (C) 2016 The Qt Company Ltd.
4** Contact: https://www.qt.io/licensing/
5**
6** This file is part of the QtCore module of the Qt Toolkit.
7**
8** $QT_BEGIN_LICENSE:LGPL$
9** Commercial License Usage
10** Licensees holding valid commercial Qt licenses may use this file in
11** accordance with the commercial license agreement provided with the
12** Software or, alternatively, in accordance with the terms contained in
13** a written agreement between you and The Qt Company. For licensing terms
14** and conditions see https://www.qt.io/terms-conditions. For further
15** information use the contact form at https://www.qt.io/contact-us.
16**
17** GNU Lesser General Public License Usage
18** Alternatively, this file may be used under the terms of the GNU Lesser
19** General Public License version 3 as published by the Free Software
20** Foundation and appearing in the file LICENSE.LGPL3 included in the
21** packaging of this file. Please review the following information to
22** ensure the GNU Lesser General Public License version 3 requirements
23** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
24**
25** GNU General Public License Usage
26** Alternatively, this file may be used under the terms of the GNU
27** General Public License version 2.0 or (at your option) the GNU General
28** Public license version 3 or any later version approved by the KDE Free
29** Qt Foundation. The licenses are as published by the Free Software
30** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
31** included in the packaging of this file. Please review the following
32** information to ensure the GNU General Public License requirements will
33** be met: https://www.gnu.org/licenses/gpl-2.0.html and
34** https://www.gnu.org/licenses/gpl-3.0.html.
35**
36** $QT_END_LICENSE$
37**
38****************************************************************************/
39
40/*!
41 \class QAnimationGroup
42 \inmodule QtCore
43 \brief The QAnimationGroup class is an abstract base class for groups of animations.
44 \since 4.6
45 \ingroup animation
46
47 An animation group is a container for animations (subclasses of
48 QAbstractAnimation). A group is usually responsible for managing
49 the \l{QAbstractAnimation::State}{state} of its animations, i.e.,
50 it decides when to start, stop, resume, and pause them. Currently,
51 Qt provides two such groups: QParallelAnimationGroup and
52 QSequentialAnimationGroup. Look up their class descriptions for
53 details.
54
55 Since QAnimationGroup inherits from QAbstractAnimation, you can
56 combine groups, and easily construct complex animation graphs.
57 You can query QAbstractAnimation for the group it belongs to
58 (using the \l{QAbstractAnimation::}{group()} function).
59
60 To start a top-level animation group, you simply use the
61 \l{QAbstractAnimation::}{start()} function from
62 QAbstractAnimation. By a top-level animation group, we think of a
63 group that itself is not contained within another group. Starting
64 sub groups directly is not supported, and may lead to unexpected
65 behavior.
66
67 \omit OK, we'll put in a snippet on this here \endomit
68
69 QAnimationGroup provides methods for adding and retrieving
70 animations. Besides that, you can remove animations by calling
71 \l removeAnimation(), and clear the animation group by calling
72 clear(). You may keep track of changes in the group's
73 animations by listening to QEvent::ChildAdded and
74 QEvent::ChildRemoved events.
75
76 \omit OK, let's find a snippet here as well. \endomit
77
78 QAnimationGroup takes ownership of the animations it manages, and
79 ensures that they are deleted when the animation group is deleted.
80
81 \sa QAbstractAnimation, QVariantAnimation, {The Animation Framework}
82*/
83
84#include "qanimationgroup.h"
85#include <QtCore/qdebug.h>
86#include <QtCore/qcoreevent.h>
87#include "qanimationgroup_p.h"
88
89#include <algorithm>
90
91QT_BEGIN_NAMESPACE
92
93
94/*!
95 Constructs a QAnimationGroup.
96 \a parent is passed to QObject's constructor.
97*/
98QAnimationGroup::QAnimationGroup(QObject *parent)
99 : QAbstractAnimation(*new QAnimationGroupPrivate, parent)
100{
101}
102
103/*!
104 \internal
105*/
106QAnimationGroup::QAnimationGroup(QAnimationGroupPrivate &dd, QObject *parent)
107 : QAbstractAnimation(dd, parent)
108{
109}
110
111/*!
112 Destroys the animation group. It will also destroy all its animations.
113*/
114QAnimationGroup::~QAnimationGroup()
115{
116 Q_D(QAnimationGroup);
117 // We need to clear the animations now while we are still a valid QAnimationGroup.
118 // If we wait until ~QObject() the QAbstractAnimation's pointer back to us would
119 // point to a QObject, not a valid QAnimationGroup.
120 d->clear(true);
121}
122
123/*!
124 Returns a pointer to the animation at \a index in this group. This
125 function is useful when you need access to a particular animation. \a
126 index is between 0 and animationCount() - 1.
127
128 \sa animationCount(), indexOfAnimation()
129*/
130QAbstractAnimation *QAnimationGroup::animationAt(int index) const
131{
132 Q_D(const QAnimationGroup);
133
134 if (index < 0 || index >= d->animations.size()) {
135 qWarning("QAnimationGroup::animationAt: index is out of bounds");
136 return nullptr;
137 }
138
139 return d->animations.at(index);
140}
141
142
143/*!
144 Returns the number of animations managed by this group.
145
146 \sa indexOfAnimation(), addAnimation(), animationAt()
147*/
148int QAnimationGroup::animationCount() const
149{
150 Q_D(const QAnimationGroup);
151 return d->animations.size();
152}
153
154/*!
155 Returns the index of \a animation. The returned index can be passed
156 to the other functions that take an index as an argument.
157
158 \sa insertAnimation(), animationAt(), takeAnimation()
159*/
160int QAnimationGroup::indexOfAnimation(QAbstractAnimation *animation) const
161{
162 Q_D(const QAnimationGroup);
163 return d->animations.indexOf(animation);
164}
165
166/*!
167 Adds \a animation to this group. This will call insertAnimation with
168 index equals to animationCount().
169
170 \note The group takes ownership of the animation.
171
172 \sa removeAnimation()
173*/
174void QAnimationGroup::addAnimation(QAbstractAnimation *animation)
175{
176 Q_D(QAnimationGroup);
177 insertAnimation(d->animations.count(), animation);
178}
179
180/*!
181 Inserts \a animation into this animation group at \a index.
182 If \a index is 0 the animation is inserted at the beginning.
183 If \a index is animationCount(), the animation is inserted at the end.
184
185 \note The group takes ownership of the animation.
186
187 \sa takeAnimation(), addAnimation(), indexOfAnimation(), removeAnimation()
188*/
189void QAnimationGroup::insertAnimation(int index, QAbstractAnimation *animation)
190{
191 Q_D(QAnimationGroup);
192
193 if (index < 0 || index > d->animations.size()) {
194 qWarning("QAnimationGroup::insertAnimation: index is out of bounds");
195 return;
196 }
197
198 if (QAnimationGroup *oldGroup = animation->group()) {
199 oldGroup->removeAnimation(animation);
200 // ensure we don't insert out of bounds if oldGroup == this
201 index = qMin(index, d->animations.size());
202 }
203
204 d->animations.insert(index, animation);
205 QAbstractAnimationPrivate::get(animation)->group = this;
206 // this will make sure that ChildAdded event is sent to 'this'
207 animation->setParent(this);
208 d->animationInsertedAt(index);
209}
210
211/*!
212 Removes \a animation from this group. The ownership of \a animation is
213 transferred to the caller.
214
215 \sa takeAnimation(), insertAnimation(), addAnimation()
216*/
217void QAnimationGroup::removeAnimation(QAbstractAnimation *animation)
218{
219 Q_D(QAnimationGroup);
220
221 if (!animation) {
222 qWarning("QAnimationGroup::remove: cannot remove null animation");
223 return;
224 }
225 int index = d->animations.indexOf(animation);
226 if (index == -1) {
227 qWarning("QAnimationGroup::remove: animation is not part of this group");
228 return;
229 }
230
231 takeAnimation(index);
232}
233
234/*!
235 Returns the animation at \a index and removes it from the animation group.
236
237 \note The ownership of the animation is transferred to the caller.
238
239 \sa removeAnimation(), addAnimation(), insertAnimation(), indexOfAnimation()
240*/
241QAbstractAnimation *QAnimationGroup::takeAnimation(int index)
242{
243 Q_D(QAnimationGroup);
244 if (index < 0 || index >= d->animations.size()) {
245 qWarning("QAnimationGroup::takeAnimation: no animation at index %d", index);
246 return nullptr;
247 }
248 QAbstractAnimation *animation = d->animations.at(index);
249 QAbstractAnimationPrivate::get(animation)->group = nullptr;
250 // ### removing from list before doing setParent to avoid inifinite recursion
251 // in ChildRemoved event
252 d->animations.removeAt(index);
253 animation->setParent(nullptr);
254 d->animationRemoved(index, animation);
255 return animation;
256}
257
258/*!
259 Removes and deletes all animations in this animation group, and resets the current
260 time to 0.
261
262 \sa addAnimation(), removeAnimation()
263*/
264void QAnimationGroup::clear()
265{
266 Q_D(QAnimationGroup);
267 d->clear(false);
268}
269
270/*!
271 \reimp
272*/
273bool QAnimationGroup::event(QEvent *event)
274{
275 Q_D(QAnimationGroup);
276 if (event->type() == QEvent::ChildAdded) {
277 QChildEvent *childEvent = static_cast<QChildEvent *>(event);
278 if (QAbstractAnimation *a = qobject_cast<QAbstractAnimation *>(childEvent->child())) {
279 if (a->group() != this)
280 addAnimation(a);
281 }
282 } else if (event->type() == QEvent::ChildRemoved) {
283 QChildEvent *childEvent = static_cast<QChildEvent *>(event);
284 // You can only rely on the child being a QObject because in the QEvent::ChildRemoved
285 // case it might be called from the destructor. Casting down to QAbstractAnimation then
286 // entails undefined behavior, so compare items as QObjects (which std::find does internally):
287 const QList<QAbstractAnimation *>::const_iterator it
288 = std::find(d->animations.cbegin(), d->animations.cend(), childEvent->child());
289 if (it != d->animations.cend())
290 takeAnimation(it - d->animations.cbegin());
291 }
292 return QAbstractAnimation::event(event);
293}
294
295void QAnimationGroupPrivate::clear(bool onDestruction)
296{
297 const QList<QAbstractAnimation *> animationsCopy = animations; // taking a copy
298 animations.clear();
299 // Clearing backwards so the indices doesn't change while we remove animations.
300 for (int i = animationsCopy.count() - 1; i >= 0; --i) {
301 QAbstractAnimation *animation = animationsCopy.at(i);
302 animation->setParent(nullptr);
303 QAbstractAnimationPrivate::get(animation)->group = nullptr;
304 // If we are in ~QAnimationGroup() it is not safe to called the virtual
305 // animationRemoved method, which can still be a method in a
306 // QAnimationGroupPrivate derived class that assumes q_ptr is still
307 // a valid derived class of QAnimationGroup.
308 if (!onDestruction)
309 animationRemoved(i, animation);
310 delete animation;
311 }
312}
313
314void QAnimationGroupPrivate::animationRemoved(int index, QAbstractAnimation *)
315{
316 Q_Q(QAnimationGroup);
317 Q_UNUSED(index);
318 if (animations.isEmpty()) {
319 currentTime = 0;
320 q->stop();
321 }
322}
323
324QT_END_NAMESPACE
325
326#include "moc_qanimationgroup.cpp"
327