1 /**********************************************************************
2
3 Audacity: A Digital Audio Editor
4
5 ToolManager.cpp
6
7 Dominic Mazzoni
8 Shane T. Mueller
9 Leland Lucius
10
11 See ToolManager.h for details.
12
13 *******************************************************************//**
14
15 \file ToolManager.cpp
16
17 Implements ToolManager
18
19 *//*******************************************************************//**
20
21 \class ToolManager
22 \brief Manages the ToolDocks and handles the dragging, floating, and
23 docking of ToolBars.
24
25 *//**********************************************************************/
26
27
28 #include "ToolManager.h"
29
30 #include "../commands/CommandContext.h"
31
32 // For compilers that support precompilation, includes "wx/wx.h".
33 #include <wx/wxprec.h>
34
35 #ifndef WX_PRECOMP
36 #include <wx/app.h>
37 #include <wx/dcclient.h>
38 #include <wx/defs.h>
39 #include <wx/event.h>
40 #include <wx/frame.h>
41 #include <wx/gdicmn.h>
42 #include <wx/intl.h>
43 #include <wx/region.h>
44 #include <wx/settings.h>
45 #include <wx/sizer.h>
46 #include <wx/sysopt.h>
47 #include <wx/timer.h>
48 #include <wx/utils.h>
49 #include <wx/window.h>
50 #endif /* */
51
52 #include <wx/minifram.h>
53 #include <wx/popupwin.h>
54
55 #include "AColor.h"
56 #include "AllThemeResources.h"
57 #include "ImageManipulation.h"
58 #include "Prefs.h"
59 #include "Project.h"
60 #include "ProjectWindows.h"
61 #include "widgets/AButton.h"
62 #include "widgets/ASlider.h"
63 #include "widgets/MeterPanelBase.h"
64 #include "widgets/Grabber.h"
65
66 ////////////////////////////////////////////////////////////
67 /// Methods for ToolFrame
68 ////////////////////////////////////////////////////////////
69 #define sizerW 11
70
71 //
72 // Constructor
73 //
ToolFrame(AudacityProject * parent,ToolManager * manager,ToolBar * bar,wxPoint pos)74 ToolFrame::ToolFrame
75 ( AudacityProject *parent, ToolManager *manager, ToolBar *bar, wxPoint pos )
76 : wxFrame( FindProjectFrame( parent ),
77 bar->GetId(),
78 wxEmptyString,
79 pos,
80 wxDefaultSize,
81 wxNO_BORDER |
82 wxFRAME_NO_TASKBAR |
83 #if !defined(__WXMAC__) // bug1358
84 wxFRAME_TOOL_WINDOW |
85 #endif
86 wxFRAME_FLOAT_ON_PARENT )
87 , mParent{ parent }
88 {
89 int width = bar->GetSize().x;
90 int border = 1;
91
92 // Save parameters
93 mManager = manager;
94 mBar = bar;
95
96 // Transfer the bar to the ferry
97 bar->Reparent(this);
98
99 // Bug 2120 (comment 6 residual): No need to set a minimum size
100 // if the toolbar is not resizable. On GTK, setting a minimum
101 // size will prevent the frame from shrinking if the toolbar gets
102 // reconfigured and needs to resize smaller.
103 if (bar->IsResizable())
104 {
105 SetMinSize(bar->GetDockedSize());
106 }
107
108 {
109 // We use a sizer to maintain proper spacing
110 auto s = std::make_unique<wxBoxSizer>(wxHORIZONTAL);
111
112 // Add the bar to the sizer
113 s->Add(bar, 1, wxEXPAND | wxALL, border);
114
115 // Add space for the resize grabber
116 if (bar->IsResizable())
117 {
118 s->Add(sizerW, 1);
119 width += sizerW;
120 }
121
122 SetSize(width + 2 * ToolBarFloatMargin,
123 bar->GetDockedSize().y + 2 * ToolBarFloatMargin);
124
125 // Attach the sizer and resize the window to fit
126 SetSizer(s.release());
127 }
128
129 Layout();
130
131 // Inform toolbar of change
132 bar->SetDocked( NULL, true );
133
134 // Make sure resizable floaters don't get any smaller than initial size
135 if( bar->IsResizable() )
136 {
137 // Calc the minimum size of the frame
138 mMinSize = bar->GetMinSize() + ( GetSize() - bar->GetSize() );
139 }
140 }
141
~ToolFrame()142 ToolFrame::~ToolFrame()
143 {
144 if(HasCapture())
145 ReleaseMouse();
146 }
147
OnGrabber(GrabberEvent & event)148 void ToolFrame::OnGrabber( GrabberEvent & event )
149 {
150 // Pass it on to the manager since it isn't in the handling hierarchy
151 mManager->ProcessEvent( event );
152 }
153
154 // The current size determines the min size for resizing...
155 // the 'lock in' is at that aspect ratio.
LockInMinSize(ToolBar * pBar)156 void ToolFrame::LockInMinSize(ToolBar * pBar)
157 {
158 mBar = pBar;
159
160 wxSize sz = mBar->GetSize();
161 SetClientSize( sz );
162 int yDesiredMin = 26;
163 int y = sz.GetHeight();
164 if (y > yDesiredMin) {
165 sz.SetWidth((sz.GetWidth() * yDesiredMin) / y);
166 sz.SetHeight( yDesiredMin );
167 }
168 mMinSize = sz -wxSize( 10, 0);
169 }
170
OnToolBarUpdate(wxCommandEvent & event)171 void ToolFrame::OnToolBarUpdate( wxCommandEvent & event )
172 {
173 // Resize floater window to exactly contain toolbar
174 // use actual size rather than minimum size.
175 if (mBar)
176 mBar->GetParent()->SetClientSize( mBar->GetSize() );// ->GetMinSize() );
177
178 // Allow it to propagate to our parent
179 event.Skip();
180 }
181
OnPaint(wxPaintEvent & WXUNUSED (event))182 void ToolFrame::OnPaint( wxPaintEvent & WXUNUSED(event) )
183 {
184 wxPaintDC dc( this );
185 wxSize sz = GetSize();
186 wxRect r;
187
188 dc.SetPen( theTheme.Colour( clrTrackPanelText ) );
189 dc.SetBackground( wxBrush( theTheme.Colour( clrMedium ) ) );
190 dc.Clear();
191 dc.SetBrush( *wxTRANSPARENT_BRUSH );
192 dc.DrawRectangle( 0, 0, sz.GetWidth(), sz.GetHeight() );
193
194 if( mBar && mBar->IsResizable() )
195 {
196 r.x = sz.x - sizerW - 2,
197 r.y = sz.y - sizerW - 2;
198 r.width = sizerW + 2;
199 r.height = sizerW + 2;
200
201 AColor::Line(dc, r.GetLeft(), r.GetBottom(), r.GetRight(), r.GetTop() );
202 AColor::Line(dc, r.GetLeft() + 3, r.GetBottom(), r.GetRight(), r.GetTop() + 3 );
203 AColor::Line(dc, r.GetLeft() + 6, r.GetBottom(), r.GetRight(), r.GetTop() + 6 );
204 AColor::Line(dc, r.GetLeft() + 9, r.GetBottom(), r.GetRight(), r.GetTop() + 9 );
205 }
206
207 }
208
OnMotion(wxMouseEvent & event)209 void ToolFrame::OnMotion( wxMouseEvent & event )
210 {
211 // Don't do anything if we're docked or not resizeable
212 if( !mBar || mBar->IsDocked() || !mBar->IsResizable() )
213 {
214 return;
215 }
216
217 // Retrieve the mouse position
218 wxPoint pos = ClientToScreen( event.GetPosition() );
219 if( HasCapture() && event.Dragging() )
220 {
221 wxRect rect = GetRect();
222
223 rect.SetBottomRight( pos );
224
225 // Keep it within max size, if specified
226 wxSize maxsz = mBar->GetMaxSize();
227 if (maxsz != wxDefaultSize)
228 {
229 if (maxsz.x != wxDefaultCoord && rect.width > maxsz.x)
230 {
231 rect.width = maxsz.x;
232 }
233 if (maxsz.y != wxDefaultCoord && rect.height > maxsz.y)
234 {
235 rect.height = maxsz.y;
236 }
237 }
238
239 if( rect.width < mMinSize.x )
240 {
241 rect.width = mMinSize.x;
242 }
243
244 if( rect.height < mMinSize.y )
245 {
246 rect.height = mMinSize.y;
247 }
248
249 Resize( rect.GetSize() );
250 }
251 else if( HasCapture() && event.LeftUp() )
252 {
253 ReleaseMouse();
254 }
255 else if( !HasCapture() )
256 {
257 wxRect rect = GetRect();
258 wxRect r;
259
260 r.x = rect.GetRight() - sizerW - 2,
261 r.y = rect.GetBottom() - sizerW - 2;
262 r.width = sizerW + 2;
263 r.height = sizerW + 2;
264
265 // Is left click within resize grabber?
266 if( r.Contains( pos ) && !event.Leaving() )
267 {
268 mOrigSize = GetSize();
269
270 SetCursor( wxCURSOR_SIZENWSE );
271 if( event.LeftDown() )
272 {
273 CaptureMouse();
274 }
275 }
276 else
277 {
278 SetCursor( wxCURSOR_ARROW );
279 }
280 }
281 }
282
OnCaptureLost(wxMouseCaptureLostEvent & WXUNUSED (event))283 void ToolFrame::OnCaptureLost( wxMouseCaptureLostEvent & WXUNUSED(event) )
284 {
285 if( HasCapture() )
286 {
287 ReleaseMouse();
288 }
289 }
290
OnClose(wxCloseEvent & event)291 void ToolFrame::OnClose( wxCloseEvent & event )
292 {
293 event.Veto();
294 }
295
OnKeyDown(wxKeyEvent & event)296 void ToolFrame::OnKeyDown( wxKeyEvent &event )
297 {
298 event.Skip();
299 if( HasCapture() && event.GetKeyCode() == WXK_ESCAPE ) {
300 Resize( mOrigSize );
301 ReleaseMouse();
302 }
303 }
304
Resize(const wxSize & size)305 void ToolFrame::Resize( const wxSize &size )
306 {
307 SetMinSize( size );
308 SetSize( size );
309 Layout();
310 Refresh( false );
311 }
312
313 IMPLEMENT_CLASS( ToolFrame, wxFrame );
314
315 BEGIN_EVENT_TABLE( ToolFrame, wxFrame )
316 EVT_GRABBER( wxID_ANY, ToolFrame::OnGrabber )
317 EVT_PAINT( ToolFrame::OnPaint )
318 EVT_MOUSE_EVENTS( ToolFrame::OnMotion )
319 EVT_MOUSE_CAPTURE_LOST( ToolFrame::OnCaptureLost )
320 EVT_CLOSE( ToolFrame::OnClose )
321 EVT_COMMAND( wxID_ANY, EVT_TOOLBAR_UPDATED, ToolFrame::OnToolBarUpdate )
322 EVT_KEY_DOWN( ToolFrame::OnKeyDown )
323 END_EVENT_TABLE()
324
325 IMPLEMENT_CLASS( ToolManager, wxEvtHandler );
326
327 ////////////////////////////////////////////////////////////
328 /// Methods for ToolManager
329 ////////////////////////////////////////////////////////////
330
BEGIN_EVENT_TABLE(ToolManager,wxEvtHandler)331 BEGIN_EVENT_TABLE( ToolManager, wxEvtHandler )
332 EVT_GRABBER( wxID_ANY, ToolManager::OnGrabber )
333 EVT_TIMER( wxID_ANY, ToolManager::OnTimer )
334 END_EVENT_TABLE()
335
336 static ToolManager::GetTopPanelHook &getTopPanelHook()
337 {
338 static ToolManager::GetTopPanelHook theHook;
339 return theHook;
340 }
341
SetGetTopPanelHook(const GetTopPanelHook & hook)342 auto ToolManager::SetGetTopPanelHook( const GetTopPanelHook &hook )
343 -> GetTopPanelHook
344 {
345 auto &theHook = getTopPanelHook();
346 auto result = theHook;
347 theHook = hook;
348 return result;
349 }
350
351 static const AudacityProject::AttachedObjects::RegisteredFactory key{
__anonea3c83480102( )352 []( AudacityProject &parent ){
353 return std::make_shared< ToolManager >( &parent ); }
354 };
355
Get(AudacityProject & project)356 ToolManager &ToolManager::Get( AudacityProject &project )
357 {
358 return project.AttachedObjects::Get< ToolManager >( key );
359 }
360
Get(const AudacityProject & project)361 const ToolManager &ToolManager::Get( const AudacityProject &project )
362 {
363 return Get( const_cast< AudacityProject & >( project ) );
364 }
365
366 //
367 // Constructor
368 //
ToolManager(AudacityProject * parent)369 ToolManager::ToolManager( AudacityProject *parent )
370 : wxEvtHandler()
371 {
372 wxPoint pt[ 3 ];
373
374 #if defined(__WXMAC__)
375 // Save original transition
376 mTransition = wxSystemOptions::GetOptionInt( wxMAC_WINDOW_PLAIN_TRANSITION );
377 #endif
378
379 // Initialize everything
380 mParent = parent;
381 mLastPos.x = mBarPos.x = -1;
382 mLastPos.y = mBarPos.y = -1;
383 mDragWindow = NULL;
384 mDragDock = NULL;
385 mDragBar = NULL;
386
387 // Create the down arrow
388 pt[ 0 ].x = 0;
389 pt[ 0 ].y = 0;
390 pt[ 1 ].x = 9;
391 pt[ 1 ].y = 9;
392 pt[ 2 ].x = 18;
393 pt[ 2 ].y = 0;
394
395 // Create the shaped region
396 mDown = std::make_unique<wxRegion>( 3, &pt[0] );
397
398 // Create the down arrow
399 pt[ 0 ].x = 9;
400 pt[ 0 ].y = 0;
401 pt[ 1 ].x = 0;
402 pt[ 1 ].y = 9;
403 pt[ 2 ].x = 9;
404 pt[ 2 ].y = 18;
405
406 // Create the shaped region
407 mLeft = std::make_unique<wxRegion>( 3, &pt[0] );
408
409 // Create the indicator frame
410 // parent is null but FramePtr ensures destruction
411 mIndicator = FramePtr{ safenew wxFrame( NULL,
412 wxID_ANY,
413 wxEmptyString,
414 wxDefaultPosition,
415 wxSize( 32, 32 ),
416 wxFRAME_TOOL_WINDOW |
417 wxFRAME_SHAPED |
418 wxNO_BORDER |
419 wxFRAME_NO_TASKBAR |
420 wxSTAY_ON_TOP )
421 };
422
423 // Hook the creation event...only needed on GTK, but doesn't hurt for all
424 mIndicator->Bind( wxEVT_CREATE,
425 &ToolManager::OnIndicatorCreate,
426 this );
427
428 // Hook the paint event...needed for all
429 mIndicator->Bind( wxEVT_PAINT,
430 &ToolManager::OnIndicatorPaint,
431 this );
432
433 // It's a little shy
434 mIndicator->Hide();
435 }
436
CreateWindows()437 void ToolManager::CreateWindows()
438 {
439 auto parent = mParent;
440 auto &window = GetProjectFrame( *parent );
441
442 // Hook the parents mouse events...using the parent helps greatly
443 // under GTK
444 window.Bind( wxEVT_LEFT_UP,
445 &ToolManager::OnMouse,
446 this );
447 window.Bind( wxEVT_MOTION,
448 &ToolManager::OnMouse,
449 this );
450 window.Bind( wxEVT_MOUSE_CAPTURE_LOST,
451 &ToolManager::OnCaptureLost,
452 this );
453
454 wxWindow *topDockParent = getTopPanelHook()( window );
455
456 // Create the top and bottom docks
457 mTopDock = safenew ToolDock( this, topDockParent, TopDockID );
458 mBotDock = safenew ToolDock( this, &window, BotDockID );
459
460 // Create all of the toolbars
461 // All have the project as parent window
462 wxASSERT(parent);
463
464 size_t ii = 0;
465 for (const auto &factory : RegisteredToolbarFactory::GetFactories()) {
466 if (factory) {
467 mBars[ii] = factory( *parent );
468 }
469 else
470 wxASSERT( false );
471 ++ii;
472 }
473
474 // We own the timer
475 mTimer.SetOwner( this );
476
477 // Process the toolbar config settings
478 ReadConfig();
479
480 wxEvtHandler::AddFilter(this);
481 }
482
483 //
484 // Destructor
485 //
486
Destroy()487 void ToolManager::Destroy()
488 {
489 if ( mTopDock || mBotDock ) { // destroy at most once
490 wxEvtHandler::RemoveFilter(this);
491
492 // Save the toolbar states
493 WriteConfig();
494
495 // This function causes the toolbars to be destroyed, so
496 // clear the configuration of the ToolDocks which refer to
497 // these toolbars. This change was needed to stop Audacity
498 // crashing when running with Jaws on Windows 10 1703.
499 mTopDock->GetConfiguration().Clear();
500 mBotDock->GetConfiguration().Clear();
501
502 mTopDock = mBotDock = nullptr; // indicate that it has been destroyed
503
504 for ( size_t ii = 0; ii < ToolBarCount; ++ii )
505 mBars[ii].reset();
506
507 mIndicator.reset();
508 }
509 }
510
~ToolManager()511 ToolManager::~ToolManager()
512 {
513 Destroy();
514 }
515
516 // This table describes the default configuration of the toolbars as
517 // a "tree" and must be kept in pre-order traversal.
518
519 // In fact this tree is more of a broom -- nothing properly branches except
520 // at the root.
521
522 // "Root" corresponds to left edge of the main window, and successive siblings
523 // go from top to bottom. But in practice layout may wrap this abstract
524 // configuration if the window size is narrow.
525
526 static struct DefaultConfigEntry {
527 int barID;
528 int rightOf; // parent
529 int below; // preceding sibling
530 } DefaultConfigTable [] = {
531 // Top dock row, may wrap
532 { TransportBarID, NoBarID, NoBarID },
533 { ToolsBarID, TransportBarID, NoBarID },
534 { RecordMeterBarID, ToolsBarID, NoBarID },
535 { PlayMeterBarID, RecordMeterBarID, NoBarID },
536 { MixerBarID, PlayMeterBarID, NoBarID },
537 { EditBarID, MixerBarID, NoBarID },
538
539 // DA: Transcription Toolbar not docked, by default.
540 #ifdef EXPERIMENTAL_DA
541 { TranscriptionBarID, NoBarID, NoBarID },
542 #else
543 { TranscriptionBarID, EditBarID, NoBarID },
544 #endif
545
546 // start another top dock row
547 { ScrubbingBarID, NoBarID, TransportBarID },
548 { DeviceBarID, ScrubbingBarID, TransportBarID },
549
550 // Hidden by default in top dock
551 { MeterBarID, NoBarID, NoBarID },
552
553 // Bottom dock
554 { SelectionBarID, NoBarID, NoBarID },
555 { TimeBarID, SelectionBarID, NoBarID },
556
557 // Hidden by default in bottom dock
558 { SpectralSelectionBarID, NoBarID, NoBarID },
559 };
560
561 // Static member function.
OnResetToolBars(const CommandContext & context)562 void ToolManager::OnResetToolBars(const CommandContext &context)
563 {
564 auto &project = context.project;
565 auto &toolManager = ToolManager::Get( project );
566
567 toolManager.Reset();
568 MenuManager::Get(project).ModifyToolbarMenus(project);
569 }
570
571
Reset()572 void ToolManager::Reset()
573 {
574 // Disconnect all docked bars
575 for ( const auto &entry : DefaultConfigTable )
576 {
577 int ndx = entry.barID;
578 ToolBar *bar = mBars[ ndx ].get();
579
580 ToolBarConfiguration::Position position {
581 (entry.rightOf == NoBarID) ? nullptr : mBars[ entry.rightOf ].get(),
582 (entry.below == NoBarID) ? nullptr : mBars[ entry.below ].get()
583 };
584 bar->SetSize( 20,20 );
585
586 wxWindow *floater;
587 ToolDock *dock;
588 bool expose = true;
589
590 // Disconnect the bar
591 if( bar->IsDocked() )
592 {
593 bar->GetDock()->Undock( bar );
594 floater = NULL;
595 }
596 else
597 {
598 floater = bar->GetParent();
599 }
600
601 // Decide which dock.
602 if (ndx == SelectionBarID
603 #ifdef EXPERIMENTAL_SPECTRAL_EDITING
604 || ndx == SpectralSelectionBarID
605 #endif
606 || ndx == TimeBarID
607 )
608 dock = mBotDock;
609 else
610 dock = mTopDock;
611
612 // PRL: Destroy the tool frame before recreating buttons.
613 // This fixes some subtle sizing problems on macOs.
614 bar->Reparent( dock );
615 //OK (and good) to DELETE floater, as bar is no longer in it.
616 if( floater )
617 floater->Destroy();
618
619 // Recreate bar buttons (and resize it)
620 bar->SetToDefaultSize();
621 bar->ReCreateButtons();
622 bar->EnableDisableButtons();
623
624 #if 0
625 if( bar->IsResizable() )
626 {
627 bar->SetSize(bar->GetBestFittingSize());
628 }
629 #endif
630
631 // Hide some bars.
632 if( ndx == MeterBarID
633 #ifdef EXPERIMENTAL_SPECTRAL_EDITING
634 || ndx == SpectralSelectionBarID
635 #endif
636 || ndx == ScrubbingBarID
637 // DA: Hides three more toolbars.
638 #ifdef EXPERIMENTAL_DA
639 || ndx == DeviceBarID
640 || ndx == TranscriptionBarID
641 || ndx == SelectionBarID
642 #endif
643 )
644 expose = false;
645
646 // Next condition will always (?) be true, as the reset configuration is
647 // with no floating toolbars.
648 if( dock != NULL )
649 {
650 // when we dock, we reparent, so bar is no longer a child of floater.
651 dock->Dock( bar, false, position );
652 Expose( ndx, expose );
653 }
654 else
655 {
656 // The (tool)bar has a dragger window round it, the floater.
657 // in turn floater will have mParent (the entire App) as its
658 // parent.
659
660 // Maybe construct a NEW floater
661 // this happens if we have just been bounced out of a dock.
662 if( floater == NULL ) {
663 wxASSERT(mParent);
664 floater = safenew ToolFrame( mParent, this, bar, wxPoint(-1,-1) );
665 bar->Reparent( floater );
666 }
667
668 // This bar is undocked and invisible.
669 // We are doing a reset toolbars, so even the invisible undocked bars should
670 // be moved somewhere sensible. Put bar near center of window.
671 // If there were multiple hidden toobars the ndx * 10 adjustment means
672 // they won't overlap too much.
673 floater->CentreOnParent( );
674 floater->Move( floater->GetPosition() + wxSize( ndx * 10 - 200, ndx * 10 ));
675 bar->SetDocked( NULL, false );
676 Expose( ndx, false );
677 }
678
679 }
680 // TODO:??
681 // If audio was playing, we stopped the VU meters,
682 // It would be nice to show them again, but hardly essential as
683 // they will show up again on the next play.
684 // SetVUMeters(AudacityProject *p);
685 Updated();
686 }
687
RegenerateTooltips()688 void ToolManager::RegenerateTooltips()
689 {
690 for (const auto &bar : mBars) {
691 if (bar)
692 bar->RegenerateTooltips();
693 }
694 }
695
FilterEvent(wxEvent & event)696 int ToolManager::FilterEvent(wxEvent &event)
697 {
698 // Snoop the global event stream for changes of focused window. Remember
699 // the last one of our own that is not a grabber.
700
701 if (event.GetEventType() == wxEVT_KILL_FOCUS) {
702 auto &focusEvent = static_cast<wxFocusEvent&>(event);
703 auto window = focusEvent.GetWindow();
704 auto top = wxGetTopLevelParent(window);
705 if(auto toolFrame = dynamic_cast<ToolFrame*>(top))
706 top = toolFrame->GetParent();
707 // window is that which will GET the focus
708 if ( window &&
709 !dynamic_cast<Grabber*>( window ) &&
710 !dynamic_cast<ToolFrame*>( window ) &&
711 top == FindProjectFrame( mParent ) )
712 // Note this is a dangle-proof wxWindowRef:
713 mLastFocus = window;
714 }
715
716 return Event_Skip;
717 }
718
719 //
720 // Read the toolbar states
721 //
ReadConfig()722 void ToolManager::ReadConfig()
723 {
724 wxString oldpath = gPrefs->GetPath();
725 std::vector<int> unordered[ DockCount ];
726 std::vector<ToolBar*> dockedAndHidden;
727 bool show[ ToolBarCount ];
728 int width[ ToolBarCount ];
729 int height[ ToolBarCount ];
730 int x, y;
731 int dock, ndx;
732 bool someFound { false };
733
734 #if defined(__WXMAC__)
735 // Disable window animation
736 wxSystemOptions::SetOption( wxMAC_WINDOW_PLAIN_TRANSITION, 1 );
737 #endif
738
739 // Change to the bar root
740 gPrefs->SetPath( wxT("/GUI/ToolBars") );
741
742 ToolBarConfiguration::Legacy topLegacy, botLegacy;
743
744 int vMajor, vMinor, vMicro;
745 gPrefs->GetVersionKeysInit(vMajor, vMinor, vMicro);
746 bool useLegacyDock = false;
747 // note that vMajor, vMinor, and vMicro will all be zero if either it's a new audacity.cfg file
748 // or the version is less than 1.3.13 (when there were no version keys according to the comments in
749 // InitPreferences()). So for new audacity.cfg
750 // file useLegacyDock will be true, but this doesn't matter as there are no Dock or DockV2 keys in the file yet.
751 if (vMajor <= 1 ||
752 (vMajor == 2 && (vMinor <= 1 || (vMinor == 2 && vMicro <= 1)))) // version <= 2.2.1
753 useLegacyDock = true;
754
755
756 // Load and apply settings for each bar
757 for( ndx = 0; ndx < ToolBarCount; ndx++ )
758 {
759 ToolBar *bar = mBars[ ndx ].get();
760 //wxPoint Center = mParent->GetPosition() + (mParent->GetSize() * 0.33);
761 //wxPoint Center(
762 // wxSystemSettings::GetMetric( wxSYS_SCREEN_X ) /2 ,
763 // wxSystemSettings::GetMetric( wxSYS_SCREEN_Y ) /2 );
764
765 // Change to the bar subkey
766 gPrefs->SetPath( bar->GetSection() );
767
768 bool bShownByDefault = true;
769 int defaultDock = TopDockID;
770
771 if( ndx == SelectionBarID )
772 defaultDock = BotDockID;
773 if( ndx == MeterBarID )
774 bShownByDefault = false;
775 if( ndx == ScrubbingBarID )
776 bShownByDefault = false;
777 if( ndx == TimeBarID )
778 defaultDock = BotDockID;
779
780 #ifdef EXPERIMENTAL_SPECTRAL_EDITING
781 if( ndx == SpectralSelectionBarID ){
782 defaultDock = BotDockID;
783 bShownByDefault = false; // Only show if asked for.
784 }
785 #endif
786
787 // Read in all the settings
788
789 if (useLegacyDock)
790 gPrefs->Read( wxT("Dock"), &dock, -1); // legacy version of DockV2
791 else
792 gPrefs->Read( wxT("DockV2"), &dock, -1);
793
794 const bool found = (dock != -1);
795 if (found)
796 someFound = true;
797 if (!found)
798 dock = defaultDock;
799
800 ToolDock *d;
801 ToolBarConfiguration::Legacy *pLegacy;
802 switch(dock)
803 {
804 case TopDockID: d = mTopDock; pLegacy = &topLegacy; break;
805 case BotDockID: d = mBotDock; pLegacy = &botLegacy; break;
806 default: d = nullptr; pLegacy = nullptr; break;
807 }
808
809 bool ordered = ToolBarConfiguration::Read(
810 d ? &d->GetConfiguration() : nullptr,
811 pLegacy,
812 bar, show[ ndx ], bShownByDefault)
813 && found;
814
815 gPrefs->Read( wxT("X"), &x, -1 );
816 gPrefs->Read( wxT("Y"), &y, -1 );
817 gPrefs->Read( wxT("W"), &width[ ndx ], -1 );
818 gPrefs->Read( wxT("H"), &height[ ndx ], -1 );
819
820 bar->SetVisible( show[ ndx ] );
821
822 // Docked or floating?
823 if( dock )
824 {
825 // Default to top dock if the ID isn't valid
826 if( dock < NoDockID || dock > DockCount ) {
827 dock = TopDockID;
828 }
829
830 // Create the bar with the correct parent
831 if( dock == TopDockID )
832 {
833 bar->Create( mTopDock );
834 }
835 else
836 {
837 bar->Create( mBotDock );
838 }
839
840 // Set the width and height
841 if( width[ ndx ] != -1 && height[ ndx ] != -1 )
842 {
843 wxSize sz( width[ ndx ], height[ ndx ] );
844 bar->SetSize( sz );
845 bar->ResizingDone();
846 }
847
848 #ifdef EXPERIMENTAL_SYNC_LOCK
849 // Set the width
850 if( width[ ndx ] >= bar->GetSize().x )
851 {
852 wxSize sz( width[ ndx ], bar->GetSize().y );
853 bar->SetSize( sz );
854 bar->Layout();
855 }
856 #else
857 // note that this section is here because if you had been using sync-lock and now you aren't,
858 // the space for the extra button is stored in audacity.cfg, and so you get an extra space
859 // in the EditToolbar.
860 // It is needed so that the meterToolbar size gets preserved.
861 // Longer-term we should find a better fix for this.
862 wxString thisBar = bar->GetSection();
863 if( thisBar != wxT("Edit"))
864 {
865 // Set the width
866 if( width[ ndx ] >= bar->GetSize().x )
867 {
868 wxSize sz( width[ ndx ], bar->GetSize().y );
869 bar->SetSize( sz );
870 bar->Layout();
871 }
872 }
873 #endif
874 // make a note of docked and hidden toolbars
875 if (!show[ndx])
876 dockedAndHidden.push_back(bar);
877
878 if (!ordered)
879 {
880 // These must go at the end
881 unordered[ dock - 1 ].push_back( ndx );
882 }
883 }
884 else
885 {
886 // Create the bar (with the top dock being temporary parent)
887 bar->Create( mTopDock );
888
889 // Construct a NEW floater
890 wxASSERT(mParent);
891 ToolFrame *f = safenew ToolFrame( mParent, this, bar, wxPoint( x, y ) );
892
893 // Set the width and height
894 if( width[ ndx ] != -1 && height[ ndx ] != -1 )
895 {
896 wxSize sz( width[ ndx ], height[ ndx ] );
897 f->SetSizeHints( sz );
898 f->SetSize( sz );
899 f->Layout();
900 if( (x!=-1) && (y!=-1) )
901 bar->SetPositioned();
902 }
903
904 // Required on Linux Xfce
905 wxSize msz(width[ndx],height[ndx]-1);
906 bar->GetParent()->SetMinSize(msz);
907
908 // Inform toolbar of change
909 bar->SetDocked( NULL, false );
910
911 // Show or hide it
912 Expose( ndx, show[ ndx ] );
913 }
914
915 // Change back to the bar root
916 //gPrefs->SetPath( wxT("..") ); <-- Causes a warning...
917 // May or may not have gone into a subdirectory,
918 // so use an absolute path.
919 gPrefs->SetPath( wxT("/GUI/ToolBars") );
920 }
921
922 mTopDock->GetConfiguration().PostRead(topLegacy);
923 mBotDock->GetConfiguration().PostRead(botLegacy);
924
925 // Add all toolbars to their target dock
926 for( dock = 0; dock < DockCount; dock++ )
927 {
928 ToolDock *d = ( dock + 1 == TopDockID ? mTopDock : mBotDock );
929
930 d->LoadConfig();
931
932 // Add all unordered toolbars
933 for( int ord = 0; ord < (int) unordered[ dock ].size(); ord++ )
934 {
935 ToolBar *t = mBars[ unordered[ dock ][ ord ] ].get();
936
937 // Dock it
938 d->Dock( t, false );
939
940 // Show or hide the bar
941 Expose( t->GetId(), show[ t->GetId() ] );
942 }
943 }
944
945 // hidden docked toolbars
946 for (auto bar : dockedAndHidden) {
947 bar->SetVisible(false );
948 bar->GetDock()->Dock(bar, false);
949 bar->Expose(false);
950 }
951
952 // Restore original config path
953 gPrefs->SetPath( oldpath );
954
955 #if defined(__WXMAC__)
956 // Reinstate original transition
957 wxSystemOptions::SetOption( wxMAC_WINDOW_PLAIN_TRANSITION, mTransition );
958 #endif
959
960 if (!someFound)
961 Reset();
962 }
963
964 //
965 // Save the toolbar states
966 //
WriteConfig()967 void ToolManager::WriteConfig()
968 {
969 if( !gPrefs )
970 {
971 return;
972 }
973
974 wxString oldpath = gPrefs->GetPath();
975 int ndx;
976
977 // Change to the bar root
978 gPrefs->SetPath( wxT("/GUI/ToolBars") );
979
980 // Save state of each bar
981 for( ndx = 0; ndx < ToolBarCount; ndx++ )
982 {
983 ToolBar *bar = mBars[ ndx ].get();
984
985 // Change to the bar subkey
986 gPrefs->SetPath( bar->GetSection() );
987
988 // Search both docks for toolbar order
989 bool to = mTopDock->GetConfiguration().Contains( bar );
990 bool bo = mBotDock->GetConfiguration().Contains( bar );
991
992 // Save
993 // Note that DockV2 was introduced in 2.2.2 to fix bug #1554. Dock is retained so that
994 // the toolbar layout is not changed when opening a version before 2.2.2, and in particular
995 // its value is compatible with versions 2.1.3 to 2.2.1 which have this bug.
996 ToolDock* dock = bar->GetDock(); // dock for both shown and hidden toolbars
997 gPrefs->Write( wxT("DockV2"), static_cast<int>(dock == mTopDock ? TopDockID : dock == mBotDock ? BotDockID : NoDockID ));
998
999 gPrefs->Write( wxT("Dock"), static_cast<int>( to ? TopDockID : bo ? BotDockID : NoDockID));
1000
1001 dock = to ? mTopDock : bo ? mBotDock : nullptr; // dock for shown toolbars
1002 ToolBarConfiguration::Write
1003 (dock ? &dock->GetConfiguration() : nullptr, bar);
1004
1005 wxPoint pos( -1, -1 );
1006 wxSize sz = bar->GetSize();
1007 if( !bar->IsDocked() && bar->IsPositioned() )
1008 {
1009 pos = bar->GetParent()->GetPosition();
1010 sz = bar->GetParent()->GetSize();
1011 }
1012 gPrefs->Write( wxT("X"), pos.x );
1013 gPrefs->Write( wxT("Y"), pos.y );
1014 gPrefs->Write( wxT("W"), sz.x );
1015 gPrefs->Write( wxT("H"), sz.y );
1016
1017 // Change back to the bar root
1018 gPrefs->SetPath( wxT("..") );
1019 }
1020
1021 // Restore original config path
1022 gPrefs->SetPath( oldpath );
1023 gPrefs->Flush();
1024 }
1025
1026 //
1027 // Return a pointer to the specified toolbar
1028 //
GetToolBar(int type) const1029 ToolBar *ToolManager::GetToolBar( int type ) const
1030 {
1031 return mBars[ type ].get();
1032 }
1033
1034 //
1035 // Return a pointer to the top dock
1036 //
GetTopDock()1037 ToolDock *ToolManager::GetTopDock()
1038 {
1039 return mTopDock;
1040 }
1041
GetTopDock() const1042 const ToolDock *ToolManager::GetTopDock() const
1043 {
1044 return mTopDock;
1045 }
1046
1047 //
1048 // Return a pointer to the bottom dock
1049 //
GetBotDock()1050 ToolDock *ToolManager::GetBotDock()
1051 {
1052 return mBotDock;
1053 }
1054
GetBotDock() const1055 const ToolDock *ToolManager::GetBotDock() const
1056 {
1057 return mBotDock;
1058 }
1059
1060 //
1061 // Queues an EVT_TOOLBAR_UPDATED command event to notify any
1062 // interest parties of an updated toolbar or dock layout
1063 //
Updated()1064 void ToolManager::Updated()
1065 {
1066 // Queue an update event
1067 wxCommandEvent e( EVT_TOOLBAR_UPDATED );
1068 GetProjectFrame( *mParent ).GetEventHandler()->AddPendingEvent( e );
1069 }
1070
1071 //
1072 // Return docked state of specified toolbar
1073 //
IsDocked(int type)1074 bool ToolManager::IsDocked( int type )
1075 {
1076 return mBars[ type ]->IsDocked();
1077 }
1078
1079 //
1080 // Returns the visibility of the specified toolbar
1081 //
IsVisible(int type)1082 bool ToolManager::IsVisible( int type )
1083 {
1084 ToolBar *t = mBars[ type ].get();
1085
1086 return t && t->IsVisible();
1087
1088 #if 0
1089 // If toolbar is floating
1090 if( !t->IsDocked() )
1091 {
1092 // Must return state of floater window
1093 return t->GetParent()->IsShown();
1094 }
1095
1096 // Return state of docked toolbar
1097 return t->IsShown();
1098 #endif
1099 }
1100
1101 //
1102 // Toggles the visible/hidden state of a toolbar
1103 //
ShowHide(int type)1104 void ToolManager::ShowHide( int type )
1105 {
1106 Expose( type, !mBars[ type ]->IsVisible() );
1107 Updated();
1108 }
1109
1110 //
1111 // Set the visible/hidden state of a toolbar
1112 //
Expose(int type,bool show)1113 void ToolManager::Expose( int type, bool show )
1114 {
1115 ToolBar *t = mBars[ type ].get();
1116
1117 // Handle docked and floaters differently
1118 if( t->IsDocked() )
1119 {
1120 t->GetDock()->Expose( type, show );
1121 }
1122 else
1123 {
1124 t->Expose( show );
1125 }
1126 }
1127
1128 //
1129 // Ask both docks to (re)layout their bars
1130 //
LayoutToolBars()1131 void ToolManager::LayoutToolBars()
1132 {
1133 // Update the layout
1134 if (mTopDock)
1135 {
1136 mTopDock->LayoutToolBars();
1137 }
1138
1139 if (mBotDock)
1140 {
1141 mBotDock->LayoutToolBars();
1142 }
1143 }
1144
1145 //
1146 // Handle toolbar dragging
1147 //
OnMouse(wxMouseEvent & event)1148 void ToolManager::OnMouse( wxMouseEvent & event )
1149 {
1150 // Go ahead and set the event to propagate
1151 event.Skip();
1152
1153 // Can't do anything if we're not dragging. This also prevents
1154 // us from intercepting events that don't belong to us from the
1155 // parent since we're Connect()ed to a couple.
1156 if( !mClicked )
1157 {
1158 return;
1159 }
1160
1161 #if defined(__WXMAC__)
1162 // Disable window animation
1163 wxSystemOptions::SetOption( wxMAC_WINDOW_PLAIN_TRANSITION, 1 );
1164 #endif
1165
1166 // Retrieve the event position
1167 wxPoint pos =
1168 ( (wxWindow *)event.GetEventObject() )->ClientToScreen( event.GetPosition() ) - mDragOffset;
1169
1170
1171 if( !event.LeftIsDown() )
1172 {
1173 // Button was released...finish the drag
1174 // Transition the bar to a dock
1175 if (!mDidDrag) {
1176 if (mPrevDock)
1177 mPrevDock->RestoreConfiguration(mPrevConfiguration);
1178 DoneDragging();
1179 return;
1180 }
1181 else if( mDragDock && !event.ShiftDown() )
1182 {
1183 // Trip over...everyone ashore that's going ashore...
1184 mDragDock->Dock( mDragBar, true, mDragBefore );
1185 Updated();
1186 mDragWindow->ClearBar();
1187
1188 // Done with the floater
1189 mDragWindow->Destroy();
1190 mDragWindow = nullptr;
1191 mDragBar->Refresh(false);
1192 }
1193 else
1194 {
1195 // Calling SetDocked() to force the grabber button to popup
1196 mDragBar->SetDocked( NULL, false );
1197 }
1198
1199 DoneDragging();
1200 }
1201 else if( event.Dragging() && pos != mLastPos )
1202 {
1203 if (!mDidDrag) {
1204 // Must set the bar afloat if it's currently docked
1205 mDidDrag = true;
1206 wxPoint mp = event.GetPosition();
1207 mp = GetProjectFrame( *mParent ).ClientToScreen(mp);
1208 if (!mDragWindow) {
1209 // We no longer have control
1210 if (mPrevDock)
1211 mPrevDock->GetConfiguration().Remove( mDragBar );
1212 UndockBar(mp);
1213 // Rearrange the remaining toolbars before trying to re-insert this one.
1214 LayoutToolBars();
1215 }
1216 }
1217
1218 // Make toolbar follow the mouse
1219 mDragWindow->Move( pos );
1220
1221 // Remember to prevent excessive movement
1222 mLastPos = pos;
1223
1224 // Calc the top dock hittest rectangle
1225 wxRect tr = mTopDock->GetRect();
1226 tr.SetBottom( tr.GetBottom() + 10 );
1227 tr.SetPosition( mTopDock->GetParent()->ClientToScreen( tr.GetPosition() ) );
1228
1229 // Calc the bottom dock hittest rectangle
1230 wxRect br = mBotDock->GetRect();
1231 br.SetTop( br.GetTop() - 10 );
1232 br.SetBottom( br.GetBottom() + 20 );
1233 br.SetPosition( mBotDock->GetParent()->ClientToScreen( br.GetPosition() ) );
1234
1235
1236 // Add half the bar height. We could use the actual bar height, but that would be confusing as a
1237 // bar removed at a place might not dock back there if just let go.
1238 // Also add 5 pixels in horizontal direction, so that a click without a move (or a very small move)
1239 // lands back where we started.
1240 pos += wxPoint( 5, 20 );
1241
1242
1243 // To find which dock, rather than test against pos, test against the whole dragger rect.
1244 // This means it is enough to overlap the dock to dock with it.
1245 wxRect barRect = mDragWindow->GetRect();
1246 ToolDock *dock = NULL;
1247 if( tr.Intersects( barRect ) )
1248 dock = mTopDock;
1249 else if( br.Intersects( barRect ) )
1250 dock = mBotDock;
1251
1252 // Looks like we have a winner...
1253 if( dock )
1254 {
1255 wxPoint p;
1256 wxRect r;
1257
1258 // Calculate where the bar would be placed
1259 mDragBefore = dock->PositionBar( mDragBar, pos, r );
1260
1261 // If different than the last time, the indicator must be moved
1262 if( r != mBarPos )
1263 {
1264 wxRect dr = dock->GetRect();
1265
1266 // Hide the indicator before changing the shape
1267 mIndicator->Hide();
1268
1269 // Decide which direction the arrow should point
1270 if( r.GetTop() >= dr.GetHeight() )
1271 {
1272 const auto &box = mDown->GetBox();
1273 p.x = dr.GetLeft() + ( dr.GetWidth() / 2 )
1274 - (box.GetWidth() / 2);
1275 p.y = dr.GetBottom() - box.GetHeight();
1276 mCurrent = mDown.get();
1277 }
1278 else
1279 {
1280 // r is the rectangle of the toolbar being dragged.
1281 // A tall undocked toolbar will become at most 2 tbs
1282 // high when docked, so the triangular drop indicator
1283 // needs to use that height, h, not the bar height
1284 // for calculating where to be drawn.
1285 const int tbs = toolbarSingle + toolbarGap;
1286 int h = wxMin(r.GetHeight(), 2*tbs-1);
1287 p.x = dr.GetLeft() + r.GetLeft();
1288 p.y = dr.GetTop() + r.GetTop() +
1289 ( ( h - mLeft->GetBox().GetHeight() ) / 2 );
1290 mCurrent = mLeft.get();
1291 }
1292
1293 // Change the shape while hidden and then show it if okay
1294 mIndicator->SetShape( *mCurrent );
1295 if( !event.ShiftDown() )
1296 {
1297 mIndicator->Show();
1298 mIndicator->Update();
1299 }
1300
1301 // Move it into position
1302 // LL: Do this after the Show() since KDE doesn't move the window
1303 // if it's not shown. (Do it outside if the previous IF as well)
1304 mIndicator->Move( dock->GetParent()->ClientToScreen( p ) );
1305
1306 // Remember for next go round
1307 mBarPos = r;
1308 }
1309 }
1310 else
1311 {
1312 // Hide the indicator if it's still shown
1313 if( mBarPos.x != -1 )
1314 {
1315 // Hide any
1316 mIndicator->Hide();
1317 mBarPos.x = -1;
1318 mBarPos.y = -1;
1319 }
1320 }
1321
1322 // Remember to which dock the drag bar belongs.
1323 mDragDock = dock;
1324 }
1325
1326 #if defined(__WXMAC__)
1327 // Reinstate original transition
1328 wxSystemOptions::SetOption( wxMAC_WINDOW_PLAIN_TRANSITION, mTransition );
1329 #endif
1330 }
1331
1332 //
1333 // Deal with NEW capture lost event
1334 //
OnCaptureLost(wxMouseCaptureLostEvent & event)1335 void ToolManager::OnCaptureLost( wxMouseCaptureLostEvent & event )
1336 {
1337 // Can't do anything if we're not dragging. This also prevents
1338 // us from intercepting events that don't belong to us from the
1339 // parent since we're Connect()ed to a couple.
1340 if( !mDragWindow )
1341 {
1342 event.Skip();
1343 return;
1344 }
1345
1346 // Simulate button up
1347 wxMouseEvent e(wxEVT_LEFT_UP);
1348 e.SetEventObject(mParent);
1349 OnMouse(e);
1350 }
1351
1352 //
1353 // Watch for shift key changes
1354 //
OnTimer(wxTimerEvent & event)1355 void ToolManager::OnTimer( wxTimerEvent & event )
1356 {
1357 // Go ahead and set the event to propagate
1358 event.Skip();
1359
1360 // Can't do anything if we're not dragging. This also prevents
1361 // us from intercepting events that don't belong to us from the
1362 // parent since we're Connect()ed to a couple.
1363 if( !mDragWindow )
1364 {
1365 return;
1366 }
1367
1368 bool state = wxGetKeyState( WXK_SHIFT );
1369 if( mLastState != state )
1370 {
1371 mLastState = state;
1372
1373 #if defined(__WXMAC__)
1374 // Disable window animation
1375 wxSystemOptions::SetOption( wxMAC_WINDOW_PLAIN_TRANSITION, 1 );
1376 #endif
1377
1378 mIndicator->Show( !state );
1379
1380 #if defined(__WXMAC__)
1381 // Disable window animation
1382 wxSystemOptions::SetOption( wxMAC_WINDOW_PLAIN_TRANSITION, mTransition );
1383 #endif
1384 }
1385
1386 return;
1387 }
1388
1389 //
1390 // Handle Indicator paint events
1391 //
1392 // Really only needed for the Mac since SetBackgroundColour()
1393 // doesn't seem to work with shaped frames.
1394 //
OnIndicatorPaint(wxPaintEvent & event)1395 void ToolManager::OnIndicatorPaint( wxPaintEvent & event )
1396 {
1397 // TODO: Better to use a bitmap than a triangular region.
1398 wxWindow *w = (wxWindow *)event.GetEventObject();
1399 wxPaintDC dc( w );
1400 // TODO: Better (faster) to use the existing spare brush.
1401 wxBrush brush( theTheme.Colour( clrTrackPanelText ) );
1402 dc.SetBackground( brush );
1403 dc.Clear();
1404 }
1405
1406 //
1407 // Handle Indicator creation event
1408 //
1409 // Without this, the initial Indicator window will be a solid blue square
1410 // until the next time it changes.
1411 //
OnIndicatorCreate(wxWindowCreateEvent & event)1412 void ToolManager::OnIndicatorCreate( wxWindowCreateEvent & event )
1413 {
1414 #if defined(__WXGTK__)
1415 mIndicator->SetShape( *mCurrent );
1416 #endif
1417 event.Skip();
1418 }
1419
UndockBar(wxPoint mp)1420 void ToolManager::UndockBar( wxPoint mp )
1421 {
1422 #if defined(__WXMAC__)
1423 // Disable window animation
1424 wxSystemOptions::SetOption( wxMAC_WINDOW_PLAIN_TRANSITION, 1 );
1425 #endif
1426
1427 // Adjust the starting position
1428 mp -= mDragOffset;
1429
1430 // Inform toolbar of change
1431 mDragBar->SetDocked( NULL, true );
1432 mDragBar->SetPositioned();
1433
1434 // Construct a NEW floater
1435 wxASSERT(mParent);
1436 mDragWindow = safenew ToolFrame( mParent, this, mDragBar, mp );
1437 mDragWindow->SetLayoutDirection(wxLayout_LeftToRight);
1438 // Make sure the ferry is visible
1439 mDragWindow->Show();
1440
1441 // Notify parent of change
1442 Updated();
1443
1444 #if defined(__WXMAC__)
1445 // Reinstate original transition
1446 wxSystemOptions::SetOption( wxMAC_WINDOW_PLAIN_TRANSITION, mTransition );
1447 #endif
1448 }
1449
1450 //
1451 // Transition a toolbar from float to dragging
1452 //
OnGrabber(GrabberEvent & event)1453 void ToolManager::OnGrabber( GrabberEvent & event )
1454 {
1455 // No need to propagate any further
1456 event.Skip( false );
1457
1458 if(event.IsEscaping())
1459 return HandleEscapeKey();
1460
1461 // Remember which bar we're dragging
1462 mDragBar = mBars[ event.GetId() ].get();
1463
1464 // Remember state, in case of ESCape key later
1465 if (mDragBar->IsDocked()) {
1466 mPrevDock = dynamic_cast<ToolDock*>(mDragBar->GetParent());
1467 wxASSERT(mPrevDock);
1468 mPrevSlot = mPrevDock->GetConfiguration().Find(mDragBar);
1469 mPrevDock->WrapConfiguration(mPrevConfiguration);
1470 }
1471 else
1472 mPrevPosition = mDragBar->GetParent()->GetPosition();
1473
1474 // Calculate the drag offset
1475 wxPoint mp = event.GetPosition();
1476 mDragOffset = mp -
1477 mDragBar->GetParent()->ClientToScreen( mDragBar->GetPosition() ) +
1478 wxPoint( 1, 1 );
1479
1480 mClicked = true;
1481 if( mPrevDock )
1482 {
1483 mDragWindow = nullptr;
1484 }
1485 else
1486 {
1487 mDragWindow = (ToolFrame *) mDragBar->GetParent();
1488 }
1489
1490 // We want all mouse events from this point on
1491 auto &window = GetProjectFrame( *mParent );
1492 if( !window.HasCapture() )
1493 window.CaptureMouse();
1494
1495 // Start monitoring shift key changes
1496 mLastState = wxGetKeyState( WXK_SHIFT );
1497 mTimer.Start( 100 );
1498 }
1499
1500
HandleEscapeKey()1501 void ToolManager::HandleEscapeKey()
1502 {
1503 if (mDragBar) {
1504 if(mPrevDock) {
1505 // Sheriff John Stone,
1506 // Why don't you leave me alone?
1507 // Well, I feel so break up
1508 // I want to go home.
1509 mPrevDock->RestoreConfiguration(mPrevConfiguration);
1510 mPrevDock->Dock( mDragBar, true, mPrevSlot );
1511 Updated();
1512
1513 // Done with the floater
1514 mDragWindow->ClearBar();
1515 mDragWindow->Destroy();
1516 mDragWindow = nullptr;
1517 mDragBar->Refresh(false);
1518 }
1519 else {
1520 // Floater remains, and returns to where it begain
1521 auto parent = mDragBar->GetParent();
1522 parent->SetPosition(mPrevPosition);
1523 mDragBar->SetDocked(NULL, false);
1524 }
1525
1526 DoneDragging();
1527 }
1528 }
1529
DoneDragging()1530 void ToolManager::DoneDragging()
1531 {
1532 // Done dragging - ensure grabber button isn't pushed
1533 if( mDragBar )
1534 {
1535 mDragBar->SetDocked( mDragBar->GetDock(), false );
1536 }
1537
1538 // Release capture
1539 auto &window = GetProjectFrame( *mParent );
1540 if( window.HasCapture() )
1541 {
1542 window.ReleaseMouse();
1543 }
1544
1545 // Hide the indicator
1546 mIndicator->Hide();
1547
1548 mDragWindow = NULL;
1549 mDragDock = NULL;
1550 mDragBar = NULL;
1551 mPrevDock = NULL;
1552 mPrevSlot = { ToolBarConfiguration::UnspecifiedPosition };
1553 mPrevConfiguration.Clear();
1554 mLastPos.x = mBarPos.x = -1;
1555 mLastPos.y = mBarPos.y = -1;
1556 mTimer.Stop();
1557 mDidDrag = false;
1558 mClicked = false;
1559
1560 RestoreFocus();
1561 }
1562
RestoreFocus()1563 bool ToolManager::RestoreFocus()
1564 {
1565 if (mLastFocus) {
1566 auto temp1 = AButton::TemporarilyAllowFocus();
1567 auto temp2 = ASlider::TemporarilyAllowFocus();
1568 auto temp3 = MeterPanelBase::TemporarilyAllowFocus();
1569 mLastFocus->SetFocus();
1570 return true;
1571 }
1572 return false;
1573 }
1574
1575 #include "../commands/CommandContext.h"
1576 #include "../Menus.h"
1577
AttachedToolBarMenuItem(ToolBarID id,const CommandID & name,const TranslatableString & label_in,const Registry::OrderingHint & hint,std::vector<ToolBarID> excludeIDs)1578 AttachedToolBarMenuItem::AttachedToolBarMenuItem(
1579 ToolBarID id, const CommandID &name, const TranslatableString &label_in,
1580 const Registry::OrderingHint &hint,
1581 std::vector< ToolBarID > excludeIDs )
1582 : mId{ id }
1583 , mAttachedItem{
1584 Registry::Placement{ wxT("View/Other/Toolbars/Toolbars/Other"), hint },
1585 ( MenuTable::FinderScope(
1586 [this](AudacityProject &) -> CommandHandlerObject&
__anonea3c83480202() 1587 { return *this; } ),
1588 MenuTable::Command( name, label_in,
1589 &AttachedToolBarMenuItem::OnShowToolBar,
1590 AlwaysEnabledFlag,
__anonea3c83480302()1591 CommandManager::Options{}.CheckTest( [id](AudacityProject &project){
1592 auto &toolManager = ToolManager::Get( project );
1593 return toolManager.IsVisible( id ); } ) ) ) }
1594 , mExcludeIds{ std::move( excludeIDs ) }
1595 {}
1596
OnShowToolBar(const CommandContext & context)1597 void AttachedToolBarMenuItem::OnShowToolBar( const CommandContext &context )
1598 {
1599 auto &project = context.project;
1600 auto &toolManager = ToolManager::Get( project );
1601
1602 if( !toolManager.IsVisible( mId ) )
1603 {
1604 for ( const auto excludedID : mExcludeIds )
1605 toolManager.Expose( excludedID, false );
1606 }
1607
1608 toolManager.ShowHide(mId);
1609 MenuManager::Get(project).ModifyToolbarMenus(project);
1610 }
1611