2 * Copyright (C) 2005-2020 Team Kodi
3 * This file is part of Kodi - https://kodi.tv
5 * SPDX-License-Identifier: GPL-2.0-or-later
6 * See LICENSES/README.md for more information.
9 #include "GUIWindowPictures.h"
13 #include "FileItemList.h"
14 #include "GUIDialogPictureInfo.h"
15 #include "GUIPassword.h"
16 #include "GUIWindowSlideShow.h"
17 #include "PictureInfoLoader.h"
18 #include "ServiceBroker.h"
21 #include "addons/gui/GUIDialogAddonInfo.h"
22 #include "application/Application.h"
23 #include "application/ApplicationComponents.h"
24 #include "application/ApplicationPlayer.h"
25 #include "dialogs/GUIDialogMediaSource.h"
26 #include "dialogs/GUIDialogProgress.h"
27 #include "guilib/GUIComponent.h"
28 #include "guilib/GUIWindowManager.h"
29 #include "input/actions/ActionIDs.h"
30 #include "media/MediaLockState.h"
31 #include "messaging/helpers/DialogOKHelper.h"
32 #include "pictures/SlideShowDelegator.h"
33 #include "playlists/PlayList.h"
34 #include "playlists/PlayListFactory.h"
35 #include "settings/MediaSourceSettings.h"
36 #include "settings/Settings.h"
37 #include "settings/SettingsComponent.h"
38 #include "utils/SortUtils.h"
39 #include "utils/StringUtils.h"
40 #include "utils/URIUtils.h"
41 #include "utils/Variant.h"
42 #include "utils/XTimeUtils.h"
43 #include "utils/log.h"
44 #include "video/VideoFileItemClassify.h"
45 #include "view/GUIViewState.h"
47 #define CONTROL_BTNSORTASC 4
48 #define CONTROL_LABELFILES 12
50 using namespace XFILE
;
51 using namespace KODI::MESSAGING
;
54 using namespace std::chrono_literals
;
56 #define CONTROL_BTNSLIDESHOW 6
57 #define CONTROL_BTNSLIDESHOW_RECURSIVE 7
58 #define CONTROL_SHUFFLE 9
60 CGUIWindowPictures::CGUIWindowPictures(void)
61 : CGUIMediaWindow(WINDOW_PICTURES
, "MyPics.xml")
63 m_thumbLoader
.SetObserver(this);
64 m_slideShowStarted
= false;
68 void CGUIWindowPictures::OnInitWindow()
70 CGUIMediaWindow::OnInitWindow();
71 if (m_slideShowStarted
)
73 CSlideShowDelegator
& slideShow
= CServiceBroker::GetSlideShowDelegator();
75 if (slideShow
.GetCurrentSlide())
76 path
= URIUtils::GetDirectory(slideShow
.GetCurrentSlide()->GetPath());
77 if (m_vecItems
->IsPath(path
))
79 if (slideShow
.GetCurrentSlide())
80 m_viewControl
.SetSelectedItem(slideShow
.GetCurrentSlide()->GetPath());
81 SaveSelectedItemInHistory();
83 m_slideShowStarted
= false;
87 CGUIWindowPictures::~CGUIWindowPictures(void) = default;
89 bool CGUIWindowPictures::OnMessage(CGUIMessage
& message
)
91 switch ( message
.GetMessage() )
93 case GUI_MSG_WINDOW_DEINIT
:
95 if (m_thumbLoader
.IsLoading())
96 m_thumbLoader
.StopThread();
101 case GUI_MSG_WINDOW_INIT
:
103 // is this the first time accessing this window?
104 if (m_vecItems
->GetPath() == "?" && message
.GetStringParam().empty())
105 message
.SetStringParam(CMediaSourceSettings::GetInstance().GetDefaultSource("pictures"));
107 m_dlgProgress
= CServiceBroker::GetGUI()->GetWindowManager().GetWindow
<CGUIDialogProgress
>(WINDOW_DIALOG_PROGRESS
);
111 case GUI_MSG_CLICKED
:
113 int iControl
= message
.GetSenderId();
114 if (iControl
== CONTROL_BTNSLIDESHOW
) // Slide Show
118 else if (iControl
== CONTROL_BTNSLIDESHOW_RECURSIVE
) // Recursive Slide Show
120 OnSlideShowRecursive();
122 else if (iControl
== CONTROL_SHUFFLE
)
124 const std::shared_ptr
<CSettings
> settings
= CServiceBroker::GetSettingsComponent()->GetSettings();
125 settings
->ToggleBool(CSettings::SETTING_SLIDESHOW_SHUFFLE
);
128 else if (m_viewControl
.HasControl(iControl
)) // list/thumb control
130 int iItem
= m_viewControl
.GetSelectedItem();
131 int iAction
= message
.GetParam1();
133 // iItem is checked for validity inside these routines
134 if (iAction
== ACTION_DELETE_ITEM
)
136 // is delete allowed?
137 if (CServiceBroker::GetSettingsComponent()->GetSettings()->GetBool(CSettings::SETTING_FILELISTS_ALLOWFILEDELETION
))
142 else if (iAction
== ACTION_PLAYER_PLAY
)
144 ShowPicture(iItem
, true);
147 else if (iAction
== ACTION_SHOW_INFO
)
156 return CGUIMediaWindow::OnMessage(message
);
159 void CGUIWindowPictures::UpdateButtons()
161 CGUIMediaWindow::UpdateButtons();
163 // Update the shuffle button
164 SET_CONTROL_SELECTED(GetID(), CONTROL_SHUFFLE
, CServiceBroker::GetSettingsComponent()->GetSettings()->GetBool(CSettings::SETTING_SLIDESHOW_SHUFFLE
));
166 // check we can slideshow or recursive slideshow
167 int nFolders
= m_vecItems
->GetFolderCount();
168 if (nFolders
== m_vecItems
->Size() ||
169 m_vecItems
->GetPath() == "addons://sources/image/")
171 CONTROL_DISABLE(CONTROL_BTNSLIDESHOW
);
175 CONTROL_ENABLE(CONTROL_BTNSLIDESHOW
);
177 if (m_guiState
.get() && !m_guiState
->HideParentDirItems())
179 if (m_vecItems
->Size() == 0 || nFolders
== 0 ||
180 m_vecItems
->GetPath() == "addons://sources/image/")
182 CONTROL_DISABLE(CONTROL_BTNSLIDESHOW_RECURSIVE
);
186 CONTROL_ENABLE(CONTROL_BTNSLIDESHOW_RECURSIVE
);
190 void CGUIWindowPictures::OnPrepareFileItems(CFileItemList
& items
)
192 CGUIMediaWindow::OnPrepareFileItems(items
);
194 for (int i
=0;i
<items
.Size();++i
)
195 if (StringUtils::EqualsNoCase(items
[i
]->GetLabel(), "folder.jpg"))
198 if (items
.GetFolderCount() == items
.Size() || !CServiceBroker::GetSettingsComponent()->GetSettings()->GetBool(CSettings::SETTING_PICTURES_USETAGS
))
201 // Start the music info loader thread
202 CPictureInfoLoader loader
;
203 loader
.SetProgressCallback(m_dlgProgress
);
206 bool bShowProgress
= !CServiceBroker::GetGUI()->GetWindowManager().HasModalDialog(true);
207 bool bProgressVisible
= false;
209 auto start
= std::chrono::steady_clock::now();
211 while (loader
.IsLoading() && m_dlgProgress
&& !m_dlgProgress
->IsCanceled())
214 { // Do we have to init a progress dialog?
215 auto end
= std::chrono::steady_clock::now();
216 auto duration
= std::chrono::duration_cast
<std::chrono::milliseconds
>(end
- start
);
218 if (!bProgressVisible
&& duration
.count() > 1500 && m_dlgProgress
)
219 { // tag loading takes more then 1.5 secs, show a progress dialog
220 CURL
url(items
.GetPath());
222 m_dlgProgress
->SetHeading(CVariant
{189});
223 m_dlgProgress
->SetLine(0, CVariant
{505});
224 m_dlgProgress
->SetLine(1, CVariant
{""});
225 m_dlgProgress
->SetLine(2, CVariant
{url
.GetWithoutUserDetails()});
226 m_dlgProgress
->Open();
227 m_dlgProgress
->ShowProgressBar(true);
228 bProgressVisible
= true;
231 if (bProgressVisible
&& m_dlgProgress
)
233 m_dlgProgress
->Progress();
235 } // if (bShowProgress)
236 KODI::TIME::Sleep(1ms
);
237 } // while (loader.IsLoading())
239 if (bProgressVisible
&& m_dlgProgress
)
240 m_dlgProgress
->Close();
243 bool CGUIWindowPictures::Update(const std::string
&strDirectory
, bool updateFilterPath
/* = true */)
245 if (m_thumbLoader
.IsLoading())
246 m_thumbLoader
.StopThread();
248 if (!CGUIMediaWindow::Update(strDirectory
, updateFilterPath
))
251 m_vecItems
->SetArt("thumb", "");
252 if (CServiceBroker::GetSettingsComponent()->GetSettings()->GetBool(CSettings::SETTING_PICTURES_GENERATETHUMBS
))
253 m_thumbLoader
.Load(*m_vecItems
);
255 CPictureThumbLoader thumbLoader
;
256 std::string thumb
= thumbLoader
.GetCachedImage(*m_vecItems
, "thumb");
257 m_vecItems
->SetArt("thumb", thumb
);
262 bool CGUIWindowPictures::OnClick(int iItem
, const std::string
&player
)
264 if ( iItem
< 0 || iItem
>= m_vecItems
->Size() ) return true;
265 CFileItemPtr pItem
= m_vecItems
->Get(iItem
);
267 if (pItem
->IsCBZ() || pItem
->IsCBR())
271 pathToUrl
= URIUtils::CreateArchivePath("zip", pItem
->GetURL(), "");
273 pathToUrl
= URIUtils::CreateArchivePath("rar", pItem
->GetURL(), "");
275 OnShowPictureRecursive(pathToUrl
.Get());
278 else if (CGUIMediaWindow::OnClick(iItem
, player
))
284 bool CGUIWindowPictures::GetDirectory(const std::string
&strDirectory
, CFileItemList
& items
)
286 if (!CGUIMediaWindow::GetDirectory(strDirectory
, items
))
290 if (items
.GetLabel().empty() && m_rootDir
.IsSource(items
.GetPath(), CMediaSourceSettings::GetInstance().GetSources("pictures"), &label
))
291 items
.SetLabel(label
);
293 if (items
.GetContent().empty() && !items
.IsVirtualDirectoryRoot() && !items
.IsPlugin())
294 items
.SetContent("images");
298 bool CGUIWindowPictures::OnPlayMedia(int iItem
, const std::string
&player
)
300 if (VIDEO::IsVideo(*m_vecItems
->Get(iItem
)))
301 return CGUIMediaWindow::OnPlayMedia(iItem
);
303 return ShowPicture(iItem
, false);
306 bool CGUIWindowPictures::ShowPicture(int iItem
, bool startSlideShow
)
308 if ( iItem
< 0 || iItem
>= m_vecItems
->Size() ) return false;
309 CFileItemPtr pItem
= m_vecItems
->Get(iItem
);
310 std::string strPicture
= pItem
->GetPath();
312 #ifdef HAS_OPTICAL_DRIVE
314 return MEDIA_DETECT::CAutorun::PlayDiscAskResume(m_vecItems
->Get(iItem
)->GetPath());
317 if (pItem
->m_bIsShareOrDrive
)
320 //! @todo this should be reactive, based on a given event app player should stop the playback
321 const auto& components
= CServiceBroker::GetAppComponents();
322 const auto appPlayer
= components
.GetComponent
<CApplicationPlayer
>();
323 if (appPlayer
->IsPlayingVideo())
324 g_application
.StopPlaying();
326 CSlideShowDelegator
& slideShow
= CServiceBroker::GetSlideShowDelegator();
328 bool bShowVideos
= CServiceBroker::GetSettingsComponent()->GetSettings()->GetBool(CSettings::SETTING_PICTURES_SHOWVIDEOS
);
329 for (const auto& pItem
: *m_vecItems
)
331 if (!pItem
->m_bIsFolder
&&
332 !(URIUtils::IsRAR(pItem
->GetPath()) || URIUtils::IsZIP(pItem
->GetPath())) &&
333 (pItem
->IsPicture() || (bShowVideos
&& VIDEO::IsVideo(*pItem
))))
335 slideShow
.Add(pItem
.get());
339 if (slideShow
.NumSlides() == 0)
342 slideShow
.Select(strPicture
);
345 slideShow
.StartSlideShow();
348 slideShow
.PlayPicture();
351 //! @todo this should trigger some event that should led the window manager to activate another window
352 // look into using OnPlay announce!
353 m_slideShowStarted
= true;
354 CServiceBroker::GetGUI()->GetWindowManager().ActivateWindow(WINDOW_SLIDESHOW
);
359 void CGUIWindowPictures::OnShowPictureRecursive(const std::string
& strPath
)
362 //! @todo this should be reactive, based on a given event app player should stop the playback
363 const auto& components
= CServiceBroker::GetAppComponents();
364 const auto appPlayer
= components
.GetComponent
<CApplicationPlayer
>();
365 if (appPlayer
->IsPlayingVideo())
366 g_application
.StopPlaying();
368 CSlideShowDelegator
& slideShow
= CServiceBroker::GetSlideShowDelegator();
369 SortDescription sorting
= m_guiState
->GetSortMethod();
370 slideShow
.AddFromPath(strPath
, true, sorting
.sortBy
, sorting
.sortOrder
, sorting
.sortAttributes
);
371 //! @todo window manager should react to a given event and start the window itself!
372 if (slideShow
.NumSlides())
374 m_slideShowStarted
= true;
375 CServiceBroker::GetGUI()->GetWindowManager().ActivateWindow(WINDOW_SLIDESHOW
);
379 void CGUIWindowPictures::OnSlideShowRecursive(const std::string
&strPicture
)
382 std::string strExtensions
;
384 CGUIViewState
* viewState
=CGUIViewState::GetViewState(GetID(), items
);
387 strExtensions
= viewState
->GetExtensions();
390 m_slideShowStarted
= true;
392 CSlideShowDelegator
& slideShow
= CServiceBroker::GetSlideShowDelegator();
393 SortDescription sorting
= m_guiState
->GetSortMethod();
394 slideShow
.RunSlideShow(strPicture
, true,
395 CServiceBroker::GetSettingsComponent()->GetSettings()->GetBool(
396 CSettings::SETTING_SLIDESHOW_SHUFFLE
),
397 false, "", true, sorting
.sortBy
, sorting
.sortOrder
,
398 sorting
.sortAttributes
, strExtensions
);
401 void CGUIWindowPictures::OnSlideShowRecursive()
403 OnSlideShowRecursive(m_vecItems
->GetPath());
406 void CGUIWindowPictures::OnSlideShow()
408 OnSlideShow(m_vecItems
->GetPath());
411 void CGUIWindowPictures::OnSlideShow(const std::string
&strPicture
)
413 std::string strExtensions
;
415 CGUIViewState
* viewState
= CGUIViewState::GetViewState(GetID(), items
);
418 strExtensions
= viewState
->GetExtensions();
421 CSlideShowDelegator
& slideShow
= CServiceBroker::GetSlideShowDelegator();
422 m_slideShowStarted
= true;
423 SortDescription sorting
= m_guiState
->GetSortMethod();
424 slideShow
.RunSlideShow(strPicture
, false, false, false, "", true, sorting
.sortBy
,
425 sorting
.sortOrder
, sorting
.sortAttributes
, strExtensions
);
428 void CGUIWindowPictures::OnRegenerateThumbs()
430 if (m_thumbLoader
.IsLoading()) return;
431 m_thumbLoader
.SetRegenerateThumbs(true);
432 m_thumbLoader
.Load(*m_vecItems
);
435 void CGUIWindowPictures::GetContextButtons(int itemNumber
, CContextButtons
&buttons
)
438 if (itemNumber
>= 0 && itemNumber
< m_vecItems
->Size())
439 item
= m_vecItems
->Get(itemNumber
);
443 if ( m_vecItems
->IsVirtualDirectoryRoot() || m_vecItems
->GetPath() == "sources://pictures/" )
445 CGUIDialogContextMenu::GetContextButtons("pictures", item
, buttons
);
451 if (!(item
->m_bIsFolder
|| item
->IsZIP() || item
->IsRAR() || item
->IsCBZ() || item
->IsCBR() || item
->IsScript()))
453 if (item
->IsPicture())
454 buttons
.Add(CONTEXT_BUTTON_INFO
, 13406); // picture info
455 buttons
.Add(CONTEXT_BUTTON_VIEW_SLIDESHOW
, item
->m_bIsFolder
? 13317 : 13422); // View Slideshow
457 if (item
->m_bIsFolder
)
458 buttons
.Add(CONTEXT_BUTTON_RECURSIVE_SLIDESHOW
, 13318); // Recursive Slideshow
460 if (!m_thumbLoader
.IsLoading())
461 buttons
.Add(CONTEXT_BUTTON_REFRESH_THUMBS
, 13315); // Create Thumbnails
462 if (CServiceBroker::GetSettingsComponent()->GetSettings()->GetBool(CSettings::SETTING_FILELISTS_ALLOWFILEDELETION
) && !item
->IsReadOnly())
464 buttons
.Add(CONTEXT_BUTTON_DELETE
, 117);
465 buttons
.Add(CONTEXT_BUTTON_RENAME
, 118);
469 if (!item
->IsPlugin() && !item
->IsScript() && !m_vecItems
->IsPlugin())
470 buttons
.Add(CONTEXT_BUTTON_SWITCH_MEDIA
, 523);
473 CGUIMediaWindow::GetContextButtons(itemNumber
, buttons
);
476 bool CGUIWindowPictures::OnContextButton(int itemNumber
, CONTEXT_BUTTON button
)
478 CFileItemPtr item
= (itemNumber
>= 0 && itemNumber
< m_vecItems
->Size()) ? m_vecItems
->Get(itemNumber
) : CFileItemPtr();
479 if (CGUIDialogContextMenu::OnContextButton("pictures", item
, button
))
486 case CONTEXT_BUTTON_VIEW_SLIDESHOW
:
487 if (item
&& item
->m_bIsFolder
)
488 OnSlideShow(item
->GetPath());
490 ShowPicture(itemNumber
, true);
492 case CONTEXT_BUTTON_RECURSIVE_SLIDESHOW
:
494 OnSlideShowRecursive(item
->GetPath());
496 case CONTEXT_BUTTON_INFO
:
497 OnItemInfo(itemNumber
);
499 case CONTEXT_BUTTON_REFRESH_THUMBS
:
500 OnRegenerateThumbs();
502 case CONTEXT_BUTTON_DELETE
:
503 OnDeleteItem(itemNumber
);
505 case CONTEXT_BUTTON_RENAME
:
506 OnRenameItem(itemNumber
);
508 case CONTEXT_BUTTON_SWITCH_MEDIA
:
509 CGUIDialogContextMenu::SwitchMedia("pictures", m_vecItems
->GetPath());
514 return CGUIMediaWindow::OnContextButton(itemNumber
, button
);
517 bool CGUIWindowPictures::OnAddMediaSource()
519 return CGUIDialogMediaSource::ShowAndAddMediaSource("pictures");
522 void CGUIWindowPictures::OnItemLoaded(CFileItem
*pItem
)
524 CPictureThumbLoader::ProcessFoldersAndArchives(pItem
);
527 void CGUIWindowPictures::LoadPlayList(const std::string
& strPlayList
)
530 "CGUIWindowPictures::LoadPlayList()... converting playlist into slideshow: {}",
532 std::unique_ptr
<PLAYLIST::CPlayList
> pPlayList(PLAYLIST::CPlayListFactory::Create(strPlayList
));
533 if (nullptr != pPlayList
)
535 if (!pPlayList
->Load(strPlayList
))
537 HELPERS::ShowOKDialogText(CVariant
{6}, CVariant
{477});
538 return ; //hmmm unable to load playlist?
542 PLAYLIST::CPlayList playlist
= *pPlayList
;
543 if (playlist
.size() > 0)
545 //! @todo this should be reactive, based on a given event app player should stop the playback
546 const auto& components
= CServiceBroker::GetAppComponents();
547 const auto appPlayer
= components
.GetComponent
<CApplicationPlayer
>();
548 if (appPlayer
->IsPlayingVideo())
549 g_application
.StopPlaying();
551 CSlideShowDelegator
& slideShow
= CServiceBroker::GetSlideShowDelegator();
552 // convert playlist items into slideshow items
554 for (int i
= 0; i
< playlist
.size(); ++i
)
556 CFileItemPtr pItem
= playlist
[i
];
557 //CLog::Log(LOGDEBUG,"-- playlist item: {}", pItem->GetPath());
558 if (pItem
->IsPicture() && !(pItem
->IsZIP() || pItem
->IsRAR() || pItem
->IsCBZ() || pItem
->IsCBR()))
560 slideShow
.Add(pItem
.get());
564 // start slideshow if there are items
565 slideShow
.StartSlideShow();
566 //! @todo this should be reactive based on a triggered event the window manager is the only component
567 // that should be responsible to activate the slideshow window
568 if (slideShow
.NumSlides())
569 CServiceBroker::GetGUI()->GetWindowManager().ActivateWindow(WINDOW_SLIDESHOW
);
573 void CGUIWindowPictures::OnItemInfo(int itemNumber
)
575 CFileItemPtr item
= m_vecItems
->Get(itemNumber
);
578 if (!m_vecItems
->IsPlugin() && (item
->IsPlugin() || item
->IsScript()))
580 CGUIDialogAddonInfo::ShowForItem(item
);
583 if (item
->m_bIsFolder
|| item
->IsZIP() || item
->IsRAR() || item
->IsCBZ() || item
->IsCBR() || !item
->IsPicture())
585 CGUIDialogPictureInfo
*pictureInfo
= CServiceBroker::GetGUI()->GetWindowManager().GetWindow
<CGUIDialogPictureInfo
>(WINDOW_DIALOG_PICTURE_INFO
);
588 pictureInfo
->SetPicture(item
.get());
593 std::string
CGUIWindowPictures::GetStartFolder(const std::string
&dir
)
595 if (StringUtils::EqualsNoCase(dir
, "plugins") ||
596 StringUtils::EqualsNoCase(dir
, "addons"))
597 return "addons://sources/image/";
601 m_rootDir
.GetSources(shares
);
602 bool bIsSourceName
= false;
603 int iIndex
= CUtil::GetMatchingSource(dir
, shares
, bIsSourceName
);
606 if (iIndex
< static_cast<int>(shares
.size()) && shares
[iIndex
].m_iHasLock
== LOCK_STATE_LOCKED
)
608 CFileItem
item(shares
[iIndex
]);
609 if (!g_passwordManager
.IsItemUnlocked(&item
,"pictures"))
613 return shares
[iIndex
].strPath
;
616 return CGUIMediaWindow::GetStartFolder(dir
);