Files
wxWidgets/src/gtk/animate.cpp

448 lines
11 KiB
C++

/////////////////////////////////////////////////////////////////////////////
// Name: src/gtk/animate.cpp
// Purpose: wxAnimation and wxAnimationCtrl
// Author: Francesco Montorsi
// Modified By:
// Created: 24/09/2006
// Id: $Id$
// Copyright: (c) Francesco Montorsi
// Licence: wxWindows licence
/////////////////////////////////////////////////////////////////////////////
// For compilers that support precompilation, includes "wx.h".
#include "wx/wxprec.h"
#if wxUSE_ANIMATIONCTRL && !defined(__WXUNIVERSAL__)
#include "wx/animate.h"
#ifndef WX_PRECOMP
#include "wx/image.h"
#include "wx/log.h"
#include "wx/stream.h"
#endif
#include <gtk/gtk.h>
// ============================================================================
// implementation
// ============================================================================
void gdk_pixbuf_area_updated(GdkPixbufLoader *loader,
gint x,
gint y,
gint width,
gint height,
wxAnimation *anim)
{
if (anim && anim->GetPixbuf() == NULL)
{
// we need to set the pixbuf only if this is the first time this signal
// has been called!
anim->SetPixbuf(gdk_pixbuf_loader_get_animation(loader));
}
}
//-----------------------------------------------------------------------------
// wxAnimation
//-----------------------------------------------------------------------------
IMPLEMENT_DYNAMIC_CLASS(wxAnimation, wxAnimationBase)
wxAnimation::wxAnimation(const wxAnimation& that)
: base_type(that)
{
m_pixbuf = that.m_pixbuf;
if (m_pixbuf)
g_object_ref(m_pixbuf);
}
wxAnimation::wxAnimation(GdkPixbufAnimation *p)
{
m_pixbuf = p;
if ( m_pixbuf )
g_object_ref(m_pixbuf);
}
wxAnimation& wxAnimation::operator=(const wxAnimation& that)
{
if (this != &that)
{
base_type::operator=(that);
UnRef();
m_pixbuf = that.m_pixbuf;
if (m_pixbuf)
g_object_ref(m_pixbuf);
}
return *this;
}
bool wxAnimation::LoadFile(const wxString &name, wxAnimationType WXUNUSED(type))
{
UnRef();
m_pixbuf = gdk_pixbuf_animation_new_from_file(
wxConvFileName->cWX2MB(name), NULL);
return IsOk();
}
bool wxAnimation::Load(wxInputStream &stream, wxAnimationType type)
{
UnRef();
char anim_type[12];
switch (type)
{
case wxANIMATION_TYPE_GIF:
strcpy(anim_type, "gif");
break;
case wxANIMATION_TYPE_ANI:
strcpy(anim_type, "ani");
break;
default:
anim_type[0] = '\0';
break;
}
// create a GdkPixbufLoader
GError *error = NULL;
GdkPixbufLoader *loader;
if (type != wxANIMATION_TYPE_INVALID && type != wxANIMATION_TYPE_ANY)
loader = gdk_pixbuf_loader_new_with_type(anim_type, &error);
else
loader = gdk_pixbuf_loader_new();
if (!loader)
{
wxLogDebug(wxT("Could not create the loader for '%s' animation type"), anim_type);
return false;
}
// connect to loader signals
g_signal_connect(loader, "area-updated", G_CALLBACK(gdk_pixbuf_area_updated), this);
guchar buf[2048];
while (stream.IsOk())
{
// read a chunk of data
stream.Read(buf, sizeof(buf));
// fetch all data into the loader
if (!gdk_pixbuf_loader_write(loader, buf, stream.LastRead(), &error))
{
gdk_pixbuf_loader_close(loader, &error);
wxLogDebug(wxT("Could not write to the loader"));
return false;
}
}
// load complete
if (!gdk_pixbuf_loader_close(loader, &error))
{
wxLogDebug(wxT("Could not close the loader"));
return false;
}
// wait until we get the last area_updated signal
return true;
}
wxImage wxAnimation::GetFrame(unsigned int WXUNUSED(frame)) const
{
return wxNullImage;
}
wxSize wxAnimation::GetSize() const
{
return wxSize(gdk_pixbuf_animation_get_width(m_pixbuf),
gdk_pixbuf_animation_get_height(m_pixbuf));
}
void wxAnimation::UnRef()
{
if (m_pixbuf)
g_object_unref(m_pixbuf);
m_pixbuf = NULL;
}
void wxAnimation::SetPixbuf(GdkPixbufAnimation* p)
{
UnRef();
m_pixbuf = p;
if (m_pixbuf)
g_object_ref(m_pixbuf);
}
//-----------------------------------------------------------------------------
// wxAnimationCtrl
//-----------------------------------------------------------------------------
IMPLEMENT_DYNAMIC_CLASS(wxAnimationCtrl, wxAnimationCtrlBase)
BEGIN_EVENT_TABLE(wxAnimationCtrl, wxAnimationCtrlBase)
EVT_TIMER(wxID_ANY, wxAnimationCtrl::OnTimer)
END_EVENT_TABLE()
void wxAnimationCtrl::Init()
{
m_anim = NULL;
m_iter = NULL;
m_bPlaying = false;
}
bool wxAnimationCtrl::Create( wxWindow *parent, wxWindowID id,
const wxAnimation& anim,
const wxPoint& pos,
const wxSize& size,
long style,
const wxString& name)
{
if (!PreCreation( parent, pos, size ) ||
!base_type::CreateBase(parent, id, pos, size, style & wxWINDOW_STYLE_MASK,
wxDefaultValidator, name))
{
wxFAIL_MSG( wxT("wxAnimationCtrl creation failed") );
return false;
}
SetWindowStyle(style);
m_widget = gtk_image_new();
gtk_widget_show(m_widget);
m_parent->DoAddChild( this );
PostCreation(size);
SetInitialSize(size);
if (anim.IsOk())
SetAnimation(anim);
// init the timer used for animation
m_timer.SetOwner(this);
return true;
}
wxAnimationCtrl::~wxAnimationCtrl()
{
ResetAnim();
ResetIter();
}
bool wxAnimationCtrl::LoadFile(const wxString &filename, wxAnimationType type)
{
wxAnimation anim;
if (!anim.LoadFile(filename, type))
return false;
SetAnimation(anim);
return true;
}
void wxAnimationCtrl::SetAnimation(const wxAnimation &anim)
{
if (IsPlaying())
Stop();
ResetAnim();
ResetIter();
// copy underlying GdkPixbuf object
m_anim = anim.GetPixbuf();
// m_anim may be null in case wxNullAnimation has been passed
if (m_anim)
{
// add a reference to the GdkPixbufAnimation
g_object_ref(m_anim);
if (!this->HasFlag(wxAC_NO_AUTORESIZE))
FitToAnimation();
}
DisplayStaticImage();
}
void wxAnimationCtrl::FitToAnimation()
{
if (!m_anim)
return;
int w = gdk_pixbuf_animation_get_width(m_anim),
h = gdk_pixbuf_animation_get_height(m_anim);
// update our size to fit animation
SetSize(w, h);
}
void wxAnimationCtrl::ResetAnim()
{
if (m_anim)
g_object_unref(m_anim);
m_anim = NULL;
}
void wxAnimationCtrl::ResetIter()
{
if (m_iter)
g_object_unref(m_iter);
m_iter = NULL;
}
bool wxAnimationCtrl::Play()
{
if (m_anim == NULL)
return false;
// init the iterator and start a one-shot timer
ResetIter();
m_iter = gdk_pixbuf_animation_get_iter (m_anim, NULL);
m_bPlaying = true;
// gdk_pixbuf_animation_iter_get_delay_time() may return -1 which means
// that the timer should not start
int n = gdk_pixbuf_animation_iter_get_delay_time(m_iter);
if (n >= 0)
m_timer.Start(n, true);
return true;
}
void wxAnimationCtrl::Stop()
{
// leave current frame displayed until Play() is called again
if (IsPlaying())
m_timer.Stop();
m_bPlaying = false;
ResetIter();
DisplayStaticImage();
}
void wxAnimationCtrl::DisplayStaticImage()
{
wxASSERT(!IsPlaying());
// m_bmpStaticReal will be updated only if necessary...
UpdateStaticImage();
if (m_bmpStaticReal.IsOk())
{
// show inactive bitmap
GdkBitmap *mask = (GdkBitmap *) NULL;
if (m_bmpStaticReal.GetMask())
mask = m_bmpStaticReal.GetMask()->GetBitmap();
if (m_bmpStaticReal.HasPixbuf())
{
gtk_image_set_from_pixbuf(GTK_IMAGE(m_widget),
m_bmpStaticReal.GetPixbuf());
}
else
{
gtk_image_set_from_pixmap(GTK_IMAGE(m_widget),
m_bmpStaticReal.GetPixmap(), mask);
}
}
else
{
if (m_anim)
{
// even if not clearly documented, gdk_pixbuf_animation_get_static_image()
// always returns the first frame of the animation
gtk_image_set_from_pixbuf(GTK_IMAGE(m_widget),
gdk_pixbuf_animation_get_static_image(m_anim));
}
else
{
ClearToBackgroundColour();
}
}
}
bool wxAnimationCtrl::IsPlaying() const
{
// NB: we cannot just return m_timer.IsRunning() as this would not
// be safe as e.g. if we are displaying a frame forever,
// then we are "officially" still playing the animation, but
// the timer is not running anymore...
return m_bPlaying;
}
wxSize wxAnimationCtrl::DoGetBestSize() const
{
if (m_anim && !this->HasFlag(wxAC_NO_AUTORESIZE))
{
return wxSize(gdk_pixbuf_animation_get_width(m_anim),
gdk_pixbuf_animation_get_height(m_anim));
}
return wxSize(100,100);
}
void wxAnimationCtrl::ClearToBackgroundColour()
{
wxSize sz = GetClientSize();
GdkPixbuf *newpix = gdk_pixbuf_new(GDK_COLORSPACE_RGB, false, 8,
sz.GetWidth(), sz.GetHeight());
if (!newpix)
return;
wxColour clr = GetBackgroundColour();
guint32 col = (clr.Red() << 24) | (clr.Green() << 16) | (clr.Blue() << 8);
gdk_pixbuf_fill(newpix, col);
gtk_image_set_from_pixbuf(GTK_IMAGE(m_widget), newpix);
g_object_unref(newpix);
}
bool wxAnimationCtrl::SetBackgroundColour( const wxColour &colour )
{
// wxWindowGTK::SetBackgroundColour works but since our m_widget is a GtkImage
// it won't show the background colour unlike the user would expect.
// Thus we clear the GtkImage contents to the background colour...
if (!wxControl::SetBackgroundColour(colour))
return false;
// if not playing the change must take place immediately but
// remember that the inactive bitmap has higher priority over the background
// colour; DisplayStaticImage() will handle that
if ( !IsPlaying() )
DisplayStaticImage();
return true;
}
//-----------------------------------------------------------------------------
// wxAnimationCtrl - event handlers
//-----------------------------------------------------------------------------
void wxAnimationCtrl::OnTimer(wxTimerEvent &ev)
{
wxASSERT(m_iter != NULL);
// gdk_pixbuf_animation_iter_advance() will automatically restart
// the animation, if necessary and we have no way to know !!
if (gdk_pixbuf_animation_iter_advance(m_iter, NULL))
{
// start a new one-shot timer
int n = gdk_pixbuf_animation_iter_get_delay_time(m_iter);
if (n >= 0)
m_timer.Start(n, true);
gtk_image_set_from_pixbuf(GTK_IMAGE(m_widget),
gdk_pixbuf_animation_iter_get_pixbuf(m_iter));
}
else
{
// no need to update the m_widget yet
m_timer.Start(10, true);
}
}
#endif // wxUSE_ANIMATIONCTRL