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