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