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