Audacity  2.2.2
LabelDialog.cpp
Go to the documentation of this file.
1 /**********************************************************************
2 
3  Audacity: A Digital Audio Editor
4 
5  LabelDialog.cpp
6 
7  Dominic Mazzoni
8 
9 *******************************************************************//*******************************************************************/
15 
16 #include "Audacity.h"
17 #include "LabelDialog.h"
18 
19 #include <wx/button.h>
20 #include <wx/defs.h>
21 #include <wx/choice.h>
22 #include <wx/dc.h>
23 #include <wx/dialog.h>
24 #include <wx/filedlg.h>
25 #include <wx/grid.h>
26 #include <wx/intl.h>
27 #include <wx/settings.h>
28 #include <wx/sizer.h>
29 #include <wx/stattext.h>
30 #include <wx/textdlg.h>
31 
32 #include "ShuttleGui.h"
33 #include "Internat.h"
34 #include "LabelTrack.h"
35 #include "Prefs.h"
36 #include "Project.h"
37 #include "ViewInfo.h"
39 #include "widgets/ErrorDialog.h"
40 
41 #include "FileNames.h"
42 #include <limits>
43 
44 enum Column
45 {
53 };
54 
55 
56 
57 class RowData
58 {
59  public:
60  RowData(int index_, const wxString &title_, const SelectedRegion &selectedRegion_)
61  : index(index_), title(title_), selectedRegion(selectedRegion_)
62  {}
63 
64  int index;
65  wxString title;
67 };
68 
69 enum {
70  ID_INSERTA = 11000,
75 };
76 
77 BEGIN_EVENT_TABLE(LabelDialog, wxDialogWrapper)
78  EVT_GRID_SELECT_CELL(LabelDialog::OnSelectCell)
79  EVT_GRID_CELL_CHANGED(LabelDialog::OnCellChange)
82  EVT_BUTTON(ID_REMOVE, LabelDialog::OnRemove)
85  EVT_BUTTON(wxID_OK, LabelDialog::OnOK)
86  EVT_BUTTON(wxID_CANCEL, LabelDialog::OnCancel)
87  EVT_COMMAND(wxID_ANY, EVT_TIMETEXTCTRL_UPDATED, LabelDialog::OnUpdate)
88  EVT_COMMAND(wxID_ANY, EVT_FREQUENCYTEXTCTRL_UPDATED,
89  LabelDialog::OnFreqUpdate)
91 
92 LabelDialog::LabelDialog(wxWindow *parent,
93  TrackFactory &factory,
94  TrackList *tracks,
95  LabelTrack *selectedTrack,
96  int index,
97  ViewInfo &viewinfo,
98  double rate,
99  const wxString & format, const wxString &freqFormat)
100 : wxDialogWrapper(parent,
101  wxID_ANY,
102  _("Edit Labels"),
103  wxDefaultPosition,
104  wxSize(800, 600),
105  wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER),
106  mFactory(factory),
107  mTracks(tracks)
108  , mSelectedTrack(selectedTrack)
109  , mIndex(index)
110  , mViewInfo(&viewinfo),
111  mRate(rate),
112  mFormat(format)
113  , mFreqFormat(freqFormat)
114 {
115  SetName(GetTitle());
116 
117  {
118  // Create the main sizer
119  auto vs = std::make_unique<wxBoxSizer>(wxVERTICAL);
120 
121  // A little instruction
122  wxStaticText *instruct =
123  safenew wxStaticText(this,
124  wxID_ANY,
125  _("Press F2 or double click to edit cell contents."));
126  instruct->SetName(instruct->GetLabel()); // fix for bug 577 (NVDA/Narrator screen readers do not read static text in dialogs)
127  vs->Add(instruct,
128  0,
129  wxALIGN_LEFT | wxALL,
130  5);
131 
132  // Create the main sizer
133  mGrid = safenew Grid(this, wxID_ANY);
134  vs->Add(mGrid, 1, wxEXPAND | wxALL, 5);
135 
136  // Create the action buttons
137  {
138  auto hs = std::make_unique<wxBoxSizer>(wxHORIZONTAL);
139  hs->Add(safenew wxButton(this, ID_INSERTA, _("Insert &After")), 1, wxCENTER | wxALL, 5);
140  hs->Add(safenew wxButton(this, ID_INSERTB, _("Insert &Before")), 1, wxCENTER | wxALL, 5);
141  hs->Add(safenew wxButton(this, ID_REMOVE, _("&Remove")), 1, wxCENTER | wxALL, 5);
142  hs->Add(safenew wxButton(this, ID_IMPORT, _("&Import...")), 1, wxCENTER | wxALL, 5);
143  hs->Add(safenew wxButton(this, ID_EXPORT, _("&Export...")), 1, wxCENTER | wxALL, 5);
144  vs->Add(hs.release(), 0, wxEXPAND | wxCENTER | wxALL, 5);
145  }
146 
147  // Create the exit buttons
148  vs->Add(CreateStdButtonSizer(this, eCancelButton | eOkButton).release(), 0, wxEXPAND);
149 
150  // Make it so
151  SetSizer(vs.release());
152  }
153 
154  // Build the initial (empty) grid
155  mGrid->CreateGrid(0, Col_Max);
156  mGrid->SetDefaultCellAlignment(wxALIGN_LEFT, wxALIGN_CENTER);
157 
158  /* i18n-hint: (noun). A track contains waves, audio etc.*/
159  mGrid->SetColLabelValue(0,_("Track"));
160  /* i18n-hint: (noun)*/
161  mGrid->SetColLabelValue(1,_("Label"));
162  /* i18n-hint: (noun) of a label*/
163  mGrid->SetColLabelValue(2,_("Start Time"));
164  /* i18n-hint: (noun) of a label*/
165  mGrid->SetColLabelValue(3,_("End Time"));
166  /* i18n-hint: (noun) of a label*/
167  mGrid->SetColLabelValue(4,_("Low Frequency"));
168  /* i18n-hint: (noun) of a label*/
169  mGrid->SetColLabelValue(5,_("High Frequency"));
170 
171  // Create and remember editors. No need to DELETE these as the wxGrid will
172  // do it for us. (The DecRef() that is needed after GetDefaultEditorForType
173  // becomes the duty of the wxGridCellAttr objects after we set them in the grid.)
174  mChoiceEditor = (ChoiceEditor *) mGrid->GetDefaultEditorForType(GRID_VALUE_CHOICE);
175  mTimeEditor = static_cast<NumericEditor*>
176  (mGrid->GetDefaultEditorForType(GRID_VALUE_TIME));
177  mFrequencyEditor = static_cast<NumericEditor *>
178  (mGrid->GetDefaultEditorForType(GRID_VALUE_FREQUENCY));
179 
180  // Initialize and set the track name column attributes
181  wxGridCellAttr *attr;
182  mGrid->SetColAttr(Col_Track, (attr = safenew wxGridCellAttr));
183  attr->SetEditor(mChoiceEditor);
184  mTrackNames.Add(_("New..."));
185 
186  // Initialize and set the time column attributes
187  mGrid->SetColAttr(Col_Stime, (attr = safenew wxGridCellAttr));
188  // Don't need DecRef() after this GetDefaultRendererForType.
189  attr->SetRenderer(mGrid->GetDefaultRendererForType(GRID_VALUE_TIME));
190  attr->SetEditor(mTimeEditor);
191  attr->SetAlignment(wxALIGN_CENTER, wxALIGN_CENTER);
192 
193  mGrid->SetColAttr(Col_Etime, attr->Clone());
194 
195  // Initialize and set the frequency column attributes
196  mGrid->SetColAttr(Col_Lfreq, (attr = safenew wxGridCellAttr));
197  // Don't need DecRef() after this GetDefaultRendererForType.
198  attr->SetRenderer(mGrid->GetDefaultRendererForType(GRID_VALUE_FREQUENCY));
199  attr->SetEditor(mFrequencyEditor);
200  attr->SetAlignment(wxALIGN_CENTER, wxALIGN_CENTER);
201 
202  mGrid->SetColAttr(Col_Hfreq, attr->Clone());
203 
204  // Seems there's a bug in wxGrid. Adding only 1 row does not
205  // allow SetCellSize() to work properly and you will not get
206  // the expected 1 row by 4 column cell.
207  //
208  // So, we set the minimum row height to 0 and basically hide
209  // the extra row by setting its height to 0. And not allowing the
210  // rows to be manually resized prevents the user from ever seeing
211  // the extra row.
212  mGrid->SetRowMinimalAcceptableHeight(0);
213  mGrid->EnableDragRowSize(false);
214 
215  // Locate all labels in current track list
216  FindAllLabels();
217 
218  // Populate the grid
219  TransferDataToWindow();
220 
221  // Resize the label name column and ensure it doesn't go below an
222  // arbitrary width.
223  //
224  // This should not be in TransferDataToWindow() since a user might
225  // resize the column and we'd resize it back to the minimum.
226  mGrid->AutoSizeColumn(Col_Label, false );
227  mGrid->SetColSize(Col_Label, wxMax(150, mGrid->GetColSize(Col_Label)));
228  mGrid->SetColMinimalWidth(Col_Label, mGrid->GetColSize(Col_Label));
229 
230  // Layout the works
231  Layout();
232 
233  // Resize width based on width of columns and the vertical scrollbar
234  wxRect r = mGrid->GetGridColLabelWindow()->GetRect();
235  wxScrollBar sb(this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxSB_VERTICAL);
236  r.width += sb.GetSize().GetWidth() + 6;
237  SetClientSize(r.width, 300);
238 
239  // Make sure it doesn't go below this size
240  r = GetRect();
241  SetSizeHints(r.GetWidth(), r.GetHeight());
242 
243  // Center on display
244  Center();
245 }
246 
248 {
249 }
250 
252 {
253  int cnt = mData.size();
254  int i;
255 
256  // Set the editor parameters. Do this each time since they may change
257  // due to NEW tracks and change in NumericTextCtrl format. Rate won't
258  // change but might as well leave it here.
264 
265  // Disable redrawing until we're done
266  mGrid->BeginBatch();
267 
268  // Delete all rows
269  if (mGrid->GetNumberRows()) {
270  mGrid->DeleteRows(0, mGrid->GetNumberRows());
271  }
272 
273  // Add the exact number that we'll need
274  mGrid->InsertRows(0, cnt);
275 
276  // Populate the rows
277  for (i = 0; i < cnt; i++) {
278  RowData &rd = mData[i];
279 
280  // Set the cell contents
281  mGrid->SetCellValue(i, Col_Track, TrackName(rd.index));
282  mGrid->SetCellValue(i, Col_Label, rd.title);
283  mGrid->SetCellValue(i, Col_Stime,
284  wxString::Format(wxT("%g"), rd.selectedRegion.t0()));
285  mGrid->SetCellValue(i, Col_Etime,
286  wxString::Format(wxT("%g"), rd.selectedRegion.t1()));
287  mGrid->SetCellValue(i, Col_Lfreq,
288  wxString::Format(wxT("%g"), rd.selectedRegion.f0()));
289  mGrid->SetCellValue(i, Col_Hfreq,
290  wxString::Format(wxT("%g"), rd.selectedRegion.f1()));
291  }
292 
293  // Autosize all the rows
294  mGrid->AutoSizeRows(true);
295 
296  // Resize the track name column. Use a wxChoice to determine the maximum
297  // width needed.
298  wxChoice tc(this, wxID_ANY, wxDefaultPosition, wxDefaultSize, mTrackNames);
299  mGrid->SetColSize(Col_Track, tc.GetSize().x);
300  mGrid->SetColMinimalWidth(Col_Track, tc.GetSize().x);
301 
302  // Autosize the time columns and set their minimal widths
303  mGrid->AutoSizeColumn(Col_Stime);
304  mGrid->AutoSizeColumn(Col_Etime);
305  mGrid->AutoSizeColumn(Col_Lfreq);
306  mGrid->AutoSizeColumn(Col_Hfreq);
307 
308  // We're done, so allow the grid to redraw
309  mGrid->EndBatch();
310 
311  return true;
312 }
313 
314 bool LabelDialog::Show(bool show)
315 {
316  bool ret = wxDialogWrapper::Show(show);
317 
318  // Set initial row
319  // (This will not work until the grid is actually displayed)
320  if (show && mInitialRow != -1) {
321  mGrid->SetGridCursor(mInitialRow, Col_Label);
322  }
323 
324  return ret;
325 }
326 
328 {
329  int cnt = mData.size();
330  int i;
332  Track *t;
333  int tndx = 0;
334 
335  // Clear label tracks of labels
336  for (t = iter.First(); t; t = iter.Next()) {
337  if (t->GetKind() == Track::Label) {
338  ++tndx;
339  LabelTrack *lt = static_cast<LabelTrack*>(t);
340  if (!mSelectedTrack) {
341  for (i = lt->GetNumLabels() - 1; i >= 0 ; i--) {
342  lt->DeleteLabel(i);
343  }
344  }
345  else if (mSelectedTrack == lt && mIndex > -1) {
346  lt->DeleteLabel(mIndex);
347  }
348  else
349  // Do nothing to the nonselected tracks
350  ;
351  }
352  }
353 
354  // Create any added tracks
355  while (tndx < (int)mTrackNames.GetCount() - 1) {
356 
357  // Extract the name
358  wxString name = mTrackNames[tndx + 1].AfterFirst(wxT('-')).Mid(1);
359 
360  // Create the NEW track and add to track list
361  auto newTrack = mFactory.NewLabelTrack();
362  newTrack->SetName(name);
363  mTracks->Add(std::move(newTrack));
364  tndx++;
365  }
366 
367  // Repopulate with updated labels
368  for (i = 0; i < cnt; i++) {
369  RowData &rd = mData[i];
370 
371  // Look for track with matching index
372  tndx = 1;
373  for (t = iter.First(); t; t = iter.Next()) {
374  if (t->GetKind() == Track::Label && rd.index == tndx++) {
375  break;
376  }
377  }
378  wxASSERT(t);
379  if (!t)
380  return false;
381 
382  // Add the label to it
383  static_cast<LabelTrack *>(t)->AddLabel(rd.selectedRegion, rd.title,-2);
384  static_cast<LabelTrack *>(t)->Unselect();
385  }
386 
387  return true;
388 }
389 
391 {
392  if (mGrid->IsCellEditControlShown()) {
393  mGrid->HideCellEditControl();
394  mGrid->SaveEditControlValue();
395  }
396 
397  return true;
398 }
399 
400 wxString LabelDialog::TrackName(int & index, const wxString &dflt)
401 {
402  // Generate a NEW track name if the passed index is out of range
403  if (index < 1 || index >= (int)mTrackNames.GetCount()) {
404  index = mTrackNames.GetCount();
405  mTrackNames.Add(wxString::Format(wxT("%d - %s"), index, dflt));
406  }
407 
408  // Return the track name
409  return mTrackNames[index];
410 }
411 
413 {
415  Track *t;
416 
417 
418  // Add labels from all label tracks
419  for (t = iter.First(); t; t = iter.Next()) {
420  if (t->GetKind() == Track::Label)
421  AddLabels(static_cast<const LabelTrack *>(t));
422  }
423 
424  FindInitialRow();
425 
426  if (mData.size() == 0) {
427  wxCommandEvent e;
428  OnInsert(e);
429  }
430 }
431 
433 {
434  wxString lab;
435  int tndx = 0;
436  int i;
437 
438  // Add a NEW track name
439  TrackName(tndx, t->GetName());
440 
441  // If editor was invoked for one label, add that one only, else add all.
442  if (!mSelectedTrack || mSelectedTrack == t) {
443  for (i = 0; i < t->GetNumLabels(); i++) {
444  const LabelStruct *ls = t->GetLabel(i);
445 
446  if (mIndex < 0 || mIndex == i)
447  mData.push_back(RowData(tndx, ls->title, ls->selectedRegion));
448  }
449  }
450 }
451 
453 {
454  int cnt = mData.size();
455  mInitialRow = -1;
456 
457  if (cnt == 0)
458  return;
459 
460  // find closest previous label
461 
462  double distMin = std::numeric_limits<double>::max();
463  double dist;
464  double t0 = mViewInfo->selectedRegion.t0();
465  int i;
466  for (i = 0; i < cnt; i++)
467  {
468  dist = t0 - mData[i].selectedRegion.t0();
469  if (dist >= 0.0 && dist < distMin)
470  {
471  mInitialRow = i;
472  distMin = dist;
473  }
474  }
475 
476  // if no previous label was found, find first label
477 
478  if (mInitialRow == -1)
479  {
480  double t0Min = std::numeric_limits<double>::max();
481  for (i = 0; i < cnt; i++)
482  {
483  if (mData[i].selectedRegion.t0() < t0Min)
484  {
485  mInitialRow = i;
486  t0Min = mData[i].selectedRegion.t0();
487  }
488  }
489  }
490 }
491 
492 void LabelDialog::OnUpdate(wxCommandEvent &event)
493 {
494  // Remember the NEW format and repopulate grid
495  mFormat = event.GetString();
497 
498  event.Skip(false);
499 }
500 
501 void LabelDialog::OnFreqUpdate(wxCommandEvent &event)
502 {
503  // Remember the NEW format and repopulate grid
504  mFreqFormat = event.GetString();
506 
507  event.Skip(false);
508 }
509 
510 void LabelDialog::OnInsert(wxCommandEvent &event)
511 {
512  int cnt = mData.size();
513  int row = 0;
514  int index = 0;
515 
516  // Make sure the edit control isn't active before inserting any rows
517  if (mGrid->IsCellEditControlShown()) {
518  mGrid->HideCellEditControl();
519  }
520 
521  // Attempt to guess which track the label should reside on
522  if (cnt > 0) {
523  row = mGrid->GetGridCursorRow();
524  if (row > 0 && row >= cnt) {
525  index = mTrackNames.Index(mGrid->GetCellValue(row - 1, Col_Track));
526  }
527  else {
528  index = mTrackNames.Index(mGrid->GetCellValue(row, Col_Track));
529  }
530  }
531 
532  // Insert NEW label before or after the current row
533  if (event.GetId() == ID_INSERTA && row < cnt) {
534  row++;
535  }
536  mData.insert(mData.begin() + row, RowData(index, wxT(""), SelectedRegion()));
537 
538  // Repopulate the grid
540 
541  // Reposition cursor to NEW row/col and put user into edit mode to
542  // set the label name
543  mGrid->SetGridCursor(row, Col_Label);
544  mGrid->EnableCellEditControl(true);
545  mGrid->ShowCellEditControl();
546 }
547 
548 void LabelDialog::OnRemove(wxCommandEvent & WXUNUSED(event))
549 {
550  int row = mGrid->GetGridCursorRow();
551  int col = mGrid->GetGridCursorCol();
552  int cnt = mData.size();
553 
554  // Don't try to remove if no labels exist
555  if (cnt == 0) {
556  return;
557  }
558 
559  // Make sure the edit control isn't active before removing rows
560  if (mGrid->IsCellEditControlShown()) {
561  mGrid->HideCellEditControl();
562  }
563 
564  // Remove the row
565  //RowData &rd = mData[row];
566  mData.erase(mData.begin() + row);
567 
568  // Repopulate the grid
570 
571  // Reposition the cursor
572  if (row > 0 && row >= --cnt) {
573  row--;
574  }
575  mGrid->SetGridCursor(row, col);
576 
577  // Make sure focus isn't lost
578  if (mData.size() == 0 && wxWindow::FindFocus() == mGrid->GetGridWindow()) {
579  wxWindow *ok = wxWindow::FindWindowById( wxID_OK, this);
580  if (ok) {
581  ok->SetFocus();
582  }
583  }
584 }
585 
586 void LabelDialog::OnImport(wxCommandEvent & WXUNUSED(event))
587 {
588  // Ask user for a filename
589  wxString fileName =
591  _("Select a text file containing labels"),
592  wxEmptyString, // Path
593  wxT(""), // Name
594  wxT(".txt"), // Extension
595  _("Text files (*.txt)|*.txt|All files|*"),
596  wxRESIZE_BORDER, // Flags
597  this); // Parent
598 
599  // They gave us one...
600  if (fileName != wxT("")) {
601  wxTextFile f;
602 
603  // Get at the data
604  f.Open(fileName);
605  if (!f.IsOpened()) {
607  wxString::Format( _("Could not open file: %s"), fileName ));
608  }
609  else {
610  // Create a temporary label track and load the labels
611  // into it
612  auto lt = mFactory.NewLabelTrack();
613  lt->Import(f);
614 
615  // Add the labels to our collection
616  AddLabels(lt.get());
617 
618  // Done with the temporary track
619  }
620 
621  // Repopulate the grid
623  }
624 }
625 
626 void LabelDialog::OnExport(wxCommandEvent & WXUNUSED(event))
627 {
628  int cnt = mData.size();
629 
630  // Silly user (could just disable the button, but that's a hassle ;-))
631  if (cnt == 0) {
632  AudacityMessageBox(_("No labels to export."));
633  return;
634  }
635 
636  // Extract the actual name.
637  wxString fName = mTrackNames[mTrackNames.GetCount() - 1].AfterFirst(wxT('-')).Mid(1);
638 
640  _("Export Labels As:"),
641  wxEmptyString,
642  fName,
643  wxT("txt"),
644  wxT("*.txt"),
645  wxFD_SAVE | wxFD_OVERWRITE_PROMPT | wxRESIZE_BORDER,
646  this);
647 
648  if (fName == wxT(""))
649  return;
650 
651  // Move existing files out of the way. Otherwise wxTextFile will
652  // append to (rather than replace) the current file.
653 
654  if (wxFileExists(fName)) {
655 #ifdef __WXGTK__
656  wxString safetyFileName = fName + wxT("~");
657 #else
658  wxString safetyFileName = fName + wxT(".bak");
659 #endif
660 
661  if (wxFileExists(safetyFileName))
662  wxRemoveFile(safetyFileName);
663 
664  wxRename(fName, safetyFileName);
665  }
666 
667  wxTextFile f(fName);
668 #ifdef __WXMAC__
669  wxFile{}.Create(fName);
670 #else
671  f.Create();
672 #endif
673  f.Open();
674  if (!f.IsOpened()) {
676  wxString::Format( _("Couldn't write to file: %s"), fName ) );
677  return;
678  }
679 
680  // Transfer our collection to a temporary label track
681  auto lt = mFactory.NewLabelTrack();
682  int i;
683 
684  for (i = 0; i < cnt; i++) {
685  RowData &rd = mData[i];
686 
687  lt->AddLabel(rd.selectedRegion, rd.title,-2);
688  }
689 
690  // Export them and clean
691  lt->Export(f);
692 
693 #ifdef __WXMAC__
694  f.Write(wxTextFileType_Mac);
695 #else
696  f.Write();
697 #endif
698  f.Close();
699 }
700 
701 void LabelDialog::OnSelectCell(wxGridEvent &event)
702 {
704  Track *t = iter.First();
705  while( t )
706  {
707  t->SetSelected( true );
708  t = iter.Next();
709  }
710 
711  if (!mData.empty())
712  {
713  RowData &rd = mData[event.GetRow()];
715 
717  }
718 
719  event.Skip();
720 }
721 
722 void LabelDialog::OnCellChange(wxGridEvent &event)
723 {
724  static bool guard = false;
725  int row = event.GetRow();
726 
727  // Guard against recursion which can happen when a change to the "NEW label" row
728  // is made. When InsertRow() is done in TransferDataToWindow(), checks are made
729  // within wxGrid to see if the edit control is active and since it hasn't yet
730  // been marked inactive on the first time through here, we get entered again.
731  // Sort of a double change. I think this is probably a bug in wxGrid.
732  if (guard) {
733  return;
734  }
735  guard = true;
736 
737  // The change was to an existing label, so go process it based
738  // on which column was changed.
739  RowData *rd = &mData[row];
740  switch (event.GetCol())
741  {
742  case Col_Track:
743  OnChangeTrack(event, row, rd);
744  break;
745 
746  case Col_Label:
747  OnChangeLabel(event, row, rd);
748  break;
749 
750  case Col_Stime:
751  OnChangeStime(event, row, rd);
752  break;
753 
754  case Col_Etime:
755  OnChangeEtime(event, row, rd);
756  break;
757 
758  case Col_Lfreq:
759  OnChangeLfreq(event, row, rd);
760  break;
761 
762  case Col_Hfreq:
763  OnChangeHfreq(event, row, rd);
764  break;
765  }
766 
767  // Done...no need for protection anymore
768  guard = false;
769 
770  return;
771 }
772 
773 void LabelDialog::OnChangeTrack(wxGridEvent & WXUNUSED(event), int row, RowData *rd)
774 {
775  wxString val = mGrid->GetCellValue(row, Col_Track);
776 
777  // User selected the "New..." choice so ask for a NEW name
778  if (mTrackNames.Index(val) == 0) {
780  _("New Label Track"),
781  _("Enter track name"),
782  /* i18n-hint: (noun) it's the name of a kind of track.*/
783  _("Label Track"));
784 
785  // User canceled so repopulating the grid will set the track
786  // name to the orignal value
787  if (d.ShowModal() == wxID_CANCEL) {
789  return;
790  }
791 
792  // Force generation of a NEW track name
793  rd->index = 0;
794  TrackName(rd->index, d.GetValue());
795  }
796  else {
797  // Remember the tracks index
798  rd->index = mTrackNames.Index(val);
799  }
800 
801  // Repopulate the grid
803 
804  return;
805 }
806 
807 void LabelDialog::OnChangeLabel(wxGridEvent & WXUNUSED(event), int row, RowData *rd)
808 {
809  // Remember the value...no need to repopulate
810  rd->title = mGrid->GetCellValue(row, Col_Label);
811 
812  return;
813 }
814 
815 void LabelDialog::OnChangeStime(wxGridEvent & WXUNUSED(event), int row, RowData *rd)
816 {
817  // Remember the value...no need to repopulate
818  double t {};
819  mGrid->GetCellValue(row, Col_Stime).ToDouble(&t);
820  rd->selectedRegion.setT0(t, false);
821  mGrid->SetCellValue(row, Col_Etime, wxString::Format(wxT("%g"),
822  rd->selectedRegion.t1()));
823 
824  return;
825 }
826 
827 void LabelDialog::OnChangeEtime(wxGridEvent & WXUNUSED(event), int row, RowData *rd)
828 {
829  // Remember the value...no need to repopulate
830  double t {};
831  mGrid->GetCellValue(row, Col_Etime).ToDouble(&t);
832  rd->selectedRegion.setT1(t, false);
833  mGrid->SetCellValue(row, Col_Stime, wxString::Format(wxT("%g"),
834  rd->selectedRegion.t0()));
835 
836  return;
837 }
838 
839 void LabelDialog::OnChangeLfreq(wxGridEvent & WXUNUSED(event), int row, RowData *rd)
840 {
841  // Remember the value...no need to repopulate
842  double f;
843  mGrid->GetCellValue(row, Col_Lfreq).ToDouble(&f);
844  rd->selectedRegion.setF0(f, false);
845  mGrid->SetCellValue(row, Col_Hfreq, wxString::Format(wxT("%g"),
846  rd->selectedRegion.f1()));
847 
848  return;
849 }
850 
851 void LabelDialog::OnChangeHfreq(wxGridEvent & WXUNUSED(event), int row, RowData *rd)
852 {
853  // Remember the value...no need to repopulate
854  double f;
855  mGrid->GetCellValue(row, Col_Hfreq).ToDouble(&f);
856  rd->selectedRegion.setF1(f, false);
857  mGrid->SetCellValue(row, Col_Lfreq, wxString::Format(wxT("%g"),
858  rd->selectedRegion.f0()));
859 
860  return;
861 }
862 
863 void LabelDialog::OnOK(wxCommandEvent & WXUNUSED(event))
864 {
865  if (mGrid->IsCellEditControlShown()) {
866  mGrid->SaveEditControlValue();
867  mGrid->HideCellEditControl();
868  return;
869  }
870 
871  // Standard handling
872  if (Validate() && TransferDataFromWindow()) {
873  EndModal(wxID_OK);
874  }
875 
876  return;
877 }
878 
879 void LabelDialog::OnCancel(wxCommandEvent & WXUNUSED(event))
880 {
881  if (mGrid->IsCellEditControlShown()) {
882  auto editor = mGrid->GetCellEditor(mGrid->GetGridCursorRow(),
883  mGrid->GetGridCursorCol());
884  editor->Reset();
885  // To avoid memory leak, don't forget DecRef()!
886  editor->DecRef();
887  mGrid->HideCellEditControl();
888  return;
889  }
890 
891  // Standard handling
892  EndModal(wxID_CANCEL);
893 
894  return;
895 }
void OnExport(const wxString &Format)
bool Show(bool show=true) override
std::unique_ptr< wxSizer > CreateStdButtonSizer(wxWindow *parent, long buttons, wxWindow *extra)
void RedrawProject(const bool bForceWaveTracks=false)
Definition: Project.cpp:1356
wxArrayString mTrackNames
Definition: LabelDialog.h:96
A list of TrackListNode items.
Definition: Track.h:553
void FindAllLabels()
EVT_COMMAND(wxID_ANY, EVT_FREQUENCYTEXTCTRL_UPDATED, LabelDialog::OnFreqUpdate) LabelDialog
Definition: LabelDialog.cpp:88
double t0() const
ViewInfo is used mainly to hold the zooming, selection and scroll information. It also has some statu...
Definition: ViewInfo.h:141
#define GRID_VALUE_TIME
Definition: Grid.h:39
SelectedRegion selectedRegion
Definition: ViewInfo.h:160
LabelTrack * mSelectedTrack
Definition: LabelDialog.h:93
void OnRemove(wxCommandEvent &event)
void OnOK(wxCommandEvent &event)
#define GRID_VALUE_CHOICE
Definition: Grid.h:124
void OnChangeLfreq(wxGridEvent &event, int row, RowData *rd)
double mRate
Definition: LabelDialog.h:97
void OnChangeLabel(wxGridEvent &event, int row, RowData *rd)
void OnCancel(wxCommandEvent &event)
void DeleteLabel(int index)
Track * Add(std::unique_ptr< TrackKind > &&t)
Add this Track or all children of this TrackList.
Definition: Track.cpp:843
wxString title
Definition: LabelTrack.h:93
NumericEditor * mFrequencyEditor
Definition: LabelDialog.h:87
int AudacityMessageBox(const wxString &message, const wxString &caption=AudacityMessageBoxCaptionStr(), long style=wxOK|wxCENTRE, wxWindow *parent=NULL, int x=wxDefaultCoord, int y=wxDefaultCoord)
Definition: ErrorDialog.h:92
wxString title
Definition: LabelDialog.cpp:65
void OnExport(wxCommandEvent &event)
RowDataArray mData
Definition: LabelDialog.h:89
int GetNumLabels() const
Definition: ErrorDialog.h:105
double t1() const
void OnCellChange(wxGridEvent &event)
#define safenew
Definition: Audacity.h:223
virtual void SetSelected(bool s)
Definition: Track.cpp:97
virtual int GetKind() const
Definition: Track.h:267
Used to create a WaveTrack, or a LabelTrack.. Implementation of the functions of this class are dispe...
Definition: Track.h:708
void OnChangeEtime(wxGridEvent &event, int row, RowData *rd)
A LabelTrack is a Track that holds labels (LabelStruct).
Definition: LabelTrack.h:114
void AddLabels(const LabelTrack *t)
double f0() const
#define GRID_VALUE_FREQUENCY
Definition: Grid.h:40
bool TransferDataToWindow() override
A LabelStruct holds information for ONE label in a LabelTrack.
Definition: LabelTrack.h:45
void OnFreqUpdate(wxCommandEvent &event)
void OnImport(wxCommandEvent &event)
void FindInitialRow()
int format
Definition: ExportPCM.cpp:56
Defines a selected portion of a project.
wxString mFormat
Definition: LabelDialog.h:98
SelectedRegion selectedRegion
Definition: LabelDialog.cpp:66
void OnChangeTrack(wxGridEvent &event, int row, RowData *rd)
bool setT1(double t, bool maySwap=true)
ViewInfo * mViewInfo
Definition: LabelDialog.h:95
const LabelStruct * GetLabel(int index) const
Dialog for editing labels.
Definition: LabelDialog.h:33
Fundamental data object of Audacity, placed in the TrackPanel. Classes derived form it include the Wa...
Definition: Track.h:67
void OnInsert(wxCommandEvent &event)
void OnImport(const CommandContext &)
void SetChoices(const wxArrayString &choices)
Definition: Grid.cpp:363
wxString GetName() const
Definition: Track.h:212
virtual Track * First(TrackList *val=nullptr)
Definition: Track.cpp:355
wxString mFreqFormat
Definition: LabelDialog.h:99
RowData(int index_, const wxString &title_, const SelectedRegion &selectedRegion_)
Definition: LabelDialog.cpp:60
void SetFormat(const wxString &format)
Definition: Grid.cpp:143
bool setF1(double f, bool maySwap=true)
bool TransferDataFromWindow() override
Column
Definition: LabelDialog.cpp:44
EVT_BUTTON(wxID_NO, DependencyDialog::OnNo) EVT_BUTTON(wxID_YES
An iterator for a TrackList.
Definition: Track.h:339
_("Move Track &Down")+wxT("\t")+(GetActiveProject() -> GetCommandManager() ->GetKeyFromName(wxT("TrackMoveDown"))), OnMoveTrack) POPUP_MENU_ITEM(OnMoveTopID, _("Move Track to &Top")+wxT("\t")+(GetActiveProject() ->GetCommandManager() ->GetKeyFromName(wxT("TrackMoveTop"))), OnMoveTrack) POPUP_MENU_ITEM(OnMoveBottomID, _("Move Track to &Bottom")+wxT("\t")+(GetActiveProject() ->GetCommandManager() ->GetKeyFromName(wxT("TrackMoveBottom"))), OnMoveTrack) void TrackMenuTable::OnSetName(wxCommandEvent &)
static wxString SelectFile(Operation op, const wxString &message, const wxString &default_path, const wxString &default_filename, const wxString &default_extension, const wxString &wildcard, int flags, wxWindow *parent)
Definition: FileNames.cpp:405
void OnChangeHfreq(wxGridEvent &event, int row, RowData *rd)
ChoiceEditor * mChoiceEditor
Definition: LabelDialog.h:85
const wxChar * name
Definition: Distortion.cpp:94
virtual Track * Next(bool skiplinked=false)
Definition: Track.cpp:396
AUDACITY_DLL_API AudacityProject * GetActiveProject()
Definition: Project.cpp:302
SelectedRegion selectedRegion
Definition: LabelTrack.h:92
double f1() const
TrackList * mTracks
Definition: LabelDialog.h:92
bool Validate()
bool setT0(double t, bool maySwap=true)
END_EVENT_TABLE()
Supplies an accessible grid based on wxGrid.
Definition: Grid.h:193
Grid * mGrid
Definition: LabelDialog.h:84
std::unique_ptr< LabelTrack > NewLabelTrack()
Definition: LabelTrack.cpp:99
NumericEditor * mTimeEditor
Definition: LabelDialog.h:86
void SetRate(double rate)
Definition: Grid.cpp:148
void OnSelectCell(wxGridEvent &event)
void OnChangeStime(wxGridEvent &event, int row, RowData *rd)
wxString TrackName(int &index, const wxString &dflt=_("Label Track"))
void OnUpdate(wxCommandEvent &event)
TrackFactory & mFactory
Definition: LabelDialog.h:91
bool setF0(double f, bool maySwap=true)