1 /**********************************************************************
2
3 Audacity: A Digital Audio Editor
4
5 TracksPrefs.cpp
6
7 Brian Gunlogson
8 Joshua Haberman
9 Dominic Mazzoni
10 James Crook
11
12
13 *******************************************************************//**
14
15 \class TracksPrefs
16 \brief A PrefsPanel for track display and behavior properties.
17
18 *//*******************************************************************/
19
20
21 #include "TracksPrefs.h"
22
23 //#include <algorithm>
24 //#include <wx/defs.h>
25
26 #include "Prefs.h"
27 #include "../ShuttleGui.h"
28 #include "../tracks/playabletrack/wavetrack/ui/WaveTrackViewConstants.h"
29
30 int TracksPrefs::iPreferencePinned = -1;
31
32 namespace {
PinnedHeadPreferenceKey()33 const wxChar *PinnedHeadPreferenceKey()
34 {
35 return wxT("/AudioIO/PinnedHead");
36 }
37
PinnedHeadPreferenceDefault()38 bool PinnedHeadPreferenceDefault()
39 {
40 return false;
41 }
42
PinnedHeadPositionPreferenceKey()43 const wxChar *PinnedHeadPositionPreferenceKey()
44 {
45 return wxT("/AudioIO/PinnedHeadPosition");
46 }
47
PinnedHeadPositionPreferenceDefault()48 double PinnedHeadPositionPreferenceDefault()
49 {
50 return 0.5;
51 }
52 }
53
54
55 namespace {
56 const auto waveformScaleKey = wxT("/GUI/DefaultWaveformScaleChoice");
57 const auto dbValueString = wxT("dB");
58 }
59
60 static EnumSetting< WaveformSettings::ScaleTypeValues > waveformScaleSetting{
61 waveformScaleKey,
62 {
63 { XO("Linear") },
64 { dbValueString, XO("Logarithmic (dB)") },
65 },
66
67 0, // linear
68
69 {
70 WaveformSettings::stLinear,
71 WaveformSettings::stLogarithmic,
72 }
73 };
74
75 //////////
76 // There is a complicated migration history here!
77 namespace {
78 const auto key0 = wxT("/GUI/DefaultViewMode");
79 const auto key1 = wxT("/GUI/DefaultViewModeNew");
80 const auto key2 = wxT("/GUI/DefaultViewModeChoice");
81 const auto key3 = wxT("/GUI/DefaultViewModeChoiceNew");
82
83 const wxString obsoleteValue{ wxT("WaveformDB") };
84 };
85
86 class TracksViewModeEnumSetting
87 : public EnumSetting< WaveTrackViewConstants::Display > {
88 public:
89 using EnumSetting< WaveTrackViewConstants::Display >::EnumSetting;
90
Migrate(wxString & value)91 void Migrate( wxString &value ) override
92 {
93 // Special logic for this preference which was three times migrated!
94
95 // PRL: Bugs 1043, 1044
96 // 2.1.1 writes a NEW key for this preference, which got NEW values,
97 // to avoid confusing version 2.1.0 if it reads the preference file afterwards.
98 // Prefer the NEW preference key if it is present
99
100 static const EnumValueSymbol waveformSymbol{ XO("Waveform") };
101 static const EnumValueSymbol spectrumSymbol{ XO("Spectrogram") };
102
103 WaveTrackViewConstants::Display viewMode;
104 int oldMode;
105 wxString newValue;
106 auto stringValue =
107 []( WaveTrackViewConstants::Display display ){
108 switch ( display ) {
109 case WaveTrackViewConstants::Spectrum:
110 return spectrumSymbol.Internal();
111 case WaveTrackViewConstants::obsoleteWaveformDBDisplay:
112 return obsoleteValue;
113 default:
114 return waveformSymbol.Internal();
115 }
116 };
117
118 if ( gPrefs->Read(key0, // The very old key
119 &oldMode,
120 (int)(WaveTrackViewConstants::Waveform) ) ) {
121 viewMode = WaveTrackViewConstants::ConvertLegacyDisplayValue(oldMode);
122 newValue = stringValue( viewMode );
123 }
124 else if ( gPrefs->Read(key1,
125 &oldMode,
126 (int)(WaveTrackViewConstants::Waveform) ) ) {
127 viewMode = static_cast<WaveTrackViewConstants::Display>( oldMode );
128 newValue = stringValue( viewMode );
129 }
130 else
131 gPrefs->Read( key2, &newValue );
132
133 if ( !gPrefs->Read( key3, &value ) ) {
134 if (newValue == obsoleteValue) {
135 newValue = waveformSymbol.Internal();
136 gPrefs->Write(waveformScaleKey, dbValueString);
137 }
138
139 Write( value = newValue );
140 gPrefs->Flush();
141 return;
142 }
143 }
144 };
145
viewModeSetting()146 static TracksViewModeEnumSetting viewModeSetting()
147 {
148 // Do a delayed computation, so that registration of sub-view types completes
149 // first
150 const auto &types = WaveTrackSubViewType::All();
151 auto symbols = transform_container< EnumValueSymbols >(
152 types, std::mem_fn( &WaveTrackSubViewType::name ) );
153 auto ids = transform_container< std::vector< WaveTrackSubViewType::Display > >(
154 types, std::mem_fn( &WaveTrackSubViewType::id ) );
155
156 // Special entry for multi
157 symbols.push_back( WaveTrackViewConstants::MultiViewSymbol );
158 ids.push_back( WaveTrackViewConstants::MultiView );
159
160 return {
161 key3,
162 symbols,
163 0, // Waveform
164 ids
165 };
166 }
167
ViewModeChoice()168 WaveTrackViewConstants::Display TracksPrefs::ViewModeChoice()
169 {
170 return viewModeSetting().ReadEnum();
171 }
172
WaveformScaleChoice()173 WaveformSettings::ScaleTypeValues TracksPrefs::WaveformScaleChoice()
174 {
175 return waveformScaleSetting.ReadEnum();
176 }
177
178 //////////
179 static EnumSetting< WaveTrackViewConstants::SampleDisplay >
180 sampleDisplaySetting{
181 wxT("/GUI/SampleViewChoice"),
182 {
183 { wxT("ConnectDots"), XO("Connect dots") },
184 { wxT("StemPlot"), XO("Stem plot") }
185 },
186 1, // StemPlot
187
188 // for migrating old preferences:
189 {
190 WaveTrackViewConstants::LinearInterpolate,
191 WaveTrackViewConstants::StemPlot
192 },
193 wxT("/GUI/SampleView")
194 };
195
SampleViewChoice()196 WaveTrackViewConstants::SampleDisplay TracksPrefs::SampleViewChoice()
197 {
198 return sampleDisplaySetting.ReadEnum();
199 }
200
201 //////////
202 static const std::initializer_list<EnumValueSymbol> choicesZoom{
203 { wxT("FitToWidth"), XO("Fit to Width") },
204 { wxT("ZoomToSelection"), XO("Zoom to Selection") },
205 { wxT("ZoomDefault"), XO("Zoom Default") },
206 { XO("Minutes") },
207 { XO("Seconds") },
208 { wxT("FifthsOfSeconds"), XO("5ths of Seconds") },
209 { wxT("TenthsOfSeconds"), XO("10ths of Seconds") },
210 { wxT("TwentiethsOfSeconds"), XO("20ths of Seconds") },
211 { wxT("FiftiethsOfSeconds"), XO("50ths of Seconds") },
212 { wxT("HundredthsOfSeconds"), XO("100ths of Seconds") },
213 { wxT("FiveHundredthsOfSeconds"), XO("500ths of Seconds") },
214 { XO("MilliSeconds") },
215 { XO("Samples") },
216 { wxT("FourPixelsPerSample"), XO("4 Pixels per Sample") },
217 { wxT("MaxZoom"), XO("Max Zoom") },
218 };
219 static auto enumChoicesZoom = {
220 WaveTrackViewConstants::kZoomToFit,
221 WaveTrackViewConstants::kZoomToSelection,
222 WaveTrackViewConstants::kZoomDefault,
223 WaveTrackViewConstants::kZoomMinutes,
224 WaveTrackViewConstants::kZoomSeconds,
225 WaveTrackViewConstants::kZoom5ths,
226 WaveTrackViewConstants::kZoom10ths,
227 WaveTrackViewConstants::kZoom20ths,
228 WaveTrackViewConstants::kZoom50ths,
229 WaveTrackViewConstants::kZoom100ths,
230 WaveTrackViewConstants::kZoom500ths,
231 WaveTrackViewConstants::kZoomMilliSeconds,
232 WaveTrackViewConstants::kZoomSamples,
233 WaveTrackViewConstants::kZoom4To1,
234 WaveTrackViewConstants::kMaxZoom,
235 };
236
237 static EnumSetting< WaveTrackViewConstants::ZoomPresets > zoom1Setting{
238 wxT("/GUI/ZoomPreset1Choice"),
239 choicesZoom,
240 2, // kZoomDefault
241
242 // for migrating old preferences:
243 enumChoicesZoom,
244 wxT("/GUI/ZoomPreset1")
245 };
246
247 static EnumSetting< WaveTrackViewConstants::ZoomPresets > zoom2Setting{
248 wxT("/GUI/ZoomPreset2Choice"),
249 choicesZoom,
250 13, // kZoom4To1
251
252 // for migrating old preferences:
253 enumChoicesZoom,
254 wxT("/GUI/ZoomPreset2")
255 };
256
Zoom1Choice()257 WaveTrackViewConstants::ZoomPresets TracksPrefs::Zoom1Choice()
258 {
259 return zoom1Setting.ReadEnum();
260 }
261
Zoom2Choice()262 WaveTrackViewConstants::ZoomPresets TracksPrefs::Zoom2Choice()
263 {
264 return zoom2Setting.ReadEnum();
265 }
266
267 //////////
TracksPrefs(wxWindow * parent,wxWindowID winid)268 TracksPrefs::TracksPrefs(wxWindow * parent, wxWindowID winid)
269 /* i18n-hint: "Tracks" include audio recordings but also other collections of
270 * data associated with a time line, such as sequences of labels, and musical
271 * notes */
272 : PrefsPanel(parent, winid, XO("Tracks"))
273 {
274 Populate();
275 }
276
~TracksPrefs()277 TracksPrefs::~TracksPrefs()
278 {
279 }
280
GetSymbol()281 ComponentInterfaceSymbol TracksPrefs::GetSymbol()
282 {
283 return TRACKS_PREFS_PLUGIN_SYMBOL;
284 }
285
GetDescription()286 TranslatableString TracksPrefs::GetDescription()
287 {
288 return XO("Preferences for Tracks");
289 }
290
HelpPageName()291 ManualPageID TracksPrefs::HelpPageName()
292 {
293 return "Tracks_Preferences";
294 }
295
Populate()296 void TracksPrefs::Populate()
297 {
298 // Keep view choices and codes in proper correspondence --
299 // we don't display them by increasing integer values.
300
301
302 // How samples are displayed when zoomed in:
303
304
305 //------------------------- Main section --------------------
306 // Now construct the GUI itself.
307 // Use 'eIsCreatingFromPrefs' so that the GUI is
308 // initialised with values from gPrefs.
309 ShuttleGui S(this, eIsCreatingFromPrefs);
310 PopulateOrExchange(S);
311 // ----------------------- End of main section --------------
312 }
313
PopulateOrExchange(ShuttleGui & S)314 void TracksPrefs::PopulateOrExchange(ShuttleGui & S)
315 {
316 S.SetBorder(2);
317 S.StartScroller();
318
319 S.StartStatic(XO("Display"));
320 {
321 S.TieCheckBox(XXO("Auto-&fit track height"),
322 {wxT("/GUI/TracksFitVerticallyZoomed"),
323 false});
324 S.TieCheckBox(XXO("Sho&w track name as overlay"),
325 {wxT("/GUI/ShowTrackNameInWaveform"),
326 false});
327 #ifdef EXPERIMENTAL_HALF_WAVE
328 S.TieCheckBox(XXO("Use &half-wave display when collapsed"),
329 {wxT("/GUI/CollapseToHalfWave"),
330 false});
331 #endif
332 #ifdef SHOW_PINNED_UNPINNED_IN_PREFS
333 S.TieCheckBox(XXO("&Pinned Recording/Playback head"),
334 {PinnedHeadPreferenceKey(),
335 PinnedHeadPreferenceDefault()});
336 #endif
337 S.TieCheckBox(XXO("A&uto-scroll if head unpinned"),
338 {wxT("/GUI/AutoScroll"),
339 true});
340
341 S.AddSpace(10);
342
343 S.StartMultiColumn(2);
344 {
345 #ifdef SHOW_PINNED_POSITION_IN_PREFS
346 S.TieNumericTextBox(
347 XXO("Pinned &head position"),
348 {PinnedHeadPositionPreferenceKey(),
349 PinnedHeadPositionPreferenceDefault()},
350 30
351 );
352 #endif
353
354 S.TieChoice(XXO("Default &view mode:"),
355 viewModeSetting() );
356
357 S.TieChoice(XXO("Default Waveform scale:"),
358 waveformScaleSetting );
359
360 S.TieChoice(XXO("Display &samples:"),
361 sampleDisplaySetting );
362
363 S.TieTextBox(XXO("Default audio track &name:"),
364 {wxT("/GUI/TrackNames/DefaultTrackName"),
365 _("Audio Track")},
366 30);
367 }
368 S.EndMultiColumn();
369 }
370 S.EndStatic();
371
372 S.StartStatic(XO("Zoom Toggle"));
373 {
374 S.StartMultiColumn(4);
375 {
376 S.TieChoice(XXO("Preset 1:"),
377 zoom1Setting );
378
379 S.TieChoice(XXO("Preset 2:"),
380 zoom2Setting );
381 }
382 }
383 S.EndStatic();
384 S.EndScroller();
385 }
386
GetPinnedHeadPreference()387 bool TracksPrefs::GetPinnedHeadPreference()
388 {
389 // JKC: Cache this setting as it is read many times during drawing, and otherwise causes screen flicker.
390 // Correct solution would be to re-write wxFileConfig to be efficient.
391 if( iPreferencePinned >= 0 )
392 return iPreferencePinned == 1;
393 bool bResult = gPrefs->ReadBool(PinnedHeadPreferenceKey(), PinnedHeadPreferenceDefault());
394 iPreferencePinned = bResult ? 1: 0;
395 return bResult;
396 }
397
SetPinnedHeadPreference(bool value,bool flush)398 void TracksPrefs::SetPinnedHeadPreference(bool value, bool flush)
399 {
400 iPreferencePinned = value ? 1 :0;
401 gPrefs->Write(PinnedHeadPreferenceKey(), value);
402 if(flush)
403 gPrefs->Flush();
404 }
405
GetPinnedHeadPositionPreference()406 double TracksPrefs::GetPinnedHeadPositionPreference()
407 {
408 auto value = gPrefs->ReadDouble(
409 PinnedHeadPositionPreferenceKey(),
410 PinnedHeadPositionPreferenceDefault());
411 return std::max(0.0, std::min(1.0, value));
412 }
413
SetPinnedHeadPositionPreference(double value,bool flush)414 void TracksPrefs::SetPinnedHeadPositionPreference(double value, bool flush)
415 {
416 value = std::max(0.0, std::min(1.0, value));
417 gPrefs->Write(PinnedHeadPositionPreferenceKey(), value);
418 if(flush)
419 gPrefs->Flush();
420 }
421
GetDefaultAudioTrackNamePreference()422 wxString TracksPrefs::GetDefaultAudioTrackNamePreference()
423 {
424 const auto name =
425 gPrefs->Read(wxT("/GUI/TrackNames/DefaultTrackName"), wxT(""));
426
427 if (name.empty() || ( name == "Audio Track" ))
428 // When nothing was specified,
429 // the default-default is whatever translation of...
430 /* i18n-hint: The default name for an audio track. */
431 return _("Audio Track");
432 else
433 return name;
434 }
435
Commit()436 bool TracksPrefs::Commit()
437 {
438 // Bug 1583: Clear the caching of the preference pinned state.
439 iPreferencePinned = -1;
440 ShuttleGui S(this, eIsSavingToPrefs);
441 PopulateOrExchange(S);
442
443 // Bug 1661: Don't store the name for new tracks if the name is the
444 // default in that language.
445 if (GetDefaultAudioTrackNamePreference() == _("Audio Track")) {
446 gPrefs->DeleteEntry(wxT("/GUI/TrackNames/DefaultTrackName"));
447 gPrefs->Flush();
448 }
449
450 return true;
451 }
452
453 namespace{
454 PrefsPanel::Registration sAttachment{ "Tracks",
455 [](wxWindow *parent, wxWindowID winid, AudacityProject *)
__anon117631d10602() 456 {
457 wxASSERT(parent); // to justify safenew
458 return safenew TracksPrefs(parent, winid);
459 }
460 };
461 }
462