2 * Copyright (C) 2005-2018 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 "GUIWindowSlideShow.h"
12 #include "FileItemList.h"
13 #include "GUIDialogPictureInfo.h"
14 #include "GUIInfoManager.h"
15 #include "GUIUserMessages.h"
16 #include "ServiceBroker.h"
18 #include "application/Application.h"
19 #include "application/ApplicationComponents.h"
20 #include "application/ApplicationPlayer.h"
21 #include "application/ApplicationPowerHandling.h"
22 #include "filesystem/Directory.h"
23 #include "guilib/GUIComponent.h"
24 #include "guilib/GUILabelControl.h"
25 #include "guilib/GUIWindowManager.h"
26 #include "guilib/LocalizeStrings.h"
27 #include "guilib/Texture.h"
28 #include "imagefiles/ImageFileURL.h"
29 #include "input/actions/Action.h"
30 #include "input/actions/ActionIDs.h"
31 #include "input/mouse/MouseEvent.h"
32 #include "interfaces/AnnouncementManager.h"
33 #include "pictures/GUIViewStatePictures.h"
34 #include "pictures/PictureThumbLoader.h"
35 #include "pictures/SlideShowDelegator.h"
36 #include "playlists/PlayListTypes.h"
37 #include "rendering/RenderSystem.h"
38 #include "settings/DisplaySettings.h"
39 #include "settings/Settings.h"
40 #include "settings/SettingsComponent.h"
41 #include "utils/Random.h"
42 #include "utils/URIUtils.h"
43 #include "utils/Variant.h"
44 #include "utils/XTimeUtils.h"
45 #include "utils/log.h"
46 #include "video/VideoFileItemClassify.h"
52 using namespace KODI::VIDEO
;
53 using namespace MESSAGING
;
54 using namespace XFILE
;
55 using namespace std::chrono_literals
;
57 #define MAX_ZOOM_FACTOR 10
58 #define MAX_PICTURE_SIZE 2048*2048
60 #define IMMEDIATE_TRANSITION_TIME 1
62 #define PICTURE_MOVE_AMOUNT 0.02f
63 #define PICTURE_MOVE_AMOUNT_ANALOG 0.01f
64 #define PICTURE_MOVE_AMOUNT_TOUCH 0.002f
65 #define PICTURE_VIEW_BOX_COLOR 0xffffff00 // YELLOW
66 #define PICTURE_VIEW_BOX_BACKGROUND 0xff000000 // BLACK
68 #define ROTATION_SNAP_RANGE 10.0f
71 #define CONTROL_PAUSE 13
73 static float zoomamount
[10] = { 1.0f
, 1.2f
, 1.5f
, 2.0f
, 2.8f
, 4.0f
, 6.0f
, 9.0f
, 13.5f
, 20.0f
};
75 CBackgroundPicLoader::CBackgroundPicLoader() : CThread("BgPicLoader")
79 CBackgroundPicLoader::~CBackgroundPicLoader()
84 void CBackgroundPicLoader::Create(CGUIWindowSlideShow
*pCallback
)
86 m_pCallback
= pCallback
;
88 CThread::Create(false);
91 void CBackgroundPicLoader::Process()
93 auto totalTime
= std::chrono::milliseconds(0);
94 unsigned int count
= 0;
96 { // loop around forever, waiting for the app to call LoadPic
97 if (AbortableWait(m_loadPic
, 10ms
) == WAIT_SIGNALED
)
101 auto start
= std::chrono::steady_clock::now();
102 std::unique_ptr
<CTexture
> texture
=
103 CTexture::LoadFromFile(m_strFileName
, m_maxWidth
, m_maxHeight
);
105 auto end
= std::chrono::steady_clock::now();
106 auto duration
= std::chrono::duration_cast
<std::chrono::milliseconds
>(end
- start
);
108 totalTime
+= duration
;
111 bool bFullSize
= false;
114 bFullSize
= ((int)texture
->GetWidth() < m_maxWidth
) && ((int)texture
->GetHeight() < m_maxHeight
);
117 int iSize
= texture
->GetWidth() * texture
->GetHeight() - MAX_PICTURE_SIZE
;
118 if ((iSize
+ (int)texture
->GetWidth() > 0) || (iSize
+ (int)texture
->GetHeight() > 0))
120 if (!bFullSize
&& texture
->GetWidth() == CServiceBroker::GetRenderSystem()->GetMaxTextureSize())
122 if (!bFullSize
&& texture
->GetHeight() == CServiceBroker::GetRenderSystem()->GetMaxTextureSize())
126 m_pCallback
->OnLoadPic(m_iPic
, m_iSlideNumber
, m_strFileName
, std::move(texture
),
133 CLog::Log(LOGDEBUG
, "Time for loading {} images: {} ms, average {} ms", count
,
134 totalTime
.count(), totalTime
.count() / count
);
137 void CBackgroundPicLoader::LoadPic(int iPic
, int iSlideNumber
, const std::string
&strFileName
, const int maxWidth
, const int maxHeight
)
140 m_iSlideNumber
= iSlideNumber
;
141 m_strFileName
= strFileName
;
142 m_maxWidth
= maxWidth
;
143 m_maxHeight
= maxHeight
;
148 CGUIWindowSlideShow::CGUIWindowSlideShow(void)
149 : CGUIDialog(WINDOW_SLIDESHOW
, "SlideShow.xml")
151 m_Resolution
= RES_INVALID
;
152 m_loadType
= KEEP_IN_MEMORY
;
153 m_bLoadNextPic
= false;
154 CServiceBroker::GetSlideShowDelegator().SetDelegate(this);
155 CServiceBroker::GetAnnouncementManager()->AddAnnouncer(this);
159 CGUIWindowSlideShow::~CGUIWindowSlideShow()
161 CServiceBroker::GetSlideShowDelegator().ResetDelegate();
162 CServiceBroker::GetAnnouncementManager()->RemoveAnnouncer(this);
165 void CGUIWindowSlideShow::Announce(ANNOUNCEMENT::AnnouncementFlag flag
,
166 const std::string
& sender
,
167 const std::string
& message
,
168 const CVariant
& data
)
170 if (flag
& ANNOUNCEMENT::Player
)
172 if (message
== "OnPlay" || message
== "OnResume")
174 if (data
.isMember("player") && data
["player"].isMember("playerid") &&
175 data
["player"]["playerid"] == static_cast<int>(PLAYLIST::Id::TYPE_VIDEO
))
181 void CGUIWindowSlideShow::AnnouncePlayerPlay(const CFileItemPtr
& item
)
184 param
["player"]["speed"] = m_bSlideShow
&& !m_bPause
? 1 : 0;
185 param
["player"]["playerid"] = static_cast<int>(PLAYLIST::Id::TYPE_PICTURE
);
186 CServiceBroker::GetAnnouncementManager()->Announce(ANNOUNCEMENT::Player
, "OnPlay", item
, param
);
189 void CGUIWindowSlideShow::AnnouncePlayerPause(const CFileItemPtr
& item
)
192 param
["player"]["speed"] = 0;
193 param
["player"]["playerid"] = static_cast<int>(PLAYLIST::Id::TYPE_PICTURE
);
194 CServiceBroker::GetAnnouncementManager()->Announce(ANNOUNCEMENT::Player
, "OnPause", item
, param
);
197 void CGUIWindowSlideShow::AnnouncePlayerStop(const CFileItemPtr
& item
)
200 param
["player"]["playerid"] = static_cast<int>(PLAYLIST::Id::TYPE_PICTURE
);
202 CServiceBroker::GetAnnouncementManager()->Announce(ANNOUNCEMENT::Player
, "OnStop", item
, param
);
205 void CGUIWindowSlideShow::AnnouncePlaylistClear()
208 data
["playlistid"] = static_cast<int>(PLAYLIST::Id::TYPE_PICTURE
);
209 CServiceBroker::GetAnnouncementManager()->Announce(ANNOUNCEMENT::Playlist
, "OnClear", data
);
212 void CGUIWindowSlideShow::AnnouncePlaylistAdd(const CFileItemPtr
& item
, int pos
)
215 data
["playlistid"] = static_cast<int>(PLAYLIST::Id::TYPE_PICTURE
);
216 data
["position"] = pos
;
217 CServiceBroker::GetAnnouncementManager()->Announce(ANNOUNCEMENT::Playlist
, "OnAdd", item
, data
);
220 void CGUIWindowSlideShow::AnnouncePropertyChanged(const std::string
&strProperty
, const CVariant
&value
)
222 if (strProperty
.empty() || value
.isNull())
226 data
["player"]["playerid"] = static_cast<int>(PLAYLIST::Id::TYPE_PICTURE
);
227 data
["property"][strProperty
] = value
;
228 CServiceBroker::GetAnnouncementManager()->Announce(ANNOUNCEMENT::Player
, "OnPropertyChanged",
232 bool CGUIWindowSlideShow::IsPlaying() const
234 return m_Image
[m_iCurrentPic
]->IsLoaded();
237 void CGUIWindowSlideShow::Reset()
239 m_bSlideShow
= false;
242 m_bPlayingVideo
= false;
243 m_bErrorMessage
= false;
247 m_Image
[0]->UnLoad();
251 m_Image
[0] = CSlideShowPic::CreateSlideShowPicture();
255 m_Image
[1]->UnLoad();
259 m_Image
[1] = CSlideShowPic::CreateSlideShowPicture();
262 m_fInitialRotate
= 0.0f
;
265 m_fInitialZoom
= 0.0f
;
270 m_iLastFailedNextSlide
= -1;
272 AnnouncePlaylistClear();
273 m_Resolution
= CServiceBroker::GetWinSystem()->GetGfxContext().GetVideoResolution();
276 void CGUIWindowSlideShow::OnDeinitWindow(int nextWindowID
)
278 if (m_Resolution
!= CDisplaySettings::GetInstance().GetCurrentResolution())
280 //FIXME: Use GUI resolution for now
281 //CServiceBroker::GetWinSystem()->GetGfxContext().SetVideoResolution(CDisplaySettings::GetInstance().GetCurrentResolution(), true);
284 if (nextWindowID
!= WINDOW_FULLSCREEN_VIDEO
&&
285 nextWindowID
!= WINDOW_FULLSCREEN_GAME
)
287 // wait for any outstanding picture loads
288 if (m_pBackgroundLoader
)
290 // sleep until the loader finishes loading the current pic
291 CLog::Log(LOGDEBUG
,"Waiting for BackgroundLoader thread to close");
292 while (m_pBackgroundLoader
->IsLoading())
293 KODI::TIME::Sleep(10ms
);
295 CLog::Log(LOGDEBUG
,"Stopping BackgroundLoader thread");
296 m_pBackgroundLoader
->StopThread();
297 m_pBackgroundLoader
.reset();
299 // and close the images.
303 CServiceBroker::GetGUI()->GetInfoManager().GetInfoProviders().GetPicturesInfoProvider().SetCurrentSlide(nullptr);
304 m_bSlideShow
= false;
306 CGUIDialog::OnDeinitWindow(nextWindowID
);
309 void CGUIWindowSlideShow::Add(const CFileItem
*picture
)
311 CFileItemPtr
item(new CFileItem(*picture
));
312 if (!item
->HasVideoInfoTag() && !item
->HasPictureInfoTag())
314 // item without tag; get mimetype then we can tell whether it's video item
315 item
->FillInMimeType();
318 // then it is a picture and force tag generation
319 item
->GetPictureInfoTag();
321 AnnouncePlaylistAdd(item
, m_slides
.size());
323 m_slides
.emplace_back(std::move(item
));
326 void CGUIWindowSlideShow::ShowNext()
328 if (m_slides
.size() == 1)
332 m_iNextSlide
= GetNextSlide();
336 m_bLoadNextPic
= true;
339 void CGUIWindowSlideShow::ShowPrevious()
341 if (m_slides
.size() == 1)
345 m_iNextSlide
= GetNextSlide();
349 m_bLoadNextPic
= true;
352 void CGUIWindowSlideShow::Select(const std::string
& strPicture
)
354 for (size_t i
= 0; i
< m_slides
.size(); ++i
)
356 const CFileItemPtr item
= m_slides
.at(i
);
357 if (item
->GetPath() == strPicture
)
360 if (!m_Image
[m_iCurrentPic
]->IsLoaded() &&
361 (!m_pBackgroundLoader
|| !m_pBackgroundLoader
->IsLoading()))
363 // will trigger loading current slide when next Process call.
365 m_iNextSlide
= GetNextSlide();
370 m_bLoadNextPic
= true;
377 void CGUIWindowSlideShow::GetSlideShowContents(CFileItemList
&list
)
379 for (size_t index
= 0; index
< m_slides
.size(); index
++)
380 list
.Add(std::make_shared
<CFileItem
>(*m_slides
.at(index
)));
383 std::shared_ptr
<const CFileItem
> CGUIWindowSlideShow::GetCurrentSlide()
385 if (m_iCurrentSlide
>= 0 && m_iCurrentSlide
< static_cast<int>(m_slides
.size()))
386 return m_slides
.at(m_iCurrentSlide
);
387 return CFileItemPtr();
390 bool CGUIWindowSlideShow::InSlideShow() const
395 void CGUIWindowSlideShow::StartSlideShow()
400 AnnouncePlayerPlay(m_slides
.at(m_iCurrentSlide
));
403 void CGUIWindowSlideShow::SetDirection(int direction
)
405 direction
= direction
>= 0 ? 1 : -1;
406 if (m_iDirection
!= direction
)
408 m_iDirection
= direction
;
409 m_iNextSlide
= GetNextSlide();
413 void CGUIWindowSlideShow::Process(unsigned int currentTime
, CDirtyRegionList
®ions
)
415 const RESOLUTION_INFO res
= CServiceBroker::GetWinSystem()->GetGfxContext().GetResInfo();
417 // reset the screensaver if we're in a slideshow
418 // (unless we are the screensaver!)
419 auto& components
= CServiceBroker::GetAppComponents();
420 const auto appPower
= components
.GetComponent
<CApplicationPowerHandling
>();
421 if (m_bSlideShow
&& !m_bPause
&& !appPower
->IsInScreenSaver())
422 appPower
->ResetScreenSaver();
423 int iSlides
= m_slides
.size();
427 // if we haven't processed yet, we should mark the whole screen
430 regions
.emplace_back(CRect(
431 0.0f
, 0.0f
, static_cast<float>(CServiceBroker::GetWinSystem()->GetGfxContext().GetWidth()),
432 static_cast<float>(CServiceBroker::GetWinSystem()->GetGfxContext().GetHeight())));
436 if (m_iCurrentSlide
< 0 || m_iCurrentSlide
>= static_cast<int>(m_slides
.size()))
438 if (m_iNextSlide
< 0 || m_iNextSlide
>= static_cast<int>(m_slides
.size()))
439 m_iNextSlide
= GetNextSlide();
441 // Create our background loader if necessary
442 if (!m_pBackgroundLoader
)
444 m_pBackgroundLoader
= std::make_unique
<CBackgroundPicLoader
>();
445 m_pBackgroundLoader
->Create(this);
448 bool bSlideShow
= m_bSlideShow
&& !m_bPause
&& !m_bPlayingVideo
;
449 if (bSlideShow
&& m_slides
.at(m_iCurrentSlide
)->HasProperty("unplayable"))
451 m_iNextSlide
= GetNextSlide();
452 if (m_iCurrentSlide
== m_iNextSlide
)
454 m_iCurrentSlide
= m_iNextSlide
;
455 m_iNextSlide
= GetNextSlide();
459 { // we have an error when loading either the current or next picture
460 // check to see if we have a picture loaded
461 CLog::Log(LOGDEBUG
, "We have an error loading picture {}!", m_pBackgroundLoader
->SlideNumber());
462 if (m_iCurrentSlide
== m_pBackgroundLoader
->SlideNumber())
464 if (m_Image
[m_iCurrentPic
]->IsLoaded())
466 // current image was already loaded, so we can ignore this error.
467 m_bErrorMessage
= false;
471 CLog::Log(LOGERROR
, "Error loading the current image {}: {}", m_iCurrentSlide
,
472 m_slides
.at(m_iCurrentSlide
)->GetPath());
473 if (!IsVideo(*m_slides
.at(m_iCurrentPic
)))
475 // try next if we are in slideshow
476 CLog::Log(LOGINFO
, "set image {} unplayable", m_slides
.at(m_iCurrentSlide
)->GetPath());
477 m_slides
.at(m_iCurrentSlide
)->SetProperty("unplayable", true);
479 if (m_bLoadNextPic
|| (bSlideShow
&& !m_bPause
&& !IsVideo(*m_slides
.at(m_iCurrentPic
))))
481 // change to next item, wait loading.
482 m_iCurrentSlide
= m_iNextSlide
;
483 m_iNextSlide
= GetNextSlide();
484 m_bErrorMessage
= false;
486 // else just drop through - there's nothing we can do (error message will be displayed)
489 else if (m_iNextSlide
== m_pBackgroundLoader
->SlideNumber())
491 CLog::Log(LOGERROR
, "Error loading the next image {}: {}", m_iNextSlide
,
492 m_slides
.at(m_iNextSlide
)->GetPath());
493 // load next image failed, then skip to load next of next if next is not video.
494 if (!IsVideo(*m_slides
.at(m_iNextSlide
)))
496 CLog::Log(LOGINFO
, "set image {} unplayable", m_slides
.at(m_iNextSlide
)->GetPath());
497 m_slides
.at(m_iNextSlide
)->SetProperty("unplayable", true);
498 // change to next item, wait loading.
499 m_iNextSlide
= GetNextSlide();
502 { // prevent reload the next pic and repeat fail.
503 m_iLastFailedNextSlide
= m_iNextSlide
;
505 m_bErrorMessage
= false;
508 { // Non-current and non-next slide, just ignore error.
509 CLog::Log(LOGERROR
, "Error loading the non-current non-next image {}/{}: {}", m_iNextSlide
,
510 m_pBackgroundLoader
->SlideNumber(), m_slides
.at(m_iNextSlide
)->GetPath());
511 m_bErrorMessage
= false;
516 { // hack, just mark it all
517 regions
.emplace_back(CRect(
518 0.0f
, 0.0f
, static_cast<float>(CServiceBroker::GetWinSystem()->GetGfxContext().GetWidth()),
519 static_cast<float>(CServiceBroker::GetWinSystem()->GetGfxContext().GetHeight())));
524 if (!m_Image
[m_iCurrentPic
]->IsLoaded() && !m_pBackgroundLoader
->IsLoading())
525 { // load first image
526 CFileItemPtr item
= m_slides
.at(m_iCurrentSlide
);
527 std::string picturePath
= GetPicturePath(item
.get());
528 if (!picturePath
.empty())
531 CLog::Log(LOGDEBUG
, "Loading the thumb {} for current video {}: {}", picturePath
,
532 m_iCurrentSlide
, item
->GetPath());
534 CLog::Log(LOGDEBUG
, "Loading the current image {}: {}", m_iCurrentSlide
, item
->GetPath());
536 // load using the background loader
537 int maxWidth
, maxHeight
;
539 GetCheckedSize((float)res
.iWidth
* m_fZoom
,
540 (float)res
.iHeight
* m_fZoom
,
541 maxWidth
, maxHeight
);
542 m_pBackgroundLoader
->LoadPic(m_iCurrentPic
, m_iCurrentSlide
, picturePath
, maxWidth
, maxHeight
);
543 m_iLastFailedNextSlide
= -1;
544 m_bLoadNextPic
= false;
548 // check if we should discard an already loaded next slide
549 if (m_Image
[1 - m_iCurrentPic
]->IsLoaded() &&
550 m_Image
[1 - m_iCurrentPic
]->SlideNumber() != m_iNextSlide
)
551 m_Image
[1 - m_iCurrentPic
]->Close();
553 if (m_iNextSlide
!= m_iCurrentSlide
&& m_Image
[m_iCurrentPic
]->IsLoaded() &&
554 !m_Image
[1 - m_iCurrentPic
]->IsLoaded() && !m_pBackgroundLoader
->IsLoading() &&
555 m_iLastFailedNextSlide
!= m_iNextSlide
)
556 { // load the next image
557 m_iLastFailedNextSlide
= -1;
558 CFileItemPtr item
= m_slides
.at(m_iNextSlide
);
559 std::string picturePath
= GetPicturePath(item
.get());
560 if (!picturePath
.empty() && (!IsVideo(*item
) || !m_bSlideShow
|| m_bPause
))
563 CLog::Log(LOGDEBUG
, "Loading the thumb {} for next video {}: {}", picturePath
, m_iNextSlide
,
566 CLog::Log(LOGDEBUG
, "Loading the next image {}: {}", m_iNextSlide
, item
->GetPath());
568 int maxWidth
, maxHeight
;
569 GetCheckedSize((float)res
.iWidth
* m_fZoom
,
570 (float)res
.iHeight
* m_fZoom
,
571 maxWidth
, maxHeight
);
572 m_pBackgroundLoader
->LoadPic(1 - m_iCurrentPic
, m_iNextSlide
, picturePath
, maxWidth
, maxHeight
);
576 bool bPlayVideo
= IsVideo(*m_slides
.at(m_iCurrentSlide
)) && m_iVideoSlide
!= m_iCurrentSlide
;
580 // render the current image
581 if (m_Image
[m_iCurrentPic
]->IsLoaded())
583 if (m_Image
[m_iCurrentPic
]->IsAnimating())
586 m_Image
[m_iCurrentPic
]->SetInSlideshow(bSlideShow
);
587 m_Image
[m_iCurrentPic
]->Pause(!bSlideShow
);
588 m_Image
[m_iCurrentPic
]->Process(currentTime
, regions
);
591 // Check if we should be transitioning immediately
592 if (m_bLoadNextPic
&& m_Image
[m_iCurrentPic
]->IsLoaded())
594 CLog::Log(LOGDEBUG
, "Starting immediate transition due to user wanting slide {}",
595 m_slides
.at(m_iNextSlide
)->GetPath());
596 if (m_Image
[m_iCurrentPic
]->StartTransition())
598 m_Image
[m_iCurrentPic
]->SetTransitionTime(1, IMMEDIATE_TRANSITION_TIME
);
599 m_bLoadNextPic
= false;
604 const auto appPlayer
= components
.GetComponent
<CApplicationPlayer
>();
606 // render the next image
607 if (m_Image
[m_iCurrentPic
]->DrawNextImage())
609 if (m_bSlideShow
&& !m_bPause
&& IsVideo(*m_slides
.at(m_iNextSlide
)))
611 // do not show thumb of video when playing slideshow
613 else if (m_Image
[1 - m_iCurrentPic
]->IsLoaded())
615 if (appPlayer
->IsPlayingVideo())
616 appPlayer
->ClosePlayer();
617 m_bPlayingVideo
= false;
620 // first time render the next image, make sure using current display effect.
621 if (!m_Image
[1 - m_iCurrentPic
]->IsStarted())
623 CSlideShowPic::DISPLAY_EFFECT effect
= GetDisplayEffect(m_iNextSlide
);
624 if (m_Image
[1 - m_iCurrentPic
]->DisplayEffectNeedChange(effect
))
625 m_Image
[1 - m_iCurrentPic
]->Reset(effect
);
628 if (m_Image
[1 - m_iCurrentPic
]->IsAnimating())
631 // set the appropriate transition time
632 m_Image
[1 - m_iCurrentPic
]->SetTransitionTime(0,
633 m_Image
[m_iCurrentPic
]->GetTransitionTime(1));
634 m_Image
[1 - m_iCurrentPic
]->Pause(!m_bSlideShow
|| m_bPause
||
635 IsVideo(*m_slides
.at(m_iNextSlide
)));
636 m_Image
[1 - m_iCurrentPic
]->Process(currentTime
, regions
);
638 else // next pic isn't loaded. We should hang around if it is in progress
640 if (m_pBackgroundLoader
->IsLoading())
642 // CLog::Log(LOGDEBUG, "Having to hold the current image ({}) while we load {}", m_vecSlides[m_iCurrentSlide], m_vecSlides[m_iNextSlide]);
643 m_Image
[m_iCurrentPic
]->Keep();
648 // check if we should swap images now
649 if (m_Image
[m_iCurrentPic
]->IsFinished() ||
650 (m_bLoadNextPic
&& !m_Image
[m_iCurrentPic
]->IsLoaded()))
652 m_bLoadNextPic
= false;
653 if (m_Image
[m_iCurrentPic
]->IsFinished())
654 CLog::Log(LOGDEBUG
, "Image {} is finished rendering, switching to {}",
655 m_slides
.at(m_iCurrentSlide
)->GetPath(), m_slides
.at(m_iNextSlide
)->GetPath());
657 // what if it's bg loading?
658 CLog::Log(LOGDEBUG
, "Image {} is not loaded, switching to {}",
659 m_slides
.at(m_iCurrentSlide
)->GetPath(), m_slides
.at(m_iNextSlide
)->GetPath());
661 if (m_Image
[m_iCurrentPic
]->IsFinished() && m_iCurrentSlide
== m_iNextSlide
&&
662 m_Image
[m_iCurrentPic
]->SlideNumber() == m_iNextSlide
)
663 m_Image
[m_iCurrentPic
]->Reset(GetDisplayEffect(m_iCurrentSlide
));
666 if (m_Image
[m_iCurrentPic
]->IsLoaded())
667 m_Image
[m_iCurrentPic
]->Reset(GetDisplayEffect(m_iCurrentSlide
));
669 m_Image
[m_iCurrentPic
]->Close();
671 if ((m_Image
[1 - m_iCurrentPic
]->IsLoaded() &&
672 m_Image
[1 - m_iCurrentPic
]->SlideNumber() == m_iNextSlide
) ||
673 (m_pBackgroundLoader
->IsLoading() && m_pBackgroundLoader
->SlideNumber() == m_iNextSlide
&&
674 m_pBackgroundLoader
->Pic() == 1 - m_iCurrentPic
))
676 m_iCurrentPic
= 1 - m_iCurrentPic
;
680 m_Image
[1 - m_iCurrentPic
]->Close();
681 m_iCurrentPic
= 1 - m_iCurrentPic
;
683 m_iCurrentSlide
= m_iNextSlide
;
684 m_iNextSlide
= GetNextSlide();
686 bPlayVideo
= IsVideo(*m_slides
.at(m_iCurrentSlide
)) && m_iVideoSlide
!= m_iCurrentSlide
;
688 AnnouncePlayerPlay(m_slides
.at(m_iCurrentSlide
));
695 if (bPlayVideo
&& !PlayVideo())
698 if (m_Image
[m_iCurrentPic
]->IsLoaded())
699 CServiceBroker::GetGUI()->GetInfoManager().GetInfoProviders().GetPicturesInfoProvider().SetCurrentSlide(m_slides
.at(m_iCurrentSlide
).get());
702 if (IsVideo(*m_slides
.at(m_iCurrentSlide
)) && appPlayer
&& appPlayer
->IsRenderingGuiLayer())
706 CGUIWindow::Process(currentTime
, regions
);
707 m_renderRegion
.SetRect(0, 0, (float)CServiceBroker::GetWinSystem()->GetGfxContext().GetWidth(), (float)CServiceBroker::GetWinSystem()->GetGfxContext().GetHeight());
710 void CGUIWindowSlideShow::Render()
712 if (m_slides
.empty())
715 CGraphicContext
& gfxCtx
= CServiceBroker::GetWinSystem()->GetGfxContext();
716 gfxCtx
.Clear(0xff000000);
718 if (IsVideo(*m_slides
.at(m_iCurrentSlide
)))
720 gfxCtx
.SetViewWindow(0, 0, m_coordsRes
.iWidth
, m_coordsRes
.iHeight
);
721 gfxCtx
.SetRenderingResolution(gfxCtx
.GetVideoResolution(), false);
723 auto& components
= CServiceBroker::GetAppComponents();
724 const auto appPlayer
= components
.GetComponent
<CApplicationPlayer
>();
726 if (appPlayer
->IsRenderingVideoLayer())
728 const CRect old
= gfxCtx
.GetScissors();
729 CRect region
= GetRenderRegion();
730 region
.Intersect(old
);
731 gfxCtx
.SetScissors(region
);
733 gfxCtx
.SetScissors(old
);
737 const ::UTILS::COLOR::Color alpha
= gfxCtx
.MergeAlpha(0xff000000) >> 24;
738 appPlayer
->Render(false, alpha
);
741 gfxCtx
.SetRenderingResolution(m_coordsRes
, m_needsScaling
);
745 if (m_Image
[m_iCurrentPic
]->IsLoaded())
746 m_Image
[m_iCurrentPic
]->Render();
748 if (m_Image
[m_iCurrentPic
]->DrawNextImage() && m_Image
[1 - m_iCurrentPic
]->IsLoaded())
749 m_Image
[1 - m_iCurrentPic
]->Render();
752 RenderErrorMessage();
753 CGUIWindow::Render();
756 void CGUIWindowSlideShow::RenderEx()
758 if (IsVideo(*m_slides
.at(m_iCurrentSlide
)))
760 auto& components
= CServiceBroker::GetAppComponents();
761 const auto appPlayer
= components
.GetComponent
<CApplicationPlayer
>();
762 appPlayer
->Render(false, 255, false);
765 CGUIWindow::RenderEx();
768 int CGUIWindowSlideShow::GetNextSlide()
770 if (m_slides
.size() <= 1)
771 return m_iCurrentSlide
;
772 int step
= m_iDirection
>= 0 ? 1 : -1;
773 int nextSlide
= (m_iCurrentSlide
+ step
+ m_slides
.size()) % m_slides
.size();
774 while (nextSlide
!= m_iCurrentSlide
)
776 if (!m_slides
.at(nextSlide
)->HasProperty("unplayable"))
778 nextSlide
= (nextSlide
+ step
+ m_slides
.size()) % m_slides
.size();
780 return m_iCurrentSlide
;
783 EVENT_RESULT
CGUIWindowSlideShow::OnMouseEvent(const CPoint
& point
, const MOUSE::CMouseEvent
& event
)
785 if (event
.m_id
== ACTION_GESTURE_NOTIFY
)
787 int result
= EVENT_RESULT_ROTATE
| EVENT_RESULT_ZOOM
;
788 if (m_iZoomFactor
== 1 || !m_Image
[m_iCurrentPic
]->m_bCanMoveHorizontally
)
789 result
|= EVENT_RESULT_SWIPE
;
791 result
|= EVENT_RESULT_PAN_HORIZONTAL
;
793 if (m_Image
[m_iCurrentPic
]->m_bCanMoveVertically
)
794 result
|= EVENT_RESULT_PAN_VERTICAL
;
796 return (EVENT_RESULT
)result
;
798 else if (event
.m_id
== ACTION_GESTURE_BEGIN
)
800 m_firstGesturePoint
= point
;
801 m_fInitialZoom
= m_fZoom
;
802 m_fInitialRotate
= m_fRotate
;
803 return EVENT_RESULT_HANDLED
;
805 else if (event
.m_id
== ACTION_GESTURE_PAN
)
807 // zoomed in - free move mode
808 if (m_iZoomFactor
!= 1 && (m_Image
[m_iCurrentPic
]->m_bCanMoveHorizontally
||
809 m_Image
[m_iCurrentPic
]->m_bCanMoveVertically
))
811 Move(PICTURE_MOVE_AMOUNT_TOUCH
/ m_iZoomFactor
* (m_firstGesturePoint
.x
- point
.x
), PICTURE_MOVE_AMOUNT_TOUCH
/ m_iZoomFactor
* (m_firstGesturePoint
.y
- point
.y
));
812 m_firstGesturePoint
= point
;
814 return EVENT_RESULT_HANDLED
;
816 else if (event
.m_id
== ACTION_GESTURE_SWIPE_LEFT
|| event
.m_id
== ACTION_GESTURE_SWIPE_RIGHT
)
818 if (m_iZoomFactor
== 1 || !m_Image
[m_iCurrentPic
]->m_bCanMoveHorizontally
)
820 // on zoomlevel 1 just detect swipe left and right
821 if (event
.m_id
== ACTION_GESTURE_SWIPE_LEFT
)
822 OnAction(CAction(ACTION_NEXT_PICTURE
));
824 OnAction(CAction(ACTION_PREV_PICTURE
));
827 else if (event
.m_id
== ACTION_GESTURE_END
|| event
.m_id
== ACTION_GESTURE_ABORT
)
829 if (m_fRotate
!= 0.0f
)
831 // "snap" to nearest of 0, 90, 180 and 270 if the
832 // difference in angle is +/-10 degrees
833 float reminder
= fmodf(m_fRotate
, 90.0f
);
834 if (fabs(reminder
) < ROTATION_SNAP_RANGE
)
836 else if (reminder
> 90.0f
- ROTATION_SNAP_RANGE
)
837 Rotate(90.0f
- reminder
);
838 else if (-reminder
> 90.0f
- ROTATION_SNAP_RANGE
)
839 Rotate(-90.0f
- reminder
);
842 m_fInitialZoom
= 0.0f
;
843 m_fInitialRotate
= 0.0f
;
844 return EVENT_RESULT_HANDLED
;
846 else if (event
.m_id
== ACTION_GESTURE_ZOOM
)
848 ZoomRelative(m_fInitialZoom
* event
.m_offsetX
, true);
849 return EVENT_RESULT_HANDLED
;
851 else if (event
.m_id
== ACTION_GESTURE_ROTATE
)
853 Rotate(m_fInitialRotate
+ event
.m_offsetX
- m_fRotate
, true);
854 return EVENT_RESULT_HANDLED
;
856 return EVENT_RESULT_UNHANDLED
;
859 bool CGUIWindowSlideShow::OnAction(const CAction
&action
)
861 switch (action
.GetID())
863 case ACTION_SHOW_INFO
:
865 CGUIDialogPictureInfo
*pictureInfo
= CServiceBroker::GetGUI()->GetWindowManager().GetWindow
<CGUIDialogPictureInfo
>(WINDOW_DIALOG_PICTURE_INFO
);
868 // no need to set the picture here, it's done in Render()
876 AnnouncePlayerStop(m_slides
.at(m_iCurrentSlide
));
877 auto& components
= CServiceBroker::GetAppComponents();
878 const auto appPlayer
= components
.GetComponent
<CApplicationPlayer
>();
879 if (appPlayer
->IsPlayingVideo())
880 appPlayer
->ClosePlayer();
885 case ACTION_NEXT_PICTURE
:
889 case ACTION_PREV_PICTURE
:
893 case ACTION_MOVE_RIGHT
:
894 if (m_iZoomFactor
== 1 || !m_Image
[m_iCurrentPic
]->m_bCanMoveHorizontally
)
897 Move(PICTURE_MOVE_AMOUNT
, 0);
900 case ACTION_MOVE_LEFT
:
901 if (m_iZoomFactor
== 1 || !m_Image
[m_iCurrentPic
]->m_bCanMoveHorizontally
)
904 Move( -PICTURE_MOVE_AMOUNT
, 0);
907 case ACTION_MOVE_DOWN
:
908 Move(0, PICTURE_MOVE_AMOUNT
);
912 Move(0, -PICTURE_MOVE_AMOUNT
);
916 case ACTION_PLAYER_PLAY
:
917 if (m_slides
.size() == 0)
919 if (IsVideo(*m_slides
.at(m_iCurrentSlide
)))
921 if (!m_bPlayingVideo
)
931 else if (!m_bSlideShow
|| m_bPause
)
936 if (m_Image
[m_iCurrentPic
]->IsLoaded())
938 CSlideShowPic::DISPLAY_EFFECT effect
= GetDisplayEffect(m_iCurrentSlide
);
939 if (m_Image
[m_iCurrentPic
]->DisplayEffectNeedChange(effect
))
940 m_Image
[m_iCurrentPic
]->Reset(effect
);
942 AnnouncePlayerPlay(m_slides
.at(m_iCurrentSlide
));
944 else if (action
.GetID() == ACTION_PAUSE
)
947 AnnouncePlayerPause(m_slides
.at(m_iCurrentSlide
));
951 case ACTION_ZOOM_OUT
:
952 Zoom(m_iZoomFactor
- 1);
956 Zoom(m_iZoomFactor
+ 1);
959 case ACTION_GESTURE_SWIPE_UP
:
960 case ACTION_GESTURE_SWIPE_DOWN
:
961 if (m_iZoomFactor
== 1 || !m_Image
[m_iCurrentPic
]->m_bCanMoveVertically
)
963 bool swipeOnLeft
= action
.GetAmount() < CServiceBroker::GetWinSystem()->GetGfxContext().GetWidth() / 2.0f
;
964 bool swipeUp
= action
.GetID() == ACTION_GESTURE_SWIPE_UP
;
965 if (swipeUp
== swipeOnLeft
)
972 case ACTION_ROTATE_PICTURE_CW
:
976 case ACTION_ROTATE_PICTURE_CCW
:
980 case ACTION_ZOOM_LEVEL_NORMAL
:
981 case ACTION_ZOOM_LEVEL_1
:
982 case ACTION_ZOOM_LEVEL_2
:
983 case ACTION_ZOOM_LEVEL_3
:
984 case ACTION_ZOOM_LEVEL_4
:
985 case ACTION_ZOOM_LEVEL_5
:
986 case ACTION_ZOOM_LEVEL_6
:
987 case ACTION_ZOOM_LEVEL_7
:
988 case ACTION_ZOOM_LEVEL_8
:
989 case ACTION_ZOOM_LEVEL_9
:
990 Zoom((action
.GetID() - ACTION_ZOOM_LEVEL_NORMAL
) + 1);
993 case ACTION_ANALOG_MOVE
:
994 // this action is used and works, when CAction object provides both x and y coordinates
995 Move(action
.GetAmount()*PICTURE_MOVE_AMOUNT_ANALOG
, -action
.GetAmount(1)*PICTURE_MOVE_AMOUNT_ANALOG
);
997 case ACTION_ANALOG_MOVE_X_LEFT
:
998 Move(-action
.GetAmount()*PICTURE_MOVE_AMOUNT_ANALOG
, 0.0f
);
1000 case ACTION_ANALOG_MOVE_X_RIGHT
:
1001 Move(action
.GetAmount()*PICTURE_MOVE_AMOUNT_ANALOG
, 0.0f
);
1003 case ACTION_ANALOG_MOVE_Y_UP
:
1004 Move(0.0f
, -action
.GetAmount()*PICTURE_MOVE_AMOUNT_ANALOG
);
1006 case ACTION_ANALOG_MOVE_Y_DOWN
:
1007 Move(0.0f
, action
.GetAmount()*PICTURE_MOVE_AMOUNT_ANALOG
);
1011 return CGUIDialog::OnAction(action
);
1017 void CGUIWindowSlideShow::RenderErrorMessage()
1019 if (!m_bErrorMessage
)
1022 const CGUIControl
*control
= GetControl(LABEL_ROW1
);
1023 if (NULL
== control
|| control
->GetControlType() != CGUIControl::GUICONTROL_LABEL
)
1028 CGUIFont
*pFont
= static_cast<const CGUILabelControl
*>(control
)->GetLabelInfo().font
;
1029 CGUITextLayout::DrawText(pFont
, 0.5f
*CServiceBroker::GetWinSystem()->GetGfxContext().GetWidth(), 0.5f
*CServiceBroker::GetWinSystem()->GetGfxContext().GetHeight(), 0xffffffff, 0, g_localizeStrings
.Get(747), XBFONT_CENTER_X
| XBFONT_CENTER_Y
);
1032 bool CGUIWindowSlideShow::OnMessage(CGUIMessage
& message
)
1034 switch ( message
.GetMessage() )
1036 case GUI_MSG_WINDOW_INIT
:
1038 m_Resolution
= (RESOLUTION
) CServiceBroker::GetSettingsComponent()->GetSettings()->GetInt(CSettings::SETTING_PICTURES_DISPLAYRESOLUTION
);
1040 //FIXME: Use GUI resolution for now
1041 if (false /*m_Resolution != CDisplaySettings::GetInstance().GetCurrentResolution() && m_Resolution != INVALID && m_Resolution!=AUTORES*/)
1042 CServiceBroker::GetWinSystem()->GetGfxContext().SetVideoResolution(m_Resolution
, false);
1044 m_Resolution
= CServiceBroker::GetWinSystem()->GetGfxContext().GetVideoResolution();
1046 CGUIDialog::OnMessage(message
);
1048 // turn off slideshow if we only have 1 image
1049 if (m_slides
.size() <= 1)
1050 m_bSlideShow
= false;
1056 case GUI_MSG_SHOW_PICTURE
:
1058 const std::string
& strFile
= message
.GetStringParam();
1060 CFileItem
item(strFile
, false);
1062 RunSlideShow("", false, false, true, "", false);
1066 case GUI_MSG_START_SLIDESHOW
:
1068 const std::string
& strFolder
= message
.GetStringParam();
1069 unsigned int iParams
= message
.GetParam1();
1070 const std::string
& beginSlidePath
= message
.GetStringParam(1);
1072 bool bRecursive
= false;
1073 bool bRandom
= false;
1074 bool bNotRandom
= false;
1075 bool bPause
= false;
1078 if ((iParams
& 1) == 1)
1080 if ((iParams
& 2) == 2)
1082 if ((iParams
& 4) == 4)
1084 if ((iParams
& 8) == 8)
1087 RunSlideShow(strFolder
, bRecursive
, bRandom
, bNotRandom
, beginSlidePath
, !bPause
);
1091 case GUI_MSG_PLAYLISTPLAYER_STOPPED
:
1096 case GUI_MSG_PLAYBACK_STOPPED
:
1098 if (m_bPlayingVideo
)
1100 m_bPlayingVideo
= false;
1108 case GUI_MSG_PLAYBACK_ENDED
:
1110 if (m_bPlayingVideo
)
1112 m_bPlayingVideo
= false;
1117 if (m_iCurrentSlide
== m_iNextSlide
)
1119 m_Image
[m_iCurrentPic
]->Close();
1120 m_iCurrentPic
= 1 - m_iCurrentPic
;
1121 m_iCurrentSlide
= m_iNextSlide
;
1122 m_iNextSlide
= GetNextSlide();
1123 AnnouncePlayerPlay(m_slides
.at(m_iCurrentSlide
));
1132 return CGUIDialog::OnMessage(message
);
1135 void CGUIWindowSlideShow::RenderPause()
1136 { // display the pause icon
1139 SET_CONTROL_VISIBLE(CONTROL_PAUSE
);
1143 SET_CONTROL_HIDDEN(CONTROL_PAUSE
);
1147 void CGUIWindowSlideShow::Rotate(float fAngle
, bool immediate
/* = false */)
1149 if (m_Image
[m_iCurrentPic
]->DrawNextImage())
1152 m_fRotate
+= fAngle
;
1154 m_Image
[m_iCurrentPic
]->Rotate(fAngle
, immediate
);
1157 void CGUIWindowSlideShow::Zoom(int iZoom
)
1159 if (iZoom
> MAX_ZOOM_FACTOR
|| iZoom
< 1)
1162 ZoomRelative(zoomamount
[iZoom
- 1]);
1165 void CGUIWindowSlideShow::ZoomRelative(float fZoom
, bool immediate
/* = false */)
1167 if (fZoom
< zoomamount
[0])
1168 fZoom
= zoomamount
[0];
1169 else if (fZoom
> zoomamount
[MAX_ZOOM_FACTOR
- 1])
1170 fZoom
= zoomamount
[MAX_ZOOM_FACTOR
- 1];
1172 if (m_Image
[m_iCurrentPic
]->DrawNextImage())
1177 // find the nearest zoom factor
1178 for (unsigned int i
= 1; i
< MAX_ZOOM_FACTOR
; i
++)
1180 if (m_fZoom
> zoomamount
[i
])
1183 if (fabs(m_fZoom
- zoomamount
[i
- 1]) < fabs(m_fZoom
- zoomamount
[i
]))
1186 m_iZoomFactor
= i
+ 1;
1191 m_Image
[m_iCurrentPic
]->Zoom(m_fZoom
, immediate
);
1194 void CGUIWindowSlideShow::Move(float fX
, float fY
)
1196 if (m_Image
[m_iCurrentPic
]->IsLoaded() && m_Image
[m_iCurrentPic
]->GetZoom() > 1)
1197 { // we move in the opposite direction, due to the fact we are moving
1198 // the viewing window, not the picture.
1199 m_Image
[m_iCurrentPic
]->Move(-fX
, -fY
);
1203 bool CGUIWindowSlideShow::PlayVideo()
1205 CFileItemPtr item
= m_slides
.at(m_iCurrentSlide
);
1206 if (!item
|| !IsVideo(*item
))
1208 CLog::Log(LOGDEBUG
, "Playing current video slide {}", item
->GetPath());
1209 m_bPlayingVideo
= true;
1210 m_iVideoSlide
= m_iCurrentSlide
;
1211 bool ret
= g_application
.PlayFile(*item
, "");
1216 CLog::Log(LOGINFO
, "set video {} unplayable", item
->GetPath());
1217 item
->SetProperty("unplayable", true);
1219 m_bPlayingVideo
= false;
1224 CSlideShowPic::DISPLAY_EFFECT
CGUIWindowSlideShow::GetDisplayEffect(int iSlideNumber
) const
1226 if (m_bSlideShow
&& !m_bPause
&& !IsVideo(*m_slides
.at(iSlideNumber
)))
1227 return CServiceBroker::GetSettingsComponent()->GetSettings()->GetBool(CSettings::SETTING_SLIDESHOW_DISPLAYEFFECTS
) ? CSlideShowPic::EFFECT_RANDOM
: CSlideShowPic::EFFECT_NONE
;
1229 return CSlideShowPic::EFFECT_NO_TIMEOUT
;
1232 void CGUIWindowSlideShow::OnLoadPic(int iPic
,
1234 const std::string
& strFileName
,
1235 std::unique_ptr
<CTexture
> pTexture
,
1240 // set the pic's texture + size etc.
1241 if (iSlideNumber
>= static_cast<int>(m_slides
.size()) || GetPicturePath(m_slides
.at(iSlideNumber
).get()) != strFileName
)
1242 { // throw this away - we must have cleared the slideshow while we were still loading
1245 CLog::Log(LOGDEBUG
, "Finished background loading slot {}, {}: {}", iPic
, iSlideNumber
,
1246 m_slides
.at(iSlideNumber
)->GetPath());
1247 m_Image
[iPic
]->SetOriginalSize(pTexture
->GetOriginalWidth(), pTexture
->GetOriginalHeight(),
1249 m_Image
[iPic
]->SetTexture(iSlideNumber
, std::move(pTexture
), GetDisplayEffect(iSlideNumber
));
1251 m_Image
[iPic
]->m_bIsComic
= false;
1252 if (URIUtils::IsInRAR(m_slides
.at(m_iCurrentSlide
)->GetPath()) || URIUtils::IsInZIP(m_slides
.at(m_iCurrentSlide
)->GetPath())) // move to top for cbr/cbz
1254 CURL
url(m_slides
.at(m_iCurrentSlide
)->GetPath());
1255 const std::string
& strHostName
= url
.GetHostName();
1256 if (URIUtils::HasExtension(strHostName
, ".cbr|.cbz"))
1258 m_Image
[iPic
]->m_bIsComic
= true;
1259 m_Image
[iPic
]->Move((float)m_Image
[iPic
]->GetOriginalWidth(),
1260 (float)m_Image
[iPic
]->GetOriginalHeight());
1264 else if (iSlideNumber
>= static_cast<int>(m_slides
.size()) || GetPicturePath(m_slides
.at(iSlideNumber
).get()) != strFileName
)
1265 { // Failed to load image. and not match values calling LoadPic, then something is changed, ignore.
1267 "CGUIWindowSlideShow::OnLoadPic({}, {}, {}) on failure not match current state (cur "
1268 "{}, next {}, curpic {}, pic[0, 1].slidenumber={}, {}, {})",
1269 iPic
, iSlideNumber
, strFileName
, m_iCurrentSlide
, m_iNextSlide
, m_iCurrentPic
,
1270 m_Image
[0]->SlideNumber(), m_Image
[1]->SlideNumber(),
1271 iSlideNumber
>= static_cast<int>(m_slides
.size())
1273 : m_slides
.at(iSlideNumber
)->GetPath());
1276 { // Failed to load image. What should be done??
1277 // We should wait for the current pic to finish rendering, then transition it out,
1278 // release the texture, and try and reload this pic from scratch
1279 m_bErrorMessage
= true;
1284 void CGUIWindowSlideShow::Shuffle()
1286 KODI::UTILS::RandomShuffle(m_slides
.begin(), m_slides
.end());
1287 m_iCurrentSlide
= 0;
1288 m_iNextSlide
= GetNextSlide();
1291 AnnouncePropertyChanged("shuffled", true);
1294 int CGUIWindowSlideShow::NumSlides() const
1296 return m_slides
.size();
1299 int CGUIWindowSlideShow::CurrentSlide() const
1301 return m_iCurrentSlide
+ 1;
1304 void CGUIWindowSlideShow::AddFromPath(const std::string
&strPath
,
1306 SortBy method
, SortOrder order
, SortAttribute sortAttributes
,
1307 const std::string
&strExtensions
)
1311 // reset the slideshow
1315 path_set recursivePaths
;
1316 AddItems(strPath
, &recursivePaths
, method
, order
, sortAttributes
);
1319 AddItems(strPath
, NULL
, method
, order
, sortAttributes
);
1323 void CGUIWindowSlideShow::RunSlideShow(const std::string
&strPath
,
1324 bool bRecursive
/* = false */, bool bRandom
/* = false */,
1325 bool bNotRandom
/* = false */, const std::string
&beginSlidePath
/* = "" */,
1326 bool startSlideShow
/* = true */, SortBy method
/* = SortByLabel */,
1327 SortOrder order
/* = SortOrderAscending */, SortAttribute sortAttributes
/* = SortAttributeNone */,
1328 const std::string
&strExtensions
)
1331 const auto& components
= CServiceBroker::GetAppComponents();
1332 const auto appPlayer
= components
.GetComponent
<CApplicationPlayer
>();
1333 if (appPlayer
->IsPlayingVideo())
1334 g_application
.StopPlaying();
1336 AddFromPath(strPath
, bRecursive
, method
, order
, sortAttributes
, strExtensions
);
1341 // mutually exclusive options
1342 // if both are set, clear both and use the gui setting
1343 if (bRandom
&& bNotRandom
)
1344 bRandom
= bNotRandom
= false;
1346 // NotRandom overrides the window setting
1347 if ((!bNotRandom
&& CServiceBroker::GetSettingsComponent()->GetSettings()->GetBool(CSettings::SETTING_SLIDESHOW_SHUFFLE
)) || bRandom
)
1350 if (!beginSlidePath
.empty())
1351 Select(beginSlidePath
);
1360 CServiceBroker::GetGUI()->GetWindowManager().ActivateWindow(WINDOW_SLIDESHOW
);
1363 void CGUIWindowSlideShow::PlayPicture()
1365 if (m_iCurrentSlide
>= 0 && m_iCurrentSlide
< static_cast<int>(m_slides
.size()))
1366 AnnouncePlayerPlay(m_slides
.at(m_iCurrentSlide
));
1369 void CGUIWindowSlideShow::AddItems(const std::string
&strPath
, path_set
*recursivePaths
, SortBy method
, SortOrder order
, SortAttribute sortAttributes
)
1371 // check whether we've already added this path
1374 std::string
path(strPath
);
1375 URIUtils::RemoveSlashAtEnd(path
);
1376 if (recursivePaths
->find(path
) != recursivePaths
->end())
1378 recursivePaths
->insert(path
);
1381 CFileItemList items
;
1382 CGUIViewStateWindowPictures
viewState(items
);
1384 // fetch directory and sort accordingly
1385 if (!CDirectory::GetDirectory(strPath
, items
, viewState
.GetExtensions(), DIR_FLAG_NO_FILE_DIRS
))
1388 items
.Sort(method
, order
, sortAttributes
);
1390 // need to go into all subdirs
1391 for (int i
= 0; i
< items
.Size(); i
++)
1393 CFileItemPtr item
= items
[i
];
1394 if (item
->m_bIsFolder
&& recursivePaths
)
1396 AddItems(item
->GetPath(), recursivePaths
);
1398 else if (!item
->m_bIsFolder
&& !URIUtils::IsRAR(item
->GetPath()) && !URIUtils::IsZIP(item
->GetPath()))
1399 { // add to the slideshow
1405 void CGUIWindowSlideShow::GetCheckedSize(float width
, float height
, int &maxWidth
, int &maxHeight
)
1407 maxWidth
= CServiceBroker::GetRenderSystem()->GetMaxTextureSize();
1408 maxHeight
= CServiceBroker::GetRenderSystem()->GetMaxTextureSize();
1411 std::string
CGUIWindowSlideShow::GetPicturePath(CFileItem
*item
)
1413 bool isVideo
= IsVideo(*item
);
1414 std::string picturePath
= item
->GetDynPath();
1417 picturePath
= item
->GetArt("thumb");
1418 if (picturePath
.empty() && !item
->HasProperty("nothumb"))
1420 CPictureThumbLoader thumbLoader
;
1421 thumbLoader
.LoadItem(item
);
1422 picturePath
= item
->GetArt("thumb");
1423 if (picturePath
.empty())
1424 item
->SetProperty("nothumb", true);
1431 void CGUIWindowSlideShow::RunSlideShow(const std::vector
<std::string
>& paths
, int start
/* = 0*/)
1433 auto dialog
= CServiceBroker::GetGUI()->GetWindowManager().GetWindow
<CGUIWindowSlideShow
>(WINDOW_SLIDESHOW
);
1436 std::vector
<CFileItemPtr
> items
;
1437 items
.reserve(paths
.size());
1438 for (const auto& path
: paths
)
1439 items
.push_back(std::make_shared
<CFileItem
>(IMAGE_FILES::URLFromFile(path
), false));
1442 dialog
->m_bPause
= true;
1443 dialog
->m_bSlideShow
= false;
1444 dialog
->m_iDirection
= 1;
1445 dialog
->m_iCurrentSlide
= start
;
1446 dialog
->m_iNextSlide
= (start
+ 1) % items
.size();
1447 dialog
->m_slides
= std::move(items
);