dolphin-emulator/Externals/wxWidgets3/src/generic/animateg.cpp
EmptyChaos 822326eea9 Update wxWidgets to 3.1.0
From wxWidgets master 81570ae070b35c9d52de47b1f14897f3ff1a66c7.

include/wx/defs.h -- __w64 warning disable patch by comex brought forward.

include/wx/msw/window.h -- added GetContentScaleFactor() which was not implemented on Windows but is necessary for wxBitmap scaling on Mac OS X so it needs to work to avoid #ifdef-ing the code.

src/gtk/window.cpp -- Modified DoSetClientSize() to direct call wxWindowGTK::DoSetSize() instead of using public wxWindowBase::SetSize() which now prevents derived classes (like wxAuiToolbar) intercepting the call and breaking it. This matches Windows which does NOT need to call DoSetSize internally. End result is this fixes Dolphin's debug tools toolbars on Linux.

src/osx/window_osx.cpp -- Same fix as for GTK since it has the same issue.

src/msw/radiobox.cpp -- Hacked to fix display in HiDPI (was clipping off end of text).

Updated CMakeLists for Linux and Mac OS X. Small code changes to Dolphin to fix debug error boxes, deprecation warnings, and retain previous UI behavior on Windows.
2016-06-26 15:25:29 +10:00

696 lines
20 KiB
C++

///////////////////////////////////////////////////////////////////////////////
// Name: src/generic/animateg.cpp
// Purpose: wxAnimation and wxAnimationCtrl
// Author: Julian Smart and Guillermo Rodriguez Garcia
// Modified by: Francesco Montorsi
// Created: 13/8/99
// Copyright: (c) Julian Smart and Guillermo Rodriguez Garcia
// Licence: wxWindows licence
///////////////////////////////////////////////////////////////////////////////
#include "wx/wxprec.h"
#ifdef __BORLANDC__
#pragma hdrstop
#endif //__BORLANDC__
#if wxUSE_ANIMATIONCTRL
#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/gifdecod.h"
#include "wx/anidecod.h"
#include "wx/listimpl.cpp"
WX_DEFINE_LIST(wxAnimationDecoderList)
wxAnimationDecoderList wxAnimation::sm_handlers;
// ----------------------------------------------------------------------------
// wxAnimation
// ----------------------------------------------------------------------------
wxIMPLEMENT_DYNAMIC_CLASS(wxAnimation, wxAnimationBase);
#define M_ANIMDATA static_cast<wxAnimationDecoder*>(m_refData)
wxSize wxAnimation::GetSize() const
{
wxCHECK_MSG( IsOk(), wxDefaultSize, wxT("invalid animation") );
return M_ANIMDATA->GetAnimationSize();
}
unsigned int wxAnimation::GetFrameCount() const
{
wxCHECK_MSG( IsOk(), 0, wxT("invalid animation") );
return M_ANIMDATA->GetFrameCount();
}
wxImage wxAnimation::GetFrame(unsigned int i) const
{
wxCHECK_MSG( IsOk(), wxNullImage, wxT("invalid animation") );
wxImage ret;
if (!M_ANIMDATA->ConvertToImage(i, &ret))
return wxNullImage;
return ret;
}
int wxAnimation::GetDelay(unsigned int i) const
{
wxCHECK_MSG( IsOk(), 0, wxT("invalid animation") );
return M_ANIMDATA->GetDelay(i);
}
wxPoint wxAnimation::GetFramePosition(unsigned int frame) const
{
wxCHECK_MSG( IsOk(), wxDefaultPosition, wxT("invalid animation") );
return M_ANIMDATA->GetFramePosition(frame);
}
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") );
return M_ANIMDATA->GetBackgroundColour();
}
bool wxAnimation::LoadFile(const wxString& filename, wxAnimationType type)
{
wxFileInputStream stream(filename);
if ( !stream.IsOk() )
return false;
return Load(stream, type);
}
bool wxAnimation::Load(wxInputStream &stream, wxAnimationType type)
{
UnRef();
const wxAnimationDecoder *handler;
if ( type == wxANIMATION_TYPE_ANY )
{
for ( wxAnimationDecoderList::compatibility_iterator node = sm_handlers.GetFirst();
node; node = node->GetNext() )
{
handler=(const wxAnimationDecoder*)node->GetData();
if ( handler->CanRead(stream) )
{
// do a copy of the handler from the static list which we will own
// as our reference data
m_refData = handler->Clone();
return M_ANIMDATA->Load(stream);
}
}
wxLogWarning( _("No handler found for animation type.") );
return false;
}
handler = FindHandler(type);
if (handler == NULL)
{
wxLogWarning( _("No animation handler for type %ld defined."), 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);
return false;
}
else
return M_ANIMDATA->Load(stream);
}
// ----------------------------------------------------------------------------
// animation decoders
// ----------------------------------------------------------------------------
void wxAnimation::AddHandler( wxAnimationDecoder *handler )
{
// Check for an existing handler of the type being added.
if (FindHandler( handler->GetType() ) == 0)
{
sm_handlers.Append( handler );
}
else
{
// This is not documented behaviour, merely the simplest 'fix'
// 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.
wxLogDebug( wxT("Adding duplicate animation handler for '%d' type"),
handler->GetType() );
delete handler;
}
}
void wxAnimation::InsertHandler( wxAnimationDecoder *handler )
{
// Check for an existing handler of the type being added.
if (FindHandler( handler->GetType() ) == 0)
{
sm_handlers.Insert( handler );
}
else
{
// see AddHandler for additional comments.
wxLogDebug( wxT("Inserting duplicate animation handler for '%d' type"),
handler->GetType() );
delete handler;
}
}
const wxAnimationDecoder *wxAnimation::FindHandler( wxAnimationType animType )
{
wxAnimationDecoderList::compatibility_iterator node = sm_handlers.GetFirst();
while (node)
{
const wxAnimationDecoder *handler = (const wxAnimationDecoder *)node->GetData();
if (handler->GetType() == animType) return handler;
node = node->GetNext();
}
return 0;
}
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()
{
wxAnimationDecoderList::compatibility_iterator node = sm_handlers.GetFirst();
while (node)
{
wxAnimationDecoder *handler = (wxAnimationDecoder *)node->GetData();
wxAnimationDecoderList::compatibility_iterator next = node->GetNext();
delete handler;
node = next;
}
sm_handlers.Clear();
}
// A module to allow wxAnimation initialization/cleanup
// without calling these functions from app.cpp or from
// the user's application.
class wxAnimationModule: public wxModule
{
wxDECLARE_DYNAMIC_CLASS(wxAnimationModule);
public:
wxAnimationModule() {}
bool OnInit() wxOVERRIDE { wxAnimation::InitStandardHandlers(); return true; }
void OnExit() wxOVERRIDE { wxAnimation::CleanUpHandlers(); }
};
wxIMPLEMENT_DYNAMIC_CLASS(wxAnimationModule, wxModule);
// ----------------------------------------------------------------------------
// wxAnimationCtrl
// ----------------------------------------------------------------------------
wxIMPLEMENT_CLASS(wxAnimationCtrl, wxAnimationCtrlBase);
wxBEGIN_EVENT_TABLE(wxAnimationCtrl, wxAnimationCtrlBase)
EVT_PAINT(wxAnimationCtrl::OnPaint)
EVT_SIZE(wxAnimationCtrl::OnSize)
EVT_TIMER(wxID_ANY, wxAnimationCtrl::OnTimer)
wxEND_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_timer.SetOwner(this);
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;
}
wxAnimationCtrl::~wxAnimationCtrl()
{
Stop();
}
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.Load(stream, type) || !anim.IsOk() )
return false;
SetAnimation(anim);
return true;
}
wxSize wxAnimationCtrl::DoGetBestSize() const
{
if (m_animation.IsOk() && !this->HasFlag(wxAC_NO_AUTORESIZE))
return m_animation.GetSize();
return wxSize(100, 100);
}
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();
DisplayStaticImage();
}
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.IsOk() && bmp.GetMask() != NULL && GetParent() != NULL )
SetBackgroundColour(GetParent()->GetBackgroundColour());
wxAnimationCtrlBase::SetInactiveBitmap(bmp);
}
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
// ----------------------------------------------------------------------------
void wxAnimationCtrl::Stop()
{
m_timer.Stop();
m_isPlaying = false;
// reset frame counter
m_currentFrame = 0;
DisplayStaticImage();
}
bool wxAnimationCtrl::Play(bool looped)
{
if (!m_animation.IsOk())
return false;
m_looped = looped;
m_currentFrame = 0;
if (!RebuildBackingStoreUpToFrame(0))
return false;
m_isPlaying = true;
// 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);
DrawCurrentFrame(clientDC);
// start the timer
int delay = m_animation.GetDelay(0);
if (delay == 0)
delay = 1; // 0 is invalid timeout for wxTimer.
m_timer.Start(delay, true);
return true;
}
// ----------------------------------------------------------------------------
// wxAnimationCtrl - rendering methods
// ----------------------------------------------------------------------------
bool wxAnimationCtrl::RebuildBackingStoreUpToFrame(unsigned int frame)
{
// if we've not created the backing store yet or it's too
// small, then recreate it
wxSize sz = m_animation.GetSize(),
winsz = GetClientSize();
int w = wxMin(sz.GetWidth(), winsz.GetWidth());
int h = wxMin(sz.GetHeight(), winsz.GetHeight());
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);
// Draw the background
DisposeToBackground(dc);
// Draw all intermediate frames that haven't been removed from the animation
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()
{
wxMemoryDC dc;
dc.SelectObject(m_backingStore);
// OPTIMIZATION:
// since wxAnimationCtrl can only play animations forward, without skipping
// frames, we can be sure that m_backingStore contains the m_currentFrame-1
// frame and thus we just need to dispose the m_currentFrame-1 frame and
// render the m_currentFrame-th one.
if (m_currentFrame == 0)
{
// before drawing the first frame always dispose to bg colour
DisposeToBackground(dc);
}
else
{
switch (m_animation.GetDisposalMethod(m_currentFrame-1))
{
case wxANIM_TOBACKGROUND:
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 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,
// the best we can do is to restore to background
DisposeToBackground(dc);
}
else
if (!RebuildBackingStoreUpToFrame(m_currentFrame-2))
Stop();
break;
case wxANIM_DONOTREMOVE:
case wxANIM_UNSPECIFIED:
break;
}
}
// now just draw the current frame on the top of the backing store
DrawFrame(dc, m_currentFrame);
dc.SelectObject(wxNullBitmap);
}
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
// needs first to convert from its internal format to wxImage RGB24;
// the wxImage is then converted as a wxBitmap and finally blitted.
// If wxAnimationDecoder had a function to convert directly from its
// internal format to a port-specific wxBitmap, it would be somewhat faster.
wxBitmap bmp(m_animation.GetFrame(frame));
dc.DrawBitmap(bmp, m_animation.GetFramePosition(frame),
true /* use mask */);
}
void wxAnimationCtrl::DrawCurrentFrame(wxDC& dc)
{
wxASSERT( m_backingStore.IsOk() );
// m_backingStore always contains the current frame
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)
{
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
// ----------------------------------------------------------------------------
void wxAnimationCtrl::OnPaint(wxPaintEvent& WXUNUSED(event))
{
// VERY IMPORTANT: the wxPaintDC *must* be created in any case
wxPaintDC dc(this);
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))
{
m_currentFrame++;
if (m_currentFrame == m_animation.GetFrameCount())
{
// Should a non-looped animation display the last frame?
if (!m_looped)
{
Stop();
return;
}
else
m_currentFrame = 0; // let's restart
}
IncrementalUpdateBackingStore();
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, 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 e.g. using
// a null proportion value for your wxAnimationCtrls
// when using them inside sizers.
if (m_animation.IsOk())
{
// 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