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