]> git.saurik.com Git - wxWidgets.git/blame - src/gtk/animate.cpp
revert nested event loop support for wxGTK1 because it causes applications hangs
[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
72045d57
VZ
7// Copyright: (c) Francesco Montorsi
8// Licence: wxWindows licence
9/////////////////////////////////////////////////////////////////////////////
10
72045d57
VZ
11// For compilers that support precompilation, includes "wx.h".
12#include "wx/wxprec.h"
13
5f8047a6 14#if wxUSE_ANIMATIONCTRL && !defined(__WXUNIVERSAL__)
72045d57
VZ
15
16#include "wx/animate.h"
c2f12218
PC
17
18#ifndef WX_PRECOMP
19 #include "wx/image.h"
20 #include "wx/log.h"
21 #include "wx/stream.h"
22#endif
23
8a2984cf 24#include "wx/wfstream.h"
c282e47d 25#include "wx/gtk/private.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();
c282e47d 87 m_pixbuf = gdk_pixbuf_animation_new_from_file(wxGTK_CONV_FN(name), 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
74be3634
FM
119 if (!loader ||
120 error != NULL) // even if the loader was allocated, an error could have happened
72045d57 121 {
8cc04507
FM
122 wxLogDebug(wxT("Could not create the loader for '%s' animation type: %s"),
123 anim_type, error->message);
72045d57
VZ
124 return false;
125 }
126
127 // connect to loader signals
128 g_signal_connect(loader, "area-updated", G_CALLBACK(gdk_pixbuf_area_updated), this);
129
72045d57 130 guchar buf[2048];
1ab88038 131 bool data_written = false;
72045d57
VZ
132 while (stream.IsOk())
133 {
134 // read a chunk of data
6fd068fc
FM
135 if (!stream.Read(buf, sizeof(buf)) &&
136 stream.GetLastError() != wxSTREAM_EOF) // EOF is OK for now
74be3634
FM
137 {
138 // gdk_pixbuf_loader_close wants the GError == NULL
139 gdk_pixbuf_loader_close(loader, NULL);
140 return false;
141 }
72045d57
VZ
142
143 // fetch all data into the loader
144 if (!gdk_pixbuf_loader_write(loader, buf, stream.LastRead(), &error))
145 {
8cc04507 146 wxLogDebug(wxT("Could not write to the loader: %s"), error->message);
1ab88038 147
74be3634
FM
148 // gdk_pixbuf_loader_close wants the GError == NULL
149 gdk_pixbuf_loader_close(loader, NULL);
72045d57
VZ
150 return false;
151 }
1ab88038
FM
152
153 data_written = true;
154 }
155
156 if (!data_written)
157 {
158 wxLogDebug("Could not read data from the stream...");
159 return false;
72045d57
VZ
160 }
161
6fd068fc
FM
162 // load complete: gdk_pixbuf_loader_close will now check if the data we
163 // wrote inside the pixbuf loader does make sense and will give an error
164 // if it doesn't (because of a truncated file, corrupted data or whatelse)
72045d57
VZ
165 if (!gdk_pixbuf_loader_close(loader, &error))
166 {
8cc04507 167 wxLogDebug(wxT("Could not close the loader: %s"), error->message);
72045d57
VZ
168 return false;
169 }
72045d57
VZ
170
171 // wait until we get the last area_updated signal
1ab88038 172 return data_written;
72045d57
VZ
173}
174
870cf35c 175wxImage wxAnimation::GetFrame(unsigned int WXUNUSED(frame)) const
c2f12218
PC
176{
177 return wxNullImage;
178}
179
180wxSize wxAnimation::GetSize() const
181{
182 return wxSize(gdk_pixbuf_animation_get_width(m_pixbuf),
183 gdk_pixbuf_animation_get_height(m_pixbuf));
184}
185
186void wxAnimation::UnRef()
187{
188 if (m_pixbuf)
189 g_object_unref(m_pixbuf);
190 m_pixbuf = NULL;
191}
192
193void wxAnimation::SetPixbuf(GdkPixbufAnimation* p)
194{
195 UnRef();
196 m_pixbuf = p;
197 if (m_pixbuf)
198 g_object_ref(m_pixbuf);
199}
72045d57
VZ
200
201//-----------------------------------------------------------------------------
202// wxAnimationCtrl
203//-----------------------------------------------------------------------------
204
205IMPLEMENT_DYNAMIC_CLASS(wxAnimationCtrl, wxAnimationCtrlBase)
206BEGIN_EVENT_TABLE(wxAnimationCtrl, wxAnimationCtrlBase)
207 EVT_TIMER(wxID_ANY, wxAnimationCtrl::OnTimer)
208END_EVENT_TABLE()
209
05a98b6d 210void wxAnimationCtrl::Init()
c2f12218
PC
211{
212 m_anim = NULL;
213 m_iter = NULL;
214 m_bPlaying = false;
215}
216
72045d57
VZ
217bool wxAnimationCtrl::Create( wxWindow *parent, wxWindowID id,
218 const wxAnimation& anim,
219 const wxPoint& pos,
220 const wxSize& size,
221 long style,
222 const wxString& name)
223{
72045d57 224 if (!PreCreation( parent, pos, size ) ||
c2f12218 225 !base_type::CreateBase(parent, id, pos, size, style & wxWINDOW_STYLE_MASK,
72045d57
VZ
226 wxDefaultValidator, name))
227 {
228 wxFAIL_MSG( wxT("wxAnimationCtrl creation failed") );
229 return false;
230 }
231
232 SetWindowStyle(style);
233
234 m_widget = gtk_image_new();
9ff9d30c 235 g_object_ref(m_widget);
72045d57
VZ
236
237 m_parent->DoAddChild( this );
238
239 PostCreation(size);
170acdc9 240 SetInitialSize(size);
72045d57 241
55ccdb93 242 if (anim.IsOk())
72045d57
VZ
243 SetAnimation(anim);
244
245 // init the timer used for animation
246 m_timer.SetOwner(this);
247
248 return true;
249}
250
251wxAnimationCtrl::~wxAnimationCtrl()
252{
253 ResetAnim();
254 ResetIter();
255}
256
257bool wxAnimationCtrl::LoadFile(const wxString &filename, wxAnimationType type)
462167a9
VZ
258{
259 wxFileInputStream fis(filename);
9690b006
FM
260 if (!fis.IsOk())
261 return false;
462167a9
VZ
262 return Load(fis, type);
263}
264
265bool wxAnimationCtrl::Load(wxInputStream& stream, wxAnimationType type)
72045d57
VZ
266{
267 wxAnimation anim;
462167a9 268 if ( !anim.Load(stream, type) || !anim.IsOk() )
72045d57
VZ
269 return false;
270
271 SetAnimation(anim);
272 return true;
273}
274
275void wxAnimationCtrl::SetAnimation(const wxAnimation &anim)
276{
277 if (IsPlaying())
278 Stop();
279
280 ResetAnim();
281 ResetIter();
282
283 // copy underlying GdkPixbuf object
284 m_anim = anim.GetPixbuf();
285
286 // m_anim may be null in case wxNullAnimation has been passed
287 if (m_anim)
288 {
289 // add a reference to the GdkPixbufAnimation
290 g_object_ref(m_anim);
291
292 if (!this->HasFlag(wxAC_NO_AUTORESIZE))
293 FitToAnimation();
72045d57 294 }
8e458bb5
RR
295
296 DisplayStaticImage();
72045d57
VZ
297}
298
299void wxAnimationCtrl::FitToAnimation()
300{
301 if (!m_anim)
302 return;
303
304 int w = gdk_pixbuf_animation_get_width(m_anim),
305 h = gdk_pixbuf_animation_get_height(m_anim);
306
307 // update our size to fit animation
1afdfc9d 308 SetSize(w, h);
72045d57
VZ
309}
310
c2f12218
PC
311void wxAnimationCtrl::ResetAnim()
312{
313 if (m_anim)
314 g_object_unref(m_anim);
315 m_anim = NULL;
316}
317
318void wxAnimationCtrl::ResetIter()
319{
320 if (m_iter)
321 g_object_unref(m_iter);
322 m_iter = NULL;
323}
324
72045d57
VZ
325bool wxAnimationCtrl::Play()
326{
327 if (m_anim == NULL)
328 return false;
329
330 // init the iterator and start a one-shot timer
331 ResetIter();
332 m_iter = gdk_pixbuf_animation_get_iter (m_anim, NULL);
333 m_bPlaying = true;
334
335 // gdk_pixbuf_animation_iter_get_delay_time() may return -1 which means
336 // that the timer should not start
337 int n = gdk_pixbuf_animation_iter_get_delay_time(m_iter);
338 if (n >= 0)
339 m_timer.Start(n, true);
340
341 return true;
342}
343
344void wxAnimationCtrl::Stop()
345{
346 // leave current frame displayed until Play() is called again
347 if (IsPlaying())
348 m_timer.Stop();
349 m_bPlaying = false;
8e458bb5
RR
350
351 ResetIter();
352 DisplayStaticImage();
353}
354
8e458bb5
RR
355void wxAnimationCtrl::DisplayStaticImage()
356{
357 wxASSERT(!IsPlaying());
358
1bd2ceb5
RR
359 // m_bmpStaticReal will be updated only if necessary...
360 UpdateStaticImage();
361
362 if (m_bmpStaticReal.IsOk())
8e458bb5
RR
363 {
364 // show inactive bitmap
9dc44eff 365 gtk_image_set_from_pixbuf(GTK_IMAGE(m_widget),
1bd2ceb5 366 m_bmpStaticReal.GetPixbuf());
8e458bb5
RR
367 }
368 else
369 {
98635ac6
VZ
370 if (m_anim)
371 {
372 // even if not clearly documented, gdk_pixbuf_animation_get_static_image()
373 // always returns the first frame of the animation
374 gtk_image_set_from_pixbuf(GTK_IMAGE(m_widget),
375 gdk_pixbuf_animation_get_static_image(m_anim));
376 }
377 else
378 {
379 ClearToBackgroundColour();
380 }
8e458bb5 381 }
72045d57
VZ
382}
383
384bool wxAnimationCtrl::IsPlaying() const
385{
386 // NB: we cannot just return m_timer.IsRunning() as this would not
387 // be safe as e.g. if we are displaying a frame forever,
388 // then we are "officially" still playing the animation, but
389 // the timer is not running anymore...
390 return m_bPlaying;
391}
392
393wxSize wxAnimationCtrl::DoGetBestSize() const
394{
395 if (m_anim && !this->HasFlag(wxAC_NO_AUTORESIZE))
396 {
397 return wxSize(gdk_pixbuf_animation_get_width(m_anim),
398 gdk_pixbuf_animation_get_height(m_anim));
399 }
400
401 return wxSize(100,100);
402}
403
404void wxAnimationCtrl::ClearToBackgroundColour()
405{
406 wxSize sz = GetClientSize();
407 GdkPixbuf *newpix = gdk_pixbuf_new(GDK_COLORSPACE_RGB, false, 8,
408 sz.GetWidth(), sz.GetHeight());
409 if (!newpix)
410 return;
411
412 wxColour clr = GetBackgroundColour();
413 guint32 col = (clr.Red() << 24) | (clr.Green() << 16) | (clr.Blue() << 8);
414 gdk_pixbuf_fill(newpix, col);
415
72045d57
VZ
416 gtk_image_set_from_pixbuf(GTK_IMAGE(m_widget), newpix);
417 g_object_unref(newpix);
418}
419
420bool wxAnimationCtrl::SetBackgroundColour( const wxColour &colour )
421{
422 // wxWindowGTK::SetBackgroundColour works but since our m_widget is a GtkImage
423 // it won't show the background colour unlike the user would expect.
424 // Thus we clear the GtkImage contents to the background colour...
425 if (!wxControl::SetBackgroundColour(colour))
426 return false;
1afdfc9d
VZ
427
428 // if not playing the change must take place immediately but
429 // remember that the inactive bitmap has higher priority over the background
430 // colour; DisplayStaticImage() will handle that
431 if ( !IsPlaying() )
432 DisplayStaticImage();
433
72045d57
VZ
434 return true;
435}
436
437
438//-----------------------------------------------------------------------------
439// wxAnimationCtrl - event handlers
440//-----------------------------------------------------------------------------
441
e4161a2a 442void wxAnimationCtrl::OnTimer(wxTimerEvent& WXUNUSED(ev))
72045d57
VZ
443{
444 wxASSERT(m_iter != NULL);
445
446 // gdk_pixbuf_animation_iter_advance() will automatically restart
447 // the animation, if necessary and we have no way to know !!
448 if (gdk_pixbuf_animation_iter_advance(m_iter, NULL))
449 {
450 // start a new one-shot timer
451 int n = gdk_pixbuf_animation_iter_get_delay_time(m_iter);
452 if (n >= 0)
453 m_timer.Start(n, true);
454
455 gtk_image_set_from_pixbuf(GTK_IMAGE(m_widget),
456 gdk_pixbuf_animation_iter_get_pixbuf(m_iter));
457 }
458 else
459 {
460 // no need to update the m_widget yet
461 m_timer.Start(10, true);
462 }
463}
464
465#endif // wxUSE_ANIMATIONCTRL