1
0
mirror of https://github.com/cookiengineer/audacity synced 2025-07-26 01:18:06 +02:00
audacity/src/toolbars/ToolManager.cpp
v.audacity 0e287db4c9 Turns out the failsafe I added yesterday in r11929 was the correct solution to bug 367. This commit just adjusts the comments.
This also has the fix for bug 232, derived from the patch by Gaurav Agarwal.

Also changed several lingering "linking" comments to "sync-lock", the agreed on current terminology.
2012-08-20 20:26:42 +00:00

1214 lines
30 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>
#if defined(__WXMAC__)
#include <wx/mac/uma.h>
#endif
#include "ToolManager.h"
#include "ControlToolBar.h"
#include "DeviceToolBar.h"
#include "EditToolBar.h"
#include "MeterToolBar.h"
#include "MixerToolBar.h"
#include "SelectionBar.h"
#include "ToolsToolBar.h"
#include "TranscriptionToolBar.h"
#include "../AColor.h"
#include "../AllThemeResources.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->GetMinSize().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 & 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 & 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[ MeterBarID ] = new MeterToolBar();
mBars[ EditBarID ] = new EditToolBar();
mBars[ MixerBarID ] = new MixerToolBar();
mBars[ TranscriptionBarID ] = new TranscriptionToolBar();
mBars[ SelectionBarID ] = new SelectionBar();
mBars[ DeviceBarID ] = new DeviceToolBar();
// 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;
// The mInputMeter and mOutputMeter may be in use if audio is playing
// when this happens.
gAudioIO->SetMeters( NULL, NULL );
// Disconnect all docked bars
for( ndx = 0; ndx < ToolBarCount; ndx++ )
{
wxWindow *parent;
ToolDock *dock;
ToolBar *bar = mBars[ ndx ];
// Disconnect the bar
if( bar->IsDocked() )
{
bar->GetDock()->Undock( bar );
parent = NULL;
}
else
{
parent = bar->GetParent();
}
if( ndx == SelectionBarID )
{
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
dock->Dock( bar );
Expose( ndx, true );
if( parent )
{
parent->Destroy();
}
}
// 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 ];
// Change to the bar subkey
gPrefs->SetPath( bar->GetSection() );
// Read in all the settings
gPrefs->Read( wxT("Dock"), &dock, ndx == SelectionBarID ? BotDockID : TopDockID );
gPrefs->Read( wxT("Order"), &ord, NoBarID );
gPrefs->Read( wxT("Show"), &show[ ndx ], true );
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 );
// 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 );
}
#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();
}
// Show or hide it
bar->Expose( show[ ndx ] );
// Inform toolbar of change
bar->SetDocked( NULL, false );
}
// 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 );
// Hide the bar
if( !show[ t->GetId() ] )
{
d->ShowHide( 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 );
// Hide the bar
if( !show[ t->GetId() ] )
{
d->ShowHide( 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"), 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() )
{
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 toolbar is floating
if( !t->IsDocked() )
{
// Must return state of floater window
return t->GetParent()->IsShown();
}
// Return state of docked toolbar
return t->IsShown();
}
//
// Toggles the visible/hidden state of a toolbar
//
void ToolManager::ShowHide( int type )
{
ToolBar *t = mBars[ type ];
// Handle docked and floaters differently
if( t->IsDocked() )
{
t->GetDock()->ShowHide( type );
}
else
{
t->Expose( !t->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() );
// 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 - mDragOffset );
// 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() ) );
// Is mouse pointer within either dock?
ToolDock *dock = NULL;
if( tr.Contains( pos ) )
{
dock = mTopDock;
}
else if( br.Contains( pos ) )
{
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.GetBottom() >= 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() +
( ( 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 );
// 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 );
}
// Indentation settings for Vim and Emacs and unique identifier for Arch, a
// version control system. Please do not modify past this point.
//
// Local Variables:
// c-basic-offset: 3
// indent-tabs-mode: nil
// End:
//
// vim: et sts=3 sw=3
// arch-tag: 2f4ec75c-bdb7-4889-96d-5d00abc41027