Merge pull request #1445 from Karlson2k/win32_xml_fix
[vuplus_xbmc] / xbmc / guilib / GUILabelControl.cpp
1 /*
2  *      Copyright (C) 2005-2013 Team XBMC
3  *      http://xbmc.org
4  *
5  *  This Program is free software; you can redistribute it and/or modify
6  *  it under the terms of the GNU General Public License as published by
7  *  the Free Software Foundation; either version 2, or (at your option)
8  *  any later version.
9  *
10  *  This Program is distributed in the hope that it will be useful,
11  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
12  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13  *  GNU General Public License for more details.
14  *
15  *  You should have received a copy of the GNU General Public License
16  *  along with XBMC; see the file COPYING.  If not, see
17  *  <http://www.gnu.org/licenses/>.
18  *
19  */
20
21 #include "GUILabelControl.h"
22 #include "utils/CharsetConverter.h"
23
24 using namespace std;
25
26 CGUILabelControl::CGUILabelControl(int parentID, int controlID, float posX, float posY, float width, float height, const CLabelInfo& labelInfo, bool wrapMultiLine, bool bHasPath)
27     : CGUIControl(parentID, controlID, posX, posY, width, height)
28     , m_label(posX, posY, width, height, labelInfo, wrapMultiLine ? CGUILabel::OVER_FLOW_WRAP : CGUILabel::OVER_FLOW_TRUNCATE)
29 {
30   m_bHasPath = bHasPath;
31   m_iCursorPos = 0;
32   m_bShowCursor = false;
33   m_dwCounter = 0;
34   ControlType = GUICONTROL_LABEL;
35   m_startHighlight = m_endHighlight = 0;
36   m_startSelection = m_endSelection = 0;
37   m_minWidth = 0;
38   if ((labelInfo.align & XBFONT_RIGHT) && m_width)
39     m_posX -= m_width;
40 }
41
42 CGUILabelControl::~CGUILabelControl(void)
43 {
44 }
45
46 void CGUILabelControl::ShowCursor(bool bShow)
47 {
48   m_bShowCursor = bShow;
49 }
50
51 void CGUILabelControl::SetCursorPos(int iPos)
52 {
53   CStdString labelUTF8 = m_infoLabel.GetLabel(m_parentID);
54   CStdStringW label;
55   g_charsetConverter.utf8ToW(labelUTF8, label);
56   if (iPos > (int)label.length()) iPos = label.length();
57   if (iPos < 0) iPos = 0;
58
59   if (m_iCursorPos != iPos)
60     MarkDirtyRegion();
61
62   m_iCursorPos = iPos;
63 }
64
65 void CGUILabelControl::SetInfo(const CGUIInfoLabel &infoLabel)
66 {
67   m_infoLabel = infoLabel;
68 }
69
70 bool CGUILabelControl::UpdateColors()
71 {
72   bool changed = CGUIControl::UpdateColors();
73   changed |= m_label.UpdateColors();
74
75   return changed;
76 }
77
78 typedef struct InsertPointInfo
79 {
80   typedef enum Type
81   {
82     HEAD = 0,
83     HEAD_AND_TAIL,
84     TAIL,
85   } Type;
86
87   bool operator < (const InsertPointInfo& info) const
88   {
89     if (pos < info.pos)
90       return true;
91     if (pos > info.pos)
92       return false;
93     // TAIL always ahead HEAD at same pos.
94     if (type > info.type)
95       return true;
96     if (type < info.type)
97       return false;
98     if (type == HEAD)
99     {
100       // HEADs in same pos, larger block's HEAD ahead
101       if (blockLength > info.blockLength)
102         return true;
103       if (blockLength < info.blockLength)
104         return false;
105       // same pos, type, blocklength, then lower priority HEAD ahead.
106       // then the higher priority block will nested in the lower priority one.
107       return priority < info.priority;
108     }
109     else if (type == TAIL)
110     {
111       // TAILs in same pos, smaill block's TAIL ahead
112       if (blockLength < info.blockLength)
113         return true;
114       if (blockLength > info.blockLength)
115         return false;
116       // same pos, type, blocklength, then higher priority TAIL ahead.
117       return priority > info.priority;
118     }
119     else
120       // order type HEAD_AND_TAIL by priority, higher ahead.
121       return priority > info.priority;
122   }
123
124   int pos;
125   Type type;
126   int blockLength;
127   int priority; // only used when same pos, same type, same blocklength
128   CStdStringW text;
129 } InsertPointInfo;
130
131 void CGUILabelControl::UpdateInfo(const CGUIListItem *item)
132 {
133   CStdString label(m_infoLabel.GetLabel(m_parentID));
134
135   bool changed = false;
136   if (m_startHighlight < m_endHighlight || m_startSelection < m_endSelection || m_bShowCursor)
137   {
138     CStdStringW utf16;
139     g_charsetConverter.utf8ToW(label, utf16);
140     vecText text; text.reserve(utf16.size()+1);
141     vecColors colors;
142     colors.push_back(m_label.GetLabelInfo().textColor);
143     colors.push_back(m_label.GetLabelInfo().disabledColor);
144     color_t select = m_label.GetLabelInfo().selectedColor;
145     if (!select)
146       select = 0xFFFF0000;
147     colors.push_back(select);
148     colors.push_back(0xFF000000);
149     for (unsigned int i = 0; i < utf16.size(); i++)
150     {
151       unsigned int ch = utf16[i];
152       if ((m_startSelection < m_endSelection) && (m_startSelection <= i && i < m_endSelection))
153         ch |= (2 << 16);
154       else if ((m_startHighlight < m_endHighlight) && (i < m_startHighlight || i >= m_endHighlight))
155         ch |= (1 << 16);
156       text.push_back(ch);
157     }
158     if (m_bShowCursor && m_iCursorPos >= 0 && (unsigned int)m_iCursorPos <= utf16.size())
159     {
160       unsigned int ch = L'|';
161       if ((++m_dwCounter % 50) <= 25)
162         ch |= (3 << 16);
163       text.insert(text.begin() + m_iCursorPos, ch);
164     }
165     changed |= m_label.SetMaxRect(m_posX, m_posY, GetWidth(), m_height);
166     changed |= m_label.SetStyledText(text, colors);
167   }
168   else
169   {
170     if (m_bHasPath)
171       label = ShortenPath(label);
172
173     changed |= m_label.SetMaxRect(m_posX, m_posY, GetWidth(), m_height);
174     changed |= m_label.SetText(label);
175   }
176   if (changed)
177     MarkDirtyRegion();
178 }
179
180 void CGUILabelControl::Process(unsigned int currentTime, CDirtyRegionList &dirtyregions)
181 {
182   bool changed = false;
183
184   changed |= m_label.SetColor(IsDisabled() ? CGUILabel::COLOR_DISABLED : CGUILabel::COLOR_TEXT);
185   changed |= m_label.SetMaxRect(m_posX, m_posY, GetWidth(), m_height);
186   changed |= m_label.Process(currentTime);
187
188   if (changed)
189     MarkDirtyRegion();
190
191   CGUIControl::Process(currentTime, dirtyregions);
192 }
193
194 CRect CGUILabelControl::CalcRenderRegion() const
195 {
196   return m_label.GetRenderRect();
197 }
198
199 void CGUILabelControl::Render()
200 {
201   m_label.Render();
202   CGUIControl::Render();
203 }
204
205
206 bool CGUILabelControl::CanFocus() const
207 {
208   return false;
209 }
210
211 void CGUILabelControl::SetLabel(const string &strLabel)
212 {
213   m_infoLabel.SetLabel(strLabel, "", GetParentID());
214   if (m_iCursorPos > (int)strLabel.size())
215     m_iCursorPos = strLabel.size();
216
217   SetInvalid();
218 }
219
220 void CGUILabelControl::SetWidthControl(float minWidth, bool bScroll)
221 {
222   if (m_label.SetScrolling(bScroll) || m_minWidth != minWidth)
223     MarkDirtyRegion();
224
225   m_minWidth = minWidth;
226 }
227
228 void CGUILabelControl::SetAlignment(uint32_t align)
229 {
230   if (m_label.GetLabelInfo().align != align)
231   {
232     MarkDirtyRegion();
233     m_label.GetLabelInfo().align = align;
234   }
235 }
236
237 #define CLAMP(x, low, high)  (((x) > (high)) ? (high) : (((x) < (low)) ? (low) : (x)))
238
239 float CGUILabelControl::GetWidth() const
240 {
241   if (m_minWidth && m_minWidth != m_width)
242   {
243     float maxWidth = m_width ? m_width : m_label.GetTextWidth();
244     return CLAMP(m_label.GetTextWidth(), m_minWidth, maxWidth);
245   }
246   return m_width;
247 }
248
249 void CGUILabelControl::SetWidth(float width)
250 {
251   m_width = width;
252   m_label.SetMaxRect(m_posX, m_posY, m_width, m_height);
253   CGUIControl::SetWidth(m_width);
254 }
255
256 bool CGUILabelControl::OnMessage(CGUIMessage& message)
257 {
258   if ( message.GetControlId() == GetID() )
259   {
260     if (message.GetMessage() == GUI_MSG_LABEL_SET)
261     {
262       SetLabel(message.GetLabel());
263       return true;
264     }
265   }
266
267   return CGUIControl::OnMessage(message);
268 }
269
270 CStdString CGUILabelControl::ShortenPath(const CStdString &path)
271 {
272   if (m_width == 0 || path.IsEmpty())
273     return path;
274
275   char cDelim = '\0';
276   size_t nPos;
277
278   nPos = path.find_last_of( '\\' );
279   if ( nPos != std::string::npos )
280     cDelim = '\\';
281   else
282   {
283     nPos = path.find_last_of( '/' );
284     if ( nPos != std::string::npos )
285       cDelim = '/';
286   }
287   if ( cDelim == '\0' )
288     return path;
289
290   CStdString workPath(path);
291   // remove trailing slashes
292   if (workPath.size() > 3)
293     if (workPath.Right(3).Compare("://") != 0 && workPath.Right(2).Compare(":\\") != 0)
294       if (nPos == workPath.size() - 1)
295       {
296         workPath.erase(workPath.size() - 1);
297         nPos = workPath.find_last_of( cDelim );
298       }
299
300   if (m_label.SetText(workPath))
301     MarkDirtyRegion();
302
303   float textWidth = m_label.GetTextWidth();
304
305   while ( textWidth > m_width )
306   {
307     size_t nGreaterDelim = workPath.find_last_of( cDelim, nPos );
308     if (nGreaterDelim == std::string::npos)
309       break;
310
311     nPos = workPath.find_last_of( cDelim, nGreaterDelim - 1 );
312     if ( nPos == std::string::npos )
313       break;
314
315     workPath.replace( nPos + 1, nGreaterDelim - nPos - 1, "..." );
316
317     if (m_label.SetText(workPath))
318       MarkDirtyRegion();
319
320     textWidth = m_label.GetTextWidth();
321   }
322   return workPath;
323 }
324
325 void CGUILabelControl::SetHighlight(unsigned int start, unsigned int end)
326 {
327   m_startHighlight = start;
328   m_endHighlight = end;
329 }
330
331 void CGUILabelControl::SetSelection(unsigned int start, unsigned int end)
332 {
333   m_startSelection = start;
334   m_endSelection = end;
335 }
336
337 CStdString CGUILabelControl::GetDescription() const
338 {
339   return m_infoLabel.GetLabel(m_parentID);
340 }