44cbe88d9b555138a07c124f5ba74ec537b28392
[vuplus_xbmc] / xbmc / pictures / GUIWindowSlideShow.cpp
1 /*
2  *      Copyright (C) 2005-2012 Team XBMC
3  *      http://www.xbmc.org
4  *
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)
8  *  any later version.
9  *
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.
14  *
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/>.
18  *
19  */
20
21 #include "threads/SystemClock.h"
22 #include "system.h"
23 #include "GUIWindowSlideShow.h"
24 #include "Application.h"
25 #include "ApplicationMessenger.h"
26 #include "utils/URIUtils.h"
27 #include "URL.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"
37 #include "FileItem.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"
47
48 using namespace XFILE;
49
50 #define MAX_ZOOM_FACTOR                     10
51 #define MAX_PICTURE_SIZE             2048*2048
52
53 #define IMMEDIATE_TRANSISTION_TIME          20
54
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
60
61 #define ROTATION_SNAP_RANGE              10.0f
62
63 #define FPS                                 25
64
65 #define BAR_IMAGE                            1
66 #define LABEL_ROW1                          10
67 #define LABEL_ROW2                          11
68 #define LABEL_ROW2_EXTRA                    12
69 #define CONTROL_PAUSE                       13
70
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 };
72
73 CBackgroundPicLoader::CBackgroundPicLoader() : CThread("CBackgroundPicLoader")
74 {
75   m_pCallback = NULL;
76   m_isLoading = false;
77 }
78
79 CBackgroundPicLoader::~CBackgroundPicLoader()
80 {
81   StopThread();
82 }
83
84 void CBackgroundPicLoader::Create(CGUIWindowSlideShow *pCallback)
85 {
86   m_pCallback = pCallback;
87   m_isLoading = false;
88   CThread::Create(false);
89 }
90
91 void CBackgroundPicLoader::Process()
92 {
93   unsigned int totalTime = 0;
94   unsigned int count = 0;
95   while (!m_bStop)
96   { // loop around forever, waiting for the app to call LoadPic
97     if (AbortableWait(m_loadPic,10) == WAIT_SIGNALED)
98     {
99       if (m_pCallback)
100       {
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;
104         count++;
105         // tell our parent
106         bool bFullSize = false;
107         if (texture)
108         {
109           bFullSize = ((int)texture->GetWidth() < m_maxWidth) && ((int)texture->GetHeight() < m_maxHeight);
110           if (!bFullSize)
111           {
112             int iSize = texture->GetWidth() * texture->GetHeight() - MAX_PICTURE_SIZE;
113             if ((iSize + (int)texture->GetWidth() > 0) || (iSize + (int)texture->GetHeight() > 0))
114               bFullSize = true;
115             if (!bFullSize && texture->GetWidth() == g_Windowing.GetMaxTextureSize())
116               bFullSize = true;
117             if (!bFullSize && texture->GetHeight() == g_Windowing.GetMaxTextureSize())
118               bFullSize = true;
119           }
120         }
121         m_pCallback->OnLoadPic(m_iPic, m_iSlideNumber, texture, bFullSize);
122         m_isLoading = false;
123       }
124     }
125   }
126   if (count > 0)
127     CLog::Log(LOGDEBUG, "Time for loading %u images: %u ms, average %u ms",
128               count, totalTime, totalTime / count);
129 }
130
131 void CBackgroundPicLoader::LoadPic(int iPic, int iSlideNumber, const CStdString &strFileName, const int maxWidth, const int maxHeight)
132 {
133   m_iPic = iPic;
134   m_iSlideNumber = iSlideNumber;
135   m_strFileName = strFileName;
136   m_maxWidth = maxWidth;
137   m_maxHeight = maxHeight;
138   m_isLoading = true;
139   m_loadPic.Set();
140 }
141
142 CGUIWindowSlideShow::CGUIWindowSlideShow(void)
143     : CGUIWindow(WINDOW_SLIDESHOW, "SlideShow.xml")
144 {
145   m_pBackgroundLoader = NULL;
146   m_slides = new CFileItemList;
147   m_Resolution = RES_INVALID;
148   m_loadType = KEEP_IN_MEMORY;
149   Reset();
150 }
151
152 CGUIWindowSlideShow::~CGUIWindowSlideShow(void)
153 {
154   Reset();
155   delete m_slides;
156 }
157
158 void CGUIWindowSlideShow::AnnouncePlayerPlay(const CFileItemPtr& item)
159 {
160   CVariant param;
161   param["player"]["speed"] = 1;
162   param["player"]["playerid"] = PLAYLIST_PICTURE;
163   ANNOUNCEMENT::CAnnouncementManager::Announce(ANNOUNCEMENT::Player, "xbmc", "OnPlay", item, param);
164 }
165
166 void CGUIWindowSlideShow::AnnouncePlayerPause(const CFileItemPtr& item)
167 {
168   CVariant param;
169   param["player"]["speed"] = 0;
170   param["player"]["playerid"] = PLAYLIST_PICTURE;
171   ANNOUNCEMENT::CAnnouncementManager::Announce(ANNOUNCEMENT::Player, "xbmc", "OnPause", item, param);
172 }
173
174 void CGUIWindowSlideShow::AnnouncePlayerStop(const CFileItemPtr& item)
175 {
176   CVariant param;
177   param["player"]["playerid"] = PLAYLIST_PICTURE;
178   param["end"] = true;
179   ANNOUNCEMENT::CAnnouncementManager::Announce(ANNOUNCEMENT::Player, "xbmc", "OnStop", item, param);
180 }
181
182 void CGUIWindowSlideShow::AnnouncePlaylistRemove(int pos)
183 {
184   CVariant data;
185   data["playlistid"] = PLAYLIST_PICTURE;
186   data["position"] = pos;
187   ANNOUNCEMENT::CAnnouncementManager::Announce(ANNOUNCEMENT::Playlist, "xbmc", "OnRemove", data);
188 }
189
190 void CGUIWindowSlideShow::AnnouncePlaylistClear()
191 {
192   CVariant data;
193   data["playlistid"] = PLAYLIST_PICTURE;
194   ANNOUNCEMENT::CAnnouncementManager::Announce(ANNOUNCEMENT::Playlist, "xbmc", "OnClear", data);
195 }
196
197 void CGUIWindowSlideShow::AnnouncePlaylistAdd(const CFileItemPtr& item, int pos)
198 {
199   CVariant data;
200   data["playlistid"] = PLAYLIST_PICTURE;
201   data["position"] = pos;
202   ANNOUNCEMENT::CAnnouncementManager::Announce(ANNOUNCEMENT::Playlist, "xbmc", "OnAdd", item, data);
203 }
204
205 void CGUIWindowSlideShow::AnnouncePropertyChanged(const std::string &strProperty, const CVariant &value)
206 {
207   if (strProperty.empty() || value.isNull())
208     return;
209
210   CVariant data;
211   data["player"]["playerid"] = PLAYLIST_PICTURE;
212   data["property"][strProperty] = value;
213   ANNOUNCEMENT::CAnnouncementManager::Announce(ANNOUNCEMENT::Player, "xbmc", "OnPropertyChanged", data);
214 }
215
216 bool CGUIWindowSlideShow::IsPlaying() const
217 {
218   return m_Image[m_iCurrentPic].IsLoaded();
219 }
220
221 void CGUIWindowSlideShow::Reset()
222 {
223   g_infoManager.SetShowCodec(false);
224   m_bSlideShow = false;
225   m_bShuffled = false;
226   m_bPause = false;
227   m_bPlayingVideo = false;
228   m_bErrorMessage = false;
229   m_bReloadImage = false;
230   m_bScreensaver = false;
231   m_Image[0].UnLoad();
232   m_Image[0].Close();
233   m_Image[1].UnLoad();
234   m_Image[1].Close();
235
236   m_fRotate = 0.0f;
237   m_fInitialRotate = 0.0f;
238   m_iZoomFactor = 1;
239   m_fZoom = 1.0f;
240   m_fInitialZoom = 0.0f;
241   m_iCurrentSlide = 0;
242   m_iNextSlide = 1;
243   m_iCurrentPic = 0;
244   m_iDirection = 1;
245   CSingleLock lock(m_slideSection);
246   m_slides->Clear();
247   AnnouncePlaylistClear();
248   m_Resolution = g_graphicsContext.GetVideoResolution();
249 }
250
251 void CGUIWindowSlideShow::OnDeinitWindow(int nextWindowID)
252
253   if (m_Resolution != g_guiSettings.m_LookAndFeelResolution)
254   {
255     //FIXME: Use GUI resolution for now
256     //g_graphicsContext.SetVideoResolution(g_guiSettings.m_LookAndFeelResolution, TRUE);
257   }
258
259   //   Reset();
260   if (nextWindowID != WINDOW_PICTURES)
261     m_ImageLib.Unload();
262
263   g_windowManager.ShowOverlay(OVERLAY_STATE_SHOWN);
264
265   // wait for any outstanding picture loads
266   if (m_pBackgroundLoader)
267   {
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())
271       Sleep(10);
272     // stop the thread
273     CLog::Log(LOGDEBUG,"Stopping BackgroundLoader thread");
274     m_pBackgroundLoader->StopThread();
275     delete m_pBackgroundLoader;
276     m_pBackgroundLoader = NULL;
277   }
278   // and close the images.
279   m_Image[0].Close();
280   m_Image[1].Close();
281   g_infoManager.ResetCurrentSlide();
282
283   CGUIWindow::OnDeinitWindow(nextWindowID);
284 }
285
286 void CGUIWindowSlideShow::Add(const CFileItem *picture)
287 {
288   CFileItemPtr item(new CFileItem(*picture));
289   if (!item->HasVideoInfoTag() && !item->HasPictureInfoTag())
290   {
291     // item without tag; assume it is a picture and force tag generation
292     item->GetPictureInfoTag();
293   }
294   AnnouncePlaylistAdd(item, m_slides->Size());
295
296   m_slides->Add(item);
297 }
298
299 void CGUIWindowSlideShow::ShowNext()
300 {
301   if (m_slides->Size() == 1)
302     return;
303
304   m_iNextSlide = m_iCurrentSlide + 1;
305   if (m_iNextSlide >= m_slides->Size())
306     m_iNextSlide = 0;
307
308   m_iDirection   = 1;
309   m_iZoomFactor  = 1;
310   m_fZoom        = 1.0f;
311   m_fRotate      = 0.0f;
312   m_bLoadNextPic = true;
313 }
314
315 void CGUIWindowSlideShow::ShowPrevious()
316 {
317   if (m_slides->Size() == 1)
318     return;
319
320   m_iNextSlide = m_iCurrentSlide - 1;
321   if (m_iNextSlide < 0)
322     m_iNextSlide = m_slides->Size() - 1;
323
324   m_iDirection   = -1;
325   m_iZoomFactor  = 1;
326   m_fZoom        = 1.0f;
327   m_fRotate      = 0.0f;
328   m_bLoadNextPic = true;
329 }
330
331
332 void CGUIWindowSlideShow::Select(const CStdString& strPicture)
333 {
334   for (int i = 0; i < m_slides->Size(); ++i)
335   {
336     const CFileItemPtr item = m_slides->Get(i);
337     if (item->GetPath() == strPicture)
338     {
339       m_iDirection = 1;
340       if (IsActive())
341         m_iNextSlide = i;
342       else
343       {
344         m_iCurrentSlide = i;
345         m_iNextSlide = GetNextSlide();
346       }
347       m_bLoadNextPic = true;
348       return ;
349     }
350   }
351 }
352
353 const CFileItemList &CGUIWindowSlideShow::GetSlideShowContents()
354 {
355   return *m_slides;
356 }
357
358 void CGUIWindowSlideShow::GetSlideShowContents(CFileItemList &list)
359 {
360   for (int index = 0; index < m_slides->Size(); index++)
361     list.Add(CFileItemPtr(new CFileItem(*m_slides->Get(index))));
362 }
363
364 const CFileItemPtr CGUIWindowSlideShow::GetCurrentSlide()
365 {
366   if (m_iCurrentSlide >= 0 && m_iCurrentSlide < m_slides->Size())
367     return m_slides->Get(m_iCurrentSlide);
368   return CFileItemPtr();
369 }
370
371 bool CGUIWindowSlideShow::InSlideShow() const
372 {
373   return m_bSlideShow;
374 }
375
376 void CGUIWindowSlideShow::StartSlideShow(bool screensaver)
377 {
378   m_bSlideShow = true;
379   m_iDirection = 1;
380   m_bScreensaver = screensaver;
381 }
382
383 void CGUIWindowSlideShow::Process(unsigned int currentTime, CDirtyRegionList &regions)
384 {
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 ;
391
392   // if we haven't rendered yet, we should mark the whole screen
393   if (!m_hasRendered)
394     regions.push_back(CRect(0.0f, 0.0f, (float)g_graphicsContext.GetWidth(), (float)g_graphicsContext.GetHeight()));
395
396   if (m_iNextSlide < 0 || m_iNextSlide >= m_slides->Size())
397     m_iNextSlide = 0;
398   if (m_iCurrentSlide < 0 || m_iCurrentSlide >= m_slides->Size())
399     m_iCurrentSlide = 0;
400
401   // Create our background loader if necessary
402   if (!m_pBackgroundLoader)
403   {
404     m_pBackgroundLoader = new CBackgroundPicLoader();
405
406     if (!m_pBackgroundLoader)
407     {
408       throw 1;
409     }
410     m_pBackgroundLoader->Create(this);
411   }
412
413   bool bSlideShow = m_bSlideShow && !m_bPause && !m_bPlayingVideo;
414
415   if (m_bErrorMessage)
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());
422       if (!bSlideShow)
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
426       }
427       m_bWaitForNextPic = true;
428       m_bErrorMessage = false;
429     }
430     else
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
433       if (bSlideShow)
434       {
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();
438         ShowNext();
439         m_bErrorMessage = false;
440       }
441       else if (m_bLoadNextPic)
442       {
443         m_iCurrentSlide = m_iNextSlide;
444         m_iNextSlide    = GetNextSlide();
445         m_bErrorMessage = false;
446       }
447       // else just drop through - there's nothing we can do (error message will be displayed)
448     }
449   }
450
451   if (m_bErrorMessage)
452   { // hack, just mark it all
453     regions.push_back(CRect(0.0f, 0.0f, (float)g_graphicsContext.GetWidth(), (float)g_graphicsContext.GetHeight()));
454     return;
455   }
456
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);
469   }
470
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();
474
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();
478
479   if (m_bReloadImage)
480   {
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);
487
488       // the actual maximal size of the image to optimize the sizing based on the known sizing (aspect ratio)
489       int width, height;
490       GetCheckedSize((float)m_Image[m_iCurrentPic].GetOriginalWidth(), (float)m_Image[m_iCurrentPic].GetOriginalHeight(), width, height);
491
492       // use the smaller of the two (no point zooming in more than we have to)
493       if (maxWidth < width)
494         width = maxWidth;
495       if (maxHeight < height)
496         height = maxHeight;
497
498       m_pBackgroundLoader->LoadPic(m_iCurrentPic, m_iCurrentSlide, m_slides->Get(m_iCurrentSlide)->GetPath(), width, height);
499     }
500   }
501   else
502   {
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);
512     }
513   }
514
515   // render the current image
516   if (m_Image[m_iCurrentPic].IsLoaded())
517   {
518     m_Image[m_iCurrentPic].SetInSlideshow(m_bSlideShow);
519     m_Image[m_iCurrentPic].Pause(m_bPause);
520     m_Image[m_iCurrentPic].Process(currentTime, regions);
521   }
522
523   if (m_slides->Get(m_iCurrentSlide)->IsVideo() && bSlideShow)
524   { 
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();
530   }
531
532   // Check if we should be transistioning immediately
533   if (m_bLoadNextPic)
534   {
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())
537     {
538       m_Image[m_iCurrentPic].SetTransistionTime(1, IMMEDIATE_TRANSISTION_TIME); // only 20 frames for the transistion
539       m_bLoadNextPic = false;
540     }
541   }
542
543   // render the next image
544   if (m_Image[m_iCurrentPic].DrawNextImage())
545   {
546     if (m_Image[1 - m_iCurrentPic].IsLoaded())
547     {
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);
552     }
553     else // next pic isn't loaded.  We should hang around if it is in progress
554     {
555       if (m_pBackgroundLoader->IsLoading())
556       {
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();
559       }
560     }
561   }
562
563   // check if we should swap images now
564   if (m_Image[m_iCurrentPic].IsFinished())
565   {
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;
570
571     m_iCurrentSlide = m_iNextSlide;
572     m_iNextSlide    = GetNextSlide();
573     AnnouncePlayerPlay(m_slides->Get(m_iCurrentSlide));
574
575     m_iZoomFactor = 1;
576     m_fZoom = 1.0f;
577     m_fRotate = 0.0f;
578   }
579
580   if (m_Image[m_iCurrentPic].IsLoaded())
581     g_infoManager.SetCurrentSlide(*m_slides->Get(m_iCurrentSlide));
582
583   RenderPause();
584   CGUIWindow::Process(currentTime, regions);
585 }
586
587 void CGUIWindowSlideShow::Render()
588 {
589   if (m_Image[m_iCurrentPic].IsLoaded())
590     m_Image[m_iCurrentPic].Render();
591
592   if (m_Image[m_iCurrentPic].DrawNextImage() && m_Image[1 - m_iCurrentPic].IsLoaded())
593     m_Image[1 - m_iCurrentPic].Render();
594
595   RenderErrorMessage();
596   CGUIWindow::Render();
597 }
598
599 int CGUIWindowSlideShow::GetNextSlide()
600 {
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();
605
606   return (m_iCurrentSlide - 1 + m_slides->Size()) % m_slides->Size();
607 }
608
609 EVENT_RESULT CGUIWindowSlideShow::OnMouseEvent(const CPoint &point, const CMouseEvent &event)
610 {
611   if (event.m_id == ACTION_GESTURE_NOTIFY)
612   {
613     if (m_iZoomFactor == 1) //zoomed out - no inertial scrolling
614       return EVENT_RESULT_PAN_HORIZONTAL_WITHOUT_INERTIA;
615
616     return EVENT_RESULT_PAN_HORIZONTAL;
617   }  
618   else if (event.m_id == ACTION_GESTURE_BEGIN)
619   {
620     m_firstGesturePoint = point;
621     m_fInitialZoom = m_fZoom;
622     m_fInitialRotate = m_fRotate;
623     return EVENT_RESULT_HANDLED;
624   }
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)
628     {
629       if (m_firstGesturePoint.x > 0 && fabs(point.x - m_firstGesturePoint.x) > 100)
630       {
631         if (point.x < m_firstGesturePoint.x)
632           OnAction(CAction(ACTION_NEXT_PICTURE));
633         else 
634           OnAction(CAction(ACTION_PREV_PICTURE));
635
636         m_firstGesturePoint.x = 0;
637       }
638     }
639     else //zoomed in - free move mode
640     {
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;
643     }
644     return EVENT_RESULT_HANDLED;
645   }
646   else if (event.m_id == ACTION_GESTURE_END)
647   {
648     if (m_fRotate != 0.0f)
649     {
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)
654         Rotate(-reminder);
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);
659     }
660
661     m_fInitialZoom = 0.0f;
662     m_fInitialRotate = 0.0f;
663     return EVENT_RESULT_HANDLED;
664   }
665   else if (event.m_id == ACTION_GESTURE_ZOOM)
666   {
667     ZoomRelative(m_fInitialZoom * event.m_offsetX, true);
668     return EVENT_RESULT_HANDLED;
669   }
670   else if (event.m_id == ACTION_GESTURE_ROTATE)
671   {
672     Rotate(m_fInitialRotate + event.m_offsetX - m_fRotate, true);
673     return EVENT_RESULT_HANDLED;
674   }
675   return EVENT_RESULT_UNHANDLED;
676 }
677
678 bool CGUIWindowSlideShow::OnAction(const CAction &action)
679 {
680   if (m_bScreensaver)
681   {
682     g_windowManager.PreviousWindow();
683     return true;
684   }
685
686   switch (action.GetID())
687   {
688   case ACTION_SHOW_CODEC:
689     {
690       CGUIDialogPictureInfo *pictureInfo = (CGUIDialogPictureInfo *)g_windowManager.GetWindow(WINDOW_DIALOG_PICTURE_INFO);
691       if (pictureInfo)
692       {
693         // no need to set the picture here, it's done in Render()
694         pictureInfo->DoModal();
695       }
696     }
697     break;
698
699   case ACTION_PREVIOUS_MENU:
700   case ACTION_NAV_BACK:
701   case ACTION_STOP:
702     if (m_bSlideShow && m_slides->Size())
703       AnnouncePlayerStop(m_slides->Get(m_iCurrentSlide));
704     g_windowManager.PreviousWindow();
705     break;
706
707   case ACTION_NEXT_PICTURE:
708       ShowNext();
709     break;
710
711   case ACTION_PREV_PICTURE:
712       ShowPrevious();
713     break;
714
715   case ACTION_MOVE_RIGHT:
716     if (m_iZoomFactor == 1 || !m_Image[m_iCurrentPic].m_bCanMoveHorizontally)
717       ShowNext();
718     else
719       Move(PICTURE_MOVE_AMOUNT, 0);
720     break;
721
722   case ACTION_MOVE_LEFT:
723     if (m_iZoomFactor == 1 || !m_Image[m_iCurrentPic].m_bCanMoveHorizontally)
724       ShowPrevious();
725     else
726       Move( -PICTURE_MOVE_AMOUNT, 0);
727     break;
728
729   case ACTION_MOVE_DOWN:
730     Move(0, PICTURE_MOVE_AMOUNT);
731     break;
732
733   case ACTION_MOVE_UP:
734     Move(0, -PICTURE_MOVE_AMOUNT);
735     break;
736
737   case ACTION_PAUSE:
738     if (m_bSlideShow)
739     {
740       m_bPause = !m_bPause;
741       if (m_slides->Size())
742       {
743         if (m_bPause)
744           AnnouncePlayerPause(m_slides->Get(m_iCurrentSlide));
745         else
746           AnnouncePlayerPlay(m_slides->Get(m_iCurrentSlide));
747       }
748     }
749     break;
750
751   case ACTION_PLAYER_PLAY:
752     if (!m_bSlideShow)
753     {
754       m_bSlideShow = true;
755       m_bPause = false;
756     }
757     else if (m_bPause)
758     {
759       m_bPause = false;
760       if (m_slides->Size())
761         AnnouncePlayerPlay(m_slides->Get(m_iCurrentSlide));
762     }
763     break;
764
765   case ACTION_ZOOM_OUT:
766     Zoom(m_iZoomFactor - 1);
767     break;
768
769   case ACTION_ZOOM_IN:
770     Zoom(m_iZoomFactor + 1);
771     break;
772
773   case ACTION_ROTATE_PICTURE_CW:
774     Rotate(90.0f);
775     break;
776
777   case ACTION_ROTATE_PICTURE_CCW:
778     Rotate(-90.0f);
779     break;
780
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);
792     break;
793
794   case ACTION_ANALOG_MOVE:
795     Move(action.GetAmount()*PICTURE_MOVE_AMOUNT_ANALOG, -action.GetAmount(1)*PICTURE_MOVE_AMOUNT_ANALOG);
796     break;
797
798   default:
799     return CGUIWindow::OnAction(action);
800   }
801   return true;
802 }
803
804 void CGUIWindowSlideShow::RenderErrorMessage()
805 {
806   if (!m_bErrorMessage)
807     return ;
808
809   const CGUIControl *control = GetControl(LABEL_ROW1);
810   if (NULL == control || control->GetControlType() != CGUIControl::GUICONTROL_LABEL)
811   {
812      CLog::Log(LOGERROR,"CGUIWindowSlideShow::RenderErrorMessage - cant get label control!");
813      return;
814   }
815
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);
818 }
819
820 bool CGUIWindowSlideShow::OnMessage(CGUIMessage& message)
821 {
822   switch ( message.GetMessage() )
823   {
824   case GUI_MSG_WINDOW_INIT:
825     {
826       m_Resolution = (RESOLUTION) g_guiSettings.GetInt("pictures.displayresolution");
827
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);
831       else
832         m_Resolution = g_graphicsContext.GetVideoResolution();
833
834       CGUIWindow::OnMessage(message);
835       if (message.GetParam1() != WINDOW_PICTURES)
836         m_ImageLib.Load();
837
838       g_windowManager.ShowOverlay(OVERLAY_STATE_HIDDEN);
839
840       // turn off slideshow if we only have 1 image
841       if (m_slides->Size() <= 1)
842         m_bSlideShow = false;
843
844       return true;
845     }
846     break;
847
848   case GUI_MSG_START_SLIDESHOW:
849     {
850       CStdString strFolder = message.GetStringParam();
851       unsigned int iParams = message.GetParam1();
852       //decode params
853       bool bRecursive = false;
854       bool bRandom = false;
855       bool bNotRandom = false;
856       if (iParams > 0)
857       {
858         if ((iParams & 1) == 1)
859           bRecursive = true;
860         if ((iParams & 2) == 2)
861           bRandom = true;
862         if ((iParams & 4) == 4)
863           bNotRandom = true;
864       }
865       RunSlideShow(strFolder, bRecursive, bRandom, bNotRandom);
866     }
867     break;
868
869     case GUI_MSG_PLAYLISTPLAYER_STOPPED:
870       {
871         m_bPlayingVideo = false;
872         if (m_bSlideShow)
873           g_windowManager.ActivateWindow(WINDOW_SLIDESHOW);
874       }
875       break;
876
877     case GUI_MSG_PLAYBACK_STARTED:
878       {
879         if (m_bSlideShow && m_bPlayingVideo)
880           g_windowManager.ActivateWindow(WINDOW_FULLSCREEN_VIDEO);
881       }
882       break;
883
884     case GUI_MSG_PLAYBACK_STOPPED:
885       {
886         if (m_bSlideShow && m_bPlayingVideo)
887         {
888           m_bSlideShow = false;
889           g_windowManager.PreviousWindow();
890         }
891       }
892       break;
893   }
894   return CGUIWindow::OnMessage(message);
895 }
896
897 void CGUIWindowSlideShow::RenderPause()
898 { // display the pause icon
899   if (m_bPause)
900   {
901     SET_CONTROL_VISIBLE(CONTROL_PAUSE);
902   }
903   else
904   {
905     SET_CONTROL_HIDDEN(CONTROL_PAUSE);
906   }
907   /*
908    static DWORD dwCounter=0;
909    dwCounter++;
910    if (dwCounter > 25)
911    {
912     dwCounter=0;
913    }
914    if (!m_bPause) return;
915    if (dwCounter <13) return;*/
916
917 }
918
919 void CGUIWindowSlideShow::Rotate(float fAngle, bool immediate /* = false */)
920 {
921   if (m_Image[m_iCurrentPic].DrawNextImage())
922     return;
923
924   m_fRotate += fAngle;
925
926   m_Image[m_iCurrentPic].Rotate(fAngle, immediate);
927 }
928
929 void CGUIWindowSlideShow::Zoom(int iZoom)
930 {
931   if (iZoom > MAX_ZOOM_FACTOR || iZoom < 1)
932     return;
933
934   ZoomRelative(zoomamount[iZoom - 1]);
935 }
936
937 void CGUIWindowSlideShow::ZoomRelative(float fZoom, bool immediate /* = false */)
938 {
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];
943
944   if (m_Image[m_iCurrentPic].DrawNextImage())
945     return;
946
947   m_fZoom = fZoom;
948
949   // find the nearest zoom factor
950 #ifdef RELOAD_ON_ZOOM
951   int iOldZoomFactor = m_iZoomFactor;
952 #endif
953   for (unsigned int i = 1; i < MAX_ZOOM_FACTOR; i++)
954   {
955     if (m_fZoom > zoomamount[i])
956       continue;
957
958     if (fabs(m_fZoom - zoomamount[i - 1]) < fabs(m_fZoom - zoomamount[i]))
959       m_iZoomFactor = i;
960     else
961       m_iZoomFactor = i + 1;
962
963     break;
964   }
965
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);
969
970 #ifdef RELOAD_ON_ZOOM
971   if (m_iZoomFactor == 1 || (iZoomFactor > iOldZoomFactor && !m_Image[m_iCurrentPic].FullSize()))
972     m_bReloadImage = true;
973 #endif
974 }
975
976 void CGUIWindowSlideShow::Move(float fX, float fY)
977 {
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);
982   }
983 }
984
985 void CGUIWindowSlideShow::OnLoadPic(int iPic, int iSlideNumber, CBaseTexture* pTexture, bool bFullSize)
986 {
987   if (pTexture)
988   {
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
993       delete pTexture;
994       return;
995     }
996     CLog::Log(LOGDEBUG, "Finished background loading %s", m_slides->Get(iSlideNumber)->GetPath().c_str());
997     if (m_bReloadImage)
998     {
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)
1001         delete pTexture;
1002         return;
1003       }
1004       m_Image[m_iCurrentPic].UpdateTexture(pTexture);
1005       m_Image[m_iCurrentPic].SetOriginalSize(pTexture->GetOriginalWidth(), pTexture->GetOriginalHeight(), bFullSize);
1006       m_bReloadImage = false;
1007     }
1008     else
1009     {
1010       if (m_bSlideShow)
1011         m_Image[iPic].SetTexture(iSlideNumber, pTexture, g_guiSettings.GetBool("slideshow.displayeffects") ? CSlideShowPic::EFFECT_RANDOM : CSlideShowPic::EFFECT_NONE);
1012       else
1013         m_Image[iPic].SetTexture(iSlideNumber, pTexture, CSlideShowPic::EFFECT_NO_TIMEOUT);
1014       m_Image[iPic].SetOriginalSize(pTexture->GetOriginalWidth(), pTexture->GetOriginalHeight(), bFullSize);
1015
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
1018       {
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))
1022         {
1023           m_Image[iPic].m_bIsComic = true;
1024           m_Image[iPic].Move((float)m_Image[iPic].GetOriginalWidth(),(float)m_Image[iPic].GetOriginalHeight());
1025         }
1026       }
1027     }
1028   }
1029   else
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;
1034   }
1035 }
1036
1037 void CGUIWindowSlideShow::Shuffle()
1038 {
1039   m_slides->Randomize();
1040   m_iCurrentSlide = 0;
1041   m_iNextSlide = 1;
1042   m_bShuffled = true;
1043
1044   AnnouncePropertyChanged("shuffled", true);
1045 }
1046
1047 int CGUIWindowSlideShow::NumSlides() const
1048 {
1049   return m_slides->Size();
1050 }
1051
1052 int CGUIWindowSlideShow::CurrentSlide() const
1053 {
1054   return m_iCurrentSlide + 1;
1055 }
1056
1057 void CGUIWindowSlideShow::AddFromPath(const CStdString &strPath,
1058                                       bool bRecursive, 
1059                                       SORT_METHOD method, SortOrder order, const CStdString &strExtensions)
1060 {
1061   if (strPath!="")
1062   {
1063     // reset the slideshow
1064     Reset();
1065     m_strExtensions = strExtensions;
1066     if (bRecursive)
1067     {
1068       path_set recursivePaths;
1069       AddItems(strPath, &recursivePaths, method, order);
1070     }
1071     else
1072       AddItems(strPath, NULL, method, order);
1073   }
1074 }
1075
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)
1080 {
1081   // stop any video
1082   if (g_application.IsPlayingVideo())
1083     g_application.StopPlaying();
1084
1085   AddFromPath(strPath, bRecursive, method, order, strExtensions);
1086
1087   // mutually exclusive options
1088   // if both are set, clear both and use the gui setting
1089   if (bRandom && bNotRandom)
1090     bRandom = bNotRandom = false;
1091
1092   // NotRandom overrides the window setting
1093   if ((!bNotRandom && g_guiSettings.GetBool("slideshow.shuffle")) || bRandom)
1094     Shuffle();
1095
1096   StartSlideShow();
1097   if (NumSlides())
1098     g_windowManager.ActivateWindow(WINDOW_SLIDESHOW);
1099 }
1100
1101 void CGUIWindowSlideShow::AddItems(const CStdString &strPath, path_set *recursivePaths, SORT_METHOD method, SortOrder order)
1102 {
1103   // check whether we've already added this path
1104   if (recursivePaths)
1105   {
1106     CStdString path(strPath);
1107     URIUtils::RemoveSlashAtEnd(path);
1108     if (recursivePaths->find(path) != recursivePaths->end())
1109       return;
1110     recursivePaths->insert(path);
1111   }
1112
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))
1116     return;
1117
1118   items.Sort(method, order);
1119
1120   // need to go into all subdirs
1121   for (int i = 0; i < items.Size(); i++)
1122   {
1123     CFileItemPtr item = items[i];
1124     if (item->m_bIsFolder && recursivePaths)
1125     {
1126       AddItems(item->GetPath(), recursivePaths);
1127     }
1128     else if (!item->m_bIsFolder && !URIUtils::IsRAR(item->GetPath()) && !URIUtils::IsZIP(item->GetPath()))
1129     { // add to the slideshow
1130       Add(item.get());
1131     }
1132   }
1133 }
1134
1135 void CGUIWindowSlideShow::GetCheckedSize(float width, float height, int &maxWidth, int &maxHeight)
1136 {
1137 #ifdef RELOAD_ON_ZOOM
1138   if (width * height > MAX_PICTURE_SIZE)
1139   {
1140     float fScale = sqrt((float)MAX_PICTURE_SIZE / (width * height));
1141     width = fScale * width;
1142     height = fScale * height;
1143   }
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();
1150 #else
1151   maxWidth = g_Windowing.GetMaxTextureSize();
1152   maxHeight = g_Windowing.GetMaxTextureSize();
1153 #endif
1154 }
1155
1156