added: Ability to clone controls within guilib. Useful for screens that autofill...
[xbmc:xbmc-antiquated.git] / XBMC / guilib / GUIMultiImage.cpp
1 /*
2  *      Copyright (C) 2005-2008 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, write to
17  *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
18  *  http://www.gnu.org/copyleft/gpl.html
19  *
20  */
21
22 #include "include.h"
23 #include "GUIMultiImage.h"
24 #include "TextureManager.h"
25 #include "FileSystem/HDDirectory.h"
26 #include "Util.h"
27 #include "FileItem.h"
28
29 using namespace std;
30 using namespace DIRECTORY;
31
32 CGUIMultiImage::CGUIMultiImage(DWORD dwParentID, DWORD dwControlId, float posX, float posY, float width, float height, const CImage& texturePath, DWORD timePerImage, DWORD fadeTime, bool randomized, bool loop, DWORD timeToPauseAtEnd)
33     : CGUIControl(dwParentID, dwControlId, posX, posY, width, height)
34 {
35   m_texturePath = texturePath;
36   if (m_texturePath.file.IsConstant())
37     m_currentPath = m_texturePath.file.GetLabel(WINDOW_INVALID);
38   m_currentImage = 0;
39   m_timePerImage = timePerImage;
40   m_timeToPauseAtEnd = timeToPauseAtEnd;
41   m_fadeTime = fadeTime;
42   m_randomized = randomized;
43   m_loop = loop;
44   m_aspectRatio = CGUIImage::CAspectRatio::AR_STRETCH;
45   ControlType = GUICONTROL_MULTI_IMAGE;
46   m_bDynamicResourceAlloc=false;
47   m_directoryLoaded = false;
48 }
49
50 CGUIMultiImage::CGUIMultiImage(const CGUIMultiImage &from)
51 : CGUIControl(from)
52 {
53   m_texturePath = from.m_texturePath;
54   m_timePerImage = from.m_timePerImage;
55   m_timeToPauseAtEnd = from.m_timeToPauseAtEnd;
56   m_fadeTime = from.m_fadeTime;
57   m_randomized = from.m_randomized;
58   m_loop = from.m_loop;
59   m_aspectRatio = from.m_aspectRatio;
60   m_directoryLoaded = false;
61   if (m_texturePath.file.IsConstant())
62     m_currentPath = m_texturePath.file.GetLabel(WINDOW_INVALID);
63   m_currentImage = 0;
64   ControlType = GUICONTROL_MULTI_IMAGE;
65 }
66
67 CGUIMultiImage::~CGUIMultiImage(void)
68 {
69 }
70
71 void CGUIMultiImage::UpdateVisibility(const CGUIListItem *item)
72 {
73   CGUIControl::UpdateVisibility(item);
74
75   // check if we're hidden, and deallocate if so
76   if (!IsVisible() && m_visible != DELAYED)
77   {
78     if (m_bDynamicResourceAlloc && IsAllocated())
79       FreeResources();
80     return;
81   }
82
83   // we are either delayed or visible, so we can allocate our resources
84
85   // check for conditional information before we
86   // alloc as this can free our resources
87   if (!m_texturePath.file.IsConstant())
88   {
89     CStdString texturePath(m_texturePath.file.GetLabel(m_dwParentID));
90     if (texturePath != m_currentPath && !texturePath.IsEmpty())
91     {
92       m_currentPath = texturePath;
93       FreeResources();
94       LoadDirectory();
95     }
96   }
97
98   // and allocate our resources
99   if (!IsAllocated())
100     AllocResources();
101 }
102
103 void CGUIMultiImage::Render()
104 {
105   if (!m_images.empty())
106   {
107     // Set a viewport so that we don't render outside the defined area
108     g_graphicsContext.SetClipRegion(m_posX, m_posY, m_width, m_height);
109
110     unsigned int nextImage = m_currentImage + 1;
111     if (nextImage >= m_images.size())
112       nextImage = m_loop ? 0 : m_currentImage;  // stay on the last image if <loop>no</loop>
113
114     if (nextImage != m_currentImage)
115     {
116       // check if we should be loading a new image yet
117       DWORD timeToShow = m_timePerImage;
118       if (0 == nextImage) // last image should be paused for a bit longer if that's what the skinner wishes.
119         timeToShow += m_timeToPauseAtEnd;
120       if (m_imageTimer.IsRunning() && m_imageTimer.GetElapsedMilliseconds() > timeToShow)
121       {
122         m_imageTimer.Stop();
123         // grab a new image
124         LoadImage(nextImage);
125         // start the fade timer
126         m_fadeTimer.StartZero();
127       }
128
129       // check if we are still fading
130       if (m_fadeTimer.IsRunning())
131       {
132         // check if the fade timer has run out
133         float timeFading = m_fadeTimer.GetElapsedMilliseconds();
134         if (timeFading >= m_fadeTime)
135         {
136           m_fadeTimer.Stop();
137           // swap images
138           m_images[m_currentImage]->FreeResources();
139           m_images[nextImage]->SetAlpha(255);
140           m_currentImage = nextImage;
141           // start the load timer
142           m_imageTimer.StartZero();
143         }
144         else
145         { // perform the fade in of next image
146           float fadeAmount = timeFading / m_fadeTime;
147           float alpha = (float)(m_diffuseColor >> 24) / 255.0f;
148           if (alpha < 1 && alpha > 0)
149           { // we have a semi-transparent image, so we need to use a more complicated
150             // fade technique.  Assuming a black background (not generally true, but still...)
151             // we have
152             // b(t) = [a - b(1-t)*a] / a*(1-b(1-t)*a),
153             // where a = alpha, and b(t):[0,1] -> [0,1] is the blend function.
154             // solving, we get
155             // b(t) = [1 - (1-a)^t] / a
156             float blendIn = (1 - pow(1-alpha, fadeAmount)) / alpha;
157             m_images[nextImage]->SetAlpha((unsigned char)(255 * blendIn));
158             float blendOut = (1 - blendIn) / (1 - blendIn*alpha); // no need to use pow() again here
159             m_images[m_currentImage]->SetAlpha((unsigned char)(255 * blendOut));
160           }
161           else
162           { // simple case, just fade in the second image
163             m_images[m_currentImage]->SetAlpha(255);
164             m_images[nextImage]->SetAlpha((unsigned char)(255*fadeAmount));
165           }
166           m_images[m_currentImage]->Render();
167         }
168         m_images[nextImage]->Render();
169       }
170       else
171       { // only one image - render it.
172         m_images[m_currentImage]->Render();
173       }
174     }
175     else
176     { // only one image - render it.
177       m_images[m_currentImage]->Render();
178     }
179     g_graphicsContext.RestoreClipRegion();
180   }
181   CGUIControl::Render();
182 }
183
184 bool CGUIMultiImage::OnAction(const CAction &action)
185 {
186   return false;
187 }
188
189 bool CGUIMultiImage::OnMessage(CGUIMessage &message)
190 {
191   if (message.GetMessage() == GUI_MSG_REFRESH_THUMBS)
192   {
193     if (!m_texturePath.file.IsConstant())
194       FreeResources();
195     return true;
196   }
197   return CGUIControl::OnMessage(message);
198 }
199
200 void CGUIMultiImage::PreAllocResources()
201 {
202   FreeResources();
203 }
204
205 void CGUIMultiImage::AllocResources()
206 {
207   FreeResources();
208   CGUIControl::AllocResources();
209
210   if (!m_directoryLoaded)
211     LoadDirectory();
212
213   // Randomize or sort our images if necessary
214   if (m_randomized)
215     random_shuffle(m_files.begin(), m_files.end());
216
217   for (unsigned int i=0; i < m_files.size(); i++)
218   {
219     CImage image(m_texturePath);
220     image.file = m_files[i];
221     CGUIImage *pImage = new CGUIImage(GetParentID(), GetID(), m_posX, m_posY, m_width, m_height, image);
222     if (pImage)
223       m_images.push_back(pImage);
224   }
225   // Load in the current image, and reset our timer
226   m_imageTimer.StartZero();
227   m_fadeTimer.Stop();
228   m_currentImage = 0;
229   if (m_images.empty())
230     return;
231
232   LoadImage(m_currentImage);
233 }
234
235 void CGUIMultiImage::LoadImage(int image)
236 {
237   if (image < 0 || image >= (int)m_images.size())
238     return;
239
240   m_images[image]->AllocResources();
241   m_images[image]->SetColorDiffuse(m_diffuseColor);
242
243   // Scale image so that it will fill our render area
244   if (m_aspectRatio != CGUIImage::CAspectRatio::AR_STRETCH)
245   {
246     // to get the pixel ratio, we must use the SCALED output sizes
247     float pixelRatio = g_graphicsContext.GetScalingPixelRatio();
248
249     float sourceAspectRatio = (float)m_images[image]->GetTextureWidth() / m_images[image]->GetTextureHeight();
250     float aspectRatio = sourceAspectRatio / pixelRatio;
251
252     float newWidth = m_width;
253     float newHeight = newWidth / aspectRatio;
254     if ((m_aspectRatio == CGUIImage::CAspectRatio::AR_SCALE && newHeight < m_height) ||
255       (m_aspectRatio == CGUIImage::CAspectRatio::AR_KEEP && newHeight > m_height))
256     {
257       newHeight = m_height;
258       newWidth = newHeight * aspectRatio;
259     }
260     m_images[image]->SetPosition(m_posX - (newWidth - m_width)*0.5f, m_posY - (newHeight - m_height)*0.5f);
261     m_images[image]->SetWidth(newWidth);
262     m_images[image]->SetHeight(newHeight);
263   }
264 }
265
266 void CGUIMultiImage::FreeResources()
267 {
268   for (unsigned int i = 0; i < m_images.size(); ++i)
269   {
270     m_images[i]->FreeResources();
271     delete m_images[i];
272   }
273
274   m_images.clear();
275   m_currentImage = 0;
276   CGUIControl::FreeResources();
277 }
278
279 void CGUIMultiImage::DynamicResourceAlloc(bool bOnOff)
280 {
281   CGUIControl::DynamicResourceAlloc(bOnOff);
282   m_bDynamicResourceAlloc=bOnOff;
283 }
284
285 bool CGUIMultiImage::CanFocus() const
286 {
287   return false;
288 }
289
290 void CGUIMultiImage::SetAspectRatio(CGUIImage::CAspectRatio::ASPECT_RATIO ratio)
291 {
292   if (m_aspectRatio != ratio)
293   {
294     m_aspectRatio = ratio;
295     m_bInvalidated = true;
296   }
297 }
298
299 void CGUIMultiImage::LoadDirectory()
300 {
301   // Load any images from our texture bundle first
302   m_files.clear();
303
304   // don't load any images if our path is empty
305   if (m_currentPath.IsEmpty()) return;
306
307   // check to see if we have a single image or a folder of images
308   CFileItem item(m_currentPath, false);
309   if (item.IsPicture())
310   {
311     m_files.push_back(g_TextureManager.GetTexturePath(m_currentPath));
312   }
313   else
314   { // folder of images
315     g_TextureManager.GetBundledTexturesFromPath(m_currentPath, m_files);
316
317     // Load in our images from the directory specified
318     // m_currentPath is relative (as are all skin paths)
319     CStdString realPath = g_TextureManager.GetTexturePath(m_currentPath);
320     CUtil::AddSlashAtEnd(realPath);
321     CHDDirectory dir;
322     CFileItemList items;
323     dir.GetDirectory(realPath, items);
324     for (int i=0; i < items.Size(); i++)
325     {
326       CFileItemPtr pItem = items[i];
327       if (pItem->IsPicture())
328         m_files.push_back(pItem->m_strPath);
329     }
330   }
331
332   // sort our images - they'll be randomized in AllocResources() if necessary
333   sort(m_files.begin(), m_files.end());
334
335   // flag as loaded - no point in constantly reloading them
336   m_directoryLoaded = true;
337 }
338