2 * Copyright (C) 2005-2012 Team XBMC
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)
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.
15 * You should have received a copy of the GNU General Public License
16 * along with XBMC; see the file COPYING. If not, see
17 * <http://www.gnu.org/licenses/>.
21 #include "threads/SystemClock.h"
23 #include "GUIWindowSlideShow.h"
24 #include "Application.h"
25 #include "ApplicationMessenger.h"
26 #include "utils/URIUtils.h"
28 #include "guilib/TextureManager.h"
29 #include "guilib/GUILabelControl.h"
30 #include "GUIInfoManager.h"
31 #include "filesystem/Directory.h"
32 #include "GUIDialogPictureInfo.h"
33 #include "GUIUserMessages.h"
34 #include "guilib/GUIWindowManager.h"
35 #include "settings/Settings.h"
36 #include "settings/GUISettings.h"
38 #include "guilib/Texture.h"
39 #include "windowing/WindowingFactory.h"
40 #include "guilib/Texture.h"
41 #include "guilib/LocalizeStrings.h"
42 #include "threads/SingleLock.h"
43 #include "utils/log.h"
44 #include "utils/TimeUtils.h"
45 #include "interfaces/AnnouncementManager.h"
46 #include "pictures/PictureInfoTag.h"
48 using namespace XFILE;
50 #define MAX_ZOOM_FACTOR 10
51 #define MAX_PICTURE_SIZE 2048*2048
53 #define IMMEDIATE_TRANSISTION_TIME 20
55 #define PICTURE_MOVE_AMOUNT 0.02f
56 #define PICTURE_MOVE_AMOUNT_ANALOG 0.01f
57 #define PICTURE_MOVE_AMOUNT_TOUCH 0.002f
58 #define PICTURE_VIEW_BOX_COLOR 0xffffff00 // YELLOW
59 #define PICTURE_VIEW_BOX_BACKGROUND 0xff000000 // BLACK
61 #define ROTATION_SNAP_RANGE 10.0f
68 #define LABEL_ROW2_EXTRA 12
69 #define CONTROL_PAUSE 13
71 static float zoomamount[10] = { 1.0f, 1.2f, 1.5f, 2.0f, 2.8f, 4.0f, 6.0f, 9.0f, 13.5f, 20.0f };
73 CBackgroundPicLoader::CBackgroundPicLoader() : CThread("CBackgroundPicLoader")
79 CBackgroundPicLoader::~CBackgroundPicLoader()
84 void CBackgroundPicLoader::Create(CGUIWindowSlideShow *pCallback)
86 m_pCallback = pCallback;
88 CThread::Create(false);
91 void CBackgroundPicLoader::Process()
93 unsigned int totalTime = 0;
94 unsigned int count = 0;
96 { // loop around forever, waiting for the app to call LoadPic
97 if (AbortableWait(m_loadPic,10) == WAIT_SIGNALED)
101 unsigned int start = XbmcThreads::SystemClockMillis();
102 CBaseTexture* texture = CTexture::LoadFromFile(m_strFileName, m_maxWidth, m_maxHeight, g_guiSettings.GetBool("pictures.useexifrotation"));
103 totalTime += XbmcThreads::SystemClockMillis() - start;
106 bool bFullSize = false;
109 bFullSize = ((int)texture->GetWidth() < m_maxWidth) && ((int)texture->GetHeight() < m_maxHeight);
112 int iSize = texture->GetWidth() * texture->GetHeight() - MAX_PICTURE_SIZE;
113 if ((iSize + (int)texture->GetWidth() > 0) || (iSize + (int)texture->GetHeight() > 0))
115 if (!bFullSize && texture->GetWidth() == g_Windowing.GetMaxTextureSize())
117 if (!bFullSize && texture->GetHeight() == g_Windowing.GetMaxTextureSize())
121 m_pCallback->OnLoadPic(m_iPic, m_iSlideNumber, texture, bFullSize);
127 CLog::Log(LOGDEBUG, "Time for loading %u images: %u ms, average %u ms",
128 count, totalTime, totalTime / count);
131 void CBackgroundPicLoader::LoadPic(int iPic, int iSlideNumber, const CStdString &strFileName, const int maxWidth, const int maxHeight)
134 m_iSlideNumber = iSlideNumber;
135 m_strFileName = strFileName;
136 m_maxWidth = maxWidth;
137 m_maxHeight = maxHeight;
142 CGUIWindowSlideShow::CGUIWindowSlideShow(void)
143 : CGUIWindow(WINDOW_SLIDESHOW, "SlideShow.xml")
145 m_pBackgroundLoader = NULL;
146 m_slides = new CFileItemList;
147 m_Resolution = RES_INVALID;
148 m_loadType = KEEP_IN_MEMORY;
152 CGUIWindowSlideShow::~CGUIWindowSlideShow(void)
158 void CGUIWindowSlideShow::AnnouncePlayerPlay(const CFileItemPtr& item)
161 param["player"]["speed"] = 1;
162 param["player"]["playerid"] = PLAYLIST_PICTURE;
163 ANNOUNCEMENT::CAnnouncementManager::Announce(ANNOUNCEMENT::Player, "xbmc", "OnPlay", item, param);
166 void CGUIWindowSlideShow::AnnouncePlayerPause(const CFileItemPtr& item)
169 param["player"]["speed"] = 0;
170 param["player"]["playerid"] = PLAYLIST_PICTURE;
171 ANNOUNCEMENT::CAnnouncementManager::Announce(ANNOUNCEMENT::Player, "xbmc", "OnPause", item, param);
174 void CGUIWindowSlideShow::AnnouncePlayerStop(const CFileItemPtr& item)
177 param["player"]["playerid"] = PLAYLIST_PICTURE;
179 ANNOUNCEMENT::CAnnouncementManager::Announce(ANNOUNCEMENT::Player, "xbmc", "OnStop", item, param);
182 void CGUIWindowSlideShow::AnnouncePlaylistRemove(int pos)
185 data["playlistid"] = PLAYLIST_PICTURE;
186 data["position"] = pos;
187 ANNOUNCEMENT::CAnnouncementManager::Announce(ANNOUNCEMENT::Playlist, "xbmc", "OnRemove", data);
190 void CGUIWindowSlideShow::AnnouncePlaylistClear()
193 data["playlistid"] = PLAYLIST_PICTURE;
194 ANNOUNCEMENT::CAnnouncementManager::Announce(ANNOUNCEMENT::Playlist, "xbmc", "OnClear", data);
197 void CGUIWindowSlideShow::AnnouncePlaylistAdd(const CFileItemPtr& item, int pos)
200 data["playlistid"] = PLAYLIST_PICTURE;
201 data["position"] = pos;
202 ANNOUNCEMENT::CAnnouncementManager::Announce(ANNOUNCEMENT::Playlist, "xbmc", "OnAdd", item, data);
205 void CGUIWindowSlideShow::AnnouncePropertyChanged(const std::string &strProperty, const CVariant &value)
207 if (strProperty.empty() || value.isNull())
211 data["player"]["playerid"] = PLAYLIST_PICTURE;
212 data["property"][strProperty] = value;
213 ANNOUNCEMENT::CAnnouncementManager::Announce(ANNOUNCEMENT::Player, "xbmc", "OnPropertyChanged", data);
216 bool CGUIWindowSlideShow::IsPlaying() const
218 return m_Image[m_iCurrentPic].IsLoaded();
221 void CGUIWindowSlideShow::Reset()
223 g_infoManager.SetShowCodec(false);
224 m_bSlideShow = false;
227 m_bPlayingVideo = false;
228 m_bErrorMessage = false;
229 m_bReloadImage = false;
230 m_bScreensaver = false;
237 m_fInitialRotate = 0.0f;
240 m_fInitialZoom = 0.0f;
245 CSingleLock lock(m_slideSection);
247 AnnouncePlaylistClear();
248 m_Resolution = g_graphicsContext.GetVideoResolution();
251 void CGUIWindowSlideShow::OnDeinitWindow(int nextWindowID)
253 if (m_Resolution != g_guiSettings.m_LookAndFeelResolution)
255 //FIXME: Use GUI resolution for now
256 //g_graphicsContext.SetVideoResolution(g_guiSettings.m_LookAndFeelResolution, TRUE);
260 if (nextWindowID != WINDOW_PICTURES)
263 g_windowManager.ShowOverlay(OVERLAY_STATE_SHOWN);
265 // wait for any outstanding picture loads
266 if (m_pBackgroundLoader)
268 // sleep until the loader finishes loading the current pic
269 CLog::Log(LOGDEBUG,"Waiting for BackgroundLoader thread to close");
270 while (m_pBackgroundLoader->IsLoading())
273 CLog::Log(LOGDEBUG,"Stopping BackgroundLoader thread");
274 m_pBackgroundLoader->StopThread();
275 delete m_pBackgroundLoader;
276 m_pBackgroundLoader = NULL;
278 // and close the images.
281 g_infoManager.ResetCurrentSlide();
283 CGUIWindow::OnDeinitWindow(nextWindowID);
286 void CGUIWindowSlideShow::Add(const CFileItem *picture)
288 CFileItemPtr item(new CFileItem(*picture));
289 if (!item->HasVideoInfoTag() && !item->HasPictureInfoTag())
291 // item without tag; assume it is a picture and force tag generation
292 item->GetPictureInfoTag();
294 AnnouncePlaylistAdd(item, m_slides->Size());
299 void CGUIWindowSlideShow::ShowNext()
301 if (m_slides->Size() == 1)
304 m_iNextSlide = m_iCurrentSlide + 1;
305 if (m_iNextSlide >= m_slides->Size())
312 m_bLoadNextPic = true;
315 void CGUIWindowSlideShow::ShowPrevious()
317 if (m_slides->Size() == 1)
320 m_iNextSlide = m_iCurrentSlide - 1;
321 if (m_iNextSlide < 0)
322 m_iNextSlide = m_slides->Size() - 1;
328 m_bLoadNextPic = true;
332 void CGUIWindowSlideShow::Select(const CStdString& strPicture)
334 for (int i = 0; i < m_slides->Size(); ++i)
336 const CFileItemPtr item = m_slides->Get(i);
337 if (item->GetPath() == strPicture)
345 m_iNextSlide = GetNextSlide();
347 m_bLoadNextPic = true;
353 const CFileItemList &CGUIWindowSlideShow::GetSlideShowContents()
358 void CGUIWindowSlideShow::GetSlideShowContents(CFileItemList &list)
360 for (int index = 0; index < m_slides->Size(); index++)
361 list.Add(CFileItemPtr(new CFileItem(*m_slides->Get(index))));
364 const CFileItemPtr CGUIWindowSlideShow::GetCurrentSlide()
366 if (m_iCurrentSlide >= 0 && m_iCurrentSlide < m_slides->Size())
367 return m_slides->Get(m_iCurrentSlide);
368 return CFileItemPtr();
371 bool CGUIWindowSlideShow::InSlideShow() const
376 void CGUIWindowSlideShow::StartSlideShow(bool screensaver)
380 m_bScreensaver = screensaver;
383 void CGUIWindowSlideShow::Process(unsigned int currentTime, CDirtyRegionList ®ions)
385 // reset the screensaver if we're in a slideshow
386 // (unless we are the screensaver!)
387 if (m_bSlideShow && !g_application.IsInScreenSaver())
388 g_application.ResetScreenSaver();
389 int iSlides = m_slides->Size();
390 if (!iSlides) return ;
392 // if we haven't rendered yet, we should mark the whole screen
394 regions.push_back(CRect(0.0f, 0.0f, (float)g_graphicsContext.GetWidth(), (float)g_graphicsContext.GetHeight()));
396 if (m_iNextSlide < 0 || m_iNextSlide >= m_slides->Size())
398 if (m_iCurrentSlide < 0 || m_iCurrentSlide >= m_slides->Size())
401 // Create our background loader if necessary
402 if (!m_pBackgroundLoader)
404 m_pBackgroundLoader = new CBackgroundPicLoader();
406 if (!m_pBackgroundLoader)
410 m_pBackgroundLoader->Create(this);
413 bool bSlideShow = m_bSlideShow && !m_bPause && !m_bPlayingVideo;
416 { // we have an error when loading either the current or next picture
417 // check to see if we have a picture loaded
418 CLog::Log(LOGDEBUG, "We have an error loading a picture!");
419 if (m_Image[m_iCurrentPic].IsLoaded())
420 { // Yes. Let's let it transistion out, wait for it to be released, then try loading again.
421 CLog::Log(LOGERROR, "Error loading the next image %s", m_slides->Get(m_iNextSlide)->GetPath().c_str());
423 { // tell the pic to start transistioning out now
424 m_Image[m_iCurrentPic].StartTransistion();
425 m_Image[m_iCurrentPic].SetTransistionTime(1, IMMEDIATE_TRANSISTION_TIME); // only 20 frames for the transistion
427 m_bWaitForNextPic = true;
428 m_bErrorMessage = false;
431 { // No. Not much we can do here. If we're in a slideshow, we mayaswell move on to the next picture
432 // change to next image
435 CLog::Log(LOGERROR, "Error loading the current image %s", m_slides->Get(m_iCurrentSlide)->GetPath().c_str());
436 m_iCurrentSlide = m_iNextSlide;
437 m_iNextSlide = GetNextSlide();
439 m_bErrorMessage = false;
441 else if (m_bLoadNextPic)
443 m_iCurrentSlide = m_iNextSlide;
444 m_iNextSlide = GetNextSlide();
445 m_bErrorMessage = false;
447 // else just drop through - there's nothing we can do (error message will be displayed)
452 { // hack, just mark it all
453 regions.push_back(CRect(0.0f, 0.0f, (float)g_graphicsContext.GetWidth(), (float)g_graphicsContext.GetHeight()));
457 if (!m_Image[m_iCurrentPic].IsLoaded() && !m_pBackgroundLoader->IsLoading())
458 { // load first image
459 CLog::Log(LOGDEBUG, "Loading the current image %s", m_slides->Get(m_iCurrentSlide)->GetPath().c_str());
460 m_bWaitForNextPic = false;
461 m_bLoadNextPic = false;
462 // load using the background loader
463 int maxWidth, maxHeight;
464 GetCheckedSize((float)g_settings.m_ResInfo[m_Resolution].iWidth * m_fZoom,
465 (float)g_settings.m_ResInfo[m_Resolution].iHeight * m_fZoom,
466 maxWidth, maxHeight);
467 if (!m_slides->Get(m_iCurrentSlide)->IsVideo())
468 m_pBackgroundLoader->LoadPic(m_iCurrentPic, m_iCurrentSlide, m_slides->Get(m_iCurrentSlide)->GetPath(), maxWidth, maxHeight);
471 // check if we should discard an already loaded next slide
472 if (m_bLoadNextPic && m_Image[1 - m_iCurrentPic].IsLoaded() && m_Image[1 - m_iCurrentPic].SlideNumber() != m_iNextSlide)
473 m_Image[1 - m_iCurrentPic].Close();
475 // if we're reloading an image (for better res on zooming we need to close any open ones as well)
476 if (m_bReloadImage && m_Image[1 - m_iCurrentPic].IsLoaded() && m_Image[1 - m_iCurrentPic].SlideNumber() != m_iCurrentSlide)
477 m_Image[1 - m_iCurrentPic].Close();
481 if (m_Image[m_iCurrentPic].IsLoaded() && !m_Image[1 - m_iCurrentPic].IsLoaded() && !m_pBackgroundLoader->IsLoading() && !m_bWaitForNextPic)
482 { // reload the image if we need to
483 CLog::Log(LOGDEBUG, "Reloading the current image %s at zoom level %i", m_slides->Get(m_iCurrentSlide)->GetPath().c_str(), m_iZoomFactor);
484 // first, our maximal size for this zoom level
485 int maxWidth = (int)((float)g_settings.m_ResInfo[m_Resolution].iWidth * m_fZoom);
486 int maxHeight = (int)((float)g_settings.m_ResInfo[m_Resolution].iWidth * m_fZoom);
488 // the actual maximal size of the image to optimize the sizing based on the known sizing (aspect ratio)
490 GetCheckedSize((float)m_Image[m_iCurrentPic].GetOriginalWidth(), (float)m_Image[m_iCurrentPic].GetOriginalHeight(), width, height);
492 // use the smaller of the two (no point zooming in more than we have to)
493 if (maxWidth < width)
495 if (maxHeight < height)
498 m_pBackgroundLoader->LoadPic(m_iCurrentPic, m_iCurrentSlide, m_slides->Get(m_iCurrentSlide)->GetPath(), width, height);
503 if (m_iNextSlide != m_iCurrentSlide && m_Image[m_iCurrentPic].IsLoaded() && !m_Image[1 - m_iCurrentPic].IsLoaded() && !m_pBackgroundLoader->IsLoading() && !m_bWaitForNextPic)
504 { // load the next image
505 CLog::Log(LOGDEBUG, "Loading the next image %s", m_slides->Get(m_iNextSlide)->GetPath().c_str());
506 int maxWidth, maxHeight;
507 GetCheckedSize((float)g_settings.m_ResInfo[m_Resolution].iWidth * m_fZoom,
508 (float)g_settings.m_ResInfo[m_Resolution].iHeight * m_fZoom,
509 maxWidth, maxHeight);
510 if (!m_slides->Get(m_iNextSlide)->IsVideo())
511 m_pBackgroundLoader->LoadPic(1 - m_iCurrentPic, m_iNextSlide, m_slides->Get(m_iNextSlide)->GetPath(), maxWidth, maxHeight);
515 // render the current image
516 if (m_Image[m_iCurrentPic].IsLoaded())
518 m_Image[m_iCurrentPic].SetInSlideshow(m_bSlideShow);
519 m_Image[m_iCurrentPic].Pause(m_bPause);
520 m_Image[m_iCurrentPic].Process(currentTime, regions);
523 if (m_slides->Get(m_iCurrentSlide)->IsVideo() && bSlideShow)
525 CLog::Log(LOGDEBUG, "Playing slide %s as video", m_slides->Get(m_iCurrentSlide)->GetPath().c_str());
526 m_bPlayingVideo = true;
527 CApplicationMessenger::Get().PlayFile(*m_slides->Get(m_iCurrentSlide));
528 m_iCurrentSlide = m_iNextSlide;
529 m_iNextSlide = GetNextSlide();
532 // Check if we should be transistioning immediately
535 CLog::Log(LOGDEBUG, "Starting immediate transistion due to user wanting slide %s", m_slides->Get(m_iNextSlide)->GetPath().c_str());
536 if (m_Image[m_iCurrentPic].StartTransistion())
538 m_Image[m_iCurrentPic].SetTransistionTime(1, IMMEDIATE_TRANSISTION_TIME); // only 20 frames for the transistion
539 m_bLoadNextPic = false;
543 // render the next image
544 if (m_Image[m_iCurrentPic].DrawNextImage())
546 if (m_Image[1 - m_iCurrentPic].IsLoaded())
548 // set the appropriate transistion time
549 m_Image[1 - m_iCurrentPic].SetTransistionTime(0, m_Image[m_iCurrentPic].GetTransistionTime(1));
550 m_Image[1 - m_iCurrentPic].Pause(m_bPause);
551 m_Image[1 - m_iCurrentPic].Process(currentTime, regions);
553 else // next pic isn't loaded. We should hang around if it is in progress
555 if (m_pBackgroundLoader->IsLoading())
557 // CLog::Log(LOGDEBUG, "Having to hold the current image (%s) while we load %s", m_vecSlides[m_iCurrentSlide].c_str(), m_vecSlides[m_iNextSlide].c_str());
558 m_Image[m_iCurrentPic].Keep();
563 // check if we should swap images now
564 if (m_Image[m_iCurrentPic].IsFinished())
566 CLog::Log(LOGDEBUG, "Image %s is finished rendering, switching to %s", m_slides->Get(m_iCurrentSlide)->GetPath().c_str(), m_slides->Get(m_iNextSlide)->GetPath().c_str());
567 m_Image[m_iCurrentPic].Close();
568 if (m_Image[1 - m_iCurrentPic].IsLoaded())
569 m_iCurrentPic = 1 - m_iCurrentPic;
571 m_iCurrentSlide = m_iNextSlide;
572 m_iNextSlide = GetNextSlide();
573 AnnouncePlayerPlay(m_slides->Get(m_iCurrentSlide));
580 if (m_Image[m_iCurrentPic].IsLoaded())
581 g_infoManager.SetCurrentSlide(*m_slides->Get(m_iCurrentSlide));
584 CGUIWindow::Process(currentTime, regions);
587 void CGUIWindowSlideShow::Render()
589 if (m_Image[m_iCurrentPic].IsLoaded())
590 m_Image[m_iCurrentPic].Render();
592 if (m_Image[m_iCurrentPic].DrawNextImage() && m_Image[1 - m_iCurrentPic].IsLoaded())
593 m_Image[1 - m_iCurrentPic].Render();
595 RenderErrorMessage();
596 CGUIWindow::Render();
599 int CGUIWindowSlideShow::GetNextSlide()
601 if (m_slides->Size() <= 1)
602 return m_iCurrentSlide;
603 if (m_bSlideShow || m_iDirection >= 0)
604 return (m_iCurrentSlide + 1) % m_slides->Size();
606 return (m_iCurrentSlide - 1 + m_slides->Size()) % m_slides->Size();
609 EVENT_RESULT CGUIWindowSlideShow::OnMouseEvent(const CPoint &point, const CMouseEvent &event)
611 if (event.m_id == ACTION_GESTURE_NOTIFY)
613 if (m_iZoomFactor == 1) //zoomed out - no inertial scrolling
614 return EVENT_RESULT_PAN_HORIZONTAL_WITHOUT_INERTIA;
616 return EVENT_RESULT_PAN_HORIZONTAL;
618 else if (event.m_id == ACTION_GESTURE_BEGIN)
620 m_firstGesturePoint = point;
621 m_fInitialZoom = m_fZoom;
622 m_fInitialRotate = m_fRotate;
623 return EVENT_RESULT_HANDLED;
625 else if (event.m_id == ACTION_GESTURE_PAN)
626 { // on zoomlevel 1 just detect swipe left and right
627 if (m_iZoomFactor == 1 || !m_Image[m_iCurrentPic].m_bCanMoveHorizontally)
629 if (m_firstGesturePoint.x > 0 && fabs(point.x - m_firstGesturePoint.x) > 100)
631 if (point.x < m_firstGesturePoint.x)
632 OnAction(CAction(ACTION_NEXT_PICTURE));
634 OnAction(CAction(ACTION_PREV_PICTURE));
636 m_firstGesturePoint.x = 0;
639 else //zoomed in - free move mode
641 Move(PICTURE_MOVE_AMOUNT_TOUCH / m_iZoomFactor * (m_firstGesturePoint.x - point.x), PICTURE_MOVE_AMOUNT_TOUCH / m_iZoomFactor * (m_firstGesturePoint.y - point.y));
642 m_firstGesturePoint = point;
644 return EVENT_RESULT_HANDLED;
646 else if (event.m_id == ACTION_GESTURE_END)
648 if (m_fRotate != 0.0f)
650 // "snap" to nearest of 0, 90, 180 and 270 if the
651 // difference in angle is +/-10 degrees
652 float reminder = fmodf(m_fRotate, 90.0f);
653 if (fabs(reminder) < ROTATION_SNAP_RANGE)
655 else if (reminder > 90.0f - ROTATION_SNAP_RANGE)
656 Rotate(90.0f - reminder);
657 else if (-reminder > 90.0f - ROTATION_SNAP_RANGE)
658 Rotate(-90.0f - reminder);
661 m_fInitialZoom = 0.0f;
662 m_fInitialRotate = 0.0f;
663 return EVENT_RESULT_HANDLED;
665 else if (event.m_id == ACTION_GESTURE_ZOOM)
667 ZoomRelative(m_fInitialZoom * event.m_offsetX, true);
668 return EVENT_RESULT_HANDLED;
670 else if (event.m_id == ACTION_GESTURE_ROTATE)
672 Rotate(m_fInitialRotate + event.m_offsetX - m_fRotate, true);
673 return EVENT_RESULT_HANDLED;
675 return EVENT_RESULT_UNHANDLED;
678 bool CGUIWindowSlideShow::OnAction(const CAction &action)
682 g_windowManager.PreviousWindow();
686 switch (action.GetID())
688 case ACTION_SHOW_CODEC:
690 CGUIDialogPictureInfo *pictureInfo = (CGUIDialogPictureInfo *)g_windowManager.GetWindow(WINDOW_DIALOG_PICTURE_INFO);
693 // no need to set the picture here, it's done in Render()
694 pictureInfo->DoModal();
699 case ACTION_PREVIOUS_MENU:
700 case ACTION_NAV_BACK:
702 if (m_bSlideShow && m_slides->Size())
703 AnnouncePlayerStop(m_slides->Get(m_iCurrentSlide));
704 g_windowManager.PreviousWindow();
707 case ACTION_NEXT_PICTURE:
711 case ACTION_PREV_PICTURE:
715 case ACTION_MOVE_RIGHT:
716 if (m_iZoomFactor == 1 || !m_Image[m_iCurrentPic].m_bCanMoveHorizontally)
719 Move(PICTURE_MOVE_AMOUNT, 0);
722 case ACTION_MOVE_LEFT:
723 if (m_iZoomFactor == 1 || !m_Image[m_iCurrentPic].m_bCanMoveHorizontally)
726 Move( -PICTURE_MOVE_AMOUNT, 0);
729 case ACTION_MOVE_DOWN:
730 Move(0, PICTURE_MOVE_AMOUNT);
734 Move(0, -PICTURE_MOVE_AMOUNT);
740 m_bPause = !m_bPause;
741 if (m_slides->Size())
744 AnnouncePlayerPause(m_slides->Get(m_iCurrentSlide));
746 AnnouncePlayerPlay(m_slides->Get(m_iCurrentSlide));
751 case ACTION_PLAYER_PLAY:
760 if (m_slides->Size())
761 AnnouncePlayerPlay(m_slides->Get(m_iCurrentSlide));
765 case ACTION_ZOOM_OUT:
766 Zoom(m_iZoomFactor - 1);
770 Zoom(m_iZoomFactor + 1);
773 case ACTION_ROTATE_PICTURE_CW:
777 case ACTION_ROTATE_PICTURE_CCW:
781 case ACTION_ZOOM_LEVEL_NORMAL:
782 case ACTION_ZOOM_LEVEL_1:
783 case ACTION_ZOOM_LEVEL_2:
784 case ACTION_ZOOM_LEVEL_3:
785 case ACTION_ZOOM_LEVEL_4:
786 case ACTION_ZOOM_LEVEL_5:
787 case ACTION_ZOOM_LEVEL_6:
788 case ACTION_ZOOM_LEVEL_7:
789 case ACTION_ZOOM_LEVEL_8:
790 case ACTION_ZOOM_LEVEL_9:
791 Zoom((action.GetID() - ACTION_ZOOM_LEVEL_NORMAL) + 1);
794 case ACTION_ANALOG_MOVE:
795 Move(action.GetAmount()*PICTURE_MOVE_AMOUNT_ANALOG, -action.GetAmount(1)*PICTURE_MOVE_AMOUNT_ANALOG);
799 return CGUIWindow::OnAction(action);
804 void CGUIWindowSlideShow::RenderErrorMessage()
806 if (!m_bErrorMessage)
809 const CGUIControl *control = GetControl(LABEL_ROW1);
810 if (NULL == control || control->GetControlType() != CGUIControl::GUICONTROL_LABEL)
812 CLog::Log(LOGERROR,"CGUIWindowSlideShow::RenderErrorMessage - cant get label control!");
816 CGUIFont *pFont = ((CGUILabelControl *)control)->GetLabelInfo().font;
817 CGUITextLayout::DrawText(pFont, 0.5f*g_graphicsContext.GetWidth(), 0.5f*g_graphicsContext.GetHeight(), 0xffffffff, 0, g_localizeStrings.Get(747), XBFONT_CENTER_X | XBFONT_CENTER_Y);
820 bool CGUIWindowSlideShow::OnMessage(CGUIMessage& message)
822 switch ( message.GetMessage() )
824 case GUI_MSG_WINDOW_INIT:
826 m_Resolution = (RESOLUTION) g_guiSettings.GetInt("pictures.displayresolution");
828 //FIXME: Use GUI resolution for now
829 if (0 /*m_Resolution != g_guiSettings.m_LookAndFeelResolution && m_Resolution != INVALID && m_Resolution!=AUTORES*/)
830 g_graphicsContext.SetVideoResolution(m_Resolution);
832 m_Resolution = g_graphicsContext.GetVideoResolution();
834 CGUIWindow::OnMessage(message);
835 if (message.GetParam1() != WINDOW_PICTURES)
838 g_windowManager.ShowOverlay(OVERLAY_STATE_HIDDEN);
840 // turn off slideshow if we only have 1 image
841 if (m_slides->Size() <= 1)
842 m_bSlideShow = false;
848 case GUI_MSG_START_SLIDESHOW:
850 CStdString strFolder = message.GetStringParam();
851 unsigned int iParams = message.GetParam1();
853 bool bRecursive = false;
854 bool bRandom = false;
855 bool bNotRandom = false;
858 if ((iParams & 1) == 1)
860 if ((iParams & 2) == 2)
862 if ((iParams & 4) == 4)
865 RunSlideShow(strFolder, bRecursive, bRandom, bNotRandom);
869 case GUI_MSG_PLAYLISTPLAYER_STOPPED:
871 m_bPlayingVideo = false;
873 g_windowManager.ActivateWindow(WINDOW_SLIDESHOW);
877 case GUI_MSG_PLAYBACK_STARTED:
879 if (m_bSlideShow && m_bPlayingVideo)
880 g_windowManager.ActivateWindow(WINDOW_FULLSCREEN_VIDEO);
884 case GUI_MSG_PLAYBACK_STOPPED:
886 if (m_bSlideShow && m_bPlayingVideo)
888 m_bSlideShow = false;
889 g_windowManager.PreviousWindow();
894 return CGUIWindow::OnMessage(message);
897 void CGUIWindowSlideShow::RenderPause()
898 { // display the pause icon
901 SET_CONTROL_VISIBLE(CONTROL_PAUSE);
905 SET_CONTROL_HIDDEN(CONTROL_PAUSE);
908 static DWORD dwCounter=0;
914 if (!m_bPause) return;
915 if (dwCounter <13) return;*/
919 void CGUIWindowSlideShow::Rotate(float fAngle, bool immediate /* = false */)
921 if (m_Image[m_iCurrentPic].DrawNextImage())
926 m_Image[m_iCurrentPic].Rotate(fAngle, immediate);
929 void CGUIWindowSlideShow::Zoom(int iZoom)
931 if (iZoom > MAX_ZOOM_FACTOR || iZoom < 1)
934 ZoomRelative(zoomamount[iZoom - 1]);
937 void CGUIWindowSlideShow::ZoomRelative(float fZoom, bool immediate /* = false */)
939 if (fZoom < zoomamount[0])
940 fZoom = zoomamount[0];
941 else if (fZoom > zoomamount[MAX_ZOOM_FACTOR - 1])
942 fZoom = zoomamount[MAX_ZOOM_FACTOR - 1];
944 if (m_Image[m_iCurrentPic].DrawNextImage())
949 // find the nearest zoom factor
950 #ifdef RELOAD_ON_ZOOM
951 int iOldZoomFactor = m_iZoomFactor;
953 for (unsigned int i = 1; i < MAX_ZOOM_FACTOR; i++)
955 if (m_fZoom > zoomamount[i])
958 if (fabs(m_fZoom - zoomamount[i - 1]) < fabs(m_fZoom - zoomamount[i]))
961 m_iZoomFactor = i + 1;
966 // set the zoom amount and then set so that the image is reloaded at the higher (or lower)
967 // resolution as necessary
968 m_Image[m_iCurrentPic].Zoom(m_fZoom, immediate);
970 #ifdef RELOAD_ON_ZOOM
971 if (m_iZoomFactor == 1 || (iZoomFactor > iOldZoomFactor && !m_Image[m_iCurrentPic].FullSize()))
972 m_bReloadImage = true;
976 void CGUIWindowSlideShow::Move(float fX, float fY)
978 if (m_Image[m_iCurrentPic].IsLoaded() && m_Image[m_iCurrentPic].GetZoom() > 1)
979 { // we move in the opposite direction, due to the fact we are moving
980 // the viewing window, not the picture.
981 m_Image[m_iCurrentPic].Move( -fX, -fY);
985 void CGUIWindowSlideShow::OnLoadPic(int iPic, int iSlideNumber, CBaseTexture* pTexture, bool bFullSize)
989 // set the pic's texture + size etc.
990 CSingleLock lock(m_slideSection);
991 if (iSlideNumber >= m_slides->Size())
992 { // throw this away - we must have cleared the slideshow while we were still loading
996 CLog::Log(LOGDEBUG, "Finished background loading %s", m_slides->Get(iSlideNumber)->GetPath().c_str());
999 if (m_Image[m_iCurrentPic].IsLoaded() && m_Image[m_iCurrentPic].SlideNumber() != iSlideNumber)
1000 { // wrong image (ie we finished loading the next image, not the current image)
1004 m_Image[m_iCurrentPic].UpdateTexture(pTexture);
1005 m_Image[m_iCurrentPic].SetOriginalSize(pTexture->GetOriginalWidth(), pTexture->GetOriginalHeight(), bFullSize);
1006 m_bReloadImage = false;
1011 m_Image[iPic].SetTexture(iSlideNumber, pTexture, g_guiSettings.GetBool("slideshow.displayeffects") ? CSlideShowPic::EFFECT_RANDOM : CSlideShowPic::EFFECT_NONE);
1013 m_Image[iPic].SetTexture(iSlideNumber, pTexture, CSlideShowPic::EFFECT_NO_TIMEOUT);
1014 m_Image[iPic].SetOriginalSize(pTexture->GetOriginalWidth(), pTexture->GetOriginalHeight(), bFullSize);
1016 m_Image[iPic].m_bIsComic = false;
1017 if (URIUtils::IsInRAR(m_slides->Get(m_iCurrentSlide)->GetPath()) || URIUtils::IsInZIP(m_slides->Get(m_iCurrentSlide)->GetPath())) // move to top for cbr/cbz
1019 CURL url(m_slides->Get(m_iCurrentSlide)->GetPath());
1020 CStdString strHostName = url.GetHostName();
1021 if (URIUtils::GetExtension(strHostName).Equals(".cbr", false) || URIUtils::GetExtension(strHostName).Equals(".cbz", false))
1023 m_Image[iPic].m_bIsComic = true;
1024 m_Image[iPic].Move((float)m_Image[iPic].GetOriginalWidth(),(float)m_Image[iPic].GetOriginalHeight());
1030 { // Failed to load image. What should be done??
1031 // We should wait for the current pic to finish rendering, then transistion it out,
1032 // release the texture, and try and reload this pic from scratch
1033 m_bErrorMessage = true;
1037 void CGUIWindowSlideShow::Shuffle()
1039 m_slides->Randomize();
1040 m_iCurrentSlide = 0;
1044 AnnouncePropertyChanged("shuffled", true);
1047 int CGUIWindowSlideShow::NumSlides() const
1049 return m_slides->Size();
1052 int CGUIWindowSlideShow::CurrentSlide() const
1054 return m_iCurrentSlide + 1;
1057 void CGUIWindowSlideShow::AddFromPath(const CStdString &strPath,
1059 SORT_METHOD method, SortOrder order, const CStdString &strExtensions)
1063 // reset the slideshow
1065 m_strExtensions = strExtensions;
1068 path_set recursivePaths;
1069 AddItems(strPath, &recursivePaths, method, order);
1072 AddItems(strPath, NULL, method, order);
1076 void CGUIWindowSlideShow::RunSlideShow(const CStdString &strPath,
1077 bool bRecursive /* = false */, bool bRandom /* = false */,
1078 bool bNotRandom /* = false */, SORT_METHOD method /* = SORT_METHOD_LABEL */,
1079 SortOrder order /* = SortOrderAscending */, const CStdString &strExtensions)
1082 if (g_application.IsPlayingVideo())
1083 g_application.StopPlaying();
1085 AddFromPath(strPath, bRecursive, method, order, strExtensions);
1087 // mutually exclusive options
1088 // if both are set, clear both and use the gui setting
1089 if (bRandom && bNotRandom)
1090 bRandom = bNotRandom = false;
1092 // NotRandom overrides the window setting
1093 if ((!bNotRandom && g_guiSettings.GetBool("slideshow.shuffle")) || bRandom)
1098 g_windowManager.ActivateWindow(WINDOW_SLIDESHOW);
1101 void CGUIWindowSlideShow::AddItems(const CStdString &strPath, path_set *recursivePaths, SORT_METHOD method, SortOrder order)
1103 // check whether we've already added this path
1106 CStdString path(strPath);
1107 URIUtils::RemoveSlashAtEnd(path);
1108 if (recursivePaths->find(path) != recursivePaths->end())
1110 recursivePaths->insert(path);
1113 // fetch directory and sort accordingly
1114 CFileItemList items;
1115 if (!CDirectory::GetDirectory(strPath, items, m_strExtensions.IsEmpty()?g_settings.m_pictureExtensions:m_strExtensions,DIR_FLAG_NO_FILE_DIRS,true))
1118 items.Sort(method, order);
1120 // need to go into all subdirs
1121 for (int i = 0; i < items.Size(); i++)
1123 CFileItemPtr item = items[i];
1124 if (item->m_bIsFolder && recursivePaths)
1126 AddItems(item->GetPath(), recursivePaths);
1128 else if (!item->m_bIsFolder && !URIUtils::IsRAR(item->GetPath()) && !URIUtils::IsZIP(item->GetPath()))
1129 { // add to the slideshow
1135 void CGUIWindowSlideShow::GetCheckedSize(float width, float height, int &maxWidth, int &maxHeight)
1137 #ifdef RELOAD_ON_ZOOM
1138 if (width * height > MAX_PICTURE_SIZE)
1140 float fScale = sqrt((float)MAX_PICTURE_SIZE / (width * height));
1141 width = fScale * width;
1142 height = fScale * height;
1144 maxWidth = (int)width;
1145 maxHeight = (int)height;
1146 if (maxWidth > (int)g_Windowing.GetMaxTextureSize())
1147 maxWidth = g_Windowing.GetMaxTextureSize();
1148 if (maxHeight > (int)g_Windowing.GetMaxTextureSize())
1149 maxHeight = g_Windowing.GetMaxTextureSize();
1151 maxWidth = g_Windowing.GetMaxTextureSize();
1152 maxHeight = g_Windowing.GetMaxTextureSize();