1 /**********************************************************************
2 
3   Audacity: A Digital Audio Editor
4 
5   ToolsToolBar.cpp
6 
7   Dominic Mazzoni
8   Shane T. Mueller
9   Leland Lucius
10 
11   See ToolsToolBar.h for details
12 
13 *******************************************************************//*!
14 
15 \class ToolsToolBar
16 \brief A kind of ToolBar with Tools on it.
17 
18   This class, which is a child of Toolbar, creates the
19   window containing the tool selection (ibeam, envelope,
20   move, zoom). The window can be embedded within a
21   normal project window, or within a ToolBarFrame.
22 
23   All of the controls in this window were custom-written for
24   Audacity - they are not native controls on any platform -
25   however, it is intended that the images could be easily
26   replaced to allow "skinning" or just customization to
27   match the look and feel of each platform.
28 
29 \see \ref Themability
30 *//*******************************************************************/
31 
32 
33 
34 #include "ToolsToolBar.h"
35 #include "ToolManager.h"
36 
37 // For compilers that support precompilation, includes "wx/wx.h".
38 #include <wx/wxprec.h>
39 
40 #include <wx/setup.h> // for wxUSE_* macros
41 
42 #ifndef WX_PRECOMP
43 #include <wx/defs.h>
44 #include <wx/event.h>
45 #include <wx/intl.h>
46 #include <wx/sizer.h>
47 #endif
48 #include <wx/tooltip.h>
49 
50 #include "Prefs.h"
51 #include "AllThemeResources.h"
52 #include "ImageManipulation.h"
53 #include "Project.h"
54 #include "../ProjectSettings.h"
55 #include "../ProjectWindow.h"
56 #include "../tracks/ui/Scrubbing.h"
57 
58 #include "../widgets/AButton.h"
59 
60 
61 IMPLEMENT_CLASS(ToolsToolBar, ToolBar);
62 
63 ////////////////////////////////////////////////////////////
64 /// Methods for ToolsToolBar
65 ////////////////////////////////////////////////////////////
66 
BEGIN_EVENT_TABLE(ToolsToolBar,ToolBar)67 BEGIN_EVENT_TABLE(ToolsToolBar, ToolBar)
68    EVT_COMMAND_RANGE(ToolCodes::firstTool + FirstToolID,
69                      ToolsToolBar::numTools - 1 + FirstToolID,
70                      wxEVT_COMMAND_BUTTON_CLICKED,
71                      ToolsToolBar::OnTool)
72 END_EVENT_TABLE()
73 
74 //Standard constructor
75 ToolsToolBar::ToolsToolBar( AudacityProject &project )
76 : ToolBar(project, ToolsBarID, XO("Tools"), wxT("Tools"))
77 {
78    using namespace ToolCodes;
79 
80    //Read the following wxASSERTs as documentating a design decision
81    wxASSERT( selectTool   == selectTool   - firstTool );
82    wxASSERT( envelopeTool == envelopeTool - firstTool );
83    wxASSERT( zoomTool     == zoomTool     - firstTool );
84    wxASSERT( drawTool     == drawTool     - firstTool );
85    wxASSERT( multiTool    == multiTool    - firstTool );
86    bool multiToolActive = false;
87    gPrefs->Read(wxT("/GUI/ToolBars/Tools/MultiToolActive"), &multiToolActive);
88 
89    if (multiToolActive)
90       mCurrentTool = multiTool;
91    else
92       mCurrentTool = selectTool;
93 
94    project.Bind(EVT_PROJECT_SETTINGS_CHANGE,
95       &ToolsToolBar::OnToolChanged, this);
96 }
97 
~ToolsToolBar()98 ToolsToolBar::~ToolsToolBar()
99 {
100    static_assert(
101       ToolsToolBar::numTools <= ToolCodes::numTools,
102       "mismatch in number of tools" );
103 }
104 
Get(AudacityProject & project)105 ToolsToolBar &ToolsToolBar::Get( AudacityProject &project )
106 {
107    auto &toolManager = ToolManager::Get( project );
108    return *static_cast<ToolsToolBar*>( toolManager.GetToolBar(ToolsBarID) );
109 }
110 
Get(const AudacityProject & project)111 const ToolsToolBar &ToolsToolBar::Get( const AudacityProject &project )
112 {
113    return Get( const_cast<AudacityProject&>( project )) ;
114 }
115 
RegenerateTooltips()116 void ToolsToolBar::RegenerateTooltips()
117 {
118 
119 // JKC:
120 //   Under Win98 Tooltips appear to be buggy, when you have a lot of
121 //   tooltip messages flying around.  I found that just creating a
122 //   twelfth tooltip caused Audacity to crash when it tried to show
123 //   any tooltip.
124 //
125 //   Win98 does NOT recover from this crash - for any application which is
126 //   using tooltips will also crash thereafter...  so you must reboot.
127 //   Rather weird.
128 //
129 //   Getting windows to process more of its stacked up messages seems
130 //   to workaround the problem.  The problem is not fully understood though
131 //   (as of April 2003).
132 
133    //	Vaughan, October 2003: Now we're crashing on Win2K if
134    // "Quit when closing last window" is unchecked, when we come back
135    // through here, on either of the wxSafeYield calls.
136    // James confirms that commenting them out does not cause his original problem
137    // to reappear, so they're commented out now.
138    //		wxSafeYield(); //Deal with some queued up messages...
139 
140    #if wxUSE_TOOLTIPS
141 
142    using namespace ToolCodes;
143 
144    static const struct Entry {
145       int tool;
146       CommandID commandName;
147       TranslatableString untranslatedLabel;
148    } table[] = {
149       { selectTool,   wxT("SelectTool"),    XO("Selection Tool")  },
150       { envelopeTool, wxT("EnvelopeTool"),  XO("Envelope Tool")   },
151       { zoomTool,     wxT("ZoomTool"),      XO("Zoom Tool")       },
152       { drawTool,     wxT("DrawTool"),      XO("Draw Tool")       },
153       { multiTool,    wxT("MultiTool"),     XO("Multi-Tool")      },
154    };
155 
156    for (const auto &entry : table) {
157       ComponentInterfaceSymbol command{
158          entry.commandName, entry.untranslatedLabel };
159       ToolBar::SetButtonToolTip( mProject,
160          *mTool[entry.tool], &command, 1u );
161    }
162 
163    #endif
164 
165    //		wxSafeYield();
166    return;
167 }
168 
UpdatePrefs()169 void ToolsToolBar::UpdatePrefs()
170 {
171    RegenerateTooltips();
172    ToolBar::UpdatePrefs();
173 }
174 
MakeTool(ToolsToolBar * pBar,teBmps eTool,int id,const TranslatableString & label)175 AButton * ToolsToolBar::MakeTool(
176    ToolsToolBar *pBar, teBmps eTool,
177    int id, const TranslatableString &label)
178 {
179    AButton *button = ToolBar::MakeButton(pBar,
180       bmpRecoloredUpSmall,
181       bmpRecoloredDownSmall,
182       bmpRecoloredUpHiliteSmall,
183       bmpRecoloredDownSmall, // Not bmpRecoloredHiliteSmall as down is inactive.
184       eTool, eTool, eTool,
185       wxWindowID(id + FirstToolID),
186       wxDefaultPosition, true,
187       theTheme.ImageSize( bmpRecoloredUpSmall ));
188    button->SetLabel( label );
189    pBar->mToolSizer->Add( button );
190    return button;
191 }
192 
193 
Populate()194 void ToolsToolBar::Populate()
195 {
196    SetBackgroundColour( theTheme.Colour( clrMedium  ) );
197    MakeButtonBackgroundsSmall();
198 
199    Add(mToolSizer = safenew wxGridSizer(2, 3, 1, 1));
200 
201    /* Tools */
202    using namespace ToolCodes;
203    mTool[ selectTool   ] = MakeTool( this, bmpIBeam, selectTool, XO("Selection Tool") );
204    mTool[ envelopeTool ] = MakeTool( this, bmpEnvelope, envelopeTool, XO("Envelope Tool") );
205    mTool[ drawTool     ] = MakeTool( this, bmpDraw, drawTool, XO("Draw Tool") );
206    mTool[ zoomTool     ] = MakeTool( this, bmpZoom, zoomTool, XO("Zoom Tool") );
207    mTool[ multiTool    ] = MakeTool( this, bmpMulti, multiTool, XO("Multi-Tool") );
208 
209    DoToolChanged();
210 
211    RegenerateTooltips();
212 }
213 
OnTool(wxCommandEvent & evt)214 void ToolsToolBar::OnTool(wxCommandEvent & evt)
215 {
216    // This will cause callback to OnToolChanged
217    auto iTool = evt.GetId() - ToolCodes::firstTool - FirstToolID;
218    auto pButton = mTool[iTool];
219    if (pButton->IsDown())
220       ProjectSettings::Get( mProject ).SetTool( iTool );
221    else
222       // Don't stay up
223       pButton->PushDown();
224 }
225 
OnToolChanged(wxCommandEvent & evt)226 void ToolsToolBar::OnToolChanged(wxCommandEvent &evt)
227 {
228    evt.Skip(); // Let other listeners hear the event too
229    if (evt.GetInt() != ProjectSettings::ChangedTool)
230       return;
231    DoToolChanged();
232    ProjectWindow::Get( mProject ).RedrawProject();
233 }
234 
DoToolChanged()235 void ToolsToolBar::DoToolChanged()
236 {
237    auto &projectSettings = ProjectSettings::Get( mProject );
238    using namespace ToolCodes;
239    mCurrentTool = projectSettings.GetTool() - firstTool;
240    for (int i = 0; i < numTools; i++)
241       if (i == mCurrentTool)
242          mTool[i]->PushDown();
243       else
244          mTool[i]->PopUp();
245 
246    gPrefs->Write(wxT("/GUI/ToolBars/Tools/MultiToolActive"),
247                  mTool[multiTool]->IsDown());
248    gPrefs->Flush();
249 }
250 
Create(wxWindow * parent)251 void ToolsToolBar::Create(wxWindow * parent)
252 {
253    ToolBar::Create(parent);
254    UpdatePrefs();
255 }
256 
257 static RegisteredToolbarFactory factory{ ToolsBarID,
__anon072cb6330102( )258    []( AudacityProject &project ){
259       return ToolBar::Holder{ safenew ToolsToolBar{ project } }; }
260 };
261 
262 namespace {
263 AttachedToolBarMenuItem sAttachment{
264    /* i18n-hint: Clicking this menu item shows a toolbar
265       that has some tools in it */
266    ToolsBarID, wxT("ShowToolsTB"), XXO("T&ools Toolbar"),
267 };
268 }
269 
270 // Following code injects menu items for changing the current tool
271 
272 #include "../TrackPanel.h"
273 
274 // private helper classes and functions
275 namespace {
276 
277 /// Called by handlers that set tools.
SetTool(AudacityProject & project,int tool)278 void SetTool(AudacityProject &project, int tool)
279 {
280    auto toolbar = &ToolsToolBar::Get( project );
281    if (toolbar) {
282       ProjectSettings::Get(project).SetTool(tool);
283       TrackPanel::Get( project ).Refresh(false);
284    }
285 }
286 
287 }
288 
289 /// Namespace for functions for View Toolbar menu
290 namespace ToolActions {
291 
292 // exported helper functions
293 // none
294 
295 // Menu handler functions
296 
297 struct Handler : CommandHandlerObject {
298 /// Handler to set the select tool active
OnSelectToolToolActions::Handler299 void OnSelectTool(const CommandContext &context)
300 {
301    SetTool(context.project, ToolCodes::selectTool);
302 }
303 
304 /// Handler to set the Envelope tool active
OnEnvelopeToolToolActions::Handler305 void OnEnvelopeTool(const CommandContext &context)
306 {
307    SetTool(context.project, ToolCodes::envelopeTool);
308 }
309 
OnDrawToolToolActions::Handler310 void OnDrawTool(const CommandContext &context)
311 {
312    SetTool(context.project, ToolCodes::drawTool);
313 }
314 
315 /// Handler to set the Zoom tool active
OnZoomToolToolActions::Handler316 void OnZoomTool(const CommandContext &context)
317 {
318    SetTool(context.project, ToolCodes::zoomTool);
319 }
320 
OnMultiToolToolActions::Handler321 void OnMultiTool(const CommandContext &context)
322 {
323    SetTool(context.project, ToolCodes::multiTool);
324 }
325 
OnPrevToolToolActions::Handler326 void OnPrevTool(const CommandContext &context)
327 {
328    auto &project = context.project;
329    auto &trackPanel = TrackPanel::Get( project );
330    auto &settings = ProjectSettings::Get( project );
331 
332    settings.SetTool(
333       (settings.GetTool() + (ToolCodes::numTools - 1 )) % ToolCodes::numTools);
334    trackPanel.Refresh(false);
335 }
336 
OnNextToolToolActions::Handler337 void OnNextTool(const CommandContext &context)
338 {
339    auto &project = context.project;
340    auto &trackPanel = TrackPanel::Get( project );
341    auto &settings = ProjectSettings::Get( project );
342 
343    settings.SetTool( (settings.GetTool() + 1) % ToolCodes::numTools );
344    trackPanel.Refresh(false);
345 }
346 
347 }; // struct Handler
348 
findCommandHandler(AudacityProject &)349 static CommandHandlerObject &findCommandHandler(AudacityProject &) {
350    // Handler is not stateful.  Doesn't need a factory registered with
351    // AudacityProject.
352    static ToolActions::Handler instance;
353    return instance;
354 };
355 
356 #define FN(X) (& ToolActions::Handler :: X)
357 
358 using namespace MenuTable;
ExtraToolsMenu()359 BaseItemSharedPtr ExtraToolsMenu()
360 {
361    static BaseItemSharedPtr menu{
362    ( FinderScope{ findCommandHandler },
363    Menu( wxT("Tools"), XXO("T&ools"),
364       Command( wxT("SelectTool"), XXO("&Selection Tool"), FN(OnSelectTool),
365          AlwaysEnabledFlag, wxT("F1") ),
366       Command( wxT("EnvelopeTool"), XXO("&Envelope Tool"),
367          FN(OnEnvelopeTool), AlwaysEnabledFlag, wxT("F2") ),
368       Command( wxT("DrawTool"), XXO("&Draw Tool"), FN(OnDrawTool),
369          AlwaysEnabledFlag, wxT("F3") ),
370       Command( wxT("ZoomTool"), XXO("&Zoom Tool"), FN(OnZoomTool),
371          AlwaysEnabledFlag, wxT("F4") ),
372       Command( wxT("MultiTool"), XXO("&Multi Tool"), FN(OnMultiTool),
373          AlwaysEnabledFlag, wxT("F6") ),
374       Command( wxT("PrevTool"), XXO("&Previous Tool"), FN(OnPrevTool),
375          AlwaysEnabledFlag, wxT("A") ),
376       Command( wxT("NextTool"), XXO("&Next Tool"), FN(OnNextTool),
377          AlwaysEnabledFlag, wxT("D") )
378    ) ) };
379    return menu;
380 }
381 
382 #undef FN
383 
384 AttachedItem sAttachment2{
385    wxT("Optional/Extra/Part1"),
386    Shared( ExtraToolsMenu() )
387 };
388 }
389