400 lines
11 KiB
C++
400 lines
11 KiB
C++
/**********************************************************************
|
|
|
|
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 <wx/wxprec.h>
|
|
|
|
#include <wx/setup.h> // for wxUSE_* macros
|
|
|
|
#ifndef WX_PRECOMP
|
|
#include <wx/intl.h>
|
|
#include <wx/sizer.h>
|
|
#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<TimeToolBar*>(toolManager.GetToolBar(TimeBarID));
|
|
}
|
|
|
|
const TimeToolBar &TimeToolBar::Get(const AudacityProject &project)
|
|
{
|
|
return Get(const_cast<AudacityProject&>(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"
|
|
}
|
|
};
|
|
}
|
|
|