fixed: Casting to CFileItem from CGUIListItem could crash (eg in Movie Information...
[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 "../xbmc/utils/GUIInfoManager.h"\r
8 #include "../xbmc/utils/CharsetConverter.h"\r
9 #include "../xbmc/FileItem.h"\r
10 \r
11 CGUIListItemLayout::CListBase::CListBase(float posX, float posY, float width, float height, int visibleCondition)\r
12 {\r
13   m_posX = posX;\r
14   m_posY = posY;\r
15   m_width = width;\r
16   m_height = height;\r
17   m_visible = true;\r
18   m_visibleCondition = visibleCondition;\r
19 }\r
20 \r
21 CGUIListItemLayout::CListBase::~CListBase()\r
22 {\r
23 }\r
24 \r
25 CGUIListItemLayout::CListLabel::CListLabel(float posX, float posY, float width, float height, int visibleCondition, const CLabelInfo &label, int info, const CStdString &content)\r
26 : CGUIListItemLayout::CListBase(posX, posY, width, height, visibleCondition)\r
27 {\r
28   m_label = label;\r
29   m_info = info;\r
30   m_type = LIST_LABEL;\r
31   g_infoManager.ParseLabel(content, m_multiInfo);\r
32 }\r
33 \r
34 CGUIListItemLayout::CListLabel::~CListLabel()\r
35 {\r
36 }\r
37 \r
38 CGUIListItemLayout::CListTexture::CListTexture(float posX, float posY, float width, float height, int visibleCondition, const CImage &image, CGUIImage::GUIIMAGE_ASPECT_RATIO aspectRatio)\r
39 : CGUIListItemLayout::CListBase(posX, posY, width, height, visibleCondition),\r
40   m_image(0, 0, posX, posY, width, height, image)\r
41 {\r
42   m_type = LIST_TEXTURE;\r
43   m_image.SetAspectRatio(aspectRatio);\r
44 }\r
45 \r
46 CGUIListItemLayout::CListTexture::~CListTexture()\r
47 {\r
48   m_image.FreeResources();\r
49 }\r
50 \r
51 CGUIListItemLayout::CListImage::CListImage(float posX, float posY, float width, float height, int visibleCondition, const CImage &image, CGUIImage::GUIIMAGE_ASPECT_RATIO aspectRatio, int info)\r
52 : CGUIListItemLayout::CListTexture(posX, posY, width, height, visibleCondition, image, aspectRatio)\r
53 {\r
54   m_info = info;\r
55   m_type = LIST_IMAGE;\r
56 }\r
57 \r
58 CGUIListItemLayout::CListImage::~CListImage()\r
59 {\r
60 }\r
61 \r
62 CGUIListItemLayout::CGUIListItemLayout()\r
63 {\r
64   m_width = 0;\r
65   m_height = 0;\r
66   m_focused = false;\r
67   m_invalidated = true;\r
68   m_isPlaying = false;\r
69 }\r
70 \r
71 CGUIListItemLayout::CGUIListItemLayout(const CGUIListItemLayout &from)\r
72 {\r
73   m_width = from.m_width;\r
74   m_height = from.m_height;\r
75   m_focused = from.m_focused;\r
76   // copy across our controls\r
77   for (ciControls it = from.m_controls.begin(); it != from.m_controls.end(); ++it)\r
78   {\r
79     CListBase *item = *it;\r
80     if (item->m_type == CListBase::LIST_LABEL)\r
81       m_controls.push_back(new CListLabel(*(CListLabel *)item));\r
82     else if (item->m_type ==  CListBase::LIST_IMAGE)\r
83       m_controls.push_back(new CListImage(*(CListImage *)item));\r
84     else if (item->m_type ==  CListBase::LIST_TEXTURE)\r
85       m_controls.push_back(new CListTexture(*(CListTexture *)item));\r
86   }\r
87   m_invalidated = true;\r
88   m_isPlaying = false;\r
89 }\r
90 \r
91 CGUIListItemLayout::~CGUIListItemLayout()\r
92 {\r
93   for (iControls it = m_controls.begin(); it != m_controls.end(); ++it)\r
94     delete *it;\r
95 }\r
96 \r
97 float CGUIListItemLayout::Size(ORIENTATION orientation)\r
98 {\r
99   return (orientation == HORIZONTAL) ? m_width : m_height;\r
100 }\r
101 \r
102 void CGUIListItemLayout::Render(CGUIListItem *item, DWORD parentID)\r
103 {\r
104   if (m_invalidated)\r
105   {\r
106     // could use a dynamic cast here if RTTI was enabled.  As it's not,\r
107     // let's use a static cast with a virtual base function\r
108     CFileItem *fileItem = item->IsFileItem() ? (CFileItem *)item : new CFileItem(*item);\r
109 \r
110     // check for boolean conditions\r
111     m_isPlaying = g_infoManager.GetItemBool(fileItem, LISTITEM_ISPLAYING, parentID);\r
112     for (iControls it = m_controls.begin(); it != m_controls.end(); it++)\r
113       UpdateItem(*it, fileItem, parentID);\r
114     // now we have to check our overlapping label pairs\r
115     for (unsigned int i = 0; i < m_controls.size(); i++)\r
116     {\r
117       if (m_controls[i]->m_type == CListBase::LIST_LABEL)\r
118       {\r
119         CListLabel *label1 = (CListLabel *)m_controls[i];\r
120         for (unsigned int j = i + 1; j < m_controls.size(); j++)\r
121         {\r
122           if (m_controls[j]->m_type == CListBase::LIST_LABEL)\r
123           { // ok, now check if they overlap\r
124             CListLabel *label2 = (CListLabel *)m_controls[j];\r
125             if ((label1->m_renderY <= label2->m_renderY + label2->m_renderH*0.5f && label2->m_renderY + label2->m_renderH*0.5f <= label1->m_renderY + label1->m_renderH) ||\r
126                 (label2->m_renderY <= label1->m_renderY + label1->m_renderH*0.5f && label1->m_renderY + label1->m_renderH*0.5f <= label2->m_renderY + label2->m_renderH))\r
127             { // overlap vertically - check horizontal\r
128               CListLabel *left = label1->m_renderX < label2->m_renderX ? label1 : label2;\r
129               CListLabel *right = label1->m_renderX < label2->m_renderX ? label2 : label1;\r
130               if ((left->m_label.align & 3) == 0 && right->m_label.align & XBFONT_RIGHT)\r
131               { // left is aligned left, right is aligned right, and they overlap vertically\r
132                 if (left->m_renderX + left->m_renderW + 10 > right->m_renderX && left->m_renderX + left->m_renderW < right->m_renderX + right->m_renderW)\r
133                 { // overlap, so chop accordingly\r
134                   float chopPoint = (left->m_posX + left->m_width + right->m_posX - right->m_width) * 0.5f;\r
135 // [1       [2...[2  1].|..........1]         2]\r
136 // [1       [2.....[2   |      1]..1]         2]\r
137 // [1       [2..........|.[2   1]..1]         2]\r
138                   if (right->m_renderX > chopPoint)\r
139                     chopPoint = right->m_renderX - 5;\r
140                   else if (left->m_renderX + left->m_renderW < chopPoint)\r
141                     chopPoint = left->m_renderX + left->m_renderW + 5;\r
142                   left->m_renderW = chopPoint - 5 - left->m_renderX;\r
143                   right->m_renderW -= (chopPoint + 5 - right->m_renderX);\r
144                   right->m_renderX = chopPoint + 5;\r
145                 }\r
146               }\r
147             }\r
148           }\r
149         }\r
150       }\r
151     }\r
152     m_invalidated = false;\r
153     // delete our temporary fileitem\r
154     if (!item->IsFileItem())\r
155       delete fileItem;\r
156   }\r
157 \r
158   // and render\r
159   for (iControls it = m_controls.begin(); it != m_controls.end(); it++)\r
160   {\r
161     CListBase *layoutItem = *it;\r
162     if (layoutItem->m_visible)\r
163     {\r
164       if (layoutItem->m_type == CListBase::LIST_LABEL)\r
165         RenderLabel((CListLabel *)layoutItem, item->IsSelected() || m_isPlaying, m_focused);\r
166       else\r
167         ((CListTexture *)layoutItem)->m_image.Render();\r
168     }\r
169   }\r
170 }\r
171 \r
172 void CGUIListItemLayout::UpdateItem(CGUIListItemLayout::CListBase *control, CFileItem *item, DWORD parentID)\r
173 {\r
174   // check boolean conditions\r
175   if (control->m_visibleCondition)\r
176     control->m_visible = g_infoManager.GetItemBool(item, control->m_visibleCondition, parentID);\r
177   if (control->m_type == CListBase::LIST_IMAGE && item)\r
178   {\r
179     CListImage *image = (CListImage *)control;\r
180     image->m_image.SetFileName(g_infoManager.GetItemImage(item, image->m_info));\r
181   }\r
182   else if (control->m_type == CListBase::LIST_LABEL)\r
183   {\r
184     CListLabel *label = (CListLabel *)control;\r
185     if (label->m_info)\r
186       g_charsetConverter.utf8ToUTF16(g_infoManager.GetItemLabel(item, label->m_info), label->m_text);\r
187     else\r
188       g_charsetConverter.utf8ToUTF16(g_infoManager.GetItemMultiLabel(item, label->m_multiInfo), label->m_text);\r
189     if (label->m_label.font)\r
190     {\r
191       label->m_label.font->GetTextExtent(label->m_text, &label->m_textW, &label->m_renderH);\r
192       label->m_renderW = min(label->m_textW, label->m_width);\r
193       if (label->m_label.align & XBFONT_CENTER_Y)\r
194         label->m_renderY = label->m_posY + (label->m_height - label->m_renderH) * 0.5f;\r
195       else\r
196         label->m_renderY = label->m_posY;\r
197       if (label->m_label.align & XBFONT_RIGHT)\r
198         label->m_renderX = label->m_posX - label->m_renderW;\r
199       else if (label->m_label.align & XBFONT_CENTER_X)\r
200         label->m_renderX = label->m_posX - label->m_renderW * 0.5f;\r
201       else\r
202         label->m_renderX = label->m_posX;\r
203     }\r
204   }\r
205 }\r
206 \r
207 void CGUIListItemLayout::RenderLabel(CListLabel *label, bool selected, bool scroll)\r
208 {\r
209   if (label->m_label.font && !label->m_text.IsEmpty())\r
210   {\r
211     DWORD color = selected ? label->m_label.selectedColor : label->m_label.textColor;\r
212     if (scroll && label->m_renderW < label->m_textW)\r
213       label->m_label.font->DrawScrollingText(label->m_renderX, label->m_renderY, &color, 1,\r
214                                   label->m_label.shadowColor, label->m_text, label->m_renderW, label->m_scrollInfo);\r
215     else\r
216       label->m_label.font->DrawTextWidth(label->m_renderX, label->m_renderY, label->m_label.angle, color,\r
217                                   label->m_label.shadowColor, label->m_text, label->m_renderW);\r
218   }\r
219 }\r
220 \r
221 void CGUIListItemLayout::ResetScrolling()\r
222 {\r
223   for (iControls it = m_controls.begin(); it != m_controls.end(); it++)\r
224   {\r
225     CListBase *layoutItem = (*it);\r
226     if (layoutItem->m_type == CListBase::LIST_LABEL)\r
227       ((CListLabel *)layoutItem)->m_scrollInfo.Reset();\r
228   }\r
229 }\r
230 \r
231 \r
232 CGUIListItemLayout::CListBase *CGUIListItemLayout::CreateItem(TiXmlElement *child)\r
233 {\r
234   // grab the type...\r
235   CGUIControlFactory factory;\r
236   CStdString type = factory.GetType(child);\r
237   CGUIControl *control = factory.Create(0, NULL, child);\r
238   float posX = 0;\r
239   float posY = 0;\r
240   float width = 10;\r
241   float height = 10;\r
242   CStdString infoString;\r
243   CImage image;\r
244   CLabelInfo label;\r
245   XMLUtils::GetFloat(child, "posx", posX);\r
246   XMLUtils::GetFloat(child, "posy", posY);\r
247   XMLUtils::GetFloat(child, "width", width);\r
248   XMLUtils::GetFloat(child, "height", height);\r
249   XMLUtils::GetString(child, "info", infoString);\r
250   XMLUtils::GetHex(child, "textcolor", label.textColor);\r
251   XMLUtils::GetHex(child, "selectedcolor", label.selectedColor);\r
252   XMLUtils::GetHex(child, "shadowcolor", label.shadowColor);\r
253   CStdString fontName;\r
254   XMLUtils::GetString(child, "font", fontName);\r
255   label.font = g_fontManager.GetFont(fontName);\r
256   int info = g_infoManager.TranslateString(infoString);\r
257   if (info && (info < LISTITEM_START || info > LISTITEM_END))\r
258   {\r
259     CLog::Log(LOGERROR, __FUNCTION__" Invalid item info %s", infoString.c_str());\r
260     return NULL;\r
261   }\r
262   factory.GetTexture(child, "texture", image);\r
263   factory.GetAlignment(child, "align", label.align);\r
264   DWORD alignY = 0;\r
265   if (factory.GetAlignmentY(child, "aligny", alignY))\r
266     label.align |= alignY;\r
267   CStdString content;\r
268   XMLUtils::GetString(child, "label", content);\r
269   CGUIImage::GUIIMAGE_ASPECT_RATIO aspectRatio = CGUIImage::ASPECT_RATIO_KEEP;\r
270   factory.GetAspectRatio(child, "aspectratio", aspectRatio);\r
271   int visibleCondition = 0;\r
272   factory.GetConditionalVisibility(child, visibleCondition);\r
273   if (type == "label")\r
274   { // info label\r
275     return new CListLabel(posX, posY, width, height, visibleCondition, label, info, content);\r
276   }\r
277   else if (type == "image")\r
278   {\r
279     if (info)\r
280     { // info image\r
281       return new CListImage(posX, posY, width, height, visibleCondition, image, aspectRatio, info);\r
282     }\r
283     else\r
284     { // texture\r
285       return new CListTexture(posX, posY, width, height, visibleCondition, image, CGUIImage::ASPECT_RATIO_STRETCH);\r
286     }\r
287   }\r
288   return NULL;\r
289 }\r
290 \r
291 void CGUIListItemLayout::LoadLayout(TiXmlElement *layout, bool focused)\r
292 {\r
293   m_focused = focused;\r
294   layout->Attribute("width", &m_width);\r
295   layout->Attribute("height", &m_height);\r
296   TiXmlElement *child = layout->FirstChildElement("control");\r
297   while (child)\r
298   {\r
299     CListBase *item = CreateItem(child);\r
300     if (item)\r
301       m_controls.push_back(item);\r
302     child = child->NextSiblingElement("control");\r
303   }\r
304 }\r
305 \r
306 //#ifdef PRE_SKIN_VERSION_2_1_COMPATIBILITY\r
307 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
308 {\r
309   m_width = width;\r
310   m_height = height;\r
311   m_focused = focused;\r
312   CListTexture *tex = new CListTexture(0, 0, width, texHeight, nofocusCondition, texture, CGUIImage::ASPECT_RATIO_STRETCH);\r
313   m_controls.push_back(tex);\r
314   if (focused)\r
315   {\r
316     CListTexture *tex = new CListTexture(0, 0, width, texHeight, focusCondition, textureFocus, CGUIImage::ASPECT_RATIO_STRETCH);\r
317     m_controls.push_back(tex);\r
318   }\r
319   CListImage *image = new CListImage(8, 0, iconWidth, texHeight, 0, CImage(""), CGUIImage::ASPECT_RATIO_KEEP, LISTITEM_ICON);\r
320   m_controls.push_back(image);\r
321   float x = iconWidth + labelInfo.offsetX + 10;\r
322   CListLabel *label = new CListLabel(x, labelInfo.offsetY, width - x - 18, height, 0, labelInfo, LISTITEM_LABEL, "");\r
323   m_controls.push_back(label);\r
324   x = labelInfo2.offsetX ? labelInfo2.offsetX : m_width - 16;\r
325   label = new CListLabel(x, labelInfo2.offsetY, x - iconWidth - 20, height, 0, labelInfo2, LISTITEM_LABEL2, "");\r
326   m_controls.push_back(label);\r
327 }\r
328 \r
329 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
330 {\r
331   m_width = width;\r
332   m_height = height;\r
333   m_focused = focused;\r
334   float centeredPosX = (m_width - texWidth)*0.5f;\r
335   // background texture\r
336   CListTexture *tex = new CListTexture(centeredPosX, 0, texWidth, texHeight, 0, image, CGUIImage::ASPECT_RATIO_STRETCH);\r
337   m_controls.push_back(tex);\r
338   // thumbnail\r
339   float xOff = 0;\r
340   float yOff = 0;\r
341   if (thumbAlign != 0)\r
342   {\r
343     xOff += (texWidth - thumbWidth) * 0.5f;\r
344     yOff += (texHeight - thumbHeight) * 0.5f;\r
345     //if thumbPosX or thumbPosX != 0 the thumb will be bumped off-center\r
346   }\r
347   CListImage *thumb = new CListImage(thumbPosX + centeredPosX + xOff, thumbPosY + yOff, thumbWidth, thumbHeight, 0, CImage(""), thumbAspect, LISTITEM_ICON);\r
348   m_controls.push_back(thumb);\r
349   // overlay\r
350   CListImage *overlay = new CListImage(thumbPosX + centeredPosX + xOff + thumbWidth - 32, thumbPosY + yOff + thumbHeight - 32, 32, 32, 0, CImage(""), thumbAspect, LISTITEM_OVERLAY);\r
351   m_controls.push_back(overlay);\r
352   // label\r
353   if (hideLabels) return;\r
354   CListLabel *label = new CListLabel(width*0.5f, texHeight, width, height, 0, labelInfo, LISTITEM_LABEL, "");\r
355   m_controls.push_back(label);\r
356 }\r
357 //#endif