added wxAnimationCtrl (patch 1570325)
[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
13 // ----------------------------------------------------------------------------
14 // headers
15 // ----------------------------------------------------------------------------
16
17 // For compilers that support precompilation, includes "wx.h".
18 #include "wx/wxprec.h"
19
20 #if wxUSE_ANIMATIONCTRL
21
22 #include "wx/animate.h"
23 #include <gtk/gtk.h>
24 #include <gtk/gtkimage.h>
25
26
27 // ============================================================================
28 // implementation
29 // ============================================================================
30
31 void gdk_pixbuf_area_updated(GdkPixbufLoader *loader,
32 gint x,
33 gint y,
34 gint width,
35 gint height,
36 wxAnimation *anim)
37 {
38 if (anim && anim->GetPixbuf() == NULL)
39 {
40 // we need to set the pixbuf only if this is the first time this signal
41 // has been called!
42 anim->SetPixbuf(gdk_pixbuf_loader_get_animation(loader));
43 }
44 }
45
46
47 //-----------------------------------------------------------------------------
48 // wxAnimation
49 //-----------------------------------------------------------------------------
50
51 IMPLEMENT_DYNAMIC_CLASS(wxAnimation, wxAnimationBase)
52
53 bool wxAnimation::LoadFile(const wxString &name, wxAnimationType WXUNUSED(type))
54 {
55 UnRef();
56 m_pixbuf = gdk_pixbuf_animation_new_from_file(name.c_str(), NULL);
57 return IsOk();
58 }
59
60 bool wxAnimation::Load(wxInputStream &stream, wxAnimationType type)
61 {
62 UnRef();
63
64 char anim_type[12];
65 switch (type)
66 {
67 case wxANIMATION_TYPE_GIF:
68 strcpy(anim_type, "gif");
69 break;
70
71 case wxANIMATION_TYPE_ANI:
72 strcpy(anim_type, "ani");
73 break;
74
75 default:
76 break;
77 }
78
79 // create a GdkPixbufLoader
80 GError *error = NULL;
81 GdkPixbufLoader *loader;
82 if (type != wxANIMATION_TYPE_INVALID && type != wxANIMATION_TYPE_ANY)
83 loader = gdk_pixbuf_loader_new_with_type(anim_type, &error);
84 else
85 loader = gdk_pixbuf_loader_new();
86
87 if (!loader)
88 {
89 wxLogDebug(wxT("Could not create the loader for '%s' animation type"), anim_type);
90 return false;
91 }
92
93 // connect to loader signals
94 g_signal_connect(loader, "area-updated", G_CALLBACK(gdk_pixbuf_area_updated), this);
95
96 //m_bLoadComplete = false;
97 guchar buf[2048];
98 while (stream.IsOk())
99 {
100 // read a chunk of data
101 stream.Read(buf, 2048);
102
103 // fetch all data into the loader
104 if (!gdk_pixbuf_loader_write(loader, buf, stream.LastRead(), &error))
105 {
106 gdk_pixbuf_loader_close(loader, &error);
107 wxLogDebug(wxT("Could not write to the loader"));
108 return false;
109 }
110 }
111
112 // load complete
113 if (!gdk_pixbuf_loader_close(loader, &error))
114 {
115 wxLogDebug(wxT("Could not close the loader"));
116 return false;
117 }
118 //m_bLoadComplete = true;
119
120 // wait until we get the last area_updated signal
121 return true;
122 }
123
124
125 //-----------------------------------------------------------------------------
126 // wxAnimationCtrl
127 //-----------------------------------------------------------------------------
128
129 IMPLEMENT_DYNAMIC_CLASS(wxAnimationCtrl, wxAnimationCtrlBase)
130 BEGIN_EVENT_TABLE(wxAnimationCtrl, wxAnimationCtrlBase)
131 EVT_TIMER(wxID_ANY, wxAnimationCtrl::OnTimer)
132 END_EVENT_TABLE()
133
134 bool wxAnimationCtrl::Create( wxWindow *parent, wxWindowID id,
135 const wxAnimation& anim,
136 const wxPoint& pos,
137 const wxSize& size,
138 long style,
139 const wxString& name)
140 {
141 m_needParent = true;
142 m_acceptsFocus = true;
143
144 if (!PreCreation( parent, pos, size ) ||
145 !wxControl::CreateBase(parent, id, pos, size, style & wxWINDOW_STYLE_MASK,
146 wxDefaultValidator, name))
147 {
148 wxFAIL_MSG( wxT("wxAnimationCtrl creation failed") );
149 return false;
150 }
151
152 SetWindowStyle(style);
153
154 m_widget = gtk_image_new();
155 gtk_widget_show( GTK_WIDGET(m_widget) );
156
157 m_parent->DoAddChild( this );
158
159 PostCreation(size);
160 SetBestSize(size);
161
162 m_anim = NULL;
163 m_iter = NULL;
164 m_bPlaying = false;
165 if (anim != wxNullAnimation)
166 SetAnimation(anim);
167
168 // init the timer used for animation
169 m_timer.SetOwner(this);
170
171 return true;
172 }
173
174 wxAnimationCtrl::~wxAnimationCtrl()
175 {
176 ResetAnim();
177 ResetIter();
178 }
179
180 bool wxAnimationCtrl::LoadFile(const wxString &filename, wxAnimationType type)
181 {
182 wxAnimation anim;
183 if (!anim.LoadFile(filename, type))
184 return false;
185
186 SetAnimation(anim);
187 return true;
188 }
189
190 void wxAnimationCtrl::SetAnimation(const wxAnimation &anim)
191 {
192 if (IsPlaying())
193 Stop();
194
195 ResetAnim();
196 ResetIter();
197
198 // copy underlying GdkPixbuf object
199 m_anim = anim.GetPixbuf();
200
201 // m_anim may be null in case wxNullAnimation has been passed
202 if (m_anim)
203 {
204 // add a reference to the GdkPixbufAnimation
205 g_object_ref(m_anim);
206
207 if (!this->HasFlag(wxAC_NO_AUTORESIZE))
208 FitToAnimation();
209
210 // display first frame
211 gtk_image_set_from_pixbuf(GTK_IMAGE(m_widget),
212 gdk_pixbuf_animation_get_static_image(m_anim));
213 }
214 else
215 {
216 // we need to clear the control to the background colour
217 ClearToBackgroundColour();
218 }
219 }
220
221 void wxAnimationCtrl::FitToAnimation()
222 {
223 if (!m_anim)
224 return;
225
226 int w = gdk_pixbuf_animation_get_width(m_anim),
227 h = gdk_pixbuf_animation_get_height(m_anim);
228
229 // update our size to fit animation
230 //if (w > 0 && h > 0)
231 // gtk_widget_set_size_request(m_widget, w, h);
232 SetSize(w, h);
233 }
234
235 bool wxAnimationCtrl::Play()
236 {
237 if (m_anim == NULL)
238 return false;
239
240 // init the iterator and start a one-shot timer
241 ResetIter();
242 m_iter = gdk_pixbuf_animation_get_iter (m_anim, NULL);
243 m_bPlaying = true;
244
245 // gdk_pixbuf_animation_iter_get_delay_time() may return -1 which means
246 // that the timer should not start
247 int n = gdk_pixbuf_animation_iter_get_delay_time(m_iter);
248 if (n >= 0)
249 m_timer.Start(n, true);
250
251 return true;
252 }
253
254 void wxAnimationCtrl::Stop()
255 {
256 // leave current frame displayed until Play() is called again
257 if (IsPlaying())
258 m_timer.Stop();
259 m_bPlaying = false;
260 }
261
262 bool wxAnimationCtrl::IsPlaying() const
263 {
264 // NB: we cannot just return m_timer.IsRunning() as this would not
265 // be safe as e.g. if we are displaying a frame forever,
266 // then we are "officially" still playing the animation, but
267 // the timer is not running anymore...
268 return m_bPlaying;
269 }
270
271 wxSize wxAnimationCtrl::DoGetBestSize() const
272 {
273 if (m_anim && !this->HasFlag(wxAC_NO_AUTORESIZE))
274 {
275 return wxSize(gdk_pixbuf_animation_get_width(m_anim),
276 gdk_pixbuf_animation_get_height(m_anim));
277 }
278
279 return wxSize(100,100);
280 }
281
282 void wxAnimationCtrl::ClearToBackgroundColour()
283 {
284 wxSize sz = GetClientSize();
285 GdkPixbuf *newpix = gdk_pixbuf_new(GDK_COLORSPACE_RGB, false, 8,
286 sz.GetWidth(), sz.GetHeight());
287 if (!newpix)
288 return;
289
290 wxColour clr = GetBackgroundColour();
291 guint32 col = (clr.Red() << 24) | (clr.Green() << 16) | (clr.Blue() << 8);
292 gdk_pixbuf_fill(newpix, col);
293
294 wxLogDebug(wxT("Clearing to background %s"), clr.GetAsString().c_str());
295
296 gtk_image_set_from_pixbuf(GTK_IMAGE(m_widget), newpix);
297 g_object_unref(newpix);
298 }
299
300 bool wxAnimationCtrl::SetBackgroundColour( const wxColour &colour )
301 {
302 // wxWindowGTK::SetBackgroundColour works but since our m_widget is a GtkImage
303 // it won't show the background colour unlike the user would expect.
304 // Thus we clear the GtkImage contents to the background colour...
305 if (!wxControl::SetBackgroundColour(colour))
306 return false;
307 ClearToBackgroundColour();
308 return true;
309 }
310
311
312 //-----------------------------------------------------------------------------
313 // wxAnimationCtrl - event handlers
314 //-----------------------------------------------------------------------------
315
316 void wxAnimationCtrl::OnTimer(wxTimerEvent &ev)
317 {
318 wxASSERT(m_iter != NULL);
319
320 // gdk_pixbuf_animation_iter_advance() will automatically restart
321 // the animation, if necessary and we have no way to know !!
322 if (gdk_pixbuf_animation_iter_advance(m_iter, NULL))
323 {
324 // start a new one-shot timer
325 int n = gdk_pixbuf_animation_iter_get_delay_time(m_iter);
326 if (n >= 0)
327 m_timer.Start(n, true);
328
329 gtk_image_set_from_pixbuf(GTK_IMAGE(m_widget),
330 gdk_pixbuf_animation_iter_get_pixbuf(m_iter));
331 }
332 else
333 {
334 // no need to update the m_widget yet
335 m_timer.Start(10, true);
336 }
337 }
338
339 #endif // wxUSE_ANIMATIONCTRL