merge with trunk. revision 10951.
[xbmc:xbmc-antiquated.git] / guilib / GUIListItemLayout.cpp
1 #include "include.h"\r
2 #include "GUIListItemLayout.h"\r
3 #include "GUIListItem.h"\r
4 #include "GuiControlFactory.h"\r
5 #include "GUIFontManager.h"\r
6 #include "XMLUtils.h"\r
7 #include "SkinInfo.h"\r
8 #include "../xbmc/utils/GUIInfoManager.h"\r
9 #include "../xbmc/utils/CharsetConverter.h"\r
10 #include "../xbmc/FileItem.h"\r
11 \r
12 CGUIListItemLayout::CListBase::CListBase()\r
13 {\r
14 }\r
15 \r
16 CGUIListItemLayout::CListBase::~CListBase()\r
17 {\r
18 }\r
19 \r
20 CGUIListItemLayout::CListLabel::CListLabel(float posX, float posY, float width, float height, int visibleCondition, const CLabelInfo &label, bool alwaysScroll, int info, const CStdString &content, const vector<CAnimation> &animations)\r
21 : CGUIListItemLayout::CListBase(),\r
22   m_label(0, 0, posX, posY, width, height, label, alwaysScroll)\r
23 {\r
24   m_type = LIST_LABEL;\r
25   m_label.SetAnimations(animations);\r
26   m_info = info;\r
27   g_infoManager.ParseLabel(content, m_multiInfo);\r
28   m_label.SetVisibleCondition(visibleCondition, false);\r
29 }\r
30 \r
31 CGUIListItemLayout::CListLabel::~CListLabel()\r
32 {\r
33 }\r
34 \r
35 \r
36 CGUIListItemLayout::CListTexture::CListTexture(float posX, float posY, float width, float height, int visibleCondition, const CImage &image, const CImage &borderImage, const FRECT &borderSize, CGUIImage::GUIIMAGE_ASPECT_RATIO aspectRatio, DWORD aspectAlign, D3DCOLOR colorDiffuse, const vector<CAnimation> &animations)\r
37 : CGUIListItemLayout::CListBase(),\r
38   m_image(0, 0, posX, posY, width, height, image, borderImage, borderSize)\r
39 {\r
40   m_type = LIST_TEXTURE;\r
41   m_image.SetAspectRatio(aspectRatio, aspectAlign);\r
42   m_image.SetAnimations(animations);\r
43   m_image.SetColorDiffuse(colorDiffuse);\r
44   m_image.SetVisibleCondition(visibleCondition, false);\r
45 }\r
46 \r
47 \r
48 CGUIListItemLayout::CListTexture::~CListTexture()\r
49 {\r
50   m_image.FreeResources();\r
51 }\r
52 \r
53 CGUIListItemLayout::CListImage::CListImage(float posX, float posY, float width, float height, int visibleCondition, const CImage &image, const CImage &borderImage, const FRECT &borderSize, CGUIImage::GUIIMAGE_ASPECT_RATIO aspectRatio, DWORD aspectAlign, D3DCOLOR colorDiffuse, const vector<CAnimation> &animations, int info)\r
54 : CGUIListItemLayout::CListTexture(posX, posY, width, height, visibleCondition, image, borderImage, borderSize, aspectRatio, aspectAlign, colorDiffuse, animations)\r
55 {\r
56   m_info = info;\r
57   m_type = LIST_IMAGE;\r
58 }\r
59 \r
60 CGUIListItemLayout::CListImage::~CListImage()\r
61 {\r
62 }\r
63 \r
64 CGUIListItemLayout::CGUIListItemLayout()\r
65 {\r
66   m_width = 0;\r
67   m_height = 0;\r
68   m_condition = 0;\r
69   m_focused = false;\r
70   m_invalidated = true;\r
71   m_isPlaying = false;\r
72 }\r
73 \r
74 CGUIListItemLayout::CGUIListItemLayout(const CGUIListItemLayout &from)\r
75 {\r
76   m_width = from.m_width;\r
77   m_height = from.m_height;\r
78   m_focused = from.m_focused;\r
79   m_condition = from.m_condition;\r
80   // copy across our controls\r
81   for (ciControls it = from.m_controls.begin(); it != from.m_controls.end(); ++it)\r
82   {\r
83     CListBase *item = *it;\r
84     if (item->m_type == CListBase::LIST_LABEL)\r
85       m_controls.push_back(new CListLabel(*(CListLabel *)item));\r
86     else if (item->m_type ==  CListBase::LIST_IMAGE)\r
87       m_controls.push_back(new CListImage(*(CListImage *)item));\r
88     else if (item->m_type ==  CListBase::LIST_TEXTURE)\r
89       m_controls.push_back(new CListTexture(*(CListTexture *)item));\r
90   }\r
91   m_invalidated = true;\r
92   m_isPlaying = false;\r
93 }\r
94 \r
95 CGUIListItemLayout::~CGUIListItemLayout()\r
96 {\r
97   for (iControls it = m_controls.begin(); it != m_controls.end(); ++it)\r
98     delete *it;\r
99 }\r
100 \r
101 float CGUIListItemLayout::Size(ORIENTATION orientation) const\r
102 {\r
103   return (orientation == HORIZONTAL) ? m_width : m_height;\r
104 }\r
105 \r
106 void CGUIListItemLayout::Render(CGUIListItem *item, DWORD parentID, DWORD time)\r
107 {\r
108   if (m_invalidated)\r
109   { // need to update our item\r
110     // could use a dynamic cast here if RTTI was enabled.  As it's not,\r
111     // let's use a static cast with a virtual base function\r
112     CFileItem *fileItem = item->IsFileItem() ? (CFileItem *)item : new CFileItem(*item);\r
113     Update(fileItem, parentID);\r
114     // delete our temporary fileitem\r
115     if (!item->IsFileItem())\r
116       delete fileItem;\r
117   }\r
118 \r
119   // and render\r
120   for (iControls it = m_controls.begin(); it != m_controls.end(); it++)\r
121   {\r
122     CListBase *layoutItem = *it;\r
123     if (layoutItem->m_type == CListBase::LIST_LABEL)\r
124     {\r
125       CGUIListLabel &label = ((CListLabel *)layoutItem)->m_label;\r
126       label.SetSelected(item->IsSelected() || m_isPlaying);\r
127       label.SetScrolling(m_focused);\r
128       label.UpdateVisibility();\r
129       label.DoRender(time);\r
130     }\r
131     else\r
132     {\r
133       ((CListTexture *)layoutItem)->m_image.UpdateVisibility();\r
134       ((CListTexture *)layoutItem)->m_image.DoRender(time);\r
135     }\r
136   }\r
137 }\r
138 \r
139 void CGUIListItemLayout::Update(CFileItem *item, DWORD parentID)\r
140 {\r
141   // check for boolean conditions\r
142   m_isPlaying = g_infoManager.GetBool(LISTITEM_ISPLAYING, parentID, item);\r
143   for (iControls it = m_controls.begin(); it != m_controls.end(); it++)\r
144     UpdateItem(*it, item, parentID);\r
145   // now we have to check our overlapping label pairs\r
146   for (unsigned int i = 0; i < m_controls.size(); i++)\r
147   {\r
148     if (m_controls[i]->m_type == CListBase::LIST_LABEL && ((CListLabel *)m_controls[i])->m_label.IsVisible())\r
149     {\r
150       CGUIListLabel &label1 = ((CListLabel *)m_controls[i])->m_label;\r
151       CRect rect1(label1.GetRenderRect());\r
152       for (unsigned int j = i + 1; j < m_controls.size(); j++)\r
153       {\r
154         if (m_controls[j]->m_type == CListBase::LIST_LABEL && ((CListLabel *)m_controls[j])->m_label.IsVisible())\r
155         { // ok, now check if they overlap\r
156           CGUIListLabel &label2 = ((CListLabel *)m_controls[j])->m_label;\r
157           if (!rect1.Intersect(label2.GetRenderRect()).IsEmpty())\r
158           { // overlap vertically and horizontally - check alignment\r
159             CGUIListLabel &left = label1.GetRenderRect().x1 < label2.GetRenderRect().x1 ? label1 : label2;\r
160             CGUIListLabel &right = label1.GetRenderRect().x1 < label2.GetRenderRect().x1 ? label2 : label1;\r
161             if ((left.GetLabelInfo().align & 3) == 0 && right.GetLabelInfo().align & XBFONT_RIGHT)\r
162             {\r
163               float chopPoint = (left.GetXPosition() + left.GetWidth() + right.GetXPosition() - right.GetWidth()) * 0.5f;\r
164 // [1       [2...[2  1].|..........1]         2]\r
165 // [1       [2.....[2   |      1]..1]         2]\r
166 // [1       [2..........|.[2   1]..1]         2]\r
167               CRect leftRect(left.GetRenderRect());\r
168               CRect rightRect(right.GetRenderRect());\r
169               if (rightRect.x1 > chopPoint)\r
170                 chopPoint = rightRect.x1 - 5;\r
171               else if (leftRect.x2 < chopPoint)\r
172                 chopPoint = leftRect.x2 + 5;\r
173               leftRect.x2 = chopPoint - 5;\r
174               rightRect.x1 = chopPoint + 5;\r
175               left.SetRenderRect(leftRect);\r
176               right.SetRenderRect(rightRect);\r
177             }\r
178           }\r
179         }\r
180       }\r
181     }\r
182   }\r
183   m_invalidated = false;\r
184 }\r
185 \r
186 void CGUIListItemLayout::UpdateItem(CGUIListItemLayout::CListBase *control, CFileItem *item, DWORD parentID)\r
187 {\r
188   if (control->m_type == CListBase::LIST_IMAGE && item)\r
189   {\r
190     CListImage *image = (CListImage *)control;\r
191     image->m_image.SetFileName(g_infoManager.GetItemImage(item, image->m_info));\r
192   }\r
193   else if (control->m_type == CListBase::LIST_LABEL)\r
194   {\r
195     CListLabel *label = (CListLabel *)control;\r
196     if (label->m_info)\r
197       label->m_label.SetLabel(g_infoManager.GetItemLabel(item, label->m_info));\r
198     else\r
199       label->m_label.SetLabel(g_infoManager.GetItemMultiLabel(item, label->m_multiInfo));\r
200   }\r
201 }\r
202 \r
203 void CGUIListItemLayout::ResetScrolling()\r
204 {\r
205   for (iControls it = m_controls.begin(); it != m_controls.end(); it++)\r
206   {\r
207     CListBase *layoutItem = (*it);\r
208     if (layoutItem->m_type == CListBase::LIST_LABEL)\r
209       ((CListLabel *)layoutItem)->m_label.SetScrolling(false);\r
210   }\r
211 }\r
212 \r
213 void CGUIListItemLayout::QueueAnimation(ANIMATION_TYPE animType)\r
214 {\r
215   for (iControls it = m_controls.begin(); it != m_controls.end(); it++)\r
216   {\r
217     CListBase *layoutItem = (*it);\r
218     if (layoutItem->m_type == CListBase::LIST_IMAGE ||\r
219         layoutItem->m_type == CListBase::LIST_TEXTURE)\r
220       ((CListTexture *)layoutItem)->m_image.QueueAnimation(animType);\r
221     else if (layoutItem->m_type == CListBase::LIST_LABEL)\r
222       ((CListLabel *)layoutItem)->m_label.QueueAnimation(animType);\r
223   }\r
224 }\r
225 \r
226 void CGUIListItemLayout::ResetAnimation(ANIMATION_TYPE animType)\r
227 {\r
228   for (iControls it = m_controls.begin(); it != m_controls.end(); it++)\r
229   {\r
230     CListBase *layoutItem = (*it);\r
231     if (layoutItem->m_type == CListBase::LIST_IMAGE ||\r
232         layoutItem->m_type == CListBase::LIST_TEXTURE)\r
233       ((CListTexture *)layoutItem)->m_image.ResetAnimation(animType);\r
234     else if (layoutItem->m_type == CListBase::LIST_LABEL)\r
235       ((CListLabel *)layoutItem)->m_label.ResetAnimation(animType);\r
236   }\r
237 }\r
238 \r
239 CGUIListItemLayout::CListBase *CGUIListItemLayout::CreateItem(TiXmlElement *child)\r
240 {\r
241   // resolve any <include> tag's in this control\r
242   g_SkinInfo.ResolveIncludes(child);\r
243 \r
244   // grab the type...\r
245   CStdString type = CGUIControlFactory::GetType(child);\r
246 \r
247   // resolve again with strType set so that <default> tags are added\r
248   g_SkinInfo.ResolveIncludes(child, type);\r
249 \r
250   float posX = 0;\r
251   float posY = 0;\r
252   float width = 10;\r
253   float height = 10;\r
254   CStdString infoString;\r
255   CImage image;\r
256   CImage borderImage;\r
257   FRECT borderSize = { 0, 0, 0, 0 };\r
258   CLabelInfo label;\r
259   CGUIControlFactory::GetFloat(child, "posx", posX);\r
260   CGUIControlFactory::GetFloat(child, "posy", posY);\r
261   CGUIControlFactory::GetFloat(child, "width", width);\r
262   CGUIControlFactory::GetFloat(child, "height", height);\r
263   XMLUtils::GetString(child, "info", infoString);\r
264   CGUIControlFactory::GetColor(child, "textcolor", label.textColor);\r
265   CGUIControlFactory::GetColor(child, "selectedcolor", label.selectedColor);\r
266   CGUIControlFactory::GetColor(child, "shadowcolor", label.shadowColor);\r
267   CStdString fontName;\r
268   XMLUtils::GetString(child, "font", fontName);\r
269   label.font = g_fontManager.GetFont(fontName);\r
270   int info = g_infoManager.TranslateString(infoString);\r
271   if (info && (info < LISTITEM_START || info > LISTITEM_END))\r
272   {\r
273     CLog::Log(LOGERROR, " Invalid item info %s", infoString.c_str());\r
274     return NULL;\r
275   }\r
276   CGUIControlFactory::GetTexture(child, "texture", image);\r
277   CGUIControlFactory::GetAlignment(child, "align", label.align);\r
278   FRECT rect = { posX, posY, width, height };\r
279   vector<CAnimation> animations;\r
280   CGUIControlFactory::GetAnimations(child, rect, animations);\r
281   D3DCOLOR colorDiffuse(0xffffffff);\r
282   CGUIControlFactory::GetColor(child, "colordiffuse", colorDiffuse);\r
283   DWORD alignY = 0;\r
284   if (CGUIControlFactory::GetAlignmentY(child, "aligny", alignY))\r
285     label.align |= alignY;\r
286   CStdString content;\r
287   XMLUtils::GetString(child, "label", content);\r
288   CGUIImage::GUIIMAGE_ASPECT_RATIO aspectRatio = CGUIImage::ASPECT_RATIO_KEEP;\r
289   DWORD aspectAlign = ASPECT_ALIGN_CENTER | ASPECT_ALIGNY_CENTER;\r
290   CGUIControlFactory::GetAspectRatio(child, "aspectratio", aspectRatio, aspectAlign);\r
291   int visibleCondition = 0;\r
292   CGUIControlFactory::GetConditionalVisibility(child, visibleCondition);\r
293   XMLUtils::GetFloat(child, "angle", label.angle); label.angle *= -1;\r
294   bool scroll(false);\r
295   XMLUtils::GetBoolean(child, "scroll", scroll);\r
296   CGUIControlFactory::GetTexture(child, "bordertexture", borderImage);\r
297   CStdString borderStr;\r
298   if (XMLUtils::GetString(child, "bordersize", borderStr))\r
299     CGUIControlFactory::GetRectFromString(borderStr, borderSize);\r
300   if (type == "label")\r
301   { // info label\r
302     return new CListLabel(posX, posY, width, height, visibleCondition, label, scroll, info, content, animations);\r
303   }\r
304   else if (type == "image")\r
305   {\r
306     if (info)\r
307     { // info image\r
308       return new CListImage(posX, posY, width, height, visibleCondition, image, borderImage, borderSize, aspectRatio, aspectAlign, colorDiffuse, animations, info);\r
309     }\r
310     else\r
311     { // texture\r
312       return new CListTexture(posX, posY, width, height, visibleCondition, image, borderImage, borderSize, CGUIImage::ASPECT_RATIO_STRETCH, aspectAlign, colorDiffuse, animations);\r
313     }\r
314   }\r
315   return NULL;\r
316 }\r
317 \r
318 void CGUIListItemLayout::LoadLayout(TiXmlElement *layout, bool focused)\r
319 {\r
320   m_focused = focused;\r
321   g_SkinInfo.ResolveConstant(layout->Attribute("width"), m_width);\r
322   g_SkinInfo.ResolveConstant(layout->Attribute("height"), m_height);\r
323   const char *condition = layout->Attribute("condition");\r
324   if (condition)\r
325     m_condition = g_infoManager.TranslateString(condition);\r
326   TiXmlElement *child = layout->FirstChildElement("control");\r
327   while (child)\r
328   {\r
329     CListBase *item = CreateItem(child);\r
330     if (item)\r
331       m_controls.push_back(item);\r
332     child = child->NextSiblingElement("control");\r
333   }\r
334 }\r
335 \r
336 //#ifdef PRE_SKIN_VERSION_2_1_COMPATIBILITY\r
337 void CGUIListItemLayout::CreateListControlLayouts(float width, float height, bool focused, const CLabelInfo &labelInfo, const CLabelInfo &labelInfo2, const CImage &texture, const CImage &textureFocus, float texHeight, float iconWidth, float iconHeight, int nofocusCondition, int focusCondition)\r
338 {\r
339   m_width = width;\r
340   m_height = height;\r
341   m_focused = focused;\r
342   vector<CAnimation> blankAnims;\r
343   CImage border;\r
344   FRECT borderRect = { 0, 0, 0, 0 };\r
345   CListTexture *tex = new CListTexture(0, 0, width, texHeight, nofocusCondition, texture, border, borderRect, CGUIImage::ASPECT_RATIO_STRETCH, 0, 0xffffffff, blankAnims);\r
346   m_controls.push_back(tex);\r
347   if (focused)\r
348   {\r
349     CListTexture *tex = new CListTexture(0, 0, width, texHeight, focusCondition, textureFocus, border, borderRect, CGUIImage::ASPECT_RATIO_STRETCH, 0, 0xffffffff, blankAnims);\r
350     m_controls.push_back(tex);\r
351   }\r
352   CListImage *image = new CListImage(8, 0, iconWidth, texHeight, 0, CImage(""), border, borderRect, CGUIImage::ASPECT_RATIO_KEEP, 0, 0xffffffff, blankAnims, LISTITEM_ICON);\r
353   m_controls.push_back(image);\r
354   float x = iconWidth + labelInfo.offsetX + 10;\r
355   CListLabel *label = new CListLabel(x, labelInfo.offsetY, width - x - 18, height, 0, labelInfo, false, LISTITEM_LABEL, "", blankAnims);\r
356   m_controls.push_back(label);\r
357   x = labelInfo2.offsetX ? labelInfo2.offsetX : m_width - 16;\r
358   label = new CListLabel(x, labelInfo2.offsetY, x - iconWidth - 20, height, 0, labelInfo2, false, LISTITEM_LABEL2, "", blankAnims);\r
359   m_controls.push_back(label);\r
360 }\r
361 \r
362 void CGUIListItemLayout::CreateThumbnailPanelLayouts(float width, float height, bool focused, const CImage &image, float texWidth, float texHeight, float thumbPosX, float thumbPosY, float thumbWidth, float thumbHeight, DWORD thumbAlign, CGUIImage::GUIIMAGE_ASPECT_RATIO thumbAspect, const CLabelInfo &labelInfo, bool hideLabels)\r
363 {\r
364   m_width = width;\r
365   m_height = height;\r
366   m_focused = focused;\r
367   float centeredPosX = (m_width - texWidth)*0.5f;\r
368   CImage border;\r
369   FRECT borderRect = { 0, 0, 0, 0 };\r
370   // background texture\r
371   vector<CAnimation> blankAnims;\r
372   CListTexture *tex = new CListTexture(centeredPosX, 0, texWidth, texHeight, 0, image, border, borderRect, CGUIImage::ASPECT_RATIO_STRETCH, 0, 0xffffffff, blankAnims);\r
373   m_controls.push_back(tex);\r
374   // thumbnail\r
375   float xOff = 0;\r
376   float yOff = 0;\r
377   if (thumbAlign != 0)\r
378   {\r
379     xOff += (texWidth - thumbWidth) * 0.5f;\r
380     yOff += (texHeight - thumbHeight) * 0.5f;\r
381     //if thumbPosX or thumbPosX != 0 the thumb will be bumped off-center\r
382   }\r
383   CListImage *thumb = new CListImage(thumbPosX + centeredPosX + xOff, thumbPosY + yOff, thumbWidth, thumbHeight, 0, CImage(""), border, borderRect, thumbAspect, 0, 0xffffffff, blankAnims, LISTITEM_ICON);\r
384   m_controls.push_back(thumb);\r
385   // overlay\r
386   CListImage *overlay = new CListImage(thumbPosX + centeredPosX + xOff + thumbWidth - 32, thumbPosY + yOff + thumbHeight - 32, 32, 32, 0, CImage(""), border, borderRect, thumbAspect, 0, 0xffffffff, blankAnims, LISTITEM_OVERLAY);\r
387   m_controls.push_back(overlay);\r
388   // label\r
389   if (hideLabels) return;\r
390   CListLabel *label = new CListLabel(width*0.5f, texHeight, width, height, 0, labelInfo, false, LISTITEM_LABEL, "", blankAnims);\r
391   m_controls.push_back(label);\r
392 }\r
393 //#endif\r
394 \r
395 #ifdef _DEBUG\r
396 void CGUIListItemLayout::DumpTextureUse()\r
397 {\r
398   for (iControls it = m_controls.begin(); it != m_controls.end(); it++)\r
399   {\r
400     CListBase *layoutItem = (*it);\r
401     if (layoutItem->m_type == CListBase::LIST_IMAGE || layoutItem->m_type == CListBase::LIST_TEXTURE)\r
402       ((CListTexture *)layoutItem)->m_image.DumpTextureUse();\r
403   }\r
404 }\r
405 #endif\r