]> git.saurik.com Git - wxWidgets.git/blobdiff - src/gtk/animate.cpp
Fix crash in wxDC::GetMultiLineTextExtent() after last commit.
[wxWidgets.git] / src / gtk / animate.cpp
index 27e535f8effcef1e1d33026d9d353a70d69a508c..a70e92e87217d5342af4e8ab2b65f39ad60b179f 100644 (file)
@@ -9,20 +9,23 @@
 // Licence:     wxWindows licence
 /////////////////////////////////////////////////////////////////////////////
 
-
-// ----------------------------------------------------------------------------
-// headers
-// ----------------------------------------------------------------------------
-
 // For compilers that support precompilation, includes "wx.h".
 #include "wx/wxprec.h"
 
-#if wxUSE_ANIMATIONCTRL
+#if wxUSE_ANIMATIONCTRL && !defined(__WXUNIVERSAL__)
 
 #include "wx/animate.h"
-#include "wx/log.h"
+
+#ifndef WX_PRECOMP
+    #include "wx/image.h"
+    #include "wx/log.h"
+    #include "wx/stream.h"
+#endif
+
+#include "wx/wfstream.h"
+#include "wx/gtk/private.h"
+
 #include <gtk/gtk.h>
-#include <gtk/gtkimage.h>
 
 
 // ============================================================================
 // ============================================================================
 
 void gdk_pixbuf_area_updated(GdkPixbufLoader *loader,
-                             gint             x,
-                             gint             y,
-                             gint             width,
-                             gint             height,
+                             gint             WXUNUSED(x),
+                             gint             WXUNUSED(y),
+                             gint             WXUNUSED(width),
+                             gint             WXUNUSED(height),
                              wxAnimation      *anim)
 {
     if (anim && anim->GetPixbuf() == NULL)
@@ -51,11 +54,38 @@ void gdk_pixbuf_area_updated(GdkPixbufLoader *loader,
 
 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);
+    m_pixbuf = gdk_pixbuf_animation_new_from_file(wxGTK_CONV_FN(name), NULL);
     return IsOk();
 }
 
@@ -75,6 +105,7 @@ bool wxAnimation::Load(wxInputStream &stream, wxAnimationType type)
         break;
 
     default:
+        anim_type[0] = '\0';
         break;
     }
 
@@ -86,43 +117,87 @@ bool wxAnimation::Load(wxInputStream &stream, wxAnimationType type)
     else
         loader = gdk_pixbuf_loader_new();
 
-    if (!loader)
+    if (!loader ||
+        error != NULL)  // even if the loader was allocated, an error could have happened
     {
-        wxLogDebug(wxT("Could not create the loader for '%s' animation type"), anim_type);
+        wxLogDebug(wxT("Could not create the loader for '%s' animation type: %s"),
+                   anim_type, error->message);
         return false;
     }
 
     // connect to loader signals
     g_signal_connect(loader, "area-updated", G_CALLBACK(gdk_pixbuf_area_updated), this);
 
-    //m_bLoadComplete = false;
     guchar buf[2048];
+    bool data_written = false;
     while (stream.IsOk())
     {
         // read a chunk of data
-        stream.Read(buf, 2048);
+        if (!stream.Read(buf, sizeof(buf)) &&
+            stream.GetLastError() != wxSTREAM_EOF)   // EOF is OK for now
+        {
+            // gdk_pixbuf_loader_close wants the GError == NULL
+            gdk_pixbuf_loader_close(loader, NULL);
+            return false;
+        }
 
         // 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"));
+            wxLogDebug(wxT("Could not write to the loader: %s"), error->message);
+
+            // gdk_pixbuf_loader_close wants the GError == NULL
+            gdk_pixbuf_loader_close(loader, NULL);
             return false;
         }
+
+        data_written = true;
+    }
+
+    if (!data_written)
+    {
+        wxLogDebug("Could not read data from the stream...");
+        return false;
     }
 
-    // load complete
+    // load complete: gdk_pixbuf_loader_close will now check if the data we
+    // wrote inside the pixbuf loader does make sense and will give an error
+    // if it doesn't (because of a truncated file, corrupted data or whatelse)
     if (!gdk_pixbuf_loader_close(loader, &error))
     {
-        wxLogDebug(wxT("Could not close the loader"));
+        wxLogDebug(wxT("Could not close the loader: %s"), error->message);
         return false;
     }
-    //m_bLoadComplete = true;
 
     // wait until we get the last area_updated signal
-    return true;
+    return data_written;
+}
+
+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
@@ -133,6 +208,13 @@ 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,
@@ -140,11 +222,8 @@ bool wxAnimationCtrl::Create( wxWindow *parent, wxWindowID id,
                               long style,
                               const wxString& name)
 {
-    m_needParent = true;
-    m_acceptsFocus = true;
-
     if (!PreCreation( parent, pos, size ) ||
-        !wxControl::CreateBase(parent, id, pos, size, style & wxWINDOW_STYLE_MASK,
+        !base_type::CreateBase(parent, id, pos, size, style & wxWINDOW_STYLE_MASK,
                                wxDefaultValidator, name))
     {
         wxFAIL_MSG( wxT("wxAnimationCtrl creation failed") );
@@ -154,17 +233,14 @@ bool wxAnimationCtrl::Create( wxWindow *parent, wxWindowID id,
     SetWindowStyle(style);
 
     m_widget = gtk_image_new();
-    gtk_widget_show( GTK_WIDGET(m_widget) );
+    g_object_ref(m_widget);
 
     m_parent->DoAddChild( this );
 
     PostCreation(size);
-    SetBestSize(size);
+    SetInitialSize(size);
 
-    m_anim = NULL;
-    m_iter = NULL;
-    m_bPlaying = false;
-    if (anim != wxNullAnimation)
+    if (anim.IsOk())
         SetAnimation(anim);
 
     // init the timer used for animation
@@ -180,9 +256,17 @@ wxAnimationCtrl::~wxAnimationCtrl()
 }
 
 bool wxAnimationCtrl::LoadFile(const wxString &filename, wxAnimationType type)
+{
+    wxFileInputStream fis(filename);
+    if (!fis.IsOk())
+        return false;
+    return Load(fis, type);
+}
+
+bool wxAnimationCtrl::Load(wxInputStream& stream, wxAnimationType type)
 {
     wxAnimation anim;
-    if (!anim.LoadFile(filename, type))
+    if ( !anim.Load(stream, type) || !anim.IsOk() )
         return false;
 
     SetAnimation(anim);
@@ -208,16 +292,9 @@ void wxAnimationCtrl::SetAnimation(const wxAnimation &anim)
 
         if (!this->HasFlag(wxAC_NO_AUTORESIZE))
             FitToAnimation();
-
-        // display first frame
-        gtk_image_set_from_pixbuf(GTK_IMAGE(m_widget),
-                                  gdk_pixbuf_animation_get_static_image(m_anim));
-    }
-    else
-    {
-        // we need to clear the control to the background colour
-        ClearToBackgroundColour();
     }
+
+    DisplayStaticImage();
 }
 
 void wxAnimationCtrl::FitToAnimation()
@@ -229,9 +306,21 @@ void wxAnimationCtrl::FitToAnimation()
         h = gdk_pixbuf_animation_get_height(m_anim);
 
     // update our size to fit animation
-    //if (w > 0 && h > 0)
-//        gtk_widget_set_size_request(m_widget, w, h);
-        SetSize(w, h);
+    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()
@@ -259,6 +348,38 @@ void wxAnimationCtrl::Stop()
     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
+        gtk_image_set_from_pixbuf(GTK_IMAGE(m_widget),
+                                      m_bmpStaticReal.GetPixbuf());
+    }
+    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
@@ -293,8 +414,6 @@ void wxAnimationCtrl::ClearToBackgroundColour()
     guint32 col = (clr.Red() << 24) | (clr.Green() << 16) | (clr.Blue() << 8);
     gdk_pixbuf_fill(newpix, col);
 
-    wxLogDebug(wxT("Clearing to background %s"), clr.GetAsString().c_str());
-
     gtk_image_set_from_pixbuf(GTK_IMAGE(m_widget), newpix);
     g_object_unref(newpix);
 }
@@ -306,7 +425,13 @@ bool wxAnimationCtrl::SetBackgroundColour( const wxColour &colour )
     // Thus we clear the GtkImage contents to the background colour...
     if (!wxControl::SetBackgroundColour(colour))
         return false;
-    ClearToBackgroundColour();
+
+    // 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;
 }
 
@@ -315,7 +440,7 @@ bool wxAnimationCtrl::SetBackgroundColour( const wxColour &colour )
 // wxAnimationCtrl - event handlers
 //-----------------------------------------------------------------------------
 
-void wxAnimationCtrl::OnTimer(wxTimerEvent &ev)
+void wxAnimationCtrl::OnTimer(wxTimerEvent& WXUNUSED(ev))
 {
     wxASSERT(m_iter != NULL);