]> git.saurik.com Git - wxWidgets.git/blobdiff - src/generic/animateg.cpp
Disable wxUSE_ENH_METAFILE for wxGTK builds.
[wxWidgets.git] / src / generic / animateg.cpp
index 2ab49659dd08e509f89c204ab8a1dbcc16a71642..d6e44ba9a225bce5993c7f61afbe0e1379e3f22e 100644 (file)
@@ -1,5 +1,5 @@
 ///////////////////////////////////////////////////////////////////////////////
-// Name:        animateg.cpp
+// Name:        src/generic/animateg.cpp
 // Purpose:     wxAnimation and wxAnimationCtrl
 // Author:      Julian Smart and Guillermo Rodriguez Garcia
 // Modified by: Francesco Montorsi
   #pragma hdrstop
 #endif  //__BORLANDC__
 
-
 #if wxUSE_ANIMATIONCTRL
 
-#include "wx/log.h"
+#include "wx/animate.h"
+
+#ifndef WX_PRECOMP
+    #include "wx/log.h"
+    #include "wx/image.h"
+    #include "wx/dcmemory.h"
+    #include "wx/dcclient.h"
+    #include "wx/module.h"
+#endif
+
 #include "wx/wfstream.h"
-#include "wx/image.h"
 #include "wx/gifdecod.h"
 #include "wx/anidecod.h"
-#include "wx/dcmemory.h"
-#include "wx/dc.h"
-#include "wx/dcclient.h"
-#include "wx/animate.h"
-#include "wx/animdecod.h"
-
 
-#include <wx/listimpl.cpp>
-WX_DEFINE_LIST(wxAnimationDecoderList);
+#include "wx/listimpl.cpp"
+WX_DEFINE_LIST(wxAnimationDecoderList)
 
 wxAnimationDecoderList wxAnimation::sm_handlers;
 
 
-
 // ----------------------------------------------------------------------------
 // wxAnimation
 // ----------------------------------------------------------------------------
 
 IMPLEMENT_DYNAMIC_CLASS(wxAnimation, wxAnimationBase)
-#define M_ANIMDATA      wx_static_cast(wxAnimationDecoder*, m_refData)
+#define M_ANIMDATA      static_cast<wxAnimationDecoder*>(m_refData)
 
 wxSize wxAnimation::GetSize() const
 {
@@ -51,14 +51,14 @@ wxSize wxAnimation::GetSize() const
     return M_ANIMDATA->GetAnimationSize();
 }
 
-size_t wxAnimation::GetFrameCount() const
+unsigned int wxAnimation::GetFrameCount() const
 {
     wxCHECK_MSG( IsOk(), 0, wxT("invalid animation") );
 
     return M_ANIMDATA->GetFrameCount();
 }
 
-wxImage wxAnimation::GetFrame(size_t i) const
+wxImage wxAnimation::GetFrame(unsigned int i) const
 {
     wxCHECK_MSG( IsOk(), wxNullImage, wxT("invalid animation") );
 
@@ -68,27 +68,41 @@ wxImage wxAnimation::GetFrame(size_t i) const
     return ret;
 }
 
-int wxAnimation::GetDelay(size_t i) const
+int wxAnimation::GetDelay(unsigned int i) const
 {
     wxCHECK_MSG( IsOk(), 0, wxT("invalid animation") );
 
     return M_ANIMDATA->GetDelay(i);
 }
 
-wxPoint wxAnimation::GetFramePosition(size_t frame) const
+wxPoint wxAnimation::GetFramePosition(unsigned int frame) const
 {
     wxCHECK_MSG( IsOk(), wxDefaultPosition, wxT("invalid animation") );
 
     return M_ANIMDATA->GetFramePosition(frame);
 }
 
-wxAnimationDisposal wxAnimation::GetDisposalMethod(size_t frame) const
+wxSize wxAnimation::GetFrameSize(unsigned int frame) const
+{
+    wxCHECK_MSG( IsOk(), wxDefaultSize, wxT("invalid animation") );
+
+    return M_ANIMDATA->GetFrameSize(frame);
+}
+
+wxAnimationDisposal wxAnimation::GetDisposalMethod(unsigned int frame) const
 {
     wxCHECK_MSG( IsOk(), wxANIM_UNSPECIFIED, wxT("invalid animation") );
 
     return M_ANIMDATA->GetDisposalMethod(frame);
 }
 
+wxColour wxAnimation::GetTransparentColour(unsigned int frame) const
+{
+    wxCHECK_MSG( IsOk(), wxNullColour, wxT("invalid animation") );
+
+    return M_ANIMDATA->GetTransparentColour(frame);
+}
+
 wxColour wxAnimation::GetBackgroundColour() const
 {
     wxCHECK_MSG( IsOk(), wxNullColour, wxT("invalid animation") );
@@ -99,7 +113,7 @@ wxColour wxAnimation::GetBackgroundColour() const
 bool wxAnimation::LoadFile(const wxString& filename, wxAnimationType type)
 {
     wxFileInputStream stream(filename);
-    if (!stream.Ok())
+    if ( !stream.IsOk() )
         return false;
 
     return Load(stream, type);
@@ -112,7 +126,7 @@ bool wxAnimation::Load(wxInputStream &stream, wxAnimationType type)
     const wxAnimationDecoder *handler;
     if ( type == wxANIMATION_TYPE_ANY )
     {
-        for ( wxAnimationDecoderList::compatibility_iterator node = sm_handlers.GetFirst(); 
+        for ( wxAnimationDecoderList::compatibility_iterator node = sm_handlers.GetFirst();
               node; node = node->GetNext() )
         {
             handler=(const wxAnimationDecoder*)node->GetData();
@@ -124,7 +138,6 @@ bool wxAnimation::Load(wxInputStream &stream, wxAnimationType type)
                 m_refData = handler->Clone();
                 return M_ANIMDATA->Load(stream);
             }
-
         }
 
         wxLogWarning( _("No handler found for animation type.") );
@@ -133,10 +146,6 @@ bool wxAnimation::Load(wxInputStream &stream, wxAnimationType type)
 
     handler = FindHandler(type);
 
-    // do a copy of the handler from the static list which we will own
-    // as our reference data
-    m_refData = handler->Clone();
-
     if (handler == NULL)
     {
         wxLogWarning( _("No animation handler for type %ld defined."), type );
@@ -144,6 +153,11 @@ bool wxAnimation::Load(wxInputStream &stream, wxAnimationType type)
         return false;
     }
 
+
+    // do a copy of the handler from the static list which we will own
+    // as our reference data
+    m_refData = handler->Clone();
+
     if (stream.IsSeekable() && !M_ANIMDATA->CanRead(stream))
     {
         wxLogError(_("Animation file is not of type %ld."), type);
@@ -171,9 +185,8 @@ void wxAnimation::AddHandler( wxAnimationDecoder *handler )
         // for preventing duplicate additions.  If someone ever has
         // a good reason to add and remove duplicate handlers (and they
         // may) we should probably refcount the duplicates.
-        //   also an issue in InsertHandler below.
 
-        wxLogDebug( _T("Adding duplicate animation handler for '%d' type"),
+        wxLogDebug( wxT("Adding duplicate animation handler for '%d' type"),
                     handler->GetType() );
         delete handler;
     }
@@ -189,7 +202,7 @@ void wxAnimation::InsertHandler( wxAnimationDecoder *handler )
     else
     {
         // see AddHandler for additional comments.
-        wxLogDebug( _T("Inserting duplicate animation handler for '%d' type"),
+        wxLogDebug( wxT("Inserting duplicate animation handler for '%d' type"),
                     handler->GetType() );
         delete handler;
     }
@@ -209,8 +222,12 @@ const wxAnimationDecoder *wxAnimation::FindHandler( wxAnimationType animType )
 
 void wxAnimation::InitStandardHandlers()
 {
+#if wxUSE_GIF
     AddHandler(new wxGIFDecoder);
+#endif // wxUSE_GIF
+#if wxUSE_ICO_CUR
     AddHandler(new wxANIDecoder);
+#endif // wxUSE_ICO_CUR
 }
 
 void wxAnimation::CleanUpHandlers()
@@ -237,15 +254,13 @@ class wxAnimationModule: public wxModule
 DECLARE_DYNAMIC_CLASS(wxAnimationModule)
 public:
     wxAnimationModule() {}
-    bool OnInit() { wxAnimation::InitStandardHandlers(); return true; };
-    void OnExit() { wxAnimation::CleanUpHandlers(); };
+    bool OnInit() { wxAnimation::InitStandardHandlers(); return true; }
+    void OnExit() { wxAnimation::CleanUpHandlers(); }
 };
 
 IMPLEMENT_DYNAMIC_CLASS(wxAnimationModule, wxModule)
 
 
-
-
 // ----------------------------------------------------------------------------
 // wxAnimationCtrl
 // ----------------------------------------------------------------------------
@@ -257,22 +272,31 @@ BEGIN_EVENT_TABLE(wxAnimationCtrl, wxAnimationCtrlBase)
     EVT_TIMER(wxID_ANY, wxAnimationCtrl::OnTimer)
 END_EVENT_TABLE()
 
+void wxAnimationCtrl::Init()
+{
+    m_currentFrame = 0;
+    m_looped = false;
+    m_isPlaying = false;
+
+    // use the window background colour by default to be consistent
+    // with the GTK+ native version
+    m_useWinBackgroundColour = true;
+}
+
 bool wxAnimationCtrl::Create(wxWindow *parent, wxWindowID id,
             const wxAnimation& animation, const wxPoint& pos,
             const wxSize& size, long style, const wxString& name)
 {
-    m_animation = animation;
-    m_currentFrame = 0;
-    m_looped = true;
-    m_isPlaying = false;
-    m_useWinBackgroundColour = false;
     m_timer.SetOwner(this);
 
-    if (!wxControl::Create(parent, id, pos, size, style, wxDefaultValidator, name))
+    if (!base_type::Create(parent, id, pos, size, style, wxDefaultValidator, name))
         return false;
 
     // by default we get the same background colour of our parent
     SetBackgroundColour(parent->GetBackgroundColour());
+
+    SetAnimation(animation);
+
     return true;
 }
 
@@ -282,10 +306,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) || 
-        !anim.IsOk())
+    if ( !anim.Load(stream, type) || !anim.IsOk() )
         return false;
 
     SetAnimation(anim);
@@ -305,28 +336,33 @@ void wxAnimationCtrl::SetAnimation(const wxAnimation& animation)
     if (IsPlaying())
         Stop();
 
+    // set new animation even if it's wxNullAnimation
     m_animation = animation;
+    if (!m_animation.IsOk())
+    {
+        DisplayStaticImage();
+        return;
+    }
 
     if (m_animation.GetBackgroundColour() == wxNullColour)
         SetUseWindowBackgroundColour();
     if (!this->HasFlag(wxAC_NO_AUTORESIZE))
         FitToAnimation();
 
-    // display first frame
-    m_currentFrame = 0;
-    if (m_animation.IsOk())
-        RebuildBackingStoreUpToFrame(0);
-    else
-    {
-        // clear to 
-        wxMemoryDC dc;
-        dc.SelectObject(m_backingStore);
-
-        // Draw the background
-        DisposeToBackground(dc);
-    }
+    DisplayStaticImage();
+}
 
-    Refresh();
+void wxAnimationCtrl::SetInactiveBitmap(const wxBitmap &bmp)
+{
+    // if the bitmap has an associated mask, we need to set our background to
+    // the colour of our parent otherwise when calling DrawCurrentFrame()
+    // (which uses the bitmap's mask), our background colour would be used for
+    // transparent areas - and that's not what we want (at least for
+    // consistency with the GTK version)
+    if ( bmp.GetMask() != NULL && GetParent() != NULL )
+        SetBackgroundColour(GetParent()->GetBackgroundColour());
+
+    wxAnimationCtrlBase::SetInactiveBitmap(bmp);
 }
 
 void wxAnimationCtrl::FitToAnimation()
@@ -334,6 +370,19 @@ void wxAnimationCtrl::FitToAnimation()
     SetSize(m_animation.GetSize());
 }
 
+bool wxAnimationCtrl::SetBackgroundColour(const wxColour& colour)
+{
+    if ( !wxWindow::SetBackgroundColour(colour) )
+        return false;
+
+    // if not playing, then this change must be seen immediately (unless
+    // there's an inactive bitmap set which has higher priority than bg colour)
+    if ( !IsPlaying() )
+        DisplayStaticImage();
+
+    return true;
+}
+
 
 // ----------------------------------------------------------------------------
 // wxAnimationCtrl - stop/play methods
@@ -341,9 +390,13 @@ void wxAnimationCtrl::FitToAnimation()
 
 void wxAnimationCtrl::Stop()
 {
-    // leave current frame displayed until Play() is called again
     m_timer.Stop();
     m_isPlaying = false;
+
+    // reset frame counter
+    m_currentFrame = 0;
+
+    DisplayStaticImage();
 }
 
 bool wxAnimationCtrl::Play(bool looped)
@@ -351,15 +404,17 @@ bool wxAnimationCtrl::Play(bool looped)
     if (!m_animation.IsOk())
         return false;
 
-    int oldframe = m_currentFrame;
     m_looped = looped;
     m_currentFrame = 0;
+
+    if (!RebuildBackingStoreUpToFrame(0))
+        return false;
+
     m_isPlaying = true;
 
-    // small optimization: if the back store was already updated to the
-    // first frame, don't rebuild it
-    if (oldframe != 0)
-        RebuildBackingStoreUpToFrame(0);
+    // do a ClearBackground() to avoid that e.g. the custom static bitmap which
+    // was eventually shown previously remains partially drawn
+    ClearBackground();
 
     // DrawCurrentFrame() will use our updated backing store
     wxClientDC clientDC(this);
@@ -369,7 +424,7 @@ bool wxAnimationCtrl::Play(bool looped)
     int delay = m_animation.GetDelay(0);
     if (delay == 0)
         delay = 1;      // 0 is invalid timeout for wxTimer.
-    m_timer.Start(delay);
+    m_timer.Start(delay, true);
 
     return true;
 }
@@ -380,7 +435,7 @@ bool wxAnimationCtrl::Play(bool looped)
 // wxAnimationCtrl - rendering methods
 // ----------------------------------------------------------------------------
 
-void wxAnimationCtrl::RebuildBackingStoreUpToFrame(size_t frame)
+bool wxAnimationCtrl::RebuildBackingStoreUpToFrame(unsigned int frame)
 {
     // if we've not created the backing store yet or it's too
     // small, then recreate it
@@ -389,9 +444,12 @@ void wxAnimationCtrl::RebuildBackingStoreUpToFrame(size_t frame)
     int w = wxMin(sz.GetWidth(), winsz.GetWidth());
     int h = wxMin(sz.GetHeight(), winsz.GetHeight());
 
-    if (m_backingStore.GetWidth() < w ||
-        m_backingStore.GetHeight() < h)
-        m_backingStore.Create(w, h);
+    if ( !m_backingStore.IsOk() ||
+            m_backingStore.GetWidth() < w || m_backingStore.GetHeight() < h )
+    {
+        if (!m_backingStore.Create(w, h))
+            return false;
+    }
 
     wxMemoryDC dc;
     dc.SelectObject(m_backingStore);
@@ -400,18 +458,23 @@ void wxAnimationCtrl::RebuildBackingStoreUpToFrame(size_t frame)
     DisposeToBackground(dc);
 
     // Draw all intermediate frames that haven't been removed from the animation
-    for (size_t i = 0; i < frame; i++)
+    for (unsigned int i = 0; i < frame; i++)
     {
         if (m_animation.GetDisposalMethod(i) == wxANIM_DONOTREMOVE ||
             m_animation.GetDisposalMethod(i) == wxANIM_UNSPECIFIED)
         {
             DrawFrame(dc, i);
         }
+        else if (m_animation.GetDisposalMethod(i) == wxANIM_TOBACKGROUND)
+            DisposeToBackground(dc, m_animation.GetFramePosition(i),
+                                    m_animation.GetFrameSize(i));
     }
 
     // finally draw this frame
     DrawFrame(dc, frame);
     dc.SelectObject(wxNullBitmap);
+
+    return true;
 }
 
 void wxAnimationCtrl::IncrementalUpdateBackingStore()
@@ -435,14 +498,15 @@ void wxAnimationCtrl::IncrementalUpdateBackingStore()
         switch (m_animation.GetDisposalMethod(m_currentFrame-1))
         {
         case wxANIM_TOBACKGROUND:
-            DisposeToBackground(dc);
+            DisposeToBackground(dc, m_animation.GetFramePosition(m_currentFrame-1),
+                                    m_animation.GetFrameSize(m_currentFrame-1));
             break;
 
         case wxANIM_TOPREVIOUS:
             // this disposal should never be used too often.
-            // E.g. GIF specification explicitely say to keep the usage of this
-            //      disposal limited to the minimum. 
-            // In fact it may require a lot of time to restore 
+            // E.g. GIF specification explicitly say to keep the usage of this
+            //      disposal limited to the minimum.
+            // In fact it may require a lot of time to restore
             if (m_currentFrame == 1)
             {
                 // if 0-th frame disposal is to restore to previous frame,
@@ -450,7 +514,8 @@ void wxAnimationCtrl::IncrementalUpdateBackingStore()
                 DisposeToBackground(dc);
             }
             else
-                RebuildBackingStoreUpToFrame(m_currentFrame-2);
+                if (!RebuildBackingStoreUpToFrame(m_currentFrame-2))
+                    Stop();
             break;
 
         case wxANIM_DONOTREMOVE:
@@ -464,7 +529,42 @@ void wxAnimationCtrl::IncrementalUpdateBackingStore()
     dc.SelectObject(wxNullBitmap);
 }
 
-void wxAnimationCtrl::DrawFrame(wxDC &dc, size_t frame)
+void wxAnimationCtrl::DisplayStaticImage()
+{
+    wxASSERT(!IsPlaying());
+
+    // m_bmpStaticReal will be updated only if necessary...
+    UpdateStaticImage();
+
+    if (m_bmpStaticReal.IsOk())
+    {
+        // copy the inactive bitmap in the backing store
+        // eventually using the mask if the static bitmap has one
+        if ( m_bmpStaticReal.GetMask() )
+        {
+            wxMemoryDC temp;
+            temp.SelectObject(m_backingStore);
+            DisposeToBackground(temp);
+            temp.DrawBitmap(m_bmpStaticReal, 0, 0, true /* use mask */);
+        }
+        else
+            m_backingStore = m_bmpStaticReal;
+    }
+    else
+    {
+        // put in the backing store the first frame of the animation
+        if (!m_animation.IsOk() ||
+            !RebuildBackingStoreUpToFrame(0))
+        {
+            m_animation = wxNullAnimation;
+            DisposeToBackground();
+        }
+    }
+
+    Refresh();
+}
+
+void wxAnimationCtrl::DrawFrame(wxDC &dc, unsigned int frame)
 {
     // PERFORMANCE NOTE:
     // this draw stuff is not as fast as possible: the wxAnimationDecoder
@@ -479,20 +579,43 @@ void wxAnimationCtrl::DrawFrame(wxDC &dc, size_t frame)
 
 void wxAnimationCtrl::DrawCurrentFrame(wxDC& dc)
 {
-    wxASSERT(m_backingStore.Ok());
+    wxASSERT( m_backingStore.IsOk() );
 
     // m_backingStore always contains the current frame
-    dc.DrawBitmap(m_backingStore, 0, 0);
+    dc.DrawBitmap(m_backingStore, 0, 0, true /* use mask in case it's present */);
+}
+
+void wxAnimationCtrl::DisposeToBackground()
+{
+    // clear the backing store
+    wxMemoryDC dc;
+    dc.SelectObject(m_backingStore);
+    if ( dc.IsOk() )
+        DisposeToBackground(dc);
 }
 
 void wxAnimationCtrl::DisposeToBackground(wxDC& dc)
 {
-    wxBrush brush(IsUsingWindowBackgroundColour() ? 
-                  this->GetBackgroundColour() : m_animation.GetBackgroundColour(), wxSOLID);
+    wxColour col = IsUsingWindowBackgroundColour()
+                    ? GetBackgroundColour()
+                    : m_animation.GetBackgroundColour();
+
+    wxBrush brush(col);
     dc.SetBackground(brush);
     dc.Clear();
 }
 
+void wxAnimationCtrl::DisposeToBackground(wxDC& dc, const wxPoint &pos, const wxSize &sz)
+{
+    wxColour col = IsUsingWindowBackgroundColour()
+                    ? GetBackgroundColour()
+                    : m_animation.GetBackgroundColour();
+    wxBrush brush(col);
+    dc.SetBrush(brush);         // SetBrush and not SetBackground !!
+    dc.SetPen(*wxTRANSPARENT_PEN);
+    dc.DrawRectangle(pos, sz);
+}
+
 // ----------------------------------------------------------------------------
 // wxAnimationCtrl - event handlers
 // ----------------------------------------------------------------------------
@@ -502,10 +625,19 @@ void wxAnimationCtrl::OnPaint(wxPaintEvent& WXUNUSED(event))
     // VERY IMPORTANT: the wxPaintDC *must* be created in any case
     wxPaintDC dc(this);
 
-    // both if we are playing or not, we need to refresh the current frame
-    if (m_backingStore.Ok())
-        DrawCurrentFrame(dc);
-    //else: m_animation is not valid and thus we don't have a valid backing store...
+    if ( m_backingStore.IsOk() )
+    {
+        // NOTE: we draw the bitmap explicitly ignoring the mask (if any);
+        //       i.e. we don't want to combine the backing store with the
+        //       possibly wrong preexisting contents of the window!
+        dc.DrawBitmap(m_backingStore, 0, 0, false /* no mask */);
+    }
+    else
+    {
+        // m_animation is not valid and thus we don't have a valid backing store...
+        // clear then our area to the background colour
+        DisposeToBackground(dc);
+    }
 }
 
 void wxAnimationCtrl::OnTimer(wxTimerEvent &WXUNUSED(event))
@@ -516,8 +648,7 @@ void wxAnimationCtrl::OnTimer(wxTimerEvent &WXUNUSED(event))
         // Should a non-looped animation display the last frame?
         if (!m_looped)
         {
-            m_timer.Stop();
-            m_isPlaying = false;
+            Stop();
             return;
         }
         else
@@ -529,21 +660,38 @@ void wxAnimationCtrl::OnTimer(wxTimerEvent &WXUNUSED(event))
     wxClientDC dc(this);
     DrawCurrentFrame(dc);
 
+#ifdef __WXMAC__
+    // without this, the animation currently doesn't redraw under Mac
+    Refresh();
+#endif // __WXMAC__
+
     // Set the timer for the next frame
     int delay = m_animation.GetDelay(m_currentFrame);
     if (delay == 0)
         delay = 1;      // 0 is invalid timeout for wxTimer.
-    m_timer.Start(delay);
+    m_timer.Start(delay, true);
 }
 
 void wxAnimationCtrl::OnSize(wxSizeEvent &WXUNUSED(event))
 {
     // NB: resizing an animation control may take a lot of time
     //     for big animations as the backing store must be
-    //     extended and rebuilt. Try to avoid it!!
+    //     extended and rebuilt. Try to avoid it e.g. using
+    //     a null proportion value for your wxAnimationCtrls
+    //     when using them inside sizers.
     if (m_animation.IsOk())
-        RebuildBackingStoreUpToFrame(m_currentFrame);
+    {
+        // be careful to change the backing store *only* if we are
+        // playing the animation as otherwise we may be displaying
+        // the inactive bitmap and overwriting the backing store
+        // with the last played frame is wrong in this case
+        if (IsPlaying())
+        {
+            if (!RebuildBackingStoreUpToFrame(m_currentFrame))
+                Stop();     // in case we are playing
+        }
+    }
 }
 
-#endif      // wxUSE_ANIMATIONCTRL
+#endif // wxUSE_ANIMATIONCTRL