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