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