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,
95  TrackFactory &factory,
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  // Set initial row
354  // (This will not work until the grid is actually displayed)
355  if (show && mInitialRow != -1) {
356  mGrid->SetGridCursor(mInitialRow, Col_Label);
357  }
358 
359  return ret;
360 }
361 
363 {
364  int cnt = mData.size();
365  int i;
366  int tndx = 0;
367 
368  // Clear label tracks of labels
369  for (auto lt : mTracks->Any<LabelTrack>()) {
370  ++tndx;
371  if (!mSelectedTrack) {
372  for (i = lt->GetNumLabels() - 1; i >= 0 ; i--) {
373  lt->DeleteLabel(i);
374  }
375  }
376  else if (mSelectedTrack == lt && mIndex > -1) {
377  lt->DeleteLabel(mIndex);
378  }
379  else
380  // Do nothing to the nonselected tracks
381  ;
382  }
383 
384  // Create any added tracks
385  while (tndx < (int)mTrackNames.GetCount() - 1) {
386 
387  // Extract the name
388  wxString name = mTrackNames[tndx + 1].AfterFirst(wxT('-')).Mid(1);
389 
390  // Create the NEW track and add to track list
391  auto newTrack = mFactory.NewLabelTrack();
392  newTrack->SetName(name);
393  mTracks->Add(std::move(newTrack));
394  tndx++;
395  }
396 
397  // Repopulate with updated labels
398  for (i = 0; i < cnt; i++) {
399  RowData &rd = mData[i];
400 
401  // Look for track with matching index
402  tndx = 1;
403  LabelTrack *lt{};
404  for (auto t : mTracks->Any<LabelTrack>()) {
405  lt = t;
406  if (rd.index == tndx++) {
407  break;
408  }
409  }
410  wxASSERT(lt);
411  if (!lt)
412  return false;
413 
414  // Add the label to it
415  lt->AddLabel(rd.selectedRegion, rd.title, -2);
416  lt->Unselect();
417  }
418 
419  return true;
420 }
421 
423 {
424  if (mGrid->IsCellEditControlShown()) {
425  mGrid->HideCellEditControl();
426  mGrid->SaveEditControlValue();
427  }
428 
429  return true;
430 }
431 
432 wxString LabelDialog::TrackName(int & index, const wxString &dflt)
433 {
434  // Generate a NEW track name if the passed index is out of range
435  if (index < 1 || index >= (int)mTrackNames.GetCount()) {
436  index = mTrackNames.GetCount();
437  mTrackNames.Add(wxString::Format(wxT("%d - %s"), index, dflt));
438  }
439 
440  // Return the track name
441  return mTrackNames[index];
442 }
443 
445 {
446  // Add labels from all label tracks
447  for (auto lt : mTracks->Any<const LabelTrack>()) {
448  AddLabels(lt);
449  }
450 
451  FindInitialRow();
452 
453  if (mData.size() == 0) {
454  wxCommandEvent e;
455  OnInsert(e);
456  }
457 }
458 
460 {
461  wxString lab;
462  int tndx = 0;
463  int i;
464 
465  // Add a NEW track name
466  TrackName(tndx, t->GetName());
467 
468  // If editor was invoked for one label, add that one only, else add all.
469  if (!mSelectedTrack || mSelectedTrack == t) {
470  for (i = 0; i < t->GetNumLabels(); i++) {
471  const LabelStruct *ls = t->GetLabel(i);
472 
473  if (mIndex < 0 || mIndex == i)
474  mData.push_back(RowData(tndx, ls->title, ls->selectedRegion));
475  }
476  }
477 }
478 
480 {
481  int cnt = mData.size();
482  mInitialRow = -1;
483 
484  if (cnt == 0)
485  return;
486 
487  // find closest previous label
488 
489  double distMin = std::numeric_limits<double>::max();
490  double dist;
491  double t0 = mViewInfo->selectedRegion.t0();
492  int i;
493  for (i = 0; i < cnt; i++)
494  {
495  dist = t0 - mData[i].selectedRegion.t0();
496  if (dist >= 0.0 && dist < distMin)
497  {
498  mInitialRow = i;
499  distMin = dist;
500  }
501  }
502 
503  // if no previous label was found, find first label
504 
505  if (mInitialRow == -1)
506  {
507  double t0Min = std::numeric_limits<double>::max();
508  for (i = 0; i < cnt; i++)
509  {
510  if (mData[i].selectedRegion.t0() < t0Min)
511  {
512  mInitialRow = i;
513  t0Min = mData[i].selectedRegion.t0();
514  }
515  }
516  }
517 }
518 
519 void LabelDialog::OnUpdate(wxCommandEvent &event)
520 {
521  // Remember the NEW format and repopulate grid
523  NumericConverter::TIME, event.GetString() );
525 
526  event.Skip(false);
527 }
528 
529 void LabelDialog::OnFreqUpdate(wxCommandEvent &event)
530 {
531  // Remember the NEW format and repopulate grid
533  NumericConverter::FREQUENCY, event.GetString() );
535 
536  event.Skip(false);
537 }
538 
539 void LabelDialog::OnInsert(wxCommandEvent &event)
540 {
541  int cnt = mData.size();
542  int row = 0;
543  int index = 0;
544 
545  // Make sure the edit control isn't active before inserting any rows
546  if (mGrid->IsCellEditControlShown()) {
547  mGrid->HideCellEditControl();
548  }
549 
550  // Attempt to guess which track the label should reside on
551  if (cnt > 0) {
552  row = mGrid->GetGridCursorRow();
553  if (row > 0 && row >= cnt) {
554  index = mTrackNames.Index(mGrid->GetCellValue(row - 1, Col_Track));
555  }
556  else {
557  index = mTrackNames.Index(mGrid->GetCellValue(row, Col_Track));
558  }
559  }
560 
561  // Insert NEW label before or after the current row
562  if (event.GetId() == ID_INSERTA && row < cnt) {
563  row++;
564  }
565  mData.insert(mData.begin() + row, RowData(index, wxT(""), SelectedRegion()));
566 
567  // Repopulate the grid
569 
570  // Reposition cursor to NEW row/col and put user into edit mode to
571  // set the label name
572  mGrid->SetGridCursor(row, Col_Label);
573  mGrid->EnableCellEditControl(true);
574  mGrid->ShowCellEditControl();
575 }
576 
577 void LabelDialog::OnRemove(wxCommandEvent & WXUNUSED(event))
578 {
579  int row = mGrid->GetGridCursorRow();
580  int col = mGrid->GetGridCursorCol();
581  int cnt = mData.size();
582 
583  // Don't try to remove if no labels exist
584  if (cnt == 0) {
585  return;
586  }
587 
588  // Make sure the edit control isn't active before removing rows
589  if (mGrid->IsCellEditControlShown()) {
590  mGrid->HideCellEditControl();
591  }
592 
593  // Remove the row
594  //RowData &rd = mData[row];
595  mData.erase(mData.begin() + row);
596 
597  // Repopulate the grid
599 
600  // Reposition the cursor
601  if (row > 0 && row >= --cnt) {
602  row--;
603  }
604  mGrid->SetGridCursor(row, col);
605 
606  // Make sure focus isn't lost
607  if (mData.size() == 0 && wxWindow::FindFocus() == mGrid->GetGridWindow()) {
608  wxWindow *ok = wxWindow::FindWindowById( wxID_OK, this);
609  if (ok) {
610  ok->SetFocus();
611  }
612  }
613 }
614 
615 void LabelDialog::OnImport(wxCommandEvent & WXUNUSED(event))
616 {
617  // Ask user for a filename
618  wxString fileName =
620  _("Select a text file containing labels"),
621  wxEmptyString, // Path
622  wxT(""), // Name
623  wxT(".txt"), // Extension
624  _("Text files (*.txt)|*.txt|All files|*"),
625  wxRESIZE_BORDER, // Flags
626  this); // Parent
627 
628  // They gave us one...
629  if (fileName != wxT("")) {
630  wxTextFile f;
631 
632  // Get at the data
633  f.Open(fileName);
634  if (!f.IsOpened()) {
636  wxString::Format( _("Could not open file: %s"), fileName ));
637  }
638  else {
639  // Create a temporary label track and load the labels
640  // into it
641  auto lt = mFactory.NewLabelTrack();
642  lt->Import(f);
643 
644  // Add the labels to our collection
645  AddLabels(lt.get());
646 
647  // Done with the temporary track
648  }
649 
650  // Repopulate the grid
652  }
653 }
654 
655 void LabelDialog::OnExport(wxCommandEvent & WXUNUSED(event))
656 {
657  int cnt = mData.size();
658 
659  // Silly user (could just disable the button, but that's a hassle ;-))
660  if (cnt == 0) {
661  AudacityMessageBox(_("No labels to export."));
662  return;
663  }
664 
665  // Extract the actual name.
666  wxString fName = mTrackNames[mTrackNames.GetCount() - 1].AfterFirst(wxT('-')).Mid(1);
667 
669  _("Export Labels As:"),
670  wxEmptyString,
671  fName,
672  wxT("txt"),
673  wxT("*.txt"),
674  wxFD_SAVE | wxFD_OVERWRITE_PROMPT | wxRESIZE_BORDER,
675  this);
676 
677  if (fName == wxT(""))
678  return;
679 
680  // Move existing files out of the way. Otherwise wxTextFile will
681  // append to (rather than replace) the current file.
682 
683  if (wxFileExists(fName)) {
684 #ifdef __WXGTK__
685  wxString safetyFileName = fName + wxT("~");
686 #else
687  wxString safetyFileName = fName + wxT(".bak");
688 #endif
689 
690  if (wxFileExists(safetyFileName))
691  wxRemoveFile(safetyFileName);
692 
693  wxRename(fName, safetyFileName);
694  }
695 
696  wxTextFile f(fName);
697 #ifdef __WXMAC__
698  wxFile{}.Create(fName);
699 #else
700  f.Create();
701 #endif
702  f.Open();
703  if (!f.IsOpened()) {
705  wxString::Format( _("Couldn't write to file: %s"), fName ) );
706  return;
707  }
708 
709  // Transfer our collection to a temporary label track
710  auto lt = mFactory.NewLabelTrack();
711  int i;
712 
713  for (i = 0; i < cnt; i++) {
714  RowData &rd = mData[i];
715 
716  lt->AddLabel(rd.selectedRegion, rd.title,-2);
717  }
718 
719  // Export them and clean
720  lt->Export(f);
721 
722 #ifdef __WXMAC__
723  f.Write(wxTextFileType_Mac);
724 #else
725  f.Write();
726 #endif
727  f.Close();
728 }
729 
730 void LabelDialog::OnSelectCell(wxGridEvent &event)
731 {
732  for (auto t: mTracks->Any())
733  t->SetSelected( true );
734 
735  if (!mData.empty())
736  {
737  RowData &rd = mData[event.GetRow()];
739 
741  }
742 
743  event.Skip();
744 }
745 
746 void LabelDialog::OnCellChange(wxGridEvent &event)
747 {
748  static bool guard = false;
749  int row = event.GetRow();
750 
751  // Guard against recursion which can happen when a change to the "NEW label" row
752  // is made. When InsertRow() is done in TransferDataToWindow(), checks are made
753  // within wxGrid to see if the edit control is active and since it hasn't yet
754  // been marked inactive on the first time through here, we get entered again.
755  // Sort of a double change. I think this is probably a bug in wxGrid.
756  if (guard) {
757  return;
758  }
759  guard = true;
760 
761  // The change was to an existing label, so go process it based
762  // on which column was changed.
763  RowData *rd = &mData[row];
764  switch (event.GetCol())
765  {
766  case Col_Track:
767  OnChangeTrack(event, row, rd);
768  break;
769 
770  case Col_Label:
771  OnChangeLabel(event, row, rd);
772  break;
773 
774  case Col_Stime:
775  OnChangeStime(event, row, rd);
776  break;
777 
778  case Col_Etime:
779  OnChangeEtime(event, row, rd);
780  break;
781 
782  case Col_Lfreq:
783  OnChangeLfreq(event, row, rd);
784  break;
785 
786  case Col_Hfreq:
787  OnChangeHfreq(event, row, rd);
788  break;
789  }
790 
791  // Done...no need for protection anymore
792  guard = false;
793 
794  return;
795 }
796 
797 void LabelDialog::OnChangeTrack(wxGridEvent & WXUNUSED(event), int row, RowData *rd)
798 {
799  wxString val = mGrid->GetCellValue(row, Col_Track);
800 
801  // User selected the "New..." choice so ask for a NEW name
802  if (mTrackNames.Index(val) == 0) {
804  _("New Label Track"),
805  _("Enter track name"),
806  /* i18n-hint: (noun) it's the name of a kind of track.*/
807  _("Label Track"));
808 
809  // User canceled so repopulating the grid will set the track
810  // name to the orignal value
811  if (d.ShowModal() == wxID_CANCEL) {
813  return;
814  }
815 
816  // Force generation of a NEW track name
817  rd->index = 0;
818  TrackName(rd->index, d.GetValue());
819  }
820  else {
821  // Remember the tracks index
822  rd->index = mTrackNames.Index(val);
823  }
824 
825  // Repopulate the grid
827 
828  return;
829 }
830 
831 void LabelDialog::OnChangeLabel(wxGridEvent & WXUNUSED(event), int row, RowData *rd)
832 {
833  // Remember the value...no need to repopulate
834  rd->title = mGrid->GetCellValue(row, Col_Label);
835 
836  return;
837 }
838 
839 void LabelDialog::OnChangeStime(wxGridEvent & WXUNUSED(event), int row, RowData *rd)
840 {
841  // Remember the value...no need to repopulate
842  double t {};
843  mGrid->GetCellValue(row, Col_Stime).ToDouble(&t);
844  rd->selectedRegion.setT0(t, false);
845  mGrid->SetCellValue(row, Col_Etime, wxString::Format(wxT("%g"),
846  rd->selectedRegion.t1()));
847 
848  return;
849 }
850 
851 void LabelDialog::OnChangeEtime(wxGridEvent & WXUNUSED(event), int row, RowData *rd)
852 {
853  // Remember the value...no need to repopulate
854  double t {};
855  mGrid->GetCellValue(row, Col_Etime).ToDouble(&t);
856  rd->selectedRegion.setT1(t, false);
857  mGrid->SetCellValue(row, Col_Stime, wxString::Format(wxT("%g"),
858  rd->selectedRegion.t0()));
859 
860  return;
861 }
862 
863 void LabelDialog::OnChangeLfreq(wxGridEvent & WXUNUSED(event), int row, RowData *rd)
864 {
865  // Remember the value...no need to repopulate
866  double f;
867  mGrid->GetCellValue(row, Col_Lfreq).ToDouble(&f);
868  rd->selectedRegion.setF0(f, false);
869  mGrid->SetCellValue(row, Col_Hfreq, wxString::Format(wxT("%g"),
870  rd->selectedRegion.f1()));
871 
872  return;
873 }
874 
875 void LabelDialog::OnChangeHfreq(wxGridEvent & WXUNUSED(event), int row, RowData *rd)
876 {
877  // Remember the value...no need to repopulate
878  double f;
879  mGrid->GetCellValue(row, Col_Hfreq).ToDouble(&f);
880  rd->selectedRegion.setF1(f, false);
881  mGrid->SetCellValue(row, Col_Lfreq, wxString::Format(wxT("%g"),
882  rd->selectedRegion.f0()));
883 
884  return;
885 }
886 
888  wxSize sz = GetSize();
889  int prefWidth, prefHeight;
890  gPrefs->Read(wxT("/LabelEditor/Width"), &prefWidth, sz.x);
891  gPrefs->Read(wxT("/LabelEditor/Height"), &prefHeight, sz.y);
892 
893  wxRect screenRect(wxGetClientDisplayRect());
894  wxSize prefSize = wxSize(prefWidth, prefHeight);
895  prefSize.DecTo(screenRect.GetSize());
896  SetSize(prefSize);
897 }
898 
900  wxSize sz = GetSize();
901  gPrefs->Write(wxT("/LabelEditor/Width"), sz.x);
902  gPrefs->Write(wxT("/LabelEditor/Height"), sz.y);
903  gPrefs->Flush();
904 }
905 
906 void LabelDialog::OnOK(wxCommandEvent & WXUNUSED(event))
907 {
908  if (mGrid->IsCellEditControlShown()) {
909  mGrid->SaveEditControlValue();
910  mGrid->HideCellEditControl();
911  return;
912  }
913 
914  // Standard handling
915  if (Validate() && TransferDataFromWindow()) {
916  WriteSize();
917  EndModal(wxID_OK);
918  }
919 
920  return;
921 }
922 
923 void LabelDialog::OnCancel(wxCommandEvent & WXUNUSED(event))
924 {
925  if (mGrid->IsCellEditControlShown()) {
926  auto editor = mGrid->GetCellEditor(mGrid->GetGridCursorRow(),
927  mGrid->GetGridCursorCol());
928  editor->Reset();
929  // To avoid memory leak, don't forget DecRef()!
930  editor->DecRef();
931  mGrid->HideCellEditControl();
932  return;
933  }
934 
935  WriteSize();
936  // Standard handling
937  EndModal(wxID_CANCEL);
938 
939  return;
940 }
bool Show(bool show=true) override
void RedrawProject(const bool bForceWaveTracks=false)
Definition: Project.cpp:1395
wxArrayString mTrackNames
Definition: LabelDialog.h:107
AudacityPrefs * gPrefs
Definition: Prefs.cpp:73
A list of TrackListNode items.
Definition: Track.h:1115
void FindAllLabels()
EVT_COMMAND(wxID_ANY, EVT_FREQUENCYTEXTCTRL_UPDATED, LabelDialog::OnFreqUpdate) LabelDialog
Definition: LabelDialog.cpp:89
double t0() const
ViewInfo is used mainly to hold the zooming, selection and scroll information. It also has some statu...
Definition: ViewInfo.h:141
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:41
SelectedRegion selectedRegion
Definition: ViewInfo.h:160
LabelTrack * mSelectedTrack
Definition: LabelDialog.h:104
void OnRemove(wxCommandEvent &event)
void OnOK(wxCommandEvent &event)
#define GRID_VALUE_CHOICE
Definition: Grid.h:126
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:671
wxString title
Definition: LabelTrack.h:92
NumericEditor * mFrequencyEditor
Definition: LabelDialog.h:98
void PopulateLabels()
auto Any() -> TrackIterRange< TrackType >
Definition: Track.h:1195
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:1563
void OnChangeEtime(wxGridEvent &event, int row, RowData *rd)
void EndHorizontalLay()
A LabelTrack is a Track that holds labels (LabelStruct).
Definition: LabelTrack.h:113
void AddLabels(const LabelTrack *t)
double f0() const
#define GRID_VALUE_FREQUENCY
Definition: Grid.h:42
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:369
RowData(int index_, const wxString &title_, const SelectedRegion &selectedRegion_)
Definition: LabelDialog.cpp:61
IdentInterfaceSymbol pairs a persistent string identifier used internally with an optional...
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:425
_("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:312
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:193
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)