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