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