1 #include "preferences/dialog/dlgprefdeck.h"
2 
3 #include <QDir>
4 #include <QDoubleSpinBox>
5 #include <QList>
6 #include <QLocale>
7 #include <QToolTip>
8 #include <QWidget>
9 
10 #include "control/controlobject.h"
11 #include "control/controlproxy.h"
12 #include "defs_urls.h"
13 #include "engine/controls/ratecontrol.h"
14 #include "engine/enginebuffer.h"
15 #include "mixer/basetrackplayer.h"
16 #include "mixer/playerinfo.h"
17 #include "mixer/playermanager.h"
18 #include "mixxx.h"
19 #include "moc_dlgprefdeck.cpp"
20 #include "preferences/usersettings.h"
21 #include "util/compatibility.h"
22 #include "util/duration.h"
23 #include "widget/wnumberpos.h"
24 
25 namespace {
26 constexpr int kDefaultRateRangePercent = 8;
27 constexpr double kRateDirectionInverted = -1;
28 constexpr RateControl::RampMode kDefaultRampingMode = RateControl::RampMode::Stepping;
29 constexpr double kDefaultTemporaryRateChangeCoarse = 4.00; // percent
30 constexpr double kDefaultTemporaryRateChangeFine = 2.00;
31 constexpr double kDefaultPermanentRateChangeCoarse = 0.50;
32 constexpr double kDefaultPermanentRateChangeFine = 0.05;
33 constexpr int kDefaultRateRampSensitivity = 250;
34 // bool kDefaultCloneDeckOnLoad is defined in header file to make it available
35 // to playermanager.cpp
36 } // namespace
37 
DlgPrefDeck(QWidget * parent,UserSettingsPointer pConfig)38 DlgPrefDeck::DlgPrefDeck(QWidget* parent,
39         UserSettingsPointer pConfig)
40         : DlgPreferencePage(parent),
41           m_pConfig(pConfig),
42           m_pControlTrackTimeDisplay(std::make_unique<ControlObject>(
43                   ConfigKey("[Controls]", "ShowDurationRemaining"))),
44           m_pControlTrackTimeFormat(std::make_unique<ControlObject>(
45                   ConfigKey("[Controls]", "TimeFormat"))),
46           m_pNumDecks(
47                   make_parented<ControlProxy>("[Master]", "num_decks", this)),
48           m_pNumSamplers(make_parented<ControlProxy>(
49                   "[Master]", "num_samplers", this)),
50           m_iNumConfiguredDecks(0),
51           m_iNumConfiguredSamplers(0) {
52     setupUi(this);
53     // Create text color for the cue mode link "?" to the manual
54     createLinkColor();
55 
56     m_pNumDecks->connectValueChanged(this, [=](double value){slotNumDecksChanged(value);});
57     slotNumDecksChanged(m_pNumDecks->get(), true);
58 
59     m_pNumSamplers->connectValueChanged(this, [=](double value){slotNumSamplersChanged(value);});
60     slotNumSamplersChanged(m_pNumSamplers->get(), true);
61 
62     // Set default value in config file and control objects, if not present
63     // Default is "0" = Mixxx Mode
64     int cueDefaultValue = m_pConfig->getValue(
65             ConfigKey("[Controls]", "CueDefault"), 0);
66 
67     // Update combo box
68     ComboBoxCueMode->addItem(tr("Mixxx mode"), static_cast<int>(CueMode::Mixxx));
69     ComboBoxCueMode->addItem(tr("Mixxx mode (no blinking)"), static_cast<int>(CueMode::MixxxNoBlinking));
70     ComboBoxCueMode->addItem(tr("Pioneer mode"), static_cast<int>(CueMode::Pioneer));
71     ComboBoxCueMode->addItem(tr("Denon mode"), static_cast<int>(CueMode::Denon));
72     ComboBoxCueMode->addItem(tr("Numark mode"), static_cast<int>(CueMode::Numark));
73     ComboBoxCueMode->addItem(tr("CUP mode"), static_cast<int>(CueMode::CueAndPlay));
74     const int cueModeIndex = cueDefaultIndexByData(cueDefaultValue);
75     ComboBoxCueMode->setCurrentIndex(cueModeIndex);
76     slotCueModeCombobox(cueModeIndex);
77     for (ControlProxy* pControl : qAsConst(m_cueControls)) {
78         pControl->set(static_cast<int>(m_cueMode));
79     }
80     connect(ComboBoxCueMode,
81             QOverload<int>::of(&QComboBox::currentIndexChanged),
82             this,
83             &DlgPrefDeck::slotCueModeCombobox);
84 
85     // Track time display configuration
86     connect(m_pControlTrackTimeDisplay.get(),
87             &ControlObject::valueChanged,
88             this,
89             QOverload<double>::of(&DlgPrefDeck::slotSetTrackTimeDisplay));
90 
91     double positionDisplayType = m_pConfig->getValue(
92             ConfigKey("[Controls]", "PositionDisplay"),
93             static_cast<double>(TrackTime::DisplayMode::ELAPSED_AND_REMAINING));
94     if (positionDisplayType ==
95             static_cast<double>(TrackTime::DisplayMode::REMAINING)) {
96         radioButtonRemaining->setChecked(true);
97         m_pControlTrackTimeDisplay->set(
98             static_cast<double>(TrackTime::DisplayMode::REMAINING));
99     } else if (positionDisplayType ==
100                    static_cast<double>(TrackTime::DisplayMode::ELAPSED_AND_REMAINING)) {
101         radioButtonElapsedAndRemaining->setChecked(true);
102         m_pControlTrackTimeDisplay->set(
103             static_cast<double>(TrackTime::DisplayMode::ELAPSED_AND_REMAINING));
104     } else {
105         radioButtonElapsed->setChecked(true);
106         m_pControlTrackTimeDisplay->set(
107             static_cast<double>(TrackTime::DisplayMode::ELAPSED));
108     }
109     connect(buttonGroupTrackTime,
110             QOverload<QAbstractButton*>::of(&QButtonGroup::buttonClicked),
111             this,
112             QOverload<QAbstractButton*>::of(
113                     &DlgPrefDeck::slotSetTrackTimeDisplay));
114 
115     // display time format
116     connect(m_pControlTrackTimeFormat.get(),
117             &ControlObject::valueChanged,
118             this,
119             &DlgPrefDeck::slotTimeFormatChanged);
120 
121     // Track Display model
122     comboBoxTimeFormat->clear();
123 
124     comboBoxTimeFormat->addItem(tr("mm:ss%1zz - Traditional")
125                                 .arg(mixxx::DurationBase::kDecimalSeparator),
126                                 static_cast<int>
127                                 (TrackTime::DisplayFormat::TRADITIONAL));
128 
129     comboBoxTimeFormat->addItem(tr("mm:ss - Traditional (Coarse)"),
130                                 static_cast<int>
131                                 (TrackTime::DisplayFormat::TRADITIONAL_COARSE));
132 
133     comboBoxTimeFormat->addItem(tr("s%1zz - Seconds")
134                                 .arg(mixxx::DurationBase::kDecimalSeparator),
135                                 static_cast<int>
136                                 (TrackTime::DisplayFormat::SECONDS));
137 
138     comboBoxTimeFormat->addItem(tr("sss%1zz - Seconds (Long)")
139                                 .arg(mixxx::DurationBase::kDecimalSeparator),
140                                 static_cast<int>
141                                 (TrackTime::DisplayFormat::SECONDS_LONG));
142 
143     comboBoxTimeFormat->addItem(tr("s%1sss%2zz - Kiloseconds")
144                                 .arg(QString(mixxx::DurationBase::kDecimalSeparator),
145                                      QString(mixxx::DurationBase::kKiloGroupSeparator)),
146                                 static_cast<int>
147                                 (TrackTime::DisplayFormat::KILO_SECONDS));
148 
149     double time_format = static_cast<double>(
150                                             m_pConfig->getValue(
151                                             ConfigKey("[Controls]", "TimeFormat"),
152                                             static_cast<int>(TrackTime::DisplayFormat::TRADITIONAL)));
153     m_pControlTrackTimeFormat->set(time_format);
154     comboBoxTimeFormat->setCurrentIndex(
155                 comboBoxTimeFormat->findData(time_format));
156 
157     // Override Playing Track on Track Load
158     // The check box reflects the opposite of the config value
159     m_bDisallowTrackLoadToPlayingDeck = !m_pConfig->getValue(
160             ConfigKey("[Controls]", "AllowTrackLoadToPlayingDeck"), false);
161     checkBoxDisallowLoadToPlayingDeck->setChecked(m_bDisallowTrackLoadToPlayingDeck);
162     connect(checkBoxDisallowLoadToPlayingDeck,
163             &QCheckBox::toggled,
164             this,
165             &DlgPrefDeck::slotDisallowTrackLoadToPlayingDeckCheckbox);
166 
167     comboBoxLoadPoint->addItem(tr("Intro start"), static_cast<int>(SeekOnLoadMode::IntroStart));
168     comboBoxLoadPoint->addItem(tr("Main cue"), static_cast<int>(SeekOnLoadMode::MainCue));
169     comboBoxLoadPoint->addItem(tr("First sound (skip silence)"), static_cast<int>(SeekOnLoadMode::FirstSound));
170     comboBoxLoadPoint->addItem(tr("Beginning of track"), static_cast<int>(SeekOnLoadMode::Beginning));
171     bool seekModeExisted = m_pConfig->exists(ConfigKey("[Controls]", "CueRecall"));
172     int seekMode = m_pConfig->getValue(ConfigKey("[Controls]", "CueRecall"),
173             static_cast<int>(SeekOnLoadMode::IntroStart));
174     comboBoxLoadPoint->setCurrentIndex(
175             comboBoxLoadPoint->findData(seekMode));
176     m_seekOnLoadMode = static_cast<SeekOnLoadMode>(seekMode);
177     connect(comboBoxLoadPoint,
178             QOverload<int>::of(&QComboBox::currentIndexChanged),
179             this,
180             &DlgPrefDeck::slotSetTrackLoadMode);
181 
182     // This option was introduced in Mixxx 2.3 with the intro & outro cues.
183     // If the user has set main cue points with the intention of starting tracks
184     // from those points, enable this option. With Denon and Numark CueModes,
185     // it is not safe to assume that the user wants to start tracks from the
186     // main cue point because it is very easy to move the main cue point without
187     // explicitly intending to in those modes (the main cue point moves whenever
188     // the deck is not at the main cue point and play is pressed).
189     bool introStartMoveDefault = (m_seekOnLoadMode == SeekOnLoadMode::MainCue ||
190                                          !seekModeExisted) &&
191             !(m_cueMode == CueMode::Denon ||
192                     m_cueMode == CueMode::Numark);
193     m_bSetIntroStartAtMainCue = m_pConfig->getValue(ConfigKey("[Controls]", "SetIntroStartAtMainCue"),
194             introStartMoveDefault);
195     // This is an ugly hack to ensure AnalyzerSilence gets the correct default
196     // value because ConfigValue::getValue does not set the value of the ConfigValue
197     // in case no value had been set previously (when mixxx.cfg is empty).
198     m_pConfig->setValue(ConfigKey("[Controls]", "SetIntroStartAtMainCue"), m_bSetIntroStartAtMainCue);
199     checkBoxIntroStartMove->setChecked(m_bSetIntroStartAtMainCue);
200     connect(checkBoxIntroStartMove,
201             &QCheckBox::toggled,
202             this,
203             &DlgPrefDeck::slotMoveIntroStartCheckbox);
204 
205     // Double-tap Load to clone a deck via keyboard or controller ([ChannelN],LoadSelectedTrack)
206     m_bCloneDeckOnLoadDoubleTap = m_pConfig->getValue(
207             ConfigKey("[Controls]", "CloneDeckOnLoadDoubleTap"), true);
208     checkBoxCloneDeckOnLoadDoubleTap->setChecked(m_bCloneDeckOnLoadDoubleTap);
209     connect(checkBoxCloneDeckOnLoadDoubleTap,
210             &QCheckBox::toggled,
211             this,
212             &DlgPrefDeck::slotCloneDeckOnLoadDoubleTapCheckbox);
213 
214     m_bRateDownIncreasesSpeed = m_pConfig->getValue(ConfigKey("[Controls]", "RateDir"), true);
215     setRateDirectionForAllDecks(m_bRateDownIncreasesSpeed);
216     checkBoxInvertSpeedSlider->setChecked(m_bRateDownIncreasesSpeed);
217     connect(checkBoxInvertSpeedSlider,
218             &QCheckBox::toggled,
219             this,
220             &DlgPrefDeck::slotRateInversionCheckbox);
221 
222     ComboBoxRateRange->clear();
223     ComboBoxRateRange->addItem(tr("4%"), 4);
224     ComboBoxRateRange->addItem(tr("6% (semitone)"), 6);
225     ComboBoxRateRange->addItem(tr("8% (Technics SL-1210)"), 8);
226     ComboBoxRateRange->addItem(tr("10%"), 10);
227     ComboBoxRateRange->addItem(tr("16%"), 16);
228     ComboBoxRateRange->addItem(tr("24%"), 24);
229     ComboBoxRateRange->addItem(tr("50%"), 50);
230     ComboBoxRateRange->addItem(tr("90%"), 90);
231     connect(ComboBoxRateRange,
232             QOverload<int>::of(&QComboBox::currentIndexChanged),
233             this,
234             &DlgPrefDeck::slotRateRangeComboBox);
235 
236     // RateRange is the legacy ConfigKey. RateRangePercent is used now.
237     if (m_pConfig->exists(ConfigKey("[Controls]", "RateRange")) &&
238         !m_pConfig->exists(ConfigKey("[Controls]", "RateRangePercent"))) {
239         int legacyIndex = m_pConfig->getValueString(ConfigKey("[Controls]", "RateRange")).toInt();
240         if (legacyIndex == 0) {
241             m_iRateRangePercent = 6;
242         } else if (legacyIndex == 1) {
243             m_iRateRangePercent = 8;
244         } else {
245             m_iRateRangePercent = (legacyIndex-1) * 10;
246         }
247     } else {
248         m_iRateRangePercent = m_pConfig->getValue(ConfigKey("[Controls]", "RateRangePercent"),
249                                                   kDefaultRateRangePercent);
250     }
251     if (!(m_iRateRangePercent > 0 && m_iRateRangePercent <= 90)) {
252         m_iRateRangePercent = kDefaultRateRangePercent;
253     }
254     setRateRangeForAllDecks(m_iRateRangePercent);
255 
256     //
257     // Key lock mode
258     //
259     connect(buttonGroupKeyLockMode,
260             QOverload<QAbstractButton*>::of(&QButtonGroup::buttonClicked),
261             this,
262             &DlgPrefDeck::slotKeyLockModeSelected);
263 
264     m_keylockMode = static_cast<KeylockMode>(
265         m_pConfig->getValue(ConfigKey("[Controls]", "keylockMode"),
266                             static_cast<int>(KeylockMode::LockOriginalKey)));
267     for (ControlProxy* pControl : qAsConst(m_keylockModeControls)) {
268         pControl->set(static_cast<double>(m_keylockMode));
269     }
270 
271     //
272     // Key unlock mode
273     //
274     connect(buttonGroupKeyUnlockMode,
275             QOverload<QAbstractButton*>::of(&QButtonGroup::buttonClicked),
276             this,
277             &DlgPrefDeck::slotKeyUnlockModeSelected);
278 
279     m_keyunlockMode = static_cast<KeyunlockMode>(
280         m_pConfig->getValue(ConfigKey("[Controls]", "keyunlockMode"),
281         static_cast<int>(KeyunlockMode::ResetLockedKey)));
282     for (ControlProxy* pControl : qAsConst(m_keyunlockModeControls)) {
283         pControl->set(static_cast<int>(m_keyunlockMode));
284     }
285 
286     //
287     // Cue Mode
288     //
289 
290     // Add "(?)" with a manual link to the label
291     labelCueMode->setText(labelCueMode->text() + QStringLiteral(" ") +
292             coloredLinkString(
293                     m_pLinkColor,
294                     QStringLiteral("(?)"),
295                     MIXXX_MANUAL_CUE_MODES_URL));
296 
297     //
298     // Speed / Pitch reset configuration
299     //
300 
301     // Update "reset speed" and "reset pitch" check boxes
302     // TODO: All defaults should only be set in slotResetToDefaults.
303     int configSPAutoReset = m_pConfig->getValue<int>(
304                     ConfigKey("[Controls]", "SpeedAutoReset"),
305                     BaseTrackPlayer::RESET_PITCH);
306 
307     m_speedAutoReset = (configSPAutoReset==BaseTrackPlayer::RESET_SPEED ||
308                         configSPAutoReset==BaseTrackPlayer::RESET_PITCH_AND_SPEED);
309     m_pitchAutoReset = (configSPAutoReset==BaseTrackPlayer::RESET_PITCH ||
310                         configSPAutoReset==BaseTrackPlayer::RESET_PITCH_AND_SPEED);
311 
312     checkBoxResetSpeed->setChecked(m_speedAutoReset);
313     checkBoxResetPitch->setChecked(m_pitchAutoReset);
314 
315     connect(checkBoxResetSpeed, &QCheckBox::toggled, this, &DlgPrefDeck::slotUpdateSpeedAutoReset);
316     connect(checkBoxResetPitch, &QCheckBox::toggled, this, &DlgPrefDeck::slotUpdatePitchAutoReset);
317 
318     //
319     // Ramping Temporary Rate Change configuration
320     //
321 
322     // Rate Ramp Sensitivity slider & spinbox
323     connect(SliderRateRampSensitivity,
324             QOverload<int>::of(&QAbstractSlider::valueChanged),
325             SpinBoxRateRampSensitivity,
326             QOverload<int>::of(&QSpinBox::setValue));
327     connect(SpinBoxRateRampSensitivity,
328             QOverload<int>::of(&QSpinBox::valueChanged),
329             SliderRateRampSensitivity,
330             QOverload<int>::of(&QAbstractSlider::setValue));
331 
332     m_iRateRampSensitivity =
333             m_pConfig->getValue(ConfigKey("[Controls]", "RateRampSensitivity"),
334                     kDefaultRateRampSensitivity);
335     SliderRateRampSensitivity->setValue(m_iRateRampSensitivity);
336     connect(SliderRateRampSensitivity,
337             &QSlider::valueChanged,
338             this,
339             &DlgPrefDeck::slotRateRampSensitivitySlider);
340 
341     // Enable/disable permanent rate spinboxes when smooth ramping is selected
342     connect(radioButtonRateRampModeLinear,
343             &QRadioButton::toggled,
344             labelSpeedRampSensitivity,
345             &QWidget::setEnabled);
346     connect(radioButtonRateRampModeLinear,
347             &QRadioButton::toggled,
348             SliderRateRampSensitivity,
349             &QWidget::setEnabled);
350     connect(radioButtonRateRampModeLinear,
351             &QRadioButton::toggled,
352             SpinBoxRateRampSensitivity,
353             &QWidget::setEnabled);
354     // Enable/disable temporary rate spinboxes when abrupt ramping is selected
355     connect(radioButtonRateRampModeStepping,
356             &QRadioButton::toggled,
357             labelSpeedTemporary,
358             &QWidget::setEnabled);
359     connect(radioButtonRateRampModeStepping,
360             &QRadioButton::toggled,
361             spinBoxTemporaryRateCoarse,
362             &QWidget::setEnabled);
363     connect(radioButtonRateRampModeStepping,
364             &QRadioButton::toggled,
365             spinBoxTemporaryRateFine,
366             &QWidget::setEnabled);
367     // Set Ramp Rate On or Off
368     connect(radioButtonRateRampModeLinear,
369             &QRadioButton::toggled,
370             this,
371             &DlgPrefDeck::slotRateRampingModeLinearButton);
372     m_bRateRamping = static_cast<RateControl::RampMode>(
373             m_pConfig->getValue(ConfigKey("[Controls]", "RateRamp"),
374                     static_cast<int>(kDefaultRampingMode)));
375     if (m_bRateRamping == RateControl::RampMode::Linear) {
376         radioButtonRateRampModeLinear->setChecked(true);
377     } else {
378         radioButtonRateRampModeStepping->setChecked(true);
379     }
380 
381     // Rate buttons configuration
382     connect(spinBoxTemporaryRateCoarse,
383             QOverload<double>::of(&QDoubleSpinBox::valueChanged),
384             this,
385             &DlgPrefDeck::slotRateTempCoarseSpinbox);
386     connect(spinBoxTemporaryRateFine,
387             QOverload<double>::of(&QDoubleSpinBox::valueChanged),
388             this,
389             &DlgPrefDeck::slotRateTempFineSpinbox);
390     connect(spinBoxPermanentRateCoarse,
391             QOverload<double>::of(&QDoubleSpinBox::valueChanged),
392             this,
393             &DlgPrefDeck::slotRatePermCoarseSpinbox);
394     connect(spinBoxPermanentRateFine,
395             QOverload<double>::of(&QDoubleSpinBox::valueChanged),
396             this,
397             &DlgPrefDeck::slotRatePermFineSpinbox);
398 
399     m_dRateTempCoarse = m_pConfig->getValue(ConfigKey("[Controls]", "RateTempLeft"),
400             kDefaultTemporaryRateChangeCoarse);
401     m_dRateTempFine = m_pConfig->getValue(ConfigKey("[Controls]", "RateTempRight"),
402             kDefaultTemporaryRateChangeFine);
403     m_dRatePermCoarse = m_pConfig->getValue(ConfigKey("[Controls]", "RatePermLeft"),
404             kDefaultPermanentRateChangeCoarse);
405     m_dRatePermFine = m_pConfig->getValue(ConfigKey("[Controls]", "RatePermRight"),
406             kDefaultPermanentRateChangeFine);
407 
408     spinBoxTemporaryRateCoarse->setValue(m_dRateTempCoarse);
409     spinBoxTemporaryRateFine->setValue(m_dRateTempFine);
410     spinBoxPermanentRateCoarse->setValue(m_dRatePermCoarse);
411     spinBoxPermanentRateFine->setValue(m_dRatePermFine);
412 
413     RateControl::setTemporaryRateChangeCoarseAmount(m_dRateTempCoarse);
414     RateControl::setTemporaryRateChangeFineAmount(m_dRateTempFine);
415     RateControl::setPermanentRateChangeCoarseAmount(m_dRatePermCoarse);
416     RateControl::setPermanentRateChangeFineAmount(m_dRatePermFine);
417 
418     slotUpdate();
419 }
420 
~DlgPrefDeck()421 DlgPrefDeck::~DlgPrefDeck() {
422     qDeleteAll(m_rateControls);
423     qDeleteAll(m_rateDirectionControls);
424     qDeleteAll(m_cueControls);
425     qDeleteAll(m_rateRangeControls);
426     qDeleteAll(m_keylockModeControls);
427     qDeleteAll(m_keyunlockModeControls);
428 }
429 
slotUpdate()430 void DlgPrefDeck::slotUpdate() {
431     checkBoxIntroStartMove->setChecked(m_pConfig->getValue(
432             ConfigKey("[Controls]", "SetIntroStartAtMainCue"), false));
433 
434     slotSetTrackTimeDisplay(m_pControlTrackTimeDisplay->get());
435 
436     checkBoxDisallowLoadToPlayingDeck->setChecked(!m_pConfig->getValue(
437             ConfigKey("[Controls]", "AllowTrackLoadToPlayingDeck"), false));
438 
439     checkBoxCloneDeckOnLoadDoubleTap->setChecked(m_pConfig->getValue(
440             ConfigKey("[Controls]", "CloneDeckOnLoadDoubleTap"), true));
441 
442     double rateRange = m_rateRangeControls[0]->get();
443     int index = ComboBoxRateRange->findData(static_cast<int>(rateRange * 100.0));
444     if (index == -1) {
445         ComboBoxRateRange->addItem(QString::number(rateRange * 100.).append("%"),
446                 rateRange * 100.);
447     }
448     ComboBoxRateRange->setCurrentIndex(index);
449 
450     double rateDirection = m_rateDirectionControls[0]->get();
451     checkBoxInvertSpeedSlider->setChecked(rateDirection == kRateDirectionInverted);
452 
453     double cueMode = m_cueControls[0]->get();
454     index = ComboBoxCueMode->findData(static_cast<int>(cueMode));
455     ComboBoxCueMode->setCurrentIndex(index);
456 
457     KeylockMode keylockMode =
458             static_cast<KeylockMode>(static_cast<int>(m_keylockModeControls[0]->get()));
459     if (keylockMode == KeylockMode::LockCurrentKey) {
460         radioButtonCurrentKey->setChecked(true);
461     } else {
462         radioButtonOriginalKey->setChecked(true);
463     }
464 
465     KeyunlockMode keyunlockMode =
466             static_cast<KeyunlockMode>(static_cast<int>(m_keyunlockModeControls[0]->get()));
467     if (keyunlockMode == KeyunlockMode::KeepLockedKey) {
468         radioButtonKeepUnlockedKey->setChecked(true);
469     } else {
470         radioButtonResetUnlockedKey->setChecked(true);
471     }
472 
473     int reset = m_pConfig->getValue(ConfigKey("[Controls]", "SpeedAutoReset"),
474         static_cast<int>(BaseTrackPlayer::RESET_PITCH));
475     if (reset == BaseTrackPlayer::RESET_PITCH) {
476         checkBoxResetPitch->setChecked(true);
477         checkBoxResetSpeed->setChecked(false);
478     } else if (reset == BaseTrackPlayer::RESET_SPEED) {
479         checkBoxResetPitch->setChecked(false);
480         checkBoxResetSpeed->setChecked(true);
481     } else if (reset == BaseTrackPlayer::RESET_PITCH_AND_SPEED) {
482         checkBoxResetPitch->setChecked(true);
483         checkBoxResetSpeed->setChecked(true);
484     } else if (reset == BaseTrackPlayer::RESET_NONE) {
485         checkBoxResetPitch->setChecked(false);
486         checkBoxResetSpeed->setChecked(false);
487     }
488 
489     if (m_bRateRamping == RateControl::RampMode::Linear) {
490         radioButtonRateRampModeLinear->setChecked(true);
491     } else {
492         radioButtonRateRampModeStepping->setChecked(true);
493     }
494 
495     SliderRateRampSensitivity->setValue(
496         m_pConfig->getValue(ConfigKey("[Controls]", "RateRampSensitivity"),
497                             kDefaultRateRampSensitivity));
498 
499     spinBoxTemporaryRateCoarse->setValue(RateControl::getTemporaryRateChangeCoarseAmount());
500     spinBoxTemporaryRateFine->setValue(RateControl::getTemporaryRateChangeFineAmount());
501     spinBoxPermanentRateCoarse->setValue(RateControl::getPermanentRateChangeCoarseAmount());
502     spinBoxPermanentRateFine->setValue(RateControl::getPermanentRateChangeFineAmount());
503 }
504 
slotResetToDefaults()505 void DlgPrefDeck::slotResetToDefaults() {
506     // Track time display mode
507     radioButtonRemaining->setChecked(true);
508 
509     // Up increases speed.
510     checkBoxInvertSpeedSlider->setChecked(false);
511 
512     // 8% Rate Range
513     ComboBoxRateRange->setCurrentIndex(ComboBoxRateRange->findData(kDefaultRateRangePercent));
514 
515     // Don't load tracks into playing decks.
516     checkBoxDisallowLoadToPlayingDeck->setChecked(true);
517 
518     // Clone decks by double-tapping Load button.
519     checkBoxCloneDeckOnLoadDoubleTap->setChecked(kDefaultCloneDeckOnLoad);
520     // Mixxx cue mode
521     ComboBoxCueMode->setCurrentIndex(0);
522 
523     // Load at intro start
524     comboBoxLoadPoint->setCurrentIndex(
525             comboBoxLoadPoint->findData(static_cast<int>(SeekOnLoadMode::IntroStart)));
526 
527     // Rate-ramping default off.
528     radioButtonRateRampModeStepping->setChecked(true);
529 
530     SliderRateRampSensitivity->setValue(kDefaultRateRampSensitivity);
531 
532     // Permanent and temporary pitch adjust fine/coarse.
533     spinBoxTemporaryRateCoarse->setValue(4.0);
534     spinBoxTemporaryRateFine->setValue(2.0);
535     spinBoxPermanentRateCoarse->setValue(0.50);
536     spinBoxPermanentRateFine->setValue(0.05);
537 
538     checkBoxResetSpeed->setChecked(false);
539     checkBoxResetPitch->setChecked(true);
540 
541     radioButtonOriginalKey->setChecked(true);
542     radioButtonResetUnlockedKey->setChecked(true);
543 }
544 
slotMoveIntroStartCheckbox(bool checked)545 void DlgPrefDeck::slotMoveIntroStartCheckbox(bool checked) {
546     m_bSetIntroStartAtMainCue = checked;
547 }
548 
slotRateRangeComboBox(int index)549 void DlgPrefDeck::slotRateRangeComboBox(int index) {
550     m_iRateRangePercent = ComboBoxRateRange->itemData(index).toInt();
551 }
552 
setRateRangeForAllDecks(int rangePercent)553 void DlgPrefDeck::setRateRangeForAllDecks(int rangePercent) {
554     for (ControlProxy* pControl : qAsConst(m_rateRangeControls)) {
555         pControl->set(rangePercent / 100.0);
556     }
557 }
558 
slotRateInversionCheckbox(bool inverted)559 void DlgPrefDeck::slotRateInversionCheckbox(bool inverted) {
560     m_bRateDownIncreasesSpeed = inverted;
561 }
562 
setRateDirectionForAllDecks(bool inverted)563 void DlgPrefDeck::setRateDirectionForAllDecks(bool inverted) {
564     double oldRateDirectionMultiplier = m_rateDirectionControls[0]->get();
565     double rateDirectionMultiplier = 1.0;
566     if (inverted) {
567         rateDirectionMultiplier = kRateDirectionInverted;
568     }
569     for (ControlProxy* pControl : qAsConst(m_rateDirectionControls)) {
570         pControl->set(rateDirectionMultiplier);
571     }
572 
573     // If the rate slider direction setting has changed,
574     // multiply the rate by -1 so the current sound does not change.
575     if (rateDirectionMultiplier != oldRateDirectionMultiplier) {
576         for (ControlProxy* pControl : qAsConst(m_rateControls)) {
577             pControl->set(-1 * pControl->get());
578         }
579     }
580 }
581 
slotKeyLockModeSelected(QAbstractButton * pressedButton)582 void DlgPrefDeck::slotKeyLockModeSelected(QAbstractButton* pressedButton) {
583     if (pressedButton == radioButtonCurrentKey) {
584         m_keylockMode = KeylockMode::LockCurrentKey;
585     } else {
586         m_keylockMode = KeylockMode::LockOriginalKey;
587     }
588 }
589 
slotKeyUnlockModeSelected(QAbstractButton * pressedButton)590 void DlgPrefDeck::slotKeyUnlockModeSelected(QAbstractButton* pressedButton) {
591     if (pressedButton == radioButtonResetUnlockedKey) {
592         m_keyunlockMode = KeyunlockMode::ResetLockedKey;
593     } else {
594         m_keyunlockMode = KeyunlockMode::KeepLockedKey;
595     }
596 }
597 
slotDisallowTrackLoadToPlayingDeckCheckbox(bool checked)598 void DlgPrefDeck::slotDisallowTrackLoadToPlayingDeckCheckbox(bool checked) {
599     m_bDisallowTrackLoadToPlayingDeck = checked;
600 }
601 
slotCueModeCombobox(int index)602 void DlgPrefDeck::slotCueModeCombobox(int index) {
603     m_cueMode = static_cast<CueMode>(ComboBoxCueMode->itemData(index).toInt());
604 }
605 
slotCloneDeckOnLoadDoubleTapCheckbox(bool checked)606 void DlgPrefDeck::slotCloneDeckOnLoadDoubleTapCheckbox(bool checked) {
607     m_bCloneDeckOnLoadDoubleTap = checked;
608 }
609 
slotSetTrackTimeDisplay(QAbstractButton * b)610 void DlgPrefDeck::slotSetTrackTimeDisplay(QAbstractButton* b) {
611     if (b == radioButtonRemaining) {
612         m_timeDisplayMode = TrackTime::DisplayMode::REMAINING;
613     } else if (b == radioButtonElapsedAndRemaining) {
614         m_timeDisplayMode = TrackTime::DisplayMode::ELAPSED_AND_REMAINING;
615     } else {
616         m_timeDisplayMode = TrackTime::DisplayMode::ELAPSED;
617     }
618 }
619 
slotSetTrackTimeDisplay(double v)620 void DlgPrefDeck::slotSetTrackTimeDisplay(double v) {
621     m_timeDisplayMode = static_cast<TrackTime::DisplayMode>(static_cast<int>(v));
622     m_pConfig->set(ConfigKey("[Controls]","PositionDisplay"), ConfigValue(v));
623     if (m_timeDisplayMode == TrackTime::DisplayMode::REMAINING) {
624         radioButtonRemaining->setChecked(true);
625     } else if (m_timeDisplayMode == TrackTime::DisplayMode::ELAPSED_AND_REMAINING) {
626         radioButtonElapsedAndRemaining->setChecked(true);
627     } else { // Elapsed
628         radioButtonElapsed->setChecked(true);
629     }
630 }
631 
slotRateTempCoarseSpinbox(double value)632 void DlgPrefDeck::slotRateTempCoarseSpinbox(double value) {
633     m_dRateTempCoarse = value;
634 }
635 
slotRateTempFineSpinbox(double value)636 void DlgPrefDeck::slotRateTempFineSpinbox(double value) {
637     m_dRateTempFine = value;
638 }
639 
slotRatePermCoarseSpinbox(double value)640 void DlgPrefDeck::slotRatePermCoarseSpinbox(double value) {
641     m_dRatePermCoarse = value;
642 }
643 
slotRatePermFineSpinbox(double value)644 void DlgPrefDeck::slotRatePermFineSpinbox(double value) {
645     m_dRatePermFine = value;
646 }
647 
slotRateRampSensitivitySlider(int value)648 void DlgPrefDeck::slotRateRampSensitivitySlider(int value) {
649     m_iRateRampSensitivity = value;
650 }
651 
slotRateRampingModeLinearButton(bool checked)652 void DlgPrefDeck::slotRateRampingModeLinearButton(bool checked) {
653     if (checked) {
654         m_bRateRamping = RateControl::RampMode::Linear;
655     } else {
656         m_bRateRamping = RateControl::RampMode::Stepping;
657     }
658 }
659 
slotTimeFormatChanged(double v)660 void DlgPrefDeck::slotTimeFormatChanged(double v) {
661     int i = static_cast<int>(v);
662     m_pConfig->set(ConfigKey("[Controls]","TimeFormat"), ConfigValue(v));
663     comboBoxTimeFormat->setCurrentIndex(
664                 comboBoxTimeFormat->findData(i));
665 }
666 
slotSetTrackLoadMode(int comboboxIndex)667 void DlgPrefDeck::slotSetTrackLoadMode(int comboboxIndex) {
668     m_seekOnLoadMode = static_cast<SeekOnLoadMode>(
669             comboBoxLoadPoint->itemData(comboboxIndex).toInt());
670 }
671 
slotApply()672 void DlgPrefDeck::slotApply() {
673     m_pConfig->set(ConfigKey("[Controls]", "SetIntroStartAtMainCue"),
674             ConfigValue(m_bSetIntroStartAtMainCue));
675 
676     double timeDisplay = static_cast<double>(m_timeDisplayMode);
677     m_pConfig->set(ConfigKey("[Controls]","PositionDisplay"), ConfigValue(timeDisplay));
678     m_pControlTrackTimeDisplay->set(timeDisplay);
679 
680     // time format
681     double timeFormat = comboBoxTimeFormat->itemData(comboBoxTimeFormat->currentIndex()).toDouble();
682     m_pControlTrackTimeFormat->set(timeFormat);
683     m_pConfig->setValue(ConfigKey("[Controls]", "TimeFormat"), timeFormat);
684 
685     // Set cue mode for every deck
686     for (ControlProxy* pControl : qAsConst(m_cueControls)) {
687         pControl->set(static_cast<int>(m_cueMode));
688     }
689     m_pConfig->setValue(ConfigKey("[Controls]", "CueDefault"), static_cast<int>(m_cueMode));
690 
691     m_pConfig->setValue(ConfigKey("[Controls]", "AllowTrackLoadToPlayingDeck"),
692                         !m_bDisallowTrackLoadToPlayingDeck);
693 
694     m_pConfig->setValue(ConfigKey("[Controls]", "CueRecall"), static_cast<int>(m_seekOnLoadMode));
695     m_pConfig->setValue(ConfigKey("[Controls]", "CloneDeckOnLoadDoubleTap"),
696             m_bCloneDeckOnLoadDoubleTap);
697 
698     // Set rate range
699     setRateRangeForAllDecks(m_iRateRangePercent);
700     m_pConfig->setValue(ConfigKey("[Controls]", "RateRangePercent"),
701                         m_iRateRangePercent);
702 
703     setRateDirectionForAllDecks(m_bRateDownIncreasesSpeed);
704     m_pConfig->setValue(ConfigKey("[Controls]", "RateDir"),
705             static_cast<int>(m_bRateDownIncreasesSpeed));
706 
707     int configSPAutoReset = BaseTrackPlayer::RESET_NONE;
708 
709     if (m_speedAutoReset && m_pitchAutoReset) {
710         configSPAutoReset = BaseTrackPlayer::RESET_PITCH_AND_SPEED;
711     } else if (m_speedAutoReset) {
712         configSPAutoReset = BaseTrackPlayer::RESET_SPEED;
713     } else if (m_pitchAutoReset) {
714         configSPAutoReset = BaseTrackPlayer::RESET_PITCH;
715     }
716 
717     m_pConfig->set(ConfigKey("[Controls]", "SpeedAutoReset"),
718                    ConfigValue(configSPAutoReset));
719 
720     m_pConfig->setValue(ConfigKey("[Controls]", "keylockMode"),
721                         static_cast<int>(m_keylockMode));
722     // Set key lock behavior for every group
723     for (ControlProxy* pControl : qAsConst(m_keylockModeControls)) {
724         pControl->set(static_cast<double>(m_keylockMode));
725     }
726 
727     m_pConfig->setValue(ConfigKey("[Controls]", "keyunlockMode"),
728                         static_cast<int>(m_keyunlockMode));
729     // Set key un-lock behavior for every group
730     for (ControlProxy* pControl : qAsConst(m_keyunlockModeControls)) {
731         pControl->set(static_cast<double>(m_keyunlockMode));
732     }
733 
734     RateControl::setRateRampMode(m_bRateRamping);
735     m_pConfig->setValue(ConfigKey("[Controls]", "RateRamp"), static_cast<int>(m_bRateRamping));
736 
737     RateControl::setRateRampSensitivity(m_iRateRampSensitivity);
738     m_pConfig->setValue(ConfigKey("[Controls]", "RateRampSensitivity"), m_iRateRampSensitivity);
739 
740     RateControl::setTemporaryRateChangeCoarseAmount(m_dRateTempCoarse);
741     RateControl::setTemporaryRateChangeFineAmount(m_dRateTempFine);
742     RateControl::setPermanentRateChangeCoarseAmount(m_dRatePermCoarse);
743     RateControl::setPermanentRateChangeFineAmount(m_dRatePermFine);
744 
745     m_pConfig->setValue(ConfigKey("[Controls]", "RateTempLeft"), m_dRateTempCoarse);
746     m_pConfig->setValue(ConfigKey("[Controls]", "RateTempRight"), m_dRateTempFine);
747     m_pConfig->setValue(ConfigKey("[Controls]", "RatePermLeft"), m_dRatePermCoarse);
748     m_pConfig->setValue(ConfigKey("[Controls]", "RatePermRight"), m_dRatePermFine);
749 }
750 
slotNumDecksChanged(double new_count,bool initializing)751 void DlgPrefDeck::slotNumDecksChanged(double new_count, bool initializing) {
752     int numdecks = static_cast<int>(new_count);
753     if (numdecks <= m_iNumConfiguredDecks) {
754         // TODO(owilliams): If we implement deck deletion, shrink the size of configured decks.
755         return;
756     }
757 
758     for (int i = m_iNumConfiguredDecks; i < numdecks; ++i) {
759         QString group = PlayerManager::groupForDeck(i);
760         m_rateControls.push_back(new ControlProxy(
761                 group, "rate"));
762         m_rateRangeControls.push_back(new ControlProxy(
763                 group, "rateRange"));
764         m_rateDirectionControls.push_back(new ControlProxy(
765                 group, "rate_dir"));
766         m_cueControls.push_back(new ControlProxy(
767                 group, "cue_mode"));
768         m_keylockModeControls.push_back(new ControlProxy(
769                 group, "keylockMode"));
770         m_keylockModeControls.last()->set(static_cast<double>(m_keylockMode));
771         m_keyunlockModeControls.push_back(new ControlProxy(
772                 group, "keyunlockMode"));
773         m_keyunlockModeControls.last()->set(static_cast<double>(m_keyunlockMode));
774     }
775 
776     m_iNumConfiguredDecks = numdecks;
777 
778     // The rate range hasn't been read from the config file when this is first called.
779     if (!initializing) {
780         setRateDirectionForAllDecks(m_rateDirectionControls[0]->get() == kRateDirectionInverted);
781         setRateRangeForAllDecks(static_cast<int>(m_rateRangeControls[0]->get() * 100.0));
782     }
783 }
784 
slotNumSamplersChanged(double new_count,bool initializing)785 void DlgPrefDeck::slotNumSamplersChanged(double new_count, bool initializing) {
786     int numsamplers = static_cast<int>(new_count);
787     if (numsamplers <= m_iNumConfiguredSamplers) {
788         return;
789     }
790 
791     for (int i = m_iNumConfiguredSamplers; i < numsamplers; ++i) {
792         QString group = PlayerManager::groupForSampler(i);
793         m_rateControls.push_back(new ControlProxy(
794                 group, "rate"));
795         m_rateRangeControls.push_back(new ControlProxy(
796                 group, "rateRange"));
797         m_rateDirectionControls.push_back(new ControlProxy(
798                 group, "rate_dir"));
799         m_cueControls.push_back(new ControlProxy(
800                 group, "cue_mode"));
801         m_keylockModeControls.push_back(new ControlProxy(
802                 group, "keylockMode"));
803         m_keylockModeControls.last()->set(static_cast<double>(m_keylockMode));
804         m_keyunlockModeControls.push_back(new ControlProxy(
805                 group, "keyunlockMode"));
806         m_keyunlockModeControls.last()->set(static_cast<double>(m_keyunlockMode));
807     }
808 
809     m_iNumConfiguredSamplers = numsamplers;
810 
811     // The rate range hasn't been read from the config file when this is first called.
812     if (!initializing) {
813         setRateDirectionForAllDecks(m_rateDirectionControls[0]->get() == kRateDirectionInverted);
814         setRateRangeForAllDecks(static_cast<int>(m_rateRangeControls[0]->get() * 100.0));
815     }
816 }
817 
slotUpdateSpeedAutoReset(bool b)818 void DlgPrefDeck::slotUpdateSpeedAutoReset(bool b) {
819     m_speedAutoReset = b;
820 }
821 
slotUpdatePitchAutoReset(bool b)822 void DlgPrefDeck::slotUpdatePitchAutoReset(bool b) {
823     m_pitchAutoReset = b;
824 }
825 
cueDefaultIndexByData(int userData) const826 int DlgPrefDeck::cueDefaultIndexByData(int userData) const {
827     for (int i = 0; i < ComboBoxCueMode->count(); ++i) {
828         if (ComboBoxCueMode->itemData(i).toInt() == userData) {
829             return i;
830         }
831     }
832     qWarning() << "No default cue behavior found for value" << userData
833                << "returning default";
834     return 0;
835 }
836