Merge pull request #1013 from amet/vfs_extend
[vuplus_xbmc] / xbmc / pictures / GUIWindowPictures.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 "threads/SystemClock.h"
23 #include "system.h"
24 #include "GUIWindowPictures.h"
25 #include "URL.h"
26 #include "Util.h"
27 #include "Picture.h"
28 #include "Application.h"
29 #include "GUIPassword.h"
30 #include "dialogs/GUIDialogMediaSource.h"
31 #include "GUIDialogPictureInfo.h"
32 #include "addons/GUIDialogAddonInfo.h"
33 #include "dialogs/GUIDialogProgress.h"
34 #include "playlists/PlayListFactory.h"
35 #include "PictureInfoLoader.h"
36 #include "guilib/GUIWindowManager.h"
37 #include "dialogs/GUIDialogOK.h"
38 #include "dialogs/GUIDialogYesNo.h"
39 #include "playlists/PlayList.h"
40 #include "settings/Settings.h"
41 #include "settings/GUISettings.h"
42 #include "utils/TimeUtils.h"
43 #include "utils/log.h"
44 #include "utils/URIUtils.h"
45 #include "Autorun.h"
46
47 #define CONTROL_BTNVIEWASICONS      2
48 #define CONTROL_BTNSORTBY           3
49 #define CONTROL_BTNSORTASC          4
50 #define CONTROL_LABELFILES         12
51
52 using namespace std;
53 using namespace XFILE;
54 using namespace PLAYLIST;
55
56 #define CONTROL_BTNSLIDESHOW   6
57 #define CONTROL_BTNSLIDESHOW_RECURSIVE   7
58 #define CONTROL_SHUFFLE      9
59
60 CGUIWindowPictures::CGUIWindowPictures(void)
61     : CGUIMediaWindow(WINDOW_PICTURES, "MyPics.xml")
62 {
63   m_thumbLoader.SetObserver(this);
64   m_slideShowStarted = false;
65 }
66
67 void CGUIWindowPictures::OnInitWindow()
68 {
69   CGUIMediaWindow::OnInitWindow();
70   if (m_slideShowStarted)
71   {
72     CGUIWindowSlideShow* wndw = (CGUIWindowSlideShow*)g_windowManager.GetWindow(WINDOW_SLIDESHOW);
73     CStdString path;
74     if (wndw && wndw->GetCurrentSlide())
75       URIUtils::GetDirectory(wndw->GetCurrentSlide()->GetPath(),path);
76     if (path.Equals(m_vecItems->GetPath()))
77     {
78       if (wndw && wndw->GetCurrentSlide())
79         m_viewControl.SetSelectedItem(wndw->GetCurrentSlide()->GetPath());
80       m_iSelectedItem = m_viewControl.GetSelectedItem();
81     }
82     m_slideShowStarted = false;
83   }
84 }
85
86 CGUIWindowPictures::~CGUIWindowPictures(void)
87 {
88 }
89
90 bool CGUIWindowPictures::OnMessage(CGUIMessage& message)
91 {
92   switch ( message.GetMessage() )
93   {
94   case GUI_MSG_WINDOW_DEINIT:
95     {
96       if (m_thumbLoader.IsLoading())
97         m_thumbLoader.StopThread();
98
99       if (message.GetParam1() != WINDOW_SLIDESHOW)
100       {
101         m_ImageLib.Unload();
102       }
103     }
104     break;
105
106   case GUI_MSG_WINDOW_INIT:
107     {
108       // is this the first time accessing this window?
109       if (m_vecItems->GetPath() == "?" && message.GetStringParam().IsEmpty())
110         message.SetStringParam(g_settings.m_defaultPictureSource);
111
112       m_dlgProgress = (CGUIDialogProgress*)g_windowManager.GetWindow(WINDOW_DIALOG_PROGRESS);
113
114       if (message.GetParam1() != WINDOW_SLIDESHOW)
115       {
116         m_ImageLib.Load();
117       }
118
119       if (!CGUIMediaWindow::OnMessage(message))
120         return false;
121
122       return true;
123     }
124     break;
125
126   case GUI_MSG_CLICKED:
127     {
128       int iControl = message.GetSenderId();
129       if (iControl == CONTROL_BTNSLIDESHOW) // Slide Show
130       {
131         OnSlideShow();
132       }
133       else if (iControl == CONTROL_BTNSLIDESHOW_RECURSIVE) // Recursive Slide Show
134       {
135         OnSlideShowRecursive();
136       }
137       else if (iControl == CONTROL_SHUFFLE)
138       {
139         g_guiSettings.ToggleBool("slideshow.shuffle");
140         g_settings.Save();
141       }
142       else if (m_viewControl.HasControl(iControl))  // list/thumb control
143       {
144         int iItem = m_viewControl.GetSelectedItem();
145         int iAction = message.GetParam1();
146
147         // iItem is checked for validity inside these routines
148         if (iAction == ACTION_DELETE_ITEM)
149         {
150           // is delete allowed?
151           if (g_guiSettings.GetBool("filelists.allowfiledeletion"))
152             OnDeleteItem(iItem);
153           else
154             return false;
155         }
156         else if (iAction == ACTION_PLAYER_PLAY)
157         {
158           ShowPicture(iItem, true);
159           return true;
160         }
161         else if (iAction == ACTION_SHOW_INFO)
162         {
163           OnInfo(iItem);
164           return true;
165         }
166       }
167     }
168     break;
169   }
170   return CGUIMediaWindow::OnMessage(message);
171 }
172
173 void CGUIWindowPictures::UpdateButtons()
174 {
175   CGUIMediaWindow::UpdateButtons();
176
177   // Update the shuffle button
178   if (g_guiSettings.GetBool("slideshow.shuffle"))
179   {
180     CGUIMessage msg2(GUI_MSG_SELECTED, GetID(), CONTROL_SHUFFLE);
181     g_windowManager.SendMessage(msg2);
182   }
183   else
184   {
185     CGUIMessage msg2(GUI_MSG_DESELECTED, GetID(), CONTROL_SHUFFLE);
186     g_windowManager.SendMessage(msg2);
187   }
188
189   // check we can slideshow or recursive slideshow
190   int nFolders = m_vecItems->GetFolderCount();
191   if (nFolders == m_vecItems->Size())
192   {
193     CONTROL_DISABLE(CONTROL_BTNSLIDESHOW);
194   }
195   else
196   {
197     CONTROL_ENABLE(CONTROL_BTNSLIDESHOW);
198   }
199   if (m_guiState.get() && !m_guiState->HideParentDirItems())
200     nFolders--;
201   if (m_vecItems->Size() == 0 || nFolders == 0)
202   {
203     CONTROL_DISABLE(CONTROL_BTNSLIDESHOW_RECURSIVE);
204   }
205   else
206   {
207     CONTROL_ENABLE(CONTROL_BTNSLIDESHOW_RECURSIVE);
208   }
209 }
210
211 void CGUIWindowPictures::OnPrepareFileItems(CFileItemList& items)
212 {
213   for (int i=0;i<items.Size();++i )
214     if (items[i]->GetLabel().Equals("folder.jpg"))
215       items.Remove(i);
216
217   if (items.GetFolderCount()==items.Size() || !g_guiSettings.GetBool("pictures.usetags"))
218     return;
219
220   // Start the music info loader thread
221   CPictureInfoLoader loader;
222   loader.SetProgressCallback(m_dlgProgress);
223   loader.Load(items);
224
225   bool bShowProgress=!g_windowManager.HasModalDialog();
226   bool bProgressVisible=false;
227
228   unsigned int tick=XbmcThreads::SystemClockMillis();
229
230   while (loader.IsLoading() && m_dlgProgress && !m_dlgProgress->IsCanceled())
231   {
232     if (bShowProgress)
233     { // Do we have to init a progress dialog?
234       unsigned int elapsed=XbmcThreads::SystemClockMillis()-tick;
235
236       if (!bProgressVisible && elapsed>1500 && m_dlgProgress)
237       { // tag loading takes more then 1.5 secs, show a progress dialog
238         CURL url(items.GetPath());
239
240         m_dlgProgress->SetHeading(189);
241         m_dlgProgress->SetLine(0, 505);
242         m_dlgProgress->SetLine(1, "");
243         m_dlgProgress->SetLine(2, url.GetWithoutUserDetails());
244         m_dlgProgress->StartModal();
245         m_dlgProgress->ShowProgressBar(true);
246         bProgressVisible = true;
247       }
248
249       if (bProgressVisible && m_dlgProgress)
250       { // keep GUI alive
251         m_dlgProgress->Progress();
252       }
253     } // if (bShowProgress)
254     Sleep(1);
255   } // while (loader.IsLoading())
256
257   if (bProgressVisible && m_dlgProgress)
258     m_dlgProgress->Close();
259 }
260
261 bool CGUIWindowPictures::Update(const CStdString &strDirectory)
262 {
263   if (m_thumbLoader.IsLoading())
264     m_thumbLoader.StopThread();
265
266   if (!CGUIMediaWindow::Update(strDirectory))
267     return false;
268
269   m_vecItems->SetThumbnailImage("");
270   if (g_guiSettings.GetBool("pictures.generatethumbs"))
271     m_thumbLoader.Load(*m_vecItems);
272   m_vecItems->SetThumbnailImage(CPictureThumbLoader::GetCachedImage(*m_vecItems, "thumb"));
273
274   return true;
275 }
276
277 bool CGUIWindowPictures::OnClick(int iItem)
278 {
279   if ( iItem < 0 || iItem >= (int)m_vecItems->Size() ) return true;
280   CFileItemPtr pItem = m_vecItems->Get(iItem);
281
282   if (pItem->IsCBZ() || pItem->IsCBR())
283   {
284     CStdString strComicPath;
285     if (pItem->IsCBZ())
286       URIUtils::CreateArchivePath(strComicPath, "zip", pItem->GetPath(), "");
287     else
288       URIUtils::CreateArchivePath(strComicPath, "rar", pItem->GetPath(), "");
289
290     OnShowPictureRecursive(strComicPath);
291     return true;
292   }
293   else if (CGUIMediaWindow::OnClick(iItem))
294     return true;
295
296   return false;
297 }
298
299 bool CGUIWindowPictures::GetDirectory(const CStdString &strDirectory, CFileItemList& items)
300 {
301   if (!CGUIMediaWindow::GetDirectory(strDirectory, items))
302     return false;
303
304   CStdString label;
305   if (items.GetLabel().IsEmpty() && m_rootDir.IsSource(items.GetPath(), g_settings.GetSourcesFromType("pictures"), &label)) 
306     items.SetLabel(label);
307
308   return true;
309 }
310
311 bool CGUIWindowPictures::OnPlayMedia(int iItem)
312 {
313   if (m_vecItems->Get(iItem)->IsVideo())
314     return CGUIMediaWindow::OnPlayMedia(iItem);
315
316   return ShowPicture(iItem, false);
317 }
318
319 bool CGUIWindowPictures::ShowPicture(int iItem, bool startSlideShow)
320 {
321   if ( iItem < 0 || iItem >= (int)m_vecItems->Size() ) return false;
322   CFileItemPtr pItem = m_vecItems->Get(iItem);
323   CStdString strPicture = pItem->GetPath();
324
325 #ifdef HAS_DVD_DRIVE
326   if (pItem->IsDVD())
327     return MEDIA_DETECT::CAutorun::PlayDiscAskResume(m_vecItems->Get(iItem)->GetPath());
328 #endif
329
330   if (pItem->m_bIsShareOrDrive)
331     return false;
332
333   CGUIWindowSlideShow *pSlideShow = (CGUIWindowSlideShow *)g_windowManager.GetWindow(WINDOW_SLIDESHOW);
334   if (!pSlideShow)
335     return false;
336   if (g_application.IsPlayingVideo())
337     g_application.StopPlaying();
338
339   pSlideShow->Reset();
340   for (int i = 0; i < (int)m_vecItems->Size();++i)
341   {
342     CFileItemPtr pItem = m_vecItems->Get(i);
343     if (!pItem->m_bIsFolder && !(URIUtils::IsRAR(pItem->GetPath()) || 
344           URIUtils::IsZIP(pItem->GetPath())) && (pItem->IsPicture() || (
345                                 g_guiSettings.GetBool("pictures.showvideos") &&
346                                 pItem->IsVideo())))
347     {
348       pSlideShow->Add(pItem.get());
349     }
350   }
351
352   if (pSlideShow->NumSlides() == 0)
353     return false;
354
355   pSlideShow->Select(strPicture);
356
357   if (startSlideShow)
358     pSlideShow->StartSlideShow(false);
359
360   m_slideShowStarted = true;
361   g_windowManager.ActivateWindow(WINDOW_SLIDESHOW);
362
363   return true;
364 }
365
366 void CGUIWindowPictures::OnShowPictureRecursive(const CStdString& strPath)
367 {
368   CGUIWindowSlideShow *pSlideShow = (CGUIWindowSlideShow *)g_windowManager.GetWindow(WINDOW_SLIDESHOW);
369   if (pSlideShow)
370   {
371     // stop any video
372     if (g_application.IsPlayingVideo())
373       g_application.StopPlaying();
374     pSlideShow->AddFromPath(strPath, true,
375                             m_guiState->GetSortMethod(),
376                             m_guiState->GetSortOrder());
377     if (pSlideShow->NumSlides())
378     {
379       m_slideShowStarted = true;
380       g_windowManager.ActivateWindow(WINDOW_SLIDESHOW);
381     }
382   }
383 }
384
385 void CGUIWindowPictures::OnSlideShowRecursive(const CStdString &strPicture)
386 {
387   CGUIWindowSlideShow *pSlideShow = (CGUIWindowSlideShow *)g_windowManager.GetWindow(WINDOW_SLIDESHOW);
388   if (pSlideShow)
389   {   
390     CStdString strExtensions;
391     CFileItemList items;
392     CGUIViewState* viewState=CGUIViewState::GetViewState(GetID(), items);
393     if (viewState)
394     {
395       strExtensions = viewState->GetExtensions();
396       delete viewState;
397     }
398     m_slideShowStarted = true;
399     pSlideShow->RunSlideShow(strPicture, true,
400                              g_guiSettings.GetBool("slideshow.shuffle"),false,
401                              m_guiState->GetSortMethod(),
402                              m_guiState->GetSortOrder(),
403                              strExtensions);
404   }
405 }
406
407 void CGUIWindowPictures::OnSlideShowRecursive()
408 {
409   CStdString strEmpty = "";
410   OnSlideShowRecursive(m_vecItems->GetPath());
411 }
412
413 void CGUIWindowPictures::OnSlideShow()
414 {
415   OnSlideShow(m_vecItems->GetPath());
416 }
417
418 void CGUIWindowPictures::OnSlideShow(const CStdString &strPicture)
419 {
420   CGUIWindowSlideShow *pSlideShow = (CGUIWindowSlideShow *)g_windowManager.GetWindow(WINDOW_SLIDESHOW);
421   if (pSlideShow)
422   {    
423     CStdString strExtensions;
424     CFileItemList items;
425     CGUIViewState* viewState=CGUIViewState::GetViewState(GetID(), items);
426     if (viewState)
427     {
428       strExtensions = viewState->GetExtensions();
429       delete viewState;
430     }
431     m_slideShowStarted = true;
432     pSlideShow->RunSlideShow(strPicture, false ,false, false,
433                              m_guiState->GetSortMethod(),
434                              m_guiState->GetSortOrder(),
435                              strExtensions);
436   }
437 }
438
439 void CGUIWindowPictures::OnRegenerateThumbs()
440 {
441   if (m_thumbLoader.IsLoading()) return;
442   m_thumbLoader.SetRegenerateThumbs(true);
443   m_thumbLoader.Load(*m_vecItems);
444 }
445
446 void CGUIWindowPictures::GetContextButtons(int itemNumber, CContextButtons &buttons)
447 {
448   CFileItemPtr item;
449   if (itemNumber >= 0 && itemNumber < m_vecItems->Size())
450     item = m_vecItems->Get(itemNumber);
451
452   if (item && !item->GetProperty("pluginreplacecontextitems").asBoolean())
453   {
454     if ( m_vecItems->IsVirtualDirectoryRoot() && item)
455     {
456       CGUIDialogContextMenu::GetContextButtons("pictures", item, buttons);
457     }
458     else
459     {
460       if (item)
461       {
462         if (!m_vecItems->IsPlugin() && (item->IsPlugin() || item->IsScript()))
463           buttons.Add(CONTEXT_BUTTON_INFO, 24003); // Add-on info
464         if (!(item->m_bIsFolder || item->IsZIP() || item->IsRAR() || item->IsCBZ() || item->IsCBR() || item->IsScript()))
465           buttons.Add(CONTEXT_BUTTON_INFO, 13406); // picture info
466         buttons.Add(CONTEXT_BUTTON_VIEW_SLIDESHOW, item->m_bIsFolder ? 13317 : 13422);      // View Slideshow
467         if (item->m_bIsFolder)
468           buttons.Add(CONTEXT_BUTTON_RECURSIVE_SLIDESHOW, 13318);     // Recursive Slideshow
469
470         if (!m_thumbLoader.IsLoading())
471           buttons.Add(CONTEXT_BUTTON_REFRESH_THUMBS, 13315);         // Create Thumbnails
472         if (g_guiSettings.GetBool("filelists.allowfiledeletion") && !item->IsReadOnly())
473         {
474           buttons.Add(CONTEXT_BUTTON_DELETE, 117);
475           buttons.Add(CONTEXT_BUTTON_RENAME, 118);
476         }
477       }
478
479       if (item->IsPlugin() || item->IsScript() || m_vecItems->IsPlugin())
480         buttons.Add(CONTEXT_BUTTON_PLUGIN_SETTINGS, 1045);
481
482       buttons.Add(CONTEXT_BUTTON_GOTO_ROOT, 20128);
483       buttons.Add(CONTEXT_BUTTON_SWITCH_MEDIA, 523);
484     }
485   }
486   CGUIMediaWindow::GetContextButtons(itemNumber, buttons);
487   if (item && !item->GetProperty("pluginreplacecontextitems").asBoolean())
488     buttons.Add(CONTEXT_BUTTON_SETTINGS, 5);                  // Settings
489 }
490
491 bool CGUIWindowPictures::OnContextButton(int itemNumber, CONTEXT_BUTTON button)
492 {
493   CFileItemPtr item = (itemNumber >= 0 && itemNumber < m_vecItems->Size()) ? m_vecItems->Get(itemNumber) : CFileItemPtr();
494   if (m_vecItems->IsVirtualDirectoryRoot() && item)
495   {
496     if (CGUIDialogContextMenu::OnContextButton("pictures", item, button))
497     {
498       Update("");
499       return true;
500     }
501   }
502   switch (button)
503   {
504   case CONTEXT_BUTTON_VIEW_SLIDESHOW:
505     if (item && item->m_bIsFolder)
506       OnSlideShow(item->GetPath());
507     else
508       ShowPicture(itemNumber, true);
509     return true;
510   case CONTEXT_BUTTON_RECURSIVE_SLIDESHOW:
511     if (item)
512       OnSlideShowRecursive(item->GetPath());
513     return true;
514   case CONTEXT_BUTTON_INFO:
515     OnInfo(itemNumber);
516     return true;
517   case CONTEXT_BUTTON_REFRESH_THUMBS:
518     OnRegenerateThumbs();
519     return true;
520   case CONTEXT_BUTTON_DELETE:
521     OnDeleteItem(itemNumber);
522     return true;
523   case CONTEXT_BUTTON_RENAME:
524     OnRenameItem(itemNumber);
525     return true;
526   case CONTEXT_BUTTON_SETTINGS:
527     g_windowManager.ActivateWindow(WINDOW_SETTINGS_MYPICTURES);
528     return true;
529   case CONTEXT_BUTTON_GOTO_ROOT:
530     Update("");
531     return true;
532   case CONTEXT_BUTTON_SWITCH_MEDIA:
533     CGUIDialogContextMenu::SwitchMedia("pictures", m_vecItems->GetPath());
534     return true;
535   default:
536     break;
537   }
538   return CGUIMediaWindow::OnContextButton(itemNumber, button);
539 }
540
541 void CGUIWindowPictures::OnItemLoaded(CFileItem *pItem)
542 {
543   CPictureThumbLoader::ProcessFoldersAndArchives(pItem);
544 }
545
546 void CGUIWindowPictures::LoadPlayList(const CStdString& strPlayList)
547 {
548   CLog::Log(LOGDEBUG,"CGUIWindowPictures::LoadPlayList()... converting playlist into slideshow: %s", strPlayList.c_str());
549   auto_ptr<CPlayList> pPlayList (CPlayListFactory::Create(strPlayList));
550   if ( NULL != pPlayList.get())
551   {
552     if (!pPlayList->Load(strPlayList))
553     {
554       CGUIDialogOK::ShowAndGetInput(6, 0, 477, 0);
555       return ; //hmmm unable to load playlist?
556     }
557   }
558
559   CPlayList playlist = *pPlayList;
560   if (playlist.size() > 0)
561   {
562     // set up slideshow
563     CGUIWindowSlideShow *pSlideShow = (CGUIWindowSlideShow *)g_windowManager.GetWindow(WINDOW_SLIDESHOW);
564     if (!pSlideShow)
565       return;
566     if (g_application.IsPlayingVideo())
567       g_application.StopPlaying();
568
569     // convert playlist items into slideshow items
570     pSlideShow->Reset();
571     for (int i = 0; i < (int)playlist.size(); ++i)
572     {
573       CFileItemPtr pItem = playlist[i];
574       //CLog::Log(LOGDEBUG,"-- playlist item: %s", pItem->GetPath().c_str());
575       if (pItem->IsPicture() && !(pItem->IsZIP() || pItem->IsRAR() || pItem->IsCBZ() || pItem->IsCBR()))
576         pSlideShow->Add(pItem.get());
577     }
578
579     // start slideshow if there are items
580     pSlideShow->StartSlideShow();
581     if (pSlideShow->NumSlides())
582       g_windowManager.ActivateWindow(WINDOW_SLIDESHOW);
583   }
584 }
585
586 void CGUIWindowPictures::OnInfo(int itemNumber)
587 {
588   CFileItemPtr item = (itemNumber >= 0 && itemNumber < m_vecItems->Size()) ? m_vecItems->Get(itemNumber) : CFileItemPtr();
589   if (!item)
590     return;
591   if (!m_vecItems->IsPlugin() && (item->IsPlugin() || item->IsScript()))
592   {
593     CGUIDialogAddonInfo::ShowForItem(item);
594     return;
595   }
596   if (item->m_bIsFolder || item->IsZIP() || item->IsRAR() || item->IsCBZ() || item->IsCBR() || !item->IsPicture())
597     return;
598   CGUIDialogPictureInfo *pictureInfo = (CGUIDialogPictureInfo *)g_windowManager.GetWindow(WINDOW_DIALOG_PICTURE_INFO);
599   if (pictureInfo)
600   {
601     pictureInfo->SetPicture(item.get());
602     pictureInfo->DoModal();
603   }
604 }
605
606 CStdString CGUIWindowPictures::GetStartFolder(const CStdString &dir)
607 {
608   if (dir.Equals("Plugins") || dir.Equals("Addons"))
609     return "addons://sources/image/";
610
611   SetupShares();
612   VECSOURCES shares;
613   m_rootDir.GetSources(shares);
614   bool bIsSourceName = false;
615   int iIndex = CUtil::GetMatchingSource(dir, shares, bIsSourceName);
616   if (iIndex > -1)
617   {
618     if (iIndex < (int)shares.size() && shares[iIndex].m_iHasLock == 2)
619     {
620       CFileItem item(shares[iIndex]);
621       if (!g_passwordManager.IsItemUnlocked(&item,"pictures"))
622         return "";
623     }
624     if (bIsSourceName)
625       return shares[iIndex].strPath;
626     return dir;
627   }
628   return CGUIMediaWindow::GetStartFolder(dir);
629 }