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