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