diff --git a/src/Internat.cpp b/src/Internat.cpp index 6a5c5f97a..687373cec 100644 --- a/src/Internat.cpp +++ b/src/Internat.cpp @@ -229,3 +229,12 @@ wxString Internat::StripAccelerators(const wxString &s) } return result; } + +wxString Internat::Parenthesize(const wxString &str) +{ + /* i18n-hint: An opening parenthesis, in some languages a right parenthesis */ + auto open = _("("); + /* i18n-hint: A closing parenthesis, in some languages a left parenthesis */ + auto close = _(")"); + return open + str + close; +} diff --git a/src/Internat.h b/src/Internat.h index 1a0306096..dd8465b81 100644 --- a/src/Internat.h +++ b/src/Internat.h @@ -73,6 +73,8 @@ public: * when they aren't, saving translators effort. */ static wxString StripAccelerators(const wxString& str); + static wxString Parenthesize(const wxString &str); + private: static wxChar mDecimalSeparator; diff --git a/src/Project.cpp b/src/Project.cpp index 5cc178c1e..b87c47050 100644 --- a/src/Project.cpp +++ b/src/Project.cpp @@ -545,7 +545,7 @@ AudacityProject *CreateNewAudacityProject() // Okay, GetActiveProject() is ready. Now we can get its CommandManager, // and add the shortcut keys to the tooltips. - p->GetControlToolBar()->RegenerateToolsTooltips(); + p->GetToolManager()->RegenerateTooltips(); ModuleManager::Get().Dispatch(ProjectInitialized); diff --git a/src/TrackPanel.cpp b/src/TrackPanel.cpp index 44e00f498..0aad99f64 100644 --- a/src/TrackPanel.cpp +++ b/src/TrackPanel.cpp @@ -491,6 +491,9 @@ TrackPanel::TrackPanel(wxWindow * parent, wxWindowID id, mAdjustLeftSelectionCursor = std::make_unique(wxCURSOR_POINT_LEFT); mAdjustRightSelectionCursor = std::make_unique(wxCURSOR_POINT_RIGHT); + // Menu pointers are set to NULL here. Delay building of menus until after + // the command managter is finished, so that we can look up shortcut + // key strings that need to appear in some of the popup menus. mWaveTrackMenu = NULL; mChannelItemsInsertionPoint = 0; @@ -500,8 +503,6 @@ TrackPanel::TrackPanel(wxWindow * parent, wxWindowID id, mRulerWaveformMenu = mRulerSpectrumMenu = NULL; - BuildMenus(); - mTrackArtist = new TrackArtist(); mTrackArtist->SetInset(1, kTopMargin, kRightMargin, kBottomMargin); @@ -586,6 +587,12 @@ TrackPanel::~TrackPanel() delete mInitialTrackSelection; } +void TrackPanel::BuildMenusIfNeeded(void) +{ + if (!mRateMenu) + BuildMenus(); +} + void TrackPanel::BuildMenus(void) { // Get rid of existing menus @@ -676,6 +683,8 @@ void TrackPanel::BuildCommonDropMenuItems(wxMenu * menu) { menu->Append(OnSetNameID, _("&Name...")); menu->AppendSeparator(); + // It is not correct to use KeyStringDisplay here -- wxWidgets will apply + // its equivalent to the key names passed to menu functions. menu->Append(OnMoveUpID, _("Move Track &Up") + wxT("\t") + (GetProject()->GetCommandManager()->GetKeyFromName(wxT("TrackMoveUp")))); menu->Append(OnMoveDownID, _("Move Track &Down") + wxT("\t") + @@ -708,6 +717,8 @@ void TrackPanel::DeleteMenus(void) { // Note that the submenus (mRateMenu, ...) // are deleted by their parent + mRateMenu = mFormatMenu = nullptr; + if (mWaveTrackMenu) { delete mWaveTrackMenu; mWaveTrackMenu = NULL; @@ -7480,6 +7491,8 @@ void TrackPanel::ScrollIntoView(int x) void TrackPanel::OnTrackMenu(Track *t) { + BuildMenusIfNeeded(); + if(!t) { t = GetFocusedTrack(); if(!t) return; @@ -8083,6 +8096,8 @@ void TrackPanel::SetRate(Track * pTrack, double rate) /// track menu. void TrackPanel::OnFormatChange(wxCommandEvent & event) { + BuildMenusIfNeeded(); + int id = event.GetId(); wxASSERT(id >= On16BitID && id <= OnFloatID); wxASSERT(mPopupMenuTarget @@ -8174,6 +8189,8 @@ static int gRates[nRates] = { 8000, 11025, 16000, 22050, 44100, 48000, 88200, 96 /// submenu of the track menu, except for "Other" (/see OnRateOther). void TrackPanel::OnRateChange(wxCommandEvent & event) { + BuildMenusIfNeeded(); + int id = event.GetId(); wxASSERT(id >= OnRate8ID && id <= OnRate384ID); wxASSERT(mPopupMenuTarget @@ -8199,6 +8216,8 @@ int TrackPanel::IdOfRate( int rate ) void TrackPanel::OnRateOther(wxCommandEvent &event) { + BuildMenusIfNeeded(); + wxASSERT(mPopupMenuTarget && mPopupMenuTarget->GetKind() == Track::Wave); diff --git a/src/TrackPanel.h b/src/TrackPanel.h index 49f5dd355..51e8d0a28 100644 --- a/src/TrackPanel.h +++ b/src/TrackPanel.h @@ -147,6 +147,7 @@ class AUDACITY_DLL_API TrackPanel final : public OverlayPanel { virtual ~ TrackPanel(); + virtual void BuildMenusIfNeeded(void); virtual void BuildMenus(void); virtual void DeleteMenus(void); diff --git a/src/commands/Keyboard.cpp b/src/commands/Keyboard.cpp index 194e2a138..6b052bd0e 100644 --- a/src/commands/Keyboard.cpp +++ b/src/commands/Keyboard.cpp @@ -43,13 +43,25 @@ wxString KeyStringNormalize(const wxString & key) #endif } -wxString KeyStringDisplay(const wxString & key) +wxString KeyStringDisplay(const wxString & key, bool useSspecialChars) { wxString newkey = KeyStringNormalize(key); #if defined(__WXMAC__) - newkey.Replace(wxT("XCtrl+"), wxT("Control+")); - newkey.Replace(wxT("Alt+"), wxT("Option+")); - newkey.Replace(wxT("Ctrl+"), wxT("Command+")); + + if (!useSspecialChars) { + // Compose user-visible keystroke names, all ASCII + newkey.Replace(wxT("XCtrl+"), wxT("Control+")); + newkey.Replace(wxT("Alt+"), wxT("Option+")); + newkey.Replace(wxT("Ctrl+"), wxT("Command+")); + } + else { + // Compuse user-visible keystroke names, with special characters + newkey.Replace(wxT("Shift+"), wxT("\u21e7")); + newkey.Replace(wxT("XCtrl+"), wxT("Control+")); + newkey.Replace(wxT("Alt+"), wxT("\u2325")); + newkey.Replace(wxT("Ctrl+"), wxT("\u2318")); + } + #endif return newkey; diff --git a/src/commands/Keyboard.h b/src/commands/Keyboard.h index 2f3ce7559..4f39e6663 100644 --- a/src/commands/Keyboard.h +++ b/src/commands/Keyboard.h @@ -14,5 +14,5 @@ #include wxString KeyStringNormalize(const wxString & key); -wxString KeyStringDisplay(const wxString & key); +wxString KeyStringDisplay(const wxString & key, bool useSpecialChars = false); wxString KeyEventToKeyString(const wxKeyEvent & keyEvent); diff --git a/src/toolbars/ControlToolBar.cpp b/src/toolbars/ControlToolBar.cpp index 720cb5fd6..4ead8e117 100644 --- a/src/toolbars/ControlToolBar.cpp +++ b/src/toolbars/ControlToolBar.cpp @@ -188,7 +188,7 @@ void ControlToolBar::Populate() mRecord->FollowModifierKeys(); #if wxUSE_TOOLTIPS - RegenerateToolsTooltips(); + RegenerateTooltips(); wxToolTip::Enable(true); wxToolTip::SetDelay(1000); #endif @@ -197,47 +197,41 @@ void ControlToolBar::Populate() ArrangeButtons(); } -void ControlToolBar::RegenerateToolsTooltips() +void ControlToolBar::RegenerateTooltips() { #if wxUSE_TOOLTIPS + std::vector commands; for (long iWinID = ID_PLAY_BUTTON; iWinID < BUTTON_COUNT; iWinID++) { - wxWindow* pCtrl = this->FindWindow(iWinID); - wxString strToolTip = pCtrl->GetLabel(); - AudacityProject* pProj = GetActiveProject(); - CommandManager* pCmdMgr = (pProj) ? pProj->GetCommandManager() : NULL; - if (pCmdMgr) + commands.clear(); + auto pCtrl = static_cast(this->FindWindow(iWinID)); + commands.push_back(pCtrl->GetLabel()); + switch (iWinID) { - wxString strKey(wxT(" (")); - switch (iWinID) - { - case ID_PLAY_BUTTON: - strKey += pCmdMgr->GetKeyFromName(wxT("Play")); - strKey += _(") / Loop Play ("); - strKey += pCmdMgr->GetKeyFromName(wxT("PlayLooped")); - break; - case ID_RECORD_BUTTON: - strKey += pCmdMgr->GetKeyFromName(wxT("Record")); - strKey += _(") / Append Record ("); - strKey += pCmdMgr->GetKeyFromName(wxT("RecordAppend")); - break; - case ID_PAUSE_BUTTON: - strKey += pCmdMgr->GetKeyFromName(wxT("Pause")); - break; - case ID_STOP_BUTTON: - strKey += pCmdMgr->GetKeyFromName(wxT("Stop")); - break; - case ID_FF_BUTTON: - strKey += pCmdMgr->GetKeyFromName(wxT("SkipEnd")); - break; - case ID_REW_BUTTON: - strKey += pCmdMgr->GetKeyFromName(wxT("SkipStart")); - break; - } - strKey += wxT(")"); - strToolTip += strKey; + case ID_PLAY_BUTTON: + commands.push_back(wxT("Play")); + commands.push_back(_("Loop Play")); + commands.push_back(wxT("PlayLooped")); + break; + case ID_RECORD_BUTTON: + commands.push_back(wxT("Record")); + commands.push_back(_("Append Record")); + commands.push_back(wxT("RecordAppend")); + break; + case ID_PAUSE_BUTTON: + commands.push_back(wxT("Pause")); + break; + case ID_STOP_BUTTON: + commands.push_back(wxT("Stop")); + break; + case ID_FF_BUTTON: + commands.push_back(wxT("SkipEnd")); + break; + case ID_REW_BUTTON: + commands.push_back(wxT("SkipStart")); + break; } - pCtrl->SetToolTip(strToolTip); + ToolBar::SetButtonToolTip(*pCtrl, commands); } #endif } @@ -262,14 +256,14 @@ void ControlToolBar::UpdatePrefs() if( updated ) { - ReCreateButtons(); // side effect: calls RegenerateToolsTooltips() + ReCreateButtons(); // side effect: calls RegenerateTooltips() Updated(); } else // The other reason to regenerate tooltips is if keyboard shortcuts for // transport buttons changed, but that's too much work to check for, so just // always do it. (Much cheaper than calling ReCreateButtons() in all cases. - RegenerateToolsTooltips(); + RegenerateTooltips(); // Set label to pull in language change @@ -383,7 +377,7 @@ void ControlToolBar::ReCreateButtons() EnableDisableButtons(); - RegenerateToolsTooltips(); + RegenerateTooltips(); } void ControlToolBar::Repaint( wxDC *dc ) diff --git a/src/toolbars/ControlToolBar.h b/src/toolbars/ControlToolBar.h index 29fc21893..9171dfcef 100644 --- a/src/toolbars/ControlToolBar.h +++ b/src/toolbars/ControlToolBar.h @@ -101,7 +101,7 @@ class ControlToolBar final : public ToolBar { void EnableDisableButtons() override; void ReCreateButtons() override; - void RegenerateToolsTooltips(); + void RegenerateTooltips() override; int WidthForStatusBar(wxStatusBar* const); void UpdateStatusBar(AudacityProject *pProject); diff --git a/src/toolbars/DeviceToolBar.h b/src/toolbars/DeviceToolBar.h index 261d3ccb6..c1a114119 100644 --- a/src/toolbars/DeviceToolBar.h +++ b/src/toolbars/DeviceToolBar.h @@ -63,7 +63,7 @@ class DeviceToolBar final : public ToolBar { void SetDevices(const DeviceSourceMap *in, const DeviceSourceMap *out); void RepositionCombos(); void SetNames(); - void RegenerateTooltips(); + void RegenerateTooltips() override; void ShowComboDialog(wxChoice *combo, const wxString &title); diff --git a/src/toolbars/EditToolBar.cpp b/src/toolbars/EditToolBar.cpp index 2dca40e34..009fc6b71 100644 --- a/src/toolbars/EditToolBar.cpp +++ b/src/toolbars/EditToolBar.cpp @@ -198,24 +198,40 @@ void EditToolBar::UpdatePrefs() void EditToolBar::RegenerateTooltips() { #if wxUSE_TOOLTIPS - mButtons[ETBCutID]->SetToolTip(_("Cut")); - mButtons[ETBCopyID]->SetToolTip(_("Copy")); - mButtons[ETBPasteID]->SetToolTip(_("Paste")); - mButtons[ETBTrimID]->SetToolTip(_("Trim Audio")); - mButtons[ETBSilenceID]->SetToolTip(_("Silence Audio")); - mButtons[ETBUndoID]->SetToolTip(_("Undo")); - mButtons[ETBRedoID]->SetToolTip(_("Redo")); - #ifdef EXPERIMENTAL_SYNC_LOCK - mButtons[ETBSyncLockID]->SetToolTip(_("Sync-Lock Tracks")); - #endif - mButtons[ETBZoomInID]->SetToolTip(_("Zoom In")); - mButtons[ETBZoomOutID]->SetToolTip(_("Zoom Out")); - mButtons[ETBZoomSelID]->SetToolTip(_("Fit Selection")); - mButtons[ETBZoomFitID]->SetToolTip(_("Fit Project")); + static const struct Entry { + int tool; + wxString commandName; + wxString untranslatedLabel; + } table[] = { + { ETBCutID, wxT("Cut"), XO("Cut") }, + { ETBCopyID, wxT("Copy"), XO("Copy") }, + { ETBPasteID, wxT("Paste"), XO("Paste") }, + { ETBTrimID, wxT("Trim"), XO("Trim Audio") }, + { ETBSilenceID, wxT("Silence"), XO("Silence Audio") }, + { ETBUndoID, wxT("Undo"), XO("Undo") }, + { ETBRedoID, wxT("Redo"), XO("Redo") }, + +#ifdef EXPERIMENTAL_SYNC_LOCK + { ETBSyncLockID, wxT("SyncLock"), XO("Sync-Lock Tracks") }, +#endif + + { ETBZoomInID, wxT("ZoomIn"), XO("Zoom In") }, + { ETBZoomOutID, wxT("ZoomOut"), XO("Zoom Out") }, + { ETBZoomSelID, wxT("ZoomSel"), XO("Fit Selection") }, + { ETBZoomFitID, wxT("FitInWindow"), XO("Fit Project") }, #if defined(EXPERIMENTAL_EFFECTS_RACK) - mButtons[ETBEffectsID]->SetToolTip(_("Open Effects Rack")); + { ETBEffectsID, wxT(""), XO("Open Effects Rack") }, #endif + }; + + std::vector commands; + for (const auto &entry : table) { + commands.clear(); + commands.push_back(wxGetTranslation(entry.untranslatedLabel)); + commands.push_back(entry.commandName); + ToolBar::SetButtonToolTip(*mButtons[entry.tool], commands); + } #endif } diff --git a/src/toolbars/EditToolBar.h b/src/toolbars/EditToolBar.h index 7f3496b51..aedf2a1bd 100644 --- a/src/toolbars/EditToolBar.h +++ b/src/toolbars/EditToolBar.h @@ -83,7 +83,7 @@ class EditToolBar final : public ToolBar { void MakeButtons(); - void RegenerateTooltips(); + void RegenerateTooltips() override; AButton *mButtons[ETBNumButtons]; @@ -136,7 +136,7 @@ public: void EnableDisableButtons(); void UpdatePrefs(); - void RegenerateTooltips(); + void RegenerateTooltips() override; private: diff --git a/src/toolbars/MeterToolBar.h b/src/toolbars/MeterToolBar.h index 6b5dac7f5..e6af911aa 100644 --- a/src/toolbars/MeterToolBar.h +++ b/src/toolbars/MeterToolBar.h @@ -53,7 +53,7 @@ class MeterToolBar final : public ToolBar { wxSize GetDockedSize(); private: - void RegenerateTooltips(); + void RegenerateTooltips() override; AudacityProject *mProject; int mWhichMeters; diff --git a/src/toolbars/MixerToolBar.cpp b/src/toolbars/MixerToolBar.cpp index 172d1c6af..43a5c0644 100644 --- a/src/toolbars/MixerToolBar.cpp +++ b/src/toolbars/MixerToolBar.cpp @@ -196,6 +196,8 @@ void MixerToolBar::UpdatePrefs() // Set label to pull in language change SetLabel(_("Mixer")); + RegenerateTooltips(); + // Give base class a chance ToolBar::UpdatePrefs(); } diff --git a/src/toolbars/MixerToolBar.h b/src/toolbars/MixerToolBar.h index cc94c67df..3ef6ed155 100644 --- a/src/toolbars/MixerToolBar.h +++ b/src/toolbars/MixerToolBar.h @@ -49,6 +49,8 @@ class MixerToolBar final : public ToolBar { void AdjustOutputGain(int adj); void AdjustInputGain(int adj); + void RegenerateTooltips() override {}; + protected: float mInputSliderVolume; float mOutputSliderVolume; diff --git a/src/toolbars/SelectionBar.cpp b/src/toolbars/SelectionBar.cpp index ce665490c..0eaa66344 100644 --- a/src/toolbars/SelectionBar.cpp +++ b/src/toolbars/SelectionBar.cpp @@ -296,6 +296,8 @@ void SelectionBar::UpdatePrefs() // Set label to pull in language change SetLabel(_("Selection")); + RegenerateTooltips(); + // Give base class a chance ToolBar::UpdatePrefs(); } diff --git a/src/toolbars/SelectionBar.h b/src/toolbars/SelectionBar.h index fe3f19e2e..4756e282d 100644 --- a/src/toolbars/SelectionBar.h +++ b/src/toolbars/SelectionBar.h @@ -49,6 +49,7 @@ class SelectionBar final : public ToolBar { void SetSelectionFormat(const wxString & format); void SetRate(double rate); void SetListener(SelectionBarListener *l); + void RegenerateTooltips() override {}; private: diff --git a/src/toolbars/SpectralSelectionBar.cpp b/src/toolbars/SpectralSelectionBar.cpp index 7dc674892..bef455779 100644 --- a/src/toolbars/SpectralSelectionBar.cpp +++ b/src/toolbars/SpectralSelectionBar.cpp @@ -209,6 +209,8 @@ void SpectralSelectionBar::UpdatePrefs() // Set label to pull in language change SetLabel(_("Spectral Selection")); + RegenerateTooltips(); + // Give base class a chance ToolBar::UpdatePrefs(); } diff --git a/src/toolbars/SpectralSelectionBar.h b/src/toolbars/SpectralSelectionBar.h index 75581f98f..54ad0bc32 100644 --- a/src/toolbars/SpectralSelectionBar.h +++ b/src/toolbars/SpectralSelectionBar.h @@ -46,6 +46,8 @@ public: void SetBandwidthSelectionFormatName(const wxString & formatName); void SetListener(SpectralSelectionBarListener *l); + void RegenerateTooltips() override {}; + private: void ValuesToControls(); diff --git a/src/toolbars/ToolBar.cpp b/src/toolbars/ToolBar.cpp index 433d7c851..b949dcfd6 100644 --- a/src/toolbars/ToolBar.cpp +++ b/src/toolbars/ToolBar.cpp @@ -48,6 +48,7 @@ in which buttons can be placed. #include "../ImageManipulation.h" #include "../Project.h" #include "../Theme.h" +#include "../commands/Keyboard.h" #include "../widgets/AButton.h" #include "../widgets/Grabber.h" @@ -761,6 +762,33 @@ void ToolBar::MakeAlternateImages(AButton &button, int idx, button.SetAlternateImages(idx, *up, *hilite, *down, *disable); } +void ToolBar::SetButtonToolTip +(AButton &button, const std::vector &commands, const wxString &separator) +{ + const auto project = GetActiveProject(); + const auto commandManager = project ? project->GetCommandManager() : nullptr; + wxString result; + auto iter = commands.begin(), end = commands.end(); + while (iter != end) { + result += *iter++; + if (iter != end) { + if (!iter->empty()) { + if (commandManager) { + auto keyStr = commandManager->GetKeyFromName(*iter); + if (keyStr.empty()) + keyStr = _("no key"); + result += wxT(" "); + result += Internat::Parenthesize(KeyStringDisplay(keyStr, true)); + } + } + ++iter; + } + if (iter != end) + result += separator; + } + button.SetToolTip(result); +} + // // This changes the state a button (from up to down or vice versa) // diff --git a/src/toolbars/ToolBar.h b/src/toolbars/ToolBar.h index 1e6d28575..8b7f0b3a9 100644 --- a/src/toolbars/ToolBar.h +++ b/src/toolbars/ToolBar.h @@ -15,6 +15,7 @@ #include "../Experimental.h" +#include #include #include #include @@ -98,6 +99,7 @@ class ToolBar /* not final */ : public wxPanel virtual void EnableDisableButtons() = 0; virtual void ReCreateButtons(); virtual void UpdatePrefs(); + virtual void RegenerateTooltips() = 0; int GetType(); wxString GetTitle(); @@ -149,7 +151,18 @@ class ToolBar /* not final */ : public wxPanel teBmps eStandardDown, teBmps eDisabled, wxSize size); - + + static + void SetButtonToolTip + (AButton &button, + // An array, alternating user-visible strings, and + // non-user-visible command names. If a shortcut key is defined + // for the command, then it is appended, parenthesized, after the + // user-visible string. + const std::vector &commands, + // If more than one pair of strings is given, then use this separator. + const wxString &separator = wxT(" / ")); + protected: void SetButton(bool down, AButton *button); diff --git a/src/toolbars/ToolManager.cpp b/src/toolbars/ToolManager.cpp index afdaafb69..4366b21d6 100644 --- a/src/toolbars/ToolManager.cpp +++ b/src/toolbars/ToolManager.cpp @@ -598,6 +598,14 @@ void ToolManager::Reset() Updated(); } +void ToolManager::RegenerateTooltips() +{ + for (auto bar : mBars) { + if (bar) + bar->RegenerateTooltips(); + } +} + // // Read the toolbar states // diff --git a/src/toolbars/ToolManager.h b/src/toolbars/ToolManager.h index 9c3050c12..40174fedd 100644 --- a/src/toolbars/ToolManager.h +++ b/src/toolbars/ToolManager.h @@ -65,6 +65,7 @@ class ToolManager final : public wxEvtHandler ToolDock *GetBotDock(); void Reset(); + void RegenerateTooltips(); private: diff --git a/src/toolbars/ToolsToolBar.cpp b/src/toolbars/ToolsToolBar.cpp index 20f0f3d03..e75842e16 100644 --- a/src/toolbars/ToolsToolBar.cpp +++ b/src/toolbars/ToolsToolBar.cpp @@ -111,7 +111,7 @@ ToolsToolBar::~ToolsToolBar() { } -void ToolsToolBar::RegenerateToolsTooltips() +void ToolsToolBar::RegenerateTooltips() { // JKC: @@ -136,12 +136,28 @@ void ToolsToolBar::RegenerateToolsTooltips() // wxSafeYield(); //Deal with some queued up messages... #if wxUSE_TOOLTIPS - mTool[selectTool]->SetToolTip(_("Selection Tool")); - mTool[envelopeTool]->SetToolTip(_("Envelope Tool")); - mTool[slideTool]->SetToolTip(_("Time Shift Tool")); - mTool[zoomTool]->SetToolTip(_("Zoom Tool")); - mTool[drawTool]->SetToolTip(_("Draw Tool")); - mTool[multiTool]->SetToolTip(_("Multi-Tool Mode")); + + static const struct Entry { + int tool; + wxString commandName; + wxString untranslatedLabel; + } table[] = { + { selectTool, wxT("SelectTool"), XO("Selection Tool") }, + { envelopeTool, wxT("EnvelopeTool"), XO("Envelope Tool") }, + { slideTool, wxT("TimeShiftTool"), XO("Time Shift Tool") }, + { zoomTool, wxT("ZoomTool"), XO("Zoom Tool") }, + { drawTool, wxT("DrawTool"), XO("Draw Tool") }, + { multiTool, wxT("MultiTool"), XO("Multi Tool") }, + }; + + std::vector commands; + for (const auto &entry : table) { + commands.clear(); + commands.push_back(wxGetTranslation(entry.untranslatedLabel)); + commands.push_back(entry.commandName); + ToolBar::SetButtonToolTip(*mTool[entry.tool], commands); + } + #endif // wxSafeYield(); @@ -150,7 +166,7 @@ void ToolsToolBar::RegenerateToolsTooltips() void ToolsToolBar::UpdatePrefs() { - RegenerateToolsTooltips(); + RegenerateTooltips(); } AButton * ToolsToolBar::MakeTool( teBmps eTool, @@ -183,7 +199,7 @@ void ToolsToolBar::Populate() mTool[mCurrentTool]->PushDown(); - RegenerateToolsTooltips(); + RegenerateTooltips(); } /// Gets the currently active tool diff --git a/src/toolbars/ToolsToolBar.h b/src/toolbars/ToolsToolBar.h index 4cc7937e9..0e76696c9 100644 --- a/src/toolbars/ToolsToolBar.h +++ b/src/toolbars/ToolsToolBar.h @@ -70,7 +70,7 @@ class ToolsToolBar final : public ToolBar { private: - void RegenerateToolsTooltips(); + void RegenerateTooltips() override; wxImage *MakeToolImage(wxImage *tool, wxImage *mask, int style); AButton *MakeTool(teBmps eTool, int id, const wxChar *label); diff --git a/src/toolbars/TranscriptionToolBar.cpp b/src/toolbars/TranscriptionToolBar.cpp index c4f22af04..334a26de2 100644 --- a/src/toolbars/TranscriptionToolBar.cpp +++ b/src/toolbars/TranscriptionToolBar.cpp @@ -298,7 +298,25 @@ void TranscriptionToolBar::UpdatePrefs() void TranscriptionToolBar::RegenerateTooltips() { - mButtons[TTB_PlaySpeed]->SetToolTip(_("Play-at-speed")); + // We could also mention the shift- and ctrl-modified versions in the + // tool tip... but it would get long + + static const struct Entry { + int tool; + wxString commandName; + wxString untranslatedLabel; + } table[] = { + { TTB_PlaySpeed, wxT("PlayAtSpeed"), XO("Play-at-speed") }, + }; + + std::vector commands; + for (const auto &entry : table) { + commands.clear(); + commands.push_back(wxGetTranslation(entry.untranslatedLabel)); + commands.push_back(entry.commandName); + ToolBar::SetButtonToolTip(*mButtons[entry.tool], commands); + } + #ifdef EXPERIMENTAL_VOICE_DETECTION mButtons[TTB_StartOn]->SetToolTip(TRANSLATABLE("Left-to-On")); diff --git a/src/toolbars/TranscriptionToolBar.h b/src/toolbars/TranscriptionToolBar.h index 734b37cce..28b7810d9 100644 --- a/src/toolbars/TranscriptionToolBar.h +++ b/src/toolbars/TranscriptionToolBar.h @@ -131,7 +131,7 @@ class TranscriptionToolBar final : public ToolBar { int id, unsigned altIdx); void GetSamples(WaveTrack *t, sampleCount *s0, sampleCount *slen); void SetButton(bool newstate, AButton *button); - void RegenerateTooltips(); + void RegenerateTooltips() override; AButton *mButtons[TTBNumButtons]; wxImage *upImage; diff --git a/src/widgets/AButton.cpp b/src/widgets/AButton.cpp index 83667a5d9..a5110932d 100644 --- a/src/widgets/AButton.cpp +++ b/src/widgets/AButton.cpp @@ -426,18 +426,8 @@ void AButton::OnMouseEvent(wxMouseEvent & event) if (newState != prevState) { Refresh(false); - if (mCursorIsInWindow) { - #if wxUSE_TOOLTIPS // Not available in wxX11 - // Display the tooltip in the status bar - wxToolTip * pTip = this->GetToolTip(); - if( pTip ) { - wxString tipText = pTip->GetTip(); - if (!mEnabled) - tipText += _(" (disabled)"); - GetActiveProject()->TP_DisplayStatusMessage(tipText); - } - #endif - } + if (mCursorIsInWindow) + UpdateStatus(); else { GetActiveProject()->TP_DisplayStatusMessage(wxT("")); } @@ -446,6 +436,22 @@ void AButton::OnMouseEvent(wxMouseEvent & event) event.Skip(); } +void AButton::UpdateStatus() +{ + if (mCursorIsInWindow) { +#if wxUSE_TOOLTIPS // Not available in wxX11 + // Display the tooltip in the status bar + wxToolTip * pTip = this->GetToolTip(); + if( pTip ) { + wxString tipText = pTip->GetTip(); + if (!mEnabled) + tipText += _(" (disabled)"); + GetActiveProject()->TP_DisplayStatusMessage(tipText); + } +#endif + } +} + void AButton::OnCaptureLost(wxMouseCaptureLostEvent & WXUNUSED(event)) { wxMouseEvent e(wxEVT_LEFT_UP); diff --git a/src/widgets/AButton.h b/src/widgets/AButton.h index 6858e7df8..3f1e3eeb4 100644 --- a/src/widgets/AButton.h +++ b/src/widgets/AButton.h @@ -99,6 +99,11 @@ class AButton final : public wxWindow { void OnPaint(wxPaintEvent & event); void OnSize(wxSizeEvent & event); void OnMouseEvent(wxMouseEvent & event); + + // Update the status bar message if the pointer is in the button. + // Else do nothing. + void UpdateStatus(); + void OnCaptureLost(wxMouseCaptureLostEvent & event); void OnKeyDown(wxKeyEvent & event); void OnSetFocus(wxFocusEvent & event); diff --git a/src/widgets/Ruler.cpp b/src/widgets/Ruler.cpp index bed7b01ae..ed0938c88 100644 --- a/src/widgets/Ruler.cpp +++ b/src/widgets/Ruler.cpp @@ -2060,19 +2060,6 @@ void AdornedRulerPanel::UpdatePrefs() RegenerateTooltips(mPrevZone); } -namespace -{ - wxString ComposeButtonLabel - (AudacityProject &project, const wxString &commandName, const wxString &label) - { - auto pCmdMgr = project.GetCommandManager(); - const auto &keyString = pCmdMgr->GetKeyFromName(commandName); - return keyString.empty() - ? label - : label + wxT(" (") + keyString + wxT(")"); - } -} - void AdornedRulerPanel::ReCreateButtons() { for (auto & button : mButtons) { @@ -2797,10 +2784,14 @@ void AdornedRulerPanel::OnContextMenu(wxContextMenuEvent & WXUNUSED(event)) void AdornedRulerPanel::UpdateButtonStates() { auto common = [this] - (wxWindow *button, const wxString &commandName, const wxString &label){ - const auto &fullLabel = ComposeButtonLabel(*mProject, commandName, label); - button->SetLabel(fullLabel); - button->SetToolTip(fullLabel); + (AButton &button, const wxString &commandName, const wxString &label) { + std::vector commands; + commands.push_back(label); + commands.push_back(commandName); + ToolBar::SetButtonToolTip(button, commands); + button.SetLabel(button.GetToolTipText()); + + button.UpdateStatus(); }; { @@ -2813,7 +2804,7 @@ void AdornedRulerPanel::UpdateButtonStates() // (which is, to toggle the state) ? _("Pinned play/record Head") : _("Unpinned play/record Head"); - common(pinButton, wxT("PinnedHead"), label); + common(*pinButton, wxT("PinnedHead"), label); } auto &scrubber = mProject->GetScrubber();