/********************************************************************** Audacity: A Digital Audio Editor TimeToolBar.cpp This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. *//*******************************************************************/ // For compilers that support precompilation, includes "wx/wx.h". #include #include // for wxUSE_* macros #ifndef WX_PRECOMP #include #include #endif #include "TimeToolBar.h" #include "SelectionBarListener.h" #include "ToolManager.h" #include "../AudioIO.h" #include "../Project.h" #include "../ProjectAudioIO.h" #include "../ProjectSettings.h" #include "../ViewInfo.h" IMPLEMENT_CLASS(TimeToolBar, ToolBar); // Having a fixed ID for the Audio Position is helpful for // the Jaws screen reader script for Audacity. enum { TimeBarFirstID = 2800, AudioPositionID }; BEGIN_EVENT_TABLE(TimeToolBar, ToolBar) EVT_COMMAND(AudioPositionID, EVT_TIMETEXTCTRL_UPDATED, TimeToolBar::OnUpdate) EVT_SIZE(TimeToolBar::OnSize) EVT_IDLE(TimeToolBar::OnIdle) END_EVENT_TABLE() TimeToolBar::TimeToolBar(AudacityProject &project) : ToolBar(project, TimeBarID, XO("Time"), wxT("Time"), true), mListener(NULL), mAudioTime(NULL) { project.Bind(EVT_PROJECT_SETTINGS_CHANGE, &TimeToolBar::OnSettingsChanged, this); } TimeToolBar::~TimeToolBar() { } TimeToolBar &TimeToolBar::Get(AudacityProject &project) { auto &toolManager = ToolManager::Get(project); return *static_cast(toolManager.GetToolBar(TimeBarID)); } const TimeToolBar &TimeToolBar::Get(const AudacityProject &project) { return Get(const_cast(project)) ; } void TimeToolBar::Populate() { const auto &settings = ProjectSettings::Get(mProject); // Get the default sample rate auto rate = settings.GetRate(); // Get the default time format auto format = settings.GetAudioTimeFormat(); // Create the read-only time control mAudioTime = safenew NumericTextCtrl(this, AudioPositionID, NumericConverter::TIME, format, 0.0, rate); mAudioTime->SetName(XO("Audio Position")); mAudioTime->SetReadOnly(true); // Add it to the toolbar Add(mAudioTime, 0, wxALIGN_CENTER, 0); // Calculate the width to height ratio wxSize digitSize = mAudioTime->GetDigitSize(); mDigitRatio = (float)digitSize.x / digitSize.y; // During initialization, we need to bypass some resizing to prevent the "best size" // from being used as we want to ensure the saved size is used instead. See SetDocked() // and OnUpdate() for more info. mSettingInitialSize = true; // Establish initial resizing limits // SetResizingLimits(); } void TimeToolBar::UpdatePrefs() { // Since the language may have changed, we need to force an update to accommodate // different length text wxCommandEvent e; e.SetInt(mAudioTime->GetFormatIndex()); OnUpdate(e); // Language may have changed so reset label SetLabel(XO("Time")); // Give the toolbar a chance ToolBar::UpdatePrefs(); } void TimeToolBar::SetToDefaultSize() { // Reset SetMaxSize(wxDefaultSize); SetMinSize(wxDefaultSize); // Set the default time format SetAudioTimeFormat(NumericConverter::HoursMinsSecondsFormat()); // Set the default size SetSize(GetInitialWidth(), 48); // Inform others the toobar has changed Updated(); } wxSize TimeToolBar::GetDockedSize() { wxSize sz = GetSize(); // Anything less than a single height bar becomes single height if (sz.y <= toolbarSingle) { sz.y = toolbarSingle; } // Otherwise it will be a double height bar else { sz.y = 2 * toolbarSingle + toolbarGap; } return sz; } void TimeToolBar::SetDocked(ToolDock *dock, bool pushed) { // It's important to call this FIRST since it unhides the resizer control. // Not doing so causes the calculated best size to be off by the width // of the resizer. ToolBar::SetDocked(dock, pushed); // Recalculate the min and max limits SetResizingLimits(); // When moving from floater to dock, fit to toolbar since the resizer will // be mispositioned if (dock) { // During initialization, the desired size is already set, so do not // override it with the "best size". See OnUpdate for further info. if (!mSettingInitialSize) { // Fit() while retaining height SetSize(GetBestSize().x, GetSize().y); // Inform others the toolbar has changed Updated(); } } } void TimeToolBar::SetListener(TimeToolBarListener *l) { // Remember the listener mListener = l; // Get (and set) the saved time format SetAudioTimeFormat(mListener->TT_GetAudioTimeFormat()); // During initialization, if the saved format is the same as the default, // OnUpdate() will not be called and need it to set the initial size. if (mSettingInitialSize) { wxCommandEvent e; e.SetInt(mAudioTime->GetFormatIndex()); OnUpdate(e); } } void TimeToolBar::SetAudioTimeFormat(const NumericFormatSymbol & format) { // Set the format if it's different from previous if (mAudioTime->SetFormatString(mAudioTime->GetBuiltinFormat(format))) { // Simulate an update since the format has changed. wxCommandEvent e; e.SetInt(mAudioTime->GetFormatIndex()); OnUpdate(e); } } // The intention of this is to get the resize handle in the // correct position, after we've let go in dragging. void TimeToolBar::ResizingDone() { // Fit() while retaining height SetSize(GetBestSize().x, GetSize().y); // Inform others the toobar has changed Updated(); } void TimeToolBar::SetResizingLimits() { // Reset limits SetMinSize(wxDefaultSize); SetMaxSize(wxDefaultSize); // If docked we use the current bar height since it's always a single or double height // toolbar. For floaters, single height toolbar is the minimum height. int minH = IsDocked() ? GetSize().y : toolbarSingle; // Get the content size given the smallest digit height we allow wxSize minSize = ComputeSizing(minDigitH); // Account for any borders added by the window manager minSize.x += (mAudioTime->GetSize().x - mAudioTime->GetClientSize().x); // Calculate the space used by other controls and sizer borders with this toolbar wxSize outer = (GetSize() - GetSizer()->GetSize()); // And account for them in the width minSize.x += outer.x; // Override the height minSize.y = minH; // Get the maximum digit height we can use. This is restricted to the toolbar's // current height minus any control borders int digH = minH - (mAudioTime->GetSize().y - mAudioTime->GetClientSize().y); // Get the content size using the digit height, if docked. Otherwise use the // maximum digit height we allow. wxSize maxSize = ComputeSizing(IsDocked() ? digH : maxDigitH); // Account for the other controls and sizer borders within this toolbar maxSize.x += outer.x; // Account for any borders added by the window manager and +1 to keep toolbar // from dropping to next smaller size when grabbing the resizer. maxSize.x += (mAudioTime->GetSize().x - mAudioTime->GetClientSize().x) + 1; // Override the height maxSize.y = IsDocked() ? minH : wxDefaultCoord; // And finally set them both SetMinSize(minSize); SetMaxSize(maxSize); } // Called when the project settings change void TimeToolBar::OnSettingsChanged(wxCommandEvent &evt) { evt.Skip(false); if (evt.GetInt() == ProjectSettings::ChangedProjectRate && mAudioTime) { const auto &settings = ProjectSettings::Get(mProject); mAudioTime->SetSampleRate(settings.GetRate()); } } // Called when the format drop downs is changed. // This causes recreation of the toolbar contents. void TimeToolBar::OnUpdate(wxCommandEvent &evt) { evt.Skip(false); // Reset to allow resizing to work SetMinSize(wxDefaultSize); SetMaxSize(wxDefaultSize); // Save format name before recreating the controls so they resize properly if (mListener) { mListener->TT_SetAudioTimeFormat(mAudioTime->GetBuiltinName(evt.GetInt())); } // During initialization, the desired size will have already been set at this point // and the "best" size" would override it, so we simply send a size event to force // the content to fit inside the toolbar. if (mSettingInitialSize) { mSettingInitialSize = false; SendSizeEvent(); } // Otherwise we want the toolbar to resize to fit around the content else { // Fit() while retaining height SetSize(GetBestSize().x, GetSize().y); } // Go set the new size limits SetResizingLimits(); // Inform others the toobar has changed Updated(); } void TimeToolBar::OnSize(wxSizeEvent &evt) { evt.Skip(); // Can fire before we're ready if (!mAudioTime) { return; } // Make sure everything is where it's supposed to be Layout(); // Get the sizer's size and remove any borders the time control might have. wxSize sizerBR = GetSizer()->GetSize() - (mAudioTime->GetSize() - mAudioTime->GetClientSize()); // Get the content size of the time control. This can be different than the // control itself due to borders and sizer enduced changes. wxSize timeBR = mAudioTime->GetDimensions(); // Get the current digit box height int h = mAudioTime->GetDigitSize().y; // Increase current size to find the best fit within the new size if (sizerBR.x >= timeBR.x && sizerBR.y >= timeBR.y) { do { h++; timeBR = ComputeSizing(h); } while (h < maxDigitH && sizerBR.x >= timeBR.x && sizerBR.y >= timeBR.y); h--; } // In all other cases, we need to decrease current size to fit within new size else if (sizerBR.x < timeBR.x || sizerBR.y < timeBR.y) { do { h--; timeBR = ComputeSizing(h); } while (h >= minDigitH && (sizerBR.x < timeBR.x || sizerBR.y < timeBR.y)); } if (h != mAudioTime->GetDigitSize().y) { mAudioTime->SetDigitSize(h * mDigitRatio, h); } // Redraw the display immediately to smooth out resizing Update(); } void TimeToolBar::OnIdle(wxIdleEvent &evt) { evt.Skip(); double audioTime; auto &projectAudioIO = ProjectAudioIO::Get(mProject); if (projectAudioIO.IsAudioActive()) { auto gAudioIO = AudioIO::Get(); audioTime = gAudioIO->GetStreamTime(); } else { const auto &playRegion = ViewInfo::Get(mProject).playRegion; audioTime = playRegion.GetStart(); } mAudioTime->SetValue(wxMax(0.0, audioTime)); } static RegisteredToolbarFactory factory { TimeBarID, []( AudacityProject &project ) { return ToolBar::Holder{ safenew TimeToolBar{ project } }; } }; namespace { AttachedToolBarMenuItem sAttachment { TimeBarID, wxT("ShowTimeTB"), /* i18n-hint: Clicking this menu item shows the toolbar for viewing actual time of the cursor */ XXO("&Time Toolbar"), { Registry::OrderingHint::After, "ShowSelectionTB" } }; }