]> git.saurik.com Git - wxWidgets.git/blob - src/gtk/animate.cpp
cleanup
[wxWidgets.git] / src / gtk / animate.cpp
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
12 // For compilers that support precompilation, includes "wx.h".
13 #include "wx/wxprec.h"
14
15 #if wxUSE_ANIMATIONCTRL && !defined(__WXUNIVERSAL__)
16
17 #include "wx/animate.h"
18
19 #ifndef WX_PRECOMP
20 #include "wx/image.h"
21 #include "wx/log.h"
22 #include "wx/stream.h"
23 #endif
24
25 #include <gtk/gtk.h>
26
27
28 // ============================================================================
29 // implementation
30 // ============================================================================
31
32 void gdk_pixbuf_area_updated(GdkPixbufLoader *loader,
33 gint WXUNUSED(x),
34 gint WXUNUSED(y),
35 gint WXUNUSED(width),
36 gint WXUNUSED(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
52 IMPLEMENT_DYNAMIC_CLASS(wxAnimation, wxAnimationBase)
53
54 wxAnimation::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
62 wxAnimation::wxAnimation(GdkPixbufAnimation *p)
63 {
64 m_pixbuf = p;
65 if ( m_pixbuf )
66 g_object_ref(m_pixbuf);
67 }
68
69 wxAnimation& 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
82 bool wxAnimation::LoadFile(const wxString &name, wxAnimationType WXUNUSED(type))
83 {
84 UnRef();
85 m_pixbuf = gdk_pixbuf_animation_new_from_file(name.fn_str(), NULL);
86 return IsOk();
87 }
88
89 bool 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:
105 anim_type[0] = '\0';
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
126 guchar buf[2048];
127 while (stream.IsOk())
128 {
129 // read a chunk of data
130 stream.Read(buf, sizeof(buf));
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 }
147
148 // wait until we get the last area_updated signal
149 return true;
150 }
151
152 wxImage wxAnimation::GetFrame(unsigned int WXUNUSED(frame)) const
153 {
154 return wxNullImage;
155 }
156
157 wxSize wxAnimation::GetSize() const
158 {
159 return wxSize(gdk_pixbuf_animation_get_width(m_pixbuf),
160 gdk_pixbuf_animation_get_height(m_pixbuf));
161 }
162
163 void wxAnimation::UnRef()
164 {
165 if (m_pixbuf)
166 g_object_unref(m_pixbuf);
167 m_pixbuf = NULL;
168 }
169
170 void wxAnimation::SetPixbuf(GdkPixbufAnimation* p)
171 {
172 UnRef();
173 m_pixbuf = p;
174 if (m_pixbuf)
175 g_object_ref(m_pixbuf);
176 }
177
178 //-----------------------------------------------------------------------------
179 // wxAnimationCtrl
180 //-----------------------------------------------------------------------------
181
182 IMPLEMENT_DYNAMIC_CLASS(wxAnimationCtrl, wxAnimationCtrlBase)
183 BEGIN_EVENT_TABLE(wxAnimationCtrl, wxAnimationCtrlBase)
184 EVT_TIMER(wxID_ANY, wxAnimationCtrl::OnTimer)
185 END_EVENT_TABLE()
186
187 void wxAnimationCtrl::Init()
188 {
189 m_anim = NULL;
190 m_iter = NULL;
191 m_bPlaying = false;
192 }
193
194 bool 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 {
201 if (!PreCreation( parent, pos, size ) ||
202 !base_type::CreateBase(parent, id, pos, size, style & wxWINDOW_STYLE_MASK,
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();
212 gtk_widget_show(m_widget);
213
214 m_parent->DoAddChild( this );
215
216 PostCreation(size);
217 SetInitialSize(size);
218
219 if (anim.IsOk())
220 SetAnimation(anim);
221
222 // init the timer used for animation
223 m_timer.SetOwner(this);
224
225 return true;
226 }
227
228 wxAnimationCtrl::~wxAnimationCtrl()
229 {
230 ResetAnim();
231 ResetIter();
232 }
233
234 bool 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
244 void 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();
263 }
264
265 DisplayStaticImage();
266 }
267
268 void 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
277 SetSize(w, h);
278 }
279
280 void wxAnimationCtrl::ResetAnim()
281 {
282 if (m_anim)
283 g_object_unref(m_anim);
284 m_anim = NULL;
285 }
286
287 void wxAnimationCtrl::ResetIter()
288 {
289 if (m_iter)
290 g_object_unref(m_iter);
291 m_iter = NULL;
292 }
293
294 bool 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
313 void wxAnimationCtrl::Stop()
314 {
315 // leave current frame displayed until Play() is called again
316 if (IsPlaying())
317 m_timer.Stop();
318 m_bPlaying = false;
319
320 ResetIter();
321 DisplayStaticImage();
322 }
323
324 void wxAnimationCtrl::DisplayStaticImage()
325 {
326 wxASSERT(!IsPlaying());
327
328 // m_bmpStaticReal will be updated only if necessary...
329 UpdateStaticImage();
330
331 if (m_bmpStaticReal.IsOk())
332 {
333 // show inactive bitmap
334 GdkBitmap *mask = (GdkBitmap *) NULL;
335 if (m_bmpStaticReal.GetMask())
336 mask = m_bmpStaticReal.GetMask()->GetBitmap();
337
338 if (m_bmpStaticReal.HasPixbuf())
339 {
340 gtk_image_set_from_pixbuf(GTK_IMAGE(m_widget),
341 m_bmpStaticReal.GetPixbuf());
342 }
343 else
344 {
345 gtk_image_set_from_pixmap(GTK_IMAGE(m_widget),
346 m_bmpStaticReal.GetPixmap(), mask);
347 }
348 }
349 else
350 {
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 }
362 }
363 }
364
365 bool 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
374 wxSize 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
385 void 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
397 gtk_image_set_from_pixbuf(GTK_IMAGE(m_widget), newpix);
398 g_object_unref(newpix);
399 }
400
401 bool 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;
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
415 return true;
416 }
417
418
419 //-----------------------------------------------------------------------------
420 // wxAnimationCtrl - event handlers
421 //-----------------------------------------------------------------------------
422
423 void wxAnimationCtrl::OnTimer(wxTimerEvent& WXUNUSED(ev))
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