1 /**********************************************************************
2 
3   Audacity: A Digital Audio Editor
4 
5   ToolBar.cpp
6 
7   Dominic Mazzoni
8   Shane T. Mueller
9   Leland Lucius
10 
11   See ToolBar.h for details.
12 
13 *******************************************************************//**
14 
15 \file ToolBar.cpp
16 
17   Implements ToolBar
18 
19 *//*******************************************************************//**
20 
21 \class ToolBar
22 \brief Works with ToolManager and ToolDock to provide a dockable window
23 in which buttons can be placed.
24 
25 *//**********************************************************************/
26 
27 
28 #include "ToolBar.h"
29 
30 // For compilers that support precompilation, includes "wx/wx.h".
31 #include <wx/wxprec.h>
32 
33 #include <wx/setup.h> // for wxUSE_* macros
34 
35 #ifndef WX_PRECOMP
36 #include <wx/dcclient.h>
37 #include <wx/defs.h>
38 #include <wx/gdicmn.h>
39 #include <wx/image.h>
40 #include <wx/intl.h>
41 #include <wx/settings.h>
42 #include <wx/sizer.h>
43 #include <wx/sysopt.h>
44 #include <wx/window.h>
45 #endif  /*  */
46 
47 #include "ToolDock.h"
48 
49 #include "AllThemeResources.h"
50 #include "AColor.h"
51 #include "ImageManipulation.h"
52 #include "Project.h"
53 #include "../commands/CommandManager.h"
54 #include "../widgets/AButton.h"
55 #include "../widgets/Grabber.h"
56 #include "Prefs.h"
57 
58 ////////////////////////////////////////////////////////////
59 /// ToolBarResizer
60 ////////////////////////////////////////////////////////////
61 
62 //
63 // Width of the resize grab area
64 //
65 #define RWIDTH 4
66 
67 /// \brief a wxWindow that provides the resizer for a toolbar on the
68 /// right hand side.  Responsible for drawing the resizer appearance,
69 /// resizing mouse events and constraining the resizing.
70 class ToolBarResizer final : public wxWindow
71 {
72 public:
73    ToolBarResizer(ToolBar *mBar);
74    virtual ~ToolBarResizer();
75 
76    // We don't need or want to accept focus.
77    // Note that AcceptsFocusFromKeyboard() is overridden rather than
78    // AcceptsFocus(), so that resize can be cancelled by ESC
AcceptsFocusFromKeyboard() const79    bool AcceptsFocusFromKeyboard() const override {return false;}
80 
81 private:
82    void OnErase(wxEraseEvent & event);
83    void OnPaint(wxPaintEvent & event);
84    void OnLeftDown(wxMouseEvent & event);
85    void OnLeftUp(wxMouseEvent & event);
86    void OnEnter(wxMouseEvent & event);
87    void OnLeave(wxMouseEvent & event);
88    void OnMotion(wxMouseEvent & event);
89    void ResizeBar(const wxSize &size);
90    void OnCaptureLost(wxMouseCaptureLostEvent & event);
91    void OnKeyDown(wxKeyEvent &event);
92 
93 private:
94    ToolBar *mBar;
95    wxPoint mResizeOffset;
96    wxSize mOrigSize;
97    wxWindowRef mOrigFocus{};
98 
99    DECLARE_EVENT_TABLE()
100 };
101 
102 //
103 // Event table
104 //
105 BEGIN_EVENT_TABLE( ToolBarResizer, wxWindow )
106    EVT_ERASE_BACKGROUND( ToolBarResizer::OnErase )
107    EVT_PAINT( ToolBarResizer::OnPaint )
108    EVT_LEFT_DOWN( ToolBarResizer::OnLeftDown )
109    EVT_LEFT_UP( ToolBarResizer::OnLeftUp )
110    EVT_ENTER_WINDOW( ToolBarResizer::OnEnter )
111    EVT_LEAVE_WINDOW( ToolBarResizer::OnLeave )
112    EVT_MOTION( ToolBarResizer::OnMotion )
113    EVT_MOUSE_CAPTURE_LOST( ToolBarResizer::OnCaptureLost )
114    EVT_KEY_DOWN( ToolBarResizer::OnKeyDown )
115 END_EVENT_TABLE();
116 
ToolBarResizer(ToolBar * bar)117 ToolBarResizer::ToolBarResizer(ToolBar *bar)
118 :  wxWindow(bar, wxID_ANY, wxDefaultPosition, wxSize(RWIDTH, -1))
119 {
120    mBar = bar;
121    SetCursor( wxCURSOR_SIZEWE );
122 }
123 
~ToolBarResizer()124 ToolBarResizer::~ToolBarResizer()
125 {
126    if(HasCapture())
127       ReleaseMouse();
128 }
129 
130 //
131 // Handle background erasure
132 //
OnErase(wxEraseEvent & WXUNUSED (event))133 void ToolBarResizer::OnErase( wxEraseEvent & WXUNUSED(event) )
134 {
135    // Ignore it to prevent flashing
136 }
137 
138 //
139 // This draws the background of a toolbar
140 //
OnPaint(wxPaintEvent & event)141 void ToolBarResizer::OnPaint( wxPaintEvent & event )
142 {
143    wxPaintDC dc( (wxWindow *) event.GetEventObject() );
144 
145    // Start with a clean background
146    //
147    // Under GTK, we specifically set the toolbar background to the background
148    // colour in the system theme.
149 #if defined( __WXGTK__ )
150 //   dc.SetBackground( wxBrush( wxSystemSettings::GetColour( wxSYS_COLOUR_BACKGROUND ) ) );
151 #endif
152    dc.SetBackground( wxBrush( theTheme.Colour( clrMedium  ) ) );
153    dc.Clear();
154 
155    wxSize sz = GetSize();
156 
157    AColor::Dark( &dc, false );
158    AColor::Line(dc, sz.x - 4,  0, sz.x - 4, sz.y );
159    AColor::Line(dc, sz.x - 1,  0, sz.x - 1, sz.y );
160 }
161 
162 //
163 // Handle toolbar resizing
164 //
OnLeftDown(wxMouseEvent & event)165 void ToolBarResizer::OnLeftDown( wxMouseEvent & event )
166 {
167    // Go ahead and set the event to propagate
168    event.Skip();
169 
170    // Retrieve the mouse position
171    // Bug 1896: This is at time of processing the event, rather than at time
172    // of generation of event.  Works around event.GetPosition() giving
173    // incorrect values if position of resizer is changing.
174    mResizeOffset = wxGetMousePosition()-mBar->GetRect().GetBottomRight();
175 
176    mOrigSize = mBar->GetSize();
177 
178    // We want all of the mouse events
179    if( !HasCapture() )
180       CaptureMouse();
181 }
182 
OnLeftUp(wxMouseEvent & event)183 void ToolBarResizer::OnLeftUp( wxMouseEvent & event )
184 {
185    // Go ahead and set the event to propagate
186    event.Skip();
187 
188    if( HasCapture() )
189    {
190       ReleaseMouse();
191       if (mOrigFocus)
192          mOrigFocus->SetFocus();
193       mOrigFocus = nullptr;
194       mBar->ResizingDone();
195    }
196 }
197 
OnEnter(wxMouseEvent &)198 void ToolBarResizer::OnEnter( wxMouseEvent & /*event*/ )
199 {
200    // Bug 1201:  On Mac, unsetting and re-setting the tooltip may be needed
201    // to make it pop up when we want it.
202    const auto text = GetToolTipText();
203    UnsetToolTip();
204    SetToolTip(text);
205    if (!mOrigFocus)
206       mOrigFocus = FindFocus();
207 }
208 
OnLeave(wxMouseEvent &)209 void ToolBarResizer::OnLeave( wxMouseEvent & /*event*/ )
210 {
211    if (!GetCapture())
212       mOrigFocus = nullptr;
213 }
214 
OnMotion(wxMouseEvent & event)215 void ToolBarResizer::OnMotion( wxMouseEvent & event )
216 {
217    // Go ahead and set the event to propagate
218    event.Skip();
219 
220    if( HasCapture() && event.Dragging() )
221    {
222       // Retrieve the mouse position
223       // Bug 1896: This is at time of processing the event, rather than at time
224       // of generation of event.  Works around event.GetPosition() giving
225       // incorrect values if position of resizer is changing.
226       wxPoint pos = wxGetMousePosition();
227 
228       wxRect r = mBar->GetRect();
229       wxSize minsz = mBar->GetMinSize();
230       wxSize maxsz = mBar->GetMaxSize();
231       wxSize psz = mBar->GetParent()->GetClientSize();
232 
233       // Adjust the size based on updated mouse position.
234       r.width = ( pos.x - mResizeOffset.x ) - r.x;
235 
236       // Keep it within max size, if specified
237       if( maxsz != wxDefaultSize )
238       {
239          if( r.width > maxsz.x )
240          {
241             r.width = maxsz.x;
242          }
243          if( r.height > maxsz.y )
244          {
245             r.height = maxsz.y;
246          }
247       }
248 
249       // Constrain
250       if( r.width < minsz.x )
251       {
252          // Don't allow resizing to go too small
253          r.width = minsz.x;
254       }
255       else if( r.GetRight() > psz.x - 3 )
256       {
257          // Don't allow resizing to go too large
258          //
259          // The 3 magic pixels are because I'm too chicken to change the
260          // calculations in ToolDock::LayoutToolBars() even though I'm
261          // the one that set them up.  :-)
262          r.SetRight( psz.x - 3 );
263       }
264 
265       ResizeBar( r.GetSize() );
266    }
267 }
268 
ResizeBar(const wxSize & size)269 void ToolBarResizer::ResizeBar(const wxSize &size)
270 {
271    mBar->SetSize( size );
272 
273    // Tell everyone we've changed sizes
274    mBar->Updated();
275 
276    // Refresh our world
277    mBar->GetParent()->Refresh();
278    mBar->GetParent()->Update();
279 }
280 
OnCaptureLost(wxMouseCaptureLostEvent & WXUNUSED (event))281 void ToolBarResizer::OnCaptureLost( wxMouseCaptureLostEvent & WXUNUSED(event) )
282 {
283    if( HasCapture() )
284    {
285       ReleaseMouse();
286       if (mOrigFocus)
287          mOrigFocus->SetFocus();
288       mOrigFocus = nullptr;
289    }
290 }
291 
OnKeyDown(wxKeyEvent & event)292 void ToolBarResizer::OnKeyDown(wxKeyEvent &event)
293 {
294    event.Skip();
295    if (HasCapture() && WXK_ESCAPE == event.GetKeyCode()) {
296       ResizeBar( mOrigSize );
297       ReleaseMouse();
298       if (mOrigFocus)
299          mOrigFocus->SetFocus();
300       mOrigFocus = nullptr;
301    }
302 }
303 
304 ////////////////////////////////////////////////////////////
305 /// Methods for ToolBar
306 ////////////////////////////////////////////////////////////
307 
308 //
309 // Define class to RTTI
310 //
311 IMPLEMENT_CLASS( ToolBar, wxPanelWrapper );
312 
313 //
314 // Custom event
315 //
316 DEFINE_EVENT_TYPE(EVT_TOOLBAR_UPDATED)
317 
318 //
319 // Event table
320 //
BEGIN_EVENT_TABLE(ToolBar,wxPanelWrapper)321 BEGIN_EVENT_TABLE( ToolBar, wxPanelWrapper )
322    EVT_PAINT( ToolBar::OnPaint )
323    EVT_ERASE_BACKGROUND( ToolBar::OnErase )
324    EVT_MOUSE_EVENTS( ToolBar::OnMouseEvents )
325 END_EVENT_TABLE()
326 
327 //
328 // Constructor
329 //
330 ToolBar::ToolBar( AudacityProject &project,
331                   int type,
332                   const TranslatableString &label,
333                   const wxString &section,
334                   bool resizable )
335 : wxPanelWrapper()
336 , mProject{ project }
337 {
338    // Save parameters
339    mType = type;
340    mLabel = label;
341    mSection = section;
342    mResizable = resizable;
343 
344    // Initialize everything
345    mParent = NULL;
346    mHSizer = NULL;
347    mVisible = false;
348    mPositioned = false;
349 
350    mGrabber = NULL;
351    mResizer = NULL;
352    SetId(mType);
353 }
354 
355 //
356 // Destructor
357 //
~ToolBar()358 ToolBar::~ToolBar()
359 {
360 }
361 
362 //
363 // Returns the toolbar title
364 //
GetTitle()365 TranslatableString ToolBar::GetTitle()
366 {
367    /* i18n-hint: %s will be replaced by the name of the kind of toolbar.*/
368    return XO("Audacity %s Toolbar").Format( GetLabel() );
369 }
370 
371 //
372 // Returns the toolbar label
373 //
GetLabel()374 TranslatableString ToolBar::GetLabel()
375 {
376    return mLabel;
377 }
378 
379 //
380 // Returns the toolbar preferences section
381 //
GetSection()382 wxString ToolBar::GetSection()
383 {
384    return mSection;
385 }
386 
387 //
388 // Returns the toolbar type
389 //
GetType()390 int ToolBar::GetType()
391 {
392    return mType;
393 }
394 
395 //
396 // Set the toolbar label
397 //
SetLabel(const wxString & label)398 void ToolBar::SetLabel(const wxString & label)
399 {
400    // Probably shouldn't reach this overload, but perhaps virtual function
401    // dispatch will take us here from a pointer to the wxPanel base class
402    mLabel = Verbatim( label );
403 }
404 
SetLabel(const TranslatableString & label)405 void ToolBar::SetLabel(const TranslatableString & label)
406 {
407    // Only this overload is publicly accessible when you have a pointer to
408    // Toolbar or a subclass of it
409    mLabel = label;
410 }
411 
412 //
413 // Returns whether the toolbar is resizable or not
414 //
IsResizable() const415 bool ToolBar::IsResizable() const
416 {
417    return mResizable;
418 }
419 
420 //
421 // Returns the dock state of the toolbar
422 //
IsDocked() const423 bool ToolBar::IsDocked() const
424 {
425    return const_cast<ToolBar*>(this)->GetDock() != nullptr;
426 }
427 
428 //
429 // Returns the visibility of the toolbar
430 //
IsVisible() const431 bool ToolBar::IsVisible() const
432 {
433    return mVisible;
434 }
435 
SetVisible(bool bVisible)436 void ToolBar::SetVisible( bool bVisible )
437 {
438    mVisible = bVisible;
439 }
440 
441 //
442 // Show or hide the toolbar
443 //
Expose(bool show)444 bool ToolBar::Expose( bool show )
445 {
446    bool was = mVisible;
447 
448    SetVisible( show );
449 
450    if( IsDocked() )
451    {
452       Show( show );
453       if( show )
454       {
455          Refresh();
456       }
457    }
458    else
459    {
460       wxWindow * pParent = GetParent();
461       if( !IsPositioned() && show ){
462          SetPositioned();
463          pParent->CentreOnParent();
464          pParent->Move( pParent->GetPosition() + wxSize( mType*10, mType*10 ));
465       }
466       pParent->Show( show );
467    }
468 
469    return was;
470 }
471 
472 //
473 // Initialize the toolbar
474 //
Create(wxWindow * parent)475 void ToolBar::Create( wxWindow *parent )
476 {
477    // Save parameters
478    mParent = parent;
479 
480    // Create the window and label it
481    wxPanelWrapper::Create( mParent,
482                     mType,
483                     wxDefaultPosition,
484                     wxDefaultSize,
485                     wxNO_BORDER | wxTAB_TRAVERSAL,
486                     GetTitle() );
487    wxPanelWrapper::SetLabel( GetLabel() );
488 
489    // Go do the rest of the creation
490    ReCreateButtons();
491 
492    // ToolManager depends on this appearing to be visible for proper dock construction
493    mVisible = true;
494 }
495 
SetToDefaultSize()496 void ToolBar::SetToDefaultSize(){
497    wxSize sz;
498    sz.SetHeight( -1 );
499    sz.SetWidth( GetInitialWidth());
500    SetSize( sz );
501 }
502 
GetSmartDockedSize()503 wxSize ToolBar::GetSmartDockedSize()
504 {
505    const int tbs = toolbarSingle + toolbarGap;
506    wxSize sz = GetSize();
507    // 46 is the size where we switch from expanded to compact.
508    if( sz.y < 46 )
509       sz.y = tbs-1;
510    else
511       sz.y = 2 * tbs -1;
512    return sz;
513 }
514 
515 
ReCreateButtons()516 void ToolBar::ReCreateButtons()
517 {
518    wxSize sz3 = GetSize();
519    //wxLogDebug( "x:%i y:%i",sz3.x, sz3.y);
520 
521    // SetSizer(NULL) detaches mHSizer and deletes it.
522    // Do not use Detach() here, as that attempts to detach mHSizer from itself!
523    SetSizer( NULL );
524 
525    // Get rid of any children we may have
526    DestroyChildren();
527    mGrabber = NULL;
528    mResizer = NULL;
529    SetLayoutDirection(wxLayout_LeftToRight);
530 
531    // Refresh the background before populating
532    if (!IsDocked())
533    {
534       GetParent()->Refresh();
535    }
536 
537    {
538       // Create the main sizer
539       auto ms = std::make_unique<wxBoxSizer>(wxHORIZONTAL);
540 
541       // Create the grabber and add it to the main sizer
542       mGrabber = safenew Grabber(this, mType);
543       ms->Add(mGrabber, 0, wxEXPAND | wxALIGN_LEFT | wxALIGN_TOP | wxRIGHT, 1);
544 
545       // Use a box sizer for laying out controls
546       ms->Add((mHSizer = safenew wxBoxSizer(wxHORIZONTAL)), 1, wxEXPAND);
547 
548       // Go add all the rest of the gadgets
549       Populate();
550 
551       // Add some space for the resize border
552       if (IsResizable())
553       {
554          // Create the resizer and add it to the main sizer
555          mResizer = safenew ToolBarResizer(this);
556          ms->Add(mResizer, 0, wxEXPAND | wxALIGN_TOP | wxLEFT, 1);
557          mResizer->SetToolTip(_("Click and drag to resize toolbar"));
558       }
559 
560       // Set dock after possibly creating resizer.
561       // (Re)Establish dock state
562       SetDocked(GetDock(), false);
563 
564       // Set the sizer
565       SetSizerAndFit(ms.release());
566    }
567 
568    // Recalculate the height to be a multiple of toolbarSingle
569    const int tbs = toolbarSingle + toolbarGap;
570    wxSize sz = GetSize();
571    sz.y = ( ( ( sz.y + tbs -1) / tbs ) * tbs ) - 1;
572 
573    // Set the true AND minimum sizes and do final layout
574    if(IsResizable())
575    {
576       // JKC we're going to allow all resizable toolbars to be resized
577       // to 1 unit high, typically 27 pixels.
578       wxSize sz2 = sz;
579       sz2.SetWidth(GetMinToolbarWidth());
580       sz2.y = tbs -1;
581       SetMinSize(sz2);
582 
583       // sz2 is now the minimum size.
584       // sz3 is the size we were.
585 
586       // We're recreating buttons, and we want to preserve original size.
587       // But not if that makes the size too small.
588 
589       // Size at least as big as minimum.
590       if( sz3.y < sz2.y )
591          sz3.y = sz2.y;
592       if( sz3.x < sz2.x )
593          sz3.x = sz2.x;
594       SetSize(sz3);
595    }
596    else
597    {
598       SetInitialSize(sz);
599    }
600    Layout();
601 }
602 
603 // The application preferences have changed, so update any elements that may
604 // depend on them.
UpdatePrefs()605 void ToolBar::UpdatePrefs()
606 {
607 #if wxUSE_TOOLTIPS
608    // Change the tooltip of the grabber
609    if ( mGrabber )
610    {
611       mGrabber->SetToolTip( GetTitle() );
612    }
613 
614    // Change the tooltip of the resizer
615    if ( mResizer )
616    {
617       mResizer->SetToolTip( _("Click and drag to resize toolbar") );
618       wxSizeEvent e;
619       GetParent()->GetEventHandler()->AddPendingEvent( e );
620       GetParent()->Refresh();
621    }
622 #endif
623 
624    return;
625 }
626 
627 //
628 // Return the pointer to the ToolBock where this bar lives
629 //
GetDock()630 ToolDock *ToolBar::GetDock()
631 {
632    return dynamic_cast<ToolDock*>(GetParent());
633 }
634 
635 //
636 // Toggle the docked/floating state
637 //
SetDocked(ToolDock * dock,bool pushed)638 void ToolBar::SetDocked( ToolDock *dock, bool pushed )
639 {
640    // Remember it
641 //   mDock = dock;
642 
643    // Change the tooltip of the grabber
644 #if wxUSE_TOOLTIPS
645    mGrabber->SetToolTip( GetTitle() );
646 #endif
647 
648    // Set the grabber button state
649    mGrabber->PushButton( pushed );
650 
651    if (mResizer)
652    {
653       mResizer->Show(dock != NULL);
654       Layout();
655    }
656 }
657 
658 //
659 // Notify parent of changes
660 //
Updated()661 void ToolBar::Updated()
662 {
663    if( IsDocked() )
664       GetDock()->Updated();
665    else
666       // Bug 2120.  Changing the choice also changes the size of the toolbar so
667       // we need to update the client size, even if undocked.
668       // If modifying/improving this, remember to test both changing the choice,
669       // and clicking on the choice but not actually changing it.
670       GetParent()->SetClientSize( GetSize() + wxSize( 2,2));
671    //wxCommandEvent e( EVT_TOOLBAR_UPDATED, GetId() );
672    //GetParent()->GetEventHandler()->AddPendingEvent( e );
673 }
674 
675 //
676 // Returns a pointer to the main sizer
677 //
GetSizer()678 wxBoxSizer *ToolBar::GetSizer()
679 {
680    return mHSizer;
681 }
682 
683 //
684 // Add a window to the main sizer
685 //
Add(wxWindow * window,int proportion,int flag,int border,wxObject * userData)686 void ToolBar::Add( wxWindow *window,
687                    int proportion,
688                    int flag,
689                    int border,
690                    wxObject* userData )
691 {
692    mHSizer->Add( window,
693                  proportion,
694                  flag,
695                  border,
696                  userData );
697 }
698 
699 //
700 // Add a child sizer to the main sizer
701 //
Add(wxSizer * sizer,int proportion,int flag,int border,wxObject * userData)702 void ToolBar::Add( wxSizer *sizer,
703                    int proportion,
704                    int flag,
705                    int border,
706                    wxObject* userData )
707 {
708    mHSizer->Add( sizer,
709                  proportion,
710                  flag,
711                  border,
712                  userData );
713 }
714 
715 //
716 // Add some space to the main sizer
717 //
Add(int width,int height,int proportion,int flag,int border,wxObject * userData)718 void ToolBar::Add( int width,
719                    int height,
720                    int proportion,
721                    int flag,
722                    int border,
723                    wxObject* userData )
724 {
725    mHSizer->Add( width,
726                  height,
727                  proportion,
728                  flag,
729                  border,
730                  userData );
731 }
732 
733 //
734 // Adds a spacer to the main sizer
735 //
AddSpacer(int size)736 void ToolBar::AddSpacer( int size )
737 {
738    mHSizer->AddSpacer( size );
739 }
740 
741 //
742 // Adds a strechable spacer to the main sizer
743 //
AddStretchSpacer(int prop)744 void ToolBar::AddStretchSpacer( int prop )
745 {
746    mHSizer->AddStretchSpacer( prop );
747 }
748 
749 //
750 // Detach a window from the main sizer
751 //
Detach(wxWindow * window)752 void ToolBar::Detach( wxWindow *window )
753 {
754    mHSizer->Detach( window );
755 }
756 
757 //
758 // Detach a child sizer from the main sizer
759 //
Detach(wxSizer * sizer)760 void ToolBar::Detach( wxSizer *sizer )
761 {
762    mHSizer->Detach( sizer );
763 }
764 
MakeMacRecoloredImage(teBmps eBmpOut,teBmps eBmpIn)765 void ToolBar::MakeMacRecoloredImage(teBmps eBmpOut, teBmps eBmpIn )
766 {
767    theTheme.ReplaceImage( eBmpOut, &theTheme.Image( eBmpIn ));
768 }
769 
MakeRecoloredImage(teBmps eBmpOut,teBmps eBmpIn)770 void ToolBar::MakeRecoloredImage( teBmps eBmpOut, teBmps eBmpIn )
771 {
772    // Don't recolour the buttons...
773    MakeMacRecoloredImage( eBmpOut, eBmpIn );
774 }
775 
MakeButtonBackgroundsLarge()776 void ToolBar:: MakeButtonBackgroundsLarge()
777 {
778 
779    bool bUseAqua = false;
780 
781 #ifdef EXPERIMENTAL_THEME_PREFS
782    gPrefs->Read( wxT("/GUI/ShowMac"), &bUseAqua, false);
783 #endif
784 
785 #ifdef USE_AQUA_THEME
786    bUseAqua = !bUseAqua;
787 #endif
788 
789    if( bUseAqua ){
790       MakeMacRecoloredImage( bmpRecoloredUpLarge,       bmpMacUpButton );
791       MakeMacRecoloredImage( bmpRecoloredDownLarge,     bmpMacDownButton );
792       MakeMacRecoloredImage( bmpRecoloredUpHiliteLarge, bmpMacHiliteUpButton );
793       MakeMacRecoloredImage( bmpRecoloredHiliteLarge,   bmpMacHiliteButton );
794    } else {
795       MakeRecoloredImage( bmpRecoloredUpLarge,       bmpUpButtonLarge );
796       MakeRecoloredImage( bmpRecoloredDownLarge,     bmpDownButtonLarge );
797       MakeRecoloredImage( bmpRecoloredUpHiliteLarge, bmpHiliteUpButtonLarge );
798       MakeRecoloredImage( bmpRecoloredHiliteLarge,   bmpHiliteButtonLarge );
799    }
800 }
801 
MakeButtonBackgroundsSmall()802 void ToolBar::MakeButtonBackgroundsSmall()
803 {
804 
805    bool bUseAqua = false;
806 
807 #ifdef EXPERIMENTAL_THEME_PREFS
808    gPrefs->Read( wxT("/GUI/ShowMac"), &bUseAqua, false);
809 #endif
810 
811 #ifdef USE_AQUA_THEME
812    bUseAqua = !bUseAqua;
813 #endif
814 
815    if( bUseAqua ){
816       MakeMacRecoloredImage( bmpRecoloredUpSmall,       bmpMacUpButtonSmall );
817       MakeMacRecoloredImage( bmpRecoloredDownSmall,     bmpMacDownButtonSmall );
818       MakeMacRecoloredImage( bmpRecoloredUpHiliteSmall, bmpMacHiliteUpButtonSmall );
819       MakeMacRecoloredImage( bmpRecoloredHiliteSmall,   bmpMacHiliteButtonSmall );
820    } else {
821       MakeRecoloredImage(    bmpRecoloredUpSmall,       bmpUpButtonSmall );
822       MakeRecoloredImage(    bmpRecoloredDownSmall,     bmpDownButtonSmall );
823       MakeRecoloredImage(    bmpRecoloredUpHiliteSmall, bmpHiliteUpButtonSmall );
824       MakeRecoloredImage(    bmpRecoloredHiliteSmall,   bmpHiliteButtonSmall );
825    }
826 }
827 
828 /// Makes a button and its four different state bitmaps
829 /// @param parent            Parent window for the button.
830 /// @param eUp               Background for when button is Up.
831 /// @param eDown             Background for when button is Down.
832 /// @param eHilite           Background for when button is Hilit.
833 /// @param eStandardUp       Foreground when enabled, up.
834 /// @param eStandardDown     Foreground when enabled, down.
835 /// @param eDisabled         Foreground when disabled.
836 /// @param id                Windows Id.
837 /// @param placement         Placement position
838 /// @param processdownevents true iff button handles down events.
839 /// @param size              Size of the background.
MakeButton(wxWindow * parent,teBmps eUp,teBmps eDown,teBmps eHilite,teBmps eDownHi,teBmps eStandardUp,teBmps eStandardDown,teBmps eDisabled,wxWindowID id,wxPoint placement,bool processdownevents,wxSize size)840 AButton * ToolBar::MakeButton(wxWindow *parent,
841                               teBmps eUp,
842                               teBmps eDown,
843                               teBmps eHilite,
844                               teBmps eDownHi,
845                               teBmps eStandardUp,
846                               teBmps eStandardDown,
847                               teBmps eDisabled,
848                               wxWindowID id,
849                               wxPoint placement,
850                               bool processdownevents,
851                               wxSize size)
852 {
853    // wxMax to cater for case of image being bigger than the button.
854    int xoff = wxMax( 0, (size.GetWidth() - theTheme.Image(eStandardUp).GetWidth())/2);
855    int yoff = wxMax( 0, (size.GetHeight() - theTheme.Image(eStandardUp).GetHeight())/2);
856 
857    typedef std::unique_ptr<wxImage> wxImagePtr;
858    wxImagePtr up2        (OverlayImage(eUp,     eStandardUp, xoff, yoff));
859    wxImagePtr hilite2    (OverlayImage(eHilite, eStandardUp, xoff, yoff));
860    wxImagePtr down2      (OverlayImage(eDown,   eStandardDown, xoff + 1, yoff + 1));
861    wxImagePtr downHi2    (OverlayImage(eDownHi, eStandardDown, xoff + 1, yoff + 1));
862    wxImagePtr disable2   (OverlayImage(eUp,     eDisabled, xoff, yoff));
863 
864    wxASSERT(parent); // to justify safenew
865    AButton * button =
866       safenew AButton(parent, id, placement, size, *up2, *hilite2, *down2, *downHi2,
867             *disable2, processdownevents);
868 
869    return button;
870 }
871 
872 //static
MakeAlternateImages(AButton & button,int idx,teBmps eUp,teBmps eDown,teBmps eHilite,teBmps eDownHi,teBmps eStandardUp,teBmps eStandardDown,teBmps eDisabled,wxSize size)873 void ToolBar::MakeAlternateImages(AButton &button, int idx,
874                                   teBmps eUp,
875                                   teBmps eDown,
876                                   teBmps eHilite,
877                                   teBmps eDownHi,
878                                   teBmps eStandardUp,
879                                   teBmps eStandardDown,
880                                   teBmps eDisabled,
881                                   wxSize size)
882 {
883    // wxMax to cater for case of image being bigger than the button.
884    int xoff = wxMax( 0, (size.GetWidth() - theTheme.Image(eStandardUp).GetWidth())/2);
885    int yoff = wxMax( 0, (size.GetHeight() - theTheme.Image(eStandardUp).GetHeight())/2);
886 
887    typedef std::unique_ptr<wxImage> wxImagePtr;
888    wxImagePtr up        (OverlayImage(eUp,     eStandardUp, xoff, yoff));
889    wxImagePtr hilite    (OverlayImage(eHilite, eStandardUp, xoff, yoff));
890    wxImagePtr down      (OverlayImage(eDown,   eStandardDown, xoff + 1, yoff + 1));
891    wxImagePtr downHi    (OverlayImage(eDownHi, eStandardDown, xoff + 1, yoff + 1));
892    wxImagePtr disable   (OverlayImage(eUp,     eDisabled, xoff, yoff));
893 
894    button.SetAlternateImages(idx, *up, *hilite, *down, *downHi, *disable);
895 }
896 
SetButtonToolTip(AudacityProject & theProject,AButton & button,const ComponentInterfaceSymbol commands[],size_t nCommands)897 void ToolBar::SetButtonToolTip
898 (AudacityProject &theProject,
899  AButton &button, const ComponentInterfaceSymbol commands[], size_t nCommands)
900 {
901    TranslatableString result;
902    const auto project = &theProject;
903    const auto commandManager =
904       project ? &CommandManager::Get( *project ) : nullptr;
905    if (commandManager)
906       result =
907          commandManager->DescribeCommandsAndShortcuts(commands, nCommands);
908    button.SetToolTip( result );
909 }
910 
911 //
912 // This changes the state a button (from up to down or vice versa)
913 //
SetButton(bool down,AButton * button)914 void ToolBar::SetButton( bool down, AButton * button )
915 {
916    if( down )
917    {
918       button->PushDown();
919    }
920    else
921    {
922       button->PopUp();
923    }
924 }
925 
926 //
927 // Handle background erasure
928 //
OnErase(wxEraseEvent & WXUNUSED (event))929 void ToolBar::OnErase( wxEraseEvent & WXUNUSED(event) )
930 {
931    // Ignore it to prevent flashing
932 }
933 
934 //
935 // This draws the background of a toolbar
936 //
OnPaint(wxPaintEvent & WXUNUSED (event))937 void ToolBar::OnPaint( wxPaintEvent & WXUNUSED(event) )
938 {
939    wxPaintDC dc( this );
940 
941    // Themed background colour.
942    dc.SetBackground( wxBrush( theTheme.Colour( clrMedium  ) ) );
943    dc.Clear();
944 
945    Repaint( &dc );
946 }
947 
OnMouseEvents(wxMouseEvent & event)948 void ToolBar::OnMouseEvents(wxMouseEvent &event)
949 {
950    // Do this hack so scrubber can detect mouse drags anywhere
951    event.ResumePropagation(wxEVENT_PROPAGATE_MAX);
952    event.Skip();
953 }
954 
GetResizeGrabberWidth()955 int ToolBar::GetResizeGrabberWidth()
956 {
957    return RWIDTH;
958 }
959 
960 namespace {
961 
GetFunctions()962 RegisteredToolbarFactory::Functions &GetFunctions()
963 {
964    static RegisteredToolbarFactory::Functions factories( ToolBarCount );
965    return factories;
966 }
967 
968 }
969 
RegisteredToolbarFactory(int id,const Function & function)970 RegisteredToolbarFactory::RegisteredToolbarFactory(
971    int id, const Function &function)
972 {
973    wxASSERT( id >= 0 && id < ToolBarCount );
974    GetFunctions()[ id ] = function;
975 }
976 
GetFactories()977 auto RegisteredToolbarFactory::GetFactories() -> const Functions&
978 {
979    return GetFunctions();
980 }
981