1
0
mirror of https://github.com/cookiengineer/audacity synced 2025-12-20 07:31:19 +01:00
Files
audacity/src/toolbars/ToolBar.cpp
Paul Licameli 7824e94030 Harmlessly qualify classes as final (or explicitly comment not)...
... Should have no effect on generated code, except perhaps some slight faster
virtual function calls.  Mostly useful as documentation of design intent.

Tried to mark every one of our classes that inherits from another, or is a
base for others, or has abstract virtual functions, and a few others besides.
2016-02-24 20:58:30 -05:00

826 lines
19 KiB
C++

/**********************************************************************
Audacity: A Digital Audio Editor
ToolBar.cpp
Dominic Mazzoni
Shane T. Mueller
Leland Lucius
See ToolBar.h for details.
*******************************************************************//**
\file ToolBar.cpp
Implements ToolBar
*//*******************************************************************//**
\class ToolBar
\brief Works with ToolManager and ToolDock to provide a dockable window
in which buttons can be placed.
*//**********************************************************************/
#include "../Audacity.h"
// For compilers that support precompilation, includes "wx/wx.h".
#include <wx/wxprec.h>
#ifndef WX_PRECOMP
#include <wx/defs.h>
#include <wx/gdicmn.h>
#include <wx/image.h>
#include <wx/intl.h>
#include <wx/settings.h>
#include <wx/sizer.h>
#include <wx/sysopt.h>
#include <wx/window.h>
#endif /* */
#include "ToolBar.h"
#include "../Experimental.h"
#include "../AllThemeResources.h"
#include "../AColor.h"
#include "../ImageManipulation.h"
#include "../Project.h"
#include "../Theme.h"
#include "../widgets/AButton.h"
#include "../widgets/Grabber.h"
////////////////////////////////////////////////////////////
/// ToolBarResizer
////////////////////////////////////////////////////////////
//
// Width of the resize grab area
//
#define RWIDTH 4
class ToolBarResizer final : public wxWindow
{
public:
ToolBarResizer(ToolBar *mBar);
virtual ~ToolBarResizer();
// We don't need or want to accept focus.
bool AcceptsFocus() const;
private:
void OnErase(wxEraseEvent & event);
void OnPaint(wxPaintEvent & event);
void OnLeftDown(wxMouseEvent & event);
void OnLeftUp(wxMouseEvent & event);
void OnEnter(wxMouseEvent & event);
void OnLeave(wxMouseEvent & event);
void OnMotion(wxMouseEvent & event);
void OnCaptureLost(wxMouseCaptureLostEvent & event);
private:
ToolBar *mBar;
wxPoint mResizeStart;
DECLARE_EVENT_TABLE();
};
//
// Event table
//
BEGIN_EVENT_TABLE( ToolBarResizer, wxWindow )
EVT_ERASE_BACKGROUND( ToolBarResizer::OnErase )
EVT_PAINT( ToolBarResizer::OnPaint )
EVT_LEFT_DOWN( ToolBarResizer::OnLeftDown )
EVT_LEFT_UP( ToolBarResizer::OnLeftUp )
EVT_MOTION( ToolBarResizer::OnMotion )
EVT_MOUSE_CAPTURE_LOST( ToolBarResizer::OnCaptureLost )
END_EVENT_TABLE();
ToolBarResizer::ToolBarResizer(ToolBar *bar)
: wxWindow(bar, wxID_ANY, wxDefaultPosition, wxSize(RWIDTH, -1))
{
mBar = bar;
SetCursor( wxCURSOR_SIZEWE );
}
ToolBarResizer::~ToolBarResizer()
{
}
bool ToolBarResizer::AcceptsFocus() const
{
return false;
}
//
// Handle background erasure
//
void ToolBarResizer::OnErase( wxEraseEvent & WXUNUSED(event) )
{
// Ignore it to prevent flashing
}
//
// This draws the background of a toolbar
//
void ToolBarResizer::OnPaint( wxPaintEvent & event )
{
wxPaintDC dc( (wxWindow *) event.GetEventObject() );
// Start with a clean background
//
// Under GTK, we specifically set the toolbar background to the background
// colour in the system theme.
#if defined( __WXGTK__ )
dc.SetBackground( wxBrush( wxSystemSettings::GetColour( wxSYS_COLOUR_BACKGROUND ) ) );
#endif
dc.Clear();
wxSize sz = GetSize();
AColor::Dark( &dc, false );
AColor::Line(dc, sz.x - 4, 0, sz.x - 4, sz.y );
AColor::Line(dc, sz.x - 1, 0, sz.x - 1, sz.y );
}
//
// Handle toolbar resizing
//
void ToolBarResizer::OnLeftDown( wxMouseEvent & event )
{
// Go ahead and set the event to propagate
event.Skip();
// Retrieve the mouse position
mResizeStart = ClientToScreen( event.GetPosition() );
// We want all of the mouse events
CaptureMouse();
}
void ToolBarResizer::OnLeftUp( wxMouseEvent & event )
{
// Go ahead and set the event to propagate
event.Skip();
if( HasCapture() )
{
ReleaseMouse();
}
}
void ToolBarResizer::OnMotion( wxMouseEvent & event )
{
// Go ahead and set the event to propagate
event.Skip();
// Retrieve the mouse position
wxPoint raw_pos = event.GetPosition();
wxPoint pos = ClientToScreen( raw_pos );
if( event.Dragging() )
{
wxRect r = mBar->GetRect();
wxSize msz = mBar->GetMinSize();
wxSize psz = mBar->GetParent()->GetClientSize();
// Adjust the size by the difference between the
// last mouse and current mouse positions.
r.width += ( pos.x - mResizeStart.x );
// Constrain
if( r.width < msz.x )
{
// Don't allow resizing to go too small
r.width = msz.x;
}
else if( r.GetRight() > psz.x - 3 )
{
// Don't allow resizing to go too large
//
// The 3 magic pixels are because I'm too chicken to change the
// calculations in ToolDock::LayoutToolBars() even though I'm
// the one that set them up. :-)
r.SetRight( psz.x - 3 );
}
else
{
// Remember for next go round
mResizeStart = pos;
}
// Resize the bar
mBar->SetSize( r.GetSize() );
// Tell everyone we've changed sizes
mBar->Updated();
// Refresh our world
mBar->GetParent()->Refresh();
mBar->GetParent()->Update();
}
}
void ToolBarResizer::OnCaptureLost( wxMouseCaptureLostEvent & WXUNUSED(event) )
{
if( HasCapture() )
{
ReleaseMouse();
}
}
////////////////////////////////////////////////////////////
/// Methods for ToolBar
////////////////////////////////////////////////////////////
//
// Define class to RTTI
//
IMPLEMENT_CLASS( ToolBar, wxPanel );
//
// Custom event
//
DEFINE_EVENT_TYPE(EVT_TOOLBAR_UPDATED)
//
// Event table
//
BEGIN_EVENT_TABLE( ToolBar, wxPanel )
EVT_PAINT( ToolBar::OnPaint )
EVT_ERASE_BACKGROUND( ToolBar::OnErase )
END_EVENT_TABLE()
//
// Constructor
//
ToolBar::ToolBar( int type,
const wxString &label,
const wxString &section,
bool resizable )
: wxPanel()
{
// Save parameters
mType = type;
mLabel = label;
mSection = section;
mResizable = resizable;
// Initialize everything
mParent = NULL;
mHSizer = NULL;
mSpacer = NULL;
mDock = NULL;
mVisible = false;
mPositioned = false;
mGrabber = NULL;
mResizer = NULL;
SetId(mType);
}
//
// Destructor
//
ToolBar::~ToolBar()
{
}
//
// Returns the toolbar title
//
wxString ToolBar::GetTitle()
{
/* i18n-hint: %s will be replaced by the name of the kind of toolbar.*/
return wxString::Format( _("Audacity %s Toolbar"), GetLabel().c_str() );
}
//
// Returns the toolbar label
//
wxString ToolBar::GetLabel()
{
return mLabel;
}
//
// Returns the toolbar preferences section
//
wxString ToolBar::GetSection()
{
return mSection;
}
//
// Returns the toolbar type
//
int ToolBar::GetType()
{
return mType;
}
//
// Set the toolbar label
//
void ToolBar::SetLabel(const wxString & label)
{
mLabel = label;
}
//
// Returns whether the toolbar is resizable or not
//
bool ToolBar::IsResizable()
{
return mResizable;
}
//
// Returns the dock state of the toolbar
//
bool ToolBar::IsDocked()
{
return mDock != NULL;
}
//
// Returns the visibility of the toolbar
//
bool ToolBar::IsVisible()
{
return mVisible;
}
void ToolBar::SetVisible( bool bVisible )
{
mVisible = bVisible;
}
//
// Show or hide the toolbar
//
bool ToolBar::Expose( bool show )
{
bool was = mVisible;
SetVisible( show );
if( IsDocked() )
{
Show( show );
}
else
{
wxWindow * pParent = GetParent();
if( !IsPositioned() && show ){
SetPositioned();
pParent->CentreOnParent();
pParent->Move( pParent->GetPosition() + wxSize( mType*10, mType*10 ));
}
pParent->Show( show );
}
return was;
}
//
// Initialize the toolbar
//
void ToolBar::Create( wxWindow *parent )
{
// Save parameters
mParent = parent;
// Create the window and label it
wxPanel::Create( mParent,
mType,
wxDefaultPosition,
wxDefaultSize,
wxNO_BORDER | wxTAB_TRAVERSAL,
GetTitle() );
wxPanel::SetLabel( GetLabel() );
// Go do the rest of the creation
ReCreateButtons();
// ToolManager depends on this appearing to be visible for proper dock construction
mVisible = true;
}
void ToolBar::ReCreateButtons()
{
// SetSizer(NULL) detaches mHSizer and deletes it.
// Do not use Detach() here, as that attempts to detach mHSizer from itself!
SetSizer( NULL );
// Get rid of any children we may have
DestroyChildren();
mGrabber = NULL;
mResizer = NULL;
{
// Create the main sizer
auto ms = std::make_unique<wxBoxSizer>(wxHORIZONTAL);
// Create the grabber and add it to the main sizer
mGrabber = safenew Grabber(this, mType);
ms->Add(mGrabber, 0, wxEXPAND | wxALIGN_LEFT | wxALIGN_TOP | wxRIGHT, 1);
// Use a box sizer for laying out controls
ms->Add((mHSizer = safenew wxBoxSizer(wxHORIZONTAL)), 1, wxEXPAND);
// (Re)Establish dock state
SetDocked(GetDock(), false);
// Go add all the rest of the gadgets
Populate();
// Add some space for the resize border
if (IsResizable())
{
// Create the resizer and add it to the main sizer
mResizer = safenew ToolBarResizer(this);
ms->Add(mResizer, 0, wxEXPAND | wxALIGN_TOP | wxLEFT, 1);
mResizer->SetToolTip(_("Click and drag to resize toolbar"));
}
// Set the sizer
SetSizerAndFit(ms.release());
}
// Recalculate the height to be a multiple of toolbarSingle
const int tbs = toolbarSingle + toolbarGap;
wxSize sz = GetSize();
sz.y = ( ( ( sz.y + tbs -1) / tbs ) * tbs ) - 1;
// Set the true AND minimum sizes and do final layout
if(IsResizable())
{
sz.SetWidth(GetMinToolbarWidth());
// JKC we're going to allow all resizable toolbars to be resized
// to 1 unit high!
wxSize sz2 = sz;
sz2.y = tbs -1;
SetMinSize(sz2);
sz.SetWidth(GetInitialWidth());
SetSize(sz);
}
else
{
SetInitialSize(sz);
}
Layout();
}
// The application preferences have changed, so update any elements that may
// depend on them.
void ToolBar::UpdatePrefs()
{
#if wxUSE_TOOLTIPS
// Change the tooltip of the grabber
if ( mGrabber )
{
mGrabber->SetToolTip( GetTitle() );
}
// Change the tooltip of the resizer
if ( mResizer )
{
mResizer->SetToolTip( _("Click and drag to resize toolbar") );
}
#endif
return;
}
//
// Return the pointer to the ToolBock where this bar lives
//
ToolDock *ToolBar::GetDock()
{
return mDock;
}
//
// Toggle the docked/floating state
//
void ToolBar::SetDocked( ToolDock *dock, bool pushed )
{
// Remember it
mDock = dock;
// Change the tooltip of the grabber
#if wxUSE_TOOLTIPS
mGrabber->SetToolTip( GetTitle() );
#endif
// Set the grabber button state
mGrabber->PushButton( pushed );
if (mResizer)
{
mResizer->Show(mDock != NULL);
Layout();
Fit();
}
}
//
// Notify parent of changes
//
void ToolBar::Updated()
{
wxCommandEvent e( EVT_TOOLBAR_UPDATED, GetId() );
GetParent()->GetEventHandler()->AddPendingEvent( e );
}
//
// Returns a pointer to the main sizer
//
wxBoxSizer *ToolBar::GetSizer()
{
return mHSizer;
}
//
// Add a window to the main sizer
//
void ToolBar::Add( wxWindow *window,
int proportion,
int flag,
int border,
wxObject* userData )
{
mHSizer->Add( window,
proportion,
flag,
border,
userData );
}
//
// Add a child sizer to the main sizer
//
void ToolBar::Add( wxSizer *sizer,
int proportion,
int flag,
int border,
wxObject* userData )
{
mHSizer->Add( sizer,
proportion,
flag,
border,
userData );
}
//
// Add some space to the main sizer
//
void ToolBar::Add( int width,
int height,
int proportion,
int flag,
int border,
wxObject* userData )
{
mHSizer->Add( width,
height,
proportion,
flag,
border,
userData );
}
//
// Adds a spacer to the main sizer
//
void ToolBar::AddSpacer( int size )
{
mHSizer->AddSpacer( size );
}
//
// Adds a strechable spacer to the main sizer
//
void ToolBar::AddStretchSpacer( int prop )
{
mHSizer->AddStretchSpacer( prop );
}
//
// Detach a window from the main sizer
//
void ToolBar::Detach( wxWindow *window )
{
mHSizer->Detach( window );
}
//
// Detach a child sizer from the main sizer
//
void ToolBar::Detach( wxSizer *sizer )
{
mHSizer->Detach( sizer );
}
void ToolBar::MakeMacRecoloredImage(teBmps eBmpOut, teBmps eBmpIn )
{
theTheme.ReplaceImage( eBmpOut, &theTheme.Image( eBmpIn ));
}
void ToolBar::MakeRecoloredImage( teBmps eBmpOut, teBmps eBmpIn )
{
wxImage * pPattern;
wxImage * pSrc = &theTheme.Image( eBmpIn );
#if defined( __WXGTK__ )
wxColour newColour = wxSystemSettings::GetColour( wxSYS_COLOUR_BACKGROUND );
#else
wxColour newColour = wxSystemSettings::GetColour( wxSYS_COLOUR_3DFACE );
#endif
wxColour baseColour = wxColour( 204, 204, 204 );
pPattern = ChangeImageColour( pSrc, baseColour, newColour );
theTheme.ReplaceImage( eBmpOut, pPattern);
delete pPattern;
}
void ToolBar:: MakeButtonBackgroundsLarge()
{
#ifdef USE_AQUA_THEME
MakeMacRecoloredImage( bmpRecoloredUpLarge, bmpMacUpButton );
MakeMacRecoloredImage( bmpRecoloredDownLarge, bmpMacDownButton );
MakeMacRecoloredImage( bmpRecoloredHiliteLarge, bmpMacHiliteButton );
#else
MakeRecoloredImage( bmpRecoloredUpLarge, bmpUpButtonLarge );
MakeRecoloredImage( bmpRecoloredDownLarge, bmpDownButtonLarge );
MakeRecoloredImage( bmpRecoloredHiliteLarge, bmpHiliteButtonLarge );
#endif
}
void ToolBar::MakeButtonBackgroundsSmall()
{
#ifdef USE_AQUA_THEME
MakeMacRecoloredImage( bmpRecoloredUpSmall, bmpMacUpButtonSmall );
MakeMacRecoloredImage( bmpRecoloredDownSmall, bmpMacDownButtonSmall );
MakeMacRecoloredImage( bmpRecoloredHiliteSmall, bmpMacHiliteButtonSmall );
#else
MakeRecoloredImage( bmpRecoloredUpSmall, bmpUpButtonSmall );
MakeRecoloredImage( bmpRecoloredDownSmall, bmpDownButtonSmall );
MakeRecoloredImage( bmpRecoloredHiliteSmall, bmpHiliteButtonSmall );
#endif
}
/// Makes a button and its four different state bitmaps
/// @param eUp Background for when button is Up.
/// @param eDown Background for when button is Down.
/// @param eHilite Background for when button is Hilit.
/// @param eStandardUp Foreground when enabled, up.
/// @param eStandardDown Foregrounde when enabled, down.
/// @param eDisabled Foreground when disabled.
/// @param id Windows Id.
/// @param placement Placement position
/// @param processdownevents true iff button handles down events.
/// @param size Size of the background.
AButton * ToolBar::MakeButton(teBmps eUp,
teBmps eDown,
teBmps eHilite,
teBmps eStandardUp,
teBmps eStandardDown,
teBmps eDisabled,
wxWindowID id,
wxPoint placement,
bool processdownevents,
wxSize size)
{
int xoff = (size.GetWidth() - theTheme.Image(eStandardUp).GetWidth())/2;
int yoff = (size.GetHeight() - theTheme.Image(eStandardUp).GetHeight())/2;
typedef std::unique_ptr<wxImage> wxImagePtr;
wxImagePtr up2 (OverlayImage(eUp, eStandardUp, xoff, yoff));
wxImagePtr hilite2 (OverlayImage(eHilite, eStandardUp, xoff, yoff));
wxImagePtr down2 (OverlayImage(eDown, eStandardDown, xoff + 1, yoff + 1));
wxImagePtr disable2 (OverlayImage(eUp, eDisabled, xoff, yoff));
AButton * button =
new AButton(this, id, placement, size, *up2, *hilite2, *down2,
*disable2, processdownevents);
return button;
}
//static
void ToolBar::MakeAlternateImages(AButton &button, int idx,
teBmps eUp,
teBmps eDown,
teBmps eHilite,
teBmps eStandardUp,
teBmps eStandardDown,
teBmps eDisabled,
wxSize size)
{
int xoff = (size.GetWidth() - theTheme.Image(eStandardUp).GetWidth())/2;
int yoff = (size.GetHeight() - theTheme.Image(eStandardUp).GetHeight())/2;
typedef std::unique_ptr<wxImage> wxImagePtr;
wxImagePtr up (OverlayImage(eUp, eStandardUp, xoff, yoff));
wxImagePtr hilite (OverlayImage(eHilite, eStandardUp, xoff, yoff));
wxImagePtr down (OverlayImage(eDown, eStandardDown, xoff + 1, yoff + 1));
wxImagePtr disable (OverlayImage(eUp, eDisabled, xoff, yoff));
button.SetAlternateImages(idx, *up, *hilite, *down, *disable);
}
//
// This changes the state a button (from up to down or vice versa)
//
void ToolBar::SetButton( bool down, AButton * button )
{
if( down )
{
button->PushDown();
}
else
{
button->PopUp();
}
}
//
// Handle background erasure
//
void ToolBar::OnErase( wxEraseEvent & WXUNUSED(event) )
{
// Ignore it to prevent flashing
}
//
// This draws the background of a toolbar
//
void ToolBar::OnPaint( wxPaintEvent & event )
{
wxPaintDC dc( (wxWindow *) event.GetEventObject() );
// Start with a clean background
//
// Under GTK, we specifically set the toolbar background to the background
// colour in the system theme.
#if defined( __WXGTK__ )
dc.SetBackground( wxBrush( wxSystemSettings::GetColour( wxSYS_COLOUR_BACKGROUND ) ) );
#endif
dc.Clear();
// EXPERIMENTAL_THEMING is set to not apply the gradient
// on wxMAC builds. on wxMAC we have the AQUA_THEME.
#ifdef USE_AQUA_THEME
Repaint( &dc );
#else
#ifdef EXPERIMENTAL_THEMING
wxImage * mpBackGradient = &theTheme.Image( bmpRecoloredUpLarge );
if( mpBackGradient != NULL )
{
wxSize imSz( mpBackGradient->GetWidth(), mpBackGradient->GetHeight() );
wxSize sz = GetSize();
int y;
for(y=0;y<sz.y;y++)
{
int yPix = ((float)y * imSz.y - 1.0f)/(sz.y-1);
wxColour col(
mpBackGradient->GetRed( 0, yPix),
mpBackGradient->GetGreen( 0, yPix),
mpBackGradient->GetBlue( 0, yPix));
// Set background colour so that controls placed on this
// toolbar such as radio buttons will draw reasonably.
// It's a little tacky setting the background colour
// here, but we can't do it in the constructor as the gradient
// may not be available yet.
// Better than this would be to set the colour when the image
// is loaded.
// We use the colour at the half way point as a suitable 'average'.
if( y==(sz.y/2) )
{
SetBackgroundColour( col );
}
wxPen Pen( col );
dc.SetPen(Pen );
AColor::Line(dc, 0, y, sz.x, y );
}
}
#endif
#endif
}
int ToolBar::GetResizeGrabberWidth()
{
return RWIDTH;
}