fixed: quirks with album information in file view
[xbmc:xbmc-antiquated.git] / xbmc / GUIWindowMusicSongs.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 "GUIWindowMusicSongs.h"
24 #include "Util.h"
25 #include "utils/GUIInfoManager.h"
26 #include "Application.h"
27 #include "CueDocument.h"
28 #include "GUIPassword.h"
29 #include "GUIDialogMusicScan.h"
30
31 using namespace AUTOPTR;
32 using namespace MEDIA_DETECT;
33
34 #define CONTROL_BTNVIEWASICONS     2
35 #define CONTROL_BTNSORTBY          3
36 #define CONTROL_BTNSORTASC         4
37 #define CONTROL_BTNTYPE            5
38 #define CONTROL_LIST              50
39 #define CONTROL_THUMBS            51
40 #define CONTROL_LABELFILES        12
41
42 #define CONTROL_BTNPLAYLISTS   7
43 #define CONTROL_BTNSCAN      9
44 #define CONTROL_BTNREC      10
45 #define CONTROL_BTNRIP      11
46
47
48 CGUIWindowMusicSongs::CGUIWindowMusicSongs(void)
49     : CGUIWindowMusicBase(WINDOW_MUSIC_FILES, "MyMusicSongs.xml")
50 {
51   m_vecItems.m_strPath="?";
52
53   m_thumbLoader.SetObserver(this);
54   // Remove old HD cache every time XBMC is loaded
55   DeleteDirectoryCache();
56 }
57
58 CGUIWindowMusicSongs::~CGUIWindowMusicSongs(void)
59 {
60 }
61
62 bool CGUIWindowMusicSongs::OnMessage(CGUIMessage& message)
63 {
64   switch ( message.GetMessage() )
65   {
66   case GUI_MSG_WINDOW_DEINIT:
67     if (m_thumbLoader.IsLoading())
68       m_thumbLoader.StopThread();
69     break;
70   case GUI_MSG_WINDOW_INIT:
71     {
72       // removed the start window check from files view
73       // the window translator does it by using a virtual window id (5)
74
75       // check for a passed destination path
76       CStdString strDestination = message.GetStringParam();
77       if (!strDestination.IsEmpty())
78       {
79         message.SetStringParam("");
80         CLog::Log(LOGINFO, "Attempting to quickpath to: %s", strDestination.c_str());
81         m_history.ClearPathHistory();
82       }
83
84       // is this the first time the window is opened?
85       if (m_vecItems.m_strPath == "?" && strDestination.IsEmpty())
86       {
87         strDestination = g_settings.m_defaultMusicSource;
88         m_vecItems.m_strPath=strDestination;
89         CLog::Log(LOGINFO, "Attempting to default to: %s", strDestination.c_str());
90       }
91
92       // try to open the destination path
93       if (!strDestination.IsEmpty())
94       {
95         // open root
96         if (strDestination.Equals("$ROOT"))
97         {
98           m_vecItems.m_strPath = "";
99           CLog::Log(LOGINFO, "  Success! Opening root listing.");
100         }
101         // open playlists location
102         else if (strDestination.Equals("$PLAYLISTS"))
103         {
104           m_vecItems.m_strPath = "special://musicplaylists/";
105           CLog::Log(LOGINFO, "  Success! Opening destination path: %s", m_vecItems.m_strPath.c_str());
106         }
107         else
108         {
109           // default parameters if the jump fails
110           m_vecItems.m_strPath.Empty();
111
112           bool bIsSourceName = false;
113
114           SetupShares();
115           VECSHARES shares;
116           m_rootDir.GetShares(shares);
117           int iIndex = CUtil::GetMatchingShare(strDestination, shares, bIsSourceName);
118           if (iIndex > -1)
119           {
120             bool unlocked = true;
121             if (shares[iIndex].m_iHasLock == 2)
122             {
123               CFileItem item(shares[iIndex]);
124               if (!g_passwordManager.IsItemUnlocked(&item,"music"))
125               {
126                 m_vecItems.m_strPath = ""; // no u don't
127                 unlocked = false;
128                 CLog::Log(LOGINFO, "  Failure! Failed to unlock destination path: %s", strDestination.c_str());
129               }
130             }
131             // set current directory to matching share
132             if (unlocked)
133             {
134               if (bIsSourceName)
135                 m_vecItems.m_strPath=shares[iIndex].strPath;
136               else
137                 m_vecItems.m_strPath=strDestination;
138               CLog::Log(LOGINFO, "  Success! Opened destination path: %s (%s)", strDestination.c_str(), m_vecItems.m_strPath.c_str());
139             }
140           }
141           else
142           {
143             CLog::Log(LOGERROR, "  Failed! Destination parameter (%s) does not match a valid source!", strDestination.c_str());
144           }
145         }
146
147         // need file filters or GetDirectory in SetHistoryPath fails
148         SetHistoryForPath(m_vecItems.m_strPath);
149       }
150
151       return CGUIWindowMusicBase::OnMessage(message);
152     }
153     break;
154
155   case GUI_MSG_DIRECTORY_SCANNED:
156     {
157       CFileItem directory(message.GetStringParam(), true);
158
159       // Only update thumb on a local drive
160       if (directory.IsHD())
161       {
162         CStdString strParent;
163         CUtil::GetParentPath(directory.m_strPath, strParent);
164         if (directory.m_strPath == m_vecItems.m_strPath || strParent == m_vecItems.m_strPath)
165         {
166           Update(m_vecItems.m_strPath);
167         }
168       }
169     }
170     break;
171
172   case GUI_MSG_NOTIFY_ALL:
173     {
174       if (message.GetParam1()==GUI_MSG_REMOVED_MEDIA)
175         DeleteRemoveableMediaDirectoryCache();
176     }
177     break;
178
179   case GUI_MSG_CLICKED:
180     {
181       int iControl = message.GetSenderId();
182
183       if (iControl == CONTROL_BTNPLAYLISTS)
184       {
185         if (!m_vecItems.m_strPath.Equals("special://musicplaylists/"))
186           Update("special://musicplaylists/");
187       }
188       else if (iControl == CONTROL_BTNSCAN)
189       {
190         OnScan(-1);
191       }
192       else if (iControl == CONTROL_BTNREC)
193       {
194         if (g_application.IsPlayingAudio() )
195         {
196           if (g_application.m_pPlayer->CanRecord() )
197           {
198             bool bIsRecording = g_application.m_pPlayer->IsRecording();
199             g_application.m_pPlayer->Record(!bIsRecording);
200             UpdateButtons();
201           }
202         }
203       }
204       else if (iControl == CONTROL_BTNRIP)
205       {
206         OnRipCD();
207       }
208     }
209     break;
210   }
211
212   return CGUIWindowMusicBase::OnMessage(message);
213 }
214
215 void CGUIWindowMusicSongs::OnScan(int iItem)
216 {
217   CStdString strPath;
218   if (iItem < 0 || iItem >= m_vecItems.Size())
219     strPath = m_vecItems.m_strPath;
220   else if (m_vecItems[iItem]->m_bIsFolder)
221     strPath = m_vecItems[iItem]->m_strPath;
222   else
223   { // TODO: MUSICDB - should we allow scanning a single item into the database?
224     //       This will require changes to the info scanner, which assumes we're running on a folder
225     strPath = m_vecItems.m_strPath;
226   }
227   DoScan(strPath);
228 }
229
230 void CGUIWindowMusicSongs::DoScan(const CStdString &strPath)
231 {
232   CGUIDialogMusicScan *musicScan = (CGUIDialogMusicScan *)m_gWindowManager.GetWindow(WINDOW_DIALOG_MUSIC_SCAN);
233   if (musicScan && musicScan->IsScanning())
234   {
235     musicScan->StopScanning();
236     return ;
237   }
238
239   // Start background loader
240   int iControl=GetFocusedControlID();
241   if (musicScan) musicScan->StartScanning(strPath);
242   SET_CONTROL_FOCUS(iControl, 0);
243   UpdateButtons();
244   return ;
245 }
246
247 bool CGUIWindowMusicSongs::GetDirectory(const CStdString &strDirectory, CFileItemList &items)
248 {
249   if (!CGUIWindowMusicBase::GetDirectory(strDirectory, items))
250     return false;
251
252   // check for .CUE files here.
253   items.FilterCueItems();
254
255   return true;
256 }
257
258 void CGUIWindowMusicSongs::OnPrepareFileItems(CFileItemList &items)
259 {
260   RetrieveMusicInfo();
261
262   items.SetCachedMusicThumbs();
263 }
264
265 void CGUIWindowMusicSongs::UpdateButtons()
266 {
267   CGUIWindowMusicBase::UpdateButtons();
268
269   bool bIsPlaying = g_application.IsPlayingAudio();
270   bool bCanRecord = false;
271   bool bIsRecording = false;
272
273   if (bIsPlaying)
274   {
275     bCanRecord = g_application.m_pPlayer->CanRecord();
276     bIsRecording = g_application.m_pPlayer->IsRecording();
277   }
278
279   // Update Record button
280   if (bIsPlaying && bCanRecord)
281   {
282     CONTROL_ENABLE(CONTROL_BTNREC);
283     if (bIsRecording)
284     {
285       SET_CONTROL_LABEL(CONTROL_BTNREC, 265); //Stop Recording
286     }
287     else
288     {
289       SET_CONTROL_LABEL(CONTROL_BTNREC, 264); //Record
290     }
291   }
292   else
293   {
294     SET_CONTROL_LABEL(CONTROL_BTNREC, 264); //Record
295     CONTROL_DISABLE(CONTROL_BTNREC);
296   }
297
298   // Update CDDA Rip button
299   CCdInfo *pCdInfo = CDetectDVDMedia::GetCdInfo();
300   if (CDetectDVDMedia::IsDiscInDrive() && pCdInfo && pCdInfo->IsAudio(1))
301   {
302     CONTROL_ENABLE(CONTROL_BTNRIP);
303   }
304   else
305   {
306     CONTROL_DISABLE(CONTROL_BTNRIP);
307   }
308
309   // Disable scan button if shoutcast
310   if (m_vecItems.IsVirtualDirectoryRoot() || m_vecItems.IsShoutCast() || m_vecItems.IsLastFM() || m_vecItems.IsMusicDb())
311   {
312     CONTROL_DISABLE(CONTROL_BTNSCAN);
313   }
314   else
315   {
316     CONTROL_ENABLE(CONTROL_BTNSCAN);
317   }
318   static int iOldLeftControl=-1;
319   if (m_vecItems.IsShoutCast() || m_vecItems.IsLastFM())
320   {
321     CONTROL_DISABLE(CONTROL_BTNVIEWASICONS);
322     CGUIControl* pControl = (CGUIControl*)GetControl(CONTROL_LIST);
323     if (pControl)
324       if (pControl->GetControlIdLeft() == CONTROL_BTNVIEWASICONS)
325       {
326         iOldLeftControl = pControl->GetControlIdLeft();
327         pControl->SetNavigation(pControl->GetControlIdUp(),pControl->GetControlIdDown(),CONTROL_BTNSORTBY,pControl->GetControlIdRight());
328       }
329   }
330   else
331   {
332     CONTROL_ENABLE(CONTROL_BTNVIEWASICONS);
333     if (iOldLeftControl != -1)
334     {
335       CGUIControl* pControl = (CGUIControl*)GetControl(CONTROL_LIST);
336       if (pControl)
337         pControl->SetNavigation(pControl->GetControlIdUp(),pControl->GetControlIdDown(),CONTROL_BTNVIEWASICONS,pControl->GetControlIdRight());
338     }
339   }
340
341   CGUIDialogMusicScan *musicScan = (CGUIDialogMusicScan *)m_gWindowManager.GetWindow(WINDOW_DIALOG_MUSIC_SCAN);
342   if (musicScan && musicScan->IsScanning())
343   {
344     SET_CONTROL_LABEL(CONTROL_BTNSCAN, 14056); // Stop Scan
345   }
346   else
347   {
348     SET_CONTROL_LABEL(CONTROL_BTNSCAN, 102); // Scan
349   }
350
351   // Update object count label
352   int iItems = m_vecItems.Size();
353   if (iItems)
354   {
355     CFileItem* pItem = m_vecItems[0];
356     if (pItem->IsParentFolder()) iItems--;
357   }
358   CStdString items;
359   items.Format("%i %s", iItems, g_localizeStrings.Get(127).c_str());
360   SET_CONTROL_LABEL(CONTROL_LABELFILES, items);
361 }
362
363 void CGUIWindowMusicSongs::GetContextButtons(int itemNumber, CContextButtons &buttons)
364 {
365   CFileItem *item = (itemNumber >= 0 && itemNumber < m_vecItems.Size()) ? m_vecItems[itemNumber] : NULL;
366
367   if (item)
368   {
369     // are we in the playlists location?
370     bool inPlaylists = m_vecItems.m_strPath.Equals(CUtil::MusicPlaylistsLocation()) || m_vecItems.m_strPath.Equals("special://musicplaylists/");
371
372     if (m_vecItems.IsVirtualDirectoryRoot())
373     {
374       // get the usual music shares, and anything for all media windows
375       CShare *share = CGUIDialogContextMenu::GetShare("music", item);
376       CGUIDialogContextMenu::GetContextButtons("music", share, buttons);
377       // enable Rip CD an audio disc
378       if (CDetectDVDMedia::IsDiscInDrive() && item->IsCDDA())
379       {
380         // those cds can also include Audio Tracks: CDExtra and MixedMode!
381         CCdInfo *pCdInfo = CDetectDVDMedia::GetCdInfo();
382         if ( pCdInfo->IsAudio(1) || pCdInfo->IsCDExtra(1) || pCdInfo->IsMixedMode(1) )
383           buttons.Add(CONTEXT_BUTTON_RIP_CD, 600);
384       }
385       CGUIMediaWindow::GetContextButtons(itemNumber, buttons);
386     }
387     else
388     {
389       CGUIWindowMusicBase::GetContextButtons(itemNumber, buttons);
390       if (!item->IsPlayList())
391       {
392         if (item->IsAudio() && !item->IsLastFM() && !item->IsShoutCast())
393           buttons.Add(CONTEXT_BUTTON_SONG_INFO, 658); // Song Info
394         else if (!item->IsParentFolder() && !item->IsLastFM() && !item->IsShoutCast() && !item->m_strPath.Left(3).Equals("new") && item->m_bIsFolder)
395         {
396           if (m_musicdatabase.GetAlbumIdByPath(item->m_strPath) > -1)
397             buttons.Add(CONTEXT_BUTTON_INFO, 13351); // Album Info
398         }
399       }
400
401       // enable Rip CD Audio or Track button if we have an audio disc
402       if (CDetectDVDMedia::IsDiscInDrive() && m_vecItems.IsCDDA())
403       {
404         // those cds can also include Audio Tracks: CDExtra and MixedMode!
405         CCdInfo *pCdInfo = CDetectDVDMedia::GetCdInfo();
406         if ( pCdInfo->IsAudio(1) || pCdInfo->IsCDExtra(1) || pCdInfo->IsMixedMode(1) )
407           buttons.Add(CONTEXT_BUTTON_RIP_TRACK, 610);
408       }
409
410       // enable CDDB lookup if the current dir is CDDA
411       if (CDetectDVDMedia::IsDiscInDrive() && m_vecItems.IsCDDA() && (g_settings.m_vecProfiles[g_settings.m_iLastLoadedProfileIndex].canWriteDatabases() || g_passwordManager.bMasterUser))
412         buttons.Add(CONTEXT_BUTTON_CDDB, 16002);
413
414       if (!item->IsParentFolder() && !item->IsReadOnly())
415       {
416         // either we're at the playlist location or its been explicitly allowed
417         if (inPlaylists || g_guiSettings.GetBool("filelists.allowfiledeletion"))
418         {
419           buttons.Add(CONTEXT_BUTTON_DELETE, 117);
420           buttons.Add(CONTEXT_BUTTON_RENAME, 118);
421         }
422       }
423     }
424
425     // Add the scan button(s)
426     CGUIDialogMusicScan *pScanDlg = (CGUIDialogMusicScan *)m_gWindowManager.GetWindow(WINDOW_DIALOG_MUSIC_SCAN);
427     if (g_guiSettings.GetBool("musiclibrary.enabled") && pScanDlg)
428     {
429       if (pScanDlg->IsScanning())
430         buttons.Add(CONTEXT_BUTTON_STOP_SCANNING, 13353);       // Stop Scanning
431       else if (
432         !inPlaylists && 
433         !m_vecItems.IsInternetStream() && 
434         !item->IsLastFM() && 
435         !item->IsShoutCast() && 
436         !item->m_strPath.Equals("add") && 
437         !item->IsParentFolder() &&
438         (g_settings.m_vecProfiles[g_settings.m_iLastLoadedProfileIndex].canWriteDatabases() || g_passwordManager.bMasterUser))
439       {
440         buttons.Add(CONTEXT_BUTTON_SCAN, 13352);
441       }
442     }
443   }
444   if (!m_vecItems.IsVirtualDirectoryRoot())
445     buttons.Add(CONTEXT_BUTTON_SWITCH_MEDIA, 523);
446   CGUIWindowMusicBase::GetNonContextButtons(buttons);
447 }
448
449 bool CGUIWindowMusicSongs::OnContextButton(int itemNumber, CONTEXT_BUTTON button)
450 {
451   CFileItem *item = (itemNumber >= 0 && itemNumber < m_vecItems.Size()) ? m_vecItems[itemNumber] : NULL;
452   if ( m_vecItems.IsVirtualDirectoryRoot() && item)
453   {
454     CShare *share = CGUIDialogContextMenu::GetShare("music", item);
455     if (CGUIDialogContextMenu::OnContextButton("music", share, button))
456     {
457       Update("");
458       return true;
459     }
460   }
461
462   switch (button)
463   {
464   case CONTEXT_BUTTON_SCAN:
465     OnScan(itemNumber);
466     return true;
467
468   case CONTEXT_BUTTON_RIP_TRACK:
469     OnRipTrack(itemNumber);
470     return true;
471
472   case CONTEXT_BUTTON_RIP_CD:
473     OnRipCD();
474     return true;
475
476   case CONTEXT_BUTTON_CDDB:
477     if (m_musicdatabase.LookupCDDBInfo(true))
478       Update(m_vecItems.m_strPath);
479     return true;
480
481   case CONTEXT_BUTTON_DELETE:
482     OnDeleteItem(itemNumber);
483     return true;
484
485   case CONTEXT_BUTTON_RENAME:
486     OnRenameItem(itemNumber);
487     return true;
488
489   case CONTEXT_BUTTON_SWITCH_MEDIA:
490                 CGUIDialogContextMenu::SwitchMedia("music", m_vecItems.m_strPath);
491                 return true;
492   default:
493     break;
494   }
495   return CGUIWindowMusicBase::OnContextButton(itemNumber, button);
496 }
497
498 void CGUIWindowMusicSongs::DeleteDirectoryCache()
499 {
500   WIN32_FIND_DATA wfd;
501   memset(&wfd, 0, sizeof(wfd));
502
503   CAutoPtrFind hFind( FindFirstFile("Z:\\*.fi", &wfd));
504   if (!hFind.isValid())
505     return ;
506   do
507   {
508     if ( !(wfd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) )
509     {
510       CStdString strFile = "Z:\\";
511       strFile += wfd.cFileName;
512       DeleteFile(strFile.c_str());
513     }
514   }
515   while (FindNextFile(hFind, &wfd));
516 }
517
518 void CGUIWindowMusicSongs::DeleteRemoveableMediaDirectoryCache()
519 {
520   WIN32_FIND_DATA wfd;
521   memset(&wfd, 0, sizeof(wfd));
522
523   CAutoPtrFind hFind( FindFirstFile("Z:\\r-*.fi", &wfd));
524   if (!hFind.isValid())
525     return ;
526   do
527   {
528     if ( !(wfd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) )
529     {
530       CStdString strFile = "Z:\\";
531       strFile += wfd.cFileName;
532       DeleteFile(strFile.c_str());
533     }
534   }
535   while (FindNextFile(hFind, &wfd));
536 }
537
538 void CGUIWindowMusicSongs::PlayItem(int iItem)
539 {
540   // unlike additemtoplaylist, we need to check the items here
541   // before calling it since the current playlist will be stopped
542   // and cleared!
543
544   // we're at the root source listing
545   if (m_vecItems.IsVirtualDirectoryRoot() && !m_vecItems[iItem]->IsDVD())
546     return;
547
548   if (m_vecItems[iItem]->IsDVD())
549     CAutorun::PlayDisc();
550   else
551     CGUIWindowMusicBase::PlayItem(iItem);
552 }
553
554 bool CGUIWindowMusicSongs::Update(const CStdString &strDirectory)
555 {
556   if (m_thumbLoader.IsLoading())
557     m_thumbLoader.StopThread();
558
559   if (!CGUIMediaWindow::Update(strDirectory))
560     return false;
561
562   if (!m_vecItems.GetContent().IsEmpty())
563     g_infoManager.m_content = m_vecItems.GetContent();
564   else
565     g_infoManager.m_content = "files";
566   m_thumbLoader.Load(m_vecItems);
567   return true;
568 }