added: Merged new multiple view code support from the multiview branch (r7408->7422...
[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(float posX, float posY, float width, float height, int visibleCondition)\r
13 {\r
14   m_posX = posX;\r
15   m_posY = posY;\r
16   m_width = width;\r
17   m_height = height;\r
18   m_visible = true;\r
19   m_visibleCondition = visibleCondition;\r
20 }\r
21 \r
22 CGUIListItemLayout::CListBase::~CListBase()\r
23 {\r
24 }\r
25 \r
26 CGUIListItemLayout::CListLabel::CListLabel(float posX, float posY, float width, float height, int visibleCondition, const CLabelInfo &label, int info, const CStdString &content)\r
27 : CGUIListItemLayout::CListBase(posX, posY, width, height, visibleCondition)\r
28 {\r
29   m_label = label;\r
30   m_info = info;\r
31   m_type = LIST_LABEL;\r
32   g_infoManager.ParseLabel(content, m_multiInfo);\r
33 }\r
34 \r
35 CGUIListItemLayout::CListLabel::~CListLabel()\r
36 {\r
37 }\r
38 \r
39 CGUIListItemLayout::CListTexture::CListTexture(float posX, float posY, float width, float height, int visibleCondition, const CImage &image, CGUIImage::GUIIMAGE_ASPECT_RATIO aspectRatio, const vector<CAnimation> &animations)\r
40 : CGUIListItemLayout::CListBase(posX, posY, width, height, visibleCondition),\r
41   m_image(0, 0, posX, posY, width, height, image)\r
42 {\r
43   m_type = LIST_TEXTURE;\r
44   m_image.SetAspectRatio(aspectRatio);\r
45   m_image.SetAnimations(animations);\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, const vector<CAnimation> &animations, int info)\r
54 : CGUIListItemLayout::CListTexture(posX, posY, width, height, visibleCondition, image, aspectRatio, 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)\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   {\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 \r
112     // check for boolean conditions\r
113     m_isPlaying = g_infoManager.GetItemBool(fileItem, LISTITEM_ISPLAYING, parentID);\r
114     for (iControls it = m_controls.begin(); it != m_controls.end(); it++)\r
115       UpdateItem(*it, fileItem, parentID);\r
116     // now we have to check our overlapping label pairs\r
117     for (unsigned int i = 0; i < m_controls.size(); i++)\r
118     {\r
119       if (m_controls[i]->m_type == CListBase::LIST_LABEL)\r
120       {\r
121         CListLabel *label1 = (CListLabel *)m_controls[i];\r
122         for (unsigned int j = i + 1; j < m_controls.size(); j++)\r
123         {\r
124           if (m_controls[j]->m_type == CListBase::LIST_LABEL)\r
125           { // ok, now check if they overlap\r
126             CListLabel *label2 = (CListLabel *)m_controls[j];\r
127             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
128                 (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
129             { // overlap vertically - check horizontal\r
130               CListLabel *left = label1->m_renderX < label2->m_renderX ? label1 : label2;\r
131               CListLabel *right = label1->m_renderX < label2->m_renderX ? label2 : label1;\r
132               if ((left->m_label.align & 3) == 0 && right->m_label.align & XBFONT_RIGHT)\r
133               { // left is aligned left, right is aligned right, and they overlap vertically\r
134                 if (left->m_renderX + left->m_renderW + 10 > right->m_renderX && left->m_renderX + left->m_renderW < right->m_renderX + right->m_renderW)\r
135                 { // overlap, so chop accordingly\r
136                   float chopPoint = (left->m_posX + left->m_width + right->m_posX - right->m_width) * 0.5f;\r
137 // [1       [2...[2  1].|..........1]         2]\r
138 // [1       [2.....[2   |      1]..1]         2]\r
139 // [1       [2..........|.[2   1]..1]         2]\r
140                   if (right->m_renderX > chopPoint)\r
141                     chopPoint = right->m_renderX - 5;\r
142                   else if (left->m_renderX + left->m_renderW < chopPoint)\r
143                     chopPoint = left->m_renderX + left->m_renderW + 5;\r
144                   left->m_renderW = chopPoint - 5 - left->m_renderX;\r
145                   right->m_renderW -= (chopPoint + 5 - right->m_renderX);\r
146                   right->m_renderX = chopPoint + 5;\r
147                 }\r
148               }\r
149             }\r
150           }\r
151         }\r
152       }\r
153     }\r
154     m_invalidated = false;\r
155     // delete our temporary fileitem\r
156     if (!item->IsFileItem())\r
157       delete fileItem;\r
158   }\r
159 \r
160   // and render\r
161   for (iControls it = m_controls.begin(); it != m_controls.end(); it++)\r
162   {\r
163     CListBase *layoutItem = *it;\r
164     if (layoutItem->m_visible)\r
165     {\r
166       if (layoutItem->m_type == CListBase::LIST_LABEL)\r
167         RenderLabel((CListLabel *)layoutItem, item->IsSelected() || m_isPlaying, m_focused);\r
168       else\r
169       {\r
170         if (time)\r
171           ((CListTexture *)layoutItem)->m_image.UpdateEffectState(time);\r
172         ((CListTexture *)layoutItem)->m_image.Render();\r
173       }\r
174     }\r
175   }\r
176 }\r
177 \r
178 void CGUIListItemLayout::UpdateItem(CGUIListItemLayout::CListBase *control, CFileItem *item, DWORD parentID)\r
179 {\r
180   // check boolean conditions\r
181   if (control->m_visibleCondition)\r
182     control->m_visible = g_infoManager.GetItemBool(item, control->m_visibleCondition, parentID);\r
183   if (control->m_type == CListBase::LIST_IMAGE && item)\r
184   {\r
185     CListImage *image = (CListImage *)control;\r
186     image->m_image.SetFileName(g_infoManager.GetItemImage(item, image->m_info));\r
187   }\r
188   else if (control->m_type == CListBase::LIST_LABEL)\r
189   {\r
190     CListLabel *label = (CListLabel *)control;\r
191     if (label->m_info)\r
192       g_charsetConverter.utf8ToUTF16(g_infoManager.GetItemLabel(item, label->m_info), label->m_text);\r
193     else\r
194       g_charsetConverter.utf8ToUTF16(g_infoManager.GetItemMultiLabel(item, label->m_multiInfo), label->m_text);\r
195     if (label->m_label.font)\r
196     {\r
197       label->m_label.font->GetTextExtent(label->m_text, &label->m_textW, &label->m_renderH);\r
198       label->m_renderW = min(label->m_textW, label->m_width);\r
199       if (label->m_label.align & XBFONT_CENTER_Y)\r
200         label->m_renderY = label->m_posY + (label->m_height - label->m_renderH) * 0.5f;\r
201       else\r
202         label->m_renderY = label->m_posY;\r
203       if (label->m_label.align & XBFONT_RIGHT)\r
204         label->m_renderX = label->m_posX - label->m_renderW;\r
205       else if (label->m_label.align & XBFONT_CENTER_X)\r
206         label->m_renderX = label->m_posX - label->m_renderW * 0.5f;\r
207       else\r
208         label->m_renderX = label->m_posX;\r
209     }\r
210   }\r
211 }\r
212 \r
213 void CGUIListItemLayout::RenderLabel(CListLabel *label, bool selected, bool scroll)\r
214 {\r
215   if (label->m_label.font && !label->m_text.IsEmpty())\r
216   {\r
217     DWORD color = selected ? label->m_label.selectedColor : label->m_label.textColor;\r
218     if (scroll && label->m_renderW < label->m_textW)\r
219       label->m_label.font->DrawScrollingText(label->m_renderX, label->m_renderY, &color, 1,\r
220                                   label->m_label.shadowColor, label->m_text, label->m_renderW, label->m_scrollInfo);\r
221     else\r
222       label->m_label.font->DrawTextWidth(label->m_renderX, label->m_renderY, label->m_label.angle, color,\r
223                                   label->m_label.shadowColor, label->m_text, label->m_renderW);\r
224   }\r
225 }\r
226 \r
227 void CGUIListItemLayout::ResetScrolling()\r
228 {\r
229   for (iControls it = m_controls.begin(); it != m_controls.end(); it++)\r
230   {\r
231     CListBase *layoutItem = (*it);\r
232     if (layoutItem->m_type == CListBase::LIST_LABEL)\r
233       ((CListLabel *)layoutItem)->m_scrollInfo.Reset();\r
234   }\r
235 }\r
236 \r
237 void CGUIListItemLayout::QueueAnimation(ANIMATION_TYPE animType)\r
238 {\r
239   for (iControls it = m_controls.begin(); it != m_controls.end(); it++)\r
240   {\r
241     CListBase *layoutItem = (*it);\r
242     if (layoutItem->m_type == CListBase::LIST_IMAGE ||\r
243         layoutItem->m_type == CListBase::LIST_TEXTURE)\r
244       ((CListTexture *)layoutItem)->m_image.QueueAnimation(animType);\r
245   }\r
246 }\r
247 \r
248 CGUIListItemLayout::CListBase *CGUIListItemLayout::CreateItem(TiXmlElement *child)\r
249 {\r
250   // resolve any <include> tag's in this control\r
251   g_SkinInfo.ResolveIncludes(child);\r
252 \r
253   // grab the type...\r
254   CGUIControlFactory factory;\r
255   CStdString type = factory.GetType(child);\r
256 \r
257   // resolve again with strType set so that <default> tags are added\r
258   g_SkinInfo.ResolveIncludes(child, type);\r
259 \r
260   float posX = 0;\r
261   float posY = 0;\r
262   float width = 10;\r
263   float height = 10;\r
264   CStdString infoString;\r
265   CImage image;\r
266   CLabelInfo label;\r
267   XMLUtils::GetFloat(child, "posx", posX);\r
268   XMLUtils::GetFloat(child, "posy", posY);\r
269   XMLUtils::GetFloat(child, "width", width);\r
270   XMLUtils::GetFloat(child, "height", height);\r
271   XMLUtils::GetString(child, "info", infoString);\r
272   XMLUtils::GetHex(child, "textcolor", label.textColor);\r
273   XMLUtils::GetHex(child, "selectedcolor", label.selectedColor);\r
274   XMLUtils::GetHex(child, "shadowcolor", label.shadowColor);\r
275   CStdString fontName;\r
276   XMLUtils::GetString(child, "font", fontName);\r
277   label.font = g_fontManager.GetFont(fontName);\r
278   int info = g_infoManager.TranslateString(infoString);\r
279   if (info && (info < LISTITEM_START || info > LISTITEM_END))\r
280   {\r
281     CLog::Log(LOGERROR, __FUNCTION__" Invalid item info %s", infoString.c_str());\r
282     return NULL;\r
283   }\r
284   factory.GetTexture(child, "texture", image);\r
285   factory.GetAlignment(child, "align", label.align);\r
286   FRECT rect = { posX, posY, width, height };\r
287   vector<CAnimation> animations;\r
288   factory.GetAnimations(child, rect, animations);\r
289   if (animations.size())\r
290   {\r
291     int test = 1;\r
292   }\r
293   DWORD alignY = 0;\r
294   if (factory.GetAlignmentY(child, "aligny", alignY))\r
295     label.align |= alignY;\r
296   CStdString content;\r
297   XMLUtils::GetString(child, "label", content);\r
298   CGUIImage::GUIIMAGE_ASPECT_RATIO aspectRatio = CGUIImage::ASPECT_RATIO_KEEP;\r
299   factory.GetAspectRatio(child, "aspectratio", aspectRatio);\r
300   int visibleCondition = 0;\r
301   factory.GetConditionalVisibility(child, visibleCondition);\r
302   if (type == "label")\r
303   { // info label\r
304     return new CListLabel(posX, posY, width, height, visibleCondition, label, info, content);\r
305   }\r
306   else if (type == "image")\r
307   {\r
308     if (info)\r
309     { // info image\r
310       return new CListImage(posX, posY, width, height, visibleCondition, image, aspectRatio, animations, info);\r
311     }\r
312     else\r
313     { // texture\r
314       return new CListTexture(posX, posY, width, height, visibleCondition, image, CGUIImage::ASPECT_RATIO_STRETCH, animations);\r
315     }\r
316   }\r
317   return NULL;\r
318 }\r
319 \r
320 void CGUIListItemLayout::LoadLayout(TiXmlElement *layout, bool focused)\r
321 {\r
322   m_focused = focused;\r
323   layout->Attribute("width", &m_width);\r
324   layout->Attribute("height", &m_height);\r
325   TiXmlElement *child = layout->FirstChildElement("control");\r
326   while (child)\r
327   {\r
328     CListBase *item = CreateItem(child);\r
329     if (item)\r
330       m_controls.push_back(item);\r
331     child = child->NextSiblingElement("control");\r
332   }\r
333 }\r
334 \r
335 //#ifdef PRE_SKIN_VERSION_2_1_COMPATIBILITY\r
336 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
337 {\r
338   m_width = width;\r
339   m_height = height;\r
340   m_focused = focused;\r
341   vector<CAnimation> blankAnims;\r
342   CListTexture *tex = new CListTexture(0, 0, width, texHeight, nofocusCondition, texture, CGUIImage::ASPECT_RATIO_STRETCH, blankAnims);\r
343   m_controls.push_back(tex);\r
344   if (focused)\r
345   {\r
346     CListTexture *tex = new CListTexture(0, 0, width, texHeight, focusCondition, textureFocus, CGUIImage::ASPECT_RATIO_STRETCH, blankAnims);\r
347     m_controls.push_back(tex);\r
348   }\r
349   CListImage *image = new CListImage(8, 0, iconWidth, texHeight, 0, CImage(""), CGUIImage::ASPECT_RATIO_KEEP, blankAnims, LISTITEM_ICON);\r
350   m_controls.push_back(image);\r
351   float x = iconWidth + labelInfo.offsetX + 10;\r
352   CListLabel *label = new CListLabel(x, labelInfo.offsetY, width - x - 18, height, 0, labelInfo, LISTITEM_LABEL, "");\r
353   m_controls.push_back(label);\r
354   x = labelInfo2.offsetX ? labelInfo2.offsetX : m_width - 16;\r
355   label = new CListLabel(x, labelInfo2.offsetY, x - iconWidth - 20, height, 0, labelInfo2, LISTITEM_LABEL2, "");\r
356   m_controls.push_back(label);\r
357 }\r
358 \r
359 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
360 {\r
361   m_width = width;\r
362   m_height = height;\r
363   m_focused = focused;\r
364   float centeredPosX = (m_width - texWidth)*0.5f;\r
365   // background texture\r
366   vector<CAnimation> blankAnims;\r
367   CListTexture *tex = new CListTexture(centeredPosX, 0, texWidth, texHeight, 0, image, CGUIImage::ASPECT_RATIO_STRETCH, blankAnims);\r
368   m_controls.push_back(tex);\r
369   // thumbnail\r
370   float xOff = 0;\r
371   float yOff = 0;\r
372   if (thumbAlign != 0)\r
373   {\r
374     xOff += (texWidth - thumbWidth) * 0.5f;\r
375     yOff += (texHeight - thumbHeight) * 0.5f;\r
376     //if thumbPosX or thumbPosX != 0 the thumb will be bumped off-center\r
377   }\r
378   CListImage *thumb = new CListImage(thumbPosX + centeredPosX + xOff, thumbPosY + yOff, thumbWidth, thumbHeight, 0, CImage(""), thumbAspect, blankAnims, LISTITEM_ICON);\r
379   m_controls.push_back(thumb);\r
380   // overlay\r
381   CListImage *overlay = new CListImage(thumbPosX + centeredPosX + xOff + thumbWidth - 32, thumbPosY + yOff + thumbHeight - 32, 32, 32, 0, CImage(""), thumbAspect, blankAnims, LISTITEM_OVERLAY);\r
382   m_controls.push_back(overlay);\r
383   // label\r
384   if (hideLabels) return;\r
385   CListLabel *label = new CListLabel(width*0.5f, texHeight, width, height, 0, labelInfo, LISTITEM_LABEL, "");\r
386   m_controls.push_back(label);\r
387 }\r
388 //#endif