mirror of
https://github.com/cookiengineer/audacity
synced 2025-05-02 00:29:41 +02:00
1267 lines
33 KiB
C++
1267 lines
33 KiB
C++
/**********************************************************************
|
|
|
|
Audacity: A Digital Audio Editor
|
|
|
|
ToolManager.cpp
|
|
|
|
Dominic Mazzoni
|
|
Shane T. Mueller
|
|
Leland Lucius
|
|
|
|
See ToolManager.h for details.
|
|
|
|
*******************************************************************//**
|
|
|
|
\file ToolManager.cpp
|
|
|
|
Implements ToolManager
|
|
|
|
*//*******************************************************************//**
|
|
|
|
\class ToolManager
|
|
\brief Manages the ToolDocks and handles the dragging, floating, and
|
|
docking of ToolBars.
|
|
|
|
*//**********************************************************************/
|
|
|
|
#include "../Audacity.h"
|
|
|
|
// For compilers that support precompilation, includes "wx/wx.h".
|
|
#include <wx/wxprec.h>
|
|
|
|
#ifndef WX_PRECOMP
|
|
#include <wx/app.h>
|
|
#include <wx/defs.h>
|
|
#include <wx/event.h>
|
|
#include <wx/frame.h>
|
|
#include <wx/gdicmn.h>
|
|
#include <wx/intl.h>
|
|
#include <wx/region.h>
|
|
#include <wx/settings.h>
|
|
#include <wx/sysopt.h>
|
|
#include <wx/timer.h>
|
|
#include <wx/utils.h>
|
|
#include <wx/window.h>
|
|
#endif /* */
|
|
|
|
#include <wx/minifram.h>
|
|
#include <wx/popupwin.h>
|
|
|
|
#include "ToolManager.h"
|
|
#include "ControlToolBar.h"
|
|
#include "DeviceToolBar.h"
|
|
#include "EditToolBar.h"
|
|
#include "MeterToolBar.h"
|
|
#include "MixerToolBar.h"
|
|
#include "SelectionBar.h"
|
|
#include "SpectralSelectionBar.h"
|
|
#include "ToolsToolBar.h"
|
|
#include "TranscriptionToolBar.h"
|
|
|
|
#include "../AColor.h"
|
|
#include "../AllThemeResources.h"
|
|
#include "../AudioIO.h"
|
|
#include "../ImageManipulation.h"
|
|
#include "../Prefs.h"
|
|
#include "../Project.h"
|
|
#include "../Theme.h"
|
|
#include "../widgets/AButton.h"
|
|
#include "../widgets/Grabber.h"
|
|
|
|
////////////////////////////////////////////////////////////
|
|
/// Methods for ToolFrame
|
|
////////////////////////////////////////////////////////////
|
|
#define sizerW 11
|
|
|
|
//
|
|
// Constructor
|
|
//
|
|
class ToolFrame:public wxFrame
|
|
{
|
|
public:
|
|
|
|
ToolFrame( wxWindow *parent, ToolManager *manager, ToolBar *bar, wxPoint pos )
|
|
: wxFrame( parent,
|
|
bar->GetId(),
|
|
wxEmptyString,
|
|
pos,
|
|
wxDefaultSize,
|
|
wxNO_BORDER |
|
|
wxFRAME_NO_TASKBAR |
|
|
wxFRAME_TOOL_WINDOW |
|
|
wxFRAME_FLOAT_ON_PARENT )
|
|
{
|
|
int width = bar->GetSize().x;
|
|
int border;
|
|
|
|
// OSX doesn't need a border, but Windows and Linux do
|
|
border = 1;
|
|
#if defined(__WXMAC__)
|
|
border = 0;
|
|
|
|
// WXMAC doesn't support wxFRAME_FLOAT_ON_PARENT, so we do
|
|
//
|
|
// LL: I've commented this out because if you have, for instance, the meter
|
|
// toolbar undocked and large and then you open a dialog like an effect,
|
|
// the dialog may appear behind the dialog and you can't move either one.
|
|
//
|
|
// However, I'm leaving it here because I don't remember why I'd included
|
|
// it in the first place.
|
|
// SetWindowClass((WindowRef)d.MacGetWindowRef(), kFloatingWindowClass);
|
|
#endif
|
|
|
|
// Save parameters
|
|
mParent = parent;
|
|
mManager = manager;
|
|
mBar = bar;
|
|
|
|
// Transfer the bar to the ferry
|
|
bar->Reparent( this );
|
|
|
|
// We use a sizer to maintain proper spacing
|
|
wxBoxSizer *s = new wxBoxSizer( wxHORIZONTAL );
|
|
|
|
// Add the bar to the sizer
|
|
s->Add( bar, 1, wxEXPAND | wxALL, border );
|
|
|
|
// Add space for the resize grabber
|
|
if( bar->IsResizable() )
|
|
{
|
|
s->Add( sizerW, 1 );
|
|
width += sizerW;
|
|
}
|
|
|
|
SetSize( width + 2, bar->GetDockedSize().y + 2 );
|
|
|
|
// Attach the sizer and resize the window to fit
|
|
SetSizer( s );
|
|
Layout();
|
|
|
|
// Inform toolbar of change
|
|
bar->SetDocked( NULL, true );
|
|
|
|
// Make sure resizable floaters don't get any smaller than initial size
|
|
if( bar->IsResizable() )
|
|
{
|
|
// Calc the minimum size of the frame
|
|
mMinSize = bar->GetMinSize() + ( GetSize() - bar->GetSize() );
|
|
}
|
|
}
|
|
|
|
//
|
|
// Transition a toolbar from float to dragging
|
|
//
|
|
void OnGrabber( GrabberEvent & event )
|
|
{
|
|
// Pass it on to the manager since it isn't in the handling hierarchy
|
|
mManager->ProcessEvent( event );
|
|
}
|
|
|
|
//
|
|
// Handle toolbar updates
|
|
//
|
|
void OnToolBarUpdate( wxCommandEvent & event )
|
|
{
|
|
// Resize floater window to exactly contain toolbar
|
|
mBar->GetParent()->SetClientSize( mBar->GetMinSize() );
|
|
|
|
// Allow it to propagate to our parent
|
|
event.Skip();
|
|
}
|
|
|
|
//
|
|
// Handle frame paint events
|
|
//
|
|
void OnPaint( wxPaintEvent & WXUNUSED(event) )
|
|
{
|
|
wxPaintDC dc( this );
|
|
wxSize sz = GetSize();
|
|
wxRect r;
|
|
|
|
dc.SetPen( wxColour( 90, 90, 90 ) );
|
|
|
|
#if !defined(__WXMAC__)
|
|
dc.SetBackground(wxBrush(wxSystemSettings::GetColour(wxSYS_COLOUR_3DFACE)));
|
|
dc.Clear();
|
|
dc.SetBrush( *wxTRANSPARENT_BRUSH );
|
|
dc.DrawRectangle( 0, 0, sz.GetWidth(), sz.GetHeight() );
|
|
#endif
|
|
|
|
if( mBar->IsResizable() )
|
|
{
|
|
r.x = sz.x - sizerW - 2,
|
|
r.y = sz.y - sizerW - 2;
|
|
r.width = sizerW + 2;
|
|
r.height = sizerW + 2;
|
|
|
|
AColor::Line(dc, r.GetLeft(), r.GetBottom(), r.GetRight(), r.GetTop() );
|
|
AColor::Line(dc, r.GetLeft() + 3, r.GetBottom(), r.GetRight(), r.GetTop() + 3 );
|
|
AColor::Line(dc, r.GetLeft() + 6, r.GetBottom(), r.GetRight(), r.GetTop() + 6 );
|
|
AColor::Line(dc, r.GetLeft() + 9, r.GetBottom(), r.GetRight(), r.GetTop() + 9 );
|
|
}
|
|
|
|
}
|
|
|
|
void OnMotion( wxMouseEvent & event )
|
|
{
|
|
// Don't do anything if we're docked or not resizeable
|
|
if( mBar->IsDocked() || !mBar->IsResizable() )
|
|
{
|
|
return;
|
|
}
|
|
|
|
// Retrieve the mouse position
|
|
wxPoint pos = ClientToScreen( event.GetPosition() );
|
|
if( HasCapture() && event.Dragging() )
|
|
{
|
|
wxRect rect = GetRect();
|
|
|
|
rect.SetBottomRight( pos );
|
|
if( rect.width < mMinSize.x )
|
|
{
|
|
rect.width = mMinSize.x;
|
|
}
|
|
|
|
if( rect.height < mMinSize.y )
|
|
{
|
|
rect.height = mMinSize.y;
|
|
}
|
|
|
|
SetMinSize( rect.GetSize() );
|
|
SetSize( rect.GetSize() );
|
|
Layout();
|
|
Refresh( false );
|
|
}
|
|
else if( HasCapture() && event.LeftUp() )
|
|
{
|
|
ReleaseMouse();
|
|
}
|
|
else if( !HasCapture() )
|
|
{
|
|
wxRect rect = GetRect();
|
|
wxRect r;
|
|
|
|
r.x = rect.GetRight() - sizerW - 2,
|
|
r.y = rect.GetBottom() - sizerW - 2;
|
|
r.width = sizerW + 2;
|
|
r.height = sizerW + 2;
|
|
|
|
// Is left click within resize grabber?
|
|
if( r.Contains( pos ) && !event.Leaving() )
|
|
{
|
|
SetCursor( wxCURSOR_SIZENWSE );
|
|
if( event.LeftDown() )
|
|
{
|
|
CaptureMouse();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
SetCursor( wxCURSOR_ARROW );
|
|
}
|
|
}
|
|
}
|
|
|
|
void OnCaptureLost( wxMouseCaptureLostEvent & WXUNUSED(event) )
|
|
{
|
|
if( HasCapture() )
|
|
{
|
|
ReleaseMouse();
|
|
}
|
|
}
|
|
|
|
//
|
|
// Do not allow the window to close through keyboard accelerators
|
|
// (like ALT+F4 on Windows)
|
|
//
|
|
void OnClose( wxCloseEvent & event )
|
|
{
|
|
event.Veto();
|
|
}
|
|
|
|
private:
|
|
|
|
wxWindow *mParent;
|
|
ToolManager *mManager;
|
|
ToolBar *mBar;
|
|
wxSize mMinSize;
|
|
|
|
public:
|
|
|
|
DECLARE_CLASS( ToolFrame );
|
|
DECLARE_EVENT_TABLE();
|
|
};
|
|
|
|
IMPLEMENT_CLASS( ToolFrame, wxFrame );
|
|
|
|
BEGIN_EVENT_TABLE( ToolFrame, wxFrame )
|
|
EVT_GRABBER( wxID_ANY, ToolFrame::OnGrabber )
|
|
EVT_PAINT( ToolFrame::OnPaint )
|
|
EVT_MOUSE_EVENTS( ToolFrame::OnMotion )
|
|
EVT_MOUSE_CAPTURE_LOST( ToolFrame::OnCaptureLost )
|
|
EVT_CLOSE( ToolFrame::OnClose )
|
|
EVT_COMMAND( wxID_ANY, EVT_TOOLBAR_UPDATED, ToolFrame::OnToolBarUpdate )
|
|
END_EVENT_TABLE()
|
|
|
|
IMPLEMENT_CLASS( ToolManager, wxEvtHandler );
|
|
|
|
////////////////////////////////////////////////////////////
|
|
/// Methods for ToolManager
|
|
////////////////////////////////////////////////////////////
|
|
|
|
BEGIN_EVENT_TABLE( ToolManager, wxEvtHandler )
|
|
EVT_GRABBER( wxID_ANY, ToolManager::OnGrabber )
|
|
EVT_TIMER( wxID_ANY, ToolManager::OnTimer )
|
|
END_EVENT_TABLE()
|
|
|
|
//
|
|
// Constructor
|
|
//
|
|
ToolManager::ToolManager( AudacityProject *parent )
|
|
: wxEvtHandler()
|
|
{
|
|
wxPoint pt[ 3 ];
|
|
|
|
#if defined(__WXMAC__)
|
|
// Save original transition
|
|
mTransition = wxSystemOptions::GetOptionInt( wxMAC_WINDOW_PLAIN_TRANSITION );
|
|
#endif
|
|
|
|
// Initialize everything
|
|
mParent = parent;
|
|
mLastPos.x = mBarPos.x = -1;
|
|
mLastPos.y = mBarPos.y = -1;
|
|
mDragWindow = NULL;
|
|
mDragDock = NULL;
|
|
mDragBar = NULL;
|
|
|
|
// Create the down arrow
|
|
pt[ 0 ].x = 0;
|
|
pt[ 0 ].y = 0;
|
|
pt[ 1 ].x = 9;
|
|
pt[ 1 ].y = 9;
|
|
pt[ 2 ].x = 18;
|
|
pt[ 2 ].y = 0;
|
|
|
|
// Create the shaped region
|
|
mDown = new wxRegion( 3, &pt[0] );
|
|
|
|
// Create the down arrow
|
|
pt[ 0 ].x = 9;
|
|
pt[ 0 ].y = 0;
|
|
pt[ 1 ].x = 0;
|
|
pt[ 1 ].y = 9;
|
|
pt[ 2 ].x = 9;
|
|
pt[ 2 ].y = 18;
|
|
|
|
// Create the shaped region
|
|
mLeft = new wxRegion( 3, &pt[0] );
|
|
|
|
// Create the indicator frame
|
|
mIndicator = new wxFrame( NULL,
|
|
wxID_ANY,
|
|
wxEmptyString,
|
|
wxDefaultPosition,
|
|
wxSize( 32, 32 ),
|
|
wxFRAME_TOOL_WINDOW |
|
|
wxFRAME_SHAPED |
|
|
wxNO_BORDER |
|
|
wxFRAME_NO_TASKBAR |
|
|
wxSTAY_ON_TOP );
|
|
|
|
// Hook the creation event...only needed on GTK, but doesn't hurt for all
|
|
mIndicator->Connect( wxEVT_CREATE,
|
|
wxWindowCreateEventHandler( ToolManager::OnIndicatorCreate ),
|
|
NULL,
|
|
this );
|
|
|
|
// Hook the paint event...needed for all
|
|
mIndicator->Connect( wxEVT_PAINT,
|
|
wxPaintEventHandler( ToolManager::OnIndicatorPaint ),
|
|
NULL,
|
|
this );
|
|
|
|
// It's a little shy
|
|
mIndicator->Hide();
|
|
|
|
// Hook the parents mouse events...using the parent helps greatly
|
|
// under GTK
|
|
mParent->Connect( wxEVT_LEFT_UP,
|
|
wxMouseEventHandler( ToolManager::OnMouse ),
|
|
NULL,
|
|
this );
|
|
mParent->Connect( wxEVT_MOTION,
|
|
wxMouseEventHandler( ToolManager::OnMouse ),
|
|
NULL,
|
|
this );
|
|
mParent->Connect( wxEVT_MOUSE_CAPTURE_LOST,
|
|
wxMouseCaptureLostEventHandler( ToolManager::OnCaptureLost ),
|
|
NULL,
|
|
this );
|
|
|
|
// Create the top and bottom docks
|
|
mTopDock = new ToolDock( this, mParent, TopDockID );
|
|
mBotDock = new ToolDock( this, mParent, BotDockID );
|
|
|
|
// Create all of the toolbars
|
|
mBars[ ToolsBarID ] = new ToolsToolBar();
|
|
mBars[ TransportBarID ] = new ControlToolBar();
|
|
mBars[ RecordMeterBarID ] = new MeterToolBar( parent, RecordMeterBarID );
|
|
mBars[ PlayMeterBarID ] = new MeterToolBar( parent, PlayMeterBarID );
|
|
mBars[ MeterBarID ] = new MeterToolBar( parent, MeterBarID );
|
|
mBars[ EditBarID ] = new EditToolBar();
|
|
mBars[ MixerBarID ] = new MixerToolBar();
|
|
mBars[ TranscriptionBarID ] = new TranscriptionToolBar();
|
|
mBars[ SelectionBarID ] = new SelectionBar();
|
|
mBars[ DeviceBarID ] = new DeviceToolBar();
|
|
#ifdef EXPERIMENTAL_SPECTRAL_EDITING
|
|
mBars[SpectralSelectionBarID] = new SpectralSelectionBar();
|
|
#endif
|
|
|
|
// We own the timer
|
|
mTimer.SetOwner( this );
|
|
|
|
// Process the toolbar config settings
|
|
ReadConfig();
|
|
}
|
|
|
|
//
|
|
// Destructer
|
|
//
|
|
ToolManager::~ToolManager()
|
|
{
|
|
// Save the toolbar states
|
|
WriteConfig();
|
|
|
|
// Remove handlers from parent
|
|
mParent->Disconnect( wxEVT_LEFT_UP,
|
|
wxMouseEventHandler( ToolManager::OnMouse ),
|
|
NULL,
|
|
this );
|
|
mParent->Disconnect( wxEVT_MOTION,
|
|
wxMouseEventHandler( ToolManager::OnMouse ),
|
|
NULL,
|
|
this );
|
|
mParent->Disconnect( wxEVT_MOUSE_CAPTURE_LOST,
|
|
wxMouseCaptureLostEventHandler( ToolManager::OnCaptureLost ),
|
|
NULL,
|
|
this );
|
|
|
|
// Remove our event handlers
|
|
mIndicator->Disconnect( wxEVT_CREATE,
|
|
wxWindowCreateEventHandler( ToolManager::OnIndicatorCreate ),
|
|
NULL,
|
|
this );
|
|
mIndicator->Disconnect( wxEVT_PAINT,
|
|
wxPaintEventHandler( ToolManager::OnIndicatorPaint ),
|
|
NULL,
|
|
this );
|
|
|
|
// Must destroy the window since it doesn't have a parent
|
|
mIndicator->Destroy();
|
|
|
|
// Delete the indicator regions
|
|
delete mLeft;
|
|
delete mDown;
|
|
}
|
|
|
|
void ToolManager::Reset()
|
|
{
|
|
int ndx;
|
|
|
|
// Disconnect all docked bars
|
|
for( ndx = 0; ndx < ToolBarCount; ndx++ )
|
|
{
|
|
wxWindow *floater;
|
|
ToolDock *dock;
|
|
ToolBar *bar = mBars[ ndx ];
|
|
bool expose = true;
|
|
|
|
// Disconnect the bar
|
|
if( bar->IsDocked() )
|
|
{
|
|
bar->GetDock()->Undock( bar );
|
|
floater = NULL;
|
|
}
|
|
else
|
|
{
|
|
floater = bar->GetParent();
|
|
}
|
|
|
|
if (ndx == SelectionBarID
|
|
#ifdef EXPERIMENTAL_SPECTRAL_EDITING
|
|
|| ndx == SpectralSelectionBarID
|
|
#endif
|
|
)
|
|
{
|
|
dock = mBotDock;
|
|
|
|
wxCommandEvent e;
|
|
bar->GetEventHandler()->ProcessEvent(e);
|
|
}
|
|
else
|
|
{
|
|
dock = mTopDock;
|
|
bar->ReCreateButtons();
|
|
}
|
|
|
|
bar->EnableDisableButtons();
|
|
#if 0
|
|
if( bar->IsResizable() )
|
|
{
|
|
bar->SetSize(bar->GetBestFittingSize());
|
|
}
|
|
#endif
|
|
|
|
if( ndx == MeterBarID
|
|
#ifdef EXPERIMENTAL_SPECTRAL_EDITING
|
|
|| ndx == SpectralSelectionBarID
|
|
#endif
|
|
)
|
|
{
|
|
expose = false;
|
|
}
|
|
|
|
if( dock != NULL )
|
|
{
|
|
// when we dock, we reparent, so bar is no longer a child of floater.
|
|
dock->Dock( bar );
|
|
Expose( ndx, expose );
|
|
//OK (and good) to delete floater, as bar is no longer in it.
|
|
if( floater )
|
|
floater->Destroy();
|
|
}
|
|
else
|
|
{
|
|
// The (tool)bar has a dragger window round it, the floater.
|
|
// in turn floater will have mParent (the entire App) as its
|
|
// parent.
|
|
|
|
// Maybe construct a new floater
|
|
// this happens if we have just been bounced out of a dock.
|
|
if( floater == NULL ) {
|
|
floater = new ToolFrame( mParent, this, bar, wxPoint(-1,-1) );
|
|
bar->Reparent( floater );
|
|
}
|
|
|
|
// This bar is undocked and invisible.
|
|
// We are doing a reset toolbars, so even the invisible undocked bars should
|
|
// be moved somewhere sensible. Put bar near center of window.
|
|
// If there were multiple hidden toobars the ndx * 10 adjustment means
|
|
// they won't overlap too much.
|
|
floater->CentreOnParent( );
|
|
floater->Move( floater->GetPosition() + wxSize( ndx * 10 - 200, ndx * 10 ));
|
|
bar->SetDocked( NULL, false );
|
|
Expose( ndx, false );
|
|
}
|
|
|
|
}
|
|
// TODO:??
|
|
// If audio was playing, we stopped the VU meters,
|
|
// It would be nice to show them again, but hardly essential as
|
|
// they will show up again on the next play.
|
|
// SetVUMeters(AudacityProject *p);
|
|
LayoutToolBars();
|
|
Updated();
|
|
}
|
|
|
|
//
|
|
// Read the toolbar states
|
|
//
|
|
void ToolManager::ReadConfig()
|
|
{
|
|
wxString oldpath = gPrefs->GetPath();
|
|
wxArrayInt unordered[ DockCount ];
|
|
int order[ DockCount ][ ToolBarCount ];
|
|
bool show[ ToolBarCount ];
|
|
int width[ ToolBarCount ];
|
|
int height[ ToolBarCount ];
|
|
int x, y;
|
|
int dock, ord, ndx;
|
|
|
|
#if defined(__WXMAC__)
|
|
// Disable window animation
|
|
wxSystemOptions::SetOption( wxMAC_WINDOW_PLAIN_TRANSITION, 1 );
|
|
#endif
|
|
|
|
// Invalidate all order entries
|
|
for( dock = 0; dock < DockCount; dock++ )
|
|
{
|
|
for( ord = 0; ord < ToolBarCount; ord++ )
|
|
{
|
|
order[ dock ][ ord ] = NoBarID;
|
|
}
|
|
}
|
|
|
|
// Change to the bar root
|
|
gPrefs->SetPath( wxT("/GUI/ToolBars") );
|
|
|
|
// Load and apply settings for each bar
|
|
for( ndx = 0; ndx < ToolBarCount; ndx++ )
|
|
{
|
|
ToolBar *bar = mBars[ ndx ];
|
|
//wxPoint Center = mParent->GetPosition() + (mParent->GetSize() * 0.33);
|
|
//wxPoint Center(
|
|
// wxSystemSettings::GetMetric( wxSYS_SCREEN_X ) /2 ,
|
|
// wxSystemSettings::GetMetric( wxSYS_SCREEN_Y ) /2 );
|
|
|
|
// Change to the bar subkey
|
|
gPrefs->SetPath( bar->GetSection() );
|
|
|
|
bool bShownByDefault = true;
|
|
int defaultDock = TopDockID;
|
|
|
|
if( ndx == SelectionBarID )
|
|
defaultDock = BotDockID;
|
|
if( ndx == MeterBarID )
|
|
bShownByDefault = false;
|
|
|
|
#ifdef EXPERIMENTAL_SPECTRAL_EDITING
|
|
if( ndx == SpectralSelectionBarID ){
|
|
defaultDock = BotDockID;
|
|
bShownByDefault = false; // Only show if asked for.
|
|
}
|
|
#endif
|
|
|
|
// Read in all the settings
|
|
gPrefs->Read( wxT("Dock"), &dock, defaultDock );
|
|
gPrefs->Read( wxT("Order"), &ord, NoBarID );
|
|
gPrefs->Read( wxT("Show"), &show[ ndx ], bShownByDefault);
|
|
|
|
gPrefs->Read( wxT("X"), &x, -1 );
|
|
gPrefs->Read( wxT("Y"), &y, -1 );
|
|
gPrefs->Read( wxT("W"), &width[ ndx ], -1 );
|
|
gPrefs->Read( wxT("H"), &height[ ndx ], -1 );
|
|
|
|
bar->SetVisible( show[ ndx ] );
|
|
|
|
// Docked or floating?
|
|
if( dock )
|
|
{
|
|
// Default to top dock if the ID isn't valid
|
|
if( dock < NoDockID || dock > DockCount ) {
|
|
dock = TopDockID;
|
|
}
|
|
|
|
// Create the bar with the correct parent
|
|
if( dock == TopDockID )
|
|
{
|
|
bar->Create( mTopDock );
|
|
}
|
|
else
|
|
{
|
|
bar->Create( mBotDock );
|
|
}
|
|
|
|
// Set the width and height
|
|
if( width[ ndx ] != -1 && height[ ndx ] != -1 )
|
|
{
|
|
wxSize sz( width[ ndx ], height[ ndx ] );
|
|
bar->SetSize( sz );
|
|
}
|
|
|
|
#ifdef EXPERIMENTAL_SYNC_LOCK
|
|
// Set the width
|
|
if( width[ ndx ] >= bar->GetSize().x )
|
|
{
|
|
wxSize sz( width[ ndx ], bar->GetSize().y );
|
|
bar->SetSize( sz );
|
|
bar->Layout();
|
|
}
|
|
#else
|
|
// note that this section is here because if you had been using sync-lock and now you aren't,
|
|
// the space for the extra button is stored in audacity.cfg, and so you get an extra space
|
|
// in the EditToolbar.
|
|
// It is needed so that the meterToolbar size gets preserved.
|
|
// Longer-term we should find a better fix for this.
|
|
wxString thisBar = bar->GetSection();
|
|
if( thisBar != wxT("Edit"))
|
|
{
|
|
// Set the width
|
|
if( width[ ndx ] >= bar->GetSize().x )
|
|
{
|
|
wxSize sz( width[ ndx ], bar->GetSize().y );
|
|
bar->SetSize( sz );
|
|
bar->Layout();
|
|
}
|
|
}
|
|
#endif
|
|
// Is order within range and unoccupied?
|
|
if( ( ord >= 0 ) &&
|
|
( ord < ToolBarCount ) &&
|
|
( order[ dock - 1 ][ ord ] == NoBarID ) )
|
|
{
|
|
// Insert at ordered location
|
|
order[ dock - 1 ][ ord ] = ndx;
|
|
}
|
|
else
|
|
{
|
|
// These must go at the end
|
|
unordered[ dock - 1 ].Add( ndx );
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Create the bar (with the top dock being temporary parent)
|
|
bar->Create( mTopDock );
|
|
|
|
|
|
|
|
// Construct a new floater
|
|
ToolFrame *f = new ToolFrame( mParent, this, bar, wxPoint( x, y ) );
|
|
|
|
// Set the width and height
|
|
if( width[ ndx ] != -1 && height[ ndx ] != -1 )
|
|
{
|
|
wxSize sz( width[ ndx ], height[ ndx ] );
|
|
f->SetSizeHints( sz );
|
|
f->SetSize( sz );
|
|
f->Layout();
|
|
if( (x!=-1) && (y!=-1) )
|
|
bar->SetPositioned();
|
|
}
|
|
|
|
// Inform toolbar of change
|
|
bar->SetDocked( NULL, false );
|
|
|
|
// Show or hide it
|
|
Expose( ndx, show[ ndx ] );
|
|
}
|
|
|
|
// Change back to the bar root
|
|
//gPrefs->SetPath( wxT("..") ); <-- Causes a warning...
|
|
// May or may not have gone into a subdirectory,
|
|
// so use an absolute path.
|
|
gPrefs->SetPath( wxT("/GUI/ToolBars") );
|
|
}
|
|
|
|
// Add all toolbars to their target dock
|
|
for( dock = 0; dock < DockCount; dock++ )
|
|
{
|
|
ToolDock *d = ( dock + 1 == TopDockID ? mTopDock : mBotDock );
|
|
|
|
// Add all ordered toolbars
|
|
for( ord = 0; ord < ToolBarCount; ord++ )
|
|
{
|
|
ndx = order[ dock ][ ord ];
|
|
|
|
// Bypass empty slots
|
|
if( ndx != NoBarID )
|
|
{
|
|
ToolBar *t = mBars[ ndx ];
|
|
|
|
// Dock it
|
|
d->Dock( t );
|
|
|
|
// Show or hide it
|
|
Expose( t->GetId(), show[ t->GetId() ] );
|
|
}
|
|
}
|
|
|
|
// Add all unordered toolbars
|
|
for( ord = 0; ord < (int) unordered[ dock ].GetCount(); ord++ )
|
|
{
|
|
ToolBar *t = mBars[ unordered[ dock ][ ord ] ];
|
|
|
|
// Dock it
|
|
d->Dock( t );
|
|
|
|
// Show or hide the bar
|
|
Expose( t->GetId(), show[ t->GetId() ] );
|
|
}
|
|
}
|
|
|
|
// Restore original config path
|
|
gPrefs->SetPath( oldpath );
|
|
|
|
#if defined(__WXMAC__)
|
|
// Reinstate original transition
|
|
wxSystemOptions::SetOption( wxMAC_WINDOW_PLAIN_TRANSITION, mTransition );
|
|
#endif
|
|
}
|
|
|
|
//
|
|
// Save the toolbar states
|
|
//
|
|
void ToolManager::WriteConfig()
|
|
{
|
|
if( !gPrefs )
|
|
{
|
|
return;
|
|
}
|
|
|
|
wxString oldpath = gPrefs->GetPath();
|
|
int ndx;
|
|
|
|
// Change to the bar root
|
|
gPrefs->SetPath( wxT("/GUI/ToolBars") );
|
|
|
|
// Save state of each bar
|
|
for( ndx = 0; ndx < ToolBarCount; ndx++ )
|
|
{
|
|
ToolBar *bar = mBars[ ndx ];
|
|
|
|
// Change to the bar subkey
|
|
gPrefs->SetPath( bar->GetSection() );
|
|
|
|
// Search both docks for toolbar order
|
|
int to = mTopDock->GetOrder( bar );
|
|
int bo = mBotDock->GetOrder( bar );
|
|
|
|
// Save
|
|
gPrefs->Write( wxT("Dock"), (int) (to ? TopDockID : bo ? BotDockID : NoDockID ));
|
|
gPrefs->Write( wxT("Order"), to + bo );
|
|
gPrefs->Write( wxT("Show"), IsVisible( ndx ) );
|
|
|
|
wxPoint pos( -1, -1 );
|
|
wxSize sz = bar->GetSize();
|
|
if( !bar->IsDocked() && bar->IsPositioned() )
|
|
{
|
|
pos = bar->GetParent()->GetPosition();
|
|
sz = bar->GetParent()->GetSize();
|
|
}
|
|
gPrefs->Write( wxT("X"), pos.x );
|
|
gPrefs->Write( wxT("Y"), pos.y );
|
|
gPrefs->Write( wxT("W"), sz.x );
|
|
gPrefs->Write( wxT("H"), sz.y );
|
|
|
|
// Kill the bar
|
|
bar->Destroy();
|
|
|
|
// Change back to the bar root
|
|
gPrefs->SetPath( wxT("..") );
|
|
}
|
|
|
|
// Restore original config path
|
|
gPrefs->SetPath( oldpath );
|
|
gPrefs->Flush();
|
|
}
|
|
|
|
//
|
|
// Return a pointer to the specified toolbar
|
|
//
|
|
ToolBar *ToolManager::GetToolBar( int type ) const
|
|
{
|
|
return mBars[ type ];
|
|
}
|
|
|
|
//
|
|
// Return a pointer to the top dock
|
|
//
|
|
ToolDock *ToolManager::GetTopDock()
|
|
{
|
|
return mTopDock;
|
|
}
|
|
|
|
//
|
|
// Return a pointer to the bottom dock
|
|
//
|
|
ToolDock *ToolManager::GetBotDock()
|
|
{
|
|
return mBotDock;
|
|
}
|
|
|
|
//
|
|
// Queues an EVT_TOOLBAR_UPDATED command event to notify any
|
|
// interest parties of an updated toolbar or dock layout
|
|
//
|
|
void ToolManager::Updated()
|
|
{
|
|
// Queue an update event
|
|
wxCommandEvent e( EVT_TOOLBAR_UPDATED );
|
|
mParent->GetEventHandler()->AddPendingEvent( e );
|
|
}
|
|
|
|
//
|
|
// Return docked state of specified toolbar
|
|
//
|
|
bool ToolManager::IsDocked( int type )
|
|
{
|
|
return mBars[ type ]->IsDocked();
|
|
}
|
|
|
|
//
|
|
// Returns the visibility of the specified toolbar
|
|
//
|
|
bool ToolManager::IsVisible( int type )
|
|
{
|
|
ToolBar *t = mBars[ type ];
|
|
|
|
return t->IsVisible();
|
|
|
|
#if 0
|
|
// If toolbar is floating
|
|
if( !t->IsDocked() )
|
|
{
|
|
// Must return state of floater window
|
|
return t->GetParent()->IsShown();
|
|
}
|
|
|
|
// Return state of docked toolbar
|
|
return t->IsShown();
|
|
#endif
|
|
}
|
|
|
|
//
|
|
// Toggles the visible/hidden state of a toolbar
|
|
//
|
|
void ToolManager::ShowHide( int type )
|
|
{
|
|
Expose( type, !mBars[ type ]->IsVisible() );
|
|
}
|
|
|
|
//
|
|
// Set the visible/hidden state of a toolbar
|
|
//
|
|
void ToolManager::Expose( int type, bool show )
|
|
{
|
|
ToolBar *t = mBars[ type ];
|
|
|
|
// Handle docked and floaters differently
|
|
if( t->IsDocked() )
|
|
{
|
|
t->GetDock()->Expose( type, show );
|
|
}
|
|
else
|
|
{
|
|
t->Expose( show );
|
|
}
|
|
}
|
|
|
|
//
|
|
// Ask both docks to (re)layout their bars
|
|
//
|
|
void ToolManager::LayoutToolBars()
|
|
{
|
|
// Update the layout
|
|
mTopDock->LayoutToolBars();
|
|
mBotDock->LayoutToolBars();
|
|
}
|
|
|
|
//
|
|
// Tell the toolbars that preferences have been updated
|
|
//
|
|
void ToolManager::UpdatePrefs()
|
|
{
|
|
for( int ndx = 0; ndx < ToolBarCount; ndx++ )
|
|
{
|
|
ToolBar *bar = mBars[ ndx ];
|
|
if( bar )
|
|
{
|
|
bar->UpdatePrefs();
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// Handle toolbar dragging
|
|
//
|
|
void ToolManager::OnMouse( wxMouseEvent & event )
|
|
{
|
|
// Go ahead and set the event to propagate
|
|
event.Skip();
|
|
|
|
// Can't do anything if we're not dragging. This also prevents
|
|
// us from intercepting events that don't belong to us from the
|
|
// parent since we're Connect()ed to a couple.
|
|
if( !mDragWindow )
|
|
{
|
|
return;
|
|
}
|
|
|
|
#if defined(__WXMAC__)
|
|
// Disable window animation
|
|
wxSystemOptions::SetOption( wxMAC_WINDOW_PLAIN_TRANSITION, 1 );
|
|
#endif
|
|
|
|
// Retrieve the event position
|
|
wxPoint pos =
|
|
( (wxWindow *)event.GetEventObject() )->ClientToScreen( event.GetPosition() ) - mDragOffset;
|
|
|
|
// Button was released...finish the drag
|
|
if( !event.LeftIsDown() )
|
|
{
|
|
// Release capture
|
|
if( mParent->HasCapture() )
|
|
{
|
|
mParent->ReleaseMouse();
|
|
}
|
|
|
|
// Hide the indicator
|
|
mIndicator->Hide();
|
|
|
|
// Transition the bar to a dock
|
|
if( mDragDock && !event.ShiftDown() )
|
|
{
|
|
// Trip over...everyone ashore that's going ashore...
|
|
mDragDock->Dock( mDragBar, mDragBefore );
|
|
|
|
// Done with the floater
|
|
mDragWindow->Destroy();
|
|
mDragBar->Refresh(false);
|
|
}
|
|
else
|
|
{
|
|
// Calling SetDocked() to force the grabber button to popup
|
|
mDragBar->SetDocked( NULL, false );
|
|
}
|
|
|
|
// Done dragging
|
|
mDragWindow = NULL;
|
|
mDragDock = NULL;
|
|
mDragBar = NULL;
|
|
mLastPos.x = mBarPos.x = -1;
|
|
mLastPos.y = mBarPos.y = -1;
|
|
mTimer.Stop();
|
|
}
|
|
else if( event.Dragging() && pos != mLastPos )
|
|
{
|
|
// Make toolbar follow the mouse
|
|
mDragWindow->Move( pos );
|
|
|
|
// Remember to prevent excessive movement
|
|
mLastPos = pos;
|
|
|
|
// Calc the top dock hittest rectangle
|
|
wxRect tr = mTopDock->GetRect();
|
|
tr.SetBottom( tr.GetBottom() + 10 );
|
|
tr.SetPosition( mTopDock->GetParent()->ClientToScreen( tr.GetPosition() ) );
|
|
|
|
// Calc the bottom dock hittest rectangle
|
|
wxRect br = mBotDock->GetRect();
|
|
br.SetTop( br.GetTop() - 10 );
|
|
br.SetBottom( br.GetBottom() + 20 );
|
|
br.SetPosition( mBotDock->GetParent()->ClientToScreen( br.GetPosition() ) );
|
|
|
|
|
|
// Add half the bar height. We could use the actual bar height, but that would be confusing as a
|
|
// bar removed at a place might not dock back there if just let go.
|
|
// Also add 5 pixels in horizontal direction, so that a click without a move (or a very small move)
|
|
// lands back where we started.
|
|
pos += wxPoint( 5, 20 );
|
|
|
|
|
|
// To find which dock, rather than test against pos, test against the whole dragger rect.
|
|
// This means it is enough to overlap the dock to dock with it.
|
|
wxRect barRect = mDragWindow->GetRect();
|
|
ToolDock *dock = NULL;
|
|
if( tr.Intersects( barRect ) )
|
|
dock = mTopDock;
|
|
else if( br.Intersects( barRect ) )
|
|
dock = mBotDock;
|
|
|
|
// Looks like we have a winner...
|
|
if( dock )
|
|
{
|
|
wxPoint p;
|
|
wxRect r;
|
|
|
|
// Calculate where the bar would be placed
|
|
mDragBefore = dock->PositionBar( mDragBar, pos, r );
|
|
|
|
// If different than the last time, the indicator must be moved
|
|
if( r != mBarPos )
|
|
{
|
|
wxRect dr = dock->GetRect();
|
|
|
|
// Hide the indicator before changing the shape
|
|
mIndicator->Hide();
|
|
|
|
// Decide which direction the arrow should point
|
|
if( r.GetTop() >= dr.GetHeight() )
|
|
{
|
|
p.x = dr.GetLeft() + ( dr.GetWidth() / 2 );
|
|
p.y = dr.GetBottom() - mDown->GetBox().GetHeight();
|
|
mCurrent = mDown;
|
|
}
|
|
else
|
|
{
|
|
p.x = dr.GetLeft() + r.GetLeft();
|
|
p.y = dr.GetTop() + r.GetTop() + mLeft->GetBox().GetHeight() / 2;
|
|
//JKC ( ( r.GetHeight() - mLeft->GetBox().GetHeight() ) / 2 );
|
|
mCurrent = mLeft;
|
|
}
|
|
|
|
// Change the shape while hidden and then show it if okay
|
|
mIndicator->SetShape( *mCurrent );
|
|
if( !event.ShiftDown() )
|
|
{
|
|
mIndicator->Show();
|
|
mIndicator->Update();
|
|
}
|
|
|
|
// Move it into position
|
|
// LL: Do this after the Show() since KDE doesn't move the window
|
|
// if it's not shown. (Do it outside if the previous IF as well)
|
|
mIndicator->Move( dock->GetParent()->ClientToScreen( p ) );
|
|
|
|
// Remember for next go round
|
|
mBarPos = r;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Hide the indicator if it's still shown
|
|
if( mBarPos.x != -1 )
|
|
{
|
|
// Hide any
|
|
mIndicator->Hide();
|
|
mBarPos.x = -1;
|
|
mBarPos.y = -1;
|
|
}
|
|
}
|
|
|
|
// Remember to which dock the drag bar belongs.
|
|
mDragDock = dock;
|
|
}
|
|
|
|
#if defined(__WXMAC__)
|
|
// Reinstate original transition
|
|
wxSystemOptions::SetOption( wxMAC_WINDOW_PLAIN_TRANSITION, mTransition );
|
|
#endif
|
|
}
|
|
|
|
//
|
|
// Deal with new capture lost event
|
|
//
|
|
void ToolManager::OnCaptureLost( wxMouseCaptureLostEvent & event )
|
|
{
|
|
// Can't do anything if we're not dragging. This also prevents
|
|
// us from intercepting events that don't belong to us from the
|
|
// parent since we're Connect()ed to a couple.
|
|
if( !mDragWindow )
|
|
{
|
|
event.Skip();
|
|
return;
|
|
}
|
|
|
|
// Simulate button up
|
|
wxMouseEvent e(wxEVT_LEFT_UP);
|
|
e.SetEventObject(mParent);
|
|
OnMouse(e);
|
|
}
|
|
|
|
//
|
|
// Watch for shift key changes
|
|
//
|
|
void ToolManager::OnTimer( wxTimerEvent & event )
|
|
{
|
|
// Go ahead and set the event to propagate
|
|
event.Skip();
|
|
|
|
// Can't do anything if we're not dragging. This also prevents
|
|
// us from intercepting events that don't belong to us from the
|
|
// parent since we're Connect()ed to a couple.
|
|
if( !mDragWindow )
|
|
{
|
|
return;
|
|
}
|
|
|
|
bool state = wxGetKeyState( WXK_SHIFT );
|
|
if( mLastState != state )
|
|
{
|
|
mLastState = state;
|
|
|
|
#if defined(__WXMAC__)
|
|
// Disable window animation
|
|
wxSystemOptions::SetOption( wxMAC_WINDOW_PLAIN_TRANSITION, 1 );
|
|
#endif
|
|
|
|
mIndicator->Show( !state );
|
|
|
|
#if defined(__WXMAC__)
|
|
// Disable window animation
|
|
wxSystemOptions::SetOption( wxMAC_WINDOW_PLAIN_TRANSITION, mTransition );
|
|
#endif
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
//
|
|
// Handle Indicator paint events
|
|
//
|
|
// Really only needed for the Mac since SetBackgroundColour()
|
|
// doesn't seem to work with shaped frames.
|
|
//
|
|
void ToolManager::OnIndicatorPaint( wxPaintEvent & event )
|
|
{
|
|
wxWindow *w = (wxWindow *)event.GetEventObject();
|
|
wxPaintDC dc( w );
|
|
dc.SetBackground( *wxBLUE_BRUSH );
|
|
dc.Clear();
|
|
}
|
|
|
|
//
|
|
// Handle Indicator creation event
|
|
//
|
|
// Without this, the initial Indicator window will be a solid blue square
|
|
// until the next time it changes.
|
|
//
|
|
void ToolManager::OnIndicatorCreate( wxWindowCreateEvent & event )
|
|
{
|
|
#if defined(__WXGTK__)
|
|
mIndicator->SetShape( *mCurrent );
|
|
#endif
|
|
event.Skip();
|
|
}
|
|
|
|
//
|
|
// Transition a toolbar from float to dragging
|
|
//
|
|
void ToolManager::OnGrabber( GrabberEvent & event )
|
|
{
|
|
// No need to propagate any further
|
|
event.Skip( false );
|
|
|
|
// Remember which bar we're dragging
|
|
mDragBar = mBars[ event.GetId() ];
|
|
|
|
// Calculate the drag offset
|
|
wxPoint mp = event.GetPosition();
|
|
mDragOffset = mp -
|
|
mDragBar->GetParent()->ClientToScreen( mDragBar->GetPosition() ) +
|
|
wxPoint( 1, 1 );
|
|
|
|
// Must set the bar afloat if it's currently docked
|
|
if( mDragBar->IsDocked() )
|
|
{
|
|
#if defined(__WXMAC__)
|
|
// Disable window animation
|
|
wxSystemOptions::SetOption( wxMAC_WINDOW_PLAIN_TRANSITION, 1 );
|
|
#endif
|
|
|
|
// Adjust the starting position
|
|
mp -= mDragOffset;
|
|
|
|
// Inform toolbar of change
|
|
mDragBar->SetDocked( NULL, true );
|
|
mDragBar->SetPositioned();
|
|
|
|
// Construct a new floater
|
|
mDragWindow = new ToolFrame( mParent, this, mDragBar, mp );
|
|
|
|
// Make sure the ferry is visible
|
|
mDragWindow->Show();
|
|
|
|
// Notify parent of change
|
|
Updated();
|
|
|
|
#if defined(__WXMAC__)
|
|
// Reinstate original transition
|
|
wxSystemOptions::SetOption( wxMAC_WINDOW_PLAIN_TRANSITION, mTransition );
|
|
#endif
|
|
}
|
|
else
|
|
{
|
|
mDragWindow = (ToolFrame *) mDragBar->GetParent();
|
|
}
|
|
|
|
// We want all mouse events from this point on
|
|
mParent->CaptureMouse();
|
|
|
|
// Start monitoring shift key changes
|
|
mLastState = wxGetKeyState( WXK_SHIFT );
|
|
mTimer.Start( 100 );
|
|
}
|