1 #include "library/librarycontrol.h"
2
3 #include <QApplication>
4 #include <QItemSelectionModel>
5 #include <QModelIndex>
6 #include <QModelIndexList>
7 #include <QtDebug>
8
9 #include "control/controlobject.h"
10 #include "control/controlpushbutton.h"
11 #include "library/library.h"
12 #include "library/libraryview.h"
13 #include "mixer/playermanager.h"
14 #include "moc_librarycontrol.cpp"
15 #include "widget/wlibrary.h"
16 #include "widget/wlibrarysidebar.h"
17 #include "widget/wsearchlineedit.h"
18 #include "widget/wtracktableview.h"
19
LoadToGroupController(LibraryControl * pParent,const QString & group)20 LoadToGroupController::LoadToGroupController(LibraryControl* pParent, const QString& group)
21 : QObject(pParent),
22 m_group(group) {
23 m_pLoadControl = std::make_unique<ControlPushButton>(ConfigKey(group, "LoadSelectedTrack"));
24 connect(m_pLoadControl.get(),
25 &ControlObject::valueChanged,
26 this,
27 &LoadToGroupController::slotLoadToGroup);
28
29 m_pLoadAndPlayControl = std::make_unique<ControlPushButton>(ConfigKey(group, "LoadSelectedTrackAndPlay"));
30 connect(m_pLoadAndPlayControl.get(),
31 &ControlObject::valueChanged,
32 this,
33 &LoadToGroupController::slotLoadToGroupAndPlay);
34
35 connect(this,
36 &LoadToGroupController::loadToGroup,
37 pParent,
38 &LibraryControl::slotLoadSelectedTrackToGroup);
39 }
40
41 LoadToGroupController::~LoadToGroupController() = default;
42
slotLoadToGroup(double v)43 void LoadToGroupController::slotLoadToGroup(double v) {
44 if (v > 0) {
45 emit loadToGroup(m_group, false);
46 }
47 }
48
slotLoadToGroupAndPlay(double v)49 void LoadToGroupController::slotLoadToGroupAndPlay(double v) {
50 if (v > 0) {
51 emit loadToGroup(m_group, true);
52 }
53 }
54
LibraryControl(Library * pLibrary)55 LibraryControl::LibraryControl(Library* pLibrary)
56 : QObject(pLibrary),
57 m_pLibrary(pLibrary),
58 m_pLibraryWidget(nullptr),
59 m_pSidebarWidget(nullptr),
60 m_pSearchbox(nullptr),
61 m_numDecks("[Master]", "num_decks", this),
62 m_numSamplers("[Master]", "num_samplers", this),
63 m_numPreviewDecks("[Master]", "num_preview_decks", this) {
64
65 slotNumDecksChanged(m_numDecks.get());
66 slotNumSamplersChanged(m_numSamplers.get());
67 slotNumPreviewDecksChanged(m_numPreviewDecks.get());
68 m_numDecks.connectValueChanged(this, &LibraryControl::slotNumDecksChanged);
69 m_numSamplers.connectValueChanged(this, &LibraryControl::slotNumSamplersChanged);
70 m_numPreviewDecks.connectValueChanged(this, &LibraryControl::slotNumPreviewDecksChanged);
71
72 // Controls to navigate vertically within currently focused widget (up/down buttons)
73 m_pMoveUp = std::make_unique<ControlPushButton>(ConfigKey("[Library]", "MoveUp"));
74 m_pMoveDown = std::make_unique<ControlPushButton>(ConfigKey("[Library]", "MoveDown"));
75 m_pMoveVertical = std::make_unique<ControlEncoder>(ConfigKey("[Library]", "MoveVertical"), false);
76 connect(m_pMoveUp.get(),
77 &ControlPushButton::valueChanged,
78 this,
79 &LibraryControl::slotMoveUp);
80 connect(m_pMoveDown.get(),
81 &ControlPushButton::valueChanged,
82 this,
83 &LibraryControl::slotMoveDown);
84 connect(m_pMoveVertical.get(),
85 &ControlEncoder::valueChanged,
86 this,
87 &LibraryControl::slotMoveVertical);
88
89 // Controls to navigate vertically within currently focused widget (up/down buttons)
90 m_pScrollUp = std::make_unique<ControlPushButton>(ConfigKey("[Library]", "ScrollUp"));
91 m_pScrollDown = std::make_unique<ControlPushButton>(ConfigKey("[Library]", "ScrollDown"));
92 m_pScrollVertical = std::make_unique<ControlEncoder>(ConfigKey("[Library]", "ScrollVertical"), false);
93 connect(m_pScrollUp.get(),
94 &ControlPushButton::valueChanged,
95 this,
96 &LibraryControl::slotScrollUp);
97 connect(m_pScrollDown.get(),
98 &ControlPushButton::valueChanged,
99 this,
100 &LibraryControl::slotScrollDown);
101 connect(m_pScrollVertical.get(),
102 &ControlEncoder::valueChanged,
103 this,
104 &LibraryControl::slotScrollVertical);
105
106 // Controls to navigate horizontally within currently selected item (left/right buttons)
107 m_pMoveLeft = std::make_unique<ControlPushButton>(ConfigKey("[Library]", "MoveLeft"));
108 m_pMoveRight = std::make_unique<ControlPushButton>(ConfigKey("[Library]", "MoveRight"));
109 m_pMoveHorizontal = std::make_unique<ControlEncoder>(ConfigKey("[Library]", "MoveHorizontal"), false);
110 connect(m_pMoveLeft.get(),
111 &ControlPushButton::valueChanged,
112 this,
113 &LibraryControl::slotMoveLeft);
114 connect(m_pMoveRight.get(),
115 &ControlPushButton::valueChanged,
116 this,
117 &LibraryControl::slotMoveRight);
118 connect(m_pMoveHorizontal.get(),
119 &ControlEncoder::valueChanged,
120 this,
121 &LibraryControl::slotMoveHorizontal);
122
123 // Control to navigate between widgets (tab/shit+tab button)
124 m_pMoveFocusForward = std::make_unique<ControlPushButton>(ConfigKey("[Library]", "MoveFocusForward"));
125 m_pMoveFocusBackward = std::make_unique<ControlPushButton>(ConfigKey("[Library]", "MoveFocusBackward"));
126 m_pMoveFocus = std::make_unique<ControlEncoder>(ConfigKey("[Library]", "MoveFocus"), false);
127 connect(m_pMoveFocusForward.get(),
128 &ControlPushButton::valueChanged,
129 this,
130 &LibraryControl::slotMoveFocusForward);
131 connect(m_pMoveFocusBackward.get(),
132 &ControlPushButton::valueChanged,
133 this,
134 &LibraryControl::slotMoveFocusBackward);
135 connect(m_pMoveFocus.get(),
136 &ControlEncoder::valueChanged,
137 this,
138 &LibraryControl::slotMoveFocus);
139
140 // Control to "goto" the currently selected item in focused widget (context dependent)
141 m_pGoToItem = std::make_unique<ControlPushButton>(ConfigKey("[Library]", "GoToItem"));
142 connect(m_pGoToItem.get(),
143 &ControlPushButton::valueChanged,
144 this,
145 &LibraryControl::slotGoToItem);
146
147 // Auto DJ controls
148 m_pAutoDjAddTop = std::make_unique<ControlPushButton>(ConfigKey("[Library]","AutoDjAddTop"));
149 connect(m_pAutoDjAddTop.get(),
150 &ControlPushButton::valueChanged,
151 this,
152 &LibraryControl::slotAutoDjAddTop);
153
154 m_pAutoDjAddBottom = std::make_unique<ControlPushButton>(ConfigKey("[Library]","AutoDjAddBottom"));
155 connect(m_pAutoDjAddBottom.get(),
156 &ControlPushButton::valueChanged,
157 this,
158 &LibraryControl::slotAutoDjAddBottom);
159
160 m_pAutoDjAddReplace = std::make_unique<ControlPushButton>(
161 ConfigKey("[Library]", "AutoDjAddReplace"));
162 connect(m_pAutoDjAddReplace.get(),
163 &ControlPushButton::valueChanged,
164 this,
165 &LibraryControl::slotAutoDjAddReplace);
166
167 // Sort controls
168 m_pSortColumn = std::make_unique<ControlEncoder>(ConfigKey("[Library]", "sort_column"));
169 m_pSortOrder = std::make_unique<ControlPushButton>(ConfigKey("[Library]", "sort_order"));
170 m_pSortOrder->setButtonMode(ControlPushButton::TOGGLE);
171 m_pSortColumnToggle = std::make_unique<ControlEncoder>(ConfigKey("[Library]", "sort_column_toggle"), false);
172 connect(m_pSortColumn.get(),
173 &ControlEncoder::valueChanged,
174 this,
175 &LibraryControl::slotSortColumn);
176 connect(m_pSortColumnToggle.get(),
177 &ControlEncoder::valueChanged,
178 this,
179 &LibraryControl::slotSortColumnToggle);
180
181 // Font sizes
182 m_pFontSizeKnob = std::make_unique<ControlObject>(
183 ConfigKey("[Library]", "font_size_knob"), false);
184 connect(m_pFontSizeKnob.get(),
185 &ControlObject::valueChanged,
186 this,
187 &LibraryControl::slotFontSize);
188
189 m_pFontSizeDecrement = std::make_unique<ControlPushButton>(
190 ConfigKey("[Library]", "font_size_decrement"));
191 connect(m_pFontSizeDecrement.get(),
192 &ControlPushButton::valueChanged,
193 this,
194 &LibraryControl::slotDecrementFontSize);
195
196 m_pFontSizeIncrement = std::make_unique<ControlPushButton>(
197 ConfigKey("[Library]", "font_size_increment"));
198 connect(m_pFontSizeIncrement.get(),
199 &ControlPushButton::valueChanged,
200 this,
201 &LibraryControl::slotIncrementFontSize);
202
203 // Track Color controls
204 m_pTrackColorPrev = std::make_unique<ControlPushButton>(ConfigKey("[Library]", "track_color_prev"));
205 m_pTrackColorNext = std::make_unique<ControlPushButton>(ConfigKey("[Library]", "track_color_next"));
206 connect(m_pTrackColorPrev.get(),
207 &ControlPushButton::valueChanged,
208 this,
209 &LibraryControl::slotTrackColorPrev);
210 connect(m_pTrackColorNext.get(),
211 &ControlPushButton::valueChanged,
212 this,
213 &LibraryControl::slotTrackColorNext);
214
215 /// Deprecated controls
216 m_pSelectNextTrack = std::make_unique<ControlPushButton>(ConfigKey("[Playlist]", "SelectNextTrack"));
217 connect(m_pSelectNextTrack.get(),
218 &ControlPushButton::valueChanged,
219 this,
220 &LibraryControl::slotSelectNextTrack);
221
222 m_pSelectPrevTrack = std::make_unique<ControlPushButton>(ConfigKey("[Playlist]", "SelectPrevTrack"));
223 connect(m_pSelectPrevTrack.get(),
224 &ControlPushButton::valueChanged,
225 this,
226 &LibraryControl::slotSelectPrevTrack);
227
228 // Ignoring no-ops is important since this is for +/- tickers.
229 m_pSelectTrack = std::make_unique<ControlObject>(ConfigKey("[Playlist]","SelectTrackKnob"), false);
230 connect(m_pSelectTrack.get(),
231 &ControlObject::valueChanged,
232 this,
233 &LibraryControl::slotSelectTrack);
234
235 m_pSelectNextSidebarItem = std::make_unique<ControlPushButton>(ConfigKey("[Playlist]", "SelectNextPlaylist"));
236 connect(m_pSelectNextSidebarItem.get(),
237 &ControlPushButton::valueChanged,
238 this,
239 &LibraryControl::slotSelectNextSidebarItem);
240
241 m_pSelectPrevSidebarItem = std::make_unique<ControlPushButton>(ConfigKey("[Playlist]", "SelectPrevPlaylist"));
242 connect(m_pSelectPrevSidebarItem.get(),
243 &ControlPushButton::valueChanged,
244 this,
245 &LibraryControl::slotSelectPrevSidebarItem);
246
247 // Ignoring no-ops is important since this is for +/- tickers.
248 m_pSelectSidebarItem = std::make_unique<ControlObject>(ConfigKey("[Playlist]", "SelectPlaylist"), false);
249 connect(m_pSelectSidebarItem.get(),
250 &ControlObject::valueChanged,
251 this,
252 &LibraryControl::slotSelectSidebarItem);
253
254 m_pToggleSidebarItem = std::make_unique<ControlPushButton>(ConfigKey("[Playlist]", "ToggleSelectedSidebarItem"));
255 connect(m_pToggleSidebarItem.get(),
256 &ControlPushButton::valueChanged,
257 this,
258 &LibraryControl::slotToggleSelectedSidebarItem);
259
260 m_pLoadSelectedIntoFirstStopped = std::make_unique<ControlPushButton>(ConfigKey("[Playlist]","LoadSelectedIntoFirstStopped"));
261 connect(m_pLoadSelectedIntoFirstStopped.get(),
262 &ControlPushButton::valueChanged,
263 this,
264 &LibraryControl::slotLoadSelectedIntoFirstStopped);
265
266 ControlDoublePrivate::insertAlias(ConfigKey("[Playlist]", "AutoDjAddTop"), ConfigKey("[Library]", "AutoDjAddTop"));
267 ControlDoublePrivate::insertAlias(ConfigKey("[Playlist]", "AutoDjAddBottom"), ConfigKey("[Library]", "AutoDjAddBottom"));
268 }
269
270 LibraryControl::~LibraryControl() = default;
271
maybeCreateGroupController(const QString & group)272 void LibraryControl::maybeCreateGroupController(const QString& group) {
273 if (m_loadToGroupControllers.find(group) == m_loadToGroupControllers.end()) {
274 m_loadToGroupControllers.emplace(group, std::make_unique<LoadToGroupController>(this, group));
275 }
276 }
277
slotNumDecksChanged(double v)278 void LibraryControl::slotNumDecksChanged(double v) {
279 int iNumDecks = static_cast<int>(v);
280
281 if (iNumDecks < 0) {
282 return;
283 }
284
285 for (int i = 0; i < iNumDecks; ++i) {
286 maybeCreateGroupController(PlayerManager::groupForDeck(i));
287 }
288 }
289
slotNumSamplersChanged(double v)290 void LibraryControl::slotNumSamplersChanged(double v) {
291 int iNumSamplers = static_cast<int>(v);
292
293 if (iNumSamplers < 0) {
294 return;
295 }
296
297 for (int i = 0; i < iNumSamplers; ++i) {
298 maybeCreateGroupController(PlayerManager::groupForSampler(i));
299 }
300 }
301
302
slotNumPreviewDecksChanged(double v)303 void LibraryControl::slotNumPreviewDecksChanged(double v) {
304 int iNumPreviewDecks = static_cast<int>(v);
305
306 if (iNumPreviewDecks < 0) {
307 return;
308 }
309
310 for (int i = 0; i < iNumPreviewDecks; ++i) {
311 maybeCreateGroupController(PlayerManager::groupForPreviewDeck(i));
312 }
313 }
314
bindSidebarWidget(WLibrarySidebar * pSidebarWidget)315 void LibraryControl::bindSidebarWidget(WLibrarySidebar* pSidebarWidget) {
316 if (m_pSidebarWidget) {
317 disconnect(m_pSidebarWidget, nullptr, this, nullptr);
318 }
319 m_pSidebarWidget = pSidebarWidget;
320 connect(m_pSidebarWidget,
321 &WLibrarySidebar::destroyed,
322 this,
323 &LibraryControl::sidebarWidgetDeleted);
324 }
325
bindLibraryWidget(WLibrary * pLibraryWidget,KeyboardEventFilter * pKeyboard)326 void LibraryControl::bindLibraryWidget(WLibrary* pLibraryWidget, KeyboardEventFilter* pKeyboard) {
327 Q_UNUSED(pKeyboard);
328 if (m_pLibraryWidget) {
329 disconnect(m_pLibraryWidget, nullptr, this, nullptr);
330 }
331 m_pLibraryWidget = pLibraryWidget;
332 connect(m_pLibraryWidget,
333 &WLibrary::destroyed,
334 this,
335 &LibraryControl::libraryWidgetDeleted);
336 }
337
bindSearchboxWidget(WSearchLineEdit * pSearchbox)338 void LibraryControl::bindSearchboxWidget(WSearchLineEdit* pSearchbox) {
339 if (m_pSearchbox) {
340 disconnect(m_pSearchbox, nullptr, this, nullptr);
341 }
342 m_pSearchbox = pSearchbox;
343 connect(this,
344 &LibraryControl::clearSearchIfClearButtonHasFocus,
345 m_pSearchbox,
346 &WSearchLineEdit::slotClearSearchIfClearButtonHasFocus);
347 connect(m_pSearchbox,
348 &WSearchLineEdit::destroyed,
349 this,
350 &LibraryControl::searchboxWidgetDeleted);
351 }
352
353
354
libraryWidgetDeleted()355 void LibraryControl::libraryWidgetDeleted() {
356 m_pLibraryWidget = nullptr;
357 }
358
sidebarWidgetDeleted()359 void LibraryControl::sidebarWidgetDeleted() {
360 m_pSidebarWidget = nullptr;
361 }
362
searchboxWidgetDeleted()363 void LibraryControl::searchboxWidgetDeleted() {
364 m_pSearchbox = nullptr;
365 }
366
slotLoadSelectedTrackToGroup(const QString & group,bool play)367 void LibraryControl::slotLoadSelectedTrackToGroup(const QString& group, bool play) {
368 if (!m_pLibraryWidget) {
369 return;
370 }
371
372 LibraryView* pActiveView = m_pLibraryWidget->getActiveView();
373 if (!pActiveView) {
374 return;
375 }
376 pActiveView->loadSelectedTrackToGroup(group, play);
377 }
378
slotLoadSelectedIntoFirstStopped(double v)379 void LibraryControl::slotLoadSelectedIntoFirstStopped(double v) {
380 if (!m_pLibraryWidget) {
381 return;
382 }
383
384 if (v > 0) {
385 LibraryView* pActiveView = m_pLibraryWidget->getActiveView();
386 if (!pActiveView) {
387 return;
388 }
389 pActiveView->loadSelectedTrack();
390 }
391 }
392
slotAutoDjAddTop(double v)393 void LibraryControl::slotAutoDjAddTop(double v) {
394 if (!m_pLibraryWidget) {
395 return;
396 }
397
398 if (v > 0) {
399 LibraryView* pActiveView = m_pLibraryWidget->getActiveView();
400 if (!pActiveView) {
401 return;
402 }
403 pActiveView->slotAddToAutoDJTop();
404 }
405 }
406
slotAutoDjAddBottom(double v)407 void LibraryControl::slotAutoDjAddBottom(double v) {
408 if (!m_pLibraryWidget) {
409 return;
410 }
411 if (v > 0) {
412 LibraryView* pActiveView = m_pLibraryWidget->getActiveView();
413 if (!pActiveView) {
414 return;
415 }
416 pActiveView->slotAddToAutoDJBottom();
417 }
418 }
419
slotAutoDjAddReplace(double v)420 void LibraryControl::slotAutoDjAddReplace(double v) {
421 if (!m_pLibraryWidget) {
422 return;
423 }
424 if (v > 0) {
425 LibraryView* pActiveView = m_pLibraryWidget->getActiveView();
426 if (!pActiveView) {
427 return;
428 }
429 pActiveView->slotAddToAutoDJReplace();
430 }
431 }
432
slotSelectNextTrack(double v)433 void LibraryControl::slotSelectNextTrack(double v) {
434 if (v > 0) {
435 slotSelectTrack(1);
436 }
437 }
438
slotSelectPrevTrack(double v)439 void LibraryControl::slotSelectPrevTrack(double v) {
440 if (v > 0) {
441 slotSelectTrack(-1);
442 }
443 }
444
slotSelectTrack(double v)445 void LibraryControl::slotSelectTrack(double v) {
446 if (!m_pLibraryWidget) {
447 return;
448 }
449
450 int i = (int)v;
451
452 LibraryView* pActiveView = m_pLibraryWidget->getActiveView();
453 if (!pActiveView) {
454 return;
455 }
456 pActiveView->moveSelection(i);
457 }
458
slotMoveUp(double v)459 void LibraryControl::slotMoveUp(double v) {
460 if (v > 0) {
461 emitKeyEvent(QKeyEvent{QEvent::KeyPress, Qt::Key_Up, Qt::NoModifier});
462 }
463 }
464
slotMoveDown(double v)465 void LibraryControl::slotMoveDown(double v) {
466 if (v > 0) {
467 emitKeyEvent(QKeyEvent{QEvent::KeyPress, Qt::Key_Down, Qt::NoModifier});
468 }
469 }
470
slotMoveVertical(double v)471 void LibraryControl::slotMoveVertical(double v) {
472 const auto key = (v < 0) ? Qt::Key_Up: Qt::Key_Down;
473 const auto times = static_cast<unsigned short>(std::abs(v));
474 emitKeyEvent(QKeyEvent{QEvent::KeyPress, key, Qt::NoModifier, QString(), false, times});
475 }
476
slotScrollUp(double v)477 void LibraryControl::slotScrollUp(double v) {
478 if (v > 0) {
479 emitKeyEvent(QKeyEvent{QEvent::KeyPress, Qt::Key_PageUp, Qt::NoModifier});
480 }
481 }
482
slotScrollDown(double v)483 void LibraryControl::slotScrollDown(double v) {
484 if (v > 0) {
485 emitKeyEvent(QKeyEvent{QEvent::KeyPress, Qt::Key_PageDown, Qt::NoModifier});
486 }
487 }
488
slotScrollVertical(double v)489 void LibraryControl::slotScrollVertical(double v) {
490 const auto key = (v < 0) ? Qt::Key_PageUp: Qt::Key_PageDown;
491 const auto times = static_cast<unsigned short>(std::abs(v));
492 emitKeyEvent(QKeyEvent{QEvent::KeyPress, key, Qt::NoModifier, QString(), false, times});
493 }
494
slotMoveLeft(double v)495 void LibraryControl::slotMoveLeft(double v) {
496 if (v > 0) {
497 emitKeyEvent(QKeyEvent{QEvent::KeyPress, Qt::Key_Left, Qt::NoModifier});
498 }
499 }
500
slotMoveRight(double v)501 void LibraryControl::slotMoveRight(double v) {
502 if (v > 0) {
503 emitKeyEvent(QKeyEvent{QEvent::KeyPress, Qt::Key_Right, Qt::NoModifier});
504 }
505 }
506
slotMoveHorizontal(double v)507 void LibraryControl::slotMoveHorizontal(double v) {
508 const auto key = (v < 0) ? Qt::Key_Left: Qt::Key_Right;
509 const auto times = static_cast<unsigned short>(std::abs(v));
510 emitKeyEvent(QKeyEvent{QEvent::KeyPress, key, Qt::NoModifier, QString(), false, times});
511 }
512
slotMoveFocusForward(double v)513 void LibraryControl::slotMoveFocusForward(double v) {
514 if (v > 0) {
515 emitKeyEvent(QKeyEvent{QEvent::KeyPress, Qt::Key_Tab, Qt::NoModifier});
516 }
517 }
518
slotMoveFocusBackward(double v)519 void LibraryControl::slotMoveFocusBackward(double v) {
520 if (v > 0) {
521 emitKeyEvent(QKeyEvent{QEvent::KeyPress, Qt::Key_Tab, Qt::ShiftModifier});
522 }
523 }
524
slotMoveFocus(double v)525 void LibraryControl::slotMoveFocus(double v) {
526 const auto shift = (v < 0) ? Qt::ShiftModifier: Qt::NoModifier;
527 const auto times = static_cast<unsigned short>(std::abs(v));
528 emitKeyEvent(QKeyEvent{QEvent::KeyPress, Qt::Key_Tab, shift, QString(), false, times});
529 }
530
emitKeyEvent(QKeyEvent && event)531 void LibraryControl::emitKeyEvent(QKeyEvent&& event) {
532 // Ensure there's a valid library widget that can receive keyboard focus.
533 // QApplication::focusWidget() is not sufficient here because it
534 // would return any focused widget like WOverview, WWaveform, QSpinBox
535 VERIFY_OR_DEBUG_ASSERT(m_pSidebarWidget) {
536 return;
537 }
538 VERIFY_OR_DEBUG_ASSERT(m_pLibraryWidget) {
539 return;
540 }
541 if (!QApplication::focusWindow()) {
542 qDebug() << "Mixxx window is not focused, don't send key events";
543 return;
544 }
545
546 bool keyIsTab = event.key() == static_cast<int>(Qt::Key_Tab);
547
548 // If the main window has focus, any widget can receive Tab.
549 // Other keys should be sent to library widgets only to not
550 // accidentally alter spinboxes etc.
551 if (!keyIsTab && !m_pSidebarWidget->hasFocus()
552 && !m_pLibraryWidget->getActiveView()->hasFocus()) {
553 setLibraryFocus();
554 }
555 if (keyIsTab && !QApplication::focusWidget()){
556 setLibraryFocus();
557 }
558
559 // Send the event pointer to the currently focused widget
560 auto* focusWidget = QApplication::focusWidget();
561 if (focusWidget) {
562 for (auto i = 0; i < event.count(); ++i) {
563 QApplication::sendEvent(focusWidget, &event);
564 }
565 }
566 }
567
setLibraryFocus()568 void LibraryControl::setLibraryFocus() {
569 // TODO: Set the focus of the library panel directly instead of sending tab from sidebar
570 VERIFY_OR_DEBUG_ASSERT(m_pSidebarWidget) {
571 return;
572 }
573 // Try to focus the sidebar.
574 m_pSidebarWidget->setFocus();
575
576 // This may have failed, for example when a Cover window still has focus,
577 // so make sure the sidebar is focused or we'll crash.
578 if (!m_pSidebarWidget->hasFocus()) {
579 return;
580 }
581 // Send Tab to move focus to the Tracks table.
582 // Obviously only works as desired if the skin widgets are arranged
583 // accordingly.
584 QKeyEvent event(QEvent::KeyPress, Qt::Key_Tab, Qt::NoModifier);
585 QApplication::sendEvent(m_pSidebarWidget, &event);
586 }
587
slotSelectSidebarItem(double v)588 void LibraryControl::slotSelectSidebarItem(double v) {
589 VERIFY_OR_DEBUG_ASSERT(m_pSidebarWidget) {
590 return;
591 }
592 if (v > 0) {
593 QApplication::postEvent(m_pSidebarWidget, new QKeyEvent(
594 QEvent::KeyPress,
595 (int)Qt::Key_Down, Qt::NoModifier, QString(), true));
596 QApplication::postEvent(m_pSidebarWidget, new QKeyEvent(
597 QEvent::KeyRelease,
598 (int)Qt::Key_Down, Qt::NoModifier, QString(), true));
599 } else if (v < 0) {
600 QApplication::postEvent(m_pSidebarWidget, new QKeyEvent(
601 QEvent::KeyPress,
602 (int)Qt::Key_Up, Qt::NoModifier, QString(), true));
603 QApplication::postEvent(m_pSidebarWidget, new QKeyEvent(
604 QEvent::KeyRelease,
605 (int)Qt::Key_Up, Qt::NoModifier, QString(), true));
606 }
607 }
608
slotSelectNextSidebarItem(double v)609 void LibraryControl::slotSelectNextSidebarItem(double v) {
610 if (v > 0) {
611 slotSelectSidebarItem(1);
612 }
613 }
614
slotSelectPrevSidebarItem(double v)615 void LibraryControl::slotSelectPrevSidebarItem(double v) {
616 if (v > 0) {
617 slotSelectSidebarItem(-1);
618 }
619 }
620
slotToggleSelectedSidebarItem(double v)621 void LibraryControl::slotToggleSelectedSidebarItem(double v) {
622 if (m_pSidebarWidget && v > 0) {
623 m_pSidebarWidget->toggleSelectedItem();
624 }
625 }
626
slotGoToItem(double v)627 void LibraryControl::slotGoToItem(double v) {
628 if (v <= 0) {
629 return;
630 }
631 VERIFY_OR_DEBUG_ASSERT(m_pSidebarWidget) {
632 return;
633 }
634 VERIFY_OR_DEBUG_ASSERT(m_pLibraryWidget) {
635 return;
636 }
637 VERIFY_OR_DEBUG_ASSERT(m_pSearchbox) {
638 return;
639 }
640
641 // Focus the library if this is a leaf node in the tree
642 if (m_pSidebarWidget->hasFocus()) {
643 // Note that Tracks and AutoDJ always return 'false':
644 // expanding those root items via controllers is considered dispensable
645 // because the subfeatures' actions can't be accessed by controllers anyway.
646 if (m_pSidebarWidget->isLeafNodeSelected()) {
647 setLibraryFocus();
648 return;
649 } else {
650 // Otherwise toggle the sidebar item expanded state
651 slotToggleSelectedSidebarItem(v);
652 }
653 }
654
655 // Load current track if a LibraryView object has focus
656 LibraryView* pActiveView = m_pLibraryWidget->getActiveView();
657 if (pActiveView && pActiveView->hasFocus()) {
658 pActiveView->loadSelectedTrack();
659 return;
660 }
661
662 // Clear the search if the searchbox has focus
663 emit clearSearchIfClearButtonHasFocus();
664
665 // If the focused window is a dialog, press Enter
666 auto* focusWindow = QApplication::focusWindow();
667 if (focusWindow && (focusWindow->type() & (Qt::Dialog | Qt::Popup))) {
668 QKeyEvent event(QEvent::KeyPress, Qt::Key_Enter, Qt::NoModifier);
669 QApplication::sendEvent(focusWindow, &event);
670 }
671
672 // TODO(xxx) instead of remote control the widgets individual, we should
673 // translate this into Alt+Return and handle it at each library widget
674 // individual https://bugs.launchpad.net/mixxx/+bug/1758618
675 //emitKeyEvent(QKeyEvent{QEvent::KeyPress, Qt::Key_Return, Qt::AltModifier});
676 }
677
slotSortColumn(double v)678 void LibraryControl::slotSortColumn(double v) {
679 m_pSortColumnToggle->set(v);
680 }
681
slotSortColumnToggle(double v)682 void LibraryControl::slotSortColumnToggle(double v) {
683 int sortColumnId = static_cast<int>(v);
684 if (sortColumnId == static_cast<int>(TrackModel::SortColumnId::CurrentIndex)) {
685 if (!m_pLibraryWidget) {
686 return;
687 }
688 // Get the ID of the column with the cursor
689 sortColumnId =
690 static_cast<int>(m_pLibraryWidget->getActiveView()
691 ->getColumnIdFromCurrentIndex());
692 }
693
694 if (static_cast<int>(m_pSortColumn->get()) == sortColumnId) {
695 m_pSortOrder->set((m_pSortOrder->get() == 0) ? 1.0 : 0.0);
696 } else {
697 m_pSortColumn->set(sortColumnId);
698 m_pSortOrder->set(0.0);
699 }
700 }
701
slotFontSize(double v)702 void LibraryControl::slotFontSize(double v) {
703 if (v == 0.0) {
704 return;
705 }
706 QFont font = m_pLibrary->getTrackTableFont();
707 font.setPointSizeF(font.pointSizeF() + v);
708 m_pLibrary->setFont(font);
709 }
710
slotIncrementFontSize(double v)711 void LibraryControl::slotIncrementFontSize(double v) {
712 if (v > 0.0) {
713 slotFontSize(1);
714 }
715 }
716
slotDecrementFontSize(double v)717 void LibraryControl::slotDecrementFontSize(double v) {
718 if (v > 0.0) {
719 slotFontSize(-1);
720 }
721 }
722
slotTrackColorPrev(double v)723 void LibraryControl::slotTrackColorPrev(double v) {
724 if (!m_pLibraryWidget) {
725 return;
726 }
727
728 if (v > 0) {
729 LibraryView* pActiveView = m_pLibraryWidget->getActiveView();
730 if (!pActiveView) {
731 return;
732 }
733 pActiveView->assignPreviousTrackColor();
734 }
735 }
736
slotTrackColorNext(double v)737 void LibraryControl::slotTrackColorNext(double v) {
738 if (!m_pLibraryWidget) {
739 return;
740 }
741
742 if (v > 0) {
743 LibraryView* pActiveView = m_pLibraryWidget->getActiveView();
744 if (!pActiveView) {
745 return;
746 }
747 pActiveView->assignNextTrackColor();
748 }
749 }
750