]> git.saurik.com Git - wxWidgets.git/blame - src/gtk/animate.cpp
Minimum is now GTK+ 2.4
[wxWidgets.git] / src / gtk / animate.cpp
CommitLineData
72045d57
VZ
1/////////////////////////////////////////////////////////////////////////////
2// Name: src/gtk/animate.cpp
3// Purpose: wxAnimation and wxAnimationCtrl
4// Author: Francesco Montorsi
5// Modified By:
6// Created: 24/09/2006
7// Id: $Id$
8// Copyright: (c) Francesco Montorsi
9// Licence: wxWindows licence
10/////////////////////////////////////////////////////////////////////////////
11
72045d57
VZ
12// For compilers that support precompilation, includes "wx.h".
13#include "wx/wxprec.h"
14
5f8047a6 15#if wxUSE_ANIMATIONCTRL && !defined(__WXUNIVERSAL__)
72045d57
VZ
16
17#include "wx/animate.h"
c2f12218
PC
18
19#ifndef WX_PRECOMP
20 #include "wx/image.h"
21 #include "wx/log.h"
22 #include "wx/stream.h"
23#endif
24
72045d57 25#include <gtk/gtk.h>
72045d57
VZ
26
27
28// ============================================================================
29// implementation
30// ============================================================================
31
32void gdk_pixbuf_area_updated(GdkPixbufLoader *loader,
e4161a2a
VZ
33 gint WXUNUSED(x),
34 gint WXUNUSED(y),
35 gint WXUNUSED(width),
36 gint WXUNUSED(height),
72045d57
VZ
37 wxAnimation *anim)
38{
39 if (anim && anim->GetPixbuf() == NULL)
40 {
41 // we need to set the pixbuf only if this is the first time this signal
42 // has been called!
43 anim->SetPixbuf(gdk_pixbuf_loader_get_animation(loader));
44 }
45}
46
47
48//-----------------------------------------------------------------------------
49// wxAnimation
50//-----------------------------------------------------------------------------
51
52IMPLEMENT_DYNAMIC_CLASS(wxAnimation, wxAnimationBase)
53
c2f12218
PC
54wxAnimation::wxAnimation(const wxAnimation& that)
55 : base_type(that)
56{
57 m_pixbuf = that.m_pixbuf;
58 if (m_pixbuf)
59 g_object_ref(m_pixbuf);
60}
61
1afdfc9d
VZ
62wxAnimation::wxAnimation(GdkPixbufAnimation *p)
63{
64 m_pixbuf = p;
65 if ( m_pixbuf )
66 g_object_ref(m_pixbuf);
67}
68
c2f12218
PC
69wxAnimation& wxAnimation::operator=(const wxAnimation& that)
70{
71 if (this != &that)
72 {
73 base_type::operator=(that);
74 UnRef();
75 m_pixbuf = that.m_pixbuf;
76 if (m_pixbuf)
77 g_object_ref(m_pixbuf);
78 }
79 return *this;
80}
81
72045d57
VZ
82bool wxAnimation::LoadFile(const wxString &name, wxAnimationType WXUNUSED(type))
83{
84 UnRef();
86501081 85 m_pixbuf = gdk_pixbuf_animation_new_from_file(name.fn_str(), NULL);
72045d57
VZ
86 return IsOk();
87}
88
89bool wxAnimation::Load(wxInputStream &stream, wxAnimationType type)
90{
91 UnRef();
92
93 char anim_type[12];
94 switch (type)
95 {
96 case wxANIMATION_TYPE_GIF:
97 strcpy(anim_type, "gif");
98 break;
99
100 case wxANIMATION_TYPE_ANI:
101 strcpy(anim_type, "ani");
102 break;
103
104 default:
c2f12218 105 anim_type[0] = '\0';
72045d57
VZ
106 break;
107 }
108
109 // create a GdkPixbufLoader
110 GError *error = NULL;
111 GdkPixbufLoader *loader;
112 if (type != wxANIMATION_TYPE_INVALID && type != wxANIMATION_TYPE_ANY)
113 loader = gdk_pixbuf_loader_new_with_type(anim_type, &error);
114 else
115 loader = gdk_pixbuf_loader_new();
116
117 if (!loader)
118 {
119 wxLogDebug(wxT("Could not create the loader for '%s' animation type"), anim_type);
120 return false;
121 }
122
123 // connect to loader signals
124 g_signal_connect(loader, "area-updated", G_CALLBACK(gdk_pixbuf_area_updated), this);
125
72045d57
VZ
126 guchar buf[2048];
127 while (stream.IsOk())
128 {
129 // read a chunk of data
c2f12218 130 stream.Read(buf, sizeof(buf));
72045d57
VZ
131
132 // fetch all data into the loader
133 if (!gdk_pixbuf_loader_write(loader, buf, stream.LastRead(), &error))
134 {
135 gdk_pixbuf_loader_close(loader, &error);
136 wxLogDebug(wxT("Could not write to the loader"));
137 return false;
138 }
139 }
140
141 // load complete
142 if (!gdk_pixbuf_loader_close(loader, &error))
143 {
144 wxLogDebug(wxT("Could not close the loader"));
145 return false;
146 }
72045d57
VZ
147
148 // wait until we get the last area_updated signal
149 return true;
150}
151
870cf35c 152wxImage wxAnimation::GetFrame(unsigned int WXUNUSED(frame)) const
c2f12218
PC
153{
154 return wxNullImage;
155}
156
157wxSize wxAnimation::GetSize() const
158{
159 return wxSize(gdk_pixbuf_animation_get_width(m_pixbuf),
160 gdk_pixbuf_animation_get_height(m_pixbuf));
161}
162
163void wxAnimation::UnRef()
164{
165 if (m_pixbuf)
166 g_object_unref(m_pixbuf);
167 m_pixbuf = NULL;
168}
169
170void wxAnimation::SetPixbuf(GdkPixbufAnimation* p)
171{
172 UnRef();
173 m_pixbuf = p;
174 if (m_pixbuf)
175 g_object_ref(m_pixbuf);
176}
72045d57
VZ
177
178//-----------------------------------------------------------------------------
179// wxAnimationCtrl
180//-----------------------------------------------------------------------------
181
182IMPLEMENT_DYNAMIC_CLASS(wxAnimationCtrl, wxAnimationCtrlBase)
183BEGIN_EVENT_TABLE(wxAnimationCtrl, wxAnimationCtrlBase)
184 EVT_TIMER(wxID_ANY, wxAnimationCtrl::OnTimer)
185END_EVENT_TABLE()
186
05a98b6d 187void wxAnimationCtrl::Init()
c2f12218
PC
188{
189 m_anim = NULL;
190 m_iter = NULL;
191 m_bPlaying = false;
192}
193
72045d57
VZ
194bool wxAnimationCtrl::Create( wxWindow *parent, wxWindowID id,
195 const wxAnimation& anim,
196 const wxPoint& pos,
197 const wxSize& size,
198 long style,
199 const wxString& name)
200{
72045d57 201 if (!PreCreation( parent, pos, size ) ||
c2f12218 202 !base_type::CreateBase(parent, id, pos, size, style & wxWINDOW_STYLE_MASK,
72045d57
VZ
203 wxDefaultValidator, name))
204 {
205 wxFAIL_MSG( wxT("wxAnimationCtrl creation failed") );
206 return false;
207 }
208
209 SetWindowStyle(style);
210
211 m_widget = gtk_image_new();
10bd1f7d 212 gtk_widget_show(m_widget);
72045d57
VZ
213
214 m_parent->DoAddChild( this );
215
216 PostCreation(size);
170acdc9 217 SetInitialSize(size);
72045d57 218
55ccdb93 219 if (anim.IsOk())
72045d57
VZ
220 SetAnimation(anim);
221
222 // init the timer used for animation
223 m_timer.SetOwner(this);
224
225 return true;
226}
227
228wxAnimationCtrl::~wxAnimationCtrl()
229{
230 ResetAnim();
231 ResetIter();
232}
233
234bool wxAnimationCtrl::LoadFile(const wxString &filename, wxAnimationType type)
235{
236 wxAnimation anim;
237 if (!anim.LoadFile(filename, type))
238 return false;
239
240 SetAnimation(anim);
241 return true;
242}
243
244void wxAnimationCtrl::SetAnimation(const wxAnimation &anim)
245{
246 if (IsPlaying())
247 Stop();
248
249 ResetAnim();
250 ResetIter();
251
252 // copy underlying GdkPixbuf object
253 m_anim = anim.GetPixbuf();
254
255 // m_anim may be null in case wxNullAnimation has been passed
256 if (m_anim)
257 {
258 // add a reference to the GdkPixbufAnimation
259 g_object_ref(m_anim);
260
261 if (!this->HasFlag(wxAC_NO_AUTORESIZE))
262 FitToAnimation();
72045d57 263 }
8e458bb5
RR
264
265 DisplayStaticImage();
72045d57
VZ
266}
267
268void wxAnimationCtrl::FitToAnimation()
269{
270 if (!m_anim)
271 return;
272
273 int w = gdk_pixbuf_animation_get_width(m_anim),
274 h = gdk_pixbuf_animation_get_height(m_anim);
275
276 // update our size to fit animation
1afdfc9d 277 SetSize(w, h);
72045d57
VZ
278}
279
c2f12218
PC
280void wxAnimationCtrl::ResetAnim()
281{
282 if (m_anim)
283 g_object_unref(m_anim);
284 m_anim = NULL;
285}
286
287void wxAnimationCtrl::ResetIter()
288{
289 if (m_iter)
290 g_object_unref(m_iter);
291 m_iter = NULL;
292}
293
72045d57
VZ
294bool wxAnimationCtrl::Play()
295{
296 if (m_anim == NULL)
297 return false;
298
299 // init the iterator and start a one-shot timer
300 ResetIter();
301 m_iter = gdk_pixbuf_animation_get_iter (m_anim, NULL);
302 m_bPlaying = true;
303
304 // gdk_pixbuf_animation_iter_get_delay_time() may return -1 which means
305 // that the timer should not start
306 int n = gdk_pixbuf_animation_iter_get_delay_time(m_iter);
307 if (n >= 0)
308 m_timer.Start(n, true);
309
310 return true;
311}
312
313void wxAnimationCtrl::Stop()
314{
315 // leave current frame displayed until Play() is called again
316 if (IsPlaying())
317 m_timer.Stop();
318 m_bPlaying = false;
8e458bb5
RR
319
320 ResetIter();
321 DisplayStaticImage();
322}
323
8e458bb5
RR
324void wxAnimationCtrl::DisplayStaticImage()
325{
326 wxASSERT(!IsPlaying());
327
1bd2ceb5
RR
328 // m_bmpStaticReal will be updated only if necessary...
329 UpdateStaticImage();
330
331 if (m_bmpStaticReal.IsOk())
8e458bb5
RR
332 {
333 // show inactive bitmap
334 GdkBitmap *mask = (GdkBitmap *) NULL;
1bd2ceb5
RR
335 if (m_bmpStaticReal.GetMask())
336 mask = m_bmpStaticReal.GetMask()->GetBitmap();
8e458bb5 337
1bd2ceb5 338 if (m_bmpStaticReal.HasPixbuf())
8e458bb5
RR
339 {
340 gtk_image_set_from_pixbuf(GTK_IMAGE(m_widget),
1bd2ceb5 341 m_bmpStaticReal.GetPixbuf());
8e458bb5
RR
342 }
343 else
344 {
345 gtk_image_set_from_pixmap(GTK_IMAGE(m_widget),
1bd2ceb5 346 m_bmpStaticReal.GetPixmap(), mask);
8e458bb5
RR
347 }
348 }
349 else
350 {
98635ac6
VZ
351 if (m_anim)
352 {
353 // even if not clearly documented, gdk_pixbuf_animation_get_static_image()
354 // always returns the first frame of the animation
355 gtk_image_set_from_pixbuf(GTK_IMAGE(m_widget),
356 gdk_pixbuf_animation_get_static_image(m_anim));
357 }
358 else
359 {
360 ClearToBackgroundColour();
361 }
8e458bb5 362 }
72045d57
VZ
363}
364
365bool wxAnimationCtrl::IsPlaying() const
366{
367 // NB: we cannot just return m_timer.IsRunning() as this would not
368 // be safe as e.g. if we are displaying a frame forever,
369 // then we are "officially" still playing the animation, but
370 // the timer is not running anymore...
371 return m_bPlaying;
372}
373
374wxSize wxAnimationCtrl::DoGetBestSize() const
375{
376 if (m_anim && !this->HasFlag(wxAC_NO_AUTORESIZE))
377 {
378 return wxSize(gdk_pixbuf_animation_get_width(m_anim),
379 gdk_pixbuf_animation_get_height(m_anim));
380 }
381
382 return wxSize(100,100);
383}
384
385void wxAnimationCtrl::ClearToBackgroundColour()
386{
387 wxSize sz = GetClientSize();
388 GdkPixbuf *newpix = gdk_pixbuf_new(GDK_COLORSPACE_RGB, false, 8,
389 sz.GetWidth(), sz.GetHeight());
390 if (!newpix)
391 return;
392
393 wxColour clr = GetBackgroundColour();
394 guint32 col = (clr.Red() << 24) | (clr.Green() << 16) | (clr.Blue() << 8);
395 gdk_pixbuf_fill(newpix, col);
396
72045d57
VZ
397 gtk_image_set_from_pixbuf(GTK_IMAGE(m_widget), newpix);
398 g_object_unref(newpix);
399}
400
401bool wxAnimationCtrl::SetBackgroundColour( const wxColour &colour )
402{
403 // wxWindowGTK::SetBackgroundColour works but since our m_widget is a GtkImage
404 // it won't show the background colour unlike the user would expect.
405 // Thus we clear the GtkImage contents to the background colour...
406 if (!wxControl::SetBackgroundColour(colour))
407 return false;
1afdfc9d
VZ
408
409 // if not playing the change must take place immediately but
410 // remember that the inactive bitmap has higher priority over the background
411 // colour; DisplayStaticImage() will handle that
412 if ( !IsPlaying() )
413 DisplayStaticImage();
414
72045d57
VZ
415 return true;
416}
417
418
419//-----------------------------------------------------------------------------
420// wxAnimationCtrl - event handlers
421//-----------------------------------------------------------------------------
422
e4161a2a 423void wxAnimationCtrl::OnTimer(wxTimerEvent& WXUNUSED(ev))
72045d57
VZ
424{
425 wxASSERT(m_iter != NULL);
426
427 // gdk_pixbuf_animation_iter_advance() will automatically restart
428 // the animation, if necessary and we have no way to know !!
429 if (gdk_pixbuf_animation_iter_advance(m_iter, NULL))
430 {
431 // start a new one-shot timer
432 int n = gdk_pixbuf_animation_iter_get_delay_time(m_iter);
433 if (n >= 0)
434 m_timer.Start(n, true);
435
436 gtk_image_set_from_pixbuf(GTK_IMAGE(m_widget),
437 gdk_pixbuf_animation_iter_get_pixbuf(m_iter));
438 }
439 else
440 {
441 // no need to update the m_widget yet
442 m_timer.Start(10, true);
443 }
444}
445
446#endif // wxUSE_ANIMATIONCTRL