Audacity 3.2.0
EqualizationCurvesDialog.cpp
Go to the documentation of this file.
1/**********************************************************************
2
3 Audacity: A Digital Audio Editor
4
5 EqualizationCurvesDialog.cpp
6
7 Mitch Golden
8 Vaughan Johnson (Preview)
9 Martyn Shaw (FIR filters, response curve, graphic EQ)
10
11 Paul Licameli split from Equalization.cpp
12
13**********************************************************************/
15#include "EffectUIServices.h"
16
17#include <wx/listctrl.h>
18#include "ShuttleGui.h"
20
32 EqualizationCurvesDialog::OnListSelectionChange)
33 EVT_LIST_ITEM_DESELECTED(CurvesListID,
34 EqualizationCurvesDialog::OnListSelectionChange)
36
38 const TranslatableString &name, int options,
39 EQCurveArray &curves, int position
40) : wxDialogWrapper(parent, wxID_ANY, XO("Manage Curves List"),
41 wxDefaultPosition, wxDefaultSize,
42 wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER)
43 , mName{ name }
44 , mOptions{ options }
45 , mCurves{ curves }
46{
47 SetLabel(XO("Manage Curves")); // Provide visual label
48 SetName(XO("Manage Curves List")); // Provide audible label
49 mParent = parent;
50 mPosition = position;
51 // make a copy of curves here to muck about with.
52 mEditCurves = curves;
53
54 Populate();
55 SetMinSize(GetSize());
56}
57
59{
60}
61
64{
65 //------------------------- Main section --------------------
68 // ----------------------- End of main section --------------
69}
70
73{
74 S.StartHorizontalLay(wxEXPAND);
75 {
76 S.StartStatic(XO("&Curves"), 1);
77 {
78 mList = S.Id(CurvesListID)
79 .Style(wxSUNKEN_BORDER | wxLC_REPORT | wxLC_HRULES | wxLC_VRULES )
80 .AddListControlReportMode({
81 { XO("Curve Name"), wxLIST_FORMAT_RIGHT }
82 });
83 }
84 S.EndStatic();
85 S.StartVerticalLay(0);
86 {
87 S.Id(UpButtonID).AddButton(XXO("Move &Up"), wxALIGN_LEFT);
88 S.Id(DownButtonID).AddButton(XXO("Move &Down"), wxALIGN_LEFT);
89 S.Id(RenameButtonID).AddButton(XXO("&Rename..."), wxALIGN_LEFT);
90 S.Id(DeleteButtonID).AddButton(XXO("D&elete..."), wxALIGN_LEFT);
91 S.Id(ImportButtonID).AddButton(XXO("I&mport..."), wxALIGN_LEFT);
92 S.Id(ExportButtonID).AddButton(XXO("E&xport..."), wxALIGN_LEFT);
93 S.Id(LibraryButtonID).AddButton(XXO("&Get More..."), wxALIGN_LEFT);
94 S.Id(DefaultsButtonID).AddButton(XXO("De&faults"), wxALIGN_LEFT);
95 }
96 S.EndVerticalLay();
97 }
98 S.EndHorizontalLay();
99 S.AddStandardButtons();
100 S.StartStatic(XO("Help"));
101 S.AddConstTextBox( {}, XO("Rename 'unnamed' to save a new entry.\n'OK' saves all changes, 'Cancel' doesn't."));
102 S.EndStatic();
104 Fit();
105
106 return;
107}
108
110{
111 mList->DeleteAllItems();
112 for (unsigned int i = 0; i < mEditCurves.size(); i++)
113 mList->InsertItem(i, mEditCurves[i].Name);
114 mList->SetColumnWidth(0, wxLIST_AUTOSIZE);
115 int curvesWidth = mList->GetColumnWidth(0);
116 mList->SetColumnWidth(0, wxLIST_AUTOSIZE_USEHEADER);
117 int headerWidth = mList->GetColumnWidth(0);
118 mList->SetColumnWidth(0, wxMax(headerWidth, curvesWidth));
119 // use 'position' to set focus
120 mList->EnsureVisible(position);
121 mList->SetItemState(position, wxLIST_STATE_SELECTED|wxLIST_STATE_FOCUSED, wxLIST_STATE_SELECTED|wxLIST_STATE_FOCUSED);
122}
123
124void EqualizationCurvesDialog::OnUp(wxCommandEvent & WXUNUSED(event))
125{
126 long item = mList->GetNextItem(-1, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED);
127 if ( item == -1 )
128 return; // no items selected
129 if( item == 0 )
130 item = mList->GetNextItem(item, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED); // top item selected, can't move up
131 int state;
132 while( item != -1 )
133 {
134 if ( item == mList->GetItemCount()-1)
135 { // 'unnamed' always stays at the bottom
137 XO("'unnamed' always stays at the bottom of the list"),
138 XO("'unnamed' is special") ); // these could get tedious!
139 return;
140 }
141 state = mList->GetItemState(item-1, wxLIST_STATE_SELECTED);
142 if ( state != wxLIST_STATE_SELECTED )
143 { // swap this with one above but only if it isn't selected
144 EQCurve temp(wxT("temp"));
145 temp.Name = mEditCurves[item].Name;
146 temp.points = mEditCurves[item].points;
147 mEditCurves[item].Name = mEditCurves[item-1].Name;
148 mEditCurves[item].points = mEditCurves[item-1].points;
149 mEditCurves[item-1].Name = temp.Name;
150 mEditCurves[item-1].points = temp.points;
151 wxString sTemp = mList->GetItemText(item);
152 mList->SetItem(item, 0, mList->GetItemText(item-1));
153 mList->SetItem(item-1, 0, sTemp);
154 mList->SetItemState(item, 0, wxLIST_STATE_SELECTED);
155 mList->SetItemState(item-1, wxLIST_STATE_SELECTED, wxLIST_STATE_SELECTED);
156 }
157 item = mList->GetNextItem(item, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED);
158 }
159}
160
161void EqualizationCurvesDialog::OnDown(wxCommandEvent & WXUNUSED(event))
162{ // looks harder than OnUp as we need to seek backwards up the list, hence GetPreviousItem
163 long item = GetPreviousItem(mList->GetItemCount());
164 if( item == -1 )
165 return; // nothing selected
166 int state;
167 while( item != -1 )
168 {
169 if( (item != mList->GetItemCount()-1) && (item != mList->GetItemCount()-2) )
170 { // can't move 'unnamed' down, or the one above it
171 state = mList->GetItemState(item+1, wxLIST_STATE_SELECTED);
172 if ( state != wxLIST_STATE_SELECTED )
173 { // swap this with one below but only if it isn't selected
174 EQCurve temp(wxT("temp"));
175 temp.Name = mEditCurves[item].Name;
176 temp.points = mEditCurves[item].points;
177 mEditCurves[item].Name = mEditCurves[item+1].Name;
178 mEditCurves[item].points = mEditCurves[item+1].points;
179 mEditCurves[item+1].Name = temp.Name;
180 mEditCurves[item+1].points = temp.points;
181 wxString sTemp = mList->GetItemText(item);
182 mList->SetItem(item, 0, mList->GetItemText(item+1));
183 mList->SetItem(item+1, 0, sTemp);
184 mList->SetItemState(item, 0, wxLIST_STATE_SELECTED);
185 mList->SetItemState(item+1, wxLIST_STATE_SELECTED, wxLIST_STATE_SELECTED);
186 }
187 }
188 item = GetPreviousItem(item);
189 }
190}
191
192long EqualizationCurvesDialog::GetPreviousItem(long item) // wx doesn't have this
193{
194 long lastItem = -1;
195 long itemTemp = mList->GetNextItem(-1, wxLIST_NEXT_ALL,
196 wxLIST_STATE_SELECTED);
197 while( (itemTemp != -1) && (itemTemp < item) )
198 {
199 lastItem = itemTemp;
200 itemTemp = mList->GetNextItem(itemTemp, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED);
201 }
202 return lastItem;
203}
204
205// Rename curve/curves
206void EqualizationCurvesDialog::OnRename(wxCommandEvent & WXUNUSED(event))
207{
208 wxString name;
209 int numCurves = mEditCurves.size();
210 int curve = 0;
211
212 // Setup list of characters that aren't allowed
213 wxArrayStringEx exclude{
214 wxT("<") ,
215 wxT(">") ,
216 wxT("'") ,
217 wxT("\"") ,
218 };
219
220 // Get the first one to be renamed
221 long item = mList->GetNextItem(-1, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED);
222 long firstItem = item; // for reselection with PopulateList
223 while(item >= 0)
224 {
225 // Prompt the user until a valid name is enter or cancelled
226 bool overwrite = false;
227 bool bad = true;
228 while( bad ) // Check for an unacceptable duplicate
229 { // Show the dialog and bail if the user cancels
230 bad = false;
231 // build the dialog
232 AudacityTextEntryDialog dlg( this,
233 XO("Rename '%s' to...").Format( mEditCurves[ item ].Name ),
234 XO("Rename...") );
235 dlg.SetTextValidator( wxFILTER_EXCLUDE_CHAR_LIST );
236 dlg.SetName(
237 wxString::Format( _("Rename '%s'"), mEditCurves[ item ].Name ) );
238 wxTextValidator *tv = dlg.GetTextValidator();
239 tv->SetExcludes( exclude ); // Tell the validator about excluded chars
240 if( dlg.ShowModal() == wxID_CANCEL )
241 {
242 bad = true;
243 break;
244 }
245
246 // Extract the name from the dialog
247 name = dlg.GetValue();
248
249 // Search list of curves for a duplicate name
250 for( curve = 0; curve < numCurves; curve++ )
251 {
252 wxString temp = mEditCurves[ curve ].Name;
253 if( name == mEditCurves[ curve ].Name ) // case sensitive
254 {
255 bad = true;
256 if( curve == item ) // trying to rename a curve with the same name
257 {
259 XO("Name is the same as the original one"),
260 XO("Same name"),
261 wxOK );
262 break;
263 }
264 int answer = EQUtils::DoMessageBox(mName,
265 XO("Overwrite existing curve '%s'?").Format( name ),
266 XO("Curve exists"),
267 wxYES_NO);
268 if (answer == wxYES)
269 {
270 bad = false;
271 overwrite = true; // we are going to overwrite the one with this name
272 break;
273 }
274 }
275 }
276 if( name.empty() || name == wxT("unnamed") )
277 bad = true;
278 }
279
280 // if bad, we cancelled the rename dialog, so nothing to do.
281 if( bad == true )
282 ;
283 else if(overwrite){
284 // Overwrite another curve.
285 // JKC: because 'overwrite' is true, 'curve' is the number of the curve that
286 // we are about to overwrite.
287 mEditCurves[ curve ].Name = name;
288 mEditCurves[ curve ].points = mEditCurves[ item ].points;
289 // if renaming the unnamed item, then select it,
290 // otherwise get rid of the item we've renamed.
291 if( item == (numCurves-1) )
292 mList->SetItem(curve, 0, name);
293 else
294 {
295 mEditCurves.erase( mEditCurves.begin() + item );
296 numCurves--;
297 }
298 }
299 else if( item == (numCurves-1) ) // renaming 'unnamed'
300 { // Create a NEW entry
301 mEditCurves.push_back( EQCurve( wxT("unnamed") ) );
302 // Copy over the points
303 mEditCurves[ numCurves ].points = mEditCurves[ numCurves - 1 ].points;
304 // Give the original unnamed entry the NEW name
305 mEditCurves[ numCurves - 1 ].Name = name;
306 numCurves++;
307 }
308 else // just rename (the 'normal' case)
309 {
310 mEditCurves[ item ].Name = name;
311 mList->SetItem(item, 0, name);
312 }
313 // get next selected item
314 item = mList->GetNextItem(item, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED);
315 }
316
317 PopulateList(firstItem); // Note: only saved to file when you OK out of the dialog
318 return;
319}
320
321// Delete curve/curves
322void EqualizationCurvesDialog::OnDelete(wxCommandEvent & WXUNUSED(event))
323{
324 // We could count them here
325 // And then put in a 'Delete N items?' prompt.
326
327#if 0 // 'one at a time' prompt code
328 // Get the first one to be deleted
329 long item = mList->GetNextItem(-1, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED);
330 // Take care, mList and mEditCurves will get out of sync as curves are deleted
331 int deleted = 0;
332 long highlight = -1;
333
334 while(item >= 0)
335 {
336 if(item == mList->GetItemCount()-1) //unnamed
337 {
339 XO("You cannot delete the 'unnamed' curve."),
340 XO("Can't delete 'unnamed'") );
341 }
342 else
343 {
344 // Create the prompt
345 auto quest = XO("Delete '%s'?")
346 .Format(mEditCurves[ item-deleted ].Name);
347
348 // Ask for confirmation before removal
350 quest,
351 XO("Confirm Deletion") );
352 if( ans == wxYES )
353 { // Remove the curve from the array
354 mEditCurves.erase( mEditCurves.begin() + (item - deleted) );
355 deleted++;
356 }
357 else
358 highlight = item-deleted; // if user presses 'No', select that curve
359 }
360 // get next selected item
361 item = mList->GetNextItem(item, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED);
362 }
363
364 if(highlight == -1)
365 PopulateList(mEditCurves.size()-1); // set 'unnamed' as the selected curve
366 else
367 PopulateList(highlight); // user said 'No' to deletion
368#else // 'DELETE all N' code
369 int count = mList->GetSelectedItemCount();
370 long item = mList->GetNextItem(-1, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED);
371 // Create the prompt
372 TranslatableString quest;
373 if( count > 1 )
374 quest = XO("Delete %d items?").Format( count );
375 else
376 if( count == 1 )
377 quest = XO("Delete '%s'?").Format( mEditCurves[ item ].Name );
378 else
379 return;
380 // Ask for confirmation before removal
382 quest,
383 XO("Confirm Deletion"),
384 wxYES_NO | wxCENTRE );
385 if( ans == wxYES )
386 { // Remove the curve(s) from the array
387 // Take care, mList and mEditCurves will get out of sync as curves are deleted
388 int deleted = 0;
389 while(item >= 0)
390 {
391 // TODO: Migrate to the standard "Manage" dialog.
392 if(item == mList->GetItemCount()-1) //unnamed
393 {
395 XO("You cannot delete the 'unnamed' curve, it is special."),
396 XO("Can't delete 'unnamed'"));
397 }
398 else
399 {
400 mEditCurves.erase( mEditCurves.begin() + item - deleted );
401 deleted++;
402 }
403 item = mList->GetNextItem(item, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED);
404 }
405 PopulateList(mEditCurves.size() - 1); // set 'unnamed' as the selected curve
406 }
407#endif
408}
409
411{
412 static const FileNames::FileTypes results{
414 };
415 return results;
416}
417
418void EqualizationCurvesDialog::OnImport( wxCommandEvent & WXUNUSED(event))
419{
420 FileDialogWrapper filePicker(
421 this,
422 XO("Choose an EQ curve file"), FileNames::DataDir(), wxT(""),
423 XMLtypes() );
424 wxString fileName;
425 if( filePicker.ShowModal() == wxID_CANCEL)
426 return;
427 else
428 fileName = filePicker.GetPath();
430 .LoadCurves(fileName, true);
431 PopulateList(0); // update the EqualizationCurvesDialog dialog
432 return;
433}
434
435void EqualizationCurvesDialog::OnExport( wxCommandEvent & WXUNUSED(event))
436{
437 FileDialogWrapper filePicker(this, XO("Export EQ curves as..."),
438 FileNames::DataDir(), wxT(""),
439 XMLtypes(),
440 wxFD_SAVE | wxFD_OVERWRITE_PROMPT | wxRESIZE_BORDER); // wxFD_CHANGE_DIR?
441 wxString fileName;
442 if( filePicker.ShowModal() == wxID_CANCEL)
443 return;
444 else
445 fileName = filePicker.GetPath();
446
447 EQCurveArray exportCurves; // Copy selected curves to export
448 exportCurves.clear();
449 long item = mList->GetNextItem(-1, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED);
450 int i=0;
451 while(item >= 0)
452 {
453 if(item != mList->GetItemCount()-1) // not 'unnamed'
454 {
455 exportCurves.push_back(mEditCurves[item].Name);
456 exportCurves[i].points = mEditCurves[item].points;
457 i++;
458 }
459 else
461 XO("You cannot export 'unnamed' curve, it is special."),
462 XO("Cannot Export 'unnamed'") );
463 // get next selected item
464 item = mList->GetNextItem(item, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED);
465 }
466 if(i>0)
467 {
468 EQCurveWriter{ exportCurves }.SaveCurves(fileName);
469 auto message = XO("%d curves exported to %s").Format( i, fileName );
471 message,
472 XO("Curves exported") );
473 }
474 else
476 XO("No curves exported"),
477 XO("No curves exported") );
478}
479
480void EqualizationCurvesDialog::OnLibrary( wxCommandEvent & WXUNUSED(event))
481{
482 // full path to wiki.
483 wxLaunchDefaultBrowser(wxT("https://wiki.audacityteam.org/wiki/EQCurvesDownload"));
484}
485
486void EqualizationCurvesDialog::OnDefaults( wxCommandEvent & WXUNUSED(event))
487{
488 // we expect this to fail in LoadCurves (due to a lack of path) and handle that there
490 .LoadCurves( wxT("EQDefaultCurves.xml") );
491 PopulateList(0); // update the EqualizationCurvesDialog dialog
492}
493
494void EqualizationCurvesDialog::OnOK(wxCommandEvent & WXUNUSED(event))
495{
496 {
497 // Make a backup of the current curves
498 wxString backupPlace =
499 wxFileName( FileNames::DataDir(), wxT("EQBackup.xml") ).GetFullPath();
500 EQCurveWriter writer{ mCurves };
501 writer.SaveCurves(backupPlace);
502 // Load back into the main dialog
504 // Save to default place
505 writer.SaveCurves();
506 } // scope of writer
507 EQCurveReader{ mCurves, mName, mOptions }.LoadCurves();
508
509 // Select something sensible
510 long item = mList->GetNextItem(-1,
511 wxLIST_NEXT_ALL,
512 wxLIST_STATE_SELECTED);
513 if (item == -1)
514 item = mList->GetItemCount()-1; // nothing selected, default to 'unnamed'
515 mItem = item;
516 EndModal(true);
517}
518
520{
521 const bool enable = mList->GetSelectedItemCount() > 0;
522 static const int ids[] = {
527 };
528 for (auto id : ids)
529 FindWindowById(id, this)->Enable(enable);
530}
wxT("CloseDown"))
END_EVENT_TABLE()
@ UpButtonID
@ ImportButtonID
@ DownButtonID
@ RenameButtonID
@ DefaultsButtonID
@ DeleteButtonID
@ ExportButtonID
EVT_BUTTON(wxID_NO, DependencyDialog::OnNo) EVT_BUTTON(wxID_YES
const TranslatableString name
Definition: Distortion.cpp:76
std::vector< EQCurve > EQCurveArray
static const FileNames::FileTypes & XMLtypes()
EVT_LIST_ITEM_SELECTED(CurvesListID, EqualizationCurvesDialog::OnListSelectionChange) EVT_LIST_ITEM_DESELECTED(CurvesListID
XO("Cut/Copy/Paste")
XXO("&Cut/Copy/Paste Toolbar")
#define _(s)
Definition: Internat.h:73
@ eIsCreating
Definition: ShuttleGui.h:37
#define S(N)
Definition: ToChars.cpp:64
Wrap wxTextEntryDialog so that caption IS translatable.
One curve in a list.
std::vector< EQPoint > points
wxString Name
Deserializer of curves from XML files.
Serializer of curves into XML files.
void SaveCurves(const wxString &fileName={})
EqualizationCurvesDialog manages the available preset curves.
void OnDown(wxCommandEvent &event)
void PopulateOrExchange(ShuttleGui &S)
Defines the dialog and does data exchange with it.
void OnDelete(wxCommandEvent &event)
void OnOK(wxCommandEvent &event)
const TranslatableString & mName
void OnDefaults(wxCommandEvent &event)
void OnExport(wxCommandEvent &event)
void Populate()
Creates the dialog and its contents.
void OnImport(wxCommandEvent &event)
void OnRename(wxCommandEvent &event)
void OnUp(wxCommandEvent &event)
void OnListSelectionChange(wxListEvent &event)
void OnLibrary(wxCommandEvent &event)
virtual wxString GetPath() const
virtual int ShowModal()
FILES_API const FileType XMLFiles
Definition: FileNames.h:73
std::vector< FileType > FileTypes
Definition: FileNames.h:75
Abstract base class used in importing a file.
Derived from ShuttleGuiBase, an Audacity specific class for shuttling data to and from GUI.
Definition: ShuttleGui.h:640
Holds a msgid for the translation catalog; may also bind format arguments.
Extend wxArrayString with move operations and construction and insertion fromstd::initializer_list.
int DoMessageBox(const TranslatableString &name, const TranslatableString &msg, const TranslatableString &titleStr, long style=wxOK|wxCENTRE)
FILES_API FilePath DataDir()
Audacity user data directory.