1/*
2 This file is part of the KDE libraries
3 SPDX-FileCopyrightText: 2000 Kurt Granroth <granroth@kde.org>
4 SPDX-FileCopyrightText: 2006 Hamish Rodda <rodda@kde.org>
5 SPDX-FileCopyrightText: 2008 Pino Toscano <pino@kde.org>
6
7 SPDX-License-Identifier: LGPL-2.0-only
8*/
9
10#include <kanimatedbutton.h>
11
12#include <QImageReader>
13#include <QMovie>
14#include <QPainter>
15#include <QPixmap>
16#include <QTimer>
17
18class KAnimatedButtonPrivate
19{
20public:
21 KAnimatedButtonPrivate(KAnimatedButton *qq)
22 : q(qq)
23 {
24 }
25
26 void updateIcons();
27 void updateCurrentIcon();
28 void movieFrameChanged(int number);
29 void movieFinished();
30 void timerUpdate();
31
32 KAnimatedButton *const q;
33 QMovie *movie = nullptr;
34
35 int frames;
36 int current_frame;
37 QPixmap pixmap;
38 QTimer timer;
39 QString icon_path;
40 QList<QPixmap *> framesCache; // We keep copies of each frame so that
41 // the icon code can properly cache them in QPixmapCache,
42 // and not fill it up with dead copies
43};
44
45KAnimatedButton::KAnimatedButton(QWidget *parent)
46 : QToolButton(parent)
47 , d(new KAnimatedButtonPrivate(this))
48{
49 connect(sender: &d->timer, signal: &QTimer::timeout, context: this, slot: [this]() {
50 d->timerUpdate();
51 });
52}
53
54KAnimatedButton::~KAnimatedButton()
55{
56 d->timer.stop();
57 qDeleteAll(c: d->framesCache);
58 delete d->movie;
59}
60
61void KAnimatedButton::start()
62{
63 if (d->movie) {
64 d->movie->start();
65 } else {
66 d->current_frame = 0;
67 d->timer.start(msec: 50);
68 }
69}
70
71void KAnimatedButton::stop()
72{
73 if (d->movie) {
74 d->movie->stop();
75 d->movie->jumpToFrame(frameNumber: 0);
76 d->movieFrameChanged(number: 0);
77 } else {
78 d->current_frame = 0;
79 d->timer.stop();
80 d->updateCurrentIcon();
81 }
82}
83
84void KAnimatedButton::setAnimationPath(const QString &path)
85{
86 if (d->icon_path == path) {
87 return;
88 }
89
90 d->timer.stop();
91 d->icon_path = path;
92 d->updateIcons();
93}
94
95QString KAnimatedButton::animationPath() const
96{
97 return d->icon_path;
98}
99
100void KAnimatedButtonPrivate::timerUpdate()
101{
102 if (!q->isVisible()) {
103 return;
104 }
105
106 current_frame++;
107 if (current_frame == frames) {
108 current_frame = 0;
109 }
110
111 updateCurrentIcon();
112}
113
114void KAnimatedButtonPrivate::updateCurrentIcon()
115{
116 if (pixmap.isNull()) {
117 return;
118 }
119
120 QPixmap *frame = framesCache[current_frame];
121 if (!frame) {
122 const int icon_size = qMin(a: pixmap.width(), b: pixmap.height());
123 const int row_size = pixmap.width() / icon_size;
124 const int row = current_frame / row_size;
125 const int column = current_frame % row_size;
126 frame = new QPixmap(icon_size, icon_size);
127 frame->fill(fillColor: Qt::transparent);
128 QPainter p(frame);
129 p.drawPixmap(p: QPoint(0, 0), pm: pixmap, sr: QRect(column * icon_size, row * icon_size, icon_size, icon_size));
130 p.end();
131 framesCache[current_frame] = frame;
132 }
133
134 q->setIcon(QIcon(*frame));
135}
136
137void KAnimatedButtonPrivate::movieFrameChanged(int number)
138{
139 Q_UNUSED(number);
140 q->setIcon(QIcon(movie->currentPixmap()));
141}
142
143void KAnimatedButtonPrivate::movieFinished()
144{
145 // if not running, make it loop
146 if (movie->state() == QMovie::NotRunning) {
147 movie->start();
148 }
149}
150
151void KAnimatedButtonPrivate::updateIcons()
152{
153 pixmap = QPixmap();
154 QMovie *newMovie = nullptr;
155 QImageReader reader(icon_path);
156 if (QMovie::supportedFormats().contains(t: reader.format())) {
157 newMovie = new QMovie(icon_path);
158 frames = 0;
159 newMovie->setCacheMode(QMovie::CacheAll);
160 QObject::connect(sender: newMovie, signal: &QMovie::frameChanged, context: q, slot: [this](int value) {
161 movieFrameChanged(number: value);
162 });
163 QObject::connect(sender: newMovie, signal: &QMovie::finished, context: q, slot: [this] {
164 movieFinished();
165 });
166 } else {
167 const QPixmap pix(icon_path);
168 if (pix.isNull()) {
169 return;
170 }
171
172 const int icon_size = qMin(a: pix.width(), b: pix.height());
173 if ((pix.height() % icon_size != 0) || (pix.width() % icon_size != 0)) {
174 return;
175 }
176
177 frames = (pix.height() / icon_size) * (pix.width() / icon_size);
178 pixmap = pix;
179 }
180
181 current_frame = 0;
182 qDeleteAll(c: framesCache);
183 framesCache.fill(t: nullptr);
184 framesCache.resize(size: frames);
185 delete movie;
186 movie = newMovie;
187
188 if (movie) {
189 movie->jumpToFrame(frameNumber: 0);
190 movieFrameChanged(number: 0);
191 } else {
192 updateCurrentIcon();
193 }
194}
195
196#include "moc_kanimatedbutton.cpp"
197

source code of kwidgetsaddons/src/kanimatedbutton.cpp