MythTV master
playlisteditorview.cpp
Go to the documentation of this file.
1// C++
2#include <cstdlib>
3#include <iostream>
4
5// qt
6#include <QKeyEvent>
7
8// MythTV
19
20// mythmusic
21#include "mainvisual.h"
22#include "musiccommon.h"
23#include "musicdata.h"
24#include "playlistcontainer.h"
25#include "playlisteditorview.h"
26#include "smartplaylist.h"
27
29 const QString &name, const QString &action,
31 bool showArrow)
32 : MythGenericTree(name),
33 m_action(action),
34 m_check(check),
35 m_showArrow(showArrow)
36{
37 if (!action.isEmpty())
38 setSelectable(true);
39
40 if (parent)
41 {
42 parent->addNode(this);
43 parent->setDrawArrow(true);
44 }
45}
46
48{
49 m_showArrow = flag;
50 if (m_buttonItem)
51 m_buttonItem->setDrawArrow(true);
52}
53
55{
56 m_check = state;
57 if (m_buttonItem)
58 {
60 m_buttonItem->setChecked(m_check);
61 }
62}
63
65{
66 auto *item = new MusicButtonItem(list, GetText());
67 item->SetData(QVariant::fromValue((MythGenericTree*) this));
68
69 if (visibleChildCount() > 0)
70 item->setDrawArrow(true);
71
72 if (m_showArrow)
73 item->setDrawArrow(true);
74
75 item->setCheckable(m_check != MythUIButtonListItem::CantCheck);
76 item->setChecked(m_check);
77
78 m_buttonItem = item;
79
80 return item;
81}
82
84
85#define LOC QString("PlaylistEditorView: ")
86#define LOC_WARN QString("PlaylistEditorView, Warning: ")
87#define LOC_ERR QString("PlaylistEditorView, Error: ")
88
90 const QString &layout, bool restorePosition)
91 :MusicCommon(parent, parentScreen, "playlisteditorview"),
92 m_layout(layout), m_restorePosition(restorePosition)
93{
95 gCoreContext->SaveSetting("MusicPlaylistEditorView", layout);
96}
97
99{
101
103
104 for (int x = 0; x < m_deleteList.count(); x++)
105 delete m_deleteList.at(x);
106 m_deleteList.clear();
107
108 delete m_rootNode;
109}
110
112{
113 bool err = false;
114
115 QString windowName;
116
117 if (m_layout == "gallery")
118 {
119 windowName = "playlisteditorview_gallery";
121 }
122 else
123 {
124 windowName = "playlisteditorview_tree";
126 }
127
128 // Load the theme for this screen
129 err = LoadWindowFromXML("music-ui.xml", windowName, this);
130
131 if (!err)
132 {
133 gPlayer->removeListener(this);
134 return false;
135 }
136
137 // find common widgets available on any view
138 err = CreateCommon();
139
140 // find widgets specific to this view
141 UIUtilE::Assign(this, m_playlistTree , "playlist_tree", &err);
142 UIUtilW::Assign(this, m_breadcrumbsText, "breadcrumbs", &err);
143 UIUtilW::Assign(this, m_positionText, "position", &err);
144
145 if (err)
146 {
147 LOG(VB_GENERAL, LOG_ERR, QString("Cannot load screen '%1'").arg(windowName));
148 gPlayer->removeListener(this);
149 return false;
150 }
151
153
155
157
158 QStringList route = gCoreContext->GetSetting("MusicTreeLastActive", "").split("\n");
159 restoreTreePosition(route);
160
165
169
171
172 return true;
173}
174
176{
177 if ((event->type() == MusicPlayerEvent::kMetadataChangedEvent) ||
179 { // NOLINT(bugprone-branch-clone)
180 // TODO: this could be more efficient
181 reloadTree();
182 }
183 else if ((event->type() == MusicPlayerEvent::kTrackRemovedEvent) ||
184 (event->type() == MusicPlayerEvent::kTrackAddedEvent) ||
186 {
188 }
189 else if (event->type() == MusicPlayerEvent::kPlaylistChangedEvent)
190 {
191 //TODO should just update the relevent playlist here
192 reloadTree();
193 }
194 else if (event->type() == MusicPlayerEvent::kCDChangedEvent)
195 {
196 //TODO should just update the cd node
197 reloadTree();
198 }
199 else if (event->type() == MythEvent::kMythEventMessage)
200 {
201 auto *me = dynamic_cast<MythEvent*>(event);
202 if (!me)
203 return;
204
205 if (me->Message().startsWith("MUSIC_RESYNC_FINISHED"))
206 {
207 QStringList list = me->Message().simplified().split(' ');
208 if (list.size() == 4)
209 {
210 int added = list[1].toInt();
211 int removed = list[2].toInt();
212 int changed = list[3].toInt();
213
214 // if something changed reload the tree
215 if (added || removed || changed)
216 reloadTree();
217 }
218 }
219 }
220 else if (event->type() == DialogCompletionEvent::kEventType)
221 {
222 auto *dce = dynamic_cast<DialogCompletionEvent*>(event);
223
224 // make sure the user didn't ESCAPE out of the menu
225 if ((dce == nullptr) || (dce->GetResult() < 0))
226 return;
227
228 QString resultid = dce->GetId();
229 QString resulttext = dce->GetResultText();
230
231 if (resultid == "smartplaylistmenu")
232 {
234 return;
235
237 if (!node)
238 return;
239
240 auto *mnode = dynamic_cast<MusicGenericTree*>(node);
241 if (!mnode)
242 return;
243
244 if (resulttext == tr("New Smart Playlist"))
245 {
246 QString category;
247 if (mnode->getAction() == "smartplaylistcategory")
248 category = mnode->GetText();
249
251 auto* editor = new SmartPlaylistEditor(mainStack);
252
253 if (!editor->Create())
254 {
255 delete editor;
256 return;
257 }
258
259 editor->newSmartPlaylist(category);
260
263
264 mainStack->AddScreen(editor);
265 }
266 else if (resulttext == tr("Remove Smart Playlist"))
267 {
268 QString category = mnode->getParent()->GetText();
269 QString name = mnode->GetText();
270
271 ShowOkPopup(tr("Are you sure you want to delete this Smart Playlist?\n"
272 "Category: %1 - Name: %2").arg(category, name),
274 }
275 else if (resulttext == tr("Edit Smart Playlist"))
276 {
277 QString category = mnode->getParent()->GetText();
278 QString name = mnode->GetText();
279
281 auto* editor = new SmartPlaylistEditor(mainStack);
282
283 if (!editor->Create())
284 {
285 delete editor;
286 return;
287 }
288
289 editor->editSmartPlaylist(category, name);
290
293
294 mainStack->AddScreen(editor);
295 }
296 else if (resulttext == tr("Replace Tracks"))
297 {
300 }
301 else if (resulttext == tr("Add Tracks"))
302 {
305 }
306 else if (resulttext == tr("Play Now"))
307 { // cancel shuffles and repeats to play now
310 updateShuffleMode(true);
314 }
315 }
316 else if (resultid == "treeplaylistmenu")
317 {
319 return;
320
322 if (!node)
323 return;
324
325 auto *mnode = dynamic_cast<MusicGenericTree*>(node);
326 if (!mnode)
327 return;
328
329 if (resulttext == tr("Remove Playlist"))
330 {
331 QString name = mnode->GetText();
332
333 ShowOkPopup(tr("Are you sure you want to delete this Playlist?\n"
334 "Name: %1").arg(name),
336 }
337 else if (resulttext == tr("Replace Tracks"))
338 {
341 }
342 else if (resulttext == tr("Add Tracks"))
343 {
346 }
347 else if (resulttext == tr("Play Now"))
348 { // cancel shuffles and repeats to play now
351 updateShuffleMode(true);
355 }
356 }
357 }
358
360}
361
363{
364 // if there is a pending jump point pass the key press to the default handler
365 if (GetMythMainWindow()->IsExitingToMain())
366 {
367 // Just call the parent handler. It will properly clean up.
368 return MusicCommon::keyPressEvent(event);
369 }
370
372 return true;
373
374 QStringList actions;
375 bool handled = GetMythMainWindow()->TranslateKeyPress("Music", event, actions);
376
377 for (int i = 0; i < actions.size() && !handled; i++)
378 {
379 const QString& action = actions[i];
380 handled = true;
381
382 if ((action == "EDIT" || action == "INFO") && GetFocusWidget() == m_playlistTree)
383 {
384 handled = false;
385
387 if (node)
388 {
389 auto *mnode = dynamic_cast<MusicGenericTree*>(node);
390 if (mnode)
391 {
392 if (mnode->getAction() == "smartplaylist" && action == "EDIT")
393 {
394 QString category = mnode->getParent()->GetText();
395 QString name = mnode->GetText();
396
398 auto* editor = new SmartPlaylistEditor(mainStack);
399
400 if (!editor->Create())
401 {
402 delete editor;
403 return true;
404 }
405
406 editor->editSmartPlaylist(category, name);
409
410 mainStack->AddScreen(editor);
411
412 handled = true;
413 }
414 else if (mnode->getAction() == "smartplaylistcategory" && action == "EDIT")
415 {
416 QString category = mnode->GetText();
417
419 auto* editor = new SmartPlaylistEditor(mainStack);
420
421 if (!editor->Create())
422 {
423 delete editor;
424 return true;
425 }
426
427 editor->newSmartPlaylist(category);
428
431
432 mainStack->AddScreen(editor);
433
434 handled = true;
435 }
436 else if (mnode->getAction() == "trackid")
437 {
438 MusicMetadata *mdata = gMusicData->m_all_music->getMetadata(mnode->getInt());
439 if (mdata)
440 {
441 if (action == "INFO")
442 showTrackInfo(mdata);
443 else
444 editTrackInfo(mdata);
445 }
446
447 handled = true;
448 }
449 }
450 }
451 }
452 else if (action == "DELETE" && GetFocusWidget() == m_playlistTree)
453 {
454 handled = false;
455
457 if (node)
458 {
459 auto *mnode = dynamic_cast<MusicGenericTree*>(node);
460 if (mnode)
461 {
462 if (mnode->getAction() == "smartplaylist")
463 {
464 QString category = mnode->getParent()->GetText();
465 QString name = mnode->GetText();
466
467 ShowOkPopup(tr("Are you sure you want to delete this Smart Playlist?\n"
468 "Category: %1 - Name: %2").arg(category, name),
470 handled = true;
471 }
472 else if (mnode->getAction() == "playlist")
473 {
474 QString name = mnode->GetText();
475
476 ShowOkPopup(tr("Are you sure you want to delete this Playlist?\n"
477 "Name: %1").arg(name),
479 handled = true;
480 }
481 }
482 }
483 }
484 else if (action == "MARK")
485 {
487 if (item)
488 treeItemClicked(item);
489 }
490 else if ((action == "PLAY") && (GetFocusWidget() == m_playlistTree))
491 {
493 if (item)
494 {
495 auto *node = item->GetData().value<MythGenericTree*>();
496 auto *mnode = dynamic_cast<MusicGenericTree*>(node);
497
498 if (mnode)
499 {
500 updateSonglist(mnode);
501
502 if (m_songList.count() > 0)
503 {
506
508
510
511 if (m_mainvisual)
512 {
513 m_mainvisual->mutex()->lock();
515 m_mainvisual->mutex()->unlock();
516 }
517 }
518 else
519 {
520 handled = false;
521 }
522 }
523 }
524 }
525 else
526 {
527 handled = false;
528 }
529 }
530
531 if (!handled && MusicCommon::keyPressEvent(event))
532 handled = true;
533
534 return handled;
535}
536
538{
539 m_songList.clear();
540
541 if (node->getAction() == "playlists" ||
542 node->getAction() == "smartplaylists" ||
543 node->getAction() == "smartplaylistcategory")
544 { // NOLINT(bugprone-branch-clone)
545 }
546 else if (node->getAction() == "trackid" || node->getAction() == "cdtrack")
547 {
548 m_songList.append(node->getInt());
549 }
550 else if (node->getAction() == "all tracks" ||
551 node->getAction() == "albums" ||
552 node->getAction() == "compartists" ||
553 node->getAction() == "artists" ||
554 node->getAction() == "genres" ||
555 node->getAction() == "ratings" ||
556 node->getAction() == "years")
557 {
558 // get the list of tracks from the previous 'All Tracks' node
559 auto *allTracksNode = dynamic_cast<MusicGenericTree*>(node->getParent()->getChildAt(0));
560 if (allTracksNode)
561 {
562 for (int x = 0; x < allTracksNode->childCount(); x++)
563 {
564 MythGenericTree *trackNode = allTracksNode->getChildAt(x);
565 if (trackNode)
566 m_songList.append(trackNode->getInt());
567 }
568 }
569 }
570 else if (node->getAction() == "album" ||
571 node->getAction() == "artist" ||
572 node->getAction() == "genre" ||
573 node->getAction() == "rating" ||
574 node->getAction() == "year" ||
575 node->getAction() == "compilations" ||
576 node->getAction() == "compartist")
577 {
578 // get the list of tracks from the 'All Tracks' node
579 auto *allTracksNode = dynamic_cast<MusicGenericTree*>(node->getChildAt(0));
580 if (allTracksNode)
581 {
582 if (allTracksNode->childCount() == 0)
583 filterTracks(allTracksNode);
584
585 for (int x = 0; x < allTracksNode->childCount(); x++)
586 {
587 MythGenericTree *trackNode = allTracksNode->getChildAt(x);
588 if (trackNode)
589 m_songList.append(trackNode->getInt());
590 }
591 }
592 }
593 else if (node->getAction() == "smartplaylist")
594 {
595 // add the selected smart playlist's tracks to the song list
596 QList<MythGenericTree*> *children = node->getAllChildren();
597 for (int x = 0; x < children->count(); x++)
598 {
599 MythGenericTree *childnode = children->at(x);
600 m_songList.append(childnode->getInt());
601 }
602 }
603 else if (node->getAction() == "playlist")
604 {
605 // get list of tracks to add from the playlist
606 int playlistID = node->getInt();
607 Playlist *playlist = gMusicData->m_all_playlists->getPlaylist(playlistID);
608
609 if (playlist)
610 {
611 for (int x = 0; x < playlist->getTrackCount(); x++)
612 {
613 MusicMetadata *mdata = playlist->getSongAt(x);
614 if (mdata)
615 m_songList.append((int) mdata->ID());
616 }
617 }
618 }
619 else if (node->getAction() == "error")
620 {
621 // a smart playlist has returned no tracks etc.
622 }
623 else
624 {
625 // fall back to getting the tracks from the MetadataPtrList
626 auto *tracks = node->GetData().value<MetadataPtrList*>();
627 for (int x = 0; x < tracks->count(); x++)
628 {
629 MusicMetadata *mdata = tracks->at(x);
630 if (mdata)
631 m_songList.append((int)mdata->ID());
632 }
633 }
634}
635
637{
639 {
642
643 MythMenu *menu = nullptr;
644 auto *mnode = dynamic_cast<MusicGenericTree*>(m_playlistTree->GetCurrentNode());
645
646 if (mnode)
647 {
648 if (mnode->getAction() == "smartplaylists" ||
649 mnode->getAction() == "smartplaylistcategory" ||
650 mnode->getAction() == "smartplaylist")
651 {
653 }
654 else if (mnode->getAction() == "playlists" ||
655 mnode->getAction() == "playlist")
656 {
658 }
659 else if ( // (mnode->getAction() == "trackid") ||
660 (mnode->getAction() == "error"))
661 {
662 }
663 else
664 {
666 }
667
668 updateSonglist(mnode);
669 }
670
671 if (menu)
672 {
673 menu->AddItem(tr("More Options"), nullptr, createMainMenu());
674
675 MythScreenStack *popupStack = GetMythMainWindow()->GetStack("popup stack");
676
677 auto *menuPopup = new MythDialogBox(menu, popupStack, "actionmenu");
678
679 if (menuPopup->Create())
680 popupStack->AddScreen(menuPopup);
681 else
682 delete menu;
683
684 return;
685 }
686 }
687
689}
690
692{
693 MythMenu *menu = nullptr;
694
696 {
697 auto *mnode = dynamic_cast<MusicGenericTree*>(m_playlistTree->GetCurrentNode());
698
699 if (!mnode)
700 return nullptr;
701
702 if (mnode->getAction() == "playlist")
703 {
704 menu = new MythMenu(tr("Playlist Actions"), this, "treeplaylistmenu");
706 {
707 menu->AddItem(tr("Play Now"));
708 menu->AddItem(tr("Add Tracks"));
709 }
710 else
711 {
712 menu->AddItem(tr("Add Tracks"));
713 menu->AddItem(tr("Play Now"));
714 }
715 menu->AddItem(tr("Replace Tracks"));
716 menu->AddItem(tr("Remove Playlist"));
717 }
718 }
719
720 return menu;
721}
722
724{
725 MythMenu *menu = nullptr;
726
728 {
729 auto *mnode = dynamic_cast<MusicGenericTree*>(m_playlistTree->GetCurrentNode());
730
731 if (!mnode)
732 return nullptr;
733
734 if (mnode->getAction() == "smartplaylists" || mnode->getAction() == "smartplaylistcategory")
735 {
736 QString label = tr("Smart Playlist Actions");
737
738 menu = new MythMenu(label, this, "smartplaylistmenu");
739
740 menu->AddItem(tr("New Smart Playlist"));
741 }
742 else if (mnode->getAction() == "smartplaylist")
743 {
744 menu = new MythMenu(tr("Smart Playlist Actions"), this, "smartplaylistmenu");
745
747 {
748 menu->AddItem(tr("Play Now"));
749 menu->AddItem(tr("Add Tracks"));
750 }
751 else
752 {
753 menu->AddItem(tr("Add Tracks"));
754 menu->AddItem(tr("Play Now"));
755 }
756 menu->AddItem(tr("Replace Tracks"));
757
758 menu->AddItem(tr("Edit Smart Playlist"));
759 menu->AddItem(tr("New Smart Playlist"));
760 menu->AddItem(tr("Remove Smart Playlist"));
761 }
762 }
763
764 return menu;
765}
766
767// NOLINTBEGIN(clang-analyzer-cplusplus.NewDeleteLeaks)
769{
770 if (!m_rootNode)
771 m_rootNode = new MusicGenericTree(nullptr, "Root Music Node");
772
773 auto *node = new MusicGenericTree(m_rootNode, tr("All Tracks"), "all tracks");
774 node->setDrawArrow(true);
775 node->SetData(QVariant::fromValue(gMusicData->m_all_music->getAllMetadata()));
776
777 node = new MusicGenericTree(m_rootNode, tr("Albums"), "albums");
778 node->setDrawArrow(true);
779 node->SetData(QVariant::fromValue(gMusicData->m_all_music->getAllMetadata()));
780
781 node = new MusicGenericTree(m_rootNode, tr("Artists"), "artists");
782 node->setDrawArrow(true);
783 node->SetData(QVariant::fromValue(gMusicData->m_all_music->getAllMetadata()));
784
785 node = new MusicGenericTree(m_rootNode, tr("Genres"), "genres");
786 node->setDrawArrow(true);
787 node->SetData(QVariant::fromValue(gMusicData->m_all_music->getAllMetadata()));
788#if 0
789 node = new MusicGenericTree(m_rootNode, tr("Tags"), "tags");
790 node->setDrawArrow(true);
791 node->SetData(QVariant::fromValue(gMusicData->all_music->getAllMetadata()));
792#endif
793 node = new MusicGenericTree(m_rootNode, tr("Ratings"), "ratings");
794 node->setDrawArrow(true);
795 node->SetData(QVariant::fromValue(gMusicData->m_all_music->getAllMetadata()));
796
797 node = new MusicGenericTree(m_rootNode, tr("Years"), "years");
798 node->setDrawArrow(true);
799 node->SetData(QVariant::fromValue(gMusicData->m_all_music->getAllMetadata()));
800
801 node = new MusicGenericTree(m_rootNode, tr("Compilations"), "compilations");
802 node->setDrawArrow(true);
803
805 auto *compTracks = new MetadataPtrList;
806 m_deleteList.append(compTracks);
807
808 for (int x = 0; x < alltracks->count(); x++)
809 {
810 MusicMetadata *mdata = alltracks->at(x);
811 if (mdata)
812 {
813 if (mdata->Compilation())
814 compTracks->append(mdata);
815 }
816 }
817 node->SetData(QVariant::fromValue(compTracks));
818
820 {
821 node = new MusicGenericTree(m_rootNode, tr("CD - %1").arg(gMusicData->m_all_music->getCDTitle()), "cd");
822 node->setDrawArrow(true);
823 node->SetData(QVariant::fromValue(gMusicData->m_all_music->getAllCDMetadata()));
824 }
825
826 node = new MusicGenericTree(m_rootNode, tr("Directory"), "directory");
827 node->setDrawArrow(true);
828 node->SetData(QVariant::fromValue(gMusicData->m_all_music->getAllMetadata()));
829
830 node = new MusicGenericTree(m_rootNode, tr("Playlists"), "playlists");
831 node->setDrawArrow(true);
832
833 node = new MusicGenericTree(m_rootNode, tr("Smart Playlists"), "smartplaylists");
834 node->setDrawArrow(true);
835}
836// NOLINTEND(clang-analyzer-cplusplus.NewDeleteLeaks)
837
839{
840 auto *node = item->GetData().value<MythGenericTree*>();
841 auto *mnode = dynamic_cast<MusicGenericTree*>(node);
842
843 if (!mnode || !gPlayer->getCurrentPlaylist() || mnode->getAction() == "error")
844 return;
845
846 if (mnode->getAction() == "trackid")
847 {
849 {
850 // remove track from the current playlist
851 gPlayer->removeTrack(mnode->getInt());
852 mnode->setCheck(MythUIButtonListItem::NotChecked);
853 }
854 else if (MusicPlayer::getPlayNow())
855 {
856 gPlayer->addTrack(mnode->getInt(), false);
859 mnode->setCheck(MythUIButtonListItem::FullChecked);
860 }
861 else
862 {
863 // add track to the current playlist
864 gPlayer->addTrack(mnode->getInt(), true);
865 mnode->setCheck(MythUIButtonListItem::FullChecked);
866 }
867 }
868 else
869 {
870 ShowMenu();
871 }
872}
873
874
876{
877 auto *node = item->GetData().value<MythGenericTree*>();;
878 auto *mnode = dynamic_cast<MusicGenericTree*>(node);
879
880 if (!mnode)
881 return;
882
883 if (item->GetText("*").isEmpty())
884 {
885 item->SetText(" ", "*");
886
887 QString artFile;
888
889 if (mnode->getAction() == "trackid")
890 {
891 MusicMetadata *mdata = gMusicData->m_all_music->getMetadata(mnode->getInt());
892 if (mdata)
893 artFile = mdata->getAlbumArtFile();
894 }
895 else if (mnode->getAction() == "album")
896 {
897 // hunt for a coverart image for the album
898 auto *tracks = node->GetData().value<MetadataPtrList*>();
899 for (int x = 0; x < tracks->count(); x++)
900 {
901 MusicMetadata *mdata = tracks->at(x);
902 if (mdata)
903 {
904 artFile = mdata->getAlbumArtFile();
905 if (!artFile.isEmpty())
906 break;
907 }
908 }
909 }
910 else if (mnode->getAction() == "compartist")
911 {
912 artFile = findIcon("artist", mnode->GetText().toLower());
913 }
914 else if (mnode->getAction() == "all tracks" || mnode->getAction() == "genres" ||
915 mnode->getAction() == "albums" || mnode->getAction() == "artists" ||
916 mnode->getAction() == "compartists"|| mnode->getAction() == "ratings" ||
917 mnode->getAction() == "years" || mnode->getAction() == "compilations" ||
918 mnode->getAction() == "cd" || mnode->getAction() == "directory" ||
919 mnode->getAction() == "playlists" || mnode->getAction() == "smartplaylists")
920 {
921 artFile = "blank.png";
922 }
923 else
924 {
925 artFile = findIcon(mnode->getAction(), mnode->GetText().toLower());
926 }
927
928 QString state = "default";
929
930 if (mnode->getAction() == "all tracks")
931 state = "alltracks";
932 else if (mnode->getAction() == "genres")
933 state = "genres";
934 else if (mnode->getAction() == "albums")
935 state = "albums";
936 else if (mnode->getAction() == "artists")
937 state = "artists";
938 else if (mnode->getAction() == "compartists")
939 state = "compartists";
940 else if (mnode->getAction() == "ratings")
941 state = "ratings";
942 else if (mnode->getAction() == "years")
943 state = "years";
944 else if (mnode->getAction() == "compilations")
945 state = "compilations";
946 else if (mnode->getAction() == "cd")
947 state = "cd";
948 else if (mnode->getAction() == "directory")
949 state = "directory";
950 else if (mnode->getAction() == "playlists")
951 state = "playlists";
952 else if (mnode->getAction() == "smartplaylists")
953 state = "smartplaylists";
954
955 item->DisplayState(state, "nodetype");
956
957 if (artFile.isEmpty())
958 item->SetImage("");
959 else
960 item->SetImage(artFile);
961 }
962}
963
965{
966 auto *mnode = dynamic_cast<MusicGenericTree*>(node);
967 if (!mnode)
968 return;
969
971 {
972 QString route = node->getRouteByString().join(" -> ");
973 route = route.remove("Root Music Node -> ");
975 }
976
977 if (m_positionText)
978 {
979 m_positionText->SetText(tr("%1 of %2").arg(node->getPosition() + 1)
980 .arg(node->siblingCount()));
981 }
982
983 if (mnode->childCount() > 0 || mnode->getAction() == "trackid")
984 return;
985
986 if (mnode->getAction() == "smartplaylists")
987 {
989 }
990 else if (mnode->getAction() == "smartplaylistcategory")
991 {
992 getSmartPlaylists(mnode);
993 }
994 else if (mnode->getAction() == "smartplaylist")
995 {
996 getSmartPlaylistTracks(mnode, mnode->getInt());
997 }
998 else if (mnode->getAction() == "playlists")
999 {
1000 getPlaylists(mnode);
1001 }
1002 else if (mnode->getAction() == "playlist")
1003 {
1004 getPlaylistTracks(mnode, mnode->getInt());
1005 }
1006 else if (mnode->getAction() == "cd")
1007 {
1008 getCDTracks(mnode);
1009 }
1010 else
1011 {
1012 filterTracks(mnode);
1013 }
1014}
1015
1017{
1018 auto *tracks = node->GetData().value<MetadataPtrList*>();
1019
1020 if (!tracks)
1021 return;
1022
1023 if (node->getAction() == "all tracks")
1024 {
1025 QMultiMap<QString, int> map;
1026 bool isAlbum = false;
1027 auto *parentNode = dynamic_cast<MusicGenericTree*>(node->getParent());
1028
1029 if (parentNode)
1030 isAlbum = parentNode->getAction() == "album";
1031
1032 for (int x = 0; x < tracks->count(); x++)
1033 {
1034 MusicMetadata *mdata = tracks->at(x);
1035 if (mdata)
1036 {
1037 QString key = mdata->Title();
1038
1039 // Add the track number if an album is selected
1040 if (isAlbum && mdata->Track() > 0)
1041 {
1042 key.prepend(QString::number(mdata->Track()) + " - ");
1043 if (mdata->Track() < 10)
1044 key.prepend("0");
1045
1046 // Add the disc number if this is a multi-disc album
1047 if (mdata->DiscNumber() > 0)
1048 {
1049 key.prepend(QString::number(mdata->DiscNumber()) + "/");
1050 if (mdata->DiscNumber() < 10)
1051 key.prepend("0");
1052 }
1053 }
1054 map.insert(key, mdata->ID());
1055 }
1056 }
1057
1058 auto i = map.constBegin();
1059 while (i != map.constEnd())
1060 {
1061 auto *newnode = new MusicGenericTree(node, i.key(), "trackid");
1062 newnode->setInt(i.value());
1063 newnode->setDrawArrow(false);
1064 bool hasTrack = gPlayer->getCurrentPlaylist() ? gPlayer->getCurrentPlaylist()->checkTrack(newnode->getInt()) : false;
1066 ++i;
1067 }
1068
1069 node->sortByString(); // Case-insensitive sort
1070 }
1071 else if (node->getAction() == "artists")
1072 {
1073 QMap<QString, MetadataPtrList*> map;
1074
1075 for (int x = 0; x < tracks->count(); x++)
1076 {
1077 MusicMetadata *mdata = tracks->at(x);
1078 if (mdata)
1079 {
1080 if (map.contains(mdata->Artist()))
1081 {
1082 MetadataPtrList *filteredTracks = map.value(mdata->Artist());
1083 filteredTracks->append(mdata);
1084 }
1085 else
1086 {
1087 auto *filteredTracks = new MetadataPtrList;
1088 m_deleteList.append(filteredTracks);
1089 filteredTracks->append(mdata);
1090 map.insert(mdata->Artist(), filteredTracks);
1091 }
1092 }
1093 }
1094
1095 QMap<QString, MetadataPtrList*>::const_iterator i = map.constBegin();
1096 while (i != map.constEnd())
1097 {
1098 auto *newnode = new MusicGenericTree(node, i.key(), "artist");
1099 newnode->SetData(QVariant::fromValue(i.value()));
1100 ++i;
1101 }
1102
1103 node->sortByString(); // Case-insensitive sort
1104 }
1105 else if (node->getAction() == "compartists")
1106 {
1107 QMap<QString, MetadataPtrList*> map;
1108
1109 for (int x = 0; x < tracks->count(); x++)
1110 {
1111 MusicMetadata *mdata = tracks->at(x);
1112 if (mdata)
1113 {
1114 if (mdata->CompilationArtist() != mdata->Artist())
1115 {
1116 if (map.contains(mdata->CompilationArtist()))
1117 {
1118 MetadataPtrList *filteredTracks = map.value(mdata->CompilationArtist());
1119 filteredTracks->append(mdata);
1120 }
1121 else
1122 {
1123 auto *filteredTracks = new MetadataPtrList;
1124 m_deleteList.append(filteredTracks);
1125 filteredTracks->append(mdata);
1126 map.insert(mdata->CompilationArtist(), filteredTracks);
1127 }
1128 }
1129 }
1130 }
1131
1132 QMap<QString, MetadataPtrList*>::const_iterator i = map.constBegin();
1133 while (i != map.constEnd())
1134 {
1135 auto *newnode = new MusicGenericTree(node, i.key(), "compartist");
1136 newnode->SetData(QVariant::fromValue(i.value()));
1137 ++i;
1138 }
1139
1140 node->sortByString(); // Case-insensitive sort
1141 }
1142 else if (node->getAction() == "albums")
1143 {
1144 QMap<QString, MetadataPtrList*> map;
1145
1146 for (int x = 0; x < tracks->count(); x++)
1147 {
1148 MusicMetadata *mdata = tracks->at(x);
1149 if (mdata)
1150 {
1151 if (map.contains(mdata->Album()))
1152 {
1153 MetadataPtrList *filteredTracks = map.value(mdata->Album());
1154 filteredTracks->append(mdata);
1155 }
1156 else
1157 {
1158 auto *filteredTracks = new MetadataPtrList;
1159 m_deleteList.append(filteredTracks);
1160 filteredTracks->append(mdata);
1161 map.insert(mdata->Album(), filteredTracks);
1162 }
1163 }
1164 }
1165
1166 QMap<QString, MetadataPtrList*>::const_iterator i = map.constBegin();
1167 while (i != map.constEnd())
1168 {
1169 auto *newnode = new MusicGenericTree(node, i.key(), "album");
1170 newnode->SetData(QVariant::fromValue(i.value()));
1171 ++i;
1172 }
1173
1174 node->sortByString(); // Case-insensitive sort
1175 }
1176 else if (node->getAction() == "genres")
1177 {
1178 QMap<QString, MetadataPtrList*> map;
1179
1180 for (int x = 0; x < tracks->count(); x++)
1181 {
1182 MusicMetadata *mdata = tracks->at(x);
1183 if (mdata)
1184 {
1185 if (map.contains(mdata->Genre()))
1186 {
1187 MetadataPtrList *filteredTracks = map.value(mdata->Genre());
1188 filteredTracks->append(mdata);
1189 }
1190 else
1191 {
1192 auto *filteredTracks = new MetadataPtrList;
1193 m_deleteList.append(filteredTracks);
1194 filteredTracks->append(mdata);
1195 map.insert(mdata->Genre(), filteredTracks);
1196 }
1197 }
1198 }
1199
1200 QMap<QString, MetadataPtrList*>::const_iterator i = map.constBegin();
1201 while (i != map.constEnd())
1202 {
1203 auto *newnode = new MusicGenericTree(node, i.key(), "genre");
1204 newnode->SetSortText(i.key()); // No manipulation of prefixes on genres
1205 newnode->SetData(QVariant::fromValue(i.value()));
1206 ++i;
1207 }
1208
1209 node->sortByString(); // Case-insensitive sort
1210 }
1211 else if (node->getAction() == "ratings")
1212 {
1213 QMap<QString, MetadataPtrList*> map;
1214
1215 for (int x = 0; x < tracks->count(); x++)
1216 {
1217 MusicMetadata *mdata = tracks->at(x);
1218 if (mdata)
1219 {
1220 QString ratingStr = tr("%n Star(s)", "", mdata->Rating());
1221 if (map.contains(ratingStr))
1222 {
1223 MetadataPtrList *filteredTracks = map.value(ratingStr);
1224 filteredTracks->append(mdata);
1225 }
1226 else
1227 {
1228 auto *filteredTracks = new MetadataPtrList;
1229 m_deleteList.append(filteredTracks);
1230 filteredTracks->append(mdata);
1231 map.insert(ratingStr, filteredTracks);
1232 }
1233 }
1234 }
1235
1236 QMap<QString, MetadataPtrList*>::const_iterator i = map.constBegin();
1237 while (i != map.constEnd())
1238 {
1239 auto *newnode = new MusicGenericTree(node, i.key(), "rating");
1240 newnode->SetData(QVariant::fromValue(i.value()));
1241 ++i;
1242 }
1243 }
1244 else if (node->getAction() == "years")
1245 {
1246 QMap<QString, MetadataPtrList*> map;
1247
1248 for (int x = 0; x < tracks->count(); x++)
1249 {
1250 MusicMetadata *mdata = tracks->at(x);
1251 if (mdata)
1252 {
1253 QString yearStr = QString("%1").arg(mdata->Year());
1254 if (map.contains(yearStr))
1255 {
1256 MetadataPtrList *filteredTracks = map.value(yearStr);
1257 filteredTracks->append(mdata);
1258 }
1259 else
1260 {
1261 auto *filteredTracks = new MetadataPtrList;
1262 m_deleteList.append(filteredTracks);
1263 filteredTracks->append(mdata);
1264 map.insert(yearStr, filteredTracks);
1265 }
1266 }
1267 }
1268
1269 QMap<QString, MetadataPtrList*>::const_iterator i = map.constBegin();
1270 while (i != map.constEnd())
1271 {
1272 auto *newnode = new MusicGenericTree(node, i.key(), "year");
1273 newnode->SetData(QVariant::fromValue(i.value()));
1274 ++i;
1275 }
1276 }
1277 else if (node->getAction() == "directory")
1278 {
1279 QMap<QString, MetadataPtrList*> map;
1280
1281 // which directories have we already filtered by
1282 QString dir;
1283 MusicGenericTree *climber = node;
1284 while (climber)
1285 {
1286 dir = climber->GetText() + '/' + dir;
1287 climber = dynamic_cast<MusicGenericTree *>(climber->getParent());
1288 }
1289
1290 // remove the top two nodes
1291 QString top2 = "Root Music Node/" + tr("Directory") + '/';
1292 if (dir.startsWith(top2))
1293 dir = dir.mid(top2.length());
1294
1295 for (int x = 0; x < tracks->count(); x++)
1296 {
1297 MusicMetadata *mdata = tracks->at(x);
1298 if (mdata)
1299 {
1300 QString filename = mdata->Filename(false);
1301
1302 if (filename.startsWith(dir))
1303 filename = filename.mid(dir.length());
1304
1305 QStringList dirs = filename.split("/");
1306
1307 QString key = dirs.count() > 1 ? dirs[0] : "[TRACK]" + dirs[0];
1308 if (map.contains(key))
1309 {
1310 MetadataPtrList *filteredTracks = map.value(key);
1311 filteredTracks->append(mdata);
1312 }
1313 else
1314 {
1315 auto *filteredTracks = new MetadataPtrList;
1316 m_deleteList.append(filteredTracks);
1317 filteredTracks->append(mdata);
1318 map.insert(key, filteredTracks);
1319 }
1320 }
1321 }
1322
1323 // add directories first
1324 QMap<QString, MetadataPtrList*>::const_iterator i = map.constBegin();
1325 while (i != map.constEnd())
1326 {
1327 if (!i.key().startsWith("[TRACK]"))
1328 {
1329 auto *newnode = new MusicGenericTree(node, i.key(), "directory");
1330 newnode->SetData(QVariant::fromValue(i.value()));
1331 }
1332 ++i;
1333 }
1334
1335 // now add tracks
1336 i = map.constBegin();
1337 while (i != map.constEnd())
1338 {
1339 if (i.key().startsWith("[TRACK]"))
1340 {
1341 auto *newnode = new MusicGenericTree(node, i.key().mid(7), "trackid");
1342 newnode->setInt(i.value()->at(0)->ID());
1343 newnode->setDrawArrow(false);
1344 bool hasTrack = gPlayer->getCurrentPlaylist() ? gPlayer->getCurrentPlaylist()->checkTrack(newnode->getInt()) : false;
1346 }
1347 ++i;
1348 }
1349 }
1350 else if (node->getAction() == "artist" || node->getAction() == "compartist" ||
1351 node->getAction() == "album" || node->getAction() == "genre" ||
1352 node->getAction() == "rating" || node->getAction() == "year" ||
1353 node->getAction() == "compilations")
1354 {
1355 // which fields have we already filtered by
1356 QStringList fields;
1357 MusicGenericTree *climber = node;
1358 while (climber)
1359 {
1360 fields.append(climber->getAction());
1361 climber = dynamic_cast<MusicGenericTree *>(climber->getParent());
1362 }
1363
1364 auto *newnode = new MusicGenericTree(node, tr("All Tracks"), "all tracks");
1365 newnode->setDrawArrow(true);
1366 newnode->SetData(node->GetData());
1367
1368 if (!fields.contains("albums"))
1369 {
1370 newnode = new MusicGenericTree(node, tr("Albums"), "albums");
1371 newnode->setDrawArrow(true);
1372 newnode->SetData(node->GetData());
1373 }
1374
1375 if (!fields.contains("artists"))
1376 {
1377 newnode = new MusicGenericTree(node, tr("Artists"), "artists");
1378 newnode->setDrawArrow(true);
1379 newnode->SetData(node->GetData());
1380 }
1381
1382 if (!fields.contains("compartists"))
1383 {
1384 // only show the Compilation Artists node if we are one the Compilations branch
1385 bool showCompArtists = false;
1386 MusicGenericTree *mnode = node;
1387 while (mnode)
1388 {
1389 if (mnode->getAction() == "compilations")
1390 {
1391 showCompArtists = true;
1392 break;
1393 }
1394
1395 mnode = dynamic_cast<MusicGenericTree *>(mnode->getParent());
1396 }
1397
1398 // only show the Comp. Artist if it differs from the Artist
1399 bool found = false;
1400 auto *tracks2 = node->GetData().value<MetadataPtrList*>();
1401 for (int x = 0; x < tracks2->count(); x++)
1402 {
1403 MusicMetadata *mdata = tracks2->at(x);
1404 if (mdata)
1405 {
1406 if (mdata->Artist() != mdata->CompilationArtist())
1407 {
1408 found = true;
1409 break;
1410 }
1411 }
1412 }
1413
1414 if (showCompArtists && found)
1415 {
1416 newnode = new MusicGenericTree(node, tr("Compilation Artists"), "compartists");
1417 newnode->setDrawArrow(true);
1418 newnode->SetData(node->GetData());
1419 }
1420 }
1421
1422 if (!fields.contains("genres"))
1423 {
1424 newnode = new MusicGenericTree(node, tr("Genres"), "genres");
1425 newnode->setDrawArrow(true);
1426 newnode->SetData(node->GetData());
1427 }
1428#if 0
1429 if (!fields.contains("tags"))
1430 {
1431 newnode = new MusicGenericTree(node, tr("Tags"), "tags");
1432 newnode->setDrawArrow(true);
1433 newnode->SetData(node->GetData());
1434 }
1435#endif
1436 if (!fields.contains("ratings"))
1437 {
1438 newnode = new MusicGenericTree(node, tr("Ratings"), "ratings");
1439 newnode->setDrawArrow(true);
1440 newnode->SetData(node->GetData());
1441 }
1442
1443 if (!fields.contains("years"))
1444 {
1445 newnode = new MusicGenericTree(node, tr("Years"), "years");
1446 newnode->setDrawArrow(true);
1447 newnode->SetData(node->GetData());
1448 }
1449 }
1450}
1451
1453{
1455
1456 if (query.exec("SELECT categoryid, name FROM music_smartplaylist_categories ORDER BY name;"))
1457 {
1458 if (query.isActive() && query.size() > 0)
1459 {
1460 // NOLINTNEXTLINE(clang-analyzer-cplusplus.NewDeleteLeaks)
1461 while (query.next())
1462 {
1463 // No memory leak. MusicGenericTree adds the new node
1464 // into a list of nodes maintained by its parent.
1465 auto *newnode =
1466 new MusicGenericTree(node, query.value(1).toString(), "smartplaylistcategory");
1467 newnode->setInt(query.value(0).toInt());
1468 }
1469 }
1470 }
1471 else
1472 {
1473 MythDB::DBError("Load smartplaylist categories", query);
1474 }
1475}
1476
1478{
1479 int categoryid = node->getInt();
1480
1482 query.prepare("SELECT smartplaylistid, name FROM music_smartplaylists WHERE categoryid = :CATEGORYID "
1483 "ORDER BY name;");
1484 query.bindValue(":CATEGORYID", categoryid);
1485 if (query.exec())
1486 {
1487 if (query.isActive() && query.size() > 0)
1488 {
1489 // NOLINTNEXTLINE(clang-analyzer-cplusplus.NewDeleteLeaks)
1490 while (query.next())
1491 {
1492 // No memory leak. MusicGenericTree adds the new node
1493 // into a list of nodes maintained by its parent.
1494 auto *newnode =
1495 new MusicGenericTree(node, query.value(1).toString(), "smartplaylist");
1496 newnode->setInt(query.value(0).toInt());
1497 }
1498 }
1499 }
1500 else
1501 {
1502 MythDB::DBError("Load smartplaylist names", query);
1503 }
1504}
1505
1507{
1509
1510 // find smartplaylist
1511 QString matchType;
1512 QString orderBy;
1513 int limitTo = 0;
1514
1515 query.prepare("SELECT smartplaylistid, matchtype, orderby, limitto "
1516 "FROM music_smartplaylists "
1517 "WHERE smartplaylistid = :SMARTPLAYLISTID;");
1518 query.bindValue(":SMARTPLAYLISTID", playlistID);
1519
1520 if (query.exec())
1521 {
1522 if (query.isActive() && query.size() > 0)
1523 {
1524 query.first();
1525 matchType = (query.value(1).toString() == "All") ? " AND " : " OR ";
1526 orderBy = query.value(2).toString();
1527 limitTo = query.value(3).toInt();
1528 }
1529 else
1530 {
1531 LOG(VB_GENERAL, LOG_WARNING,
1532 LOC + QString("Cannot find smartplaylist: %1").arg(playlistID));
1533 return;
1534 }
1535 }
1536 else
1537 {
1538 MythDB::DBError("Find SmartPlaylist", query);
1539 return;
1540 }
1541
1542 // get smartplaylist items
1543 QString whereClause = "WHERE ";
1544
1545 query.prepare("SELECT field, operator, value1, value2 "
1546 "FROM music_smartplaylist_items "
1547 "WHERE smartplaylistid = :ID;");
1548 query.bindValue(":ID", playlistID);
1549 if (query.exec())
1550 {
1551 bool bFirst = true;
1552 while (query.next())
1553 {
1554 QString fieldName = query.value(0).toString();
1555 QString operatorName = query.value(1).toString();
1556 QString value1 = query.value(2).toString();
1557 QString value2 = query.value(3).toString();
1558 if (!bFirst)
1559 {
1560 whereClause += matchType + getCriteriaSQL(fieldName,
1561 operatorName, value1, value2);
1562 }
1563 else
1564 {
1565 bFirst = false;
1566 whereClause += " " + getCriteriaSQL(fieldName, operatorName,
1567 value1, value2);
1568 }
1569 }
1570 }
1571
1572 // add order by clause
1573 whereClause += getOrderBySQL(orderBy);
1574
1575 // add limit
1576 if (limitTo > 0)
1577 whereClause += " LIMIT " + QString::number(limitTo);
1578
1579
1580 // find the tracks for this smartplaylist
1581 QString theQuery;
1582
1583 theQuery = "SELECT song_id, name FROM music_songs "
1584 "LEFT JOIN music_directories ON"
1585 " music_songs.directory_id=music_directories.directory_id "
1586 "LEFT JOIN music_artists ON"
1587 " music_songs.artist_id=music_artists.artist_id "
1588 "LEFT JOIN music_albums ON"
1589 " music_songs.album_id=music_albums.album_id "
1590 "LEFT JOIN music_genres ON"
1591 " music_songs.genre_id=music_genres.genre_id "
1592 "LEFT JOIN music_artists AS music_comp_artists ON "
1593 "music_albums.artist_id=music_comp_artists.artist_id ";
1594 if (whereClause.length() > 0)
1595 theQuery += whereClause;
1596
1597 if (!query.exec(theQuery))
1598 {
1599 MythDB::DBError("Load songlist from query", query);
1600 return;
1601 }
1602
1603 while (query.next())
1604 {
1605 auto *newnode =
1606 new MusicGenericTree(node, query.value(1).toString(), "trackid");
1607 newnode->setInt(query.value(0).toInt());
1608 newnode->setDrawArrow(false);
1609 bool hasTrack = gPlayer->getCurrentPlaylist() ? gPlayer->getCurrentPlaylist()->checkTrack(newnode->getInt()) : false;
1611 }
1612
1613 // check we found some tracks if not add something to let the user know
1614 if (node->childCount() == 0)
1615 {
1616 auto *newnode =
1617 new MusicGenericTree(node, tr("** No matching tracks **"), "error");
1618 newnode->setDrawArrow(false);
1619 }
1620}
1621
1623{
1624 QList<Playlist*> *playlists = gMusicData->m_all_playlists->getPlaylists();
1625
1626 // NOLINTNEXTLINE(clang-analyzer-cplusplus.NewDeleteLeaks)
1627 for (int x =0; x < playlists->count(); x++)
1628 {
1629 Playlist *playlist = playlists->at(x);
1630 // No memory leak. MusicGenericTree adds the new node
1631 // into a list of nodes maintained by its parent.
1632 auto *newnode =
1633 new MusicGenericTree(node, playlist->getName(), "playlist");
1634 newnode->setInt(playlist->getID());
1635 }
1636}
1637
1639{
1641
1642 for (int x = 0; x < tracks->count(); x++)
1643 {
1644 MusicMetadata *mdata = tracks->at(x);
1645 QString title = QString("%1 - %2").arg(mdata->Track()).arg(mdata->FormatTitle());
1646 auto *newnode = new MusicGenericTree(node, title, "trackid");
1647 newnode->setInt(mdata->ID());
1648 newnode->setDrawArrow(false);
1649 bool hasTrack = gPlayer->getCurrentPlaylist() ? gPlayer->getCurrentPlaylist()->checkTrack(mdata->ID()) : false;
1651 }
1652}
1653
1655{
1656 Playlist *playlist = gMusicData->m_all_playlists->getPlaylist(playlistID);
1657
1658 if (playlist)
1659 {
1660 for (int x = 0; x < playlist->getTrackCount(); x++)
1661 {
1662 MusicMetadata *mdata = playlist->getSongAt(x);
1663 if (mdata)
1664 {
1665 auto *newnode = new MusicGenericTree(node, mdata->Title(), "trackid");
1666 newnode->setInt(mdata->ID());
1667 newnode->setDrawArrow(false);
1668 bool hasTrack = gPlayer->getCurrentPlaylist() ? gPlayer->getCurrentPlaylist()->checkTrack(mdata->ID()) : false;
1670 }
1671 }
1672 }
1673
1674 // check we found some tracks if not add something to let the user know
1675 if (node->childCount() == 0)
1676 {
1677 auto *newnode =
1678 new MusicGenericTree(node, tr("** Empty Playlist!! **"), "error");
1679 newnode->setDrawArrow(false);
1680 }
1681}
1682
1684{
1686}
1687
1689{
1690 for (int x = 0; x < node->childCount(); x++)
1691 {
1692 auto *mnode = dynamic_cast<MusicGenericTree*>(node->getChildAt(x));
1693 if (mnode)
1694 {
1695 if (mnode->getAction() == "trackid")
1696 {
1697 bool hasTrack = gPlayer->getCurrentPlaylist() ? gPlayer->getCurrentPlaylist()->checkTrack(mnode->getInt()) : false;
1699 }
1700 else
1701 {
1702 if (mnode->childCount())
1703 updateSelectedTracks(mnode);
1704 }
1705 }
1706 }
1707}
1708
1710{
1711 QStringList route = m_playlistTree->GetCurrentNode()->getRouteByString();
1712
1714
1715 for (int x = 0; x < m_deleteList.count(); x++)
1716 delete m_deleteList.at(x);
1717 m_deleteList.clear();
1718
1722
1723 restoreTreePosition(route);
1724}
1725
1726void PlaylistEditorView::restoreTreePosition(const QStringList &route)
1727{
1728 if (route.count() < 2)
1729 return;
1730
1731 // traverse up the tree creating each nodes children as we go
1733 for (int x = 1 ; x < route.count(); x++)
1734 {
1735 node = node->getChildByName(route.at(x));
1736
1737 if (node)
1738 treeNodeChanged(node);
1739 else
1740 break;
1741 }
1742
1744}
1745
1747{
1748 if (m_playlistTree)
1749 {
1750 QString route = m_playlistTree->GetCurrentNode()->getRouteByString().join("\n");
1751 gCoreContext->SaveSetting("MusicTreeLastActive", route);
1752 }
1753}
1754
1755void PlaylistEditorView::smartPLChanged(const QString &category, const QString &name)
1756{
1757 reloadTree();
1758
1759 // move to the smart playlist in tree
1760 QStringList route;
1761 route << "Root Music Node" << tr("Smart Playlists") << category << name;
1762 restoreTreePosition(route);
1763}
1764
1766{
1767 if (!ok)
1768 return;
1769
1771 if (node)
1772 {
1773 auto *mnode = dynamic_cast<MusicGenericTree*>(node);
1774 if (mnode)
1775 {
1776 if (mnode->getAction() == "smartplaylist")
1777 {
1778 QString category = mnode->getParent()->GetText();
1779 QString name = mnode->GetText();
1780
1782 reloadTree();
1783 }
1784 }
1785 }
1786}
1787
1789{
1790 if (!ok)
1791 return;
1792
1794 if (node)
1795 {
1796 auto *mnode = dynamic_cast<MusicGenericTree*>(node);
1797 if (mnode)
1798 {
1799 if (mnode->getAction() == "playlist")
1800 {
1801 int id = mnode->getInt();
1802
1805 }
1806 }
1807 }
1808}
MusicMetadata * getMetadata(int an_id)
int getCDTrackCount(void) const
MetadataPtrList * getAllCDMetadata(void)
MetadataPtrList * getAllMetadata(void)
QString getCDTitle(void) const
Event dispatched from MythUI modal dialogs to a listening class containing a result of some form.
Definition: mythdialogbox.h:41
static const Type kEventType
Definition: mythdialogbox.h:56
QSqlQuery wrapper that fetches a DB connection from the connection pool.
Definition: mythdbcon.h:128
bool prepare(const QString &query)
QSqlQuery::prepare() is not thread safe in Qt <= 3.3.2.
Definition: mythdbcon.cpp:837
bool first(void)
Wrap QSqlQuery::first() so we can display the query results.
Definition: mythdbcon.cpp:822
QVariant value(int i) const
Definition: mythdbcon.h:204
int size(void) const
Definition: mythdbcon.h:214
bool isActive(void) const
Definition: mythdbcon.h:215
bool exec(void)
Wrap QSqlQuery::exec() so we can display SQL.
Definition: mythdbcon.cpp:618
void bindValue(const QString &placeholder, const QVariant &val)
Add a single binding.
Definition: mythdbcon.cpp:888
bool next(void)
Wrap QSqlQuery::next() so we can display the query results.
Definition: mythdbcon.cpp:812
static MSqlQueryInfo InitCon(ConnectionReuse _reuse=kNormalConnection)
Only use this in combination with MSqlQuery constructor.
Definition: mythdbcon.cpp:550
void prepare(void) override
Definition: mainvisual.cpp:138
MythMenu * createPlaylistOptionsMenu(void)
bool m_moveTrackMode
Definition: musiccommon.h:152
QList< int > m_songList
Definition: musiccommon.h:170
void updateShuffleMode(bool updateUIList=false)
MythMenu * createMainMenu(void)
void doUpdatePlaylist(void)
void stopVisualizer(void)
PlaylistOptions m_playlistOptions
Definition: musiccommon.h:166
static void showTrackInfo(MusicMetadata *mdata)
MusicView m_currentView
Definition: musiccommon.h:141
void ShowMenu(void) override
bool CreateCommon(void)
Definition: musiccommon.cpp:78
static void editTrackInfo(MusicMetadata *mdata)
bool keyPressEvent(QKeyEvent *e) override
Key event handler.
MainVisual * m_mainvisual
Definition: musiccommon.h:144
void customEvent(QEvent *event) override
void updateUIPlaylist(void)
AllMusic * m_all_music
Definition: musicdata.h:52
PlaylistContainer * m_all_playlists
Definition: musicdata.h:51
void setDrawArrow(bool flag)
void setCheck(MythUIButtonListItem::CheckState state)
MusicGenericTree(MusicGenericTree *parent, const QString &name, const QString &action="", MythUIButtonListItem::CheckState check=MythUIButtonListItem::CantCheck, bool showArrow=true)
QString getAction(void) const
QPointer< MusicButtonItem > m_buttonItem
MythUIButtonListItem * CreateListButton(MythUIButtonList *list) override
MythUIButtonListItem::CheckState m_check
int Year() const
QString CompilationArtist() const
QString Title() const
QString getAlbumArtFile(void)
int Track() const
IdType ID() const
QString Filename(bool find=true)
QString Artist() const
int DiscNumber() const
int Rating() const
bool Compilation() const
QString Genre() const
QString FormatTitle()
QString Album() const
static const Type kCDChangedEvent
Definition: musicplayer.h:48
static const Type kMetadataChangedEvent
Definition: musicplayer.h:45
static const Type kAlbumArtChangedEvent
Definition: musicplayer.h:47
static const Type kAllTracksRemovedEvent
Definition: musicplayer.h:44
static const Type kTrackAddedEvent
Definition: musicplayer.h:41
static const Type kPlaylistChangedEvent
Definition: musicplayer.h:49
static const Type kTrackRemovedEvent
Definition: musicplayer.h:42
void removeTrack(int trackID)
void addTrack(int trackID, bool updateUI)
void removeListener(QObject *listener)
bool setCurrentTrackPos(int pos)
static bool getPlayNow(void)
void setShuffleMode(ShuffleMode mode)
Playlist * getCurrentPlaylist(void)
void setRepeatMode(RepeatMode mode)
Definition: musicplayer.h:192
void SaveSetting(const QString &key, int newValue)
QString GetSetting(const QString &key, const QString &defaultval="")
static void DBError(const QString &where, const MSqlQuery &query)
Definition: mythdb.cpp:226
Basic menu dialog, message and a list of options.
This class is used as a container for messages.
Definition: mythevent.h:17
const QString & Message() const
Definition: mythevent.h:65
static const Type kMythEventMessage
Definition: mythevent.h:79
int getInt() const
QVariant GetData(void) const
QString GetText(const QString &name="") const
MythGenericTree * getParent(void) const
int siblingCount(void) const
MythGenericTree * addNode(const QString &a_string, int an_int=0, bool selectable_flag=false, bool visible=true)
QStringList getRouteByString(void)
MythGenericTree * getChildAt(uint reference) const
int childCount(void) const
uint visibleChildCount() const
void setSelectable(bool flag)
MythGenericTree * getChildByName(const QString &a_name) const
QList< MythGenericTree * > * getAllChildren() const
MythScreenStack * GetMainStack()
bool TranslateKeyPress(const QString &Context, QKeyEvent *Event, QStringList &Actions, bool AllowJumps=true)
Get a list of actions for a keypress in the given context.
MythScreenStack * GetStack(const QString &Stackname)
void addListener(QObject *listener)
Add a listener to the observable.
void removeListener(QObject *listener)
Remove a listener to the observable.
virtual void AddScreen(MythScreenType *screen, bool allowFade=true)
Screen in which all other widgets are contained and rendered.
void BuildFocusList(void)
MythUIType * GetFocusWidget(void) const
void DisplayState(const QString &state, const QString &name)
void SetImage(MythImage *image, const QString &name="")
Sets an image directly, should only be used in special circumstances since it bypasses the cache.
QString GetText(const QString &name="") const
void SetText(const QString &text, const QString &name="", const QString &state="")
List widget, displays list items in a variety of themeable arrangements and can trigger signals when ...
MythGenericTree * GetCurrentNode(void) const
void Reset(void) override
Reset the widget to it's original state, should not reset changes made by the theme.
void RemoveCurrentItem(bool deleteNode=false)
Remove the currently selected item from the tree.
void itemVisible(MythUIButtonListItem *item)
void nodeChanged(MythGenericTree *node)
bool SetNodeByString(QStringList route)
Using a path based on the node string, set the currently selected node.
void itemClicked(MythUIButtonListItem *item)
bool AssignTree(MythGenericTree *tree)
Assign the root node of the tree to be displayed.
MythUIButtonListItem * GetItemCurrent(void) const
Return the currently selected list item.
virtual void SetText(const QString &text)
Definition: mythuitext.cpp:115
Playlist * getPlaylist(int id)
QList< Playlist * > * getPlaylists(void)
void deletePlaylist(int kill_me)
static void getCDTracks(MusicGenericTree *node)
MythUIText * m_positionText
MythUIText * m_breadcrumbsText
void ShowMenu(void) override
void filterTracks(MusicGenericTree *node)
static void getSmartPlaylistTracks(MusicGenericTree *node, int playlistID)
bool Create(void) override
MusicGenericTree * m_rootNode
static void getSmartPlaylistCategories(MusicGenericTree *node)
void treeItemClicked(MythUIButtonListItem *item)
static void getSmartPlaylists(MusicGenericTree *node)
static void treeItemVisible(MythUIButtonListItem *item)
static void getPlaylists(MusicGenericTree *node)
~PlaylistEditorView(void) override
QList< MetadataPtrList * > m_deleteList
void treeNodeChanged(MythGenericTree *node)
bool keyPressEvent(QKeyEvent *event) override
Key event handler.
void smartPLChanged(const QString &category, const QString &name)
static void getPlaylistTracks(MusicGenericTree *node, int playlistID)
MythUIButtonTree * m_playlistTree
void restoreTreePosition(const QStringList &route)
MythMenu * createPlaylistMenu(void)
MythMenu * createSmartPlaylistMenu(void)
void deleteSmartPlaylist(bool ok)
void customEvent(QEvent *event) override
void updateSonglist(MusicGenericTree *node)
PlaylistEditorView(MythScreenStack *parent, MythScreenType *parentScreen, const QString &layout, bool restorePosition=false)
int getID(void) const
Definition: playlist.h:111
QString getName(void)
Definition: playlist.h:106
int getTrackCount(void)
Definition: playlist.h:82
MusicMetadata * getSongAt(int pos) const
Definition: playlist.cpp:1096
bool checkTrack(MusicMetadata::IdType trackID) const
Definition: playlist.cpp:38
void smartPLChanged(const QString &category, const QString &name)
static bool deleteSmartPlaylist(const QString &category, const QString &name)
QMutex * mutex()
Definition: visualization.h:24
static bool LoadWindowFromXML(const QString &xmlfile, const QString &windowname, MythUIType *parent)
@ MV_PLAYLISTEDITORTREE
Definition: musiccommon.h:35
@ MV_PLAYLISTEDITORGALLERY
Definition: musiccommon.h:36
MusicData * gMusicData
Definition: musicdata.cpp:23
QList< MusicMetadata * > MetadataPtrList
MusicPlayer * gPlayer
Definition: musicplayer.cpp:38
QString findIcon(const QString &type, const QString &name, bool ignoreCache)
find an image for a artist or genre
Definition: musicutils.cpp:34
MythCoreContext * gCoreContext
This global variable contains the MythCoreContext instance for the app.
MythConfirmationDialog * ShowOkPopup(const QString &message, bool showCancel)
Non-blocking version of MythPopupBox::showOkPopup()
#define LOG(_MASK_, _LEVEL_, _QSTRING_)
Definition: mythlogging.h:39
MythMainWindow * GetMythMainWindow(void)
static MythThemedMenu * menu
@ PL_FIRST
Definition: playlist.h:32
@ PL_FIRSTNEW
Definition: playlist.h:33
@ PL_CURRENT
Definition: playlist.h:34
@ PL_REPLACE
Definition: playlist.h:24
@ PL_INSERTATEND
Definition: playlist.h:26
#define LOC
QString getOrderBySQL(const QString &orderByFields)
QString getCriteriaSQL(const QString &fieldName, const QString &operatorName, QString value1, QString value2)
PlayPLOption playPLOption
Definition: playlist.h:40
InsertPLOption insertPLOption
Definition: playlist.h:39
static bool Assign(ContainerType *container, UIType *&item, const QString &name, bool *err=nullptr)
Definition: mythuiutils.h:27