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