diff --git a/src/LabelDialog.cpp b/src/LabelDialog.cpp index 5b0afc2a6..b34bfcaa2 100644 --- a/src/LabelDialog.cpp +++ b/src/LabelDialog.cpp @@ -47,6 +47,8 @@ enum Column Col_Label, Col_Stime, Col_Etime, + Col_Lfreq, + Col_Hfreq, Col_Max }; @@ -83,6 +85,8 @@ BEGIN_EVENT_TABLE(LabelDialog, wxDialog) EVT_BUTTON(wxID_OK, LabelDialog::OnOK) EVT_BUTTON(wxID_CANCEL, LabelDialog::OnCancel) EVT_COMMAND(wxID_ANY, EVT_TIMETEXTCTRL_UPDATED, LabelDialog::OnUpdate) + EVT_COMMAND(wxID_ANY, EVT_FREQUENCYTEXTCTRL_UPDATED, + LabelDialog::OnFreqUpdate) END_EVENT_TABLE() LabelDialog::LabelDialog(wxWindow *parent, @@ -92,7 +96,7 @@ LabelDialog::LabelDialog(wxWindow *parent, int index, ViewInfo &viewinfo, double rate, - const wxString & format) + const wxString & format, const wxString &freqFormat) : wxDialog(parent, wxID_ANY, _("Edit Labels"), @@ -106,6 +110,7 @@ LabelDialog::LabelDialog(wxWindow *parent, , mViewInfo(&viewinfo), mRate(rate), mFormat(format) + , mFreqFormat(freqFormat) { SetName(GetTitle()); @@ -158,12 +163,19 @@ LabelDialog::LabelDialog(wxWindow *parent, mGrid->SetColLabelValue(2,_("Start Time")); /* i18n-hint: (noun) of a label*/ mGrid->SetColLabelValue(3,_("End Time")); + /* i18n-hint: (noun) of a label*/ + mGrid->SetColLabelValue(4,_("Low Frequency")); + /* i18n-hint: (noun) of a label*/ + mGrid->SetColLabelValue(5,_("High Frequency")); // Create and remember editors. No need to DELETE these as the wxGrid will // do it for us. (The DecRef() that is needed after GetDefaultEditorForType // becomes the duty of the wxGridCellAttr objects after we set them in the grid.) mChoiceEditor = (ChoiceEditor *) mGrid->GetDefaultEditorForType(GRID_VALUE_CHOICE); - mTimeEditor = (TimeEditor *) mGrid->GetDefaultEditorForType(GRID_VALUE_TIME); + mTimeEditor = static_cast + (mGrid->GetDefaultEditorForType(GRID_VALUE_TIME)); + mFrequencyEditor = static_cast + (mGrid->GetDefaultEditorForType(GRID_VALUE_FREQUENCY)); // Initialize and set the track name column attributes wxGridCellAttr *attr; @@ -180,6 +192,15 @@ LabelDialog::LabelDialog(wxWindow *parent, mGrid->SetColAttr(Col_Etime, attr->Clone()); + // Initialize and set the frequency column attributes + mGrid->SetColAttr(Col_Lfreq, (attr = safenew wxGridCellAttr)); + // Don't need DecRef() after this GetDefaultRendererForType. + attr->SetRenderer(mGrid->GetDefaultRendererForType(GRID_VALUE_FREQUENCY)); + attr->SetEditor(mFrequencyEditor); + attr->SetAlignment(wxALIGN_CENTER, wxALIGN_CENTER); + + mGrid->SetColAttr(Col_Hfreq, attr->Clone()); + // Seems there's a bug in wxGrid. Adding only 1 row does not // allow SetCellSize() to work properly and you will not get // the expected 1 row by 4 column cell. @@ -238,6 +259,8 @@ bool LabelDialog::TransferDataToWindow() mChoiceEditor->SetChoices(mTrackNames); mTimeEditor->SetFormat(mFormat); mTimeEditor->SetRate(mRate); + mFrequencyEditor->SetFormat(mFreqFormat); + mFrequencyEditor->SetRate(mRate); // Disable redrawing until we're done mGrid->BeginBatch(); @@ -261,9 +284,10 @@ bool LabelDialog::TransferDataToWindow() wxString::Format(wxT("%g"), rd.selectedRegion.t0())); mGrid->SetCellValue(i, Col_Etime, wxString::Format(wxT("%g"), rd.selectedRegion.t1())); - - // PRL: to do: -- populate future additional selection fields - // and write event code to update them from controls + mGrid->SetCellValue(i, Col_Lfreq, + wxString::Format(wxT("%g"), rd.selectedRegion.f0())); + mGrid->SetCellValue(i, Col_Hfreq, + wxString::Format(wxT("%g"), rd.selectedRegion.f1())); } // Autosize all the rows @@ -278,6 +302,8 @@ bool LabelDialog::TransferDataToWindow() // Autosize the time columns and set their minimal widths mGrid->AutoSizeColumn(Col_Stime); mGrid->AutoSizeColumn(Col_Etime); + mGrid->AutoSizeColumn(Col_Lfreq); + mGrid->AutoSizeColumn(Col_Hfreq); // We're done, so allow the grid to redraw mGrid->EndBatch(); @@ -466,6 +492,15 @@ void LabelDialog::OnUpdate(wxCommandEvent &event) event.Skip(false); } +void LabelDialog::OnFreqUpdate(wxCommandEvent &event) +{ + // Remember the NEW format and repopulate grid + mFreqFormat = event.GetString(); + TransferDataToWindow(); + + event.Skip(false); +} + void LabelDialog::OnInsert(wxCommandEvent &event) { int cnt = mData.size(); @@ -716,6 +751,14 @@ void LabelDialog::OnCellChange(wxGridEvent &event) case Col_Etime: OnChangeEtime(event, row, rd); break; + + case Col_Lfreq: + OnChangeLfreq(event, row, rd); + break; + + case Col_Hfreq: + OnChangeHfreq(event, row, rd); + break; } // Done...no need for protection anymore @@ -790,6 +833,30 @@ void LabelDialog::OnChangeEtime(wxGridEvent & WXUNUSED(event), int row, RowData return; } +void LabelDialog::OnChangeLfreq(wxGridEvent & WXUNUSED(event), int row, RowData *rd) +{ + // Remember the value...no need to repopulate + double f; + mGrid->GetCellValue(row, Col_Lfreq).ToDouble(&f); + rd->selectedRegion.setF0(f, false); + mGrid->SetCellValue(row, Col_Hfreq, wxString::Format(wxT("%g"), + rd->selectedRegion.f1())); + + return; +} + +void LabelDialog::OnChangeHfreq(wxGridEvent & WXUNUSED(event), int row, RowData *rd) +{ + // Remember the value...no need to repopulate + double f; + mGrid->GetCellValue(row, Col_Hfreq).ToDouble(&f); + rd->selectedRegion.setF1(f, false); + mGrid->SetCellValue(row, Col_Lfreq, wxString::Format(wxT("%g"), + rd->selectedRegion.f0())); + + return; +} + void LabelDialog::OnOK(wxCommandEvent & WXUNUSED(event)) { if (mGrid->IsCellEditControlShown()) { diff --git a/src/LabelDialog.h b/src/LabelDialog.h index 66d071504..62d78fde7 100644 --- a/src/LabelDialog.h +++ b/src/LabelDialog.h @@ -47,7 +47,7 @@ class LabelDialog final : public wxDialog ViewInfo &viewinfo, double rate, - const wxString & format); + const wxString & format, const wxString &freqFormat); ~LabelDialog(); bool Show(bool show = true) override; @@ -63,6 +63,7 @@ class LabelDialog final : public wxDialog wxString TrackName(int & index, const wxString &dflt = _("Label Track")); void OnUpdate(wxCommandEvent &event); + void OnFreqUpdate(wxCommandEvent &event); void OnInsert(wxCommandEvent &event); void OnRemove(wxCommandEvent &event); void OnImport(wxCommandEvent &event); @@ -73,6 +74,8 @@ class LabelDialog final : public wxDialog void OnChangeLabel(wxGridEvent &event, int row, RowData *rd); void OnChangeStime(wxGridEvent &event, int row, RowData *rd); void OnChangeEtime(wxGridEvent &event, int row, RowData *rd); + void OnChangeLfreq(wxGridEvent &event, int row, RowData *rd); + void OnChangeHfreq(wxGridEvent &event, int row, RowData *rd); void OnOK(wxCommandEvent &event); void OnCancel(wxCommandEvent &event); @@ -80,7 +83,8 @@ class LabelDialog final : public wxDialog Grid *mGrid; ChoiceEditor *mChoiceEditor; - TimeEditor *mTimeEditor; + NumericEditor *mTimeEditor; + NumericEditor *mFrequencyEditor; RowDataArray mData; @@ -92,6 +96,7 @@ class LabelDialog final : public wxDialog wxArrayString mTrackNames; double mRate; wxString mFormat; + wxString mFreqFormat; int mInitialRow; diff --git a/src/Menus.cpp b/src/Menus.cpp index ba8aff877..3bc770965 100644 --- a/src/Menus.cpp +++ b/src/Menus.cpp @@ -6553,12 +6553,13 @@ void AudacityProject::OnAddLabelPlaying() void AudacityProject::DoEditLabels(LabelTrack *lt, int index) { - wxString format = GetSelectionFormat(); + wxString format = GetSelectionFormat(), + freqFormat = GetFrequencySelectionFormatName(); LabelDialog dlg(this, *GetTrackFactory(), mTracks, lt, index, mViewInfo, mRate, - format); + format, freqFormat); if (dlg.ShowModal() == wxID_OK) { PushState(_("Edited labels"), _("Label")); diff --git a/src/widgets/Grid.cpp b/src/widgets/Grid.cpp index d0584e4a4..e55e2c508 100644 --- a/src/widgets/Grid.cpp +++ b/src/widgets/Grid.cpp @@ -25,27 +25,25 @@ #include "Grid.h" #include "NumericTextCtrl.h" +#include "../SelectedRegion.h" -TimeEditor::TimeEditor() -{ - TimeEditor(wxT("seconds"), 44100); -} - -TimeEditor::TimeEditor(const wxString &format, double rate) +NumericEditor::NumericEditor + (NumericConverter::Type type, const wxString &format, double rate) { + mType = type; mFormat = format; mRate = rate; mOld = 0.0; } -TimeEditor::~TimeEditor() +NumericEditor::~NumericEditor() { } -void TimeEditor::Create(wxWindow *parent, wxWindowID id, wxEvtHandler *handler) +void NumericEditor::Create(wxWindow *parent, wxWindowID id, wxEvtHandler *handler) { wxASSERT(parent); // to justify safenew - m_control = safenew NumericTextCtrl(NumericConverter::TIME, parent, + auto control = safenew NumericTextCtrl(mType, parent, wxID_ANY, mFormat, mOld, @@ -53,11 +51,14 @@ void TimeEditor::Create(wxWindow *parent, wxWindowID id, wxEvtHandler *handler) wxDefaultPosition, wxDefaultSize, true); + if (mType == NumericTextCtrl::FREQUENCY) + control->SetInvalidValue(SelectedRegion::UndefinedFrequency); + m_control = control; wxGridCellEditor::Create(parent, id, handler); } -void TimeEditor::SetSize(const wxRect &rect) +void NumericEditor::SetSize(const wxRect &rect) { wxSize size = m_control->GetSize(); @@ -68,32 +69,24 @@ void TimeEditor::SetSize(const wxRect &rect) m_control->Move(x, y); } -void TimeEditor::BeginEdit(int row, int col, wxGrid *grid) +void NumericEditor::BeginEdit(int row, int col, wxGrid *grid) { wxGridTableBase *table = grid->GetTable(); mOldString = table->GetValue(row, col); mOldString.ToDouble(&mOld); - GetTimeCtrl()->SetValue(mOld); - GetTimeCtrl()->EnableMenu(); + auto control = GetNumericTextControl(); + control->SetValue(mOld); + control->EnableMenu(); - GetTimeCtrl()->SetFocus(); + control->SetFocus(); } -bool TimeEditor::EndEdit(int row, int col, wxGrid *grid) -{ - wxString newvalue; - bool changed = EndEdit(row, col, grid, mOldString, &newvalue); - if (changed) { - ApplyEdit(row, col, grid); - } - return changed; -} -bool TimeEditor::EndEdit(int WXUNUSED(row), int WXUNUSED(col), const wxGrid *WXUNUSED(grid), const wxString &WXUNUSED(oldval), wxString *newval) +bool NumericEditor::EndEdit(int WXUNUSED(row), int WXUNUSED(col), const wxGrid *WXUNUSED(grid), const wxString &WXUNUSED(oldval), wxString *newval) { - double newtime = GetTimeCtrl()->GetValue(); + double newtime = GetNumericTextControl()->GetValue(); bool changed = newtime != mOld; if (changed) { @@ -104,17 +97,17 @@ bool TimeEditor::EndEdit(int WXUNUSED(row), int WXUNUSED(col), const wxGrid *WXU return changed; } -void TimeEditor::ApplyEdit(int row, int col, wxGrid *grid) +void NumericEditor::ApplyEdit(int row, int col, wxGrid *grid) { grid->GetTable()->SetValue(row, col, mValueAsString); } -void TimeEditor::Reset() +void NumericEditor::Reset() { - GetTimeCtrl()->SetValue(mOld); + GetNumericTextControl()->SetValue(mOld); } -bool TimeEditor::IsAcceptedKey(wxKeyEvent &event) +bool NumericEditor::IsAcceptedKey(wxKeyEvent &event) { if (wxGridCellEditor::IsAcceptedKey(event)) { if (event.GetKeyCode() == WXK_RETURN) { @@ -126,37 +119,41 @@ bool TimeEditor::IsAcceptedKey(wxKeyEvent &event) } // Clone is required by wxwidgets; implemented via copy constructor -wxGridCellEditor *TimeEditor::Clone() const +wxGridCellEditor *NumericEditor::Clone() const { - return safenew TimeEditor(mFormat, mRate); + return safenew NumericEditor{ mType, mFormat, mRate }; } -wxString TimeEditor::GetValue() const +wxString NumericEditor::GetValue() const { - return wxString::Format(wxT("%g"), GetTimeCtrl()->GetValue()); + return wxString::Format(wxT("%g"), GetNumericTextControl()->GetValue()); } -wxString TimeEditor::GetFormat() +wxString NumericEditor::GetFormat() const { return mFormat; } -double TimeEditor::GetRate() +double NumericEditor::GetRate() const { return mRate; } -void TimeEditor::SetFormat(const wxString &format) +void NumericEditor::SetFormat(const wxString &format) { mFormat = format; } -void TimeEditor::SetRate(double rate) +void NumericEditor::SetRate(double rate) { mRate = rate; } -void TimeRenderer::Draw(wxGrid &grid, +NumericRenderer::~NumericRenderer() +{ +} + +void NumericRenderer::Draw(wxGrid &grid, wxGridCellAttr &attr, wxDC &dc, const wxRect &rect, @@ -167,25 +164,26 @@ void TimeRenderer::Draw(wxGrid &grid, wxGridCellRenderer::Draw(grid, attr, dc, rect, row, col, isSelected); wxGridTableBase *table = grid.GetTable(); - TimeEditor *te = (TimeEditor *) grid.GetCellEditor(row, col); + NumericEditor *ne = + static_cast(grid.GetCellEditor(row, col)); wxString tstr; - if (te) { + if (ne) { double value; table->GetValue(row, col).ToDouble(&value); - NumericTextCtrl tt(NumericConverter::TIME, &grid, + NumericTextCtrl tt(mType, &grid, wxID_ANY, - te->GetFormat(), + ne->GetFormat(), value, - te->GetRate(), + ne->GetRate(), wxPoint(10000, 10000), // create offscreen wxDefaultSize, true); tstr = tt.GetString(); - te->DecRef(); + ne->DecRef(); } dc.SetBackgroundMode(wxTRANSPARENT); @@ -218,39 +216,40 @@ void TimeRenderer::Draw(wxGrid &grid, grid.DrawTextRectangle(dc, tstr, rect, hAlign, vAlign); } -wxSize TimeRenderer::GetBestSize(wxGrid &grid, +wxSize NumericRenderer::GetBestSize(wxGrid &grid, wxGridCellAttr & WXUNUSED(attr), wxDC & WXUNUSED(dc), int row, int col) { wxGridTableBase *table = grid.GetTable(); - TimeEditor *te = (TimeEditor *) grid.GetCellEditor(row, col); + NumericEditor *ne = + static_cast(grid.GetCellEditor(row, col)); wxSize sz; - if (te) { + if (ne) { double value; table->GetValue(row, col).ToDouble(&value); - NumericTextCtrl tt(NumericConverter::TIME, &grid, + NumericTextCtrl tt(mType, &grid, wxID_ANY, - te->GetFormat(), + ne->GetFormat(), value, - te->GetRate(), + ne->GetRate(), wxPoint(10000, 10000), // create offscreen wxDefaultSize, true); sz = tt.GetSize(); - te->DecRef(); + ne->DecRef(); } return sz; } // Clone is required by wxwidgets; implemented via copy constructor -wxGridCellRenderer *TimeRenderer::Clone() const +wxGridCellRenderer *NumericRenderer::Clone() const { - return safenew TimeRenderer(); + return safenew NumericRenderer{ mType }; } ChoiceEditor::ChoiceEditor(size_t count, const wxString choices[]) @@ -392,13 +391,21 @@ Grid::Grid(wxWindow *parent, GetGridWindow()->SetAccessible(mAx = safenew GridAx(this)); #endif + // RegisterDataType takes ownership of renderer and editor + RegisterDataType(GRID_VALUE_TIME, - new TimeRenderer, - new TimeEditor); + safenew NumericRenderer{ NumericConverter::TIME }, + safenew NumericEditor + { NumericTextCtrl::TIME, wxT("seconds"), 44100.0 }); + + RegisterDataType(GRID_VALUE_FREQUENCY, + safenew NumericRenderer{ NumericConverter::FREQUENCY }, + safenew NumericEditor + { NumericTextCtrl::FREQUENCY, wxT("Hz"), 44100.0 }); RegisterDataType(GRID_VALUE_CHOICE, - new wxGridCellStringRenderer, - new ChoiceEditor); + safenew wxGridCellStringRenderer, + safenew ChoiceEditor); } Grid::~Grid() diff --git a/src/widgets/Grid.h b/src/widgets/Grid.h index 728963d3f..1a434f08b 100644 --- a/src/widgets/Grid.h +++ b/src/widgets/Grid.h @@ -18,6 +18,7 @@ #include #include #include +#include "NumericTextCtrl.h" #if wxUSE_ACCESSIBILITY #include @@ -29,82 +30,88 @@ class GridAx; class NumericTextCtrl; // ---------------------------------------------------------------------------- -// TimeEditor +// NumericEditor // // wxGridCellEditor for the NumericTextCtrl. // ---------------------------------------------------------------------------- #define GRID_VALUE_TIME wxT("Time") +#define GRID_VALUE_FREQUENCY wxT("Frequency") -class TimeEditor final : public wxGridCellEditor +class NumericEditor /* not final */ : public wxGridCellEditor { - public: +public: - TimeEditor(); + NumericEditor + (NumericConverter::Type type, const wxString &format, double rate); - TimeEditor(const wxString &format, double rate); - - ~TimeEditor(); + ~NumericEditor(); // Precondition: parent != NULL - void Create(wxWindow *parent, wxWindowID id, wxEvtHandler *handler); + void Create(wxWindow *parent, wxWindowID id, wxEvtHandler *handler) override; - bool IsAcceptedKey(wxKeyEvent &event); + bool IsAcceptedKey(wxKeyEvent &event) override; - void SetSize(const wxRect &rect); + void SetSize(const wxRect &rect) override; - void BeginEdit(int row, int col, wxGrid *grid); + void BeginEdit(int row, int col, wxGrid *grid) override; - bool EndEdit(int row, int col, wxGrid *grid); + bool EndEdit(int row, int col, const wxGrid *grid, const wxString &oldval, wxString *newval) override; - bool EndEdit(int row, int col, const wxGrid *grid, const wxString &oldval, wxString *newval); + void ApplyEdit(int row, int col, wxGrid *grid) override; - void ApplyEdit(int row, int col, wxGrid *grid); + void Reset() override; - void Reset(); - - wxString GetFormat(); - double GetRate(); + wxString GetFormat() const; + double GetRate() const; void SetFormat(const wxString &format); void SetRate(double rate); wxGridCellEditor *Clone() const override; - wxString GetValue() const; + wxString GetValue() const override; - NumericTextCtrl *GetTimeCtrl() const { return (NumericTextCtrl *)m_control; } + NumericTextCtrl *GetNumericTextControl() const + { return static_cast(m_control); } private: wxString mFormat; double mRate; + NumericConverter::Type mType; double mOld; wxString mOldString; wxString mValueAsString; }; // ---------------------------------------------------------------------------- -// TimeRenderer +// NumericRenderer // // wxGridCellRenderer for the NumericTextCtrl. // ---------------------------------------------------------------------------- -class TimeRenderer final : public wxGridCellRenderer +class NumericRenderer final : public wxGridCellRenderer { public: - void Draw(wxGrid &grid, + NumericRenderer(NumericConverter::Type type) : mType{ type } {} + ~NumericRenderer() override; + + void Draw(wxGrid &grid, wxGridCellAttr &attr, wxDC &dc, const wxRect &rect, int row, int col, - bool isSelected); + bool isSelected) override; wxSize GetBestSize(wxGrid &grid, wxGridCellAttr &attr, wxDC &dc, int row, - int col); + int col) override; wxGridCellRenderer *Clone() const override; + +private: + NumericConverter::Type mType; }; // ---------------------------------------------------------------------------- diff --git a/src/widgets/NumericTextCtrl.h b/src/widgets/NumericTextCtrl.h index 90baf2f54..cc863af29 100644 --- a/src/widgets/NumericTextCtrl.h +++ b/src/widgets/NumericTextCtrl.h @@ -160,7 +160,7 @@ class NumericTextCtrl final : public wxControl, public NumericConverter void SetReadOnly(bool readOnly = true); void EnableMenu(bool enable = true); - // The text control permits typing '-' to make the value invalid only if this + // The text control permits typing DELETE to make the value invalid only if this // function has previously been called. // Maybe you want something other than the default of -1 to indicate the invalid value // this control returns to the program, so you can specify.