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;
381 if (m_slides->Size())
382 AnnouncePlayerPlay(m_slides->Get(m_iCurrentSlide));
385 void CGUIWindowSlideShow::Process(unsigned int currentTime, CDirtyRegionList ®ions)
387 // reset the screensaver if we're in a slideshow
388 // (unless we are the screensaver!)
389 if (m_bSlideShow && !g_application.IsInScreenSaver())
390 g_application.ResetScreenSaver();
391 int iSlides = m_slides->Size();
392 if (!iSlides) return ;
394 // if we haven't rendered yet, we should mark the whole screen
396 regions.push_back(CRect(0.0f, 0.0f, (float)g_graphicsContext.GetWidth(), (float)g_graphicsContext.GetHeight()));
398 if (m_iNextSlide < 0 || m_iNextSlide >= m_slides->Size())
400 if (m_iCurrentSlide < 0 || m_iCurrentSlide >= m_slides->Size())
403 // Create our background loader if necessary
404 if (!m_pBackgroundLoader)
406 m_pBackgroundLoader = new CBackgroundPicLoader();
408 if (!m_pBackgroundLoader)
412 m_pBackgroundLoader->Create(this);
415 bool bSlideShow = m_bSlideShow && !m_bPause && !m_bPlayingVideo;
418 { // we have an error when loading either the current or next picture
419 // check to see if we have a picture loaded
420 CLog::Log(LOGDEBUG, "We have an error loading a picture!");
421 if (m_Image[m_iCurrentPic].IsLoaded())
422 { // Yes. Let's let it transistion out, wait for it to be released, then try loading again.
423 CLog::Log(LOGERROR, "Error loading the next image %s", m_slides->Get(m_iNextSlide)->GetPath().c_str());
425 { // tell the pic to start transistioning out now
426 m_Image[m_iCurrentPic].StartTransistion();
427 m_Image[m_iCurrentPic].SetTransistionTime(1, IMMEDIATE_TRANSISTION_TIME); // only 20 frames for the transistion
429 m_bWaitForNextPic = true;
430 m_bErrorMessage = false;
433 { // No. Not much we can do here. If we're in a slideshow, we mayaswell move on to the next picture
434 // change to next image
437 CLog::Log(LOGERROR, "Error loading the current image %s", m_slides->Get(m_iCurrentSlide)->GetPath().c_str());
438 m_iCurrentSlide = m_iNextSlide;
439 m_iNextSlide = GetNextSlide();
441 m_bErrorMessage = false;
443 else if (m_bLoadNextPic)
445 m_iCurrentSlide = m_iNextSlide;
446 m_iNextSlide = GetNextSlide();
447 m_bErrorMessage = false;
449 // else just drop through - there's nothing we can do (error message will be displayed)
454 { // hack, just mark it all
455 regions.push_back(CRect(0.0f, 0.0f, (float)g_graphicsContext.GetWidth(), (float)g_graphicsContext.GetHeight()));
459 if (!m_Image[m_iCurrentPic].IsLoaded() && !m_pBackgroundLoader->IsLoading())
460 { // load first image
461 CLog::Log(LOGDEBUG, "Loading the current image %s", m_slides->Get(m_iCurrentSlide)->GetPath().c_str());
462 m_bWaitForNextPic = false;
463 m_bLoadNextPic = false;
464 // load using the background loader
465 int maxWidth, maxHeight;
466 GetCheckedSize((float)g_settings.m_ResInfo[m_Resolution].iWidth * m_fZoom,
467 (float)g_settings.m_ResInfo[m_Resolution].iHeight * m_fZoom,
468 maxWidth, maxHeight);
469 if (!m_slides->Get(m_iCurrentSlide)->IsVideo())
470 m_pBackgroundLoader->LoadPic(m_iCurrentPic, m_iCurrentSlide, m_slides->Get(m_iCurrentSlide)->GetPath(), maxWidth, maxHeight);
473 // check if we should discard an already loaded next slide
474 if (m_bLoadNextPic && m_Image[1 - m_iCurrentPic].IsLoaded() && m_Image[1 - m_iCurrentPic].SlideNumber() != m_iNextSlide)
475 m_Image[1 - m_iCurrentPic].Close();
477 // if we're reloading an image (for better res on zooming we need to close any open ones as well)
478 if (m_bReloadImage && m_Image[1 - m_iCurrentPic].IsLoaded() && m_Image[1 - m_iCurrentPic].SlideNumber() != m_iCurrentSlide)
479 m_Image[1 - m_iCurrentPic].Close();
483 if (m_Image[m_iCurrentPic].IsLoaded() && !m_Image[1 - m_iCurrentPic].IsLoaded() && !m_pBackgroundLoader->IsLoading() && !m_bWaitForNextPic)
484 { // reload the image if we need to
485 CLog::Log(LOGDEBUG, "Reloading the current image %s at zoom level %i", m_slides->Get(m_iCurrentSlide)->GetPath().c_str(), m_iZoomFactor);
486 // first, our maximal size for this zoom level
487 int maxWidth = (int)((float)g_settings.m_ResInfo[m_Resolution].iWidth * m_fZoom);
488 int maxHeight = (int)((float)g_settings.m_ResInfo[m_Resolution].iWidth * m_fZoom);
490 // the actual maximal size of the image to optimize the sizing based on the known sizing (aspect ratio)
492 GetCheckedSize((float)m_Image[m_iCurrentPic].GetOriginalWidth(), (float)m_Image[m_iCurrentPic].GetOriginalHeight(), width, height);
494 // use the smaller of the two (no point zooming in more than we have to)
495 if (maxWidth < width)
497 if (maxHeight < height)
500 m_pBackgroundLoader->LoadPic(m_iCurrentPic, m_iCurrentSlide, m_slides->Get(m_iCurrentSlide)->GetPath(), width, height);
505 if (m_iNextSlide != m_iCurrentSlide && m_Image[m_iCurrentPic].IsLoaded() && !m_Image[1 - m_iCurrentPic].IsLoaded() && !m_pBackgroundLoader->IsLoading() && !m_bWaitForNextPic)
506 { // load the next image
507 CLog::Log(LOGDEBUG, "Loading the next image %s", m_slides->Get(m_iNextSlide)->GetPath().c_str());
508 int maxWidth, maxHeight;
509 GetCheckedSize((float)g_settings.m_ResInfo[m_Resolution].iWidth * m_fZoom,
510 (float)g_settings.m_ResInfo[m_Resolution].iHeight * m_fZoom,
511 maxWidth, maxHeight);
512 if (!m_slides->Get(m_iNextSlide)->IsVideo())
513 m_pBackgroundLoader->LoadPic(1 - m_iCurrentPic, m_iNextSlide, m_slides->Get(m_iNextSlide)->GetPath(), maxWidth, maxHeight);
517 // render the current image
518 if (m_Image[m_iCurrentPic].IsLoaded())
520 m_Image[m_iCurrentPic].SetInSlideshow(m_bSlideShow);
521 m_Image[m_iCurrentPic].Pause(m_bPause);
522 m_Image[m_iCurrentPic].Process(currentTime, regions);
525 if (m_slides->Get(m_iCurrentSlide)->IsVideo() && bSlideShow)
527 CLog::Log(LOGDEBUG, "Playing slide %s as video", m_slides->Get(m_iCurrentSlide)->GetPath().c_str());
528 m_bPlayingVideo = true;
529 CApplicationMessenger::Get().PlayFile(*m_slides->Get(m_iCurrentSlide));
530 m_iCurrentSlide = m_iNextSlide;
531 m_iNextSlide = GetNextSlide();
534 // Check if we should be transistioning immediately
537 CLog::Log(LOGDEBUG, "Starting immediate transistion due to user wanting slide %s", m_slides->Get(m_iNextSlide)->GetPath().c_str());
538 if (m_Image[m_iCurrentPic].StartTransistion())
540 m_Image[m_iCurrentPic].SetTransistionTime(1, IMMEDIATE_TRANSISTION_TIME); // only 20 frames for the transistion
541 m_bLoadNextPic = false;
545 // render the next image
546 if (m_Image[m_iCurrentPic].DrawNextImage())
548 if (m_Image[1 - m_iCurrentPic].IsLoaded())
550 // set the appropriate transistion time
551 m_Image[1 - m_iCurrentPic].SetTransistionTime(0, m_Image[m_iCurrentPic].GetTransistionTime(1));
552 m_Image[1 - m_iCurrentPic].Pause(m_bPause);
553 m_Image[1 - m_iCurrentPic].Process(currentTime, regions);
555 else // next pic isn't loaded. We should hang around if it is in progress
557 if (m_pBackgroundLoader->IsLoading())
559 // 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());
560 m_Image[m_iCurrentPic].Keep();
565 // check if we should swap images now
566 if (m_Image[m_iCurrentPic].IsFinished())
568 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());
569 m_Image[m_iCurrentPic].Close();
570 if (m_Image[1 - m_iCurrentPic].IsLoaded())
571 m_iCurrentPic = 1 - m_iCurrentPic;
573 m_iCurrentSlide = m_iNextSlide;
574 m_iNextSlide = GetNextSlide();
575 AnnouncePlayerPlay(m_slides->Get(m_iCurrentSlide));
582 if (m_Image[m_iCurrentPic].IsLoaded())
583 g_infoManager.SetCurrentSlide(*m_slides->Get(m_iCurrentSlide));
586 CGUIWindow::Process(currentTime, regions);
589 void CGUIWindowSlideShow::Render()
591 if (m_Image[m_iCurrentPic].IsLoaded())
592 m_Image[m_iCurrentPic].Render();
594 if (m_Image[m_iCurrentPic].DrawNextImage() && m_Image[1 - m_iCurrentPic].IsLoaded())
595 m_Image[1 - m_iCurrentPic].Render();
597 RenderErrorMessage();
598 CGUIWindow::Render();
601 int CGUIWindowSlideShow::GetNextSlide()
603 if (m_slides->Size() <= 1)
604 return m_iCurrentSlide;
605 if (m_bSlideShow || m_iDirection >= 0)
606 return (m_iCurrentSlide + 1) % m_slides->Size();
608 return (m_iCurrentSlide - 1 + m_slides->Size()) % m_slides->Size();
611 EVENT_RESULT CGUIWindowSlideShow::OnMouseEvent(const CPoint &point, const CMouseEvent &event)
613 if (event.m_id == ACTION_GESTURE_NOTIFY)
615 if (m_iZoomFactor == 1) //zoomed out - no inertial scrolling
616 return EVENT_RESULT_PAN_HORIZONTAL_WITHOUT_INERTIA;
618 return EVENT_RESULT_PAN_HORIZONTAL;
620 else if (event.m_id == ACTION_GESTURE_BEGIN)
622 m_firstGesturePoint = point;
623 m_fInitialZoom = m_fZoom;
624 m_fInitialRotate = m_fRotate;
625 return EVENT_RESULT_HANDLED;
627 else if (event.m_id == ACTION_GESTURE_PAN)
628 { // on zoomlevel 1 just detect swipe left and right
629 if (m_iZoomFactor == 1 || !m_Image[m_iCurrentPic].m_bCanMoveHorizontally)
631 if (m_firstGesturePoint.x > 0 && fabs(point.x - m_firstGesturePoint.x) > 100)
633 if (point.x < m_firstGesturePoint.x)
634 OnAction(CAction(ACTION_NEXT_PICTURE));
636 OnAction(CAction(ACTION_PREV_PICTURE));
638 m_firstGesturePoint.x = 0;
641 else //zoomed in - free move mode
643 Move(PICTURE_MOVE_AMOUNT_TOUCH / m_iZoomFactor * (m_firstGesturePoint.x - point.x), PICTURE_MOVE_AMOUNT_TOUCH / m_iZoomFactor * (m_firstGesturePoint.y - point.y));
644 m_firstGesturePoint = point;
646 return EVENT_RESULT_HANDLED;
648 else if (event.m_id == ACTION_GESTURE_END)
650 if (m_fRotate != 0.0f)
652 // "snap" to nearest of 0, 90, 180 and 270 if the
653 // difference in angle is +/-10 degrees
654 float reminder = fmodf(m_fRotate, 90.0f);
655 if (fabs(reminder) < ROTATION_SNAP_RANGE)
657 else if (reminder > 90.0f - ROTATION_SNAP_RANGE)
658 Rotate(90.0f - reminder);
659 else if (-reminder > 90.0f - ROTATION_SNAP_RANGE)
660 Rotate(-90.0f - reminder);
663 m_fInitialZoom = 0.0f;
664 m_fInitialRotate = 0.0f;
665 return EVENT_RESULT_HANDLED;
667 else if (event.m_id == ACTION_GESTURE_ZOOM)
669 ZoomRelative(m_fInitialZoom * event.m_offsetX, true);
670 return EVENT_RESULT_HANDLED;
672 else if (event.m_id == ACTION_GESTURE_ROTATE)
674 Rotate(m_fInitialRotate + event.m_offsetX - m_fRotate, true);
675 return EVENT_RESULT_HANDLED;
677 return EVENT_RESULT_UNHANDLED;
680 bool CGUIWindowSlideShow::OnAction(const CAction &action)
684 g_windowManager.PreviousWindow();
688 switch (action.GetID())
690 case ACTION_SHOW_CODEC:
692 CGUIDialogPictureInfo *pictureInfo = (CGUIDialogPictureInfo *)g_windowManager.GetWindow(WINDOW_DIALOG_PICTURE_INFO);
695 // no need to set the picture here, it's done in Render()
696 pictureInfo->DoModal();
701 case ACTION_PREVIOUS_MENU:
702 case ACTION_NAV_BACK:
704 if (m_slides->Size())
705 AnnouncePlayerStop(m_slides->Get(m_iCurrentSlide));
706 g_windowManager.PreviousWindow();
709 case ACTION_NEXT_PICTURE:
713 case ACTION_PREV_PICTURE:
717 case ACTION_MOVE_RIGHT:
718 if (m_iZoomFactor == 1 || !m_Image[m_iCurrentPic].m_bCanMoveHorizontally)
721 Move(PICTURE_MOVE_AMOUNT, 0);
724 case ACTION_MOVE_LEFT:
725 if (m_iZoomFactor == 1 || !m_Image[m_iCurrentPic].m_bCanMoveHorizontally)
728 Move( -PICTURE_MOVE_AMOUNT, 0);
731 case ACTION_MOVE_DOWN:
732 Move(0, PICTURE_MOVE_AMOUNT);
736 Move(0, -PICTURE_MOVE_AMOUNT);
742 m_bPause = !m_bPause;
743 if (m_slides->Size())
746 AnnouncePlayerPause(m_slides->Get(m_iCurrentSlide));
748 AnnouncePlayerPlay(m_slides->Get(m_iCurrentSlide));
753 case ACTION_PLAYER_PLAY:
762 if (m_slides->Size())
763 AnnouncePlayerPlay(m_slides->Get(m_iCurrentSlide));
767 case ACTION_ZOOM_OUT:
768 Zoom(m_iZoomFactor - 1);
772 Zoom(m_iZoomFactor + 1);
775 case ACTION_ROTATE_PICTURE_CW:
779 case ACTION_ROTATE_PICTURE_CCW:
783 case ACTION_ZOOM_LEVEL_NORMAL:
784 case ACTION_ZOOM_LEVEL_1:
785 case ACTION_ZOOM_LEVEL_2:
786 case ACTION_ZOOM_LEVEL_3:
787 case ACTION_ZOOM_LEVEL_4:
788 case ACTION_ZOOM_LEVEL_5:
789 case ACTION_ZOOM_LEVEL_6:
790 case ACTION_ZOOM_LEVEL_7:
791 case ACTION_ZOOM_LEVEL_8:
792 case ACTION_ZOOM_LEVEL_9:
793 Zoom((action.GetID() - ACTION_ZOOM_LEVEL_NORMAL) + 1);
796 case ACTION_ANALOG_MOVE:
797 Move(action.GetAmount()*PICTURE_MOVE_AMOUNT_ANALOG, -action.GetAmount(1)*PICTURE_MOVE_AMOUNT_ANALOG);
801 return CGUIWindow::OnAction(action);
806 void CGUIWindowSlideShow::RenderErrorMessage()
808 if (!m_bErrorMessage)
811 const CGUIControl *control = GetControl(LABEL_ROW1);
812 if (NULL == control || control->GetControlType() != CGUIControl::GUICONTROL_LABEL)
814 CLog::Log(LOGERROR,"CGUIWindowSlideShow::RenderErrorMessage - cant get label control!");
818 CGUIFont *pFont = ((CGUILabelControl *)control)->GetLabelInfo().font;
819 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);
822 bool CGUIWindowSlideShow::OnMessage(CGUIMessage& message)
824 switch ( message.GetMessage() )
826 case GUI_MSG_WINDOW_INIT:
828 m_Resolution = (RESOLUTION) g_guiSettings.GetInt("pictures.displayresolution");
830 //FIXME: Use GUI resolution for now
831 if (0 /*m_Resolution != g_guiSettings.m_LookAndFeelResolution && m_Resolution != INVALID && m_Resolution!=AUTORES*/)
832 g_graphicsContext.SetVideoResolution(m_Resolution);
834 m_Resolution = g_graphicsContext.GetVideoResolution();
836 CGUIWindow::OnMessage(message);
837 if (message.GetParam1() != WINDOW_PICTURES)
840 g_windowManager.ShowOverlay(OVERLAY_STATE_HIDDEN);
842 // turn off slideshow if we only have 1 image
843 if (m_slides->Size() <= 1)
844 m_bSlideShow = false;
850 case GUI_MSG_START_SLIDESHOW:
852 CStdString strFolder = message.GetStringParam();
853 unsigned int iParams = message.GetParam1();
855 bool bRecursive = false;
856 bool bRandom = false;
857 bool bNotRandom = false;
860 if ((iParams & 1) == 1)
862 if ((iParams & 2) == 2)
864 if ((iParams & 4) == 4)
867 RunSlideShow(strFolder, bRecursive, bRandom, bNotRandom);
871 case GUI_MSG_PLAYLISTPLAYER_STOPPED:
873 m_bPlayingVideo = false;
875 g_windowManager.ActivateWindow(WINDOW_SLIDESHOW);
879 case GUI_MSG_PLAYBACK_STARTED:
881 if (m_bSlideShow && m_bPlayingVideo)
882 g_windowManager.ActivateWindow(WINDOW_FULLSCREEN_VIDEO);
886 case GUI_MSG_PLAYBACK_STOPPED:
888 if (m_bSlideShow && m_bPlayingVideo)
890 m_bSlideShow = false;
891 g_windowManager.PreviousWindow();
896 return CGUIWindow::OnMessage(message);
899 void CGUIWindowSlideShow::RenderPause()
900 { // display the pause icon
903 SET_CONTROL_VISIBLE(CONTROL_PAUSE);
907 SET_CONTROL_HIDDEN(CONTROL_PAUSE);
910 static DWORD dwCounter=0;
916 if (!m_bPause) return;
917 if (dwCounter <13) return;*/
921 void CGUIWindowSlideShow::Rotate(float fAngle, bool immediate /* = false */)
923 if (m_Image[m_iCurrentPic].DrawNextImage())
928 m_Image[m_iCurrentPic].Rotate(fAngle, immediate);
931 void CGUIWindowSlideShow::Zoom(int iZoom)
933 if (iZoom > MAX_ZOOM_FACTOR || iZoom < 1)
936 ZoomRelative(zoomamount[iZoom - 1]);
939 void CGUIWindowSlideShow::ZoomRelative(float fZoom, bool immediate /* = false */)
941 if (fZoom < zoomamount[0])
942 fZoom = zoomamount[0];
943 else if (fZoom > zoomamount[MAX_ZOOM_FACTOR - 1])
944 fZoom = zoomamount[MAX_ZOOM_FACTOR - 1];
946 if (m_Image[m_iCurrentPic].DrawNextImage())
951 // find the nearest zoom factor
952 #ifdef RELOAD_ON_ZOOM
953 int iOldZoomFactor = m_iZoomFactor;
955 for (unsigned int i = 1; i < MAX_ZOOM_FACTOR; i++)
957 if (m_fZoom > zoomamount[i])
960 if (fabs(m_fZoom - zoomamount[i - 1]) < fabs(m_fZoom - zoomamount[i]))
963 m_iZoomFactor = i + 1;
968 // set the zoom amount and then set so that the image is reloaded at the higher (or lower)
969 // resolution as necessary
970 m_Image[m_iCurrentPic].Zoom(m_fZoom, immediate);
972 #ifdef RELOAD_ON_ZOOM
973 if (m_iZoomFactor == 1 || (iZoomFactor > iOldZoomFactor && !m_Image[m_iCurrentPic].FullSize()))
974 m_bReloadImage = true;
978 void CGUIWindowSlideShow::Move(float fX, float fY)
980 if (m_Image[m_iCurrentPic].IsLoaded() && m_Image[m_iCurrentPic].GetZoom() > 1)
981 { // we move in the opposite direction, due to the fact we are moving
982 // the viewing window, not the picture.
983 m_Image[m_iCurrentPic].Move( -fX, -fY);
987 void CGUIWindowSlideShow::OnLoadPic(int iPic, int iSlideNumber, CBaseTexture* pTexture, bool bFullSize)
991 // set the pic's texture + size etc.
992 CSingleLock lock(m_slideSection);
993 if (iSlideNumber >= m_slides->Size())
994 { // throw this away - we must have cleared the slideshow while we were still loading
998 CLog::Log(LOGDEBUG, "Finished background loading %s", m_slides->Get(iSlideNumber)->GetPath().c_str());
1001 if (m_Image[m_iCurrentPic].IsLoaded() && m_Image[m_iCurrentPic].SlideNumber() != iSlideNumber)
1002 { // wrong image (ie we finished loading the next image, not the current image)
1006 m_Image[m_iCurrentPic].UpdateTexture(pTexture);
1007 m_Image[m_iCurrentPic].SetOriginalSize(pTexture->GetOriginalWidth(), pTexture->GetOriginalHeight(), bFullSize);
1008 m_bReloadImage = false;
1013 m_Image[iPic].SetTexture(iSlideNumber, pTexture, g_guiSettings.GetBool("slideshow.displayeffects") ? CSlideShowPic::EFFECT_RANDOM : CSlideShowPic::EFFECT_NONE);
1015 m_Image[iPic].SetTexture(iSlideNumber, pTexture, CSlideShowPic::EFFECT_NO_TIMEOUT);
1016 m_Image[iPic].SetOriginalSize(pTexture->GetOriginalWidth(), pTexture->GetOriginalHeight(), bFullSize);
1018 m_Image[iPic].m_bIsComic = false;
1019 if (URIUtils::IsInRAR(m_slides->Get(m_iCurrentSlide)->GetPath()) || URIUtils::IsInZIP(m_slides->Get(m_iCurrentSlide)->GetPath())) // move to top for cbr/cbz
1021 CURL url(m_slides->Get(m_iCurrentSlide)->GetPath());
1022 CStdString strHostName = url.GetHostName();
1023 if (URIUtils::GetExtension(strHostName).Equals(".cbr", false) || URIUtils::GetExtension(strHostName).Equals(".cbz", false))
1025 m_Image[iPic].m_bIsComic = true;
1026 m_Image[iPic].Move((float)m_Image[iPic].GetOriginalWidth(),(float)m_Image[iPic].GetOriginalHeight());
1032 { // Failed to load image. What should be done??
1033 // We should wait for the current pic to finish rendering, then transistion it out,
1034 // release the texture, and try and reload this pic from scratch
1035 m_bErrorMessage = true;
1039 void CGUIWindowSlideShow::Shuffle()
1041 m_slides->Randomize();
1042 m_iCurrentSlide = 0;
1046 AnnouncePropertyChanged("shuffled", true);
1049 int CGUIWindowSlideShow::NumSlides() const
1051 return m_slides->Size();
1054 int CGUIWindowSlideShow::CurrentSlide() const
1056 return m_iCurrentSlide + 1;
1059 void CGUIWindowSlideShow::AddFromPath(const CStdString &strPath,
1061 SORT_METHOD method, SortOrder order, const CStdString &strExtensions)
1065 // reset the slideshow
1067 m_strExtensions = strExtensions;
1070 path_set recursivePaths;
1071 AddItems(strPath, &recursivePaths, method, order);
1074 AddItems(strPath, NULL, method, order);
1078 void CGUIWindowSlideShow::RunSlideShow(const CStdString &strPath,
1079 bool bRecursive /* = false */, bool bRandom /* = false */,
1080 bool bNotRandom /* = false */, SORT_METHOD method /* = SORT_METHOD_LABEL */,
1081 SortOrder order /* = SortOrderAscending */, const CStdString &strExtensions)
1084 if (g_application.IsPlayingVideo())
1085 g_application.StopPlaying();
1087 AddFromPath(strPath, bRecursive, method, order, strExtensions);
1089 // mutually exclusive options
1090 // if both are set, clear both and use the gui setting
1091 if (bRandom && bNotRandom)
1092 bRandom = bNotRandom = false;
1094 // NotRandom overrides the window setting
1095 if ((!bNotRandom && g_guiSettings.GetBool("slideshow.shuffle")) || bRandom)
1100 g_windowManager.ActivateWindow(WINDOW_SLIDESHOW);
1103 void CGUIWindowSlideShow::AddItems(const CStdString &strPath, path_set *recursivePaths, SORT_METHOD method, SortOrder order)
1105 // check whether we've already added this path
1108 CStdString path(strPath);
1109 URIUtils::RemoveSlashAtEnd(path);
1110 if (recursivePaths->find(path) != recursivePaths->end())
1112 recursivePaths->insert(path);
1115 // fetch directory and sort accordingly
1116 CFileItemList items;
1117 if (!CDirectory::GetDirectory(strPath, items, m_strExtensions.IsEmpty()?g_settings.m_pictureExtensions:m_strExtensions,DIR_FLAG_NO_FILE_DIRS,true))
1120 items.Sort(method, order);
1122 // need to go into all subdirs
1123 for (int i = 0; i < items.Size(); i++)
1125 CFileItemPtr item = items[i];
1126 if (item->m_bIsFolder && recursivePaths)
1128 AddItems(item->GetPath(), recursivePaths);
1130 else if (!item->m_bIsFolder && !URIUtils::IsRAR(item->GetPath()) && !URIUtils::IsZIP(item->GetPath()))
1131 { // add to the slideshow
1137 void CGUIWindowSlideShow::GetCheckedSize(float width, float height, int &maxWidth, int &maxHeight)
1139 #ifdef RELOAD_ON_ZOOM
1140 if (width * height > MAX_PICTURE_SIZE)
1142 float fScale = sqrt((float)MAX_PICTURE_SIZE / (width * height));
1143 width = fScale * width;
1144 height = fScale * height;
1146 maxWidth = (int)width;
1147 maxHeight = (int)height;
1148 if (maxWidth > (int)g_Windowing.GetMaxTextureSize())
1149 maxWidth = g_Windowing.GetMaxTextureSize();
1150 if (maxHeight > (int)g_Windowing.GetMaxTextureSize())
1151 maxHeight = g_Windowing.GetMaxTextureSize();
1153 maxWidth = g_Windowing.GetMaxTextureSize();
1154 maxHeight = g_Windowing.GetMaxTextureSize();