diff --git a/src/LabelDialog.cpp b/src/LabelDialog.cpp index d623a6e73..fcbff4b84 100644 --- a/src/LabelDialog.cpp +++ b/src/LabelDialog.cpp @@ -36,7 +36,7 @@ #include "Project.h" #include "Track.h" #include "ViewInfo.h" -#include "widgets/TimeTextCtrl.h" +#include "widgets/NumericTextCtrl.h" #include "FileDialog.h" @@ -226,7 +226,7 @@ bool LabelDialog::TransferDataToWindow() int i; // Set the editor parameters. Do this each time since they may change - // due to new tracks and change in TimeTextCtrl format. Rate won't + // due to new tracks and change in NumericTextCtrl format. Rate won't // change but might as well leave it here. mChoiceEditor->SetChoices(mTrackNames); mTimeEditor->SetFormat(mFormat); diff --git a/src/Menus.cpp b/src/Menus.cpp index 5add99d10..431cf1e06 100644 --- a/src/Menus.cpp +++ b/src/Menus.cpp @@ -67,7 +67,6 @@ simplifies construction of menu items. #include "export/Export.h" #include "export/ExportMultiple.h" #include "prefs/PrefsDialog.h" -#include "widgets/TimeTextCtrl.h" #include "ShuttleGui.h" #include "HistoryWindow.h" #include "LyricsWindow.h" @@ -714,8 +713,12 @@ void AudacityProject::CreateMenusAndCommands() c->AddCheck(wxT("ShowPlayMeterTB"), _("&Playback Meter Toolbar"), FN(OnShowPlayMeterToolBar), 0, AlwaysEnabledFlag, AlwaysEnabledFlag); /* i18n-hint: Clicking this menu item shows the toolbar with the mixer*/ c->AddCheck(wxT("ShowMixerTB"), _("Mi&xer Toolbar"), FN(OnShowMixerToolBar), 0, AlwaysEnabledFlag, AlwaysEnabledFlag); - /* i18n-hint: Clicking this menu item shows the toolbar for selecting audio*/ + /* i18n-hint: Clicking this menu item shows the toolbar for selecting a time range of audio*/ c->AddCheck(wxT("ShowSelectionTB"), _("&Selection Toolbar"), FN(OnShowSelectionToolBar), 0, AlwaysEnabledFlag, AlwaysEnabledFlag); +#ifdef EXPERIMENTAL_SPECTRAL_EDITING + /* i18n-hint: Clicking this menu item shows the toolbar for selecting a frequency range of audio*/ + c->AddCheck(wxT("ShowSpectralSelectionTB"), _("&Spectral Selection Toolbar"), FN(OnShowSpectralSelectionToolBar), 0, AlwaysEnabledFlag, AlwaysEnabledFlag); +#endif /* i18n-hint: Clicking this menu item shows a toolbar that has some tools in it*/ c->AddCheck(wxT("ShowToolsTB"), _("T&ools Toolbar"), FN(OnShowToolsToolBar), 0, AlwaysEnabledFlag, AlwaysEnabledFlag); /* i18n-hint: Clicking this menu item shows the toolbar for transcription (currently just vary play speed)*/ @@ -1824,8 +1827,6 @@ void AudacityProject::ModifyToolbarMenus() return; } - mCommandManager.Check(wxT("ShowTransportTB"), - mToolManager->IsVisible(TransportBarID)); mCommandManager.Check(wxT("ShowDeviceTB"), mToolManager->IsVisible(DeviceBarID)); mCommandManager.Check(wxT("ShowEditTB"), @@ -1840,10 +1841,16 @@ void AudacityProject::ModifyToolbarMenus() mToolManager->IsVisible(MixerBarID)); mCommandManager.Check(wxT("ShowSelectionTB"), mToolManager->IsVisible(SelectionBarID)); +#ifdef EXPERIMENTAL_SPECTRAL_EDITING + mCommandManager.Check(wxT("ShowSpectralSelectionTB"), + mToolManager->IsVisible(SpectralSelectionBarID)); +#endif mCommandManager.Check(wxT("ShowToolsTB"), mToolManager->IsVisible(ToolsBarID)); mCommandManager.Check(wxT("ShowTranscriptionTB"), mToolManager->IsVisible(TranscriptionBarID)); + mCommandManager.Check(wxT("ShowTransportTB"), + mToolManager->IsVisible(TransportBarID)); // Now, go through each toolbar, and call EnableDisableButtons() for (int i = 0; i < ToolBarCount; i++) { @@ -4909,6 +4916,14 @@ void AudacityProject::OnShowSelectionToolBar() ModifyToolbarMenus(); } +#ifdef EXPERIMENTAL_SPECTRAL_EDITING +void AudacityProject::OnShowSpectralSelectionToolBar() +{ + mToolManager->ShowHide( SpectralSelectionBarID ); + ModifyToolbarMenus(); +} +#endif + void AudacityProject::OnShowToolsToolBar() { mToolManager->ShowHide( ToolsBarID ); diff --git a/src/Menus.h b/src/Menus.h index 3dc4d0b00..06606be06 100644 --- a/src/Menus.h +++ b/src/Menus.h @@ -282,6 +282,9 @@ void OnShowRecordMeterToolBar(); void OnShowPlayMeterToolBar(); void OnShowMixerToolBar(); void OnShowSelectionToolBar(); +#ifdef EXPERIMENTAL_SPECTRAL_EDITING +void OnShowSpectralSelectionToolBar(); +#endif void OnShowToolsToolBar(); void OnShowTranscriptionToolBar(); void OnResetToolBars(); diff --git a/src/Project.cpp b/src/Project.cpp index 6a07597e7..42ad28754 100644 --- a/src/Project.cpp +++ b/src/Project.cpp @@ -148,6 +148,7 @@ scroll information. It also has some status flags. #include "toolbars/MeterToolBar.h" #include "toolbars/MixerToolBar.h" #include "toolbars/SelectionBar.h" +#include "toolbars/SpectralSelectionBar.h" #include "toolbars/ToolsToolBar.h" #include "toolbars/TranscriptionToolBar.h" @@ -845,6 +846,9 @@ AudacityProject::AudacityProject(wxWindow * parent, wxWindowID id, // mToolManager = new ToolManager( this ); GetSelectionBar()->SetListener(this); +#ifdef EXPERIMENTAL_SPECTRAL_EDITING + GetSpectralSelectionBar()->SetListener(this); +#endif mToolManager->LayoutToolBars(); // Fix the sliders on the mixer toolbar so that the tip windows @@ -1224,6 +1228,83 @@ void AudacityProject::AS_SetSelectionFormat(const wxString & format) gPrefs->Flush(); } +double AudacityProject::SSBL_GetRate() const +{ + return mRate; +} + +const wxString & AudacityProject::SSBL_GetFrequencySelectionFormatName() +{ + return GetFrequencySelectionFormatName(); +} + +void AudacityProject::SSBL_SetFrequencySelectionFormatName(const wxString & formatName) +{ + mFrequencySelectionFormatName = formatName; + + gPrefs->Write(wxT("/FrequencySelectionFormatName"), mFrequencySelectionFormatName); + gPrefs->Flush(); +} + +const wxString & AudacityProject::SSBL_GetLogFrequencySelectionFormatName() +{ + return GetLogFrequencySelectionFormatName(); +} + +void AudacityProject::SSBL_SetLogFrequencySelectionFormatName(const wxString & formatName) +{ + mLogFrequencySelectionFormatName = formatName; + + gPrefs->Write(wxT("/LogFrequencySelectionFormatName"), mLogFrequencySelectionFormatName); + gPrefs->Flush(); +} + +void AudacityProject::SSBL_ModifySpectralSelection(double &bottom, double &top, bool done) +{ +#ifdef EXPERIMENTAL_SPECTRAL_EDITING + double nyq = mRate / 2.0; + bottom = std::max(1.0, std::min(nyq, bottom)); + top = std::max(0.0, std::min(nyq, top)); + mViewInfo.selectedRegion.setFrequencies(bottom, top); + mTrackPanel->Refresh(false); + if (done) { + ModifyState(false); + } +#else + bottom; top; done; +#endif +} + +const wxString & AudacityProject::GetFrequencySelectionFormatName() const +{ + return mFrequencySelectionFormatName; +} + +void AudacityProject::SetFrequencySelectionFormatName(const wxString & formatName) +{ + SSBL_SetFrequencySelectionFormatName(formatName); +#ifdef EXPERIMENTAL_SPECTRAL_EDITING + if (GetSpectralSelectionBar()) { + GetSpectralSelectionBar()->SetFrequencySelectionFormatName(formatName); + } +#endif +} + +const wxString & AudacityProject::GetLogFrequencySelectionFormatName() const +{ + return mLogFrequencySelectionFormatName; +} + +void AudacityProject::SetLogFrequencySelectionFormatName(const wxString & formatName) +{ + SSBL_SetLogFrequencySelectionFormatName(formatName); +#ifdef EXPERIMENTAL_SPECTRAL_EDITING + if (GetSpectralSelectionBar()) { + GetSpectralSelectionBar()->SetLogFrequencySelectionFormatName(formatName); + } +#endif +} + void AudacityProject::SetSelectionFormat(const wxString & format) { AS_SetSelectionFormat(format); @@ -1232,7 +1313,7 @@ void AudacityProject::SetSelectionFormat(const wxString & format) } } -const wxString & AudacityProject::GetSelectionFormat() +const wxString & AudacityProject::GetSelectionFormat() const { return mSelectionFormat; } @@ -4066,11 +4147,21 @@ MixerToolBar *AudacityProject::GetMixerToolBar() SelectionBar *AudacityProject::GetSelectionBar() { return (SelectionBar *) - (mToolManager ? - mToolManager->GetToolBar(SelectionBarID) : - NULL); + (mToolManager ? + mToolManager->GetToolBar(SelectionBarID) : + NULL); } +#ifdef EXPERIMENTAL_SPECTRAL_EDITING +SpectralSelectionBar *AudacityProject::GetSpectralSelectionBar() +{ + return static_cast( + (mToolManager ? + mToolManager->GetToolBar(SpectralSelectionBarID) : + NULL)); +} +#endif + ToolsToolBar *AudacityProject::GetToolsToolBar() { return (ToolsToolBar *) @@ -4388,6 +4479,10 @@ void AudacityProject::TP_DisplaySelection() GetSelectionBar()->SetTimes(mViewInfo.selectedRegion.t0(), mViewInfo.selectedRegion.t1(), audioTime); +#ifdef EXPERIMENTAL_SPECTRAL_EDITING + GetSpectralSelectionBar()->SetFrequencies + (mViewInfo.selectedRegion.f0(), mViewInfo.selectedRegion.f1()); +#endif if (!gAudioIO->IsBusy() && !mLockPlayRegion) mRuler->SetPlayRegion(mViewInfo.selectedRegion.t0(), diff --git a/src/Project.h b/src/Project.h index 518953914..75c70acd6 100644 --- a/src/Project.h +++ b/src/Project.h @@ -19,6 +19,7 @@ #define __AUDACITY_PROJECT__ #include "Audacity.h" +#include "Experimental.h" #include "DirManager.h" #include "UndoManager.h" @@ -29,6 +30,7 @@ #include "effects/EffectManager.h" #include "xml/XMLTagHandler.h" #include "toolbars/SelectionBarListener.h" +#include "toolbars/SpectralSelectionBarListener.h" #include #include @@ -66,6 +68,7 @@ class EditToolBar; class MeterToolBar; class MixerToolBar; class SelectionBar; +class SpectralSelectionBar; class Toolbar; class ToolManager; class ToolsToolBar; @@ -122,6 +125,7 @@ class ImportXMLTagHandler : public XMLTagHandler class AUDACITY_DLL_API AudacityProject: public wxFrame, public TrackPanelListener, public SelectionBarListener, + public SpectralSelectionBarListener, public XMLTagHandler, public AudioIOListener { @@ -313,7 +317,15 @@ class AUDACITY_DLL_API AudacityProject: public wxFrame, // Selection Format void SetSelectionFormat(const wxString & format); - const wxString & GetSelectionFormat(); + const wxString & GetSelectionFormat() const; + + // Spectral Selection Formats + + void SetFrequencySelectionFormatName(const wxString & format); + const wxString & GetFrequencySelectionFormatName() const; + + void SetLogFrequencySelectionFormatName(const wxString & format); + const wxString & GetLogFrequencySelectionFormatName() const; // Scrollbars @@ -362,6 +374,9 @@ class AUDACITY_DLL_API AudacityProject: public wxFrame, MeterToolBar *GetMeterToolBar(); MixerToolBar *GetMixerToolBar(); SelectionBar *GetSelectionBar(); +#ifdef EXPERIMENTAL_SPECTRAL_EDITING + SpectralSelectionBar *GetSpectralSelectionBar(); +#endif ToolsToolBar *GetToolsToolBar(); TranscriptionToolBar *GetTranscriptionToolBar(); @@ -378,6 +393,18 @@ class AUDACITY_DLL_API AudacityProject: public wxFrame, virtual void AS_SetSelectionFormat(const wxString & format); virtual void AS_ModifySelection(double &start, double &end, bool done); + // SpectralSelectionBarListener callback methods + + virtual double SSBL_GetRate() const; + + virtual const wxString & SSBL_GetFrequencySelectionFormatName(); + virtual void SSBL_SetFrequencySelectionFormatName(const wxString & formatName); + + virtual const wxString & SSBL_GetLogFrequencySelectionFormatName(); + virtual void SSBL_SetLogFrequencySelectionFormatName(const wxString & formatName); + + virtual void SSBL_ModifySpectralSelection(double &bottom, double &top, bool done); + void SetStateTo(unsigned int n); // XMLTagHandler callback methods @@ -453,6 +480,8 @@ class AUDACITY_DLL_API AudacityProject: public wxFrame, int mSnapTo; wxString mSelectionFormat; + wxString mFrequencySelectionFormatName; + wxString mLogFrequencySelectionFormatName; TrackList *mLastSavedTracks; diff --git a/src/Snap.cpp b/src/Snap.cpp index 1cfd26df9..c28f27d50 100644 --- a/src/Snap.cpp +++ b/src/Snap.cpp @@ -16,7 +16,7 @@ #include "Snap.h" #include "TrackPanel.h" #include "WaveTrack.h" -#include "widgets/TimeTextCtrl.h" +#include "widgets/NumericTextCtrl.h" // Change this to "true" to snap to nearest and "false" to snap to previous // As of 2013/10/23, defaulting to "true" until a decision is made on @@ -30,6 +30,7 @@ static int CompareSnapPoints(SnapPoint *s1, SnapPoint *s2) SnapManager::SnapManager(TrackList *tracks, TrackClipArray *exclusions, double zoom, int pixelTolerance, bool noTimeSnap) + : mConverter(NumericConverter::TIME) { int i; @@ -110,10 +111,10 @@ SnapManager::SnapManager(TrackList *tracks, TrackClipArray *exclusions, void SnapManager::CondListAdd(double t, Track *tr) { if (mSnapToTime) { - mConverter.SetTimeValue(t); + mConverter.SetValue(t); } - if (!mSnapToTime || mConverter.GetTimeValue() == t) { + if (!mSnapToTime || mConverter.GetValue() == t) { mSnapPoints->Add(new SnapPoint(t, tr)); } } @@ -259,7 +260,7 @@ bool SnapManager::Snap(Track *currentTrack, // Snap time to the grid mConverter.ValueToControls(t, GetActiveProject()->GetSnapTo() == SNAP_NEAREST); mConverter.ControlsToValue(); - *out_t = mConverter.GetTimeValue(); + *out_t = mConverter.GetValue(); *snappedTime = true; } } diff --git a/src/Snap.h b/src/Snap.h index e33919c16..a4d32ca7c 100644 --- a/src/Snap.h +++ b/src/Snap.h @@ -19,7 +19,7 @@ #include #include "Track.h" -#include "widgets/TimeTextCtrl.h" +#include "widgets/NumericTextCtrl.h" class TrackClipArray; @@ -80,7 +80,7 @@ class SnapManager { SnapPointArray *mSnapPoints; // Info for snap-to-time - TimeConverter mConverter; + NumericConverter mConverter; bool mSnapToTime; }; diff --git a/src/TimeDialog.cpp b/src/TimeDialog.cpp index 128946025..9f2907af8 100644 --- a/src/TimeDialog.cpp +++ b/src/TimeDialog.cpp @@ -21,6 +21,7 @@ #include #include +#include "widgets/NumericTextCtrl.h" #include "ShuttleGui.h" #include "TimeDialog.h" @@ -53,7 +54,8 @@ void TimeDialog::PopulateOrExchange(ShuttleGui &S) S.StartStatic(mPrompt, true); { mTimeCtrl = new - TimeTextCtrl(this, + NumericTextCtrl( + NumericConverter::TIME, this, wxID_ANY, mFormat, mTime, @@ -82,7 +84,7 @@ bool TimeDialog::TransferDataToWindow() { mTimeCtrl->SetFormatString(mTimeCtrl->GetBuiltinFormat(mFormat)); mTimeCtrl->SetSampleRate(mRate); - mTimeCtrl->SetTimeValue(mTime); + mTimeCtrl->SetValue(mTime); mTimeCtrl->SetFocus(); return true; @@ -90,7 +92,7 @@ bool TimeDialog::TransferDataToWindow() bool TimeDialog::TransferDataFromWindow() { - mTime = mTimeCtrl->GetTimeValue(); + mTime = mTimeCtrl->GetValue(); return true; } diff --git a/src/TimeDialog.h b/src/TimeDialog.h index 7dc875e65..99f6cfb09 100644 --- a/src/TimeDialog.h +++ b/src/TimeDialog.h @@ -16,8 +16,7 @@ #include #include -#include "widgets/TimeTextCtrl.h" - +class NumericTextCtrl; class ShuttleGui; class TimeDialog:public wxDialog @@ -51,7 +50,7 @@ class TimeDialog:public wxDialog double mRate; double mTime; - TimeTextCtrl *mTimeCtrl; + NumericTextCtrl *mTimeCtrl; DECLARE_EVENT_TABLE(); }; diff --git a/src/TimerRecordDialog.cpp b/src/TimerRecordDialog.cpp index 0e9edb814..fe7bd4ab3 100644 --- a/src/TimerRecordDialog.cpp +++ b/src/TimerRecordDialog.cpp @@ -33,6 +33,7 @@ #include "Project.h" #include "Internat.h" #include "Prefs.h" +#include "widgets/NumericTextCtrl.h" #define TIMER_ID 7000 @@ -86,7 +87,8 @@ TimerRecordDialog::TimerRecordDialog(wxWindow* parent) ShuttleGui S(this, eIsCreating); this->PopulateOrExchange(S); - // Set initial focus to "1" of "01h" in Duration TimeTextCtrl, instead of OK button (default). + // Set initial focus to "1" of "01h" in Duration NumericTextCtrl, + // instead of OK button (default). m_pTimeTextCtrl_Duration->SetFocus(); m_pTimeTextCtrl_Duration->SetFieldFocus(3); @@ -104,7 +106,7 @@ void TimerRecordDialog::OnTimer(wxTimerEvent& WXUNUSED(event)) if (m_DateTime_Start < dateTime_UNow) { m_DateTime_Start = dateTime_UNow; m_pDatePickerCtrl_Start->SetValue(m_DateTime_Start); - m_pTimeTextCtrl_Start->SetTimeValue(wxDateTime_to_AudacityTime(m_DateTime_Start)); + m_pTimeTextCtrl_Start->SetValue(wxDateTime_to_AudacityTime(m_DateTime_Start)); this->UpdateEnd(); // Keep Duration constant and update End for changed Start. } } @@ -112,7 +114,7 @@ void TimerRecordDialog::OnTimer(wxTimerEvent& WXUNUSED(event)) void TimerRecordDialog::OnDatePicker_Start(wxDateEvent& WXUNUSED(event)) { m_DateTime_Start = m_pDatePickerCtrl_Start->GetValue(); - double dTime = m_pTimeTextCtrl_Start->GetTimeValue(); + double dTime = m_pTimeTextCtrl_Start->GetValue(); long hr = (long)(dTime / 3600.0); long min = (long)((dTime - (hr * 3600.0)) / 60.0); long sec = (long)(dTime - (hr * 3600.0) - (min * 60.0)); @@ -133,16 +135,18 @@ void TimerRecordDialog::OnDatePicker_Start(wxDateEvent& WXUNUSED(event)) void TimerRecordDialog::OnTimeText_Start(wxCommandEvent& WXUNUSED(event)) { - //v TimeTextCtrl doesn't implement upper ranges, i.e., if I tell it "024 h 060 m 060 s", then - // user increments the hours past 23, it rolls over to 0 (although if you increment below 0, it stays at 0). + //v NumericTextCtrl doesn't implement upper ranges, i.e., + // if I tell it "024 h 060 m 060 s", then + // user increments the hours past 23, it rolls over to 0 + // (although if you increment below 0, it stays at 0). // So instead, set the max to 99 and just catch hours > 24 and fix the ctrls. - double dTime = m_pTimeTextCtrl_Start->GetTimeValue(); + double dTime = m_pTimeTextCtrl_Start->GetValue(); long days = (long)(dTime / (24.0 * 3600.0)); if (days > 0) { dTime -= (double)days * 24.0 * 3600.0; m_DateTime_Start += wxTimeSpan::Days(days); m_pDatePickerCtrl_Start->SetValue(m_DateTime_Start); - m_pTimeTextCtrl_Start->SetTimeValue(dTime); + m_pTimeTextCtrl_Start->SetValue(dTime); } wxDateEvent dummyDateEvent; @@ -152,7 +156,7 @@ void TimerRecordDialog::OnTimeText_Start(wxCommandEvent& WXUNUSED(event)) void TimerRecordDialog::OnDatePicker_End(wxDateEvent& WXUNUSED(event)) { m_DateTime_End = m_pDatePickerCtrl_End->GetValue(); - double dTime = m_pTimeTextCtrl_End->GetTimeValue(); + double dTime = m_pTimeTextCtrl_End->GetValue(); long hr = (long)(dTime / 3600.0); long min = (long)((dTime - (hr * 3600.0)) / 60.0); long sec = (long)(dTime - (hr * 3600.0) - (min * 60.0)); @@ -165,7 +169,7 @@ void TimerRecordDialog::OnDatePicker_End(wxDateEvent& WXUNUSED(event)) if (m_DateTime_End < m_DateTime_Start) { m_DateTime_End = m_DateTime_Start; m_pDatePickerCtrl_End->SetValue(m_DateTime_End); - m_pTimeTextCtrl_End->SetTimeValue(wxDateTime_to_AudacityTime(m_DateTime_End)); + m_pTimeTextCtrl_End->SetValue(wxDateTime_to_AudacityTime(m_DateTime_End)); } this->UpdateDuration(); // Keep Start constant and update Duration for changed End. @@ -173,16 +177,18 @@ void TimerRecordDialog::OnDatePicker_End(wxDateEvent& WXUNUSED(event)) void TimerRecordDialog::OnTimeText_End(wxCommandEvent& WXUNUSED(event)) { - //v TimeTextCtrl doesn't implement upper ranges, i.e., if I tell it "024 h 060 m 060 s", then - // user increments the hours past 23, it rolls over to 0 (although if you increment below 0, it stays at 0). + //v NumericTextCtrl doesn't implement upper ranges, i.e., + // if I tell it "024 h 060 m 060 s", then + // user increments the hours past 23, it rolls over to 0 + // (although if you increment below 0, it stays at 0). // So instead, set the max to 99 and just catch hours > 24 and fix the ctrls. - double dTime = m_pTimeTextCtrl_End->GetTimeValue(); + double dTime = m_pTimeTextCtrl_End->GetValue(); long days = (long)(dTime / (24.0 * 3600.0)); if (days > 0) { dTime -= (double)days * 24.0 * 3600.0; m_DateTime_End += wxTimeSpan::Days(days); m_pDatePickerCtrl_End->SetValue(m_DateTime_End); - m_pTimeTextCtrl_End->SetTimeValue(dTime); + m_pTimeTextCtrl_End->SetValue(dTime); } wxDateEvent dummyDateEvent; @@ -191,7 +197,7 @@ void TimerRecordDialog::OnTimeText_End(wxCommandEvent& WXUNUSED(event)) void TimerRecordDialog::OnTimeText_Duration(wxCommandEvent& WXUNUSED(event)) { - double dTime = m_pTimeTextCtrl_Duration->GetTimeValue(); + double dTime = m_pTimeTextCtrl_Duration->GetValue(); long hr = (long)(dTime / 3600.0); long min = (long)((dTime - (hr * 3600.0)) / 60.0); long sec = (long)(dTime - (hr * 3600.0) - (min * 60.0)); @@ -342,10 +348,12 @@ void TimerRecordDialog::PopulateOrExchange(ShuttleGui& S) m_pDatePickerCtrl_Start->SetRange(wxDateTime::Today(), wxInvalidDateTime); // No backdating. S.AddWindow(m_pDatePickerCtrl_Start); - m_pTimeTextCtrl_Start = new TimeTextCtrl(this, ID_TIMETEXT_START); + m_pTimeTextCtrl_Start = new NumericTextCtrl( + NumericConverter::TIME, this, ID_TIMETEXT_START); m_pTimeTextCtrl_Start->SetName(_("Start Time")); m_pTimeTextCtrl_Start->SetFormatString(strFormat); - m_pTimeTextCtrl_Start->SetTimeValue(wxDateTime_to_AudacityTime(m_DateTime_Start)); + m_pTimeTextCtrl_Start-> + SetValue(wxDateTime_to_AudacityTime(m_DateTime_Start)); S.AddWindow(m_pTimeTextCtrl_Start); m_pTimeTextCtrl_Start->EnableMenu(false); } @@ -362,10 +370,11 @@ void TimerRecordDialog::PopulateOrExchange(ShuttleGui& S) m_pDatePickerCtrl_End->SetName(_("End Date")); S.AddWindow(m_pDatePickerCtrl_End); - m_pTimeTextCtrl_End = new TimeTextCtrl(this, ID_TIMETEXT_END); + m_pTimeTextCtrl_End = new NumericTextCtrl( + NumericConverter::TIME, this, ID_TIMETEXT_END); m_pTimeTextCtrl_End->SetName(_("End Time")); m_pTimeTextCtrl_End->SetFormatString(strFormat); - m_pTimeTextCtrl_End->SetTimeValue(wxDateTime_to_AudacityTime(m_DateTime_End)); + m_pTimeTextCtrl_End->SetValue(wxDateTime_to_AudacityTime(m_DateTime_End)); S.AddWindow(m_pTimeTextCtrl_End); m_pTimeTextCtrl_End->EnableMenu(false); } @@ -382,10 +391,12 @@ void TimerRecordDialog::PopulateOrExchange(ShuttleGui& S) * seconds. */ wxString strFormat1 = _("099 days 024 h 060 m 060 s"); - m_pTimeTextCtrl_Duration = new TimeTextCtrl(this, ID_TIMETEXT_DURATION); + m_pTimeTextCtrl_Duration = new NumericTextCtrl( + NumericConverter::TIME, this, ID_TIMETEXT_DURATION); m_pTimeTextCtrl_Duration->SetName(_("Duration")); m_pTimeTextCtrl_Duration->SetFormatString(strFormat1); - m_pTimeTextCtrl_Duration->SetTimeValue(m_TimeSpan_Duration.GetSeconds().ToDouble()); + m_pTimeTextCtrl_Duration-> + SetValue(m_TimeSpan_Duration.GetSeconds().ToDouble()); S.AddWindow(m_pTimeTextCtrl_Duration); m_pTimeTextCtrl_Duration->EnableMenu(false); } @@ -409,7 +420,7 @@ bool TimerRecordDialog::TransferDataFromWindow() long sec; m_DateTime_Start = m_pDatePickerCtrl_Start->GetValue(); - dTime = m_pTimeTextCtrl_Start->GetTimeValue(); + dTime = m_pTimeTextCtrl_Start->GetValue(); hr = (long)(dTime / 3600.0); min = (long)((dTime - (hr * 3600.0)) / 60.0); sec = (long)(dTime - (hr * 3600.0) - (min * 60.0)); @@ -418,7 +429,7 @@ bool TimerRecordDialog::TransferDataFromWindow() m_DateTime_Start.SetSecond(sec); m_DateTime_End = m_pDatePickerCtrl_End->GetValue(); - dTime = m_pTimeTextCtrl_End->GetTimeValue(); + dTime = m_pTimeTextCtrl_End->GetValue(); hr = (long)(dTime / 3600.0); min = (long)((dTime - (hr * 3600.0)) / 60.0); sec = (long)(dTime - (hr * 3600.0) - (min * 60.0)); @@ -435,7 +446,7 @@ bool TimerRecordDialog::TransferDataFromWindow() void TimerRecordDialog::UpdateDuration() { m_TimeSpan_Duration = m_DateTime_End - m_DateTime_Start; - m_pTimeTextCtrl_Duration->SetTimeValue(m_TimeSpan_Duration.GetSeconds().ToDouble()); + m_pTimeTextCtrl_Duration->SetValue(m_TimeSpan_Duration.GetSeconds().ToDouble()); } // Update m_DateTime_End and ctrls based on m_DateTime_Start and m_TimeSpan_Duration. @@ -446,7 +457,7 @@ void TimerRecordDialog::UpdateEnd() m_pDatePickerCtrl_End->SetValue(m_DateTime_End); m_pDatePickerCtrl_End->SetRange(m_DateTime_Start, wxInvalidDateTime); // No backdating. m_pDatePickerCtrl_End->Refresh(); - m_pTimeTextCtrl_End->SetTimeValue(wxDateTime_to_AudacityTime(m_DateTime_End)); + m_pTimeTextCtrl_End->SetValue(wxDateTime_to_AudacityTime(m_DateTime_End)); } int TimerRecordDialog::WaitForStart() diff --git a/src/TimerRecordDialog.h b/src/TimerRecordDialog.h index 9b0dc2a23..d3f6868d2 100644 --- a/src/TimerRecordDialog.h +++ b/src/TimerRecordDialog.h @@ -20,10 +20,10 @@ #include #include -#include "widgets/TimeTextCtrl.h" - #include "ShuttleGui.h" +class NumericTextCtrl; + class TimerRecordDialog : public wxDialog { public: @@ -59,12 +59,12 @@ private: // controls wxDatePickerCtrl* m_pDatePickerCtrl_Start; - TimeTextCtrl* m_pTimeTextCtrl_Start; + NumericTextCtrl* m_pTimeTextCtrl_Start; wxDatePickerCtrl* m_pDatePickerCtrl_End; - TimeTextCtrl* m_pTimeTextCtrl_End; + NumericTextCtrl* m_pTimeTextCtrl_End; - TimeTextCtrl* m_pTimeTextCtrl_Duration; + NumericTextCtrl* m_pTimeTextCtrl_Duration; wxTimer m_timer; diff --git a/src/TrackPanel.cpp b/src/TrackPanel.cpp index 520004929..3fc3da207 100644 --- a/src/TrackPanel.cpp +++ b/src/TrackPanel.cpp @@ -220,7 +220,7 @@ is time to refresh some aspect of the screen. #include "widgets/ASlider.h" #include "widgets/Ruler.h" -#include "widgets/TimeTextCtrl.h" +#include "widgets/NumericTextCtrl.h" #include @@ -462,7 +462,7 @@ TrackPanel::TrackPanel(wxWindow * parent, wxWindowID id, mTrackArtist(NULL), mBacking(NULL), mRefreshBacking(false), - mConverter(), + mConverter(NumericConverter::TIME), mAutoScrolling(false), mVertScrollRemainder(0), vrulerSize(36,0) @@ -7527,23 +7527,23 @@ void TrackPanel::OnCursorRight( bool shift, bool ctrl, bool keyup ) // negative to move backward. double TrackPanel::GridMove(double t, int minPix) { - TimeTextCtrl ttc(this, wxID_ANY, wxT(""), 0.0, GetProject()->GetRate()); + NumericTextCtrl ttc(NumericConverter::TIME, this, wxID_ANY, wxT(""), 0.0, GetProject()->GetRate()); ttc.SetFormatName(GetProject()->GetSelectionFormat()); - ttc.SetTimeValue(t); + ttc.SetValue(t); // Try incrementing/decrementing the value; if we've moved far enough we're // done double result; minPix >= 0 ? ttc.Increment() : ttc.Decrement(); - result = ttc.GetTimeValue(); + result = ttc.GetValue(); if (fabs(result - t) * mViewInfo->zoom >= fabs((double)minPix)) { return result; } // Otherwise, move minPix pixels, then snap to the time. result = t + minPix / mViewInfo->zoom; - ttc.SetTimeValue(result); - result = ttc.GetTimeValue(); + ttc.SetValue(result); + result = ttc.GetValue(); return result; } diff --git a/src/TrackPanel.h b/src/TrackPanel.h index a447c98f7..99d6d8e21 100644 --- a/src/TrackPanel.h +++ b/src/TrackPanel.h @@ -24,7 +24,7 @@ #include "WaveClip.h" #include "WaveTrack.h" #include "UndoManager.h" //JKC: Included for PUSH_XXX definitions. -#include "widgets/TimeTextCtrl.h" +#include "widgets/NumericTextCtrl.h" class wxMenu; class wxRect; @@ -635,7 +635,7 @@ protected: wxInt64 mSnapRight; bool mSnapPreferRightEdge; - TimeConverter mConverter; + NumericConverter mConverter; Track * mDrawingTrack; // Keeps track of which track you are drawing on between events cf. HandleDraw() int mDrawingTrackTop; // Keeps track of the top position of the drawing track. diff --git a/src/effects/Contrast.cpp b/src/effects/Contrast.cpp index b42310038..5d045b6a3 100644 --- a/src/effects/Contrast.cpp +++ b/src/effects/Contrast.cpp @@ -22,6 +22,7 @@ #include "../FileNames.h" #include "../widgets/LinkingHtmlWindow.h" #include "../widgets/HelpSystem.h" +#include "../widgets/NumericTextCtrl.h" #include "FileDialog.h" #include @@ -72,13 +73,13 @@ void InitContrastDialog(wxWindow * parent) // Zero dialog boxes. Do we need to do this here? if( !gContrastDialog->bFGset ) { - gContrastDialog->mForegroundStartT->SetTimeValue(0.0); - gContrastDialog->mForegroundEndT->SetTimeValue(0.0); + gContrastDialog->mForegroundStartT->SetValue(0.0); + gContrastDialog->mForegroundEndT->SetValue(0.0); } if( !gContrastDialog->bBGset ) { - gContrastDialog->mBackgroundStartT->SetTimeValue(0.0); - gContrastDialog->mBackgroundEndT->SetTimeValue(0.0); + gContrastDialog->mBackgroundStartT->SetValue(0.0); + gContrastDialog->mBackgroundEndT->SetValue(0.0); } gContrastDialog->CentreOnParent(); @@ -259,7 +260,7 @@ ContrastDialog::ContrastDialog(wxWindow * parent, wxWindowID id, if (mForegroundStartT == NULL) { mForegroundStartT = new - TimeTextCtrl(this, + NumericTextCtrl(NumericConverter::TIME, this, ID_FOREGROUNDSTART_T, _("hh:mm:ss + hundredths"), 0.0, @@ -275,7 +276,7 @@ ContrastDialog::ContrastDialog(wxWindow * parent, wxWindowID id, if (mForegroundEndT == NULL) { mForegroundEndT = new - TimeTextCtrl(this, + NumericTextCtrl(NumericConverter::TIME, this, ID_FOREGROUNDEND_T, _("hh:mm:ss + hundredths"), 0.0, @@ -297,7 +298,7 @@ ContrastDialog::ContrastDialog(wxWindow * parent, wxWindowID id, if (mBackgroundStartT == NULL) { mBackgroundStartT = new - TimeTextCtrl(this, + NumericTextCtrl(NumericConverter::TIME, this, ID_BACKGROUNDSTART_T, _("hh:mm:ss + hundredths"), 0.0, @@ -313,7 +314,7 @@ ContrastDialog::ContrastDialog(wxWindow * parent, wxWindowID id, if (mBackgroundEndT == NULL) { mBackgroundEndT = new - TimeTextCtrl(this, + NumericTextCtrl(NumericConverter::TIME, this, ID_BACKGROUNDEND_T, _("hh:mm:ss + hundredths"), 0.0, @@ -375,8 +376,8 @@ ContrastDialog::~ContrastDialog() void ContrastDialog::OnGetForegroundDB( wxCommandEvent & WXUNUSED(event)) { - SetStartTime(mForegroundStartT->GetTimeValue()); - SetEndTime(mForegroundEndT->GetTimeValue()); + SetStartTime(mForegroundStartT->GetValue()); + SetEndTime(mForegroundEndT->GetValue()); foregrounddB = GetDB(); m_pButton_UseCurrentF->SetFocus(); results(); @@ -384,8 +385,8 @@ void ContrastDialog::OnGetForegroundDB( wxCommandEvent & WXUNUSED(event)) void ContrastDialog::OnGetBackgroundDB( wxCommandEvent & WXUNUSED(event)) { - SetStartTime(mBackgroundStartT->GetTimeValue()); - SetEndTime(mBackgroundEndT->GetTimeValue()); + SetStartTime(mBackgroundStartT->GetValue()); + SetEndTime(mBackgroundEndT->GetValue()); backgrounddB = GetDB(); m_pButton_UseCurrentB->SetFocus(); results(); @@ -410,8 +411,8 @@ void ContrastDialog::OnUseSelectionF(wxCommandEvent & event) Track *t = iter.First(); while (t) { if (t->GetSelected() && t->GetKind() == Track::Wave) { - mForegroundStartT->SetTimeValue(p->mViewInfo.selectedRegion.t0()); - mForegroundEndT->SetTimeValue(p->mViewInfo.selectedRegion.t1()); + mForegroundStartT->SetValue(p->mViewInfo.selectedRegion.t0()); + mForegroundEndT->SetValue(p->mViewInfo.selectedRegion.t1()); break; } t = iter.Next(); @@ -427,8 +428,8 @@ void ContrastDialog::OnUseSelectionB(wxCommandEvent & event) Track *t = iter.First(); while (t) { if (t->GetSelected() && t->GetKind() == Track::Wave) { - mBackgroundStartT->SetTimeValue(p->mViewInfo.selectedRegion.t0()); - mBackgroundEndT->SetTimeValue(p->mViewInfo.selectedRegion.t1()); + mBackgroundStartT->SetValue(p->mViewInfo.selectedRegion.t0()); + mBackgroundEndT->SetValue(p->mViewInfo.selectedRegion.t1()); break; } t = iter.Next(); @@ -521,12 +522,12 @@ void ContrastDialog::OnExport(wxCommandEvent & WXUNUSED(event)) f.AddLine(wxString::Format(_("Filename = %s."), project->GetFileName().c_str() )); f.AddLine(wxT("")); f.AddLine(_("Foreground")); - float t = (float)mForegroundStartT->GetTimeValue(); + float t = (float)mForegroundStartT->GetValue(); int h = (int)(t/3600); // there must be a standard function for this! int m = (int)((t - h*3600)/60); float s = t - h*3600.0 - m*60.0; f.AddLine(wxString::Format(_("Time started = %2d hour(s), %2d minute(s), %.2f seconds."), h, m, s )); - t = (float)mForegroundEndT->GetTimeValue(); + t = (float)mForegroundEndT->GetValue(); h = (int)(t/3600); m = (int)((t - h*3600)/60); s = t - h*3600.0 - m*60.0; @@ -540,12 +541,12 @@ void ContrastDialog::OnExport(wxCommandEvent & WXUNUSED(event)) f.AddLine(wxString::Format(_("Average RMS = dB."))); f.AddLine(wxT("")); f.AddLine(_("Background")); - t = (float)mBackgroundStartT->GetTimeValue(); + t = (float)mBackgroundStartT->GetValue(); h = (int)(t/3600); m = (int)((t - h*3600)/60); s = t - h*3600.0 - m*60.0; f.AddLine(wxString::Format(_("Time started = %2d hour(s), %2d minute(s), %.2f seconds."), h, m, s )); - t = (float)mBackgroundEndT->GetTimeValue(); + t = (float)mBackgroundEndT->GetValue(); h = (int)(t/3600); m = (int)((t - h*3600)/60); s = t - h*3600.0 - m*60.0; @@ -603,10 +604,10 @@ void ContrastDialog::OnReset(wxCommandEvent & event) bFGset = false; bBGset = false; - mForegroundStartT->SetTimeValue(0.0); - mForegroundEndT->SetTimeValue(0.0); - mBackgroundStartT->SetTimeValue(0.0); - mBackgroundEndT->SetTimeValue(0.0); + mForegroundStartT->SetValue(0.0); + mForegroundEndT->SetValue(0.0); + mBackgroundStartT->SetValue(0.0); + mBackgroundEndT->SetValue(0.0); wxCommandEvent dummyEvt; OnGetForegroundDB(event); diff --git a/src/effects/Contrast.h b/src/effects/Contrast.h index cbc65e551..845b0b7a7 100644 --- a/src/effects/Contrast.h +++ b/src/effects/Contrast.h @@ -9,8 +9,6 @@ #ifndef __AUDACITY_CONTRAST_DIALOG__ #define __AUDACITY_CONTRAST_DIALOG__ -#include "../widgets/TimeTextCtrl.h" - #include #include @@ -19,6 +17,7 @@ class wxSizer; class wxString; class Envelope; +class NumericTextCtrl; class WaveTrack; void InitContrastDialog(wxWindow * parent); @@ -48,10 +47,10 @@ public: wxButton * m_pButton_Reset; wxButton * m_pButton_Close; - TimeTextCtrl *mForegroundStartT; - TimeTextCtrl *mForegroundEndT; - TimeTextCtrl *mBackgroundStartT; - TimeTextCtrl *mBackgroundEndT; + NumericTextCtrl *mForegroundStartT; + NumericTextCtrl *mForegroundEndT; + NumericTextCtrl *mBackgroundStartT; + NumericTextCtrl *mBackgroundEndT; bool bFGset; bool bBGset; diff --git a/src/effects/DtmfGen.cpp b/src/effects/DtmfGen.cpp index 5605c534a..271faef05 100644 --- a/src/effects/DtmfGen.cpp +++ b/src/effects/DtmfGen.cpp @@ -27,6 +27,7 @@ #include "../Prefs.h" #include "../ShuttleGui.h" #include "../WaveTrack.h" +#include "../widgets/NumericTextCtrl.h" #include #include @@ -456,7 +457,7 @@ void DtmfDialog::PopulateOrExchange( ShuttleGui & S ) if (mDtmfDurationT == NULL) { mDtmfDurationT = new - TimeTextCtrl(this, + NumericTextCtrl(NumericConverter::TIME, this, ID_DTMF_DURATION_TEXT, /* use this instead of "seconds" because if a selection is passed to the * effect, I want it (dDuration) to be used as the duration, and with @@ -496,7 +497,7 @@ void DtmfDialog::PopulateOrExchange( ShuttleGui & S ) bool DtmfDialog::TransferDataToWindow() { mDtmfDutyS->SetValue((int)dDutyCycle); - mDtmfDurationT->SetTimeValue(dDuration); + mDtmfDurationT->SetValue(dDuration); mDtmfStringT->SetValue(dString); return true; @@ -523,7 +524,7 @@ void DtmfDialog::Recalculate(void) { double slot; dString = mDtmfStringT->GetValue(); - dDuration = mDtmfDurationT->GetTimeValue(); + dDuration = mDtmfDurationT->GetValue(); dNTones = wxStrlen(dString); dDutyCycle = TrapLong(mDtmfDutyS->GetValue(), DUTY_MIN, DUTY_MAX); diff --git a/src/effects/DtmfGen.h b/src/effects/DtmfGen.h index 9f3d57041..5eb5ce7f9 100644 --- a/src/effects/DtmfGen.h +++ b/src/effects/DtmfGen.h @@ -23,10 +23,11 @@ #include "../ShuttleGui.h" #include "../WaveTrack.h" -#include "../widgets/TimeTextCtrl.h" #include "Generator.h" +class NumericTextCtrl; + #define __UNINITIALIZED__ (-1) class EffectDtmf : public Generator { @@ -112,7 +113,7 @@ class DtmfDialog:public EffectDialog { EffectDtmf *mEffect; wxSlider *mDtmfDutyS; wxTextCtrl *mDtmfStringT; - TimeTextCtrl *mDtmfDurationT; + NumericTextCtrl *mDtmfDurationT; wxStaticText *mDtmfToneT; wxStaticText *mDtmfSilenceT; wxStaticText *mDtmfDutyT; diff --git a/src/effects/Noise.cpp b/src/effects/Noise.cpp index aa4d0751a..8cc10eefe 100644 --- a/src/effects/Noise.cpp +++ b/src/effects/Noise.cpp @@ -19,6 +19,7 @@ #include "../Prefs.h" #include "../ShuttleGui.h" #include "../WaveTrack.h" +#include "../widgets/NumericTextCtrl.h" #include @@ -197,7 +198,7 @@ void NoiseDialog::PopulateOrExchange( ShuttleGui & S ) if (mNoiseDurationT == NULL) { mNoiseDurationT = new - TimeTextCtrl(this, + NumericTextCtrl(NumericConverter::TIME, this, wxID_ANY, wxT(""), nDuration, @@ -223,7 +224,7 @@ bool NoiseDialog::TransferDataToWindow() EffectDialog::TransferDataToWindow(); // Must handle this ourselves since ShuttleGui doesn't know about it - mNoiseDurationT->SetTimeValue(nDuration); + mNoiseDurationT->SetValue(nDuration); return true; } @@ -235,7 +236,7 @@ bool NoiseDialog::TransferDataFromWindow() nAmplitude = TrapDouble(nAmplitude, AMP_MIN, AMP_MAX); // Must handle this ourselves since ShuttleGui doesn't know about it - nDuration = mNoiseDurationT->GetTimeValue(); + nDuration = mNoiseDurationT->GetValue(); return true; } diff --git a/src/effects/Noise.h b/src/effects/Noise.h index 0dfcf2f78..d6ec5c933 100644 --- a/src/effects/Noise.h +++ b/src/effects/Noise.h @@ -17,11 +17,11 @@ #include #include "Generator.h" -#include "../widgets/TimeTextCtrl.h" class wxString; class wxChoice; class wxTextCtrl; +class NumericTextCtrl; class ShuttleGui; #define __UNINITIALIZED__ (-1) @@ -114,7 +114,7 @@ class NoiseDialog:public EffectDialog { private: EffectNoise *mEffect; - TimeTextCtrl *mNoiseDurationT; + NumericTextCtrl *mNoiseDurationT; }; #endif diff --git a/src/effects/Repeat.cpp b/src/effects/Repeat.cpp index ea0c4455c..c448d4457 100644 --- a/src/effects/Repeat.cpp +++ b/src/effects/Repeat.cpp @@ -26,7 +26,7 @@ #include "../ShuttleGui.h" #include "../WaveTrack.h" #include "../LabelTrack.h" -#include "../widgets/TimeTextCtrl.h" +#include "../widgets/NumericTextCtrl.h" #include "../Project.h" #include @@ -261,7 +261,7 @@ void RepeatDialog::DisplayNewTime() wxString str; str = _("New selection length: "); - TimeTextCtrl tt(this, + NumericTextCtrl tt(NumericTextCtrl::TIME, this, wxID_ANY, wxT(""), selectionTimeSecs * (repeatCount + 1), @@ -270,7 +270,7 @@ void RepeatDialog::DisplayNewTime() wxDefaultSize, true); tt.SetFormatString(tt.GetBuiltinFormat(_("hh:mm:ss"))); - str += tt.GetTimeString(); + str += tt.GetString(); mTotalTime->SetLabel(str); mTotalTime->SetName(str); // fix for bug 577 (NVDA/Narrator screen readers do not read static text in dialogs) diff --git a/src/effects/Silence.h b/src/effects/Silence.h index cbeb1a133..72b1eff59 100644 --- a/src/effects/Silence.h +++ b/src/effects/Silence.h @@ -18,7 +18,6 @@ #include #include "Generator.h" -#include "../widgets/TimeTextCtrl.h" class wxSizer; class wxTextCtrl; diff --git a/src/effects/ToneGen.cpp b/src/effects/ToneGen.cpp index 9e02059b3..68ce8ba94 100644 --- a/src/effects/ToneGen.cpp +++ b/src/effects/ToneGen.cpp @@ -30,6 +30,7 @@ frequency changes smoothly during the tone. #include "../ShuttleGui.h" #include "../WaveTrack.h" #include "../Prefs.h" +#include "../widgets/NumericTextCtrl.h" #include #include @@ -272,8 +273,8 @@ void ToneGenDialog::PopulateOrExchangeStandard( ShuttleGui & S ) S.AddPrompt(_("Duration") + wxString(wxT(":"))); if (mToneDurationT == NULL) { - mToneDurationT = - new TimeTextCtrl(this, + mToneDurationT = new + NumericTextCtrl(NumericConverter::TIME, this, wxID_ANY, isSelection ? _("hh:mm:ss + samples") : _("hh:mm:ss + milliseconds"), mDuration, @@ -320,7 +321,7 @@ void ToneGenDialog::PopulateOrExchangeExtended( ShuttleGui & S ) if (mToneDurationT == NULL) { mToneDurationT = new - TimeTextCtrl(this, + NumericTextCtrl(NumericConverter::TIME, this, wxID_ANY, isSelection ? _("hh:mm:ss + samples") : _("hh:mm:ss + milliseconds"), mDuration, @@ -349,7 +350,7 @@ bool ToneGenDialog::TransferDataToWindow() EffectDialog::TransferDataToWindow(); // Must handle this ourselves since ShuttleGui doesn't know about it - mToneDurationT->SetTimeValue(mDuration); + mToneDurationT->SetValue(mDuration); return true; } @@ -364,7 +365,7 @@ bool ToneGenDialog::TransferDataFromWindow() frequency[1] = TrapDouble(frequency[1], FREQ_MIN, (float)(GetActiveProject()->GetRate())/2.); // Must handle this ourselves since ShuttleGui doesn't know about it - mDuration = mToneDurationT->GetTimeValue(); + mDuration = mToneDurationT->GetValue(); return true; } diff --git a/src/effects/ToneGen.h b/src/effects/ToneGen.h index 5257be0c5..d87d49065 100644 --- a/src/effects/ToneGen.h +++ b/src/effects/ToneGen.h @@ -14,7 +14,6 @@ #define __AUDACITY_EFFECT_TONEGEN__ #include "Generator.h" -#include "../widgets/TimeTextCtrl.h" #include "../Experimental.h" #include @@ -22,6 +21,7 @@ class wxString; class wxChoice; class wxTextCtrl; +class NumericTextCtrl; class ShuttleGui; #define __UNINITIALIZED__ (-1) @@ -126,7 +126,7 @@ class ToneGenDialog:public EffectDialog { wxArrayString *interpolations; private: - TimeTextCtrl *mToneDurationT; + NumericTextCtrl *mToneDurationT; }; #endif diff --git a/src/toolbars/SelectionBar.cpp b/src/toolbars/SelectionBar.cpp index abee03fc3..f2c908f40 100644 --- a/src/toolbars/SelectionBar.cpp +++ b/src/toolbars/SelectionBar.cpp @@ -51,7 +51,7 @@ with changes in the SelectionBar. #include "../AColor.h" #include "../Prefs.h" #include "../Snap.h" -#include "../widgets/TimeTextCtrl.h" +#include "../widgets/NumericTextCtrl.h" IMPLEMENT_CLASS(SelectionBar, ToolBar); @@ -89,10 +89,14 @@ SelectionBar::SelectionBar() mListener(NULL), mRate(0.0), mStart(0.0), mEnd(0.0), mAudio(0.0), mLeftTime(NULL), mRightTime(NULL), mAudioTime(NULL) { - // Make sure we have a valid rate as the TimeTextCtrl()s created in Populate() - // depend on it. Otherwise, division-by-zero floating point exceptions will occur. - // Refer to bug #462 for a scenario where the division-by-zero causes Audacity to fail. - mRate = (double) gPrefs->Read(wxT("/SamplingRate/DefaultProjectSampleRate"), AudioIO::GetOptimalSupportedSampleRate()); + // Make sure we have a valid rate as the NumericTextCtrl()s + // created in Populate() + // depend on it. Otherwise, division-by-zero floating point exceptions + // will occur. + // Refer to bug #462 for a scenario where the division-by-zero causes + // Audacity to fail. + mRate = (double) gPrefs->Read(wxT("/SamplingRate/DefaultProjectSampleRate"), + AudioIO::GetOptimalSupportedSampleRate()); } SelectionBar::~SelectionBar() @@ -247,12 +251,14 @@ void SelectionBar::Populate() NULL, this); - mLeftTime = new TimeTextCtrl(this, OnLeftTimeID, formatName, 0.0, mRate); + mLeftTime = new NumericTextCtrl( + NumericConverter::TIME, this, OnLeftTimeID, formatName, 0.0, mRate); mLeftTime->SetName(_("Selection Start:")); mLeftTime->EnableMenu(); mainSizer->Add(mLeftTime, 0, wxALIGN_CENTER_VERTICAL | wxRIGHT, 5); - mRightTime = new TimeTextCtrl(this, OnRightTimeID, formatName, 0.0, mRate); + mRightTime = new NumericTextCtrl( + NumericConverter::TIME, this, OnRightTimeID, formatName, 0.0, mRate); mRightTime->SetName(wxString(_("Selection ")) + (showSelectionLength ? _("Length") : _("End"))); @@ -264,7 +270,8 @@ void SelectionBar::Populate() wxLI_VERTICAL), 0, wxRIGHT, 5); - mAudioTime = new TimeTextCtrl(this, wxID_ANY, formatName, 0.0, mRate); + mAudioTime = new NumericTextCtrl( + NumericConverter::TIME, this, wxID_ANY, formatName, 0.0, mRate); mAudioTime->SetName(_("Audio Position:")); mAudioTime->EnableMenu(); mainSizer->Add(mAudioTime, 0, wxALIGN_CENTER_VERTICAL | wxRIGHT, 0); @@ -308,8 +315,8 @@ void SelectionBar::OnSize(wxSizeEvent &evt) void SelectionBar::ModifySelection(bool done) { - mStart = mLeftTime->GetTimeValue(); - double right = mRightTime->GetTimeValue(); + mStart = mLeftTime->GetValue(); + double right = mRightTime->GetValue(); if (mRightEndButton->GetValue()) { if(mStart > right) @@ -406,10 +413,10 @@ void SelectionBar::OnUpdate(wxCommandEvent &evt) void SelectionBar::ValuesToControls() { - mLeftTime->SetTimeValue(mStart); + mLeftTime->SetValue(mStart); if (mRightEndButton->GetValue()) - mRightTime->SetTimeValue(mEnd); + mRightTime->SetValue(mEnd); else { // mRightTime is the length. // Be sure to take into account the sub-sample offset. @@ -417,10 +424,10 @@ void SelectionBar::ValuesToControls() double t = (sampleCount)floor(mEnd * mRate + 0.5); t -= (sampleCount)floor(mStart * mRate + 0.5); t /= mRate; - mRightTime->SetTimeValue(t); + mRightTime->SetValue(t); } - mAudioTime->SetTimeValue(mAudio); + mAudioTime->SetValue(mAudio); } void SelectionBar::SetTimes(double start, double end, double audio) @@ -434,20 +441,21 @@ void SelectionBar::SetTimes(double start, double end, double audio) double SelectionBar::GetLeftTime() { - return mLeftTime->GetTimeValue(); + return mLeftTime->GetValue(); } double SelectionBar::GetRightTime() { if (mRightEndButton->GetValue()) - return mRightTime->GetTimeValue(); + return mRightTime->GetValue(); else { // What would be shown if we were showing the end time - TimeTextCtrl ttc(this, wxID_ANY, wxT(""), 0.0, mRate); + NumericTextCtrl ttc( + NumericConverter::TIME, this, wxID_ANY, wxT(""), 0.0, mRate); ttc.SetFormatString(mRightTime->GetFormatString()); ttc.SetSampleRate(mRate); - ttc.SetTimeValue(mEnd); - return ttc.GetTimeValue(); + ttc.SetValue(mEnd); + return ttc.GetValue(); } } diff --git a/src/toolbars/SelectionBar.h b/src/toolbars/SelectionBar.h index 78bbf3728..ec23eb228 100644 --- a/src/toolbars/SelectionBar.h +++ b/src/toolbars/SelectionBar.h @@ -25,7 +25,7 @@ class wxRadioButton; class wxSizeEvent; class SelectionBarListener; -class TimeTextCtrl; +class NumericTextCtrl; class SelectionBar:public ToolBar { @@ -78,11 +78,11 @@ class SelectionBar:public ToolBar { double mStart, mEnd, mAudio; wxString mField[10]; - TimeTextCtrl *mLeftTime; - TimeTextCtrl *mRightTime; + NumericTextCtrl *mLeftTime; + NumericTextCtrl *mRightTime; wxRadioButton *mRightEndButton; wxRadioButton *mRightLengthButton; - TimeTextCtrl *mAudioTime; + NumericTextCtrl *mAudioTime; wxComboBox *mRateBox; wxChoice *mSnapTo; diff --git a/src/toolbars/SpectralSelectionBar.cpp b/src/toolbars/SpectralSelectionBar.cpp new file mode 100644 index 000000000..0964774e3 --- /dev/null +++ b/src/toolbars/SpectralSelectionBar.cpp @@ -0,0 +1,308 @@ +/********************************************************************** + +Audacity: A Digital Audio Editor + +SpectralSelectionBar.cpp + +Copyright 2014 Dominic Mazzoni + +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. + +*******************************************************************//** + +\class SpectralSelectionBar +\brief (not quite a Toolbar) at foot of screen for setting and viewing the +frequency selection range. + +*//****************************************************************//** + +\class SpectralSelectionBarListener +\brief A class used to forward events to do +with changes in the SpectralSelectionBar. + +*//*******************************************************************/ + + +#include "../Audacity.h" + +#include + +// For compilers that support precompilation, includes "wx/wx.h". +#include + +#ifndef WX_PRECOMP +#include +#include +#include +#include +#include +#include +#include +#include +#endif +#include + +#include "SpectralSelectionBarListener.h" +#include "SpectralSelectionBar.h" + +#include "../AudacityApp.h" +#include "../SelectedRegion.h" +#include "../widgets/NumericTextCtrl.h" + +#ifdef EXPERIMENTAL_SPECTRAL_EDITING + +IMPLEMENT_CLASS(SpectralSelectionBar, ToolBar); + +enum { + SpectralSelectionBarFirstID = 2750, + OnCenterID, + OnWidthID +}; + +BEGIN_EVENT_TABLE(SpectralSelectionBar, ToolBar) +EVT_SIZE(SpectralSelectionBar::OnSize) +EVT_TEXT(OnCenterID, SpectralSelectionBar::OnCenter) +EVT_TEXT(OnWidthID, SpectralSelectionBar::OnWidth) +EVT_COMMAND(wxID_ANY, EVT_FREQUENCYTEXTCTRL_UPDATED, SpectralSelectionBar::OnUpdate) +EVT_COMMAND(wxID_ANY, EVT_LOGFREQUENCYTEXTCTRL_UPDATED, SpectralSelectionBar::OnUpdate) +END_EVENT_TABLE() + +SpectralSelectionBar::SpectralSelectionBar() +: ToolBar(SpectralSelectionBarID, _("SpectralSelection"), wxT("SpectralSelection")), +mListener(NULL), mCenter(0.0), mWidth(0.0), +mCenterCtrl(NULL), mWidthCtrl(NULL) +{ +} + +SpectralSelectionBar::~SpectralSelectionBar() +{ +} + +void SpectralSelectionBar::Create(wxWindow * parent) +{ + ToolBar::Create(parent); +} + +void SpectralSelectionBar::Populate() +{ + // This will be inherited by all children: + SetFont(wxFont(9, wxFONTFAMILY_SWISS, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_NORMAL)); + + wxFlexGridSizer *mainSizer; + wxBoxSizer *hSizer; + + /* we don't actually need a control yet, but we want to use its methods + * to do some look-ups, so we'll have to create one. We can't make the + * look-ups static because they depend on translations which are done at + * runtime */ + + wxString frequencyFormatName = mListener + ? mListener->SSBL_GetFrequencySelectionFormatName() + : wxString(wxEmptyString); + wxString logFrequencyFormatName = mListener + ? mListener->SSBL_GetLogFrequencySelectionFormatName() + : wxString(wxEmptyString); + + mainSizer = new wxFlexGridSizer(2, 1, 1); + Add(mainSizer, 0, wxALIGN_CENTER_VERTICAL); + + // + // Top row (labels) + // + + mainSizer->Add(new wxStaticText(this, -1, _("Center:"), + wxDefaultPosition, wxDefaultSize), + 0, wxALIGN_CENTER_VERTICAL | wxRIGHT, 5); + + mainSizer->Add(new wxStaticText(this, -1, _("Bandwidth:")), + 0, wxALIGN_CENTER_VERTICAL | wxRIGHT, 0); + + // + // Bottom row (controls) + // + + mCenterCtrl = new NumericTextCtrl( + NumericConverter::FREQUENCY, this, OnCenterID, frequencyFormatName, 0.0); + mCenterCtrl->SetName(_("Center Frequency:")); + mCenterCtrl->EnableMenu(); + mainSizer->Add(mCenterCtrl, 0, wxALIGN_CENTER_VERTICAL | wxRIGHT, 5); + + mWidthCtrl = new NumericTextCtrl( + NumericConverter::LOG_FREQUENCY, this, OnWidthID, logFrequencyFormatName, 0.0); + mWidthCtrl->SetName(wxString(_("Bandwidth:"))); + mWidthCtrl->EnableMenu(); + mainSizer->Add(mWidthCtrl, 0, wxALIGN_CENTER_VERTICAL | wxRIGHT, 0); + + mainSizer->Layout(); + + Layout(); + + SetMinSize(GetSizer()->GetMinSize()); +} + +void SpectralSelectionBar::UpdatePrefs() +{ + { + wxCommandEvent e(EVT_FREQUENCYTEXTCTRL_UPDATED); + e.SetInt(mCenterCtrl->GetFormatIndex()); + OnUpdate(e); + } + + { + wxCommandEvent e(EVT_LOGFREQUENCYTEXTCTRL_UPDATED); + e.SetInt(mWidthCtrl->GetFormatIndex()); + OnUpdate(e); + } + + // Set label to pull in language change + SetLabel(_("SpectralSelection")); + + // Give base class a chance + ToolBar::UpdatePrefs(); +} + +void SpectralSelectionBar::SetListener(SpectralSelectionBarListener *l) +{ + mListener = l; + SetFrequencySelectionFormatName(mListener->SSBL_GetFrequencySelectionFormatName()); + SetLogFrequencySelectionFormatName(mListener->SSBL_GetLogFrequencySelectionFormatName()); +}; + +void SpectralSelectionBar::OnSize(wxSizeEvent &evt) +{ + Refresh(true); + + evt.Skip(); +} + +void SpectralSelectionBar::ModifySpectralSelection(bool done) +{ + const double nyq = mListener->SSBL_GetRate() / 2.0; + + mCenter = mCenterCtrl->GetValue(); + mWidth = mWidthCtrl->GetValue(); + double bottom, top; + if (mCenter < 0 && mWidth < 0) + bottom = top = SelectedRegion::UndefinedFrequency; + else { + if (mCenter < 0) { + mWidth = log(std::min(nyq, exp(mWidth))); + // Choose arbitrary center for the width + mCenter = sqrt(nyq); + } + else if (mWidth < 0) { + mCenter = std::max(1.0, std::min(nyq, mCenter)); + // Choose arbitrary width for the center + const double ratio = std::min(mCenter, nyq / mCenter); + mWidth = log(ratio * ratio); + } + else { + mCenter = std::max(1.0, std::min(nyq, mCenter)); + double ratio = std::min(mCenter, nyq / mCenter); + mWidth = std::min(2 * log(ratio), mWidth); + } + + const double ratio = exp(mWidth / 2); + bottom = mCenter / ratio, top = mCenter * ratio; + } + + // Notify project and track panel, which may change + // the values again, and call back to us in SetFrequencies() + mListener->SSBL_ModifySpectralSelection(bottom, top, done); +} + +void SpectralSelectionBar::OnCenter(wxCommandEvent & event) +{ + ModifySpectralSelection(event.GetInt() != 0); +} + +void SpectralSelectionBar::OnWidth(wxCommandEvent & event) +{ + ModifySpectralSelection(event.GetInt() != 0); +} + +void SpectralSelectionBar::OnUpdate(wxCommandEvent &evt) +{ + int index = evt.GetInt(); + wxWindow *w = FindFocus(); + bool centerFocus = (w == mCenterCtrl); + bool widthFocus = (w == mWidthCtrl); + + evt.Skip(false); + + // Save formats before recreating the controls so they resize properly + wxEventType type = evt.GetEventType(); + int frequencyFormatIndex = mCenterCtrl->GetFormatIndex(); + int widthFormatIndex = mWidthCtrl->GetFormatIndex(); + if (type == EVT_FREQUENCYTEXTCTRL_UPDATED) { + frequencyFormatIndex = index; + mListener->SSBL_SetFrequencySelectionFormatName + (mCenterCtrl->GetBuiltinName(index)); + } + else if (type == EVT_LOGFREQUENCYTEXTCTRL_UPDATED) { + widthFormatIndex = index; + mListener->SSBL_SetLogFrequencySelectionFormatName + (mWidthCtrl->GetBuiltinName(index)); + } + + // ToolBar::ReCreateButtons() will get rid of our sizers and controls + // so reset pointers first. + mCenterCtrl = mWidthCtrl = NULL; + + ToolBar::ReCreateButtons(); + + ValuesToControls(); + + mCenterCtrl->SetFormatName + (mCenterCtrl->GetBuiltinName(frequencyFormatIndex)); + mWidthCtrl->SetFormatName + (mWidthCtrl->GetBuiltinName(widthFormatIndex)); + + if (centerFocus) { + mCenterCtrl->SetFocus(); + } + else if (widthFocus) { + mWidthCtrl->SetFocus(); + } + + Updated(); +} + +void SpectralSelectionBar::ValuesToControls() +{ + mCenterCtrl->SetValue(mCenter); + mWidthCtrl->SetValue(mWidth); +} + +void SpectralSelectionBar::SetFrequencies(double bottom, double top) +{ + if (bottom > 0 && top >= bottom) + mWidth = log(top / bottom), mCenter = sqrt(top * bottom); + else + mWidth = mCenter = -1.0; + + ValuesToControls(); +} + +void SpectralSelectionBar::SetFrequencySelectionFormatName(const wxString & formatName) +{ + mCenterCtrl->SetFormatName(formatName); + + wxCommandEvent e(EVT_FREQUENCYTEXTCTRL_UPDATED); + e.SetInt(mCenterCtrl->GetFormatIndex()); + OnUpdate(e); +} + +void SpectralSelectionBar::SetLogFrequencySelectionFormatName(const wxString & formatName) +{ + mWidthCtrl->SetFormatName(formatName); + + wxCommandEvent e(EVT_LOGFREQUENCYTEXTCTRL_UPDATED); + e.SetInt(mWidthCtrl->GetFormatIndex()); + OnUpdate(e); +} + +#endif // #ifdef EXPERIMENTAL_SPECTRAL_EDITING diff --git a/src/toolbars/SpectralSelectionBar.h b/src/toolbars/SpectralSelectionBar.h new file mode 100644 index 000000000..34f6c4e1f --- /dev/null +++ b/src/toolbars/SpectralSelectionBar.h @@ -0,0 +1,73 @@ +/********************************************************************** + +Audacity: A Digital Audio Editor + +SpectralSelectionBar.h + +Paul Licameli + +**********************************************************************/ + +#ifndef __AUDACITY_SPECTRAL_SELECTION_BAR__ +#define __AUDACITY_SPECTRAL_SELECTION_BAR__ + +#include + +#include "ToolBar.h" + +class wxBitmap; +class wxCheckBox; +class wxChoice; +class wxComboBox; +class wxCommandEvent; +class wxDC; +class wxRadioButton; +class wxSizeEvent; + +class SpectralSelectionBarListener; +class NumericTextCtrl; + +class SpectralSelectionBar :public ToolBar { + +public: + + SpectralSelectionBar(); + virtual ~SpectralSelectionBar(); + + void Create(wxWindow *parent); + + virtual void Populate(); + virtual void Repaint(wxDC * WXUNUSED(dc)) {}; + virtual void EnableDisableButtons() {}; + virtual void UpdatePrefs(); + + void SetFrequencies(double bottom, double top); + void SetFrequencySelectionFormatName(const wxString & formatName); + void SetLogFrequencySelectionFormatName(const wxString & formatName); + void SetListener(SpectralSelectionBarListener *l); + +private: + + void ValuesToControls(); + void OnUpdate(wxCommandEvent &evt); + void OnCenter(wxCommandEvent &evt); + void OnWidth(wxCommandEvent &evt); + + void OnSize(wxSizeEvent &evt); + + void ModifySpectralSelection(bool done = false); + + SpectralSelectionBarListener * mListener; + double mCenter, mWidth; + + NumericTextCtrl *mCenterCtrl; + NumericTextCtrl *mWidthCtrl; + +public: + + DECLARE_CLASS(SpectralSelectionBar); + DECLARE_EVENT_TABLE(); +}; + +#endif + diff --git a/src/toolbars/SpectralSelectionBarListener.h b/src/toolbars/SpectralSelectionBarListener.h new file mode 100644 index 000000000..0801c9074 --- /dev/null +++ b/src/toolbars/SpectralSelectionBarListener.h @@ -0,0 +1,37 @@ +/********************************************************************** + + Audacity: A Digital Audio Editor + + SpectralSelectionBarListener.h + + Paul Licameli + +**********************************************************************/ + +#ifndef __AUDACITY_SPECTRAL_SELECTION_BAR_LISTENER__ +#define __AUDACITY_SPECTRAL_SELECTION_BAR_LISTENER__ + +#include "../Audacity.h" + +class wxString; +class SelectedRegion; + +class AUDACITY_DLL_API SpectralSelectionBarListener { + + public: + + SpectralSelectionBarListener(){}; + virtual ~SpectralSelectionBarListener(){}; + + virtual double SSBL_GetRate() const = 0; + + virtual const wxString & SSBL_GetFrequencySelectionFormatName() = 0; + virtual void SSBL_SetFrequencySelectionFormatName(const wxString & formatName) = 0; + + virtual const wxString & SSBL_GetLogFrequencySelectionFormatName() = 0; + virtual void SSBL_SetLogFrequencySelectionFormatName(const wxString & formatName) = 0; + + virtual void SSBL_ModifySpectralSelection(double &bottom, double &top, bool done) = 0; +}; + +#endif diff --git a/src/toolbars/ToolBar.h b/src/toolbars/ToolBar.h index 075b09ce2..c9c21f874 100644 --- a/src/toolbars/ToolBar.h +++ b/src/toolbars/ToolBar.h @@ -13,6 +13,8 @@ #ifndef __AUDACITY_TOOLBAR__ #define __AUDACITY_TOOLBAR__ +#include "../Experimental.h" + #include #include #include @@ -69,6 +71,9 @@ enum TranscriptionBarID, SelectionBarID, DeviceBarID, +#ifdef EXPERIMENTAL_SPECTRAL_EDITING + SpectralSelectionBarID, +#endif ToolBarCount }; diff --git a/src/toolbars/ToolManager.cpp b/src/toolbars/ToolManager.cpp index e048eab87..cbbf2d816 100644 --- a/src/toolbars/ToolManager.cpp +++ b/src/toolbars/ToolManager.cpp @@ -54,6 +54,7 @@ #include "MeterToolBar.h" #include "MixerToolBar.h" #include "SelectionBar.h" +#include "SpectralSelectionBar.h" #include "ToolsToolBar.h" #include "TranscriptionToolBar.h" @@ -412,6 +413,9 @@ ToolManager::ToolManager( AudacityProject *parent ) mBars[ TranscriptionBarID ] = new TranscriptionToolBar(); mBars[ SelectionBarID ] = new SelectionBar(); mBars[ DeviceBarID ] = new DeviceToolBar(); +#ifdef EXPERIMENTAL_SPECTRAL_EDITING + mBars[SpectralSelectionBarID] = new SpectralSelectionBar(); +#endif // We own the timer mTimer.SetOwner( this ); @@ -486,7 +490,11 @@ void ToolManager::Reset() floater = bar->GetParent(); } - if( ndx == SelectionBarID ) + if (ndx == SelectionBarID +#ifdef EXPERIMENTAL_SPECTRAL_EDITING + || ndx == SpectralSelectionBarID +#endif + ) { dock = mBotDock; @@ -598,7 +606,12 @@ void ToolManager::ReadConfig() gPrefs->SetPath( bar->GetSection() ); - int defaultDock = ndx == SelectionBarID ? BotDockID : TopDockID; + int defaultDock = (ndx == SelectionBarID +#ifdef EXPERIMENTAL_SPECTRAL_EDITING + || ndx == SpectralSelectionBarID +#endif + ) + ? BotDockID : TopDockID; if( ndx == MeterBarID ) defaultDock = 0; // Read in all the settings diff --git a/src/widgets/Grid.cpp b/src/widgets/Grid.cpp index c0d32396b..d50c7fd8e 100644 --- a/src/widgets/Grid.cpp +++ b/src/widgets/Grid.cpp @@ -24,7 +24,7 @@ #include #include "Grid.h" -#include "TimeTextCtrl.h" +#include "NumericTextCtrl.h" TimeEditor::TimeEditor() { @@ -44,7 +44,7 @@ TimeEditor::~TimeEditor() void TimeEditor::Create(wxWindow *parent, wxWindowID id, wxEvtHandler *handler) { - m_control = new TimeTextCtrl(parent, + m_control = new NumericTextCtrl(NumericConverter::TIME, parent, wxID_ANY, mFormat, mOld, @@ -73,7 +73,7 @@ void TimeEditor::BeginEdit(int row, int col, wxGrid *grid) table->GetValue(row, col).ToDouble(&mOld); - GetTimeCtrl()->SetTimeValue(mOld); + GetTimeCtrl()->SetValue(mOld); GetTimeCtrl()->EnableMenu(); GetTimeCtrl()->SetFocus(); @@ -103,7 +103,7 @@ void TimeEditor::ApplyEdit(int row, int col, wxGrid *grid) bool TimeEditor::EndEdit(int row, int col, wxGrid *grid) { - double newtime = GetTimeCtrl()->GetTimeValue(); + double newtime = GetTimeCtrl()->GetValue(); bool changed = newtime != mOld; if (changed) { @@ -117,7 +117,7 @@ bool TimeEditor::EndEdit(int row, int col, wxGrid *grid) void TimeEditor::Reset() { - GetTimeCtrl()->SetTimeValue(mOld); + GetTimeCtrl()->SetValue(mOld); } bool TimeEditor::IsAcceptedKey(wxKeyEvent &event) @@ -138,7 +138,7 @@ wxGridCellEditor *TimeEditor::Clone() const wxString TimeEditor::GetValue() const { - return wxString::Format(wxT("%g"), GetTimeCtrl()->GetTimeValue()); + return wxString::Format(wxT("%g"), GetTimeCtrl()->GetValue()); } wxString TimeEditor::GetFormat() @@ -180,7 +180,7 @@ void TimeRenderer::Draw(wxGrid &grid, table->GetValue(row, col).ToDouble(&value); - TimeTextCtrl tt(&grid, + NumericTextCtrl tt(NumericConverter::TIME, &grid, wxID_ANY, te->GetFormat(), value, @@ -188,7 +188,7 @@ void TimeRenderer::Draw(wxGrid &grid, wxPoint(10000, 10000), // create offscreen wxDefaultSize, true); - tstr = tt.GetTimeString(); + tstr = tt.GetString(); te->DecRef(); } @@ -236,7 +236,7 @@ wxSize TimeRenderer::GetBestSize(wxGrid &grid, if (te) { double value; table->GetValue(row, col).ToDouble(&value); - TimeTextCtrl tt(&grid, + NumericTextCtrl tt(NumericConverter::TIME, &grid, wxID_ANY, te->GetFormat(), value, @@ -790,7 +790,7 @@ wxAccStatus GridAx::GetName(int childId, wxString *name) double value; v.ToDouble(&value); - TimeTextCtrl tt(mGrid, + NumericTextCtrl tt(NumericConverter::TIME, mGrid, wxID_ANY, c->GetFormat(), value, @@ -798,7 +798,7 @@ wxAccStatus GridAx::GetName(int childId, wxString *name) wxPoint(10000, 10000), // create offscreen wxDefaultSize, true); - v = tt.GetTimeString(); + v = tt.GetString(); } if (c) diff --git a/src/widgets/Grid.h b/src/widgets/Grid.h index 551b4f28b..0abb483eb 100644 --- a/src/widgets/Grid.h +++ b/src/widgets/Grid.h @@ -26,12 +26,12 @@ class GridAx; #endif -class TimeTextCtrl; +class NumericTextCtrl; // ---------------------------------------------------------------------------- // TimeEditor // -// wxGridCellEditor for the TimeTextCtrl. +// wxGridCellEditor for the NumericTextCtrl. // ---------------------------------------------------------------------------- #define GRID_VALUE_TIME wxT("Time") @@ -71,7 +71,7 @@ class TimeEditor:public wxGridCellEditor wxGridCellEditor *Clone() const; wxString GetValue() const; - TimeTextCtrl *GetTimeCtrl() const { return (TimeTextCtrl *)m_control; }; + NumericTextCtrl *GetTimeCtrl() const { return (NumericTextCtrl *)m_control; } private: @@ -84,7 +84,7 @@ class TimeEditor:public wxGridCellEditor // ---------------------------------------------------------------------------- // TimeRenderer // -// wxGridCellRenderer for the TimeTextCtrl. +// wxGridCellRenderer for the NumericTextCtrl. // ---------------------------------------------------------------------------- class TimeRenderer : public wxGridCellRenderer diff --git a/src/widgets/NumericTextCtrl.cpp b/src/widgets/NumericTextCtrl.cpp new file mode 100644 index 000000000..4e37e08c4 --- /dev/null +++ b/src/widgets/NumericTextCtrl.cpp @@ -0,0 +1,2010 @@ +/********************************************************************** + + Audacity: A Digital Audio Editor + + NumericTextCtrl.cpp + + Dominic Mazzoni + + +********************************************************************//** +NumericConverter +\class NumericConverter +\brief NumericConverter provides the advanced formatting control used +in the selection bar of Audacity. + + Any negative value given to the converter is considered invalid and + all digit positions of the resulting string will be filled with hyphens. + Otherwise: + + The NumericConverter makes use of a format string to specify the + exact way that a single value is split into several fields, + such as the hh:mm:ss format. The advantage of this format string + is that it is very small and compact, but human-readable and + somewhat intuitive, so that it's easy to add new layouts + in the future. It's also designed to make it easier to add + i18n support, since the way that numbers are displayed in different + languages could conceivably vary a lot. + + The number to be formatted may be expressed in seconds, so the format + string can specify the relationship of each field to the number of + seconds. + + The class is also reused to format some non-time values such as + frequency and log of frequency. + + Let's start by considering an example: here's the format string + that prints an integer number of seconds in the hour minute + second h:m:s format: + + *:60:60 + + The "*" is a wildcard, saying that the leftmost field can contain + numbers of arbitrary magnitude. The next character, ':', since it + is not a digit or a wildcard, is interpreted as a delimiter, and + will be displayed between those fields. The next number, 60, + indicates that the range of the next field (minutes) is 60. + Then there's another ':' delimiter, and finally the last field + (seconds) is 60. So, if you give it a number like 3758 + it is formatted as: + + 3758 seconds, "*:60:60" -> "1:2:38" + + Note that 3758 = 1*60*60 + 2*60 + 38. + + When NumericConverter formats an integer, you can think of its process + as working from right to left. Given the value "3758", it fills + in the seconds by dividing by 60, sticking the remainder in the + seconds field and then passing the quotient to the next field to + the left. + + In order to format a field with leading zeros, simply add a leading + zero to that field, like this: + + 3758 seconds, "*:060:060" -> "1:02:38" + + In order to format fractions, simply include a field delimiter + ending with a decimal point. If the delimiter is simply '.' with + nothing else, then the '.' is actually displayed. Otherwise the + '.' is dropped, and the other characters in the delimiter are + displayed instead. + + Here's how we'd display hours, minutes, and seconds with three + decimal places after the seconds: + + 3758.5 seconds, "*:060:060.01000" -> "1:02:38.500" + + Similarly, here's how we'd display the fractional part of + seconds as film frames (24 per second) instead of milliseconds: + + 3758.5 seconds, "*:060:060 and .24 frames" -> "1:02:38 and 12 frames" + + Note that the decimal '.' is associated with the delimeter, not + with the 24. + + Additionally, the special character '#' can be used in place of a number + to represent the current sample rate. Use '0#' to add leading + zeros to that field. For example: + + 3758.5 seconds, "*:060:060+.#samples" -> "1:02:38+22050samples" + + (Almost) Finally, there is a rule that allows you to change the units into + something other than seconds. To do this, put a "|" character on + the far right, followed by a number specifying the scaling factor. + As an exception to previous rules, decimal points are allowed + in the final scaling factor - the period is not interpreted as it + would be before the "|" character. (This is fine, because all + previous fields must be integers to make sense.) Anyway, if you + include a scaling factor after a "|", the number will be + multiplied by this factor before it is formatted. For example, to + express the current time in NTSC frames (~29.97 fps), you could + use the following formatting: + + 3758.5 seconds, "*.01000 frames|29.97002997" -> "112642.358 frames" + + Finally there is a further special character that can be used after a "|" + and that is "N". This applies special rule for NTSC drop-frame timecode. + + Summary of format string rules: + + - The characters '0-9', '*', and '#' are numeric. Any sequence of + these characters is treated as defining a new field by specifying + its range. All other characters become delimiters between fields. + (The one exception is that '.' is treated as numeric after the + optional '|'.) + - A field with a range of '*', which only makes sense as the + leftmost field, means the field should display as large a number + as necessary. (Note: this no longer makes sense here and applies to a + previous version). + - The character '#' represents the current sample rate. + - If a field specifier beings with a leading zero, it will be formatted + with leading zeros, too - enough to display the maximum value + that field can display. So the number 7 in a field specified + as '01000' would be formatted as '007'. Bond. James Bond. + - Any non-numeric characters before the first field are treated + as a prefix, and will be displayed to the left of the first field. + - A delimiter ending in '.' is treated specially. All fields after + this delimeter are fractional fields, after the decimal point. + - The '|' character is treated as a special delimiter. The number + to the right of this character (which is allowed to contain a + decimal point) is treated as a scaling factor. The number is + multiplied by this factor before converting. + - The special character 'N' after '|' is only used for NTSC drop-frame. + +*******************************************************************//** + +\class NumericTextCtrlAx +\brief NumericTextCtrlAx gives the NumericTextCtrl Accessibility. + +*******************************************************************//** + +\class NumericConverter +\brief NumericConverter has all the time conversion and snapping +functionality that used to live in NumericTextCtrl. The idea is to have +a GUI-less class which can do the conversions, so that we can use it +in sanpping without having a window created each time. + +*//****************************************************************//** + +\class BuiltinFormatString +\brief BuiltinFormatString is a structure used in the NumericTextCtrl +and holds both a descriptive name for the string format and a +printf inspired style format string, optimised for displaying time in +different formats. + +*//****************************************************************//** + +\class NumericField +\brief NumericField is a class used in NumericTextCtrl + +*//****************************************************************//** + +\class DigitInfo +\brief DigitInfo is a class used in NumericTextCtrl + +**********************************************************************/ + + +#include "../Audacity.h" +#include "../AudacityApp.h" +#include "NumericTextCtrl.h" +#include "../Sequence.h" // for sampleCount +#include "../Theme.h" +#include "../AllThemeResources.h" +#include "../AColor.h" + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// +// ---------------------------------------------------------------------------- +// BuiltinFormatString Struct +// ---------------------------------------------------------------------------- +// +/** \brief struct to hold a formatting control string and its user facing name + * Used in an array to hold the built-in time formats that are always available + * to the user */ +struct BuiltinFormatString +{ + wxString name; + wxString formatStr; +}; + +// +// ---------------------------------------------------------------------------- +// NumericField Class +// ---------------------------------------------------------------------------- +// +class NumericField +{ +public: + NumericField(bool _frac, int _base, int _range, bool _zeropad) + { + frac = _frac; + base = _base; + range = _range; + zeropad = _zeropad; + digits = 0; + } + void CreateDigitFormatStr() + { + if (range > 1) + digits = (int)ceil(log10(range-1.0)); + else + digits = 5; // hack: default + if (zeropad && range>1) + formatStr.Printf(wxT("%%0%dd"), digits); // ex. "%03d" if digits is 3 + else { + formatStr.Printf(wxT("%%0%dd"), digits); + } + } + bool frac; // is it a fractional field + int base; // divide by this (multiply, after decimal point) + int range; // then take modulo this + int digits; + int pos; // Index of this field in the ValueString + int fieldX; // x-position of the field on-screen + int fieldW; // width of the field on-screen + int labelX; // x-position of the label on-screen + bool zeropad; + wxString label; + wxString formatStr; + wxString str; +}; + +// +// ---------------------------------------------------------------------------- +// DigitInfo Class +// ---------------------------------------------------------------------------- +// +class DigitInfo +{ +public: + DigitInfo(int _field, int _index, int _pos, wxRect _box) + { + field = _field; + index = _index; + pos = _pos; + digitBox = _box; + } + int field; // Which field + int index; // Index of this digit within the field + int pos; // Position in the ValueString + wxRect digitBox; +}; + +#include +WX_DEFINE_OBJARRAY(NumericFieldArray); +WX_DEFINE_OBJARRAY(DigitInfoArray); + +namespace { + +/** \brief array of formats the control knows about internally + * array of string pairs for name of the format and the format string + * needed to create that format output. This is used for the pop-up + * list of formats to choose from in the control. */ +const BuiltinFormatString TimeConverterFormats[] = { + { + /* i18n-hint: Name of time display format that shows time in seconds */ + _("seconds"), + /* i18n-hint: Format string for displaying time in seconds. Change the comma + * in the middle to the 1000s separator for your locale, and the 'seconds' + * on the end to the word for seconds. Don't change the numbers. */ + _("01000,01000 seconds") + }, + + { + /* i18n-hint: Name of time display format that shows time in hours, minutes + * and seconds */ + _("hh:mm:ss"), + /* i18n-hint: Format string for displaying time in hours, minutes and + * seconds. Change the 'h' to the abbreviation for hours, 'm' to the + * abbreviation for minutes and 's' to the abbreviation for seconds. Don't + * change the numbers unless there aren't 60 seconds in a minute in your + * locale */ + _("0100 h 060 m 060 s") + }, + + { + /* i18n-hint: Name of time display format that shows time in days, hours, + * minutes and seconds */ + _("dd:hh:mm:ss"), + /* i18n-hint: Format string for displaying time in days, hours, minutes and + * seconds. Change the 'days' to the word for days, 'h' to the abbreviation + * for hours, 'm' to the abbreviation for minutes and 's' to the + * abbreviation for seconds. Don't change the numbers unless there aren't + * 24 hours in a day in your locale */ + _("0100 days 024 h 060 m 060 s") + }, + + { + /* i18n-hint: Name of time display format that shows time in hours, + * minutes, seconds and hundredths of a second (1/100 second) */ + _("hh:mm:ss + hundredths"), + /* i18n-hint: Format string for displaying time in hours, minutes, seconds + * and hundredths of a second. Change the 'h' to the abbreviation for hours, + * 'm' to the abbreviation for minutes and 's' to the abbreviation for seconds + * (the hundredths are shown as decimal seconds) . Don't change the numbers + * unless there aren't 60 minutes in an hour in your locale */ + _("0100 h 060 m 060.0100 s") + }, + + { + /* i18n-hint: Name of time display format that shows time in hours, + * minutes, seconds and milliseconds (1/1000 second) */ + _("hh:mm:ss + milliseconds"), + /* i18n-hint: Format string for displaying time in hours, minutes, seconds + * and milliseconds. Change the 'h' to the abbreviation for hours, 'm' to the + * abbreviation for minutes and 's' to the abbreviation for seconds (the + * milliseconds are shown as decimal seconds) . Don't change the numbers + * unless there aren't 60 minutes in an hour in your locale */ + _("0100 h 060 m 060.01000 s") + }, + + { + /* i18n-hint: Name of time display format that shows time in hours, + * minutes, seconds and samples (at the current project sample rate) */ + _("hh:mm:ss + samples"), + /* i18n-hint: Format string for displaying time in hours, minutes, seconds + * and samples. Change the 'h' to the abbreviation for hours, 'm' to the + * abbreviation for minutes, 's' to the abbreviation for seconds and + * translate samples . Don't change the numbers + * unless there aren't 60 seconds in a minute in your locale */ + _("0100 h 060 m 060 s+.# samples") + }, + + { + /* i18n-hint: Name of time display format that shows time in samples (at the + * current project sample rate). For example the number of a sample at 1 + * second into a recording at 44.1KHz would be 44,100. + */ + _("samples"), + /* i18n-hint: Format string for displaying time in samples (lots of samples). + * Change the ',' to the 1000s separator for your locale, and translate + * samples. If 1000s aren't a base multiple for your number system, then you + * can change the numbers to an appropriate one, and put a 0 on the front */ + _("01000,01000,01000 samples|#") + }, + + { + /* i18n-hint: Name of time display format that shows time in hours, minutes, + * seconds and frames at 24 frames per second (commonly used for films) */ + _("hh:mm:ss + film frames (24 fps)"), + /* i18n-hint: Format string for displaying time in hours, minutes, seconds + * and frames at 24 frames per second. Change the 'h' to the abbreviation + * for hours, 'm' to the abbreviation for minutes, 's' to the abbreviation + * for seconds and translate 'frames' . Don't change the numbers + * unless there aren't 60 seconds in a minute in your locale */ + _("0100 h 060 m 060 s+.24 frames") + }, + + { + /* i18n-hint: Name of time display format that shows time in frames (lots of + * frames) at 24 frames per second (commonly used for films) */ + _("film frames (24 fps)"), + /* i18n-hint: Format string for displaying time in frames at 24 frames per + * second. Change the comma + * in the middle to the 1000s separator for your locale, + * translate 'frames' and leave the rest alone */ + _("01000,01000 frames|24") + }, + + { + /* i18n-hint: Name of time display format that shows time in hours, minutes, + * seconds and frames at NTSC TV drop-frame rate (used for American / + * Japanese TV, and very odd) */ + _("hh:mm:ss + NTSC drop frames"), + /* i18n-hint: Format string for displaying time in hours, minutes, seconds + * and frames with NTSC drop frames. Change the 'h' to the abbreviation + * for hours, 'm' to the abbreviation for minutes, 's' to the abbreviation + * for seconds and translate 'frames'. Leave the |N alone, it's important! */ + _("0100 h 060 m 060 s+.30 frames|N") + }, + + { + /* i18n-hint: Name of time display format that shows time in hours, minutes, + * seconds and frames at NTSC TV non-drop-frame rate (used for American / + * Japanese TV, and doesn't quite match wall time */ + _("hh:mm:ss + NTSC non-drop frames"), + /* i18n-hint: Format string for displaying time in hours, minutes, seconds + * and frames with NTSC drop frames. Change the 'h' to the abbreviation + * for hours, 'm' to the abbreviation for minutes, 's' to the abbreviation + * for seconds and translate 'frames'. Leave the | .999000999 alone, + * the whole things really is slightly off-speed! */ + _("0100 h 060 m 060 s+.030 frames| .999000999") + }, + + { + /* i18n-hint: Name of time display format that shows time in frames at NTSC + * TV frame rate (used for American / Japanese TV */ + _("NTSC frames"), + /* i18n-hint: Format string for displaying time in frames with NTSC frames. + * Change the comma + * in the middle to the 1000s separator for your locale, + * translate 'frames' and leave the rest alone. That really is the frame + * rate! */ + _("01000,01000 frames|29.97002997") + }, + + { + /* i18n-hint: Name of time display format that shows time in hours, minutes, + * seconds and frames at PAL TV frame rate (used for European TV) */ + _("hh:mm:ss + PAL frames (25 fps)"), + /* i18n-hint: Format string for displaying time in hours, minutes, seconds + * and frames with PAL TV frames. Change the 'h' to the abbreviation + * for hours, 'm' to the abbreviation for minutes, 's' to the abbreviation + * for seconds and translate 'frames'. Nice simple time code! */ + _("0100 h 060 m 060 s+.25 frames") + }, + + { + /* i18n-hint: Name of time display format that shows time in frames at PAL + * TV frame rate (used for European TV) */ + _("PAL frames (25 fps)"), + /* i18n-hint: Format string for displaying time in frames with NTSC frames. + * Change the comma + * in the middle to the 1000s separator for your locale, + * translate 'frames' and leave the rest alone. */ + _("01000,01000 frames|25") + }, + + { + /* i18n-hint: Name of time display format that shows time in hours, minutes, + * seconds and frames at CD Audio frame rate (75 frames per second) */ + _("hh:mm:ss + CDDA frames (75 fps)"), + /* i18n-hint: Format string for displaying time in hours, minutes, seconds + * and frames with CD Audio frames. Change the 'h' to the abbreviation + * for hours, 'm' to the abbreviation for minutes, 's' to the abbreviation + * for seconds and translate 'frames'. */ + _("0100 h 060 m 060 s+.75 frames") + }, + + { + /* i18n-hint: Name of time display format that shows time in frames at CD + * Audio frame rate (75 frames per second) */ + _("CDDA frames (75 fps)"), + /* i18n-hint: Format string for displaying time in frames with CD Audio + * frames. Change the comma + * in the middle to the 1000s separator for your locale, + * translate 'frames' and leave the rest alone */ + _("01000,01000 frames|75") + }, +}; + +/** \brief array of formats the control knows about internally + * array of string pairs for name of the format and the format string + * needed to create that format output. This is used for the pop-up + * list of formats to choose from in the control. */ +const BuiltinFormatString FrequencyConverterFormats[] = { + /* i18n-hint: Name of display format that shows frequency in hertz */ + { + _("hertz"), + /* i18n-hint: Format string for displaying frequency in hertz. Change + * the decimal point for your locale. Don't change the numbers. */ + _("0100000.0100 hertz") + }, +}; + +/** \brief array of formats the control knows about internally + * array of string pairs for name of the format and the format string + * needed to create that format output. This is used for the pop-up + * list of formats to choose from in the control. */ +const BuiltinFormatString LogFrequencyConverterFormats[] = { + { + /* i18n-hint: Name of display format that shows log of frequency + * in octaves */ + _("octaves"), + /* i18n-hint: Format string for displaying log of frequency in octaves. + * Change the decimal points for your locale. Don't change the numbers. */ + // Scale factor is 1 / ln (2) + _("100.01000 octaves|1.442695041") + }, + + { + /* i18n-hint: Name of display format that shows log of frequency + * in steps and cents */ + _("steps + cents"), + /* i18n-hint: Format string for displaying log of frequency in steps + * and cents. + * Change the decimal points for your locale. Don't change the numbers. */ + // Scale factor is 12 / ln (2) + _("100 steps .0100 cents|17.312340491") + }, + + { + /* i18n-hint: Name of display format that shows log of frequency + * in decades */ + _("decades"), + /* i18n-hint: Format string for displaying log of frequency in decades. + * Change the decimal points for your locale. Don't change the numbers. */ + // Scale factor is 1 / ln (10) + _("100.0100 decades|0.434294482") + }, +}; + +} + +// +// ---------------------------------------------------------------------------- +// NumericConverter Class +// ---------------------------------------------------------------------------- +// +NumericConverter::NumericConverter(Type type, + const wxString & formatName, + double value, + double sampleRate) +{ + mDefaultNdx = 0; + + mType = type; + switch (type) { + case TIME: + mBuiltinFormatStrings = TimeConverterFormats; + mNBuiltins = sizeof(TimeConverterFormats) / + sizeof (TimeConverterFormats[0]); + mDefaultNdx = 4; // Default to "hh:mm:ss + milliseconds". + break; + case FREQUENCY: + mBuiltinFormatStrings = FrequencyConverterFormats; + mNBuiltins = sizeof(FrequencyConverterFormats) / + sizeof (FrequencyConverterFormats[0]); + break; + case LOG_FREQUENCY: + mBuiltinFormatStrings = LogFrequencyConverterFormats; + mNBuiltins = sizeof(LogFrequencyConverterFormats) / + sizeof (LogFrequencyConverterFormats[0]); + default: + break; + } + + mPrefix = wxT(""); + mValueTemplate = wxT(""); + mValueMask = wxT(""); + mValueString = wxT(""); + + mScalingFactor = 1.0f; + mSampleRate = 1.0f; + mNtscDrop = false; + + mFocusedDigit = 0; + + SetSampleRate(sampleRate); + SetFormatName(formatName); + SetValue(value); +} + +void NumericConverter::ParseFormatString( const wxString & format) +{ + mPrefix = wxT(""); + mFields.Clear(); + mDigits.Clear(); + mScalingFactor = 1.0; + + bool inFrac = false; + int fracMult = 1; + int numWholeFields = 0; + int numFracFields = 0; + wxString numStr; + wxString delimStr; + unsigned int i; + + mNtscDrop = false; + for(i=0; i= '0' && format[i] <='9') || + format[i] == wxT('*') || format[i] == wxT('#')) { + numStr += format[i]; + if (delimStr != wxT("")) + handleDelim = true; + } + else { + delimStr += format[i]; + if (numStr != wxT("")) + handleNum = true; + } + + if (i == format.Length() - 1) { + if (numStr != wxT("")) + handleNum = true; + if (delimStr != wxT("")) + handleDelim = true; + } + + if (handleNum) { + bool zeropad = false; + long range = 0; + + if (numStr.Right(1) == wxT("#")) + range = (long int)mSampleRate; + else if (numStr.Right(1) != wxT("*")) { + numStr.ToLong(&range); + } + if (numStr.GetChar(0)=='0' && numStr.Length()>1) + zeropad = true; + + // Hack: always zeropad + zeropad = true; + + if (inFrac) { + int base = fracMult * range; + mFields.Add(NumericField(inFrac, base, range, zeropad)); + fracMult *= range; + numFracFields++; + } + else { + unsigned int j; + for(j=0; j 1) + delimStr = delimStr.BeforeLast('.'); + } + + if (inFrac) { + if (numFracFields == 0) { + // Should never happen + return; + } + if (handleNum && numFracFields > 1) + mFields[mFields.GetCount()-2].label = delimStr; + else + mFields[mFields.GetCount()-1].label = delimStr; + } + else { + if (numWholeFields == 0) + mPrefix = delimStr; + else { + mFields[numWholeFields-1].label = delimStr; + } + } + + if (goToFrac) + inFrac = true; + delimStr = wxT(""); + } + } + + for(i=0; i= 0) { + frames = (int)(theValue*30./1.001 + (nearest ? 0.5f : 0.0f)); + tenMins = frames/17982; + frames -= tenMins*17982; + mins = tenMins * 10; + if(frames >= 1800) { + frames -= 1800; + mins++; + addMins = frames/1798; + frames -= addMins*1798; + mins += addMins; + secs = frames/30; + frames -= secs*30; + frames += 2; + if( frames >= 30 ) { + secs++; + frames -= 30; + } + } + else { + secs = frames/30; + frames -= secs*30; + } + t_int = mins * 60 + secs; + t_frac = frames / 30.; + } + + for(i=0; i= 0) + value = (int)(t_frac * mFields[i].base); + // JKC: TODO: Find out what the range is supposed to do. + // It looks bogus too. + //if (mFields[i].range > 0) + // value = value % mFields[i].range; + } + else { + if (t_int >= 0) { + value = (t_int / mFields[i].base); + if (mFields[i].range > 0) + value = value % mFields[i].range; + } + } + + wxString field; + if (value < 0) { + for (int ii = 0; ii < mFields[i].digits; ++ii) + field += wxT("-"); + } + else + field = wxString::Format(mFields[i].formatStr, value); + mValueString += field; + mValueString += mFields[i].label; + } +} + +void NumericConverter::ControlsToValue() +{ + unsigned int i; + double t = 0.0; + + if (mFields.GetCount() > 0 && + mValueString.Mid(mFields[0].pos, 1) == wxChar('-')) { + mValue = -1; + return; + } + + for(i=0; i 0 ) { + frames += 1800; + addMins = mins - 1; + } + frames += addMins * 1798; + t_int -= mins*60; + if( mins == 0 ) //first min of a block of 10, don't drop frames 0 and 1 + frames += t_int * 30 + t_frac*30.; + else { //drop frames 0 and 1 of first seconds of these minutes + if( t_int > 0 ) + frames += 28 + (t_int-1)*30 + t_frac*30.; + else + frames += t_frac*30. -2.; + } + t = frames * 1.001 / 30.; + } + + mValue = t; +} + +void NumericConverter::SetFormatName(const wxString & formatName) +{ + SetFormatString(GetBuiltinFormat(formatName)); +} + +void NumericConverter::SetFormatString(const wxString & formatString) +{ + mFormatString = formatString; + ParseFormatString(mFormatString); + ValueToControls(); + ControlsToValue(); +} + +void NumericConverter::SetSampleRate(double sampleRate) +{ + mSampleRate = sampleRate; + ParseFormatString(mFormatString); + ValueToControls(); + ControlsToValue(); +} + +void NumericConverter::SetValue(double newValue) +{ + mValue = newValue; + ValueToControls(); + ControlsToValue(); +} + +double NumericConverter::GetValue() +{ + ControlsToValue(); + return mValue; +} + +wxString NumericConverter::GetFormatString() +{ + return mFormatString; +} + +int NumericConverter::GetFormatIndex() +{ + // int ndx = 1; + int ndx = std::min(1, GetNumBuiltins() - 1); + int i; + + for (i = 0; i < GetNumBuiltins(); i++) { + if (mFormatString == GetBuiltinFormat(i)) { + ndx = i; + break; + } + } + + return ndx; +} + +int NumericConverter::GetNumBuiltins() +{ + return mNBuiltins; +} + +wxString NumericConverter::GetBuiltinName(const int index) +{ + if (index >= 0 && index < GetNumBuiltins()) + return mBuiltinFormatStrings[index].name; + + return wxEmptyString; +} + +wxString NumericConverter::GetBuiltinFormat(const int index) +{ + if (index >= 0 && index < GetNumBuiltins()) + return mBuiltinFormatStrings[index].formatStr; + + return wxEmptyString; +} + +wxString NumericConverter::GetBuiltinFormat(const wxString &name) +{ + int ndx = mDefaultNdx; + int i; + + for (i = 0; i < GetNumBuiltins(); i++) { + if (name == GetBuiltinName(i)) { + ndx = i; + break; + } + } + + return GetBuiltinFormat(ndx); +} + +wxString NumericConverter::GetString() +{ + ValueToControls(); + + return mValueString; +} + +void NumericConverter::Increment() +{ + mFocusedDigit = mDigits.GetCount() - 1; + Adjust(1, 1); +} + +void NumericConverter::Decrement() +{ + mFocusedDigit = mDigits.GetCount() - 1; + Adjust(1, -1); +} + +void NumericConverter::Adjust(int steps, int dir) +{ + wxASSERT(dir == -1 || dir == 1); + wxASSERT(steps > 0); + if (steps < 0) + steps = -steps; + + while (steps != 0) + { + for (size_t i = 0; i < mFields.GetCount(); i++) + { + if ((mDigits[mFocusedDigit].pos >= mFields[i].pos) && + (mDigits[mFocusedDigit].pos < mFields[i].pos + mFields[i].digits)) + { //it's this field + if (!mNtscDrop) + { + ControlsToValue(); + } + else + { + mNtscDrop = false; + ControlsToValue(); + mNtscDrop = true; + } + + if (mValue < 0) + mValue = 0; + + mValue *= mScalingFactor; + + double mult = pow(10., mFields[i].digits - (mDigits[mFocusedDigit].pos - mFields[i].pos) - 1); + if (mFields[i].frac) + { + mValue += ((mult / (double)mFields[i].base) * dir); + } + else + { + mValue += ((mult * (double)mFields[i].base) * dir); + } + + if (mNtscDrop) + { + if ((mValue - (int)mValue) * 30 < 2) + { + if ((((int)mValue) % 60 == 0) && (((int)mValue) % 600 != 0)) + { + mValue = (int)mValue + (dir > 0 ? 2. : -1.) / 30.; + } + } + } + + if (mValue < 0.) + { + mValue = 0.; + } + + mValue /= mScalingFactor; + + if (!mNtscDrop) + { + ValueToControls(); + } + else + { + mNtscDrop = false; + ValueToControls(); + mNtscDrop = true; + ControlsToValue(); + } + break; + } + } + steps--; + } + + ControlsToValue(); +} + +#define ID_MENU 9800 + +// Custom events + +DEFINE_EVENT_TYPE(EVT_TIMETEXTCTRL_UPDATED) +DEFINE_EVENT_TYPE(EVT_FREQUENCYTEXTCTRL_UPDATED) +DEFINE_EVENT_TYPE(EVT_LOGFREQUENCYTEXTCTRL_UPDATED) + +BEGIN_EVENT_TABLE(NumericTextCtrl, wxControl) + EVT_ERASE_BACKGROUND(NumericTextCtrl::OnErase) + EVT_PAINT(NumericTextCtrl::OnPaint) + EVT_CONTEXT_MENU(NumericTextCtrl::OnContext) + EVT_MENU_RANGE(ID_MENU, ID_MENU+100, NumericTextCtrl::OnMenu) + EVT_MOUSE_EVENTS(NumericTextCtrl::OnMouse) + EVT_KEY_DOWN(NumericTextCtrl::OnKeyDown) + EVT_SET_FOCUS(NumericTextCtrl::OnFocus) + EVT_KILL_FOCUS(NumericTextCtrl::OnFocus) + EVT_COMMAND(wxID_ANY, EVT_CAPTURE_KEY, NumericTextCtrl::OnCaptureKey) +END_EVENT_TABLE() + +IMPLEMENT_CLASS(NumericTextCtrl, wxControl) + +NumericTextCtrl::NumericTextCtrl(NumericConverter::Type type, + wxWindow *parent, + wxWindowID id, + wxString formatName, + double timeValue, + double sampleRate, + const wxPoint &pos, + const wxSize &size, + bool autoPos): + wxControl(parent, id, pos, size, wxSUNKEN_BORDER | wxWANTS_CHARS), + NumericConverter(type, formatName, timeValue, sampleRate), + mBackgroundBitmap(NULL), + mDigitFont(NULL), + mLabelFont(NULL), + mLastField(1), + mAutoPos(autoPos) + , mType(type) +{ + + mDigitBoxW = 10; + mDigitBoxH = 16; + + mMenuEnabled = true; + mButtonWidth = 9; + + Layout(); + Fit(); + ValueToControls(); + //PRL -- would this fix the following? + //ValueToControls(); + + //mchinen - aug 15 09 - this seems to put the mValue back to zero, and do nothing else. + //ControlsToValue(); + + mScrollRemainder = 0.0; + +#if wxUSE_ACCESSIBILITY + SetLabel(wxT("")); + SetName(wxT("")); + SetAccessible(new NumericTextCtrlAx(this)); +#endif +} + +NumericTextCtrl::~NumericTextCtrl() +{ + wxCommandEvent e(EVT_RELEASE_KEYBOARD); + e.SetEventObject(this); + GetParent()->GetEventHandler()->ProcessEvent(e); + + if (mBackgroundBitmap) + delete mBackgroundBitmap; + if (mDigitFont) + delete mDigitFont; + if (mLabelFont) + delete mLabelFont; +} + +// Set the focus to the first (left-most) non-zero digit +// If all digits are zero, the right-most position is focused +void NumericTextCtrl::UpdateAutoFocus() +{ + if (!mAutoPos) + return; + + mFocusedDigit = 0; + while (mFocusedDigit < ((int)mDigits.GetCount() - 1)) { + wxChar dgt = mValueString[mDigits[mFocusedDigit].pos]; + if (dgt != '0') { + break; + } + mFocusedDigit++; + } +} + +void NumericTextCtrl::SetFormatName(const wxString & formatName) +{ + SetFormatString(GetBuiltinFormat(formatName)); +} + +void NumericTextCtrl::SetFormatString(const wxString & formatString) +{ + NumericConverter::SetFormatString(formatString); + Layout(); + Fit(); + ValueToControls(); + ControlsToValue(); + UpdateAutoFocus(); +} + +void NumericTextCtrl::SetSampleRate(double sampleRate) +{ + NumericConverter::SetSampleRate(sampleRate); + Layout(); + Fit(); + ValueToControls(); + ControlsToValue(); +} + +void NumericTextCtrl::SetValue(double newValue) +{ + NumericConverter::SetValue(newValue); + ValueToControls(); + ControlsToValue(); +} + +void NumericTextCtrl::EnableMenu(bool enable) +{ +#if wxUSE_TOOLTIPS + wxString tip(_("(Use context menu to change format.)")); + if (enable) + SetToolTip(tip); + else { + wxToolTip *tt = GetToolTip(); + if (tt && tt->GetTip() == tip) + SetToolTip(NULL); + } +#endif + mMenuEnabled = enable; + mButtonWidth = enable ? 9 : 0; + Layout(); + Fit(); +} + +bool NumericTextCtrl::Layout() +{ + unsigned int i, j; + int x, pos; + + wxMemoryDC memDC; + if (mBackgroundBitmap) { + delete mBackgroundBitmap; + mBackgroundBitmap = NULL; + } + // Placeholder bitmap so the memDC has something to reference + mBackgroundBitmap = new wxBitmap(1, 1); + memDC.SelectObject(*mBackgroundBitmap); + + mDigits.Clear(); + + mBorderLeft = 1; + mBorderTop = 1; + mBorderRight = 1; + mBorderBottom = 1; + + int fontSize = 4; + wxCoord strW, strH; + wxString exampleText = wxT("0"); + + // Keep making the font bigger until it's too big, then subtract one. + memDC.SetFont(wxFont(fontSize, wxFONTFAMILY_DEFAULT, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_NORMAL)); + memDC.GetTextExtent(exampleText, &strW, &strH); + while (strW <= mDigitBoxW && strH <= mDigitBoxH) { + fontSize++; + memDC.SetFont(wxFont(fontSize, wxFONTFAMILY_DEFAULT, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_NORMAL)); + memDC.GetTextExtent(exampleText, &strW, &strH); + } + fontSize--; + + if (mDigitFont) + delete mDigitFont; + mDigitFont = new wxFont(fontSize, wxFONTFAMILY_DEFAULT, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_NORMAL); + memDC.SetFont(*mDigitFont); + memDC.GetTextExtent(exampleText, &strW, &strH); + mDigitW = strW; + mDigitH = strH; + + // The label font should be a little smaller + fontSize--; + if (mLabelFont) + delete mLabelFont; + mLabelFont = new wxFont(fontSize, wxFONTFAMILY_DEFAULT, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_NORMAL); + + // Figure out the x-position of each field and label in the box + x = mBorderLeft; + pos = 0; + + memDC.SetFont(*mLabelFont); + memDC.GetTextExtent(mPrefix, &strW, &strH); + x += strW; + pos += mPrefix.Length(); + + for(i=0; i GetNumBuiltins()) { + event.Skip(); + return; + } + + SetFormatString(GetBuiltinFormat(id)); + + int eventType = 0; + switch (mType) { + case NumericConverter::TIME: + eventType = EVT_TIMETEXTCTRL_UPDATED; + break; + case NumericConverter::FREQUENCY: + eventType = EVT_FREQUENCYTEXTCTRL_UPDATED; + break; + case NumericConverter::LOG_FREQUENCY: + eventType = EVT_LOGFREQUENCYTEXTCTRL_UPDATED; + break; + default: + wxASSERT(false); + break; + } + + wxCommandEvent e(eventType, GetId()); + e.SetInt(id); + e.SetString(GetBuiltinName(id)); + GetParent()->GetEventHandler()->AddPendingEvent(e); +} + +void NumericTextCtrl::OnContext(wxContextMenuEvent &event) +{ + wxMenu menu; + int i; + + if (!mMenuEnabled) { + event.Skip(); + return; + } + + SetFocus(); + + for(i=0; i= mWidth) { + wxContextMenuEvent e; + OnContext(e); + } + else if (event.LeftDown()) { + SetFocus(); + + int bestDist = 9999; + unsigned int i; + + mFocusedDigit = 0; + for(i=0; i 0 ? (double)event.m_wheelDelta : 120.0) + + mScrollRemainder; + mScrollRemainder = steps - floor(steps); + steps = floor(steps); + + Adjust((int)fabs(steps), steps < 0.0 ? -1 : 1); + Updated(); + } +} + +void NumericTextCtrl::OnFocus(wxFocusEvent &event) +{ + wxCommandEvent e(EVT_CAPTURE_KEYBOARD); + + if (event.GetEventType() == wxEVT_KILL_FOCUS) { + e.SetEventType(EVT_RELEASE_KEYBOARD); + } + e.SetEventObject(this); + GetParent()->GetEventHandler()->ProcessEvent(e); + + Refresh(false); +} + +void NumericTextCtrl::OnCaptureKey(wxCommandEvent &event) +{ + wxKeyEvent *kevent = (wxKeyEvent *)event.GetEventObject(); + int keyCode = kevent->GetKeyCode(); + + // Convert numeric keypad entries. + if ((keyCode >= WXK_NUMPAD0) && (keyCode <= WXK_NUMPAD9)) keyCode -= WXK_NUMPAD0 - '0'; + + switch (keyCode) + { + case WXK_BACK: + case WXK_LEFT: + case WXK_RIGHT: + case WXK_HOME: + case WXK_END: + case WXK_UP: + case WXK_DOWN: + case WXK_TAB: + case WXK_RETURN: + case WXK_NUMPAD_ENTER: + return; + + default: + if (keyCode >= '0' && keyCode <= '9') + return; + } + + event.Skip(); + + return; +} + +void NumericTextCtrl::OnKeyUp(wxKeyEvent &event) +{ + int keyCode = event.GetKeyCode(); + + event.Skip(true); + + if ((keyCode >= WXK_NUMPAD0) && (keyCode <= WXK_NUMPAD9)) + keyCode -= WXK_NUMPAD0 - '0'; + + if ((keyCode >= '0' && keyCode <= '9') || + (keyCode == WXK_BACK) || + (keyCode == WXK_UP) || + (keyCode == WXK_DOWN)) { + Updated(true); + } +} + +void NumericTextCtrl::OnKeyDown(wxKeyEvent &event) +{ + if (mDigits.GetCount() == 0) + { + mFocusedDigit = 0; + return; + } + + event.Skip(); + + int keyCode = event.GetKeyCode(); + int digit = mFocusedDigit; + + if (mFocusedDigit < 0) + mFocusedDigit = 0; + if (mFocusedDigit >= (int)mDigits.GetCount()) + mFocusedDigit = mDigits.GetCount()-1; + + // Convert numeric keypad entries. + if ((keyCode >= WXK_NUMPAD0) && (keyCode <= WXK_NUMPAD9)) keyCode -= WXK_NUMPAD0 - '0'; + + wxChar *theDigit = &mValueString[mDigits[mFocusedDigit].pos]; + if (keyCode >= '0' && keyCode <= '9') { + if (*theDigit == wxChar('-')) { + mValue = 0; + ValueToControls(); + // Beware relocation of the string + theDigit = &mValueString[mDigits[mFocusedDigit].pos]; + } + *theDigit = wxChar(keyCode); + ControlsToValue(); + ValueToControls(); + mFocusedDigit = (mFocusedDigit + 1) % (mDigits.GetCount()); + Updated(); + } + + else if (keyCode == WXK_BACK) { + // Moves left, replaces that char with '0', stays there... + mFocusedDigit--; + mFocusedDigit += mDigits.GetCount(); + mFocusedDigit %= mDigits.GetCount(); + theDigit = &mValueString[mDigits[mFocusedDigit].pos]; + if (*theDigit != wxChar('-')) + *theDigit = '0'; + ControlsToValue(); + ValueToControls(); + Updated(); + } + + else if (keyCode == WXK_LEFT) { + mFocusedDigit--; + mFocusedDigit += mDigits.GetCount(); + mFocusedDigit %= mDigits.GetCount(); + Refresh(); + } + + else if (keyCode == WXK_RIGHT) { + mFocusedDigit++; + mFocusedDigit %= mDigits.GetCount(); + Refresh(); + } + + else if (keyCode == WXK_HOME) { + mFocusedDigit = 0; + Refresh(); + } + + else if (keyCode == WXK_END) { + mFocusedDigit = mDigits.GetCount() - 1; + Refresh(); + } + + else if (keyCode == WXK_UP) { + Adjust(1, 1); + Updated(); + } + + else if (keyCode == WXK_DOWN) { + Adjust(1, -1); + Updated(); + } + + else if (keyCode == WXK_TAB) { + wxWindow *parent = GetParent(); + wxNavigationKeyEvent nevent; + nevent.SetWindowChange(event.ControlDown()); + nevent.SetDirection(!event.ShiftDown()); + nevent.SetEventObject(parent); + nevent.SetCurrentFocus(parent); + GetParent()->GetEventHandler()->ProcessEvent(nevent); + event.Skip(false); + } + + else if (keyCode == WXK_RETURN || keyCode == WXK_NUMPAD_ENTER) { + wxTopLevelWindow *tlw = wxDynamicCast(wxGetTopLevelParent(this), wxTopLevelWindow); + wxWindow *def = tlw->GetDefaultItem(); + if (def && def->IsEnabled()) { + wxCommandEvent cevent(wxEVT_COMMAND_BUTTON_CLICKED, + def->GetId()); + GetParent()->GetEventHandler()->ProcessEvent(cevent); + event.Skip(false); + } + } + + else { + event.Skip(); + return; + } + + if (digit != mFocusedDigit) { + SetFieldFocus(mFocusedDigit); + } +} + +void NumericTextCtrl::SetFieldFocus(int digit) +{ +#if wxUSE_ACCESSIBILITY + if (mDigits.GetCount() == 0) + { + mFocusedDigit = 0; + return; + } + mFocusedDigit = digit; + mLastField = mDigits[mFocusedDigit].field + 1; + + // This looks strange (and it is), but it was the only way I could come + // up with that allowed Jaws, Window-Eyes, and NVDA to read the control + // somewhat the same. See NumericTextCtrlAx below for even more odd looking + // hackery. + // + // If you change SetFieldFocus(), Updated(), or NumericTextCtrlAx, make sure + // you test with Jaws, Window-Eyes, and NVDA. + GetAccessible()->NotifyEvent(wxACC_EVENT_OBJECT_FOCUS, + this, + wxOBJID_CLIENT, + 0); + GetAccessible()->NotifyEvent(wxACC_EVENT_OBJECT_FOCUS, + this, + wxOBJID_CLIENT, + mFocusedDigit + 1); +#endif +} + +void NumericTextCtrl::Updated(bool keyup /* = false */) +{ + wxCommandEvent event(wxEVT_COMMAND_TEXT_UPDATED, GetId()); + + // This will give listeners the ability to do tasks when the + // update has been completed, like when the UP ARROW has been + // held down and is finally released. + event.SetInt(keyup); + event.SetEventObject(this); + GetEventHandler()->ProcessEvent(event); + +#if wxUSE_ACCESSIBILITY + if (!keyup) { + GetAccessible()->NotifyEvent(wxACC_EVENT_OBJECT_NAMECHANGE, + this, + wxOBJID_CLIENT, + mFocusedDigit + 1); + SetFieldFocus(mFocusedDigit); + } +#endif +} + +void NumericTextCtrl::ValueToControls() +{ + NumericConverter::ValueToControls(mValue); + Refresh(false); +} + + +void NumericTextCtrl::ControlsToValue() +{ + NumericConverter::ControlsToValue(); +} + +#if wxUSE_ACCESSIBILITY + +NumericTextCtrlAx::NumericTextCtrlAx(NumericTextCtrl *ctrl) +: wxWindowAccessible(ctrl) +{ + mCtrl = ctrl; + mLastField = -1; + mLastDigit = -1; +} + +NumericTextCtrlAx::~NumericTextCtrlAx() +{ +} + +// Performs the default action. childId is 0 (the action for this object) +// or > 0 (the action for a child). +// Return wxACC_NOT_SUPPORTED if there is no default action for this +// window (e.g. an edit control). +wxAccStatus NumericTextCtrlAx::DoDefaultAction(int WXUNUSED(childId)) +{ + return wxACC_NOT_SUPPORTED; +} + +// Retrieves the address of an IDispatch interface for the specified child. +// All objects must support this property. +wxAccStatus NumericTextCtrlAx::GetChild(int childId, wxAccessible **child) +{ + if (childId == wxACC_SELF) { + *child = this; + } + else { + *child = NULL; + } + + return wxACC_OK; +} + +// Gets the number of children. +wxAccStatus NumericTextCtrlAx::GetChildCount(int *childCount) +{ + *childCount = mCtrl->mDigits.GetCount(); + + return wxACC_OK; +} + +// Gets the default action for this object (0) or > 0 (the action for +// a child). Return wxACC_OK even if there is no action. actionName +// is the action, or the empty string if there is no action. The +// retrieved string describes the action that is performed on an +// object, not what the object does as a result. For example, a +// toolbar button that prints a document has a default action of +// "Press" rather than "Prints the current document." +wxAccStatus NumericTextCtrlAx::GetDefaultAction(int WXUNUSED(childId), wxString *actionName) +{ + actionName->Clear(); + + return wxACC_OK; +} + +// Returns the description for this object or a child. +wxAccStatus NumericTextCtrlAx::GetDescription(int WXUNUSED(childId), wxString *description) +{ + description->Clear(); + + return wxACC_OK; +} + +// Gets the window with the keyboard focus. +// If childId is 0 and child is NULL, no object in +// this subhierarchy has the focus. +// If this object has the focus, child should be 'this'. +wxAccStatus NumericTextCtrlAx::GetFocus(int *childId, wxAccessible **child) +{ + *childId = mCtrl->GetFocusedDigit(); + *child = this; + + return wxACC_OK; +} + +// Returns help text for this object or a child, similar to tooltip text. +wxAccStatus NumericTextCtrlAx::GetHelpText(int WXUNUSED(childId), wxString *helpText) +{ +// removed help text, as on balance it's more of an irritation than useful +#if 0 // was #if wxUSE_TOOLTIPS + wxToolTip *pTip = mCtrl->GetToolTip(); + if (pTip) { + *helpText = pTip->GetTip(); + } + + return wxACC_OK; +#else + helpText->Clear(); + + return wxACC_NOT_SUPPORTED; +#endif +} + +// Returns the keyboard shortcut for this object or child. +// Return e.g. ALT+K +wxAccStatus NumericTextCtrlAx::GetKeyboardShortcut(int WXUNUSED(childId), wxString *shortcut) +{ + shortcut->Clear(); + + return wxACC_OK; +} + +// Returns the rectangle for this object (id = 0) or a child element (id > 0). +// rect is in screen coordinates. +wxAccStatus NumericTextCtrlAx::GetLocation(wxRect & rect, int elementId) +{ + if ((elementId != wxACC_SELF) && + // We subtract 1, below, and need to avoid neg index to mDigits. + (elementId > 0)) + { +// rect.x += mCtrl->mFields[elementId - 1].fieldX; +// rect.width = mCtrl->mFields[elementId - 1].fieldW; + rect = mCtrl->mDigits[elementId - 1].digitBox; + rect.SetPosition(mCtrl->ClientToScreen(rect.GetPosition())); + } + else + { + rect = mCtrl->GetRect(); + rect.SetPosition(mCtrl->GetParent()->ClientToScreen(rect.GetPosition())); + } + + return wxACC_OK; +} + +// Gets the name of the specified object. +wxAccStatus NumericTextCtrlAx::GetName(int childId, wxString *name) +{ + // Slightly messy trick to save us some prefixing. + NumericFieldArray & mFields = mCtrl->mFields; + + wxString value = mCtrl->GetString(); + int field = mCtrl->GetFocusedField(); + + // Return the entire string including the control label + // when the requested child ID is wxACC_SELF. (Mainly when + // the control gets the focus.) + if ((childId == wxACC_SELF) || + // We subtract 1 from childId in the other cases below, and + // need to avoid neg index to mDigits, so funnel into this clause. + (childId < 1)) + { + *name = mCtrl->GetName(); + if (name->IsEmpty()) { + *name = mCtrl->GetLabel(); + } + + *name += wxT(" ") + + mCtrl->GetString(); + } + // The user has moved from one field of the time to another so + // report the value of the field and the field's label. + else if (mLastField != field) { + wxString label = mFields[field - 1].label; + int cnt = mFields.GetCount(); + wxString decimal = wxLocale::GetInfo(wxLOCALE_DECIMAL_POINT, wxLOCALE_CAT_NUMBER); + + // If the new field is the last field, then check it to see if + // it represents fractions of a second. + // PRL: click a digit of the control and use left and right arrow keys + // to exercise this code + const bool isTime = (mCtrl->mType == NumericTextCtrl::TIME); + if (field > 1 && field == cnt) { + if (mFields[field - 2].label == decimal) { + int digits = mFields[field - 1].digits; + if (digits == 2) { + if (isTime) + label = _("centiseconds"); + else { + // other units + // PRL: does this create translation problems? + label = _("hundredths of "); + label += mFields[field - 1].label; + } + } + else if (digits == 3) { + if (isTime) + label = _("milliseconds"); + else { + // other units + // PRL: does this create translation problems? + label = _("thousandths of "); + label += mFields[field - 1].label; + } + } + } + } + // If the field following this one represents fractions of a + // second then use that label instead of the decimal point. + else if (label == decimal && field == cnt - 1) { + label = mFields[field].label; + } + + *name = mFields[field - 1].str + + wxT(" ") + + label + + wxT(", ") + // comma inserts a slight pause + mCtrl->GetString().at(mCtrl->mDigits[childId - 1].pos); + mLastField = field; + mLastDigit = childId; + } + // The user has moved from one digit to another within a field so + // just report the digit under the cursor. + else if (mLastDigit != childId) { + *name = mCtrl->GetString().at(mCtrl->mDigits[childId - 1].pos); + mLastDigit = childId; + } + // The user has updated the value of a field, so report the field's + // value only. + else if (field > 0) + { + *name = mFields[field - 1].str; + } + + return wxACC_OK; +} + +// Returns a role constant. +wxAccStatus NumericTextCtrlAx::GetRole(int WXUNUSED(childId), wxAccRole *role) +{ + *role = wxROLE_SYSTEM_STATICTEXT; + return wxACC_OK; +} + +// Gets a variant representing the selected children +// of this object. +// Acceptable values: +// - a null variant (IsNull() returns TRUE) +// - a list variant (GetType() == wxT("list")) +// - an integer representing the selected child element, +// or 0 if this object is selected (GetType() == wxT("long")) +// - a "void*" pointer to a wxAccessible child object +wxAccStatus NumericTextCtrlAx::GetSelections(wxVariant * WXUNUSED(selections)) +{ + return wxACC_NOT_IMPLEMENTED; +} + +// Returns a state constant. +wxAccStatus NumericTextCtrlAx::GetState(int WXUNUSED(childId), long *state) +{ + *state = wxACC_STATE_SYSTEM_FOCUSABLE; + *state |= (mCtrl == wxWindow::FindFocus() ? wxACC_STATE_SYSTEM_FOCUSED : 0); + + return wxACC_OK; +} + +// Returns a localized string representing the value for the object +// or child. +wxAccStatus NumericTextCtrlAx::GetValue(int WXUNUSED(childId), wxString * WXUNUSED(strValue)) +{ + return wxACC_NOT_IMPLEMENTED; +} + +#endif + diff --git a/src/widgets/NumericTextCtrl.h b/src/widgets/NumericTextCtrl.h new file mode 100644 index 000000000..53d1c81b8 --- /dev/null +++ b/src/widgets/NumericTextCtrl.h @@ -0,0 +1,286 @@ +/********************************************************************** + + Audacity: A Digital Audio Editor + + NumericTextCtrl.h + + Dominic Mazzoni + + See NumericTextCtrl.cpp for documentation on how to use the + format string to specify how a NumericTextCtrl's fields are + laid out. + +**********************************************************************/ + +#ifndef __AUDACITY_TIME_TEXT_CTRL__ +#define __AUDACITY_TIME_TEXT_CTRL__ + +#include +#include +#include +#include +#include +#include +#include + +#if wxUSE_ACCESSIBILITY +#include +#endif + +// One event type for each type of control. Event is raised when a control +// changes its format. Owners of controls of the same type can listen and +// update their formats to agree. +DECLARE_EXPORTED_EVENT_TYPE(AUDACITY_DLL_API, EVT_TIMETEXTCTRL_UPDATED, -1); +DECLARE_EXPORTED_EVENT_TYPE(AUDACITY_DLL_API, EVT_FREQUENCYTEXTCTRL_UPDATED, -1); +DECLARE_EXPORTED_EVENT_TYPE(AUDACITY_DLL_API, EVT_LOGFREQUENCYTEXTCTRL_UPDATED, + -1); + +/** \brief struct to hold a formatting control string and it's user facing name + * Used in an array to hold the built-in time formats that are always available + * to the user */ +struct BuiltinFormatString; + +class NumericField; +WX_DECLARE_OBJARRAY(NumericField, NumericFieldArray); + +class DigitInfo; +WX_DECLARE_OBJARRAY(DigitInfo, DigitInfoArray); + +class NumericConverter +{ +public: + + enum Type { + TIME, + FREQUENCY, + LOG_FREQUENCY, + }; + + NumericConverter(Type type, + const wxString & formatName = wxEmptyString, + double value = 0.0f, + double sampleRate = 1.0f /* to prevent div by 0 */); + + virtual void ValueToControls(); + virtual void ValueToControls(double rawValue, bool nearest = true); + virtual void ControlsToValue(); + virtual void ParseFormatString(const wxString & format); + + void PrintDebugInfo(); + void SetFormatName(const wxString & formatName); + void SetFormatString(const wxString & formatString); + void SetSampleRate(double sampleRate); + void SetValue(double newValue); + double GetValue(); + + wxString GetString(); + + wxString GetFormatString(); + int GetFormatIndex(); + + int GetNumBuiltins(); + wxString GetBuiltinName(const int index); + wxString GetBuiltinFormat(const int index); + wxString GetBuiltinFormat(const wxString & name); + + // Adjust the value by the number "steps" in the active format. + // Increment if "dir" is 1, decrement if "dir" is -1. + void Adjust(int steps, int dir); + + void Increment(); + void Decrement(); + +protected: + Type mType; + + double mValue; + + wxString mFormatString; + + NumericFieldArray mFields; + wxString mPrefix; + wxString mValueTemplate; + wxString mValueMask; + wxString mValueString; + + double mScalingFactor; + double mSampleRate; + bool mNtscDrop; + + int mFocusedDigit; + DigitInfoArray mDigits; + + const BuiltinFormatString *mBuiltinFormatStrings; + int mNBuiltins; + int mDefaultNdx; +}; + +class NumericTextCtrl: public wxControl, public NumericConverter +{ + friend class NumericTextCtrlAx; + + public: + DECLARE_DYNAMIC_CLASS(NumericTextCtrl); + + NumericTextCtrl(NumericConverter::Type type, + wxWindow *parent, + wxWindowID id, + wxString formatName = wxEmptyString, + double value = 0.0, + double sampleRate = 44100, + const wxPoint &pos = wxDefaultPosition, + const wxSize &size = wxDefaultSize, + bool autoPos = false); + + virtual ~NumericTextCtrl(); + + virtual bool Layout(); + virtual void Fit(); + + void SetSampleRate(double sampleRate); + void SetValue(double newValue); + void SetFormatString(const wxString & formatString); + void SetFormatName(const wxString & formatName); + + void SetFieldFocus(int digit); + + void EnableMenu(bool enable = true); + + int GetFocusedField() { return mLastField; } + int GetFocusedDigit() { return mFocusedDigit; } + +private: + + void OnCaptureKey(wxCommandEvent &event); + void OnKeyDown(wxKeyEvent &event); + void OnKeyUp(wxKeyEvent &event); + void OnMouse(wxMouseEvent &event); + void OnErase(wxEraseEvent &event); + void OnPaint(wxPaintEvent &event); + void OnFocus(wxFocusEvent &event); + void OnContext(wxContextMenuEvent &event); + void OnMenu(wxCommandEvent &event); + + void ValueToControls(); + void ControlsToValue(); + + // If autoPos was enabled, focus the first non-zero digit + void UpdateAutoFocus(); + + void Updated(bool keyup = false); + +private: + + bool mMenuEnabled; + + wxBitmap *mBackgroundBitmap; + + wxFont *mDigitFont; + wxFont *mLabelFont; + int mDigitBoxW; + int mDigitBoxH; + int mDigitW; + int mDigitH; + int mBorderLeft; + int mBorderTop; + int mBorderRight; + int mBorderBottom; + int mWidth; + int mHeight; + int mButtonWidth; + + int mLastField; + + // If true, the focus will be set to the first non-zero digit + bool mAutoPos; + + // Keeps track of extra fractional scrollwheel steps + double mScrollRemainder; + + NumericConverter::Type mType; + + DECLARE_EVENT_TABLE() +}; + +#if wxUSE_ACCESSIBILITY + +class NumericTextCtrlAx: public wxWindowAccessible +{ +public: + NumericTextCtrlAx(NumericTextCtrl * ctrl); + + virtual ~ NumericTextCtrlAx(); + + // Performs the default action. childId is 0 (the action for this object) + // or > 0 (the action for a child). + // Return wxACC_NOT_SUPPORTED if there is no default action for this + // window (e.g. an edit control). + virtual wxAccStatus DoDefaultAction(int childId); + + // Retrieves the address of an IDispatch interface for the specified child. + // All objects must support this property. + virtual wxAccStatus GetChild(int childId, wxAccessible **child); + + // Gets the number of children. + virtual wxAccStatus GetChildCount(int *childCount); + + // Gets the default action for this object (0) or > 0 (the action for a child). + // Return wxACC_OK even if there is no action. actionName is the action, or the empty + // string if there is no action. + // The retrieved string describes the action that is performed on an object, + // not what the object does as a result. For example, a toolbar button that prints + // a document has a default action of "Press" rather than "Prints the current document." + virtual wxAccStatus GetDefaultAction(int childId, wxString *actionName); + + // Returns the description for this object or a child. + virtual wxAccStatus GetDescription(int childId, wxString *description); + + // Gets the window with the keyboard focus. + // If childId is 0 and child is NULL, no object in + // this subhierarchy has the focus. + // If this object has the focus, child should be 'this'. + virtual wxAccStatus GetFocus(int *childId, wxAccessible **child); + + // Returns help text for this object or a child, similar to tooltip text. + virtual wxAccStatus GetHelpText(int childId, wxString *helpText); + + // Returns the keyboard shortcut for this object or child. + // Return e.g. ALT+K + virtual wxAccStatus GetKeyboardShortcut(int childId, wxString *shortcut); + + // Returns the rectangle for this object (id = 0) or a child element (id > 0). + // rect is in screen coordinates. + virtual wxAccStatus GetLocation(wxRect & rect, int elementId); + + // Gets the name of the specified object. + virtual wxAccStatus GetName(int childId, wxString *name); + + // Returns a role constant. + virtual wxAccStatus GetRole(int childId, wxAccRole *role); + + // Gets a variant representing the selected children + // of this object. + // Acceptable values: + // - a null variant (IsNull() returns TRUE) + // - a list variant (GetType() == wxT("list")) + // - an integer representing the selected child element, + // or 0 if this object is selected (GetType() == wxT("long")) + // - a "void*" pointer to a wxAccessible child object + virtual wxAccStatus GetSelections(wxVariant *selections); + + // Returns a state constant. + virtual wxAccStatus GetState(int childId, long *state); + + // Returns a localized string representing the value for the object + // or child. + virtual wxAccStatus GetValue(int childId, wxString *strValue); + +private: + NumericTextCtrl *mCtrl; + int mLastField; + int mLastDigit; +}; + +#endif // wxUSE_ACCESSIBILITY + +#endif // __AUDACITY_TIME_TEXT_CTRL__ diff --git a/src/widgets/TimeTextCtrl.cpp b/src/widgets/TimeTextCtrl.cpp index bc709deed..e69de29bb 100644 --- a/src/widgets/TimeTextCtrl.cpp +++ b/src/widgets/TimeTextCtrl.cpp @@ -1,1767 +0,0 @@ -/********************************************************************** - - Audacity: A Digital Audio Editor - - TimeTextCtrl.cpp - - Dominic Mazzoni - - -********************************************************************//** - -\class TimeTextCtrl -\brief TimeTextCtrl provides the advanced time formatting control used -in the status bar of Audacity. - - The TimeTextCtrl makes use of a format string to specify the - exact way that a single time value is split into several fields, - such as the hh:mm:ss format. The advantage of this format string - is that it is very small and compact, but human-readable and - somewhat intuitive, so that it's easy to add new time layouts - in the future. It's also designed to make it easier to add - i18n support, since the way that times are displayed in different - languages could conceivably vary a lot. - - The time to be formatted is expressed in seconds, so the format - string specifies the relationship of each field to the number of - seconds. - - Let's start by considering an example: here's the format string - that prints an integer number of seconds in the hour minute - second h:m:s format: - - *:60:60 - - The "*" is a wildcard, saying that the leftmost field can contain - numbers of arbitrary magnitude. The next character, ':', since it - is not a digit or a wildcard, is interpreted as a delimiter, and - will be displayed between those fields. The next number, 60, - indicates that the range of the next field (minutes) is 60. - Then there's another ':' delimiter, and finally the last field - (seconds) is 60. So, if you give it a number like 3758 (note - format it as: - - 3758 seconds, "*:60:60" -> "1:2:38" - - Note that 3758 = 1*60*60 + 2*60 + 38. - - When TimeTextCtrl formats an integer, you can think of its process - as working from right to left. Given the value "3758", it fills - in the seconds by dividing by 60, sticking the remainder in the - seconds field and then passing the quotient to the next field to - the left. - - In order to format a field with leading zeros, simply add a leading - zero to that field, like this: - - 3758 seconds, "*:060:060" -> "1:02:38" - - In order to format fractions, simply include a field delimiter - ending with a decimal point. If the delimiter is simply '.' with - nothing else, then the '.' is actually displayed. Otherwise the - '.' is dropped, and the other characters in the delimiter are - displayed instead. - - Here's how we'd display hours, minutes, and seconds with three - decimal places after the seconds: - - 3758.5 seconds, "*:060:060.01000" -> "1:02:38.500" - - Similarly, here's how we'd display the fractional part of - seconds as film frames (24 per second) instead of milliseconds: - - 3758.5 seconds, "*:060:060 and .24 frames" -> "1:02:38 and 12 frames" - - Note that the decimal '.' is associated with the delimeter, not - with the 24. - - Additionally, the special character '#' can be used in place of a number - to represent the current sample rate. Use '0#' to add leading - zeros to that field. For example: - - 3758.5 seconds, "*:060:060+.#samples" -> "1:02:38+22050samples" - - (Almost) Finally, there is a rule that allows you to change the units into - something other than seconds. To do this, put a "|" character on - the far right, followed by a number specifying the scaling factor. - As an exception to previous rules, decimal points are allowed - in the final scaling factor - the period is not interpreted as it - would be before the "|" character. (This is fine, because all - previous fields must be integers to make sense.) Anyway, if you - include a scaling factor after a "|", the time will be - multiplied by this factor before it is formatted. For example, to - express the current time in NTSC frames (~29.97 fps), you could - use the following formatting: - - 3758.5 seconds, "*.01000 frames|29.97002997" -> "112642.358 frames" - - Finally there is a further special character that can be used after a "|" - and that is "N". This applies special rule for NTSC drop-frame timecode. - - Summary of format string rules: - - - The characters '0-9', '*', and '#' are numeric. Any sequence of - these characters is treated as defining a new field by specifying - its range. All other characters become delimiters between fields. - (The one exception is that '.' is treated as numeric after the - optional '|'.) - - A field with a range of '*', which only makes sense as the - leftmost field, means the field should display as large a number - as necessary. (Note: this no longer makes sense here and applies to a - previous version). - - The character '#' represents the current sample rate. - - If a field specifier beings with a leading zero, it will be formatted - with leading zeros, too - enough to display the maximum value - that field can display. So the number 7 in a field specified - as '01000' would be formatted as '007'. Bond. James Bond. - - Any non-numeric characters before the first field are treated - as a prefix, and will be displayed to the left of the first field. - - A delimiter ending in '.' is treated specially. All fields after - this delimeter are fractional fields, after the decimal point. - - The '|' character is treated as a special delimiter. The number - to the right of this character (which is allowed to contain a - decimal point) is treated as a scaling factor. The time is - multiplied by this factor before multiplying. - - The special character 'N' after '|' is only used for NTSC drop-frame. - -*******************************************************************//** - -\class TimeTextCtrlAx -\brief TimeTextCtrlAx gives the TimeTextCtrl Accessibility. - -*******************************************************************//** - -\class TimeConverter -\brief TimeConverter has all the time conversion and snapping -functionality that used to live in TimeTextCtrl. The idea is to have -a GUI-less class which can do the conversions, so that we can use it -in sanpping without having a window created each time. - -*//****************************************************************//** - -\class BuiltinFormatString -\brief BuiltinFormatString is a structure used in the TimeTextCtrl -and holds both a descriptive name for the string format and a -printf inspired style format string, optimised for displaying time in -different formats. - -*//****************************************************************//** - -\class TimeField -\brief TimeField is a class used in the TimeTextCtrl - -*//****************************************************************//** - -\class DigitInfo -\brief DigitInfo is a class used in the TimeTextCtrl - -**********************************************************************/ - - -#include "../Audacity.h" -#include "../AudacityApp.h" -#include "TimeTextCtrl.h" -#include "../Sequence.h" // for sampleCount -#include "../Theme.h" -#include "../AllThemeResources.h" -#include "../AColor.h" - -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -// -// ---------------------------------------------------------------------------- -// TimeField Class -// ---------------------------------------------------------------------------- -// -class TimeField -{ -public: - TimeField(bool _frac, int _base, int _range, bool _zeropad) - { - frac = _frac; - base = _base; - range = _range; - zeropad = _zeropad; - digits = 0; - } - void CreateDigitFormatStr() - { - if (range > 1) - digits = (int)ceil(log10(range-1.0)); - else - digits = 5; // hack: default - if (zeropad && range>1) - formatStr.Printf(wxT("%%0%dd"), digits); // ex. "%03d" if digits is 3 - else { - formatStr.Printf(wxT("%%0%dd"), digits); - } - } - bool frac; // is it a fractional field - int base; // divide by this (multiply, after decimal point) - int range; // then take modulo this - int digits; - int pos; // Index of this field in the ValueString - int fieldX; // x-position of the field on-screen - int fieldW; // width of the field on-screen - int labelX; // x-position of the label on-screen - bool zeropad; - wxString label; - wxString formatStr; - wxString str; -}; - -// -// ---------------------------------------------------------------------------- -// DigitInfo Class -// ---------------------------------------------------------------------------- -// -class DigitInfo -{ -public: - DigitInfo(int _field, int _index, int _pos, wxRect _box) - { - field = _field; - index = _index; - pos = _pos; - digitBox = _box; - } - int field; // Which field - int index; // Index of this digit within the field - int pos; // Position in the ValueString - wxRect digitBox; -}; - -#include -WX_DEFINE_OBJARRAY(TimeFieldArray); -WX_DEFINE_OBJARRAY(DigitInfoArray); - -// -// ---------------------------------------------------------------------------- -// TimeConverter Class -// ---------------------------------------------------------------------------- -// -TimeConverter::TimeConverter(const wxString & formatName, - double timeValue, - double sampleRate) -{ - /* i18n-hint: Name of time display format that shows time in seconds */ - BuiltinFormatStrings[0].name = _("seconds"); - /* i18n-hint: Format string for displaying time in seconds. Change the comma - * in the middle to the 1000s separator for your locale, and the 'seconds' - * on the end to the word for seconds. Don't change the numbers. */ - BuiltinFormatStrings[0].formatStr = _("01000,01000 seconds"); - /* i18n-hint: Name of time display format that shows time in hours, minutes - * and seconds */ - BuiltinFormatStrings[1].name = _("hh:mm:ss"); - /* i18n-hint: Format string for displaying time in hours, minutes and - * seconds. Change the 'h' to the abbreviation for hours, 'm' to the - * abbreviation for minutes and 's' to the abbreviation for seconds. Don't - * change the numbers unless there aren't 60 seconds in a minute in your - * locale */ - BuiltinFormatStrings[1].formatStr = _("0100 h 060 m 060 s"); - /* i18n-hint: Name of time display format that shows time in days, hours, - * minutes and seconds */ - BuiltinFormatStrings[2].name = _("dd:hh:mm:ss"); - /* i18n-hint: Format string for displaying time in days, hours, minutes and - * seconds. Change the 'days' to the word for days, 'h' to the abbreviation - * for hours, 'm' to the abbreviation for minutes and 's' to the - * abbreviation for seconds. Don't change the numbers unless there aren't - * 24 hours in a day in your locale */ - BuiltinFormatStrings[2].formatStr = _("0100 days 024 h 060 m 060 s"); - /* i18n-hint: Name of time display format that shows time in hours, - * minutes, seconds and hundredths of a second (1/100 second) */ - BuiltinFormatStrings[3].name = _("hh:mm:ss + hundredths"); - /* i18n-hint: Format string for displaying time in hours, minutes, seconds - * and hundredths of a second. Change the 'h' to the abbreviation for hours, - * 'm' to the abbreviation for minutes and 's' to the abbreviation for seconds (the - * hundredths are shown as decimal seconds) . Don't change the numbers - * unless there aren't 60 minutes in an hour in your locale */ - BuiltinFormatStrings[3].formatStr =_("0100 h 060 m 060.0100 s"); - /* i18n-hint: Name of time display format that shows time in hours, - * minutes, seconds and milliseconds (1/1000 second) */ - BuiltinFormatStrings[4].name = _("hh:mm:ss + milliseconds"); - /* i18n-hint: Format string for displaying time in hours, minutes, seconds - * and milliseconds. Change the 'h' to the abbreviation for hours, 'm' to the - * abbreviation for minutes and 's' to the abbreviation for seconds (the - * milliseconds are shown as decimal seconds) . Don't change the numbers - * unless there aren't 60 minutes in an hour in your locale */ - BuiltinFormatStrings[4].formatStr =_("0100 h 060 m 060.01000 s"); - /* i18n-hint: Name of time display format that shows time in hours, - * minutes, seconds and samples (at the current project sample rate) */ - BuiltinFormatStrings[5].name = _("hh:mm:ss + samples"); - /* i18n-hint: Format string for displaying time in hours, minutes, seconds - * and samples. Change the 'h' to the abbreviation for hours, 'm' to the - * abbreviation for minutes, 's' to the abbreviation for seconds and - * translate samples . Don't change the numbers - * unless there aren't 60 seconds in a minute in your locale */ - BuiltinFormatStrings[5].formatStr = _("0100 h 060 m 060 s+.# samples"); - /* i18n-hint: Name of time display format that shows time in samples (at the - * current project sample rate). For example the number of a sample at 1 - * second into a recording at 44.1KHz would be 44,100. - */ - BuiltinFormatStrings[6].name = _("samples"); - /* i18n-hint: Format string for displaying time in samples (lots of samples). - * Change the ',' to the 1000s separator for your locale, and translate - * samples. If 1000s aren't a base multiple for your number system, then you - * can change the numbers to an appropriate one, and put a 0 on the front */ - BuiltinFormatStrings[6].formatStr = _("01000,01000,01000 samples|#"); - /* i18n-hint: Name of time display format that shows time in hours, minutes, - * seconds and frames at 24 frames per second (commonly used for films) */ - BuiltinFormatStrings[7].name = _("hh:mm:ss + film frames (24 fps)"); - /* i18n-hint: Format string for displaying time in hours, minutes, seconds - * and frames at 24 frames per second. Change the 'h' to the abbreviation - * for hours, 'm' to the abbreviation for minutes, 's' to the abbreviation - * for seconds and translate 'frames' . Don't change the numbers - * unless there aren't 60 seconds in a minute in your locale */ - BuiltinFormatStrings[7].formatStr = _("0100 h 060 m 060 s+.24 frames"); - /* i18n-hint: Name of time display format that shows time in frames (lots of - * frames) at 24 frames per second (commonly used for films) */ - BuiltinFormatStrings[8].name = _("film frames (24 fps)"); - /* i18n-hint: Format string for displaying time in frames at 24 frames per - * second. Translate 'frames' and leave the rest alone */ - BuiltinFormatStrings[8].formatStr = _("01000,01000 frames|24"); - /* i18n-hint: Name of time display format that shows time in hours, minutes, - * seconds and frames at NTSC TV drop-frame rate (used for American / - * Japanese TV, and very odd) */ - BuiltinFormatStrings[9].name = _("hh:mm:ss + NTSC drop frames"); - /* i18n-hint: Format string for displaying time in hours, minutes, seconds - * and frames with NTSC drop frames. Change the 'h' to the abbreviation - * for hours, 'm' to the abbreviation for minutes, 's' to the abbreviation - * for seconds and translate 'frames'. Leave the |N alone, it's important! */ - BuiltinFormatStrings[9].formatStr = _("0100 h 060 m 060 s+.30 frames|N"); - /* i18n-hint: Name of time display format that shows time in hours, minutes, - * seconds and frames at NTSC TV non-drop-frame rate (used for American / - * Japanese TV, and doesn't quite match wall time */ - BuiltinFormatStrings[10].name = _("hh:mm:ss + NTSC non-drop frames"); - /* i18n-hint: Format string for displaying time in hours, minutes, seconds - * and frames with NTSC drop frames. Change the 'h' to the abbreviation - * for hours, 'm' to the abbreviation for minutes, 's' to the abbreviation - * for seconds and translate 'frames'. Leave the | .999000999 alone, - * the whole things really is slightly off-speed! */ - BuiltinFormatStrings[10].formatStr = _("0100 h 060 m 060 s+.030 frames| .999000999"); - /* i18n-hint: Name of time display format that shows time in frames at NTSC - * TV frame rate (used for American / Japanese TV */ - BuiltinFormatStrings[11].name = _("NTSC frames"); - /* i18n-hint: Format string for displaying time in frames with NTSC frames. - * Translate 'frames' and leave the rest alone. That really is the frame - * rate! */ - BuiltinFormatStrings[11].formatStr = _("01000,01000 frames|29.97002997"); - /* i18n-hint: Name of time display format that shows time in hours, minutes, - * seconds and frames at PAL TV frame rate (used for European TV) */ - BuiltinFormatStrings[12].name = _("hh:mm:ss + PAL frames (25 fps)"); - /* i18n-hint: Format string for displaying time in hours, minutes, seconds - * and frames with PAL TV frames. Change the 'h' to the abbreviation - * for hours, 'm' to the abbreviation for minutes, 's' to the abbreviation - * for seconds and translate 'frames'. Nice simple time code! */ - BuiltinFormatStrings[12].formatStr = _("0100 h 060 m 060 s+.25 frames"); - /* i18n-hint: Name of time display format that shows time in frames at PAL - * TV frame rate (used for European TV) */ - BuiltinFormatStrings[13].name = _("PAL frames (25 fps)"); - /* i18n-hint: Format string for displaying time in frames with NTSC frames. - * Translate 'frames' and leave the rest alone. */ - BuiltinFormatStrings[13].formatStr = _("01000,01000 frames|25"); - /* i18n-hint: Name of time display format that shows time in hours, minutes, - * seconds and frames at CD Audio frame rate (75 frames per second) */ - BuiltinFormatStrings[14].name = _("hh:mm:ss + CDDA frames (75 fps)"); - /* i18n-hint: Format string for displaying time in hours, minutes, seconds - * and frames with CD Audio frames. Change the 'h' to the abbreviation - * for hours, 'm' to the abbreviation for minutes, 's' to the abbreviation - * for seconds and translate 'frames'. */ - BuiltinFormatStrings[14].formatStr = _("0100 h 060 m 060 s+.75 frames"); - /* i18n-hint: Name of time display format that shows time in frames at CD - * Audio frame rate (75 frames per second) */ - BuiltinFormatStrings[15].name = _("CDDA frames (75 fps)"); - /* i18n-hint: Format string for displaying time in frames with CD Audio - * frames. Translate 'frames' and leave the rest alone */ - BuiltinFormatStrings[15].formatStr = _("01000,01000 frames|75"); - - mPrefix = wxT(""); - mValueTemplate = wxT(""); - mValueMask = wxT(""); - mValueString = wxT(""); - - mScalingFactor = 1.0f; - mSampleRate = 1.0f; - mNtscDrop = false; - - mFocusedDigit = 0; - - SetSampleRate(sampleRate); - SetFormatName(formatName); - SetTimeValue(timeValue); -} - -void TimeConverter::ParseFormatString( const wxString & format) -{ - mPrefix = wxT(""); - mFields.Clear(); - mDigits.Clear(); - mScalingFactor = 1.0; - - bool inFrac = false; - int fracMult = 1; - int numWholeFields = 0; - int numFracFields = 0; - wxString numStr; - wxString delimStr; - unsigned int i; - - mNtscDrop = false; - for(i=0; i= '0' && format[i] <='9') || - format[i] == wxT('*') || format[i] == wxT('#')) { - numStr += format[i]; - if (delimStr != wxT("")) - handleDelim = true; - } - else { - delimStr += format[i]; - if (numStr != wxT("")) - handleNum = true; - } - - if (i == format.Length() - 1) { - if (numStr != wxT("")) - handleNum = true; - if (delimStr != wxT("")) - handleDelim = true; - } - - if (handleNum) { - bool zeropad = false; - long range = 0; - - if (numStr.Right(1) == wxT("#")) - range = (long int)mSampleRate; - else if (numStr.Right(1) != wxT("*")) { - numStr.ToLong(&range); - } - if (numStr.GetChar(0)=='0' && numStr.Length()>1) - zeropad = true; - - // Hack: always zeropad - zeropad = true; - - if (inFrac) { - int base = fracMult * range; - mFields.Add(TimeField(inFrac, base, range, zeropad)); - fracMult *= range; - numFracFields++; - } - else { - unsigned int j; - for(j=0; j 1) - delimStr = delimStr.BeforeLast('.'); - } - - if (inFrac) { - if (numFracFields == 0) { - // Should never happen - return; - } - if (handleNum && numFracFields > 1) - mFields[mFields.GetCount()-2].label = delimStr; - else - mFields[mFields.GetCount()-1].label = delimStr; - } - else { - if (numWholeFields == 0) - mPrefix = delimStr; - else { - mFields[numWholeFields-1].label = delimStr; - } - } - - if (goToFrac) - inFrac = true; - delimStr = wxT(""); - } - } - - for(i=0; i= 1800) { - frames -= 1800; - mins++; - addMins = frames/1798; - frames -= addMins*1798; - mins += addMins; - secs = frames/30; - frames -= secs*30; - frames += 2; - if( frames >= 30 ) { - secs++; - frames -= 30; - } - } - else { - secs = frames/30; - frames -= secs*30; - } - t_int = mins * 60 + secs; - t_frac = frames / 30.; - } - - for(i=0; i 0) - // value = value % mFields[i].range; - } - else { - value = (t_int / mFields[i].base); - if (mFields[i].range > 0) - value = value % mFields[i].range; - } - - wxString field = wxString::Format(mFields[i].formatStr, value); - mValueString += field; - mValueString += mFields[i].label; - } -} - -void TimeConverter::ControlsToValue() -{ - unsigned int i; - double t = 0.0; - - for(i=0; i 0 ) { - frames += 1800; - addMins = mins - 1; - } - frames += addMins * 1798; - t_int -= mins*60; - if( mins == 0 ) //first min of a block of 10, don't drop frames 0 and 1 - frames += t_int * 30 + t_frac*30.; - else { //drop frames 0 and 1 of first seconds of these minutes - if( t_int > 0 ) - frames += 28 + (t_int-1)*30 + t_frac*30.; - else - frames += t_frac*30. -2.; - } - t = frames * 1.001 / 30.; - } - - mTimeValue = t; -} - -void TimeConverter::SetFormatName(const wxString & formatName) -{ - SetFormatString(GetBuiltinFormat(formatName)); -} - -void TimeConverter::SetFormatString(const wxString & formatString) -{ - mFormatString = formatString; - ParseFormatString(mFormatString); - ValueToControls(); - ControlsToValue(); -} - -void TimeConverter::SetSampleRate(double sampleRate) -{ - mSampleRate = sampleRate; - ParseFormatString(mFormatString); - ValueToControls(); - ControlsToValue(); -} - -void TimeConverter::SetTimeValue(double newTime) -{ - mTimeValue = newTime; - ValueToControls(); - ControlsToValue(); -} - -double TimeConverter::GetTimeValue() -{ - ControlsToValue(); - return mTimeValue; -} - -wxString TimeConverter::GetFormatString() -{ - return mFormatString; -} - -int TimeConverter::GetFormatIndex() -{ - int ndx = 1; - int i; - - for (i = 0; i < GetNumBuiltins(); i++) { - if (mFormatString == GetBuiltinFormat(i)) { - ndx = i; - break; - } - } - - return ndx; -} - -int TimeConverter::GetNumBuiltins() -{ - return (sizeof(BuiltinFormatStrings) / sizeof(BuiltinFormatStrings[0])); -} - -wxString TimeConverter::GetBuiltinName(const int index) -{ - if (index >= 0 && index < GetNumBuiltins()) - return BuiltinFormatStrings[index].name; - - return wxEmptyString; -} - -wxString TimeConverter::GetBuiltinFormat(const int index) -{ - if (index >= 0 && index < GetNumBuiltins()) - return BuiltinFormatStrings[index].formatStr; - - return wxEmptyString; -} - -wxString TimeConverter::GetBuiltinFormat(const wxString &name) -{ - int ndx = 4; // Default to "hh:mm:ss + milliseconds". - int i; - - for (i = 0; i < GetNumBuiltins(); i++) { - if (name == GetBuiltinName(i)) { - ndx = i; - break; - } - } - - return GetBuiltinFormat(ndx); -} - -wxString TimeConverter::GetTimeString() -{ - ValueToControls(); - - return mValueString; -} - -void TimeConverter::Increment() -{ - mFocusedDigit = mDigits.GetCount() - 1; - Adjust(1, 1); -} - -void TimeConverter::Decrement() -{ - mFocusedDigit = mDigits.GetCount() - 1; - Adjust(1, -1); -} - -void TimeConverter::Adjust(int steps, int dir) -{ - wxASSERT(dir == -1 || dir == 1); - wxASSERT(steps > 0); - if (steps < 0) - steps = -steps; - - while (steps != 0) - { - for (size_t i = 0; i < mFields.GetCount(); i++) - { - if ((mDigits[mFocusedDigit].pos >= mFields[i].pos) && (mDigits[mFocusedDigit].pos < mFields[i].pos + mFields[i].digits)) - { //it's this field - if (!mNtscDrop) - { - ControlsToValue(); - } - else - { - mNtscDrop = false; - ControlsToValue(); - mNtscDrop = true; - } - mTimeValue *= mScalingFactor; - - double mult = pow(10., mFields[i].digits - (mDigits[mFocusedDigit].pos - mFields[i].pos) - 1); - if (mFields[i].frac) - { - mTimeValue += ((mult / (double)mFields[i].base) * dir); - } - else - { - mTimeValue += ((mult * (double)mFields[i].base) * dir); - } - - if (mNtscDrop) - { - if ((mTimeValue - (int)mTimeValue) * 30 < 2) - { - if ((((int)mTimeValue) % 60 == 0) && (((int)mTimeValue) % 600 != 0)) - { - mTimeValue = (int)mTimeValue + (dir > 0 ? 2. : -1.) / 30.; - } - } - } - - if (mTimeValue < 0.) - { - mTimeValue = 0.; - } - - mTimeValue /= mScalingFactor; - - if (!mNtscDrop) - { - ValueToControls(); - } - else - { - mNtscDrop = false; - ValueToControls(); - mNtscDrop = true; - ControlsToValue(); - } - break; - } - } - steps--; - } - - ControlsToValue(); -} - -#define ID_MENU 9800 - -// Custom events - -DEFINE_EVENT_TYPE(EVT_TIMETEXTCTRL_UPDATED) - -BEGIN_EVENT_TABLE(TimeTextCtrl, wxControl) - EVT_ERASE_BACKGROUND(TimeTextCtrl::OnErase) - EVT_PAINT(TimeTextCtrl::OnPaint) - EVT_CONTEXT_MENU(TimeTextCtrl::OnContext) - EVT_MENU_RANGE(ID_MENU, ID_MENU+100, TimeTextCtrl::OnMenu) - EVT_MOUSE_EVENTS(TimeTextCtrl::OnMouse) - EVT_KEY_DOWN(TimeTextCtrl::OnKeyDown) - EVT_SET_FOCUS(TimeTextCtrl::OnFocus) - EVT_KILL_FOCUS(TimeTextCtrl::OnFocus) - EVT_COMMAND(wxID_ANY, EVT_CAPTURE_KEY, TimeTextCtrl::OnCaptureKey) -END_EVENT_TABLE() - -IMPLEMENT_CLASS(TimeTextCtrl, wxControl) - -TimeTextCtrl::TimeTextCtrl(wxWindow *parent, - wxWindowID id, - wxString formatName, - double timeValue, - double sampleRate, - const wxPoint &pos, - const wxSize &size, - bool autoPos): - wxControl(parent, id, pos, size, wxSUNKEN_BORDER | wxWANTS_CHARS), - TimeConverter(formatName, timeValue, sampleRate), - mBackgroundBitmap(NULL), - mDigitFont(NULL), - mLabelFont(NULL), - mLastField(1), - mAutoPos(autoPos) -{ - - mDigitBoxW = 10; - mDigitBoxH = 16; - - mMenuEnabled = true; - mButtonWidth = 9; - - Layout(); - Fit(); - ValueToControls(); - //mchinen - aug 15 09 - this seems to put the mTimeValue back to zero, and do nothing else. - //ControlsToValue(); - - mScrollRemainder = 0.0; - -#if wxUSE_ACCESSIBILITY - SetLabel(wxT("")); - SetName(wxT("")); - SetAccessible(new TimeTextCtrlAx(this)); -#endif -} - -TimeTextCtrl::~TimeTextCtrl() -{ - wxCommandEvent e(EVT_RELEASE_KEYBOARD); - e.SetEventObject(this); - GetParent()->GetEventHandler()->ProcessEvent(e); - - if (mBackgroundBitmap) - delete mBackgroundBitmap; - if (mDigitFont) - delete mDigitFont; - if (mLabelFont) - delete mLabelFont; -} - -// Set the focus to the first (left-most) non-zero digit -// If all digits are zero, the right-most position is focused -void TimeTextCtrl::UpdateAutoFocus() -{ - if (!mAutoPos) - return; - - mFocusedDigit = 0; - while (mFocusedDigit < ((int)mDigits.GetCount() - 1)) { - wxChar dgt = mValueString[mDigits[mFocusedDigit].pos]; - if (dgt != '0') { - break; - } - mFocusedDigit++; - } -} - -void TimeTextCtrl::SetFormatName(const wxString & formatName) -{ - SetFormatString(GetBuiltinFormat(formatName)); -} - -void TimeTextCtrl::SetFormatString(const wxString & formatString) -{ - TimeConverter::SetFormatString(formatString); - Layout(); - Fit(); - ValueToControls(); - ControlsToValue(); - UpdateAutoFocus(); -} - -void TimeTextCtrl::SetSampleRate(double sampleRate) -{ - TimeConverter::SetSampleRate(sampleRate); - Layout(); - Fit(); - ValueToControls(); - ControlsToValue(); -} - -void TimeTextCtrl::SetTimeValue(double newTime) -{ - TimeConverter::SetTimeValue(newTime); - ValueToControls(); - ControlsToValue(); -} - -void TimeTextCtrl::EnableMenu(bool enable) -{ -#if wxUSE_TOOLTIPS - wxString tip(_("(Use context menu to change format.)")); - if (enable) - SetToolTip(tip); - else { - wxToolTip *tt = GetToolTip(); - if (tt && tt->GetTip() == tip) - SetToolTip(NULL); - } -#endif - mMenuEnabled = enable; - mButtonWidth = enable ? 9 : 0; - Layout(); - Fit(); -} - -bool TimeTextCtrl::Layout() -{ - unsigned int i, j; - int x, pos; - - wxMemoryDC memDC; - if (mBackgroundBitmap) { - delete mBackgroundBitmap; - mBackgroundBitmap = NULL; - } - // Placeholder bitmap so the memDC has something to reference - mBackgroundBitmap = new wxBitmap(1, 1); - memDC.SelectObject(*mBackgroundBitmap); - - mDigits.Clear(); - - mBorderLeft = 1; - mBorderTop = 1; - mBorderRight = 1; - mBorderBottom = 1; - - int fontSize = 4; - wxCoord strW, strH; - wxString exampleText = wxT("0"); - - // Keep making the font bigger until it's too big, then subtract one. - memDC.SetFont(wxFont(fontSize, wxFONTFAMILY_DEFAULT, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_NORMAL)); - memDC.GetTextExtent(exampleText, &strW, &strH); - while(strW <= mDigitBoxW && strH <= mDigitBoxH) { - fontSize++; - memDC.SetFont(wxFont(fontSize, wxFONTFAMILY_DEFAULT, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_NORMAL)); - memDC.GetTextExtent(exampleText, &strW, &strH); - } - fontSize--; - - if (mDigitFont) - delete mDigitFont; - mDigitFont = new wxFont(fontSize, wxFONTFAMILY_DEFAULT, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_NORMAL); - memDC.SetFont(*mDigitFont); - memDC.GetTextExtent(exampleText, &strW, &strH); - mDigitW = strW; - mDigitH = strH; - - // The label font should be a little smaller - fontSize--; - if (mLabelFont) - delete mLabelFont; - mLabelFont = new wxFont(fontSize, wxFONTFAMILY_DEFAULT, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_NORMAL); - - // Figure out the x-position of each field and label in the box - x = mBorderLeft; - pos = 0; - - memDC.SetFont(*mLabelFont); - memDC.GetTextExtent(mPrefix, &strW, &strH); - x += strW; - pos += mPrefix.Length(); - - for(i=0; i GetNumBuiltins()) { - event.Skip(); - return; - } - - SetFormatString(GetBuiltinFormat(id)); - - wxCommandEvent e(EVT_TIMETEXTCTRL_UPDATED, GetId()); - e.SetInt(id); - e.SetString(GetBuiltinName(id)); - GetParent()->GetEventHandler()->AddPendingEvent(e); -} - -void TimeTextCtrl::OnContext(wxContextMenuEvent &event) -{ - wxMenu menu; - int i; - - if (!mMenuEnabled) { - event.Skip(); - return; - } - - SetFocus(); - - for(i=0; i= mWidth) { - wxContextMenuEvent e; - OnContext(e); - } - else if (event.LeftDown()) { - SetFocus(); - - int bestDist = 9999; - unsigned int i; - - mFocusedDigit = 0; - for(i=0; i 0 ? (double)event.m_wheelDelta : 120.0) + - mScrollRemainder; - mScrollRemainder = steps - floor(steps); - steps = floor(steps); - - Adjust((int)fabs(steps), steps < 0.0 ? -1 : 1); - Updated(); - } -} - -void TimeTextCtrl::OnFocus(wxFocusEvent &event) -{ - wxCommandEvent e(EVT_CAPTURE_KEYBOARD); - - if (event.GetEventType() == wxEVT_KILL_FOCUS) { - e.SetEventType(EVT_RELEASE_KEYBOARD); - } - e.SetEventObject(this); - GetParent()->GetEventHandler()->ProcessEvent(e); - - Refresh(false); -} - -void TimeTextCtrl::OnCaptureKey(wxCommandEvent &event) -{ - wxKeyEvent *kevent = (wxKeyEvent *)event.GetEventObject(); - int keyCode = kevent->GetKeyCode(); - - // Convert numeric keypad entries. - if ((keyCode >= WXK_NUMPAD0) && (keyCode <= WXK_NUMPAD9)) keyCode -= WXK_NUMPAD0 - '0'; - - switch (keyCode) - { - case WXK_BACK: - case WXK_LEFT: - case WXK_RIGHT: - case WXK_HOME: - case WXK_END: - case WXK_UP: - case WXK_DOWN: - case WXK_TAB: - case WXK_RETURN: - case WXK_NUMPAD_ENTER: - return; - - default: - if (keyCode >= '0' && keyCode <= '9') - return; - } - - event.Skip(); - - return; -} - -void TimeTextCtrl::OnKeyUp(wxKeyEvent &event) -{ - int keyCode = event.GetKeyCode(); - - event.Skip(true); - - if ((keyCode >= WXK_NUMPAD0) && (keyCode <= WXK_NUMPAD9)) - keyCode -= WXK_NUMPAD0 - '0'; - - if ((keyCode >= '0' && keyCode <= '9') || - (keyCode == WXK_BACK) || - (keyCode == WXK_UP) || - (keyCode == WXK_DOWN)) { - Updated(true); - } -} - -void TimeTextCtrl::OnKeyDown(wxKeyEvent &event) -{ - if (mDigits.GetCount() == 0) - { - mFocusedDigit = 0; - return; - } - - event.Skip(); - - int keyCode = event.GetKeyCode(); - int digit = mFocusedDigit; - - if (mFocusedDigit < 0) - mFocusedDigit = 0; - if (mFocusedDigit >= (int)mDigits.GetCount()) - mFocusedDigit = mDigits.GetCount()-1; - - // Convert numeric keypad entries. - if ((keyCode >= WXK_NUMPAD0) && (keyCode <= WXK_NUMPAD9)) keyCode -= WXK_NUMPAD0 - '0'; - - if (keyCode >= '0' && keyCode <= '9') { - mValueString[mDigits[mFocusedDigit].pos] = wxChar(keyCode); - ControlsToValue(); - ValueToControls(); - mFocusedDigit = (mFocusedDigit+1)%(mDigits.GetCount()); - Updated(); - } - - else if (keyCode == WXK_BACK) { - // Moves left, replaces that char with '0', stays there... - mFocusedDigit--; - mFocusedDigit += mDigits.GetCount(); - mFocusedDigit %= mDigits.GetCount(); - mValueString[mDigits[mFocusedDigit].pos] = '0'; - ControlsToValue(); - ValueToControls(); - Updated(); - } - - else if (keyCode == WXK_LEFT) { - mFocusedDigit--; - mFocusedDigit += mDigits.GetCount(); - mFocusedDigit %= mDigits.GetCount(); - Refresh(); - } - - else if (keyCode == WXK_RIGHT) { - mFocusedDigit++; - mFocusedDigit %= mDigits.GetCount(); - Refresh(); - } - - else if (keyCode == WXK_HOME) { - mFocusedDigit = 0; - Refresh(); - } - - else if (keyCode == WXK_END) { - mFocusedDigit = mDigits.GetCount() - 1; - Refresh(); - } - - else if (keyCode == WXK_UP) { - Adjust(1, 1); - Updated(); - } - - else if (keyCode == WXK_DOWN) { - Adjust(1, -1); - Updated(); - } - - else if (keyCode == WXK_TAB) { - wxWindow *parent = GetParent(); - wxNavigationKeyEvent nevent; - nevent.SetWindowChange(event.ControlDown()); - nevent.SetDirection(!event.ShiftDown()); - nevent.SetEventObject(parent); - nevent.SetCurrentFocus(parent); - GetParent()->GetEventHandler()->ProcessEvent(nevent); - event.Skip(false); - } - - else if (keyCode == WXK_RETURN || keyCode == WXK_NUMPAD_ENTER) { - wxTopLevelWindow *tlw = wxDynamicCast(wxGetTopLevelParent(this), wxTopLevelWindow); - wxWindow *def = tlw->GetDefaultItem(); - if (def && def->IsEnabled()) { - wxCommandEvent cevent(wxEVT_COMMAND_BUTTON_CLICKED, - def->GetId()); - GetParent()->GetEventHandler()->ProcessEvent(cevent); - event.Skip(false); - } - } - - else { - event.Skip(); - return; - } - - if (digit != mFocusedDigit) { - SetFieldFocus(mFocusedDigit); - } -} - -void TimeTextCtrl::SetFieldFocus(int digit) -{ -#if wxUSE_ACCESSIBILITY - if (mDigits.GetCount() == 0) - { - mFocusedDigit = 0; - return; - } - mFocusedDigit = digit; - mLastField = mDigits[mFocusedDigit].field + 1; - - // This looks strange (and it is), but it was the only way I could come - // up with that allowed Jaws, Window-Eyes, and NVDA to read the control - // somewhat the same. See TimeTextCtrlAx below for even more odd looking - // hackery. - // - // If you change SetFieldFocus(), Updated(), or TimeTextCtrlAx, make sure - // you test with Jaws, Window-Eyes, and NVDA. - GetAccessible()->NotifyEvent(wxACC_EVENT_OBJECT_FOCUS, - this, - wxOBJID_CLIENT, - 0); - GetAccessible()->NotifyEvent(wxACC_EVENT_OBJECT_FOCUS, - this, - wxOBJID_CLIENT, - mFocusedDigit + 1); -#endif -} - -void TimeTextCtrl::Updated(bool keyup /* = false */) -{ - wxCommandEvent event(wxEVT_COMMAND_TEXT_UPDATED, GetId()); - - // This will give listeners the ability to do tasks when the - // update has been completed, like when the UP ARROW has been - // held down and is finally released. - event.SetInt(keyup); - event.SetEventObject(this); - GetEventHandler()->ProcessEvent(event); - -#if wxUSE_ACCESSIBILITY - if (!keyup) { - GetAccessible()->NotifyEvent(wxACC_EVENT_OBJECT_NAMECHANGE, - this, - wxOBJID_CLIENT, - mFocusedDigit + 1); - SetFieldFocus(mFocusedDigit); - } -#endif -} - -void TimeTextCtrl::ValueToControls() -{ - TimeConverter::ValueToControls(mTimeValue); - Refresh(false); -} - - -void TimeTextCtrl::ControlsToValue() -{ - TimeConverter::ControlsToValue(); -} - -#if wxUSE_ACCESSIBILITY - -TimeTextCtrlAx::TimeTextCtrlAx(TimeTextCtrl *ctrl) -: wxWindowAccessible(ctrl) -{ - mCtrl = ctrl; - mLastField = -1; - mLastDigit = -1; -} - -TimeTextCtrlAx::~TimeTextCtrlAx() -{ -} - -// Performs the default action. childId is 0 (the action for this object) -// or > 0 (the action for a child). -// Return wxACC_NOT_SUPPORTED if there is no default action for this -// window (e.g. an edit control). -wxAccStatus TimeTextCtrlAx::DoDefaultAction(int WXUNUSED(childId)) -{ - return wxACC_NOT_SUPPORTED; -} - -// Retrieves the address of an IDispatch interface for the specified child. -// All objects must support this property. -wxAccStatus TimeTextCtrlAx::GetChild(int childId, wxAccessible **child) -{ - if (childId == wxACC_SELF) { - *child = this; - } - else { - *child = NULL; - } - - return wxACC_OK; -} - -// Gets the number of children. -wxAccStatus TimeTextCtrlAx::GetChildCount(int *childCount) -{ - *childCount = mCtrl->mDigits.GetCount(); - - return wxACC_OK; -} - -// Gets the default action for this object (0) or > 0 (the action for -// a child). Return wxACC_OK even if there is no action. actionName -// is the action, or the empty string if there is no action. The -// retrieved string describes the action that is performed on an -// object, not what the object does as a result. For example, a -// toolbar button that prints a document has a default action of -// "Press" rather than "Prints the current document." -wxAccStatus TimeTextCtrlAx::GetDefaultAction(int WXUNUSED(childId), wxString *actionName) -{ - actionName->Clear(); - - return wxACC_OK; -} - -// Returns the description for this object or a child. -wxAccStatus TimeTextCtrlAx::GetDescription(int WXUNUSED(childId), wxString *description) -{ - description->Clear(); - - return wxACC_OK; -} - -// Gets the window with the keyboard focus. -// If childId is 0 and child is NULL, no object in -// this subhierarchy has the focus. -// If this object has the focus, child should be 'this'. -wxAccStatus TimeTextCtrlAx::GetFocus(int *childId, wxAccessible **child) -{ - *childId = mCtrl->GetFocusedDigit(); - *child = this; - - return wxACC_OK; -} - -// Returns help text for this object or a child, similar to tooltip text. -wxAccStatus TimeTextCtrlAx::GetHelpText(int WXUNUSED(childId), wxString *helpText) -{ -// removed help text, as on balance it's more of an irritation than useful -#if 0 // was #if wxUSE_TOOLTIPS - wxToolTip *pTip = mCtrl->GetToolTip(); - if (pTip) { - *helpText = pTip->GetTip(); - } - - return wxACC_OK; -#else - helpText->Clear(); - - return wxACC_NOT_SUPPORTED; -#endif -} - -// Returns the keyboard shortcut for this object or child. -// Return e.g. ALT+K -wxAccStatus TimeTextCtrlAx::GetKeyboardShortcut(int WXUNUSED(childId), wxString *shortcut) -{ - shortcut->Clear(); - - return wxACC_OK; -} - -// Returns the rectangle for this object (id = 0) or a child element (id > 0). -// rect is in screen coordinates. -wxAccStatus TimeTextCtrlAx::GetLocation(wxRect & rect, int elementId) -{ - if ((elementId != wxACC_SELF) && - // We subtract 1, below, and need to avoid neg index to mDigits. - (elementId > 0)) - { -// rect.x += mCtrl->mFields[elementId - 1].fieldX; -// rect.width = mCtrl->mFields[elementId - 1].fieldW; - rect = mCtrl->mDigits[elementId - 1].digitBox; - rect.SetPosition(mCtrl->ClientToScreen(rect.GetPosition())); - } - else - { - rect = mCtrl->GetRect(); - rect.SetPosition(mCtrl->GetParent()->ClientToScreen(rect.GetPosition())); - } - - return wxACC_OK; -} - -// Gets the name of the specified object. -wxAccStatus TimeTextCtrlAx::GetName(int childId, wxString *name) -{ - // Slightly messy trick to save us some prefixing. - TimeFieldArray & mFields = mCtrl->mFields; - - wxString value = mCtrl->GetTimeString(); - int field = mCtrl->GetFocusedField(); - - // Return the entire time string including the control label - // when the requested child ID is wxACC_SELF. (Mainly when - // the control gets the focus.) - if ((childId == wxACC_SELF) || - // We subtract 1 from childId in the other cases below, and - // need to avoid neg index to mDigits, so funnel into this clause. - (childId < 1)) - { - *name = mCtrl->GetName(); - if (name->IsEmpty()) { - *name = mCtrl->GetLabel(); - } - - *name += wxT(" ") + - mCtrl->GetTimeString(); - } - // The user has moved from one field of the time to another so - // report the value of the field and the field's label. - else if (mLastField != field) { - wxString label = mFields[field - 1].label; - int cnt = mFields.GetCount(); - wxString decimal = wxLocale::GetInfo(wxLOCALE_DECIMAL_POINT, wxLOCALE_CAT_NUMBER); - - // If the new field is the last field, then check it to see if - // it represents fractions of a second. - if (field > 1 && field == cnt) { - if (mFields[field - 2].label == decimal) { - int digits = mFields[field - 1].digits; - if (digits == 2) { - label = _("centiseconds"); - } - else if (digits == 3) { - label = _("milliseconds"); - } - } - } - // If the field following this one represents fractions of a - // second then use that label instead of the decimal point. - else if (label == decimal && field == cnt - 1) { - label = mFields[field].label; - } - - *name = mFields[field - 1].str + - wxT(" ") + - label + - wxT(", ") + // comma inserts a slight pause - mCtrl->GetTimeString().at(mCtrl->mDigits[childId - 1].pos); - mLastField = field; - mLastDigit = childId; - } - // The user has moved from one digit to another within a field so - // just report the digit under the cursor. - else if (mLastDigit != childId) { - *name = mCtrl->GetTimeString().at(mCtrl->mDigits[childId - 1].pos); - mLastDigit = childId; - } - // The user has updated the value of a field, so report the field's - // value only. - else if (field > 0) - { - *name = mFields[field - 1].str; - } - - return wxACC_OK; -} - -// Returns a role constant. -wxAccStatus TimeTextCtrlAx::GetRole(int WXUNUSED(childId), wxAccRole *role) -{ - *role = wxROLE_SYSTEM_STATICTEXT; - return wxACC_OK; -} - -// Gets a variant representing the selected children -// of this object. -// Acceptable values: -// - a null variant (IsNull() returns TRUE) -// - a list variant (GetType() == wxT("list")) -// - an integer representing the selected child element, -// or 0 if this object is selected (GetType() == wxT("long")) -// - a "void*" pointer to a wxAccessible child object -wxAccStatus TimeTextCtrlAx::GetSelections(wxVariant * WXUNUSED(selections)) -{ - return wxACC_NOT_IMPLEMENTED; -} - -// Returns a state constant. -wxAccStatus TimeTextCtrlAx::GetState(int WXUNUSED(childId), long *state) -{ - *state = wxACC_STATE_SYSTEM_FOCUSABLE; - *state |= (mCtrl == wxWindow::FindFocus() ? wxACC_STATE_SYSTEM_FOCUSED : 0); - - return wxACC_OK; -} - -// Returns a localized string representing the value for the object -// or child. -wxAccStatus TimeTextCtrlAx::GetValue(int WXUNUSED(childId), wxString * WXUNUSED(strValue)) -{ - return wxACC_NOT_IMPLEMENTED; -} - -#endif - diff --git a/src/widgets/TimeTextCtrl.h b/src/widgets/TimeTextCtrl.h index 3aaaa13d0..e69de29bb 100644 --- a/src/widgets/TimeTextCtrl.h +++ b/src/widgets/TimeTextCtrl.h @@ -1,273 +0,0 @@ -/********************************************************************** - - Audacity: A Digital Audio Editor - - TimeTextCtrl.h - - Dominic Mazzoni - - See TimeTextCtrl.cpp for documentation on how to use the - format string to specify how a TimeTextCtrl's fields are - laid out. - -**********************************************************************/ - -#ifndef __AUDACITY_TIME_TEXT_CTRL__ -#define __AUDACITY_TIME_TEXT_CTRL__ - -#include -#include -#include -#include -#include -#include -#include - -#if wxUSE_ACCESSIBILITY -#include -#endif - -DECLARE_EXPORTED_EVENT_TYPE(AUDACITY_DLL_API, EVT_TIMETEXTCTRL_UPDATED, -1); - -/** \brief struct to hold a formatting control string and it's user facing name - * Used in an array to hold the built-in time formats that are always available - * to the user */ -struct BuiltinFormatString -{ - wxString name; - wxString formatStr; -}; - -class TimeField; -WX_DECLARE_OBJARRAY(TimeField, TimeFieldArray); - -class DigitInfo; -WX_DECLARE_OBJARRAY(DigitInfo, DigitInfoArray); - -class TimeConverter -{ -public: - TimeConverter(const wxString & formatName = wxEmptyString, - double timeValue = 0.0f, - double sampleRate = 1.0f /* to prevent div by 0 */); - - virtual void ValueToControls(); - virtual void ValueToControls(double RawTime, bool nearest = true); - virtual void ControlsToValue(); - virtual void ParseFormatString(const wxString & format); - - void PrintDebugInfo(); - void SetFormatName(const wxString & formatName); - void SetFormatString(const wxString & formatString); - void SetSampleRate(double sampleRate); - void SetTimeValue(double newTime); - double GetTimeValue(); - - wxString GetTimeString(); - - wxString GetFormatString(); - int GetFormatIndex(); - - int GetNumBuiltins(); - wxString GetBuiltinName(const int index); - wxString GetBuiltinFormat(const int index); - wxString GetBuiltinFormat(const wxString & name); - - // Adjust the value by the number "steps" in the active format. - // Increment if "dir" is 1, descrement if "dir" is -1. - void Adjust(int steps, int dir); - - void Increment(); - void Decrement(); - -protected: - /** \brief array of formats the control knows about internally - * array of string pairs for name of the format and the format string - * needed to create that format output. This is used for the pop-up - * list of formats to choose from in the control. Note that the size will - * need adjusting if new time formats are added */ - BuiltinFormatString BuiltinFormatStrings[16]; - double mTimeValue; - - wxString mFormatString; - - TimeFieldArray mFields; - wxString mPrefix; - wxString mValueTemplate; - wxString mValueMask; - wxString mValueString; - - double mScalingFactor; - double mSampleRate; - bool mNtscDrop; - - int mFocusedDigit; - DigitInfoArray mDigits; -}; - -class TimeTextCtrl: public wxControl, public TimeConverter -{ - friend class TimeTextCtrlAx; - - public: - DECLARE_DYNAMIC_CLASS(TimeTextCtrl); - - TimeTextCtrl(wxWindow *parent, - wxWindowID id, - wxString formatName = wxEmptyString, - double timeValue = 0.0, - double sampleRate = 44100, - const wxPoint &pos = wxDefaultPosition, - const wxSize &size = wxDefaultSize, - bool autoPos = false); - - virtual ~TimeTextCtrl(); - - virtual bool Layout(); - virtual void Fit(); - - void SetSampleRate(double sampleRate); - void SetTimeValue(double newTime); - void SetFormatString(const wxString & formatString); - void SetFormatName(const wxString & formatName); - - void SetFieldFocus(int digit); - - void EnableMenu(bool enable = true); - - int GetFocusedField() { return mLastField; }; - int GetFocusedDigit() { return mFocusedDigit; }; - -private: - - void OnCaptureKey(wxCommandEvent &event); - void OnKeyDown(wxKeyEvent &event); - void OnKeyUp(wxKeyEvent &event); - void OnMouse(wxMouseEvent &event); - void OnErase(wxEraseEvent &event); - void OnPaint(wxPaintEvent &event); - void OnFocus(wxFocusEvent &event); - void OnContext(wxContextMenuEvent &event); - void OnMenu(wxCommandEvent &event); - - void ValueToControls(); - void ControlsToValue(); - - // If autoPos was enabled, focus the first non-zero digit - void UpdateAutoFocus(); - - void Updated(bool keyup = false); - -private: - - bool mMenuEnabled; - - wxBitmap *mBackgroundBitmap; - - wxFont *mDigitFont; - wxFont *mLabelFont; - int mDigitBoxW; - int mDigitBoxH; - int mDigitW; - int mDigitH; - int mBorderLeft; - int mBorderTop; - int mBorderRight; - int mBorderBottom; - int mWidth; - int mHeight; - int mButtonWidth; - - int mLastField; - - // If true, the focus will be set to the first non-zero digit - bool mAutoPos; - - // Keeps track of extra fractional scrollwheel steps - double mScrollRemainder; - - DECLARE_EVENT_TABLE() -}; - -#if wxUSE_ACCESSIBILITY - -class TimeTextCtrlAx: public wxWindowAccessible -{ -public: - TimeTextCtrlAx(TimeTextCtrl * ctrl); - - virtual ~ TimeTextCtrlAx(); - - // Performs the default action. childId is 0 (the action for this object) - // or > 0 (the action for a child). - // Return wxACC_NOT_SUPPORTED if there is no default action for this - // window (e.g. an edit control). - virtual wxAccStatus DoDefaultAction(int childId); - - // Retrieves the address of an IDispatch interface for the specified child. - // All objects must support this property. - virtual wxAccStatus GetChild(int childId, wxAccessible **child); - - // Gets the number of children. - virtual wxAccStatus GetChildCount(int *childCount); - - // Gets the default action for this object (0) or > 0 (the action for a child). - // Return wxACC_OK even if there is no action. actionName is the action, or the empty - // string if there is no action. - // The retrieved string describes the action that is performed on an object, - // not what the object does as a result. For example, a toolbar button that prints - // a document has a default action of "Press" rather than "Prints the current document." - virtual wxAccStatus GetDefaultAction(int childId, wxString *actionName); - - // Returns the description for this object or a child. - virtual wxAccStatus GetDescription(int childId, wxString *description); - - // Gets the window with the keyboard focus. - // If childId is 0 and child is NULL, no object in - // this subhierarchy has the focus. - // If this object has the focus, child should be 'this'. - virtual wxAccStatus GetFocus(int *childId, wxAccessible **child); - - // Returns help text for this object or a child, similar to tooltip text. - virtual wxAccStatus GetHelpText(int childId, wxString *helpText); - - // Returns the keyboard shortcut for this object or child. - // Return e.g. ALT+K - virtual wxAccStatus GetKeyboardShortcut(int childId, wxString *shortcut); - - // Returns the rectangle for this object (id = 0) or a child element (id > 0). - // rect is in screen coordinates. - virtual wxAccStatus GetLocation(wxRect & rect, int elementId); - - // Gets the name of the specified object. - virtual wxAccStatus GetName(int childId, wxString *name); - - // Returns a role constant. - virtual wxAccStatus GetRole(int childId, wxAccRole *role); - - // Gets a variant representing the selected children - // of this object. - // Acceptable values: - // - a null variant (IsNull() returns TRUE) - // - a list variant (GetType() == wxT("list")) - // - an integer representing the selected child element, - // or 0 if this object is selected (GetType() == wxT("long")) - // - a "void*" pointer to a wxAccessible child object - virtual wxAccStatus GetSelections(wxVariant *selections); - - // Returns a state constant. - virtual wxAccStatus GetState(int childId, long *state); - - // Returns a localized string representing the value for the object - // or child. - virtual wxAccStatus GetValue(int childId, wxString *strValue); - -private: - TimeTextCtrl *mCtrl; - int mLastField; - int mLastDigit; -}; - -#endif // wxUSE_ACCESSIBILITY - -#endif // __AUDACITY_TIME_TEXT_CTRL__