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