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