Merge pull request #4687 from ruuk/textboxgettext
[vuplus_xbmc] / xbmc / guilib / GUIMultiImage.cpp
1 /*
2  *      Copyright (C) 2005-2013 Team XBMC
3  *      http://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 "GUIMultiImage.h"
22 #include "TextureManager.h"
23 #include "filesystem/Directory.h"
24 #include "utils/URIUtils.h"
25 #include "utils/JobManager.h"
26 #include "FileItem.h"
27 #include "settings/AdvancedSettings.h"
28 #include "Key.h"
29 #include "TextureCache.h"
30 #include "WindowIDs.h"
31 #include "utils/StringUtils.h"
32
33 using namespace std;
34 using namespace XFILE;
35
36 CGUIMultiImage::CGUIMultiImage(int parentID, int controlID, float posX, float posY, float width, float height, const CTextureInfo& texture, unsigned int timePerImage, unsigned int fadeTime, bool randomized, bool loop, unsigned int timeToPauseAtEnd)
37     : CGUIControl(parentID, controlID, posX, posY, width, height),
38       m_image(0, 0, posX, posY, width, height, texture)
39 {
40   m_currentImage = 0;
41   m_timePerImage = timePerImage + fadeTime;
42   m_timeToPauseAtEnd = timeToPauseAtEnd;
43   m_image.SetCrossFade(fadeTime);
44   m_randomized = randomized;
45   m_loop = loop;
46   ControlType = GUICONTROL_MULTI_IMAGE;
47   m_bDynamicResourceAlloc=false;
48   m_directoryStatus = UNLOADED;
49   m_jobID = 0;
50 }
51
52 CGUIMultiImage::CGUIMultiImage(const CGUIMultiImage &from)
53   : CGUIControl(from), m_texturePath(), m_imageTimer(), m_files(), m_image(from.m_image)
54 {
55   m_timePerImage = from.m_timePerImage;
56   m_timeToPauseAtEnd = from.m_timeToPauseAtEnd;
57   m_randomized = from.m_randomized;
58   m_loop = from.m_loop;
59   m_bDynamicResourceAlloc=false;
60   m_directoryStatus = UNLOADED;
61   if (m_texturePath.IsConstant())
62     m_currentPath = m_texturePath.GetLabel(WINDOW_INVALID);
63   m_currentImage = 0;
64   ControlType = GUICONTROL_MULTI_IMAGE;
65   m_jobID = 0;
66 }
67
68 CGUIMultiImage::~CGUIMultiImage(void)
69 {
70   CancelLoading();
71 }
72
73 void CGUIMultiImage::UpdateVisibility(const CGUIListItem *item)
74 {
75   CGUIControl::UpdateVisibility(item);
76
77   // check if we're hidden, and deallocate if so
78   if (!IsVisible() && m_visible != DELAYED)
79   {
80     if (m_bDynamicResourceAlloc && m_bAllocated)
81       FreeResources();
82     return;
83   }
84
85   // we are either delayed or visible, so we can allocate our resources
86   if (m_directoryStatus == UNLOADED)
87     LoadDirectory();
88
89   if (!m_bAllocated)
90     AllocResources();
91
92   if (m_directoryStatus == LOADED)
93     OnDirectoryLoaded();
94 }
95
96 void CGUIMultiImage::UpdateInfo(const CGUIListItem *item)
97 {
98   // check for conditional information before we
99   // alloc as this can free our resources
100   if (!m_texturePath.IsConstant())
101   {
102     CStdString texturePath;
103     if (item)
104       texturePath = m_texturePath.GetItemLabel(item, true);
105     else
106       texturePath = m_texturePath.GetLabel(m_parentID);
107     if (texturePath != m_currentPath && !texturePath.empty())
108     {
109       // a new path - set our current path and tell ourselves to load our directory
110       m_currentPath = texturePath;
111       CancelLoading();
112     }
113   }
114 }
115
116 void CGUIMultiImage::Process(unsigned int currentTime, CDirtyRegionList &dirtyregions)
117 {
118   // Set a viewport so that we don't render outside the defined area
119   if (m_directoryStatus == READY && !m_files.empty())
120   {
121     unsigned int nextImage = m_currentImage + 1;
122     if (nextImage >= m_files.size())
123       nextImage = m_loop ? 0 : m_currentImage;  // stay on the last image if <loop>no</loop>
124
125     if (nextImage != m_currentImage)
126     {
127       // check if we should be loading a new image yet
128       unsigned int timeToShow = m_timePerImage;
129       if (0 == nextImage) // last image should be paused for a bit longer if that's what the skinner wishes.
130         timeToShow += m_timeToPauseAtEnd;
131       if (m_imageTimer.IsRunning() && m_imageTimer.GetElapsedMilliseconds() > timeToShow)
132       {
133         // grab a new image
134         m_currentImage = nextImage;
135         m_image.SetFileName(m_files[m_currentImage]);
136         MarkDirtyRegion();
137
138         m_imageTimer.StartZero();
139       }
140     }
141   }
142
143   if (g_graphicsContext.SetClipRegion(m_posX, m_posY, m_width, m_height))
144   {
145     if (m_image.SetColorDiffuse(m_diffuseColor))
146       MarkDirtyRegion();
147
148     m_image.DoProcess(currentTime, dirtyregions);
149
150     g_graphicsContext.RestoreClipRegion();
151   }
152
153   CGUIControl::Process(currentTime, dirtyregions);
154 }
155
156 void CGUIMultiImage::Render()
157 {
158   m_image.Render();
159   CGUIControl::Render();
160 }
161
162 bool CGUIMultiImage::OnAction(const CAction &action)
163 {
164   return false;
165 }
166
167 bool CGUIMultiImage::OnMessage(CGUIMessage &message)
168 {
169   if (message.GetMessage() == GUI_MSG_REFRESH_THUMBS)
170   {
171     if (!m_texturePath.IsConstant())
172       FreeResources();
173     return true;
174   }
175   return CGUIControl::OnMessage(message);
176 }
177
178 void CGUIMultiImage::AllocResources()
179 {
180   FreeResources();
181   CGUIControl::AllocResources();
182
183   if (m_directoryStatus == UNLOADED)
184     LoadDirectory();
185 }
186
187 void CGUIMultiImage::FreeResources(bool immediately)
188 {
189   m_image.FreeResources(immediately);
190   m_currentImage = 0;
191   CancelLoading();
192   m_files.clear();
193   CGUIControl::FreeResources(immediately);
194 }
195
196 void CGUIMultiImage::DynamicResourceAlloc(bool bOnOff)
197 {
198   CGUIControl::DynamicResourceAlloc(bOnOff);
199   m_bDynamicResourceAlloc=bOnOff;
200 }
201
202 void CGUIMultiImage::SetInvalid()
203 {
204   m_image.SetInvalid();
205   CGUIControl::SetInvalid();
206 }
207
208 bool CGUIMultiImage::CanFocus() const
209 {
210   return false;
211 }
212
213 void CGUIMultiImage::SetAspectRatio(const CAspectRatio &ratio)
214 {
215   m_image.SetAspectRatio(ratio);
216 }
217
218 void CGUIMultiImage::LoadDirectory()
219 {
220   // clear current stuff out
221   m_files.clear();
222
223   // don't load any images if our path is empty
224   if (m_currentPath.empty()) return;
225
226   /* Check the fast cases:
227    1. Picture extension
228    2. Cached picture (in case an extension is not present)
229    3. Bundled folder
230    */
231   CFileItem item(m_currentPath, false);
232   if (item.IsPicture() || CTextureCache::Get().HasCachedImage(m_currentPath))
233     m_files.push_back(m_currentPath);
234   else // bundled folder?
235     g_TextureManager.GetBundledTexturesFromPath(m_currentPath, m_files);
236   if (!m_files.empty())
237   { // found - nothing more to do
238     OnDirectoryLoaded();
239     return;
240   }
241   // slow(er) checks necessary - do them in the background
242   CSingleLock lock(m_section);
243   m_directoryStatus = LOADING;
244   m_jobID = CJobManager::GetInstance().AddJob(new CMultiImageJob(m_currentPath), this, CJob::PRIORITY_NORMAL);
245 }
246
247 void CGUIMultiImage::OnDirectoryLoaded()
248 {
249   // Randomize or sort our images if necessary
250   if (m_randomized)
251     random_shuffle(m_files.begin(), m_files.end());
252   else
253     sort(m_files.begin(), m_files.end());
254
255   // flag as loaded - no point in constantly reloading them
256   m_directoryStatus = READY;
257   m_imageTimer.StartZero();
258   m_currentImage = 0;
259   m_image.SetFileName(m_files.empty() ? "" : m_files[0]);
260 }
261
262 void CGUIMultiImage::CancelLoading()
263 {
264   CSingleLock lock(m_section);
265   if (m_directoryStatus == LOADING)
266     CJobManager::GetInstance().CancelJob(m_jobID);
267   m_directoryStatus = UNLOADED;
268 }
269
270 void CGUIMultiImage::OnJobComplete(unsigned int jobID, bool success, CJob *job)
271 {
272   CSingleLock lock(m_section);
273   if (m_directoryStatus == LOADING && strncmp(job->GetType(), "multiimage", 10) == 0)
274   {
275     m_files = ((CMultiImageJob *)job)->m_files;
276     m_directoryStatus = LOADED;
277   }
278 }
279
280 void CGUIMultiImage::SetInfo(const CGUIInfoLabel &info)
281 {
282   m_texturePath = info;
283   if (m_texturePath.IsConstant())
284     m_currentPath = m_texturePath.GetLabel(WINDOW_INVALID);
285 }
286
287 CStdString CGUIMultiImage::GetDescription() const
288 {
289   return m_image.GetDescription();
290 }
291
292 CGUIMultiImage::CMultiImageJob::CMultiImageJob(const CStdString &path)
293   : m_path(path)
294 {
295 }
296
297 bool CGUIMultiImage::CMultiImageJob::DoWork()
298 {
299   // check to see if we have a single image or a folder of images
300   CFileItem item(m_path, false);
301   item.FillInMimeType();
302   if (item.IsPicture() || StringUtils::StartsWithNoCase(item.GetMimeType(), "image/"))
303   {
304     m_files.push_back(m_path);
305   }
306   else
307   {
308     // Load in images from the directory specified
309     // m_path is relative (as are all skin paths)
310     CStdString realPath = g_TextureManager.GetTexturePath(m_path, true);
311     if (realPath.empty())
312       return true;
313
314     URIUtils::AddSlashAtEnd(realPath);
315     CFileItemList items;
316     CDirectory::GetDirectory(realPath, items, g_advancedSettings.m_pictureExtensions + "|.tbn|.dds", DIR_FLAG_NO_FILE_DIRS | DIR_FLAG_NO_FILE_INFO);
317     for (int i=0; i < items.Size(); i++)
318     {
319       CFileItem* pItem = items[i].get();
320       if (pItem && (pItem->IsPicture() || StringUtils::StartsWithNoCase(pItem->GetMimeType(), "image/")))
321         m_files.push_back(pItem->GetPath());
322     }
323   }
324   return true;
325 }