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: animate.cpp 43898 2006-12-10 14:18:37Z VZ $
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 
gdk_pixbuf_area_updated(GdkPixbufLoader * loader,gint x,gint y,gint width,gint height,wxAnimation * anim)32 void 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 
IMPLEMENT_DYNAMIC_CLASS(wxAnimation,wxAnimationBase)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 
wxAnimation(GdkPixbufAnimation * p)62 wxAnimation::wxAnimation(GdkPixbufAnimation *p)
63 {
64     m_pixbuf = p;
65     if ( m_pixbuf )
66         g_object_ref(m_pixbuf);
67 }
68 
operator =(const wxAnimation & that)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 
LoadFile(const wxString & name,wxAnimationType WXUNUSED (type))82 bool wxAnimation::LoadFile(const wxString &name, wxAnimationType WXUNUSED(type))
83 {
84     UnRef();
85     m_pixbuf = gdk_pixbuf_animation_new_from_file(
86         wxConvFileName->cWX2MB(name), NULL);
87     return IsOk();
88 }
89 
Load(wxInputStream & stream,wxAnimationType type)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 
GetFrame(unsigned int WXUNUSED (frame)) const153 wxImage wxAnimation::GetFrame(unsigned int WXUNUSED(frame)) const
154 {
155     return wxNullImage;
156 }
157 
GetSize() const158 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 
UnRef()164 void wxAnimation::UnRef()
165 {
166     if (m_pixbuf)
167         g_object_unref(m_pixbuf);
168     m_pixbuf = NULL;
169 }
170 
SetPixbuf(GdkPixbufAnimation * p)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 
IMPLEMENT_DYNAMIC_CLASS(wxAnimationCtrl,wxAnimationCtrlBase)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 
Create(wxWindow * parent,wxWindowID id,const wxAnimation & anim,const wxPoint & pos,const wxSize & size,long style,const wxString & name)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     m_needParent = true;
203     m_acceptsFocus = true;
204 
205     if (!PreCreation( parent, pos, size ) ||
206         !base_type::CreateBase(parent, id, pos, size, style & wxWINDOW_STYLE_MASK,
207                                wxDefaultValidator, name))
208     {
209         wxFAIL_MSG( wxT("wxAnimationCtrl creation failed") );
210         return false;
211     }
212 
213     SetWindowStyle(style);
214 
215     m_widget = gtk_image_new();
216     gtk_widget_show( GTK_WIDGET(m_widget) );
217 
218     m_parent->DoAddChild( this );
219 
220     PostCreation(size);
221     SetInitialSize(size);
222 
223     if (anim.IsOk())
224         SetAnimation(anim);
225 
226     // init the timer used for animation
227     m_timer.SetOwner(this);
228 
229     return true;
230 }
231 
~wxAnimationCtrl()232 wxAnimationCtrl::~wxAnimationCtrl()
233 {
234     ResetAnim();
235     ResetIter();
236 }
237 
LoadFile(const wxString & filename,wxAnimationType type)238 bool wxAnimationCtrl::LoadFile(const wxString &filename, wxAnimationType type)
239 {
240     wxAnimation anim;
241     if (!anim.LoadFile(filename, type))
242         return false;
243 
244     SetAnimation(anim);
245     return true;
246 }
247 
SetAnimation(const wxAnimation & anim)248 void wxAnimationCtrl::SetAnimation(const wxAnimation &anim)
249 {
250     if (IsPlaying())
251         Stop();
252 
253     ResetAnim();
254     ResetIter();
255 
256     // copy underlying GdkPixbuf object
257     m_anim = anim.GetPixbuf();
258 
259     // m_anim may be null in case wxNullAnimation has been passed
260     if (m_anim)
261     {
262         // add a reference to the GdkPixbufAnimation
263         g_object_ref(m_anim);
264 
265         if (!this->HasFlag(wxAC_NO_AUTORESIZE))
266             FitToAnimation();
267     }
268 
269     DisplayStaticImage();
270 }
271 
FitToAnimation()272 void wxAnimationCtrl::FitToAnimation()
273 {
274     if (!m_anim)
275         return;
276 
277     int w = gdk_pixbuf_animation_get_width(m_anim),
278         h = gdk_pixbuf_animation_get_height(m_anim);
279 
280     // update our size to fit animation
281     SetSize(w, h);
282 }
283 
ResetAnim()284 void wxAnimationCtrl::ResetAnim()
285 {
286     if (m_anim)
287         g_object_unref(m_anim);
288     m_anim = NULL;
289 }
290 
ResetIter()291 void wxAnimationCtrl::ResetIter()
292 {
293     if (m_iter)
294         g_object_unref(m_iter);
295     m_iter = NULL;
296 }
297 
Play()298 bool wxAnimationCtrl::Play()
299 {
300     if (m_anim == NULL)
301         return false;
302 
303     // init the iterator and start a one-shot timer
304     ResetIter();
305     m_iter = gdk_pixbuf_animation_get_iter (m_anim, NULL);
306     m_bPlaying = true;
307 
308     // gdk_pixbuf_animation_iter_get_delay_time() may return -1 which means
309     // that the timer should not start
310     int n = gdk_pixbuf_animation_iter_get_delay_time(m_iter);
311     if (n >= 0)
312         m_timer.Start(n, true);
313 
314     return true;
315 }
316 
Stop()317 void wxAnimationCtrl::Stop()
318 {
319     // leave current frame displayed until Play() is called again
320     if (IsPlaying())
321         m_timer.Stop();
322     m_bPlaying = false;
323 
324     ResetIter();
325     DisplayStaticImage();
326 }
327 
DisplayStaticImage()328 void wxAnimationCtrl::DisplayStaticImage()
329 {
330     wxASSERT(!IsPlaying());
331 
332     // m_bmpStaticReal will be updated only if necessary...
333     UpdateStaticImage();
334 
335     if (m_bmpStaticReal.IsOk())
336     {
337         // show inactive bitmap
338         GdkBitmap *mask = (GdkBitmap *) NULL;
339         if (m_bmpStaticReal.GetMask())
340             mask = m_bmpStaticReal.GetMask()->GetBitmap();
341 
342         if (m_bmpStaticReal.HasPixbuf())
343         {
344             gtk_image_set_from_pixbuf(GTK_IMAGE(m_widget),
345                                       m_bmpStaticReal.GetPixbuf());
346         }
347         else
348         {
349             gtk_image_set_from_pixmap(GTK_IMAGE(m_widget),
350                                       m_bmpStaticReal.GetPixmap(), mask);
351         }
352     }
353     else
354     {
355         if (m_anim)
356         {
357             // even if not clearly documented, gdk_pixbuf_animation_get_static_image()
358             // always returns the first frame of the animation
359             gtk_image_set_from_pixbuf(GTK_IMAGE(m_widget),
360                                         gdk_pixbuf_animation_get_static_image(m_anim));
361         }
362         else
363         {
364             ClearToBackgroundColour();
365         }
366     }
367 }
368 
IsPlaying() const369 bool wxAnimationCtrl::IsPlaying() const
370 {
371     // NB: we cannot just return m_timer.IsRunning() as this would not
372     //     be safe as e.g. if we are displaying a frame forever,
373     //     then we are "officially" still playing the animation, but
374     //     the timer is not running anymore...
375     return m_bPlaying;
376 }
377 
DoGetBestSize() const378 wxSize wxAnimationCtrl::DoGetBestSize() const
379 {
380     if (m_anim && !this->HasFlag(wxAC_NO_AUTORESIZE))
381     {
382         return wxSize(gdk_pixbuf_animation_get_width(m_anim),
383                       gdk_pixbuf_animation_get_height(m_anim));
384     }
385 
386     return wxSize(100,100);
387 }
388 
ClearToBackgroundColour()389 void wxAnimationCtrl::ClearToBackgroundColour()
390 {
391     wxSize sz = GetClientSize();
392     GdkPixbuf *newpix = gdk_pixbuf_new(GDK_COLORSPACE_RGB, false, 8,
393                                        sz.GetWidth(), sz.GetHeight());
394     if (!newpix)
395         return;
396 
397     wxColour clr = GetBackgroundColour();
398     guint32 col = (clr.Red() << 24) | (clr.Green() << 16) | (clr.Blue() << 8);
399     gdk_pixbuf_fill(newpix, col);
400 
401     gtk_image_set_from_pixbuf(GTK_IMAGE(m_widget), newpix);
402     g_object_unref(newpix);
403 }
404 
SetBackgroundColour(const wxColour & colour)405 bool wxAnimationCtrl::SetBackgroundColour( const wxColour &colour )
406 {
407     // wxWindowGTK::SetBackgroundColour works but since our m_widget is a GtkImage
408     // it won't show the background colour unlike the user would expect.
409     // Thus we clear the GtkImage contents to the background colour...
410     if (!wxControl::SetBackgroundColour(colour))
411         return false;
412 
413     // if not playing the change must take place immediately but
414     // remember that the inactive bitmap has higher priority over the background
415     // colour; DisplayStaticImage() will handle that
416     if ( !IsPlaying() )
417         DisplayStaticImage();
418 
419     return true;
420 }
421 
422 
423 //-----------------------------------------------------------------------------
424 // wxAnimationCtrl - event handlers
425 //-----------------------------------------------------------------------------
426 
OnTimer(wxTimerEvent & ev)427 void wxAnimationCtrl::OnTimer(wxTimerEvent &ev)
428 {
429     wxASSERT(m_iter != NULL);
430 
431     // gdk_pixbuf_animation_iter_advance() will automatically restart
432     // the animation, if necessary and we have no way to know !!
433     if (gdk_pixbuf_animation_iter_advance(m_iter, NULL))
434     {
435         // start a new one-shot timer
436         int n = gdk_pixbuf_animation_iter_get_delay_time(m_iter);
437         if (n >= 0)
438             m_timer.Start(n, true);
439 
440         gtk_image_set_from_pixbuf(GTK_IMAGE(m_widget),
441                                   gdk_pixbuf_animation_iter_get_pixbuf(m_iter));
442     }
443     else
444     {
445         // no need to update the m_widget yet
446         m_timer.Start(10, true);
447     }
448 }
449 
450 #endif      // wxUSE_ANIMATIONCTRL
451