added: initial work on mixed mode smart playlists
[xbmc:paulepanters-xbmc.git] / xbmc / GUIWindowMusicBase.cpp
1 /*
2  *      Copyright (C) 2005-2007 Team XboxMediaCenter
3  *      http://www.xboxmediacenter.com
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 GNU Make; 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 "GUIWindowMusicBase.h"
24 #include "GUIWindowMusicInfo.h"
25 #include "FileSystem/ZipManager.h"
26 #ifdef HAS_FILESYSTEM
27 #include "FileSystem/DAAPDirectory.h"
28 #endif
29 #include "PlayListFactory.h"
30 #include "Util.h"
31 #include "PlayListM3U.h"
32 #include "Application.h"
33 #include "PlayListPlayer.h"
34 #include "FileSystem/DirectoryCache.h"
35 #ifdef HAS_CDDA_RIPPER
36 #include "CDRip/CDDARipper.h"
37 #endif
38 #include "GUIPassword.h"
39 #include "GUIDialogMusicScan.h"
40 #include "GUIDialogMediaSource.h"
41 #include "PartyModeManager.h"
42 #include "utils/GUIInfoManager.h"
43 #include "filesystem/MusicDatabaseDirectory.h"
44 #include "GUIDialogSongInfo.h"
45 #include "GUIDialogSmartPlaylistEditor.h"
46
47 using namespace XFILE;
48 using namespace DIRECTORY;
49 using namespace PLAYLIST;
50 using namespace MUSIC_GRABBER;
51
52 #define CONTROL_BTNVIEWASICONS  2
53 #define CONTROL_BTNSORTBY       3
54 #define CONTROL_BTNSORTASC      4
55 #define CONTROL_BTNTYPE         5
56 #define CONTROL_LIST            50
57 #define CONTROL_THUMBS          51
58 #define CONTROL_BIGLIST         52
59
60 CGUIWindowMusicBase::CGUIWindowMusicBase(DWORD dwID, const CStdString &xmlFile)
61     : CGUIMediaWindow(dwID, xmlFile)
62 {
63
64 }
65
66 CGUIWindowMusicBase::~CGUIWindowMusicBase ()
67 {
68 }
69
70 /// \brief Handle actions on window.
71 /// \param action Action that can be reacted on.
72 bool CGUIWindowMusicBase::OnAction(const CAction& action)
73 {
74   if (action.wID == ACTION_PREVIOUS_MENU)
75   {
76     CGUIDialogMusicScan *musicScan = (CGUIDialogMusicScan *)m_gWindowManager.GetWindow(WINDOW_DIALOG_MUSIC_SCAN);
77     if (musicScan && !musicScan->IsDialogRunning())
78     {
79       CUtil::ThumbCacheClear();
80       CUtil::RemoveTempFiles();
81     }
82   }
83
84   if (action.wID == ACTION_SHOW_PLAYLIST)
85   {
86     m_gWindowManager.ActivateWindow(WINDOW_MUSIC_PLAYLIST);
87     return true;
88   }
89
90   return CGUIMediaWindow::OnAction(action);
91 }
92
93 /*!
94  \brief Handle messages on window.
95  \param message GUI Message that can be reacted on.
96  \return if a message can't be processed, return \e false
97
98  On these messages this class reacts.\n
99  When retrieving...
100   - #GUI_MSG_WINDOW_DEINIT\n
101    ...the last focused control is saved to m_iLastControl.
102   - #GUI_MSG_WINDOW_INIT\n
103    ...the musicdatabase is opend and the music extensions and shares are set.
104    The last focused control is set.
105   - #GUI_MSG_CLICKED\n
106    ... the base class reacts on the following controls:\n
107     Buttons:\n
108     - #CONTROL_BTNVIEWASICONS - switch between list, thumb and with large items
109     - #CONTROL_BTNTYPE - switch between music windows
110     - #CONTROL_BTNSEARCH - Search for items\n
111     Other Controls:
112     - #CONTROL_LIST and #CONTROL_THUMB\n
113      Have the following actions in message them clicking on them.
114      - #ACTION_QUEUE_ITEM - add selected item to playlist
115      - #ACTION_SHOW_INFO - retrieve album info from the internet
116      - #ACTION_SELECT_ITEM - Item has been selected. Overwrite OnClick() to react on it
117  */
118 bool CGUIWindowMusicBase::OnMessage(CGUIMessage& message)
119 {
120   switch ( message.GetMessage() )
121   {
122   case GUI_MSG_WINDOW_DEINIT:
123     {
124       m_musicdatabase.Close();
125     }
126     break;
127
128   case GUI_MSG_WINDOW_INIT:
129     {
130       m_dlgProgress = (CGUIDialogProgress*)m_gWindowManager.GetWindow(WINDOW_DIALOG_PROGRESS);
131
132       m_musicdatabase.Open();
133
134       if (!CGUIMediaWindow::OnMessage(message))
135         return false;
136
137       // save current window, unless the current window is the music playlist window
138       if (GetID() != WINDOW_MUSIC_PLAYLIST && g_stSettings.m_iMyMusicStartWindow != GetID())
139       {
140         g_stSettings.m_iMyMusicStartWindow = GetID();
141         g_settings.Save();
142       }
143
144       return true;
145     }
146     break;
147
148     // update the display
149     case GUI_MSG_SCAN_FINISHED:
150     case GUI_MSG_REFRESH_THUMBS:
151     {
152       Update(m_vecItems.m_strPath);
153     }
154     break;
155
156   case GUI_MSG_CLICKED:
157     {
158       int iControl = message.GetSenderId();
159       if (iControl == CONTROL_BTNTYPE)
160       {
161         CGUIMessage msg(GUI_MSG_ITEM_SELECTED, GetID(), CONTROL_BTNTYPE);
162         m_gWindowManager.SendMessage(msg);
163
164         int nWindow = WINDOW_MUSIC_FILES + msg.GetParam1();
165
166         if (nWindow == GetID())
167           return true;
168
169         g_stSettings.m_iMyMusicStartWindow = nWindow;
170         g_settings.Save();
171         m_gWindowManager.ChangeActiveWindow(nWindow);
172
173         CGUIMessage msg2(GUI_MSG_SETFOCUS, g_stSettings.m_iMyMusicStartWindow, CONTROL_BTNTYPE);
174         g_graphicsContext.SendMessage(msg2);
175
176         return true;
177       }
178       else if (m_viewControl.HasControl(iControl))  // list/thumb control
179       {
180         int iItem = m_viewControl.GetSelectedItem();
181         int iAction = message.GetParam1();
182
183         // iItem is checked for validity inside these routines
184         if (iAction == ACTION_QUEUE_ITEM || iAction == ACTION_MOUSE_MIDDLE_CLICK)
185         {
186           OnQueueItem(iItem);
187         }
188         else if (iAction == ACTION_SHOW_INFO)
189         {
190           OnInfo(iItem);
191         }
192         else if (iAction == ACTION_DELETE_ITEM)
193         {
194           // is delete allowed?
195           // must be at the playlists directory
196           if (m_vecItems.m_strPath.Equals("special://musicplaylists/"))
197             OnDeleteItem(iItem);
198
199           // or be at the files window and have file deletion enabled
200           else if (GetID() == WINDOW_MUSIC_FILES && g_guiSettings.GetBool("filelists.allowfiledeletion"))
201             OnDeleteItem(iItem);
202
203           else
204             return false;
205         }
206         // use play button to add folders of items to temp playlist
207         else if (iAction == ACTION_PLAYER_PLAY)
208         {
209           // if playback is paused or playback speed != 1, return
210           if (g_application.IsPlayingAudio())
211           {
212             if (g_application.m_pPlayer->IsPaused()) return false;
213             if (g_application.GetPlaySpeed() != 1) return false;
214           }
215
216           // not playing audio, or playback speed == 1
217           PlayItem(iItem);
218           return true;
219         }
220       }
221     }
222   }
223   return CGUIMediaWindow::OnMessage(message);
224 }
225
226 void CGUIWindowMusicBase::OnInfoAll(int iItem)
227 {
228   if (m_dlgProgress)
229   {
230     m_dlgProgress->SetHeading(20097);
231     m_dlgProgress->StartModal();
232   }
233   // Update object count
234   int numAlbums = m_vecItems.Size();
235   int iSkipped = 0;
236   if (numAlbums)
237   {
238     // check for parent dir
239     // check for "all" items
240     // they should always be the first two items
241     for (int i = 0; i <= (numAlbums>=2 ? 1 : 0); i++)
242     {
243       CFileItem* pItem = m_vecItems[i];
244       if (
245         pItem->IsParentFolder() || /* parent folder */
246         pItem->GetLabel().Equals(g_localizeStrings.Get(15102)) ||  /* all albums  */
247         pItem->GetLabel().Equals(g_localizeStrings.Get(15103)) ||  /* all artists */
248         pItem->GetLabel().Equals(g_localizeStrings.Get(15104)) ||  /* all songs   */
249         pItem->GetLabel().Equals(g_localizeStrings.Get(15105))     /* all genres  */
250         )
251       {
252         numAlbums--;
253         iSkipped++;
254       }
255     }
256
257     for (int i = 0; i < (int)numAlbums; i++)
258     {
259       CStdString strLine;
260       strLine.Format("%s (%i of %i)", g_localizeStrings.Get(20098).c_str(), i + 1, numAlbums);
261       m_dlgProgress->SetLine(0, strLine);
262       m_dlgProgress->SetLine(1, m_vecItems[i + iSkipped]->GetLabel());
263       // m_dlgProgress->SetLine(1, m_vecItems[i]->GetLabel());
264       m_dlgProgress->SetLine(2, "");
265       m_dlgProgress->Progress();
266       // OnInfo(i,false);
267       OnInfo(i + iSkipped, false);
268       CGUIMessage msg(GUI_MSG_REFRESH_THUMBS,0,0);
269       g_graphicsContext.SendMessage(msg);
270       if (m_dlgProgress->IsCanceled())
271         break;
272     }
273   }
274   m_dlgProgress->Close();
275 }
276
277 /// \brief Retrieves music info for albums from allmusic.com and displays them in CGUIWindowMusicInfo
278 /// \param iItem Item in list/thumb control
279 void CGUIWindowMusicBase::OnInfo(int iItem, bool bShowInfo)
280 {
281   if ( iItem < 0 || iItem >= m_vecItems.Size() ) return ;
282   OnInfo(m_vecItems[iItem], bShowInfo);
283 }
284
285 void CGUIWindowMusicBase::OnInfo(CFileItem *pItem, bool bShowInfo)
286 {
287   if (pItem->m_bIsFolder && pItem->IsParentFolder()) return ;
288
289   // show dialog box indicating we're searching the album name
290   if (m_dlgProgress && bShowInfo)
291   {
292     m_dlgProgress->SetHeading(185);
293     m_dlgProgress->SetLine(0, 501);
294     m_dlgProgress->SetLine(1, "");
295     m_dlgProgress->SetLine(2, "");
296     m_dlgProgress->StartModal();
297     m_dlgProgress->Progress();
298     if (m_dlgProgress->IsCanceled()) return ;
299   }
300
301   CStdString strPath;
302   if (pItem->m_bIsFolder)
303     strPath = pItem->m_strPath;
304   else
305   {
306     CUtil::GetDirectory(pItem->m_strPath, strPath);
307     CUtil::AddSlashAtEnd(strPath);
308   }
309
310   // Try to find an album to lookup from the current item
311   CAlbum album;
312   if (pItem->IsMusicDb())
313   {
314     if (pItem->m_bIsFolder)
315     { // Look up is done on an album so grab the albumid
316       DIRECTORY::MUSICDATABASEDIRECTORY::CQueryParams params;
317       DIRECTORY::MUSICDATABASEDIRECTORY::CDirectoryNode::GetDatabaseInfo(pItem->m_strPath, params);
318       album.idAlbum = params.GetAlbumId();
319       album.strAlbum = pItem->GetMusicInfoTag()->GetAlbum();
320       album.strArtist = pItem->GetMusicInfoTag()->GetArtist();
321     }
322     else
323     { // Lookup is done on a song - grab the album from the database
324       DIRECTORY::MUSICDATABASEDIRECTORY::CQueryParams params;
325       DIRECTORY::MUSICDATABASEDIRECTORY::CDirectoryNode::GetDatabaseInfo(pItem->m_strPath, params);
326       m_musicdatabase.GetAlbumFromSong(params.GetSongId(), album);
327     }
328     // we're going to need it's path as well (we assume that there's only one) - this is for
329     // assigning thumbs to folders, and obtaining the local folder.jpg
330     m_musicdatabase.GetAlbumPath(album.idAlbum, strPath);
331   }
332   else if (pItem->m_bIsFolder)
333   { // lookup is done on a folder - find the albums in the folder
334     CFileItemList items;
335     GetDirectory(strPath, items);
336     OnRetrieveMusicInfo(items);
337
338     // check the first song we find in the folder, and grab it's album info
339     bool foundAlbum(false);
340     for (int i = 0; i < items.Size() && !foundAlbum; i++)
341     {
342       CFileItem* pItem = items[i];
343       if (pItem->HasMusicInfoTag() && pItem->GetMusicInfoTag()->Loaded() && !pItem->GetMusicInfoTag()->GetAlbum().IsEmpty())
344       {
345         // great, have a song - use it.
346         CSong song(*pItem->GetMusicInfoTag());
347         // this function won't be needed if/when the tag has idSong information
348         if (!m_musicdatabase.GetAlbumFromSong(song, album))
349         { // album isn't in the database - construct it from the tag info we have
350           CMusicInfoTag *tag = pItem->GetMusicInfoTag();
351           album.strAlbum = tag->GetAlbum();
352           album.strArtist = tag->GetAlbumArtist().IsEmpty() ? tag->GetArtist() : tag->GetAlbumArtist();
353           album.idAlbum = -1; // the -1 indicates it's not in the database
354         }
355         foundAlbum = true;
356       }
357     }
358     if (!foundAlbum)
359     {
360       CLog::Log(LOGINFO, __FUNCTION__" called on a folder containing no songs with tag info - nothing can be done");
361       if (m_dlgProgress && bShowInfo) m_dlgProgress->Close();
362       return;
363     }
364   }
365   else
366   { // lookup is done on a file - can only do useful stuff if we have a tag
367     if (!pItem->LoadMusicTag() || !pItem->GetMusicInfoTag()->Loaded())
368     {
369       CLog::Log(LOGINFO, __FUNCTION__" called on a file without tag info - nothing can be done");
370       if (m_dlgProgress && bShowInfo) m_dlgProgress->Close();
371       return;
372     }
373     // lookup the song in the database and get it's album info
374     CSong song(*pItem->GetMusicInfoTag());
375     if (!m_musicdatabase.GetAlbumFromSong(song, album))
376     { // album isn't in the database - construct it from the tag info we have
377       CMusicInfoTag *tag = pItem->GetMusicInfoTag();
378       album.strAlbum = tag->GetAlbum();
379       album.strArtist = tag->GetAlbumArtist().IsEmpty() ? tag->GetArtist() : tag->GetAlbumArtist();
380       album.idAlbum = -1; // the -1 indicates it's not in the database
381     }
382   }
383
384   if (m_dlgProgress && bShowInfo) m_dlgProgress->Close();
385
386   ShowAlbumInfo(album, strPath, false, bShowInfo);
387 }
388
389 void CGUIWindowMusicBase::OnManualAlbumInfo()
390 {
391   CAlbum album;
392   album.idAlbum = -1; // not in the db
393   if (!CGUIDialogKeyboard::ShowAndGetInput(album.strAlbum, g_localizeStrings.Get(16011), false)) return;
394
395   CStdString strNewArtist = "";
396   if (!CGUIDialogKeyboard::ShowAndGetInput(album.strArtist, g_localizeStrings.Get(16025), false)) return;
397   ShowAlbumInfo(album,"",true);
398 }
399
400 void CGUIWindowMusicBase::ShowAlbumInfo(const CAlbum& album, const CStdString& path, bool bRefresh, bool bShowInfo)
401 {
402   bool saveDb = album.idAlbum != -1;
403   if (!g_settings.m_vecProfiles[g_settings.m_iLastLoadedProfileIndex].canWriteDatabases() && !g_passwordManager.bMasterUser)
404     saveDb = false;
405
406   // check cache
407   CAlbum albumInfo;
408   VECSONGS albumSongs;
409   if (!bRefresh && m_musicdatabase.GetAlbumInfo(album.idAlbum, albumInfo, albumSongs))
410   {
411     if (!bShowInfo)
412       return;
413
414     CGUIWindowMusicInfo *pDlgAlbumInfo = (CGUIWindowMusicInfo*)m_gWindowManager.GetWindow(WINDOW_MUSIC_INFO);
415     if (pDlgAlbumInfo)
416     {
417       pDlgAlbumInfo->SetAlbum(albumInfo, albumSongs, path);
418       if (bShowInfo)
419         pDlgAlbumInfo->DoModal();
420       else
421         pDlgAlbumInfo->RefreshThumb();  // downloads the thumb if we don't already have one
422
423       if (!pDlgAlbumInfo->NeedRefresh())
424       {
425         if (pDlgAlbumInfo->HasUpdatedThumb())
426           UpdateThumb(albumInfo, path);
427         return;
428       }
429       bRefresh = true;
430     }
431   }
432
433   // If we are scanning for music info in the background,
434   // other writing access to the database is prohibited.
435   CGUIDialogMusicScan* dlgMusicScan = (CGUIDialogMusicScan*)m_gWindowManager.GetWindow(WINDOW_DIALOG_MUSIC_SCAN);
436   if (dlgMusicScan->IsDialogRunning())
437   {
438     CGUIDialogOK::ShowAndGetInput(189, 14057, 0, 0);
439     return;
440   }
441
442   // find album info
443   CMusicAlbumInfo info;
444   if (FindAlbumInfo(album.strAlbum, album.strArtist, info, bShowInfo ? (bRefresh ? SELECTION_FORCED : SELECTION_ALLOWED) : SELECTION_AUTO))
445   {
446     // download the album info
447     if ( info.Loaded() )
448     {
449       // set album title from musicinfotag, not the one we got from allmusic.com
450       info.SetTitle(album.strAlbum);
451
452       if (saveDb)
453       {
454         // save to database
455         m_musicdatabase.SetAlbumInfo(album.idAlbum, info.GetAlbum(), info.GetSongs());
456       }
457       if (m_dlgProgress && bShowInfo)
458         m_dlgProgress->Close();
459
460       // ok, show album info
461       CGUIWindowMusicInfo *pDlgAlbumInfo = (CGUIWindowMusicInfo*)m_gWindowManager.GetWindow(WINDOW_MUSIC_INFO);
462       if (pDlgAlbumInfo)
463       {
464         pDlgAlbumInfo->SetAlbum(info.GetAlbum(), info.GetSongs(), path);
465         if (bShowInfo)
466           pDlgAlbumInfo->DoModal();
467         else
468           pDlgAlbumInfo->RefreshThumb();  // downloads the thumb if we don't already have one
469
470         CAlbum albumInfo = info.GetAlbum();
471         albumInfo.idAlbum = album.idAlbum;
472         if (pDlgAlbumInfo->HasUpdatedThumb())
473           UpdateThumb(albumInfo, path);
474
475         if (pDlgAlbumInfo->NeedRefresh())
476         {
477           ShowAlbumInfo(album, path, true, bShowInfo);
478           return;
479         }
480       }
481     }
482     else
483     {
484       // failed 2 download album info
485       CGUIDialogOK::ShowAndGetInput(185, 0, 500, 0);
486     }
487   }
488
489   if (m_dlgProgress && bShowInfo)
490     m_dlgProgress->Close();
491
492 }
493
494 /*
495 /// \brief Can be overwritten to implement an own tag filling function.
496 /// \param items File items to fill
497 void CGUIWindowMusicBase::OnRetrieveMusicInfo(CFileItemList& items)
498 {
499 }
500 */
501
502 /// \brief Retrieve tag information for \e m_vecItems
503 void CGUIWindowMusicBase::RetrieveMusicInfo()
504 {
505   DWORD dwStartTick = timeGetTime();
506
507   OnRetrieveMusicInfo(m_vecItems);
508
509   CLog::Log(LOGDEBUG, "RetrieveMusicInfo() took %imsec", timeGetTime()-dwStartTick);
510 }
511
512 /// \brief Add selected list/thumb control item to playlist and start playing
513 /// \param iItem Selected Item in list/thumb control
514 void CGUIWindowMusicBase::OnQueueItem(int iItem)
515 {
516   if ( iItem < 0 || iItem >= m_vecItems.Size() ) return ;
517
518   int iOldSize=g_playlistPlayer.GetPlaylist(PLAYLIST_MUSIC).size();
519
520   // add item 2 playlist
521   CFileItem item(*m_vecItems[iItem]);
522
523   if (item.IsRAR() || item.IsZIP())
524     return;
525
526   //  Allow queuing of unqueueable items
527   //  when we try to queue them directly
528   if (!item.CanQueue())
529     item.SetCanQueue(true);
530
531   CLog::Log(LOGDEBUG, "Adding file %s%s to music playlist", item.m_strPath.c_str(), item.m_bIsFolder ? " (folder) " : "");
532   CFileItemList queuedItems;
533   AddItemToPlayList(&item, queuedItems);
534
535   // select next item
536   m_viewControl.SetSelectedItem(iItem + 1);
537
538   // if party mode, add items but DONT start playing
539   if (g_partyModeManager.IsEnabled())
540   {
541     g_partyModeManager.AddUserSongs(queuedItems, false);
542     return;
543   }
544
545   g_playlistPlayer.Add(PLAYLIST_MUSIC, queuedItems);
546   if (g_playlistPlayer.GetPlaylist(PLAYLIST_MUSIC).size() && !g_application.IsPlayingAudio())
547   {
548     if (m_guiState.get())
549       m_guiState->SetPlaylistDirectory("playlistmusic://");
550
551     g_playlistPlayer.Reset();
552     g_playlistPlayer.SetCurrentPlaylist(PLAYLIST_MUSIC);
553     g_playlistPlayer.Play(iOldSize); // start playing at the first new item
554   }
555 }
556
557 /// \brief Add unique file and folders and its subfolders to playlist
558 /// \param pItem The file item to add
559 void CGUIWindowMusicBase::AddItemToPlayList(const CFileItem* pItem, CFileItemList &queuedItems)
560 {
561   if (!pItem->CanQueue() || pItem->IsRAR() || pItem->IsZIP() || pItem->IsParentFolder()) // no zip/rar enques thank you!
562     return;
563
564   if (pItem->IsMusicDb() && pItem->m_bIsFolder && !pItem->IsParentFolder())
565   { // we have a music database folder, just grab the "all" item underneath it
566     CMusicDatabaseDirectory dir;
567     if (!dir.ContainsSongs(pItem->m_strPath))
568     { // grab the ALL item in this category
569       // Genres will still require 2 lookups, and queuing the entire Genre folder
570       // will require 3 lookups (genre, artist, album)
571       CFileItem item(pItem->m_strPath + "-1/", true);
572       item.SetCanQueue(true); // workaround for CanQueue() check above
573       AddItemToPlayList(&item, queuedItems);
574       return;
575     }
576   }
577   if (pItem->m_bIsFolder || (m_gWindowManager.GetActiveWindow() == WINDOW_MUSIC_NAV && pItem->IsPlayList()))
578   {
579     // Check if we add a locked share
580     if ( pItem->m_bIsShareOrDrive )
581     {
582       CFileItem item = *pItem;
583       if ( !g_passwordManager.IsItemUnlocked( &item, "music" ) )
584         return ;
585     }
586
587     // recursive
588     CFileItemList items;
589     GetDirectory(pItem->m_strPath, items);
590     //OnRetrieveMusicInfo(items);
591     SortItems(items);
592     for (int i = 0; i < items.Size(); ++i)
593       AddItemToPlayList(items[i], queuedItems);
594   }
595   else
596   {
597     if (pItem->IsPlayList())
598     {
599       auto_ptr<CPlayList> pPlayList (CPlayListFactory::Create(*pItem));
600       if ( NULL != pPlayList.get())
601       {
602         // load it
603         if (!pPlayList->Load(pItem->m_strPath))
604         {
605           CGUIDialogOK::ShowAndGetInput(6, 0, 477, 0);
606           return; //hmmm unable to load playlist?
607         }
608
609         CPlayList playlist = *pPlayList;
610         for (int i = 0; i < (int)playlist.size(); ++i)
611           AddItemToPlayList(&playlist[i], queuedItems);
612         return;
613       }
614     }
615     else if(pItem->IsInternetStream())
616     { // just queue the internet stream, it will be expanded on play
617       queuedItems.Add(new CFileItem(*pItem));
618     }
619     else if (!pItem->IsNFO() && pItem->IsAudio())
620     {
621       CFileItem *itemCheck = queuedItems.Get(pItem->m_strPath);
622       if (!itemCheck || itemCheck->m_lStartOffset != pItem->m_lStartOffset)
623       { // add item
624         CLog::Log(LOGDEBUG, "Adding item (%s) to playlist", pItem->m_strPath.c_str());
625         queuedItems.Add(new CFileItem(*pItem));
626       }
627     }
628   }
629 }
630
631 void CGUIWindowMusicBase::UpdateButtons()
632 {
633   // Update window selection control
634
635   // Remove labels from the window selection
636   CGUIMessage msg(GUI_MSG_LABEL_RESET, GetID(), CONTROL_BTNTYPE);
637   g_graphicsContext.SendMessage(msg);
638
639   // Add labels to the window selection
640   CGUIMessage msg2(GUI_MSG_LABEL_ADD, GetID(), CONTROL_BTNTYPE);
641   msg2.SetLabel(g_localizeStrings.Get(744)); // Files
642   g_graphicsContext.SendMessage(msg2);
643
644   msg2.SetLabel(g_localizeStrings.Get(15100)); // Library
645   g_graphicsContext.SendMessage(msg2);
646
647   // Select the current window as default item
648   CONTROL_SELECT_ITEM(CONTROL_BTNTYPE, g_stSettings.m_iMyMusicStartWindow - WINDOW_MUSIC_FILES);
649
650   CGUIMediaWindow::UpdateButtons();
651 }
652
653 bool CGUIWindowMusicBase::FindAlbumInfo(const CStdString& strAlbum, const CStdString& strArtist, CMusicAlbumInfo& album, ALLOW_SELECTION allowSelection)
654 {
655   // quietly return if Internet lookups are disabled
656   if (!g_guiSettings.GetBool("network.enableinternet")) return false;
657
658   // show dialog box indicating we're searching the album
659   if (m_dlgProgress && allowSelection != SELECTION_AUTO)
660   {
661     m_dlgProgress->SetHeading(185);
662     m_dlgProgress->SetLine(0, strAlbum);
663     m_dlgProgress->SetLine(1, strArtist);
664     m_dlgProgress->SetLine(2, "");
665     m_dlgProgress->StartModal();
666   }
667
668   try
669   {
670     Sleep(1);
671     CMusicInfoScraper scraper;
672     scraper.FindAlbuminfo(strAlbum, strArtist);
673
674     while (!scraper.Completed())
675     {
676       if (m_dlgProgress && m_dlgProgress->IsDialogRunning())
677       {
678         if (m_dlgProgress->IsCanceled())
679           scraper.Cancel();
680         m_dlgProgress->Progress();
681       }
682       Sleep(1);
683     }
684
685     if (scraper.Successfull())
686     {
687       // did we found at least 1 album?
688       int iAlbumCount = scraper.GetAlbumCount();
689       if (iAlbumCount >= 1)
690       {
691         //yes
692         // if we found more then 1 album, let user choose one
693         int iSelectedAlbum = 0;
694         if (iAlbumCount > 1)
695         {
696           //show dialog with all albums found
697           CGUIDialogSelect *pDlg = (CGUIDialogSelect*)m_gWindowManager.GetWindow(WINDOW_DIALOG_SELECT);
698           if (pDlg)
699           {
700             pDlg->SetHeading(g_localizeStrings.Get(181).c_str());
701             pDlg->Reset();
702             pDlg->EnableButton(true);
703             pDlg->SetButtonLabel(413); // manual
704
705             int bestMatch = -1;
706             double minRelevance = (allowSelection == SELECTION_AUTO) ? 0.95 : 1.0;
707             double bestRelevance = 0;
708             double secondBestRelevance = 0;
709             for (int i = 0; i < iAlbumCount; ++i)
710             {
711               CMusicAlbumInfo& info = scraper.GetAlbum(i);
712               double relevance = CUtil::AlbumRelevance(info.GetAlbum().strAlbum, strAlbum, info.GetAlbum().strArtist, strArtist);
713
714               // if we're doing auto-selection (ie querying all albums at once, then allow 95->100% for perfect matches)
715               // otherwise, perfect matches only
716               if (relevance >= max(minRelevance, bestRelevance))
717               { // we auto-select the best of these
718                 secondBestRelevance = bestRelevance;
719                 bestRelevance = relevance;
720                 bestMatch = i;
721               }
722
723               // set the label to [relevance]  album - artist
724               CStdString strTemp;
725               strTemp.Format("[%0.2f]  %s", relevance, info.GetTitle2());
726               CFileItem item(strTemp);
727               item.m_idepth = i; // use this to hold the index of the album in the scraper
728               pDlg->Add(&item);
729             }
730             if (bestMatch > -1 && bestRelevance != secondBestRelevance && allowSelection != SELECTION_FORCED)
731             { // autochoose the single best matching item
732               iSelectedAlbum = bestMatch;
733             }
734             else if (allowSelection == SELECTION_AUTO)
735             { //  nothing found, or two best matches to choose from
736               return false;
737             }
738             else
739             { // let the user choose
740               pDlg->Sort(false);
741               pDlg->DoModal();
742
743               // and wait till user selects one
744               if (pDlg->GetSelectedLabel() < 0) 
745               { // none chosen
746                 if (!pDlg->IsButtonPressed()) return false;
747                 // manual button pressed
748                 CStdString strNewAlbum = strAlbum;
749                 if (!CGUIDialogKeyboard::ShowAndGetInput(strNewAlbum, g_localizeStrings.Get(16011), false)) return false;
750                 if (strNewAlbum == "") return false;
751
752                 CStdString strNewArtist = strArtist;
753                 if (!CGUIDialogKeyboard::ShowAndGetInput(strNewArtist, g_localizeStrings.Get(16025), false)) return false;
754
755                 if (m_dlgProgress)
756                 {
757                   m_dlgProgress->SetLine(0, strNewAlbum);
758                   m_dlgProgress->SetLine(1, strNewArtist);
759                   m_dlgProgress->Progress();
760                 }
761                 return FindAlbumInfo(strNewAlbum, strNewArtist, album, allowSelection);
762               }
763               iSelectedAlbum = pDlg->GetSelectedItem().m_idepth;
764             }
765           }
766         }
767
768         // ok, downloading the album info
769         scraper.LoadAlbuminfo(iSelectedAlbum);
770
771         while (!scraper.Completed())
772         {
773           if (m_dlgProgress)
774           {
775             if (m_dlgProgress->IsCanceled())
776               scraper.Cancel();
777             m_dlgProgress->Progress();
778           }
779           Sleep(1);
780         }
781
782         if (scraper.Successfull())
783           album = scraper.GetAlbum(iSelectedAlbum);
784
785         return scraper.Successfull();
786       }
787       else
788       { // no albums found
789         CGUIDialogOK::ShowAndGetInput(185, 0, 187, 0);
790         return false;
791       }
792     }
793
794     if (!scraper.IsCanceled() && allowSelection != SELECTION_AUTO)
795     { // unable 2 connect to www.allmusic.com
796       CGUIDialogOK::ShowAndGetInput(185, 0, 499, 0);
797     }
798   }
799   catch (...)
800   {
801     if (m_dlgProgress && m_dlgProgress->IsDialogRunning())
802       m_dlgProgress->Close();
803
804     CLog::Log(LOGERROR, "Exception while downloading album info");
805   }
806   return false;
807 }
808
809 void CGUIWindowMusicBase::GetContextButtons(int itemNumber, CContextButtons &buttons)
810 {
811   CFileItem *item = (itemNumber >= 0 && itemNumber < m_vecItems.Size()) ? m_vecItems[itemNumber] : NULL;
812
813   if (item && !item->IsParentFolder())
814   {
815     buttons.Add(CONTEXT_BUTTON_QUEUE_ITEM, 13347);
816
817     // allow a folder to be ad-hoc queued and played by the default player
818     if (item->m_bIsFolder || (item->IsPlayList() && !g_advancedSettings.m_playlistAsFolders))
819       buttons.Add(CONTEXT_BUTTON_PLAY_ITEM, 208); // Play
820     else
821     { // check what players we have, if we have multiple display play with option
822       VECPLAYERCORES vecCores;
823       CPlayerCoreFactory::GetPlayers(*item, vecCores);
824       if (vecCores.size() >= 1)
825         buttons.Add(CONTEXT_BUTTON_PLAY_WITH, 15213); // Play With...
826     }
827
828     if (item->IsSmartPlayList() || m_vecItems.IsSmartPlayList())
829       buttons.Add(CONTEXT_BUTTON_EDIT_SMART_PLAYLIST, 586);
830     else if (item->IsPlayList() || m_vecItems.IsPlayList())
831       buttons.Add(CONTEXT_BUTTON_EDIT, 586);  
832   }
833   CGUIMediaWindow::GetContextButtons(itemNumber, buttons);
834 }
835
836 void CGUIWindowMusicBase::GetNonContextButtons(CContextButtons &buttons)
837 {
838   if (!m_vecItems.IsVirtualDirectoryRoot())
839     buttons.Add(CONTEXT_BUTTON_GOTO_ROOT, 20128);
840   if (g_playlistPlayer.GetPlaylist(PLAYLIST_MUSIC).size() > 0)
841     buttons.Add(CONTEXT_BUTTON_NOW_PLAYING, 13350);
842   buttons.Add(CONTEXT_BUTTON_SETTINGS, 5);
843 }
844
845 bool CGUIWindowMusicBase::OnContextButton(int itemNumber, CONTEXT_BUTTON button)
846 {
847   switch (button)
848   {
849   case CONTEXT_BUTTON_QUEUE_ITEM:
850     OnQueueItem(itemNumber);
851     return true;
852
853   case CONTEXT_BUTTON_INFO:
854     OnInfo(itemNumber);
855     return true;
856
857   case CONTEXT_BUTTON_SONG_INFO:
858     {
859       CGUIDialogSongInfo *dialog = (CGUIDialogSongInfo *)m_gWindowManager.GetWindow(WINDOW_DIALOG_SONG_INFO);
860       if (dialog)
861       {
862         dialog->SetSong(m_vecItems[itemNumber]);
863         dialog->DoModal(GetID());
864         if (dialog->NeedsUpdate())
865         { // update our file list
866           m_vecItems.RemoveDiscCache();
867           Update(m_vecItems.m_strPath);
868         }
869       }
870       return true;
871     }
872
873   case CONTEXT_BUTTON_EDIT:
874     {
875       CStdString playlist = m_vecItems[itemNumber]->IsPlayList() ? m_vecItems[itemNumber]->m_strPath : m_vecItems.m_strPath; // save path as activatewindow will destroy our items
876       m_gWindowManager.ActivateWindow(WINDOW_MUSIC_PLAYLIST_EDITOR, playlist);
877       return true;
878     }
879     
880   case CONTEXT_BUTTON_EDIT_SMART_PLAYLIST:
881     {
882       CStdString playlist = m_vecItems[itemNumber]->IsSmartPlayList() ? m_vecItems[itemNumber]->m_strPath : m_vecItems.m_strPath; // save path as activatewindow will destroy our items
883       if (CGUIDialogSmartPlaylistEditor::EditPlaylist(playlist))
884       { // need to update
885         m_vecItems.RemoveDiscCache();
886         Update(m_vecItems.m_strPath);
887       }
888       return true;
889     }
890
891   case CONTEXT_BUTTON_PLAY_ITEM:
892     PlayItem(itemNumber);
893     return true;
894
895   case CONTEXT_BUTTON_PLAY_WITH:
896     {
897       VECPLAYERCORES vecCores;  // base class?
898       CPlayerCoreFactory::GetPlayers(*m_vecItems[itemNumber], vecCores);
899       g_application.m_eForcedNextPlayer = CPlayerCoreFactory::SelectPlayerDialog(vecCores);
900       if( g_application.m_eForcedNextPlayer != EPC_NONE )
901         OnClick(itemNumber);
902       return true;
903     }
904
905   case CONTEXT_BUTTON_STOP_SCANNING:
906     { 
907       CGUIDialogMusicScan *scanner = (CGUIDialogMusicScan *)m_gWindowManager.GetWindow(WINDOW_DIALOG_MUSIC_SCAN);
908       if (scanner)
909         scanner->StopScanning();
910       return true;
911     }
912
913   case CONTEXT_BUTTON_NOW_PLAYING:
914     m_gWindowManager.ActivateWindow(WINDOW_MUSIC_PLAYLIST);
915     return true;
916
917   case CONTEXT_BUTTON_GOTO_ROOT:
918     Update("");
919     return true;
920
921   case CONTEXT_BUTTON_SETTINGS:
922     m_gWindowManager.ActivateWindow(WINDOW_SETTINGS_MYMUSIC);
923     return true;
924   }
925
926   return CGUIMediaWindow::OnContextButton(itemNumber, button);
927 }
928
929 void CGUIWindowMusicBase::OnRipCD()
930 {
931   CCdInfo *pCdInfo = CDetectDVDMedia::GetCdInfo();
932   if (CDetectDVDMedia::IsDiscInDrive() && pCdInfo && pCdInfo->IsAudio(1))
933   {
934     if (!g_application.CurrentFileItem().IsCDDA())
935     {
936 #ifdef HAS_CDDA_RIPPER
937       CCDDARipper ripper;
938       ripper.RipCD();
939 #endif
940     }
941     else
942     {
943       CGUIDialogOK* pDlgOK = (CGUIDialogOK*)m_gWindowManager.GetWindow(WINDOW_DIALOG_OK);
944       pDlgOK->SetHeading(257); // Error
945       pDlgOK->SetLine(0, g_localizeStrings.Get(20099)); //
946       pDlgOK->SetLine(1, ""); //
947       pDlgOK->SetLine(2, "");
948       pDlgOK->DoModal();
949     }
950   }
951 }
952
953 void CGUIWindowMusicBase::OnRipTrack(int iItem)
954 {
955   CCdInfo *pCdInfo = CDetectDVDMedia::GetCdInfo();
956   if (CDetectDVDMedia::IsDiscInDrive() && pCdInfo && pCdInfo->IsAudio(1))
957   {
958     if (!g_application.CurrentFileItem().IsCDDA())
959     {
960 #ifdef HAS_CDDA_RIPPER
961       CCDDARipper ripper;
962       ripper.RipTrack(m_vecItems[iItem]);
963 #endif
964     }
965     else
966     {
967       CGUIDialogOK* pDlgOK = (CGUIDialogOK*)m_gWindowManager.GetWindow(WINDOW_DIALOG_OK);
968       pDlgOK->SetHeading(257); // Error
969       pDlgOK->SetLine(0, g_localizeStrings.Get(20099)); //
970       pDlgOK->SetLine(1, ""); //
971       pDlgOK->SetLine(2, "");
972       pDlgOK->DoModal();
973     }
974   }
975 }
976
977 void CGUIWindowMusicBase::PlayItem(int iItem)
978 {
979   // restrictions should be placed in the appropiate window code
980   // only call the base code if the item passes since this clears
981   // the current playlist
982
983   const CFileItem* pItem = m_vecItems[iItem];
984
985   // special case for DAAP playlist folders
986   bool bIsDAAPplaylist = false;
987 #ifdef HAS_FILESYSTEM
988   if (pItem->IsDAAP() && pItem->m_bIsFolder)
989   {
990     CDAAPDirectory dirDAAP;
991     if (dirDAAP.GetCurrLevel(pItem->m_strPath) == 0)
992       bIsDAAPplaylist = true;
993   }
994 #endif
995   // if its a folder, build a playlist
996   if (pItem->m_bIsFolder || (m_gWindowManager.GetActiveWindow() == WINDOW_MUSIC_NAV && pItem->IsPlayList()))
997   {
998     CFileItem item(*m_vecItems[iItem]);
999
1000     //  Allow queuing of unqueueable items
1001     //  when we try to queue them directly
1002     if (!item.CanQueue())
1003       item.SetCanQueue(true);
1004
1005     // skip ".."
1006     if (item.IsParentFolder())
1007       return;
1008
1009     CFileItemList queuedItems;
1010     AddItemToPlayList(&item, queuedItems);
1011     if (g_partyModeManager.IsEnabled())
1012     {
1013       g_partyModeManager.AddUserSongs(queuedItems, true);
1014       return;
1015     }
1016
1017     /*
1018     CStdString strPlayListDirectory = m_vecItems.m_strPath;
1019     if (CUtil::HasSlashAtEnd(strPlayListDirectory))
1020       strPlayListDirectory.Delete(strPlayListDirectory.size() - 1);
1021     */
1022
1023     g_playlistPlayer.ClearPlaylist(PLAYLIST_MUSIC);
1024     g_playlistPlayer.Reset();
1025     g_playlistPlayer.Add(PLAYLIST_MUSIC, queuedItems);
1026     g_playlistPlayer.SetCurrentPlaylist(PLAYLIST_MUSIC);
1027
1028     // activate the playlist window if its not activated yet
1029     if (bIsDAAPplaylist && GetID() == m_gWindowManager.GetActiveWindow())
1030       m_gWindowManager.ActivateWindow(WINDOW_MUSIC_PLAYLIST);
1031
1032     // play!
1033     g_playlistPlayer.Play();
1034   }
1035   else if (pItem->IsPlayList())
1036   {
1037     // load the playlist the old way
1038     LoadPlayList(pItem->m_strPath);
1039   }
1040   else
1041   {
1042     // just a single item, play it
1043     // TODO: Add music-specific code for single playback of an item here (See OnClick in MediaWindow, and OnPlayMedia below)
1044     OnClick(iItem);
1045   }
1046 }
1047
1048 void CGUIWindowMusicBase::LoadPlayList(const CStdString& strPlayList)
1049 {
1050   // if partymode is active, we disable it
1051   if (g_partyModeManager.IsEnabled())
1052     g_partyModeManager.Disable();
1053
1054   // load a playlist like .m3u, .pls
1055   // first get correct factory to load playlist
1056   auto_ptr<CPlayList> pPlayList (CPlayListFactory::Create(strPlayList));
1057   if ( NULL != pPlayList.get())
1058   {
1059     // load it
1060     if (!pPlayList->Load(strPlayList))
1061     {
1062       CGUIDialogOK::ShowAndGetInput(6, 0, 477, 0);
1063       return ; //hmmm unable to load playlist?
1064     }
1065   }
1066
1067   int iSize = pPlayList->size();
1068   if (g_application.ProcessAndStartPlaylist(strPlayList, *pPlayList, PLAYLIST_MUSIC))
1069   {
1070     if (m_guiState.get())
1071       m_guiState->SetPlaylistDirectory("playlistmusic://");
1072     // activate the playlist window if its not activated yet
1073     if (GetID() == m_gWindowManager.GetActiveWindow() && iSize > 1)
1074     {
1075       m_gWindowManager.ActivateWindow(WINDOW_MUSIC_PLAYLIST);
1076     }
1077   }
1078 }
1079
1080 bool CGUIWindowMusicBase::OnPlayMedia(int iItem)
1081 {
1082   CFileItem* pItem = m_vecItems[iItem];
1083   if (pItem->m_strPath == "add" && pItem->GetLabel() == g_localizeStrings.Get(1026)) // 'add source button' in empty root
1084   {
1085     if (CGUIDialogMediaSource::ShowAndAddMediaSource("music"))
1086     {
1087       Update("");
1088       return true;
1089     }
1090     return false;
1091   }
1092
1093   // party mode
1094   if (g_partyModeManager.IsEnabled() && !pItem->IsLastFM())
1095   {
1096     CPlayList playlistTemp;
1097     CPlayList::CPlayListItem playlistItem;
1098     CUtil::ConvertFileItemToPlayListItem(m_vecItems[iItem], playlistItem);
1099     playlistTemp.Add(playlistItem);
1100     g_partyModeManager.AddUserSongs(playlistTemp, true);
1101     return true;
1102   }
1103   else if (!pItem->IsPlayList() && !pItem->IsInternetStream())
1104   { // single music file - if we get here then we have autoplaynextitem turned off, but we
1105     // still want to use the playlist player in order to handle more queued items following etc.
1106     g_playlistPlayer.Reset();
1107     g_playlistPlayer.ClearPlaylist(PLAYLIST_MUSIC);
1108     g_playlistPlayer.Add(PLAYLIST_MUSIC, pItem);
1109     g_playlistPlayer.SetCurrentPlaylist(PLAYLIST_MUSIC);
1110     g_playlistPlayer.Play();
1111     return true;
1112   }
1113   return CGUIMediaWindow::OnPlayMedia(iItem);
1114 }
1115
1116 void CGUIWindowMusicBase::UpdateThumb(const CAlbum &album, const CStdString &path)
1117 {
1118   // check user permissions
1119   bool saveDb = album.idAlbum != -1;
1120   bool saveDirThumb = true;
1121   if (!g_settings.m_vecProfiles[g_settings.m_iLastLoadedProfileIndex].canWriteDatabases() && !g_passwordManager.bMasterUser)
1122   {
1123     saveDb = false;
1124     saveDirThumb = false;
1125   }
1126
1127   CStdString albumThumb(CUtil::GetCachedAlbumThumb(album.strAlbum, album.strArtist));
1128
1129   // Update the thumb in the music database (songs + albums)
1130   CStdString albumPath(path);
1131   if (saveDb && CFile::Exists(albumThumb))
1132     m_musicdatabase.SaveAlbumThumb(album.idAlbum, albumThumb);
1133
1134   // Update currently playing song if it's from the same album.  This is necessary as when the album
1135   // first gets it's cover, the info manager's item doesn't have the updated information (so will be
1136   // sending a blank thumb to the skin.)
1137   if (g_application.IsPlayingAudio())
1138   {
1139     CStdString strSongFolder;
1140     const CMusicInfoTag* tag=g_infoManager.GetCurrentSongTag();
1141     if (tag)
1142     {
1143       // really, this may not be enough as it is to reliably update this item.  eg think of various artists albums
1144       // that aren't tagged as such (and aren't yet scanned).  But we probably can't do anything better than this
1145       // in that case
1146       if (album.strAlbum == tag->GetAlbum() && (album.strArtist == tag->GetAlbumArtist() || album.strArtist == tag->GetArtist()))
1147         g_infoManager.SetCurrentAlbumThumb(albumThumb);
1148     }
1149   }
1150
1151   // Save this thumb as the directory thumb if it's the only album in the folder (files view nicety)
1152   // We do this by grabbing all the songs in the folder, and checking to see whether they come
1153   // from the same album.
1154   if (saveDirThumb && CFile::Exists(albumThumb) && !albumPath.IsEmpty() && !CUtil::IsCDDA(albumPath))
1155   {
1156     CFileItemList items;
1157     GetDirectory(albumPath, items);
1158     OnRetrieveMusicInfo(items);
1159     VECSONGS songs;
1160     for (int i = 0; i < items.Size(); i++)
1161     {
1162       CFileItem *item = items[i];
1163       if (item->HasMusicInfoTag() && item->GetMusicInfoTag()->Loaded())
1164       {
1165         CSong song(*item->GetMusicInfoTag());
1166         songs.push_back(song);
1167       }
1168     }
1169     CMusicInfoScanner::CheckForVariousArtists(songs);
1170     CStdString album, artist;
1171     if (CMusicInfoScanner::HasSingleAlbum(songs, album, artist))
1172     { // can cache as the folder thumb
1173       CStdString folderThumb(CUtil::GetCachedMusicThumb(albumPath));
1174       ::CopyFile(albumThumb, folderThumb, false);
1175     }
1176   }
1177
1178   // update the file listing
1179   int iSelectedItem = m_viewControl.GetSelectedItem();
1180   if (iSelectedItem >= 0 && m_vecItems[iSelectedItem])
1181   {
1182     CFileItem* pSelectedItem=m_vecItems[iSelectedItem];
1183     if (pSelectedItem->m_bIsFolder)
1184     {
1185       // refresh only the icon of
1186       // the current folder
1187       pSelectedItem->FreeMemory();
1188       if (!pSelectedItem->HasThumbnail())
1189         pSelectedItem->SetThumbnailImage(albumThumb);
1190       pSelectedItem->FillInDefaultIcon();
1191     }
1192     else
1193     {
1194       // Refresh all items
1195       m_vecItems.RemoveDiscCache();
1196       Update(m_vecItems.m_strPath);
1197     }
1198
1199     //  Do we have to autoswitch to the thumb control?
1200     m_guiState.reset(CGUIViewState::GetViewState(GetID(), m_vecItems));
1201     UpdateButtons();
1202   }
1203 }
1204
1205 void CGUIWindowMusicBase::OnRetrieveMusicInfo(CFileItemList& items)
1206 {
1207   if (items.GetFolderCount()==items.Size() || items.IsMusicDb() || (!g_guiSettings.GetBool("musicfiles.usetags") && !items.IsCDDA()))
1208     return;
1209
1210   // Start the music info loader thread
1211   m_musicInfoLoader.SetProgressCallback(m_dlgProgress);
1212   m_musicInfoLoader.Load(items);
1213
1214   bool bShowProgress=!m_gWindowManager.HasModalDialog();
1215   bool bProgressVisible=false;
1216
1217   DWORD dwTick=timeGetTime();
1218
1219   while (m_musicInfoLoader.IsLoading())
1220   {
1221     if (bShowProgress)
1222     { // Do we have to init a progress dialog?
1223       DWORD dwElapsed=timeGetTime()-dwTick;
1224
1225       if (!bProgressVisible && dwElapsed>1500 && m_dlgProgress)
1226       { // tag loading takes more then 1.5 secs, show a progress dialog
1227         CURL url(items.m_strPath);
1228         CStdString strStrippedPath;
1229         url.GetURLWithoutUserDetails(strStrippedPath);
1230         m_dlgProgress->SetHeading(189);
1231         m_dlgProgress->SetLine(0, 505);
1232         m_dlgProgress->SetLine(1, "");
1233         m_dlgProgress->SetLine(2, strStrippedPath );
1234         m_dlgProgress->StartModal();
1235         m_dlgProgress->ShowProgressBar(true);
1236         bProgressVisible = true;
1237       }
1238
1239       if (bProgressVisible && m_dlgProgress)
1240       { // keep GUI alive
1241         m_dlgProgress->Progress();
1242       }
1243     } // if (bShowProgress)
1244     Sleep(1);
1245   } // while (m_musicInfoLoader.IsLoading())
1246
1247   if (bProgressVisible && m_dlgProgress)
1248     m_dlgProgress->Close();
1249 }
1250
1251 bool CGUIWindowMusicBase::GetDirectory(const CStdString &strDirectory, CFileItemList &items)
1252 {
1253   items.SetThumbnailImage("");
1254   bool bResult = CGUIMediaWindow::GetDirectory(strDirectory,items);
1255   if (bResult)
1256     items.SetMusicThumb();
1257
1258   // add in the "New Playlist" item if we're in the playlists folder
1259   if (items.m_strPath == "special://musicplaylists/" && !items.Contains("newplaylist://"))
1260   {
1261     CFileItem *newPlaylist = new CFileItem("newplaylist://", false);
1262     newPlaylist->SetLabel(g_localizeStrings.Get(525));
1263     newPlaylist->SetLabelPreformated(true);
1264     items.Add(newPlaylist);
1265
1266     newPlaylist = new CFileItem("newsmartplaylist://music", false);
1267     newPlaylist->SetLabel(g_localizeStrings.Get(21437));
1268     newPlaylist->SetLabelPreformated(true);
1269     items.Add(newPlaylist);
1270   }
1271
1272   return bResult;
1273 }