1 #include "library/autodj/dlgautodj.h"
2
3 #include <QMessageBox>
4
5 #include "library/playlisttablemodel.h"
6 #include "library/trackcollectionmanager.h"
7 #include "moc_dlgautodj.cpp"
8 #include "track/track.h"
9 #include "util/assert.h"
10 #include "util/compatibility.h"
11 #include "util/duration.h"
12 #include "widget/wlibrary.h"
13 #include "widget/wtracktableview.h"
14
15 namespace {
16 const char* kPreferenceGroupName = "[Auto DJ]";
17 const char* kRepeatPlaylistPreference = "Requeue";
18 } // anonymous namespace
19
DlgAutoDJ(WLibrary * parent,UserSettingsPointer pConfig,Library * pLibrary,AutoDJProcessor * pProcessor,KeyboardEventFilter * pKeyboard)20 DlgAutoDJ::DlgAutoDJ(
21 WLibrary* parent,
22 UserSettingsPointer pConfig,
23 Library* pLibrary,
24 AutoDJProcessor* pProcessor,
25 KeyboardEventFilter* pKeyboard)
26 : QWidget(parent),
27 Ui::DlgAutoDJ(),
28 m_pConfig(pConfig),
29 m_pAutoDJProcessor(pProcessor),
30 m_pTrackTableView(new WTrackTableView(this, m_pConfig,
31 pLibrary->trackCollections(),
32 parent->getTrackTableBackgroundColorOpacity(),
33 /*no sorting*/ false)),
34 m_bShowButtonText(parent->getShowButtonText()),
35 m_pAutoDJTableModel(nullptr) {
36 setupUi(this);
37
38 m_pTrackTableView->installEventFilter(pKeyboard);
39
40 connect(m_pTrackTableView,
41 &WTrackTableView::loadTrack,
42 this,
43 &DlgAutoDJ::loadTrack);
44 connect(m_pTrackTableView,
45 &WTrackTableView::loadTrackToPlayer,
46 this,
47 &DlgAutoDJ::loadTrackToPlayer);
48 connect(m_pTrackTableView,
49 &WTrackTableView::trackSelected,
50 this,
51 &DlgAutoDJ::trackSelected);
52 connect(m_pTrackTableView,
53 &WTrackTableView::trackSelected,
54 this,
55 &DlgAutoDJ::updateSelectionInfo);
56
57 connect(pLibrary,
58 &Library::setTrackTableFont,
59 m_pTrackTableView,
60 &WTrackTableView::setTrackTableFont);
61 connect(pLibrary,
62 &Library::setTrackTableRowHeight,
63 m_pTrackTableView,
64 &WTrackTableView::setTrackTableRowHeight);
65 connect(pLibrary,
66 &Library::setSelectedClick,
67 m_pTrackTableView,
68 &WTrackTableView::setSelectedClick);
69
70 QBoxLayout* box = qobject_cast<QBoxLayout*>(layout());
71 VERIFY_OR_DEBUG_ASSERT(box) { //Assumes the form layout is a QVBox/QHBoxLayout!
72 } else {
73 box->removeWidget(m_pTrackTablePlaceholder);
74 m_pTrackTablePlaceholder->hide();
75 box->insertWidget(1, m_pTrackTableView);
76 }
77
78 // We do _NOT_ take ownership of this from AutoDJProcessor.
79 m_pAutoDJTableModel = m_pAutoDJProcessor->getTableModel();
80 m_pTrackTableView->loadTrackModel(m_pAutoDJTableModel);
81
82 // Override some playlist-view properties:
83
84 // Do not set this because it disables auto-scrolling
85 //m_pTrackTableView->setDragDropMode(QAbstractItemView::InternalMove);
86
87 connect(pushButtonAutoDJ,
88 &QPushButton::clicked,
89 this,
90 &DlgAutoDJ::toggleAutoDJButton);
91
92 setupActionButton(pushButtonFadeNow, &DlgAutoDJ::fadeNowButton, tr("Fade"));
93 setupActionButton(pushButtonSkipNext, &DlgAutoDJ::skipNextButton, tr("Skip"));
94 setupActionButton(pushButtonShuffle, &DlgAutoDJ::shufflePlaylistButton, tr("Shuffle"));
95 setupActionButton(pushButtonAddRandom, &DlgAutoDJ::addRandomButton, tr("Random"));
96
97 m_enableBtnTooltip = tr(
98 "Enable Auto DJ\n"
99 "\n"
100 "Shortcut: Shift+F12");
101 m_disableBtnTooltip = tr(
102 "Disable Auto DJ\n"
103 "\n"
104 "Shortcut: Shift+F12");
105 QString fadeBtnTooltip = tr(
106 "Trigger the transition to the next track\n"
107 "\n"
108 "Shortcut: Shift+F11");
109 QString skipBtnTooltip = tr(
110 "Skip the next track in the Auto DJ queue\n"
111 "\n"
112 "Shortcut: Shift+F10");
113 QString shuffleBtnTooltip = tr(
114 "Shuffle the content of the Auto DJ queue\n"
115 "\n"
116 "Shortcut: Shift+F9");
117 QString addRandomBtnTooltip = tr(
118 "Adds a random track from track sources (crates) to the Auto DJ queue.\n"
119 "If no track sources are configured, the track is added from the library instead.");
120 QString repeatBtnTooltip = tr(
121 "Repeat the playlist");
122 QString spinBoxTransitionTooltip = tr(
123 "Determines the duration of the transition");
124 QString labelTransitionTooltip = tr(
125 // "sec" as in seconds
126 "Seconds");
127 QString fadeModeTooltip = tr(
128 "Auto DJ Fade Modes\n"
129 "\n"
130 "Full Intro + Outro:\n"
131 "Play the full intro and outro. Use the intro or outro length as the\n"
132 "crossfade time, whichever is shorter. If no intro or outro are marked,\n"
133 "use the selected crossfade time.\n"
134 "\n"
135 "Fade At Outro Start:\n"
136 "Start crossfading at the outro start. If the outro is longer than the\n"
137 "intro, cut off the end of the outro. Use the intro or outro length as\n"
138 "the crossfade time, whichever is shorter. If no intro or outro are\n"
139 "marked, use the selected crossfade time.\n"
140 "\n"
141 "Full Track:\n"
142 "Play the whole track. Begin crossfading from the selected number of\n"
143 "seconds before the end of the track. A negative crossfade time adds\n"
144 "silence between tracks.\n"
145 "\n"
146 "Skip Silence:\n"
147 "Play the whole track except for silence at the beginning and end.\n"
148 "Begin crossfading from the selected number of seconds before the\n"
149 "last sound.");
150
151 pushButtonFadeNow->setToolTip(fadeBtnTooltip);
152 pushButtonSkipNext->setToolTip(skipBtnTooltip);
153 pushButtonShuffle->setToolTip(shuffleBtnTooltip);
154 pushButtonAddRandom->setToolTip(addRandomBtnTooltip);
155 pushButtonRepeatPlaylist->setToolTip(repeatBtnTooltip);
156 spinBoxTransition->setToolTip(spinBoxTransitionTooltip);
157 labelTransitionAppendix->setToolTip(labelTransitionTooltip);
158 fadeModeCombobox->setToolTip(fadeModeTooltip);
159
160 // Prevent the interactive widgets from being focused with Tab or Shift+Tab
161 fadeModeCombobox->setFocusPolicy(Qt::ClickFocus);
162 spinBoxTransition->setFocusPolicy(Qt::ClickFocus);
163 // work around QLineEdit being protected
164 spinBoxTransition->findChild<QLineEdit*>()->setFocusPolicy(Qt::ClickFocus);
165
166 connect(spinBoxTransition,
167 QOverload<int>::of(&QSpinBox::valueChanged),
168 this,
169 &DlgAutoDJ::transitionSliderChanged);
170
171 fadeModeCombobox->addItem(tr("Full Intro + Outro"),
172 static_cast<int>(AutoDJProcessor::TransitionMode::FullIntroOutro));
173 fadeModeCombobox->addItem(tr("Fade At Outro Start"),
174 static_cast<int>(AutoDJProcessor::TransitionMode::FadeAtOutroStart));
175 fadeModeCombobox->addItem(tr("Full Track"),
176 static_cast<int>(AutoDJProcessor::TransitionMode::FixedFullTrack));
177 fadeModeCombobox->addItem(tr("Skip Silence"),
178 static_cast<int>(AutoDJProcessor::TransitionMode::FixedSkipSilence));
179 fadeModeCombobox->setCurrentIndex(
180 fadeModeCombobox->findData(static_cast<int>(m_pAutoDJProcessor->getTransitionMode())));
181 connect(fadeModeCombobox,
182 QOverload<int>::of(&QComboBox::currentIndexChanged),
183 this,
184 &DlgAutoDJ::slotTransitionModeChanged);
185
186 connect(pushButtonRepeatPlaylist,
187 &QPushButton::clicked,
188 this,
189 &DlgAutoDJ::slotRepeatPlaylistChanged);
190 if (m_bShowButtonText) {
191 pushButtonRepeatPlaylist->setText(tr("Repeat"));
192 }
193 bool repeatPlaylist = m_pConfig->getValue<bool>(
194 ConfigKey(kPreferenceGroupName, kRepeatPlaylistPreference));
195 pushButtonRepeatPlaylist->setChecked(repeatPlaylist);
196 slotRepeatPlaylistChanged(repeatPlaylist);
197
198 // Setup DlgAutoDJ UI based on the current AutoDJProcessor state. Keep in
199 // mind that AutoDJ may already be active when DlgAutoDJ is created (due to
200 // skin changes, etc.).
201 spinBoxTransition->setValue(static_cast<int>(m_pAutoDJProcessor->getTransitionTime()));
202 connect(m_pAutoDJProcessor,
203 &AutoDJProcessor::transitionTimeChanged,
204 this,
205 &DlgAutoDJ::transitionTimeChanged);
206
207 connect(m_pAutoDJProcessor,
208 &AutoDJProcessor::autoDJStateChanged,
209 this,
210 &DlgAutoDJ::autoDJStateChanged);
211 autoDJStateChanged(m_pAutoDJProcessor->getState());
212
213 updateSelectionInfo();
214 }
215
~DlgAutoDJ()216 DlgAutoDJ::~DlgAutoDJ() {
217 qDebug() << "~DlgAutoDJ()";
218
219 // Delete m_pTrackTableView before the table model. This is because the
220 // table view saves the header state using the model.
221 delete m_pTrackTableView;
222 }
223
setupActionButton(QPushButton * pButton,void (DlgAutoDJ::* pSlot)(bool),const QString & fallbackText)224 void DlgAutoDJ::setupActionButton(QPushButton* pButton,
225 void (DlgAutoDJ::*pSlot)(bool),
226 const QString& fallbackText) {
227 connect(pButton, &QPushButton::clicked, this, pSlot);
228 if (m_bShowButtonText) {
229 pButton->setText(fallbackText);
230 }
231 }
232
onShow()233 void DlgAutoDJ::onShow() {
234 m_pAutoDJTableModel->select();
235 }
236
onSearch(const QString & text)237 void DlgAutoDJ::onSearch(const QString& text) {
238 // Do not allow filtering the Auto DJ playlist, because
239 // Auto DJ will work from the filtered table
240 Q_UNUSED(text);
241 }
242
loadSelectedTrack()243 void DlgAutoDJ::loadSelectedTrack() {
244 m_pTrackTableView->loadSelectedTrack();
245 }
246
loadSelectedTrackToGroup(const QString & group,bool play)247 void DlgAutoDJ::loadSelectedTrackToGroup(const QString& group, bool play) {
248 m_pTrackTableView->loadSelectedTrackToGroup(group, play);
249 }
250
moveSelection(int delta)251 void DlgAutoDJ::moveSelection(int delta) {
252 m_pTrackTableView->moveSelection(delta);
253 }
254
shufflePlaylistButton(bool)255 void DlgAutoDJ::shufflePlaylistButton(bool) {
256 QModelIndexList indexList = m_pTrackTableView->selectionModel()->selectedRows();
257
258 // Activate regardless of button being checked
259 m_pAutoDJProcessor->shufflePlaylist(indexList);
260 }
261
skipNextButton(bool)262 void DlgAutoDJ::skipNextButton(bool) {
263 // Activate regardless of button being checked
264 m_pAutoDJProcessor->skipNext();
265 }
266
fadeNowButton(bool)267 void DlgAutoDJ::fadeNowButton(bool) {
268 // Activate regardless of button being checked
269 m_pAutoDJProcessor->fadeNow();
270 }
271
toggleAutoDJButton(bool enable)272 void DlgAutoDJ::toggleAutoDJButton(bool enable) {
273 AutoDJProcessor::AutoDJError error = m_pAutoDJProcessor->toggleAutoDJ(enable);
274 switch (error) {
275 case AutoDJProcessor::ADJ_BOTH_DECKS_PLAYING:
276 QMessageBox::warning(nullptr,
277 tr("Auto DJ"),
278 tr("One deck must be stopped to enable Auto DJ mode."),
279 QMessageBox::Ok);
280 // Make sure the button becomes unpushed.
281 pushButtonAutoDJ->setChecked(false);
282 break;
283 case AutoDJProcessor::ADJ_DECKS_3_4_PLAYING:
284 QMessageBox::warning(nullptr,
285 tr("Auto DJ"),
286 tr("Decks 3 and 4 must be stopped to enable Auto DJ mode."),
287 QMessageBox::Ok);
288 pushButtonAutoDJ->setChecked(false);
289 break;
290 case AutoDJProcessor::ADJ_OK:
291 default:
292 break;
293 }
294 }
295
transitionTimeChanged(int time)296 void DlgAutoDJ::transitionTimeChanged(int time) {
297 spinBoxTransition->setValue(time);
298 }
299
transitionSliderChanged(int value)300 void DlgAutoDJ::transitionSliderChanged(int value) {
301 m_pAutoDJProcessor->setTransitionTime(value);
302 }
303
autoDJStateChanged(AutoDJProcessor::AutoDJState state)304 void DlgAutoDJ::autoDJStateChanged(AutoDJProcessor::AutoDJState state) {
305 if (state == AutoDJProcessor::ADJ_DISABLED) {
306 pushButtonAutoDJ->setChecked(false);
307 pushButtonAutoDJ->setToolTip(m_enableBtnTooltip);
308 if (m_bShowButtonText) {
309 pushButtonAutoDJ->setText(tr("Enable"));
310 }
311 pushButtonFadeNow->setEnabled(false);
312 pushButtonSkipNext->setEnabled(false);
313 } else {
314 // No matter the mode, you can always disable once it is enabled.
315 pushButtonAutoDJ->setChecked(true);
316 pushButtonAutoDJ->setToolTip(m_disableBtnTooltip);
317 if (m_bShowButtonText) {
318 pushButtonAutoDJ->setText(tr("Disable"));
319 }
320
321 // If fading, you can't hit fade now.
322 if (state == AutoDJProcessor::ADJ_LEFT_FADING ||
323 state == AutoDJProcessor::ADJ_RIGHT_FADING ||
324 state == AutoDJProcessor::ADJ_ENABLE_P1LOADED) {
325 pushButtonFadeNow->setEnabled(false);
326 } else {
327 pushButtonFadeNow->setEnabled(true);
328 }
329
330 // You can always skip the next track if we are enabled.
331 pushButtonSkipNext->setEnabled(true);
332 }
333 }
334
slotTransitionModeChanged(int comboboxIndex)335 void DlgAutoDJ::slotTransitionModeChanged(int comboboxIndex) {
336 m_pAutoDJProcessor->setTransitionMode(static_cast<AutoDJProcessor::TransitionMode>(
337 fadeModeCombobox->itemData(comboboxIndex).toInt()));
338 }
339
slotRepeatPlaylistChanged(int checkState)340 void DlgAutoDJ::slotRepeatPlaylistChanged(int checkState) {
341 bool checked = static_cast<bool>(checkState);
342 m_pConfig->setValue(ConfigKey(kPreferenceGroupName, kRepeatPlaylistPreference),
343 checked);
344 }
345
updateSelectionInfo()346 void DlgAutoDJ::updateSelectionInfo() {
347 double duration = 0.0;
348
349 QModelIndexList indices = m_pTrackTableView->selectionModel()->selectedRows();
350
351 for (int i = 0; i < indices.size(); ++i) {
352 TrackPointer pTrack = m_pAutoDJTableModel->getTrack(indices.at(i));
353 if (pTrack) {
354 duration += pTrack->getDuration();
355 }
356 }
357
358 QString label;
359
360 if (!indices.isEmpty()) {
361 label.append(mixxx::DurationBase::formatTime(duration));
362 label.append(QString(" (%1)").arg(indices.size()));
363 labelSelectionInfo->setToolTip(tr("Displays the duration and number of selected tracks."));
364 labelSelectionInfo->setText(label);
365 labelSelectionInfo->setEnabled(true);
366 } else {
367 labelSelectionInfo->setText("");
368 labelSelectionInfo->setEnabled(false);
369 }
370 }
371
hasFocus() const372 bool DlgAutoDJ::hasFocus() const {
373 return m_pTrackTableView->hasFocus();
374 }
375