merged: Linuxport revisions through to r15292.
[xbmc:xbmc-antiquated.git] / xbmc / GUIWindowMusicNav.cpp
1 /*
2  *      Copyright (C) 2005-2008 Team XBMC
3  *      http://www.xbmc.org
4  *
5  *  This Program is free software; you can redistribute it and/or modify
6  *  it under the terms of the GNU General Public License as published by
7  *  the Free Software Foundation; either version 2, or (at your option)
8  *  any later version.
9  *
10  *  This Program is distributed in the hope that it will be useful,
11  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
12  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13  *  GNU General Public License for more details.
14  *
15  *  You should have received a copy of the GNU General Public License
16  *  along with XBMC; see the file COPYING.  If not, write to
17  *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
18  *  http://www.gnu.org/copyleft/gpl.html
19  *
20  */
21
22 #include "stdafx.h"
23 #include "GUIWindowMusicNav.h"
24 #include "Util.h"
25 #include "utils/GUIInfoManager.h"
26 #include "PlayListM3U.h"
27 #include "PlayListPlayer.h"
28 #include "GUIPassword.h"
29 #ifdef PRE_SKIN_VERSION_2_1_COMPATIBILITY
30 #include "GUILabelControl.h"
31 #include "GUIFontManager.h"
32 #endif
33 #include "GUIDialogFileBrowser.h"
34 #include "GUIDialogContentSettings.h"
35 #include "Picture.h"
36 #include "FileSystem/MusicDatabaseDirectory.h"
37 #include "FileSystem/VideoDatabaseDirectory.h"
38 #include "PartyModeManager.h"
39 #include "PlayListFactory.h"
40 #include "GUIDialogMusicScan.h"
41 #include "VideoDatabase.h"
42 #include "GUIWindowVideoNav.h"
43 #include "MusicInfoTag.h"
44 #include "GUIWindowManager.h"
45 #include "GUIDialogOK.h"
46 #include "GUIDialogKeyboard.h"
47 #include "GUIEditControl.h"
48 #include "FileSystem/File.h"
49 #include "FileItem.h"
50
51 using namespace std;
52 using namespace DIRECTORY;
53 using namespace PLAYLIST;
54 using namespace MUSICDATABASEDIRECTORY;
55
56 #define CONTROL_BTNVIEWASICONS     2
57 #define CONTROL_BTNSORTBY          3
58 #define CONTROL_BTNSORTASC         4
59 #define CONTROL_BTNTYPE            5
60 #define CONTROL_LIST              50
61 #define CONTROL_THUMBS            51
62 #define CONTROL_BIGLIST           52
63 #define CONTROL_LABELFILES        12
64
65 #define CONTROL_SEARCH             8
66 #define CONTROL_FILTER            15
67 #define CONTROL_BTNPARTYMODE      16
68 #define CONTROL_BTNMANUALINFO     17
69 #define CONTROL_BTN_FILTER        19
70 #define CONTROL_LABELEMPTY        18
71
72 CGUIWindowMusicNav::CGUIWindowMusicNav(void)
73     : CGUIWindowMusicBase(WINDOW_MUSIC_NAV, "MyMusicNav.xml")
74 {
75   m_vecItems->m_strPath = "?";
76   m_bDisplayEmptyDatabaseMessage = false;
77   m_thumbLoader.SetObserver(this);
78   m_unfilteredItems = new CFileItemList;
79   m_searchWithEdit = false;
80 }
81
82 CGUIWindowMusicNav::~CGUIWindowMusicNav(void)
83 {
84   delete m_unfilteredItems;
85 }
86
87 bool CGUIWindowMusicNav::OnMessage(CGUIMessage& message)
88 {
89   switch (message.GetMessage())
90   {
91   case GUI_MSG_WINDOW_RESET:
92     m_vecItems->m_strPath = "?";
93     break;
94   case GUI_MSG_WINDOW_DEINIT:
95     if (m_thumbLoader.IsLoading())
96       m_thumbLoader.StopThread();
97     break;
98   case GUI_MSG_WINDOW_INIT:
99     {
100       // check for valid quickpath parameter
101       CStdString strDestination = message.GetStringParam();
102       if (!strDestination.IsEmpty())
103       {
104         message.SetStringParam("");
105         CLog::Log(LOGINFO, "Attempting to quickpath to: %s", strDestination.c_str());
106       }
107
108       // is this the first time the window is opened?
109       if (m_vecItems->m_strPath == "?" && strDestination.IsEmpty())
110       {
111         strDestination = g_settings.m_defaultMusicLibSource;
112         m_vecItems->m_strPath = strDestination;
113         CLog::Log(LOGINFO, "Attempting to default to: %s", strDestination.c_str());
114       }
115
116       if (!strDestination.IsEmpty())
117       {
118         if (strDestination.Equals("$ROOT") || strDestination.Equals("Root"))
119         {
120           m_vecItems->m_strPath = "";
121         }
122         else if (strDestination.Equals("Genres"))
123         {
124           m_vecItems->m_strPath = "musicdb://1/";
125           SetHistoryForPath(m_vecItems->m_strPath);
126         }
127         else if (strDestination.Equals("Artists"))
128         {
129           m_vecItems->m_strPath = "musicdb://2/";
130           SetHistoryForPath(m_vecItems->m_strPath);
131         }
132         else if (strDestination.Equals("Albums"))
133         {
134           m_vecItems->m_strPath = "musicdb://3/";
135           SetHistoryForPath(m_vecItems->m_strPath);
136         }
137         else if (strDestination.Equals("Songs"))
138         {
139           m_vecItems->m_strPath = "musicdb://4/";
140           SetHistoryForPath(m_vecItems->m_strPath);
141         }
142         else if (strDestination.Equals("Top100"))
143         {
144           m_vecItems->m_strPath = "musicdb://5/";
145           SetHistoryForPath(m_vecItems->m_strPath);
146         }
147         else if (strDestination.Equals("Top100Songs"))
148         {
149           m_vecItems->m_strPath = "musicdb://5/2/";
150           SetHistoryForPath(m_vecItems->m_strPath);
151         }
152         else if (strDestination.Equals("Top100Albums"))
153         {
154           m_vecItems->m_strPath = "musicdb://5/1/";
155           SetHistoryForPath(m_vecItems->m_strPath);
156         }
157         else if (strDestination.Equals("RecentlyAddedAlbums"))
158         {
159           m_vecItems->m_strPath = "musicdb://6/";
160           SetHistoryForPath(m_vecItems->m_strPath);
161         }
162         else if (strDestination.Equals("RecentlyPlayedAlbums"))
163         {
164           m_vecItems->m_strPath = "musicdb://7/";
165           SetHistoryForPath(m_vecItems->m_strPath);
166         }
167         else if (strDestination.Equals("Compilations"))
168         {
169           m_vecItems->m_strPath = "musicdb://8/";
170           SetHistoryForPath(m_vecItems->m_strPath);
171         }
172         else if (strDestination.Equals("Playlists"))
173         {
174           m_vecItems->m_strPath = "special://musicplaylists/";
175           SetHistoryForPath(m_vecItems->m_strPath);
176         }
177         else if (strDestination.Equals("Years"))
178         {
179           m_vecItems->m_strPath = "musicdb://9/";
180           SetHistoryForPath(m_vecItems->m_strPath);
181         }
182         else if (strDestination.Equals("Plugins"))
183         {
184           m_vecItems->m_strPath = "plugin://music/";
185           SetHistoryForPath(m_vecItems->m_strPath);
186         }
187         else
188         {
189           CLog::Log(LOGWARNING, "Warning, destination parameter (%s) may not be valid", strDestination.c_str());
190           m_vecItems->m_strPath = strDestination;
191           SetHistoryForPath(m_vecItems->m_strPath);
192           break;
193         }
194       }
195
196       DisplayEmptyDatabaseMessage(false); // reset message state
197
198       if (!CGUIWindowMusicBase::OnMessage(message))
199         return false;
200
201       //  base class has opened the database, do our check
202       DisplayEmptyDatabaseMessage(m_musicdatabase.GetSongsCount() <= 0);
203
204       if (m_bDisplayEmptyDatabaseMessage)
205       {
206         // no library - make sure we focus on a known control, and default to the root.
207         SET_CONTROL_FOCUS(CONTROL_BTNTYPE, 0);
208         m_vecItems->m_strPath = "";
209         SetHistoryForPath("");
210         Update("");
211       }
212
213       return true;
214     }
215     break;
216
217   case GUI_MSG_CLICKED:
218     {
219       int iControl = message.GetSenderId();
220       if (iControl == CONTROL_BTNPARTYMODE)
221       {
222         if (g_partyModeManager.IsEnabled())
223           g_partyModeManager.Disable();
224         else
225         {
226           if (!g_partyModeManager.Enable())
227           {
228             SET_CONTROL_SELECTED(GetID(),CONTROL_BTNPARTYMODE,false);
229             return false;
230           }
231
232           // Playlist directory is the root of the playlist window
233           if (m_guiState.get()) m_guiState->SetPlaylistDirectory("playlistmusic://");
234
235           return true;
236         }
237         UpdateButtons();
238       }
239       else if (iControl == CONTROL_BTNMANUALINFO)
240       {
241         OnManualAlbumInfo();
242         return true;
243       }
244       else if (iControl == CONTROL_BTN_FILTER)
245       {
246         if (GetControl(iControl)->GetControlType() == CGUIControl::GUICONTROL_EDIT)
247         { // filter updated
248           CGUIMessage selected(GUI_MSG_ITEM_SELECTED, GetID(), CONTROL_BTN_FILTER);
249           OnMessage(selected);
250           m_filter = selected.GetLabel();
251           OnFilterItems();
252           return true;
253         }
254         if (m_filter.IsEmpty())
255           CGUIDialogKeyboard::ShowAndGetFilter(m_filter, false);
256         else
257         {
258           m_filter.Empty();
259           OnFilterItems();
260         }
261         return true;
262       }
263       else if (iControl == CONTROL_SEARCH)
264       {
265         if (m_searchWithEdit)
266         {
267           // search updated - reset timer
268           m_searchTimer.StartZero();
269           // grab our search string
270           CGUIMessage selected(GUI_MSG_ITEM_SELECTED, GetID(), CONTROL_SEARCH);
271           OnMessage(selected);
272           m_search = selected.GetLabel();
273           return true;
274         }
275         CGUIDialogKeyboard::ShowAndGetFilter(m_search, true);
276         return true;
277       }
278     }
279     break;
280   case GUI_MSG_PLAYBACK_STOPPED:
281   case GUI_MSG_PLAYBACK_ENDED:
282   case GUI_MSG_PLAYLISTPLAYER_STOPPED:
283   case GUI_MSG_PLAYBACK_STARTED:
284     {
285       SET_CONTROL_SELECTED(GetID(),CONTROL_BTNPARTYMODE, g_partyModeManager.IsEnabled());
286     }
287     break;
288   case GUI_MSG_NOTIFY_ALL:
289     {
290       if (message.GetParam1() == GUI_MSG_FILTER_ITEMS && IsActive())
291       {
292         if (message.GetParam2() == 1) // append
293           m_filter += message.GetStringParam();
294         else if (message.GetParam2() == 2)
295         { // delete
296           if (m_filter.size())
297             m_filter = m_filter.Left(m_filter.size() - 1);
298         }
299         else
300           m_filter = message.GetStringParam();
301         OnFilterItems();
302         return true;
303       }
304       if (message.GetParam1() == GUI_MSG_SEARCH_UPDATE && IsActive())
305       {
306         // search updated - reset timer
307         m_searchTimer.StartZero();
308         m_search = message.GetStringParam();
309       }
310     }
311   }
312   return CGUIWindowMusicBase::OnMessage(message);
313 }
314
315 CStdString CGUIWindowMusicNav::GetQuickpathName(const CStdString& strPath) const
316 {
317   if (strPath.Equals("musicdb://1/"))
318     return "Genres";
319   else if (strPath.Equals("musicdb://2/"))
320     return "Artists";
321   else if (strPath.Equals("musicdb://3/"))
322     return "Albums";
323   else if (strPath.Equals("musicdb://4/"))
324     return "Songs";
325   else if (strPath.Equals("musicdb://5/"))
326     return "Top100";
327   else if (strPath.Equals("musicdb://5/2/"))
328     return "Top100Songs";
329   else if (strPath.Equals("musicdb://5/1/"))
330     return "Top100Albums";
331   else if (strPath.Equals("musicdb://6/"))
332     return "RecentlyAddedAlbums";
333   else if (strPath.Equals("musicdb://7/"))
334     return "RecentlyPlayedAlbums";
335   else if (strPath.Equals("musicdb://8/"))
336     return "Compilations";
337   else if (strPath.Equals("musicdb://9/"))
338     return "Years";
339   else if (strPath.Equals("special://musicplaylists/"))
340     return "Playlists";
341   else
342   {
343     CLog::Log(LOGERROR, "  CGUIWindowMusicNav::GetQuickpathName: Unknown parameter (%s)", strPath.c_str());
344     return strPath;
345   }
346 }
347
348 bool CGUIWindowMusicNav::OnClick(int iItem)
349 {
350   if (iItem < 0 || iItem >= m_vecItems->Size()) return false;
351
352   CFileItemPtr item = m_vecItems->Get(iItem);
353   if (item->m_strPath.Left(14) == "musicsearch://")
354   {
355     if (m_searchWithEdit)
356       OnSearchUpdate();
357     else
358       CGUIDialogKeyboard::ShowAndGetFilter(m_search, true);
359     return true;
360   }
361   return CGUIWindowMusicBase::OnClick(iItem);
362 }
363
364 bool CGUIWindowMusicNav::GetDirectory(const CStdString &strDirectory, CFileItemList &items)
365 {
366   if (m_bDisplayEmptyDatabaseMessage)
367     return true;
368
369   if (strDirectory.IsEmpty())
370     AddSearchFolder();
371
372   if (m_thumbLoader.IsLoading())
373     m_thumbLoader.StopThread();
374
375   bool bResult = CGUIWindowMusicBase::GetDirectory(strDirectory, items);
376   if (bResult)
377   {
378     if (items.IsPlayList())
379       OnRetrieveMusicInfo(items);
380     if (!items.IsMusicDb())
381     {
382       items.SetCachedMusicThumbs();
383       m_thumbLoader.Load(*m_vecItems);
384     }
385   }
386
387   // update our content in the info manager
388   if (strDirectory.Left(10).Equals("videodb://"))
389   {
390     CVideoDatabaseDirectory dir;
391     VIDEODATABASEDIRECTORY::NODE_TYPE node = dir.GetDirectoryChildType(strDirectory);
392     if (node == VIDEODATABASEDIRECTORY::NODE_TYPE_TITLE_MUSICVIDEOS)
393       items.SetContent("musicvideos");
394   }
395   else if (items.GetContent().IsEmpty())
396   {
397     CMusicDatabaseDirectory dir;
398     NODE_TYPE node = dir.GetDirectoryChildType(strDirectory);
399     if (node == NODE_TYPE_ALBUM)
400       items.SetContent("albums");
401     else if (node == NODE_TYPE_ARTIST)
402       items.SetContent("artists");
403     else if (node == NODE_TYPE_SONG)
404       items.SetContent("songs");
405   }
406
407   // clear the filter
408   m_filter.Empty();
409   return bResult;
410 }
411
412 void CGUIWindowMusicNav::UpdateButtons()
413 {
414   CGUIWindowMusicBase::UpdateButtons();
415
416   // Update object count
417   int iItems = m_vecItems->Size();
418   if (iItems)
419   {
420     // check for parent dir and "all" items
421     // should always be the first two items
422     for (int i = 0; i <= (iItems>=2 ? 1 : 0); i++)
423     {
424       CFileItemPtr pItem = m_vecItems->Get(i);
425       if (pItem->IsParentFolder()) iItems--;
426       if (pItem->m_strPath.Left(4).Equals("/-1/")) iItems--;
427     }
428     // or the last item
429     if (m_vecItems->Size() > 2 &&
430       m_vecItems->Get(m_vecItems->Size()-1)->m_strPath.Left(4).Equals("/-1/"))
431       iItems--;
432   }
433   CStdString items;
434   items.Format("%i %s", iItems, g_localizeStrings.Get(127).c_str());
435   SET_CONTROL_LABEL(CONTROL_LABELFILES, items);
436
437   // set the filter label
438   CStdString strLabel;
439
440   // "Playlists"
441   if (m_vecItems->m_strPath.Equals("special://musicplaylists/"))
442     strLabel = g_localizeStrings.Get(136);
443   // "{Playlist Name}"
444   else if (m_vecItems->IsPlayList())
445   {
446     // get playlist name from path
447     CStdString strDummy;
448     CUtil::Split(m_vecItems->m_strPath, strDummy, strLabel);
449   }
450   // everything else is from a musicdb:// path
451   else
452   {
453     CMusicDatabaseDirectory dir;
454     dir.GetLabel(m_vecItems->m_strPath, strLabel);
455   }
456
457   SET_CONTROL_LABEL(CONTROL_FILTER, strLabel);
458
459   SET_CONTROL_SELECTED(GetID(),CONTROL_BTNPARTYMODE, g_partyModeManager.IsEnabled());
460
461   SET_CONTROL_SELECTED(GetID(),CONTROL_BTN_FILTER, !m_filter.IsEmpty());
462   SET_CONTROL_LABEL2(CONTROL_BTN_FILTER, m_filter);
463
464   if (m_searchWithEdit)
465   {
466     SendMessage(GUI_MSG_SET_TYPE, CONTROL_SEARCH, CGUIEditControl::INPUT_TYPE_SEARCH);
467     SET_CONTROL_LABEL2(CONTROL_SEARCH, m_search);
468   }
469 }
470
471 void CGUIWindowMusicNav::PlayItem(int iItem)
472 {
473   // unlike additemtoplaylist, we need to check the items here
474   // before calling it since the current playlist will be stopped
475   // and cleared!
476
477   // root is not allowed
478   if (m_vecItems->IsVirtualDirectoryRoot())
479     return;
480
481   CGUIWindowMusicBase::PlayItem(iItem);
482 }
483
484 void CGUIWindowMusicNav::OnWindowLoaded()
485 {
486 #ifdef PRE_SKIN_VERSION_2_1_COMPATIBILITY
487   const CGUIControl *pList = GetControl(CONTROL_LIST);
488   if (pList && !GetControl(CONTROL_LABELEMPTY))
489   {
490     CLabelInfo info;
491     info.align = XBFONT_CENTER_X | XBFONT_CENTER_Y;
492     info.font = g_fontManager.GetFont("font13");
493     info.textColor = 0xffffffff;
494     CGUILabelControl *pLabel = new CGUILabelControl(GetID(),CONTROL_LABELEMPTY,pList->GetXPosition(),pList->GetYPosition(),pList->GetWidth(),pList->GetHeight(),info,false,false);
495     pLabel->SetAnimations(pList->GetAnimations());
496     Add(pLabel);
497   }
498 #endif
499
500   const CGUIControl *control = GetControl(CONTROL_SEARCH);
501   m_searchWithEdit = (control && control->GetControlType() == CGUIControl::GUICONTROL_EDIT);
502
503   SendMessage(GUI_MSG_SET_TYPE, CONTROL_BTN_FILTER, CGUIEditControl::INPUT_TYPE_FILTER);
504   CGUIWindowMusicBase::OnWindowLoaded();
505 }
506
507 void CGUIWindowMusicNav::GetContextButtons(int itemNumber, CContextButtons &buttons)
508 {
509   CGUIWindowMusicBase::GetContextButtons(itemNumber, buttons);
510
511   CGUIDialogMusicScan *musicScan = (CGUIDialogMusicScan *)m_gWindowManager.GetWindow(WINDOW_DIALOG_MUSIC_SCAN);
512   CFileItemPtr item;
513   if (itemNumber >= 0 && itemNumber < m_vecItems->Size())
514     item = m_vecItems->Get(itemNumber);
515   if (item && (item->GetExtraInfo().Find("lastfm") < 0))
516   {
517     // are we in the playlists location?
518     bool inPlaylists = m_vecItems->m_strPath.Equals(CUtil::MusicPlaylistsLocation()) ||
519                        m_vecItems->m_strPath.Equals("special://musicplaylists/");
520
521     CMusicDatabaseDirectory dir;
522     SScraperInfo info;
523     m_musicdatabase.GetScraperForPath(item->m_strPath,info);
524     // enable music info button on an album or on a song.
525     if (item->IsAudio() && !item->IsPlayList() && !item->IsSmartPlayList() &&
526        !item->IsLastFM() && !item->IsShoutCast())
527     {
528       buttons.Add(CONTEXT_BUTTON_SONG_INFO, 658);
529     }
530     else if (item->IsVideoDb())
531     {
532       if (!item->m_bIsFolder) // music video
533        buttons.Add(CONTEXT_BUTTON_INFO, 20393);
534       if (item->m_strPath.Left(14).Equals("videodb://3/4/") &&
535           item->m_strPath.size() > 14 && item->m_bIsFolder)
536       {
537         long idArtist = m_musicdatabase.GetArtistByName(m_vecItems->Get(itemNumber)->GetLabel());
538         if (idArtist > - 1)
539           buttons.Add(CONTEXT_BUTTON_INFO,21891);
540       }
541     }
542     else if (!inPlaylists && (dir.HasAlbumInfo(item->m_strPath)||
543                               dir.IsArtistDir(item->m_strPath)   )      &&
544              !dir.IsAllItem(item->m_strPath) && !item->IsParentFolder() &&
545              !item->IsLastFM() && !item->IsShoutCast()                  &&
546              !item->m_strPath.Left(14).Equals("musicsearch://"))
547     {
548       if (dir.IsArtistDir(item->m_strPath))
549         buttons.Add(CONTEXT_BUTTON_INFO, 21891);
550       else
551         buttons.Add(CONTEXT_BUTTON_INFO, 13351);
552     }
553
554     // enable query all albums button only in album view
555     if (dir.HasAlbumInfo(item->m_strPath) && !dir.IsAllItem(item->m_strPath) &&
556         item->m_bIsFolder && !item->IsVideoDb() && !item->IsParentFolder()   &&
557        !item->IsLastFM() &&  !item->IsShoutCast()                            &&
558        !item->m_strPath.Left(14).Equals("musicsearch://"))
559     {
560       buttons.Add(CONTEXT_BUTTON_INFO_ALL, 20059);
561     }
562
563     // enable query all artist button only in album view
564     if (dir.IsArtistDir(item->m_strPath)        && !dir.IsAllItem(item->m_strPath) &&
565         item->m_bIsFolder && !item->IsVideoDb() && !info.strContent.IsEmpty())
566     {
567       buttons.Add(CONTEXT_BUTTON_INFO_ALL, 21884);
568     }
569
570     // turn off set artist image if not at artist listing.
571     if ((dir.IsArtistDir(item->m_strPath) && !dir.IsAllItem(item->m_strPath)) ||
572         (item->m_strPath.Left(14).Equals("videodb://3/4/") && item->m_strPath.size() > 14 && item->m_bIsFolder))
573     {
574       buttons.Add(CONTEXT_BUTTON_SET_ARTIST_THUMB, 13359);
575     }
576
577     if (m_vecItems->m_strPath.Equals("plugin://music/"))
578       buttons.Add(CONTEXT_BUTTON_SET_PLUGIN_THUMB, 1044);
579
580     //Set default or clear default
581     NODE_TYPE nodetype = dir.GetDirectoryType(item->m_strPath);
582     if (!item->IsParentFolder() && !inPlaylists &&
583         (nodetype == NODE_TYPE_ROOT     ||
584          nodetype == NODE_TYPE_OVERVIEW ||
585          nodetype == NODE_TYPE_TOP100))
586     {
587       if (!item->m_strPath.Equals(g_settings.m_defaultMusicLibSource))
588         buttons.Add(CONTEXT_BUTTON_SET_DEFAULT, 13335); // set default
589       if (strcmp(g_settings.m_defaultMusicLibSource, ""))
590         buttons.Add(CONTEXT_BUTTON_CLEAR_DEFAULT, 13403); // clear default
591     }
592     NODE_TYPE childtype = dir.GetDirectoryChildType(item->m_strPath);
593     if (childtype == NODE_TYPE_ALBUM || childtype == NODE_TYPE_ARTIST ||
594         nodetype == NODE_TYPE_GENRE  || nodetype == NODE_TYPE_ALBUM)
595     {
596       // we allow the user to set content for
597       // 1. general artist and album nodes
598       // 2. specific per genre
599       // 3. specific per artist
600       // 4. specific per album
601       buttons.Add(CONTEXT_BUTTON_SET_CONTENT,20195);
602     }
603     if (item->HasMusicInfoTag() && item->GetMusicInfoTag()->GetArtist().size() > 0)
604     {
605       CVideoDatabase database;
606       database.Open();
607       if (database.GetMatchingMusicVideo(item->GetMusicInfoTag()->GetArtist()) > -1)
608         buttons.Add(CONTEXT_BUTTON_GO_TO_ARTIST, 20400);
609     }
610     if (item->HasMusicInfoTag() && item->GetMusicInfoTag()->GetArtist().size() > 0 &&
611         item->GetMusicInfoTag()->GetAlbum().size() > 0 &&
612         item->GetMusicInfoTag()->GetTitle().size() > 0)
613     {
614       CVideoDatabase database;
615       database.Open();
616       if (database.GetMatchingMusicVideo(item->GetMusicInfoTag()->GetArtist(),item->GetMusicInfoTag()->GetAlbum(),item->GetMusicInfoTag()->GetTitle()) > -1)
617         buttons.Add(CONTEXT_BUTTON_PLAY_OTHER, 20401);
618     }
619     if (item->HasVideoInfoTag() && !item->m_bIsFolder)
620     {
621       if (item->GetVideoInfoTag()->m_playCount > 0)
622         buttons.Add(CONTEXT_BUTTON_MARK_UNWATCHED, 16104); //Mark as UnWatched
623       else
624         buttons.Add(CONTEXT_BUTTON_MARK_WATCHED, 16103);   //Mark as Watched
625       if (g_settings.m_vecProfiles[g_settings.m_iLastLoadedProfileIndex].canWriteDatabases()
626        || g_passwordManager.bMasterUser)
627       {
628         buttons.Add(CONTEXT_BUTTON_RENAME, 16105);
629         buttons.Add(CONTEXT_BUTTON_DELETE, 646);
630       }
631     }
632   }
633   // noncontextual buttons
634
635   if (musicScan && musicScan->IsScanning())
636     buttons.Add(CONTEXT_BUTTON_STOP_SCANNING, 13353);     // Stop Scanning
637   else if (musicScan)
638     buttons.Add(CONTEXT_BUTTON_UPDATE_LIBRARY, 653);
639
640   CGUIWindowMusicBase::GetNonContextButtons(buttons);
641 }
642
643 bool CGUIWindowMusicNav::OnContextButton(int itemNumber, CONTEXT_BUTTON button)
644 {
645   CFileItemPtr item;
646   if (itemNumber >= 0 && itemNumber < m_vecItems->Size())
647     item = m_vecItems->Get(itemNumber);
648
649   switch (button)
650   {
651   case CONTEXT_BUTTON_INFO:
652     {
653       if (!item->IsVideoDb())
654         return CGUIWindowMusicBase::OnContextButton(itemNumber,button);
655       if (item->m_strPath.Left(14).Equals("videodb://3/4/"))
656       {
657         long idArtist = m_musicdatabase.GetArtistByName(item->GetLabel());
658         if (idArtist == -1)
659           return false;
660         item->m_strPath.Format("musicdb://2/%ld/", m_musicdatabase.GetArtistByName(item->GetLabel()));
661         CGUIWindowMusicBase::OnContextButton(itemNumber,button);
662         Update(m_vecItems->m_strPath);
663         m_viewControl.SetSelectedItem(itemNumber);
664         return true;
665       }
666       CGUIWindowVideoNav* pWindow = (CGUIWindowVideoNav*)m_gWindowManager.GetWindow(WINDOW_VIDEO_NAV);
667       if (pWindow)
668       {
669         SScraperInfo info;
670         pWindow->OnInfo(item.get(),info);
671         Update(m_vecItems->m_strPath);
672       }
673       return true;
674     }
675
676   case CONTEXT_BUTTON_INFO_ALL:
677     OnInfoAll(itemNumber);
678     return true;
679
680   case CONTEXT_BUTTON_SET_ARTIST_THUMB:
681   case CONTEXT_BUTTON_SET_PLUGIN_THUMB:
682     SetThumb(itemNumber, button);
683     return true;
684
685   case CONTEXT_BUTTON_UPDATE_LIBRARY:
686     {
687       CGUIDialogMusicScan *scanner = (CGUIDialogMusicScan *)m_gWindowManager.GetWindow(WINDOW_DIALOG_MUSIC_SCAN);
688       if (scanner)
689         scanner->StartScanning("");
690       return true;
691     }
692
693   case CONTEXT_BUTTON_SET_DEFAULT:
694     g_settings.m_defaultMusicLibSource = GetQuickpathName(item->m_strPath);
695     g_settings.Save();
696     return true;
697
698   case CONTEXT_BUTTON_CLEAR_DEFAULT:
699     g_settings.m_defaultMusicLibSource.Empty();
700     g_settings.Save();
701     return true;
702
703   case CONTEXT_BUTTON_GO_TO_ARTIST:
704     {
705       CStdString strPath;
706       CVideoDatabase database;
707       database.Open();
708       strPath.Format("videodb://3/4/%ld/",database.GetMatchingMusicVideo(item->GetMusicInfoTag()->GetArtist()));
709       m_gWindowManager.ActivateWindow(WINDOW_VIDEO_NAV,strPath);
710       return true;
711     }
712
713   case CONTEXT_BUTTON_PLAY_OTHER:
714     {
715       CVideoDatabase database;
716       database.Open();
717       CVideoInfoTag details;
718       database.GetMusicVideoInfo("",details,database.GetMatchingMusicVideo(item->GetMusicInfoTag()->GetArtist(),item->GetMusicInfoTag()->GetAlbum(),item->GetMusicInfoTag()->GetTitle()));
719       g_applicationMessenger.PlayFile(CFileItem(details));
720       return true;
721     }
722
723   case CONTEXT_BUTTON_MARK_WATCHED:
724     CGUIWindowVideoBase::MarkWatched(item);
725     CUtil::DeleteVideoDatabaseDirectoryCache();
726     Update(m_vecItems->m_strPath);
727     return true;
728
729   case CONTEXT_BUTTON_MARK_UNWATCHED:
730     CGUIWindowVideoBase::MarkUnWatched(item);
731     CUtil::DeleteVideoDatabaseDirectoryCache();
732     Update(m_vecItems->m_strPath);
733     return true;
734
735   case CONTEXT_BUTTON_RENAME:
736     CGUIWindowVideoBase::UpdateVideoTitle(item.get());
737     CUtil::DeleteVideoDatabaseDirectoryCache();
738     Update(m_vecItems->m_strPath);
739     return true;
740
741   case CONTEXT_BUTTON_DELETE:
742     CGUIWindowVideoNav::DeleteItem(item.get());
743     CUtil::DeleteVideoDatabaseDirectoryCache();
744     Update(m_vecItems->m_strPath);
745     return true;
746
747   case CONTEXT_BUTTON_SET_CONTENT:
748     {
749       bool bScan=false;
750       SScraperInfo info;
751       if (!m_musicdatabase.GetScraperForPath(item->m_strPath,info))
752         info.strContent = "albums";
753
754       int iLabel=132;
755       // per genre or for all artists
756       if (m_vecItems->m_strPath.Equals("musicdb://1/") || item->m_strPath.Equals("musicdb://2/"))
757       {
758         iLabel = 133;
759       }
760
761       if (CGUIDialogContentSettings::Show(info, bScan,iLabel))
762       {
763         m_musicdatabase.SetScraperForPath(item->m_strPath,info);
764         if (bScan)
765           OnInfoAll(itemNumber,true);
766       }
767       return true;
768     }
769
770   default:
771     break;
772   }
773
774   return CGUIWindowMusicBase::OnContextButton(itemNumber, button);
775 }
776
777 void CGUIWindowMusicNav::SetThumb(int iItem, CONTEXT_BUTTON button)
778 {
779   CFileItemPtr pItem = m_vecItems->Get(iItem);
780   CFileItemList items;
781   CStdString picturePath;
782   CStdString strPath=pItem->m_strPath;
783   CStdString strThumb;
784   CStdString cachedThumb;
785
786   if (button == CONTEXT_BUTTON_SET_ARTIST_THUMB)
787   {
788     long idArtist = -1;
789     if (pItem->IsMusicDb())
790     {
791       CUtil::RemoveSlashAtEnd(strPath);
792       int nPos=strPath.ReverseFind("/");
793       if (nPos>-1)
794       {
795         //  try to guess where the user should start
796         //  browsing for the artist thumb
797         idArtist=atol(strPath.Mid(nPos+1));
798       }
799     }
800     else if (pItem->IsVideoDb())
801       idArtist = m_musicdatabase.GetArtistByName(pItem->GetLabel());
802
803     m_musicdatabase.GetArtistPath(idArtist, picturePath);
804
805     cachedThumb = pItem->GetCachedArtistThumb();
806
807     CArtist artist;
808     m_musicdatabase.GetArtistInfo(idArtist,artist);
809     int i=1;
810     for (std::vector<CScraperUrl::SUrlEntry>::iterator iter=artist.thumbURL.m_url.begin();iter != artist.thumbURL.m_url.end();++iter)
811     {
812       CStdString thumbFromWeb;
813       CStdString strLabel;
814       strLabel.Format("allmusicthumb%i.jpg",i);
815       CUtil::AddFileToFolder("z:\\", strLabel, thumbFromWeb);
816       if (CScraperUrl::DownloadThumbnail(thumbFromWeb,*iter))
817       {
818         CStdString strItemPath;
819         strItemPath.Format("thumb://Remote%i",i++);
820         CFileItemPtr item(new CFileItem(strItemPath, false));
821         item->SetThumbnailImage(thumbFromWeb);
822         CStdString strLabel;
823         item->SetLabel(g_localizeStrings.Get(20015));
824         items.Add(item);
825       }
826     }
827   }
828   else
829   {
830     strPath = m_vecItems->Get(iItem)->m_strPath;
831     strPath.Replace("plugin://music/","Q:\\plugins\\music\\");
832     strPath.Replace("/","\\");
833     picturePath = strPath;
834     CFileItem item(strPath,true);
835     cachedThumb = item.GetCachedProgramThumb();
836   }
837
838   if (XFILE::CFile::Exists(cachedThumb))
839   {
840     CFileItemPtr item(new CFileItem("thumb://Current", false));
841     item->SetThumbnailImage(cachedThumb);
842     item->SetLabel(g_localizeStrings.Get(20016));
843     items.Add(item);
844   }
845
846   if (button == CONTEXT_BUTTON_SET_PLUGIN_THUMB)
847   {
848     if (items.Size() == 0)
849     {
850       CFileItem item2(strPath,false);
851       CUtil::AddFileToFolder(strPath,"default.py",item2.m_strPath);
852       if (XFILE::CFile::Exists(item2.GetCachedProgramThumb()))
853       {
854         CFileItemPtr item(new CFileItem("thumb://Current", false));
855         item->SetThumbnailImage(item2.GetCachedProgramThumb());
856         item->SetLabel(g_localizeStrings.Get(20016));
857         items.Add(item);
858       }
859     }
860
861     CUtil::AddFileToFolder(strPath,"default.tbn",strThumb);
862     if (XFILE::CFile::Exists(strThumb))
863     {
864       CFileItemPtr item(new CFileItem(strThumb,false));
865       item->SetThumbnailImage(strThumb);
866       item->SetLabel(g_localizeStrings.Get(20017));
867       items.Add(item);
868     }
869   }
870
871   CUtil::AddFileToFolder(picturePath,"folder.jpg",strThumb);
872   if (XFILE::CFile::Exists(strThumb))
873   {
874     CFileItemPtr pItem(new CFileItem(strThumb,false));
875     pItem->SetLabel(g_localizeStrings.Get(20017));
876     pItem->SetThumbnailImage(strThumb);
877     items.Add(pItem);
878   }
879
880   CFileItemPtr nItem(new CFileItem("thumb://None",false));
881   nItem->SetLabel(g_localizeStrings.Get(20018));
882   if (button == CONTEXT_BUTTON_SET_ARTIST_THUMB)
883     nItem->SetThumbnailImage("DefaultArtistBig.png");
884   else
885     nItem->SetThumbnailImage("DefaultFolderBig.png");
886   items.Add(nItem);
887
888   if (CGUIDialogFileBrowser::ShowAndGetImage(items, g_settings.m_musicSources,
889                                              g_localizeStrings.Get(20019), picturePath))
890   {
891     CPicture picture;
892     if (picturePath.Equals("thumb://Current"))
893       return;
894
895     if (picturePath.Equals("thumb://None"))
896     {
897       XFILE::CFile::Delete(cachedThumb);
898       if (button == CONTEXT_BUTTON_SET_PLUGIN_THUMB)
899       {
900         CPicture picture;
901         picture.CacheSkinImage("DefaultFolderBig.png",cachedThumb);
902         CFileItem item2(strPath,false);
903         CUtil::AddFileToFolder(strPath,"default.py",item2.m_strPath);
904         XFILE::CFile::Delete(item2.GetCachedProgramThumb());
905       }
906     }
907     else if (button == CONTEXT_BUTTON_SET_PLUGIN_THUMB)
908       XFILE::CFile::Cache(picturePath,cachedThumb);
909
910     if (!picturePath.Equals("thumb://None") && picturePath.Left(8).Equals("thumb://") && items.Get(picturePath))
911       picturePath = items.Get(picturePath)->GetThumbnailImage();
912
913     if (picturePath.Equals("thumb://None") ||
914         picture.DoCreateThumbnail(picturePath, cachedThumb))
915     {
916       CMusicDatabaseDirectory dir;
917       dir.ClearDirectoryCache(m_vecItems->m_strPath);
918       CGUIMessage msg(GUI_MSG_NOTIFY_ALL, 0, 0, GUI_MSG_REFRESH_THUMBS);
919       g_graphicsContext.SendMessage(msg);
920       Update(m_vecItems->m_strPath);
921     }
922     else
923       CLog::Log(LOGERROR, " %s Could not cache artist/plugin thumb: %s", __FUNCTION__, picturePath.c_str());
924   }
925 }
926
927 bool CGUIWindowMusicNav::GetSongsFromPlayList(const CStdString& strPlayList, CFileItemList &items)
928 {
929   CStdString strParentPath=m_history.GetParentPath();
930
931   if (m_guiState.get() && !m_guiState->HideParentDirItems())
932   {
933     CFileItemPtr pItem(new CFileItem(".."));
934     pItem->m_strPath = strParentPath;
935     items.Add(pItem);
936   }
937
938   items.m_strPath=strPlayList;
939   CLog::Log(LOGDEBUG,"CGUIWindowMusicNav, opening playlist [%s]", strPlayList.c_str());
940
941   auto_ptr<CPlayList> pPlayList (CPlayListFactory::Create(strPlayList));
942   if ( NULL != pPlayList.get())
943   {
944     // load it
945     if (!pPlayList->Load(strPlayList))
946     {
947       CGUIDialogOK::ShowAndGetInput(6, 0, 477, 0);
948       return false; //hmmm unable to load playlist?
949     }
950     CPlayList playlist = *pPlayList;
951     // convert playlist items to songs
952     for (int i = 0; i < (int)playlist.size(); ++i)
953     {
954       items.Add(playlist[i]);
955     }
956   }
957
958   return true;
959 }
960
961 void CGUIWindowMusicNav::DisplayEmptyDatabaseMessage(bool bDisplay)
962 {
963   m_bDisplayEmptyDatabaseMessage = bDisplay;
964 }
965
966 void CGUIWindowMusicNav::OnSearchUpdate()
967 {
968   CStdString search(m_search);
969   CUtil::URLEncode(search);
970   if (!search.IsEmpty())
971   {
972     CStdString path = "musicsearch://" + search + "/";
973     m_history.ClearPathHistory();
974     Update(path);
975   }
976   else if (m_vecItems->IsVirtualDirectoryRoot())
977   {
978     Update("");
979   }
980 }
981
982 void CGUIWindowMusicNav::Render()
983 {
984   // update our searching
985   if (m_searchTimer.IsRunning() && m_searchTimer.GetElapsedMilliseconds() > 1000)
986   {
987     OnSearchUpdate();
988     m_searchTimer.Stop();
989   }
990   if (m_bDisplayEmptyDatabaseMessage)
991     SET_CONTROL_LABEL(CONTROL_LABELEMPTY,g_localizeStrings.Get(745)+'\n'+g_localizeStrings.Get(746));
992   else
993     SET_CONTROL_LABEL(CONTROL_LABELEMPTY,"");
994   CGUIWindowMusicBase::Render();
995 }
996
997 void CGUIWindowMusicNav::ClearFileItems()
998 {
999   m_viewControl.Clear();
1000   m_vecItems->Clear();
1001   m_unfilteredItems->Clear();
1002 }
1003
1004 void CGUIWindowMusicNav::OnFilterItems()
1005 {
1006   CStdString currentItem;
1007   int item = m_viewControl.GetSelectedItem();
1008   if (item >= 0)
1009     currentItem = m_vecItems->Get(item)->m_strPath;
1010
1011   m_viewControl.Clear();
1012
1013   FilterItems(*m_vecItems);
1014
1015   // and update our view control + buttons
1016   m_viewControl.SetItems(*m_vecItems);
1017   m_viewControl.SetSelectedItem(currentItem);
1018   UpdateButtons();
1019 }
1020
1021 void CGUIWindowMusicNav::FilterItems(CFileItemList &items)
1022 {
1023   if (m_vecItems->IsVirtualDirectoryRoot())
1024     return;
1025
1026   items.ClearItems();
1027
1028   CStdString filter(m_filter);
1029   filter.TrimLeft().ToLower();
1030   bool numericMatch = StringUtils::IsNaturalNumber(filter);
1031
1032   for (int i = 0; i < m_unfilteredItems->Size(); i++)
1033   {
1034     CFileItemPtr item = m_unfilteredItems->Get(i);
1035     if (item->IsParentFolder() || filter.IsEmpty() ||
1036         CMusicDatabaseDirectory::IsAllItem(item->m_strPath))
1037     {
1038       items.Add(item);
1039       continue;
1040     }
1041     // TODO: Need to update this to get all labels, ideally out of the displayed info (ie from m_layout and m_focusedLayout)
1042     // though that isn't practical.  Perhaps a better idea would be to just grab the info that we should filter on based on
1043     // where we are in the library tree.
1044     // Another idea is tying the filter string to the current level of the tree, so that going deeper disables the filter,
1045     // but it's re-enabled on the way back out.
1046     CStdString match;
1047 /*    if (item->GetFocusedLayout())
1048       match = item->GetFocusedLayout()->GetAllText();
1049     else if (item->GetLayout())
1050       match = item->GetLayout()->GetAllText();
1051     else*/
1052     match = item->GetLabel();
1053
1054     if (numericMatch)
1055       StringUtils::WordToDigits(match);
1056     size_t pos = StringUtils::FindWords(match.c_str(), filter.c_str());
1057
1058     if (pos != CStdString::npos)
1059       items.Add(item);
1060   }
1061 }
1062
1063 void CGUIWindowMusicNav::OnFinalizeFileItems(CFileItemList &items)
1064 {
1065   CGUIMediaWindow::OnFinalizeFileItems(items);
1066   m_unfilteredItems->Append(items);
1067   // now filter as necessary
1068   if (!m_filter.IsEmpty())
1069     FilterItems(items);
1070 }
1071
1072 void CGUIWindowMusicNav::AddSearchFolder()
1073 {
1074   if (m_guiState.get())
1075   {
1076     // add our remove the musicsearch source
1077     VECSOURCES &sources = m_guiState->GetSources();
1078     bool haveSearchSource = false;
1079     bool needSearchSource = !m_search.IsEmpty() || !m_searchWithEdit; // we always need it if we don't have the edit control
1080     for (IVECSOURCES it = sources.begin(); it != sources.end(); ++it)
1081     {
1082       CMediaSource& share = *it;
1083       if (share.strPath == "musicsearch://")
1084       {
1085         haveSearchSource = true;
1086         if (!needSearchSource)
1087         { // remove it
1088           sources.erase(it);
1089           break;
1090         }
1091       }
1092     }
1093     if (!haveSearchSource && needSearchSource)
1094     {
1095       // add earch share
1096       CMediaSource share;
1097       share.strName=g_localizeStrings.Get(137); // Search
1098       share.strPath = "musicsearch://";
1099       share.m_strThumbnailImage="defaultFolderBig.png";
1100       share.m_iDriveType = CMediaSource::SOURCE_TYPE_LOCAL;
1101       sources.push_back(share);
1102     }
1103     m_rootDir.SetSources(sources);
1104   }
1105 }