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