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 "GUIDialogPictureInfo.h"
14 #include "GUIPassword.h"
15 #include "GUIWindowSlideShow.h"
16 #include "PictureInfoLoader.h"
17 #include "ServiceBroker.h"
20 #include "addons/gui/GUIDialogAddonInfo.h"
21 #include "application/Application.h"
22 #include "application/ApplicationComponents.h"
23 #include "application/ApplicationPlayer.h"
24 #include "dialogs/GUIDialogMediaSource.h"
25 #include "dialogs/GUIDialogProgress.h"
26 #include "guilib/GUIComponent.h"
27 #include "guilib/GUIWindowManager.h"
28 #include "input/actions/ActionIDs.h"
29 #include "interfaces/AnnouncementManager.h"
30 #include "media/MediaLockState.h"
31 #include "messaging/helpers/DialogOKHelper.h"
32 #include "playlists/PlayList.h"
33 #include "playlists/PlayListFactory.h"
34 #include "settings/MediaSourceSettings.h"
35 #include "settings/Settings.h"
36 #include "settings/SettingsComponent.h"
37 #include "utils/SortUtils.h"
38 #include "utils/StringUtils.h"
39 #include "utils/URIUtils.h"
40 #include "utils/Variant.h"
41 #include "utils/XTimeUtils.h"
42 #include "utils/log.h"
43 #include "view/GUIViewState.h"
45 #define CONTROL_BTNSORTASC 4
46 #define CONTROL_LABELFILES 12
48 using namespace XFILE
;
49 using namespace KODI::MESSAGING
;
51 using namespace std::chrono_literals
;
53 #define CONTROL_BTNSLIDESHOW 6
54 #define CONTROL_BTNSLIDESHOW_RECURSIVE 7
55 #define CONTROL_SHUFFLE 9
57 CGUIWindowPictures::CGUIWindowPictures(void)
58 : CGUIMediaWindow(WINDOW_PICTURES
, "MyPics.xml")
60 m_thumbLoader
.SetObserver(this);
61 m_slideShowStarted
= false;
65 void CGUIWindowPictures::OnInitWindow()
67 CGUIMediaWindow::OnInitWindow();
68 if (m_slideShowStarted
)
70 CGUIWindowSlideShow
* wndw
= CServiceBroker::GetGUI()->GetWindowManager().GetWindow
<CGUIWindowSlideShow
>(WINDOW_SLIDESHOW
);
72 if (wndw
&& wndw
->GetCurrentSlide())
73 path
= URIUtils::GetDirectory(wndw
->GetCurrentSlide()->GetPath());
74 if (m_vecItems
->IsPath(path
))
76 if (wndw
&& wndw
->GetCurrentSlide())
77 m_viewControl
.SetSelectedItem(wndw
->GetCurrentSlide()->GetPath());
78 SaveSelectedItemInHistory();
80 m_slideShowStarted
= false;
84 CGUIWindowPictures::~CGUIWindowPictures(void) = default;
86 bool CGUIWindowPictures::OnMessage(CGUIMessage
& message
)
88 switch ( message
.GetMessage() )
90 case GUI_MSG_WINDOW_DEINIT
:
92 if (m_thumbLoader
.IsLoading())
93 m_thumbLoader
.StopThread();
98 case GUI_MSG_WINDOW_INIT
:
100 // is this the first time accessing this window?
101 if (m_vecItems
->GetPath() == "?" && message
.GetStringParam().empty())
102 message
.SetStringParam(CMediaSourceSettings::GetInstance().GetDefaultSource("pictures"));
104 m_dlgProgress
= CServiceBroker::GetGUI()->GetWindowManager().GetWindow
<CGUIDialogProgress
>(WINDOW_DIALOG_PROGRESS
);
108 case GUI_MSG_CLICKED
:
110 int iControl
= message
.GetSenderId();
111 if (iControl
== CONTROL_BTNSLIDESHOW
) // Slide Show
115 else if (iControl
== CONTROL_BTNSLIDESHOW_RECURSIVE
) // Recursive Slide Show
117 OnSlideShowRecursive();
119 else if (iControl
== CONTROL_SHUFFLE
)
121 const std::shared_ptr
<CSettings
> settings
= CServiceBroker::GetSettingsComponent()->GetSettings();
122 settings
->ToggleBool(CSettings::SETTING_SLIDESHOW_SHUFFLE
);
125 else if (m_viewControl
.HasControl(iControl
)) // list/thumb control
127 int iItem
= m_viewControl
.GetSelectedItem();
128 int iAction
= message
.GetParam1();
130 // iItem is checked for validity inside these routines
131 if (iAction
== ACTION_DELETE_ITEM
)
133 // is delete allowed?
134 if (CServiceBroker::GetSettingsComponent()->GetSettings()->GetBool(CSettings::SETTING_FILELISTS_ALLOWFILEDELETION
))
139 else if (iAction
== ACTION_PLAYER_PLAY
)
141 ShowPicture(iItem
, true);
144 else if (iAction
== ACTION_SHOW_INFO
)
153 return CGUIMediaWindow::OnMessage(message
);
156 void CGUIWindowPictures::UpdateButtons()
158 CGUIMediaWindow::UpdateButtons();
160 // Update the shuffle button
161 SET_CONTROL_SELECTED(GetID(), CONTROL_SHUFFLE
, CServiceBroker::GetSettingsComponent()->GetSettings()->GetBool(CSettings::SETTING_SLIDESHOW_SHUFFLE
));
163 // check we can slideshow or recursive slideshow
164 int nFolders
= m_vecItems
->GetFolderCount();
165 if (nFolders
== m_vecItems
->Size() ||
166 m_vecItems
->GetPath() == "addons://sources/image/")
168 CONTROL_DISABLE(CONTROL_BTNSLIDESHOW
);
172 CONTROL_ENABLE(CONTROL_BTNSLIDESHOW
);
174 if (m_guiState
.get() && !m_guiState
->HideParentDirItems())
176 if (m_vecItems
->Size() == 0 || nFolders
== 0 ||
177 m_vecItems
->GetPath() == "addons://sources/image/")
179 CONTROL_DISABLE(CONTROL_BTNSLIDESHOW_RECURSIVE
);
183 CONTROL_ENABLE(CONTROL_BTNSLIDESHOW_RECURSIVE
);
187 void CGUIWindowPictures::OnPrepareFileItems(CFileItemList
& items
)
189 CGUIMediaWindow::OnPrepareFileItems(items
);
191 for (int i
=0;i
<items
.Size();++i
)
192 if (StringUtils::EqualsNoCase(items
[i
]->GetLabel(), "folder.jpg"))
195 if (items
.GetFolderCount() == items
.Size() || !CServiceBroker::GetSettingsComponent()->GetSettings()->GetBool(CSettings::SETTING_PICTURES_USETAGS
))
198 // Start the music info loader thread
199 CPictureInfoLoader loader
;
200 loader
.SetProgressCallback(m_dlgProgress
);
203 bool bShowProgress
= !CServiceBroker::GetGUI()->GetWindowManager().HasModalDialog(true);
204 bool bProgressVisible
= false;
206 auto start
= std::chrono::steady_clock::now();
208 while (loader
.IsLoading() && m_dlgProgress
&& !m_dlgProgress
->IsCanceled())
211 { // Do we have to init a progress dialog?
212 auto end
= std::chrono::steady_clock::now();
213 auto duration
= std::chrono::duration_cast
<std::chrono::milliseconds
>(end
- start
);
215 if (!bProgressVisible
&& duration
.count() > 1500 && m_dlgProgress
)
216 { // tag loading takes more then 1.5 secs, show a progress dialog
217 CURL
url(items
.GetPath());
219 m_dlgProgress
->SetHeading(CVariant
{189});
220 m_dlgProgress
->SetLine(0, CVariant
{505});
221 m_dlgProgress
->SetLine(1, CVariant
{""});
222 m_dlgProgress
->SetLine(2, CVariant
{url
.GetWithoutUserDetails()});
223 m_dlgProgress
->Open();
224 m_dlgProgress
->ShowProgressBar(true);
225 bProgressVisible
= true;
228 if (bProgressVisible
&& m_dlgProgress
)
230 m_dlgProgress
->Progress();
232 } // if (bShowProgress)
233 KODI::TIME::Sleep(1ms
);
234 } // while (loader.IsLoading())
236 if (bProgressVisible
&& m_dlgProgress
)
237 m_dlgProgress
->Close();
240 bool CGUIWindowPictures::Update(const std::string
&strDirectory
, bool updateFilterPath
/* = true */)
242 if (m_thumbLoader
.IsLoading())
243 m_thumbLoader
.StopThread();
245 if (!CGUIMediaWindow::Update(strDirectory
, updateFilterPath
))
248 m_vecItems
->SetArt("thumb", "");
249 if (CServiceBroker::GetSettingsComponent()->GetSettings()->GetBool(CSettings::SETTING_PICTURES_GENERATETHUMBS
))
250 m_thumbLoader
.Load(*m_vecItems
);
252 CPictureThumbLoader thumbLoader
;
253 std::string thumb
= thumbLoader
.GetCachedImage(*m_vecItems
, "thumb");
254 m_vecItems
->SetArt("thumb", thumb
);
259 bool CGUIWindowPictures::OnClick(int iItem
, const std::string
&player
)
261 if ( iItem
< 0 || iItem
>= m_vecItems
->Size() ) return true;
262 CFileItemPtr pItem
= m_vecItems
->Get(iItem
);
264 if (pItem
->IsCBZ() || pItem
->IsCBR())
268 pathToUrl
= URIUtils::CreateArchivePath("zip", pItem
->GetURL(), "");
270 pathToUrl
= URIUtils::CreateArchivePath("rar", pItem
->GetURL(), "");
272 OnShowPictureRecursive(pathToUrl
.Get());
275 else if (CGUIMediaWindow::OnClick(iItem
, player
))
281 bool CGUIWindowPictures::GetDirectory(const std::string
&strDirectory
, CFileItemList
& items
)
283 if (!CGUIMediaWindow::GetDirectory(strDirectory
, items
))
287 if (items
.GetLabel().empty() && m_rootDir
.IsSource(items
.GetPath(), CMediaSourceSettings::GetInstance().GetSources("pictures"), &label
))
288 items
.SetLabel(label
);
290 if (items
.GetContent().empty() && !items
.IsVirtualDirectoryRoot() && !items
.IsPlugin())
291 items
.SetContent("images");
295 bool CGUIWindowPictures::OnPlayMedia(int iItem
, const std::string
&player
)
297 if (m_vecItems
->Get(iItem
)->IsVideo())
298 return CGUIMediaWindow::OnPlayMedia(iItem
);
300 return ShowPicture(iItem
, false);
303 bool CGUIWindowPictures::ShowPicture(int iItem
, bool startSlideShow
)
305 if ( iItem
< 0 || iItem
>= m_vecItems
->Size() ) return false;
306 CFileItemPtr pItem
= m_vecItems
->Get(iItem
);
307 std::string strPicture
= pItem
->GetPath();
311 return MEDIA_DETECT::CAutorun::PlayDiscAskResume(m_vecItems
->Get(iItem
)->GetPath());
314 if (pItem
->m_bIsShareOrDrive
)
317 CGUIWindowSlideShow
*pSlideShow
= CServiceBroker::GetGUI()->GetWindowManager().GetWindow
<CGUIWindowSlideShow
>(WINDOW_SLIDESHOW
);
320 const auto& components
= CServiceBroker::GetAppComponents();
321 const auto appPlayer
= components
.GetComponent
<CApplicationPlayer
>();
322 if (appPlayer
->IsPlayingVideo())
323 g_application
.StopPlaying();
326 bool bShowVideos
= CServiceBroker::GetSettingsComponent()->GetSettings()->GetBool(CSettings::SETTING_PICTURES_SHOWVIDEOS
);
327 for (const auto& pItem
: *m_vecItems
)
329 if (!pItem
->m_bIsFolder
&&
330 !(URIUtils::IsRAR(pItem
->GetPath()) || URIUtils::IsZIP(pItem
->GetPath())) &&
331 (pItem
->IsPicture() || (bShowVideos
&& pItem
->IsVideo())))
333 pSlideShow
->Add(pItem
.get());
337 if (pSlideShow
->NumSlides() == 0)
340 pSlideShow
->Select(strPicture
);
343 pSlideShow
->StartSlideShow();
347 param
["player"]["speed"] = 1;
348 param
["player"]["playerid"] = PLAYLIST::TYPE_PICTURE
;
349 CServiceBroker::GetAnnouncementManager()->Announce(ANNOUNCEMENT::Player
, "OnPlay",
350 pSlideShow
->GetCurrentSlide(), param
);
353 m_slideShowStarted
= true;
354 CServiceBroker::GetGUI()->GetWindowManager().ActivateWindow(WINDOW_SLIDESHOW
);
359 void CGUIWindowPictures::OnShowPictureRecursive(const std::string
& strPath
)
361 CGUIWindowSlideShow
*pSlideShow
= CServiceBroker::GetGUI()->GetWindowManager().GetWindow
<CGUIWindowSlideShow
>(WINDOW_SLIDESHOW
);
365 const auto& components
= CServiceBroker::GetAppComponents();
366 const auto appPlayer
= components
.GetComponent
<CApplicationPlayer
>();
367 if (appPlayer
->IsPlayingVideo())
368 g_application
.StopPlaying();
370 SortDescription sorting
= m_guiState
->GetSortMethod();
371 pSlideShow
->AddFromPath(strPath
, true,
372 sorting
.sortBy
, sorting
.sortOrder
, sorting
.sortAttributes
);
373 if (pSlideShow
->NumSlides())
375 m_slideShowStarted
= true;
376 CServiceBroker::GetGUI()->GetWindowManager().ActivateWindow(WINDOW_SLIDESHOW
);
381 void CGUIWindowPictures::OnSlideShowRecursive(const std::string
&strPicture
)
383 CGUIWindowSlideShow
*pSlideShow
= CServiceBroker::GetGUI()->GetWindowManager().GetWindow
<CGUIWindowSlideShow
>(WINDOW_SLIDESHOW
);
386 std::string strExtensions
;
388 CGUIViewState
* viewState
=CGUIViewState::GetViewState(GetID(), items
);
391 strExtensions
= viewState
->GetExtensions();
394 m_slideShowStarted
= true;
396 SortDescription sorting
= m_guiState
->GetSortMethod();
397 pSlideShow
->RunSlideShow(strPicture
, true,
398 CServiceBroker::GetSettingsComponent()->GetSettings()->GetBool(CSettings::SETTING_SLIDESHOW_SHUFFLE
),false,
400 sorting
.sortBy
, sorting
.sortOrder
, sorting
.sortAttributes
,
405 void CGUIWindowPictures::OnSlideShowRecursive()
407 OnSlideShowRecursive(m_vecItems
->GetPath());
410 void CGUIWindowPictures::OnSlideShow()
412 OnSlideShow(m_vecItems
->GetPath());
415 void CGUIWindowPictures::OnSlideShow(const std::string
&strPicture
)
417 CGUIWindowSlideShow
*pSlideShow
= CServiceBroker::GetGUI()->GetWindowManager().GetWindow
<CGUIWindowSlideShow
>(WINDOW_SLIDESHOW
);
420 std::string strExtensions
;
422 CGUIViewState
* viewState
=CGUIViewState::GetViewState(GetID(), items
);
425 strExtensions
= viewState
->GetExtensions();
428 m_slideShowStarted
= true;
430 SortDescription sorting
= m_guiState
->GetSortMethod();
431 pSlideShow
->RunSlideShow(strPicture
, false ,false, false,
433 sorting
.sortBy
, sorting
.sortOrder
, sorting
.sortAttributes
,
438 void CGUIWindowPictures::OnRegenerateThumbs()
440 if (m_thumbLoader
.IsLoading()) return;
441 m_thumbLoader
.SetRegenerateThumbs(true);
442 m_thumbLoader
.Load(*m_vecItems
);
445 void CGUIWindowPictures::GetContextButtons(int itemNumber
, CContextButtons
&buttons
)
448 if (itemNumber
>= 0 && itemNumber
< m_vecItems
->Size())
449 item
= m_vecItems
->Get(itemNumber
);
453 if ( m_vecItems
->IsVirtualDirectoryRoot() || m_vecItems
->GetPath() == "sources://pictures/" )
455 CGUIDialogContextMenu::GetContextButtons("pictures", item
, buttons
);
461 if (!(item
->m_bIsFolder
|| item
->IsZIP() || item
->IsRAR() || item
->IsCBZ() || item
->IsCBR() || item
->IsScript()))
463 if (item
->IsPicture())
464 buttons
.Add(CONTEXT_BUTTON_INFO
, 13406); // picture info
465 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
470 if (!m_thumbLoader
.IsLoading())
471 buttons
.Add(CONTEXT_BUTTON_REFRESH_THUMBS
, 13315); // Create Thumbnails
472 if (CServiceBroker::GetSettingsComponent()->GetSettings()->GetBool(CSettings::SETTING_FILELISTS_ALLOWFILEDELETION
) && !item
->IsReadOnly())
474 buttons
.Add(CONTEXT_BUTTON_DELETE
, 117);
475 buttons
.Add(CONTEXT_BUTTON_RENAME
, 118);
479 if (!item
->IsPlugin() && !item
->IsScript() && !m_vecItems
->IsPlugin())
480 buttons
.Add(CONTEXT_BUTTON_SWITCH_MEDIA
, 523);
483 CGUIMediaWindow::GetContextButtons(itemNumber
, buttons
);
486 bool CGUIWindowPictures::OnContextButton(int itemNumber
, CONTEXT_BUTTON button
)
488 CFileItemPtr item
= (itemNumber
>= 0 && itemNumber
< m_vecItems
->Size()) ? m_vecItems
->Get(itemNumber
) : CFileItemPtr();
489 if (CGUIDialogContextMenu::OnContextButton("pictures", item
, button
))
496 case CONTEXT_BUTTON_VIEW_SLIDESHOW
:
497 if (item
&& item
->m_bIsFolder
)
498 OnSlideShow(item
->GetPath());
500 ShowPicture(itemNumber
, true);
502 case CONTEXT_BUTTON_RECURSIVE_SLIDESHOW
:
504 OnSlideShowRecursive(item
->GetPath());
506 case CONTEXT_BUTTON_INFO
:
507 OnItemInfo(itemNumber
);
509 case CONTEXT_BUTTON_REFRESH_THUMBS
:
510 OnRegenerateThumbs();
512 case CONTEXT_BUTTON_DELETE
:
513 OnDeleteItem(itemNumber
);
515 case CONTEXT_BUTTON_RENAME
:
516 OnRenameItem(itemNumber
);
518 case CONTEXT_BUTTON_SWITCH_MEDIA
:
519 CGUIDialogContextMenu::SwitchMedia("pictures", m_vecItems
->GetPath());
524 return CGUIMediaWindow::OnContextButton(itemNumber
, button
);
527 bool CGUIWindowPictures::OnAddMediaSource()
529 return CGUIDialogMediaSource::ShowAndAddMediaSource("pictures");
532 void CGUIWindowPictures::OnItemLoaded(CFileItem
*pItem
)
534 CPictureThumbLoader::ProcessFoldersAndArchives(pItem
);
537 void CGUIWindowPictures::LoadPlayList(const std::string
& strPlayList
)
540 "CGUIWindowPictures::LoadPlayList()... converting playlist into slideshow: {}",
542 std::unique_ptr
<PLAYLIST::CPlayList
> pPlayList(PLAYLIST::CPlayListFactory::Create(strPlayList
));
543 if (nullptr != pPlayList
)
545 if (!pPlayList
->Load(strPlayList
))
547 HELPERS::ShowOKDialogText(CVariant
{6}, CVariant
{477});
548 return ; //hmmm unable to load playlist?
552 PLAYLIST::CPlayList playlist
= *pPlayList
;
553 if (playlist
.size() > 0)
556 CGUIWindowSlideShow
*pSlideShow
= CServiceBroker::GetGUI()->GetWindowManager().GetWindow
<CGUIWindowSlideShow
>(WINDOW_SLIDESHOW
);
559 const auto& components
= CServiceBroker::GetAppComponents();
560 const auto appPlayer
= components
.GetComponent
<CApplicationPlayer
>();
561 if (appPlayer
->IsPlayingVideo())
562 g_application
.StopPlaying();
564 // convert playlist items into slideshow items
566 for (int i
= 0; i
< playlist
.size(); ++i
)
568 CFileItemPtr pItem
= playlist
[i
];
569 //CLog::Log(LOGDEBUG,"-- playlist item: {}", pItem->GetPath());
570 if (pItem
->IsPicture() && !(pItem
->IsZIP() || pItem
->IsRAR() || pItem
->IsCBZ() || pItem
->IsCBR()))
571 pSlideShow
->Add(pItem
.get());
574 // start slideshow if there are items
575 pSlideShow
->StartSlideShow();
576 if (pSlideShow
->NumSlides())
577 CServiceBroker::GetGUI()->GetWindowManager().ActivateWindow(WINDOW_SLIDESHOW
);
581 void CGUIWindowPictures::OnItemInfo(int itemNumber
)
583 CFileItemPtr item
= m_vecItems
->Get(itemNumber
);
586 if (!m_vecItems
->IsPlugin() && (item
->IsPlugin() || item
->IsScript()))
588 CGUIDialogAddonInfo::ShowForItem(item
);
591 if (item
->m_bIsFolder
|| item
->IsZIP() || item
->IsRAR() || item
->IsCBZ() || item
->IsCBR() || !item
->IsPicture())
593 CGUIDialogPictureInfo
*pictureInfo
= CServiceBroker::GetGUI()->GetWindowManager().GetWindow
<CGUIDialogPictureInfo
>(WINDOW_DIALOG_PICTURE_INFO
);
596 pictureInfo
->SetPicture(item
.get());
601 std::string
CGUIWindowPictures::GetStartFolder(const std::string
&dir
)
603 if (StringUtils::EqualsNoCase(dir
, "plugins") ||
604 StringUtils::EqualsNoCase(dir
, "addons"))
605 return "addons://sources/image/";
609 m_rootDir
.GetSources(shares
);
610 bool bIsSourceName
= false;
611 int iIndex
= CUtil::GetMatchingSource(dir
, shares
, bIsSourceName
);
614 if (iIndex
< static_cast<int>(shares
.size()) && shares
[iIndex
].m_iHasLock
== LOCK_STATE_LOCKED
)
616 CFileItem
item(shares
[iIndex
]);
617 if (!g_passwordManager
.IsItemUnlocked(&item
,"pictures"))
621 return shares
[iIndex
].strPath
;
624 return CGUIMediaWindow::GetStartFolder(dir
);