]> git.saurik.com Git - wxWidgets.git/blame - src/gtk/animate.cpp
avoiding nesting dcs on the same window concurrently
[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
62wxAnimation& wxAnimation::operator=(const wxAnimation& that)
63{
64 if (this != &that)
65 {
66 base_type::operator=(that);
67 UnRef();
68 m_pixbuf = that.m_pixbuf;
69 if (m_pixbuf)
70 g_object_ref(m_pixbuf);
71 }
72 return *this;
73}
74
72045d57
VZ
75bool wxAnimation::LoadFile(const wxString &name, wxAnimationType WXUNUSED(type))
76{
77 UnRef();
fba93835
RR
78 m_pixbuf = gdk_pixbuf_animation_new_from_file(
79 wxConvFileName->cWX2MB(name), NULL);
72045d57
VZ
80 return IsOk();
81}
82
83bool wxAnimation::Load(wxInputStream &stream, wxAnimationType type)
84{
85 UnRef();
86
87 char anim_type[12];
88 switch (type)
89 {
90 case wxANIMATION_TYPE_GIF:
91 strcpy(anim_type, "gif");
92 break;
93
94 case wxANIMATION_TYPE_ANI:
95 strcpy(anim_type, "ani");
96 break;
97
98 default:
c2f12218 99 anim_type[0] = '\0';
72045d57
VZ
100 break;
101 }
102
103 // create a GdkPixbufLoader
104 GError *error = NULL;
105 GdkPixbufLoader *loader;
106 if (type != wxANIMATION_TYPE_INVALID && type != wxANIMATION_TYPE_ANY)
107 loader = gdk_pixbuf_loader_new_with_type(anim_type, &error);
108 else
109 loader = gdk_pixbuf_loader_new();
110
111 if (!loader)
112 {
113 wxLogDebug(wxT("Could not create the loader for '%s' animation type"), anim_type);
114 return false;
115 }
116
117 // connect to loader signals
118 g_signal_connect(loader, "area-updated", G_CALLBACK(gdk_pixbuf_area_updated), this);
119
120 //m_bLoadComplete = false;
121 guchar buf[2048];
122 while (stream.IsOk())
123 {
124 // read a chunk of data
c2f12218 125 stream.Read(buf, sizeof(buf));
72045d57
VZ
126
127 // fetch all data into the loader
128 if (!gdk_pixbuf_loader_write(loader, buf, stream.LastRead(), &error))
129 {
130 gdk_pixbuf_loader_close(loader, &error);
131 wxLogDebug(wxT("Could not write to the loader"));
132 return false;
133 }
134 }
135
136 // load complete
137 if (!gdk_pixbuf_loader_close(loader, &error))
138 {
139 wxLogDebug(wxT("Could not close the loader"));
140 return false;
141 }
142 //m_bLoadComplete = true;
143
144 // wait until we get the last area_updated signal
145 return true;
146}
147
c2f12218
PC
148wxImage wxAnimation::GetFrame(size_t i) const
149{
150 return wxNullImage;
151}
152
153wxSize wxAnimation::GetSize() const
154{
155 return wxSize(gdk_pixbuf_animation_get_width(m_pixbuf),
156 gdk_pixbuf_animation_get_height(m_pixbuf));
157}
158
159void wxAnimation::UnRef()
160{
161 if (m_pixbuf)
162 g_object_unref(m_pixbuf);
163 m_pixbuf = NULL;
164}
165
166void wxAnimation::SetPixbuf(GdkPixbufAnimation* p)
167{
168 UnRef();
169 m_pixbuf = p;
170 if (m_pixbuf)
171 g_object_ref(m_pixbuf);
172}
72045d57
VZ
173
174//-----------------------------------------------------------------------------
175// wxAnimationCtrl
176//-----------------------------------------------------------------------------
177
178IMPLEMENT_DYNAMIC_CLASS(wxAnimationCtrl, wxAnimationCtrlBase)
179BEGIN_EVENT_TABLE(wxAnimationCtrl, wxAnimationCtrlBase)
180 EVT_TIMER(wxID_ANY, wxAnimationCtrl::OnTimer)
181END_EVENT_TABLE()
182
05a98b6d 183void wxAnimationCtrl::Init()
c2f12218
PC
184{
185 m_anim = NULL;
186 m_iter = NULL;
187 m_bPlaying = false;
188}
189
72045d57
VZ
190bool wxAnimationCtrl::Create( wxWindow *parent, wxWindowID id,
191 const wxAnimation& anim,
192 const wxPoint& pos,
193 const wxSize& size,
194 long style,
195 const wxString& name)
196{
197 m_needParent = true;
198 m_acceptsFocus = true;
199
200 if (!PreCreation( parent, pos, size ) ||
c2f12218 201 !base_type::CreateBase(parent, id, pos, size, style & wxWINDOW_STYLE_MASK,
72045d57
VZ
202 wxDefaultValidator, name))
203 {
204 wxFAIL_MSG( wxT("wxAnimationCtrl creation failed") );
205 return false;
206 }
207
208 SetWindowStyle(style);
209
210 m_widget = gtk_image_new();
211 gtk_widget_show( GTK_WIDGET(m_widget) );
212
213 m_parent->DoAddChild( this );
214
215 PostCreation(size);
216 SetBestSize(size);
217
72045d57
VZ
218 if (anim != wxNullAnimation)
219 SetAnimation(anim);
220
221 // init the timer used for animation
222 m_timer.SetOwner(this);
223
224 return true;
225}
226
227wxAnimationCtrl::~wxAnimationCtrl()
228{
229 ResetAnim();
230 ResetIter();
231}
232
233bool wxAnimationCtrl::LoadFile(const wxString &filename, wxAnimationType type)
234{
235 wxAnimation anim;
236 if (!anim.LoadFile(filename, type))
237 return false;
238
239 SetAnimation(anim);
240 return true;
241}
242
243void wxAnimationCtrl::SetAnimation(const wxAnimation &anim)
244{
245 if (IsPlaying())
246 Stop();
247
248 ResetAnim();
249 ResetIter();
250
251 // copy underlying GdkPixbuf object
252 m_anim = anim.GetPixbuf();
253
254 // m_anim may be null in case wxNullAnimation has been passed
255 if (m_anim)
256 {
257 // add a reference to the GdkPixbufAnimation
258 g_object_ref(m_anim);
259
260 if (!this->HasFlag(wxAC_NO_AUTORESIZE))
261 FitToAnimation();
262
263 // display first frame
264 gtk_image_set_from_pixbuf(GTK_IMAGE(m_widget),
265 gdk_pixbuf_animation_get_static_image(m_anim));
266 }
267 else
268 {
269 // we need to clear the control to the background colour
270 ClearToBackgroundColour();
271 }
272}
273
274void wxAnimationCtrl::FitToAnimation()
275{
276 if (!m_anim)
277 return;
278
279 int w = gdk_pixbuf_animation_get_width(m_anim),
280 h = gdk_pixbuf_animation_get_height(m_anim);
281
282 // update our size to fit animation
283 //if (w > 0 && h > 0)
284// gtk_widget_set_size_request(m_widget, w, h);
285 SetSize(w, h);
286}
287
c2f12218
PC
288void wxAnimationCtrl::ResetAnim()
289{
290 if (m_anim)
291 g_object_unref(m_anim);
292 m_anim = NULL;
293}
294
295void wxAnimationCtrl::ResetIter()
296{
297 if (m_iter)
298 g_object_unref(m_iter);
299 m_iter = NULL;
300}
301
72045d57
VZ
302bool wxAnimationCtrl::Play()
303{
304 if (m_anim == NULL)
305 return false;
306
307 // init the iterator and start a one-shot timer
308 ResetIter();
309 m_iter = gdk_pixbuf_animation_get_iter (m_anim, NULL);
310 m_bPlaying = true;
311
312 // gdk_pixbuf_animation_iter_get_delay_time() may return -1 which means
313 // that the timer should not start
314 int n = gdk_pixbuf_animation_iter_get_delay_time(m_iter);
315 if (n >= 0)
316 m_timer.Start(n, true);
317
318 return true;
319}
320
321void wxAnimationCtrl::Stop()
322{
323 // leave current frame displayed until Play() is called again
324 if (IsPlaying())
325 m_timer.Stop();
326 m_bPlaying = false;
327}
328
329bool wxAnimationCtrl::IsPlaying() const
330{
331 // NB: we cannot just return m_timer.IsRunning() as this would not
332 // be safe as e.g. if we are displaying a frame forever,
333 // then we are "officially" still playing the animation, but
334 // the timer is not running anymore...
335 return m_bPlaying;
336}
337
338wxSize wxAnimationCtrl::DoGetBestSize() const
339{
340 if (m_anim && !this->HasFlag(wxAC_NO_AUTORESIZE))
341 {
342 return wxSize(gdk_pixbuf_animation_get_width(m_anim),
343 gdk_pixbuf_animation_get_height(m_anim));
344 }
345
346 return wxSize(100,100);
347}
348
349void wxAnimationCtrl::ClearToBackgroundColour()
350{
351 wxSize sz = GetClientSize();
352 GdkPixbuf *newpix = gdk_pixbuf_new(GDK_COLORSPACE_RGB, false, 8,
353 sz.GetWidth(), sz.GetHeight());
354 if (!newpix)
355 return;
356
357 wxColour clr = GetBackgroundColour();
358 guint32 col = (clr.Red() << 24) | (clr.Green() << 16) | (clr.Blue() << 8);
359 gdk_pixbuf_fill(newpix, col);
360
361 wxLogDebug(wxT("Clearing to background %s"), clr.GetAsString().c_str());
362
363 gtk_image_set_from_pixbuf(GTK_IMAGE(m_widget), newpix);
364 g_object_unref(newpix);
365}
366
367bool wxAnimationCtrl::SetBackgroundColour( const wxColour &colour )
368{
369 // wxWindowGTK::SetBackgroundColour works but since our m_widget is a GtkImage
370 // it won't show the background colour unlike the user would expect.
371 // Thus we clear the GtkImage contents to the background colour...
372 if (!wxControl::SetBackgroundColour(colour))
373 return false;
374 ClearToBackgroundColour();
375 return true;
376}
377
378
379//-----------------------------------------------------------------------------
380// wxAnimationCtrl - event handlers
381//-----------------------------------------------------------------------------
382
383void wxAnimationCtrl::OnTimer(wxTimerEvent &ev)
384{
385 wxASSERT(m_iter != NULL);
386
387 // gdk_pixbuf_animation_iter_advance() will automatically restart
388 // the animation, if necessary and we have no way to know !!
389 if (gdk_pixbuf_animation_iter_advance(m_iter, NULL))
390 {
391 // start a new one-shot timer
392 int n = gdk_pixbuf_animation_iter_get_delay_time(m_iter);
393 if (n >= 0)
394 m_timer.Start(n, true);
395
396 gtk_image_set_from_pixbuf(GTK_IMAGE(m_widget),
397 gdk_pixbuf_animation_iter_get_pixbuf(m_iter));
398 }
399 else
400 {
401 // no need to update the m_widget yet
402 m_timer.Start(10, true);
403 }
404}
405
406#endif // wxUSE_ANIMATIONCTRL