Merge pull request #3141 from stupid-boy/GUIControl
[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   // NOTE: this optimization handles fixed labels only (i.e. not info labels).
214   // One way it might be extended to all labels would be for GUIInfoLabel ( or here )
215   // to store the label prior to parsing, and then compare that against what you're setting.
216   if (m_infoLabel.GetLabel(GetParentID(), false) != strLabel)
217   {
218     m_infoLabel.SetLabel(strLabel, "", GetParentID());
219     if (m_iCursorPos > (int)strLabel.size())
220       m_iCursorPos = strLabel.size();
221
222     SetInvalid();
223   }
224 }
225
226 void CGUILabelControl::SetWidthControl(float minWidth, bool bScroll)
227 {
228   if (m_label.SetScrolling(bScroll) || m_minWidth != minWidth)
229     MarkDirtyRegion();
230
231   m_minWidth = minWidth;
232 }
233
234 void CGUILabelControl::SetAlignment(uint32_t align)
235 {
236   if (m_label.GetLabelInfo().align != align)
237   {
238     MarkDirtyRegion();
239     m_label.GetLabelInfo().align = align;
240   }
241 }
242
243 #define CLAMP(x, low, high)  (((x) > (high)) ? (high) : (((x) < (low)) ? (low) : (x)))
244
245 float CGUILabelControl::GetWidth() const
246 {
247   if (m_minWidth && m_minWidth != m_width)
248   {
249     float maxWidth = m_width ? m_width : m_label.GetTextWidth();
250     return CLAMP(m_label.GetTextWidth(), m_minWidth, maxWidth);
251   }
252   return m_width;
253 }
254
255 void CGUILabelControl::SetWidth(float width)
256 {
257   m_width = width;
258   m_label.SetMaxRect(m_posX, m_posY, m_width, m_height);
259   CGUIControl::SetWidth(m_width);
260 }
261
262 bool CGUILabelControl::OnMessage(CGUIMessage& message)
263 {
264   if ( message.GetControlId() == GetID() )
265   {
266     if (message.GetMessage() == GUI_MSG_LABEL_SET)
267     {
268       SetLabel(message.GetLabel());
269       return true;
270     }
271   }
272
273   return CGUIControl::OnMessage(message);
274 }
275
276 CStdString CGUILabelControl::ShortenPath(const CStdString &path)
277 {
278   if (m_width == 0 || path.IsEmpty())
279     return path;
280
281   char cDelim = '\0';
282   size_t nPos;
283
284   nPos = path.find_last_of( '\\' );
285   if ( nPos != std::string::npos )
286     cDelim = '\\';
287   else
288   {
289     nPos = path.find_last_of( '/' );
290     if ( nPos != std::string::npos )
291       cDelim = '/';
292   }
293   if ( cDelim == '\0' )
294     return path;
295
296   CStdString workPath(path);
297   // remove trailing slashes
298   if (workPath.size() > 3)
299     if (workPath.Right(3).Compare("://") != 0 && workPath.Right(2).Compare(":\\") != 0)
300       if (nPos == workPath.size() - 1)
301       {
302         workPath.erase(workPath.size() - 1);
303         nPos = workPath.find_last_of( cDelim );
304       }
305
306   if (m_label.SetText(workPath))
307     MarkDirtyRegion();
308
309   float textWidth = m_label.GetTextWidth();
310
311   while ( textWidth > m_width )
312   {
313     size_t nGreaterDelim = workPath.find_last_of( cDelim, nPos );
314     if (nGreaterDelim == std::string::npos)
315       break;
316
317     nPos = workPath.find_last_of( cDelim, nGreaterDelim - 1 );
318     if ( nPos == std::string::npos )
319       break;
320
321     workPath.replace( nPos + 1, nGreaterDelim - nPos - 1, "..." );
322
323     if (m_label.SetText(workPath))
324       MarkDirtyRegion();
325
326     textWidth = m_label.GetTextWidth();
327   }
328   return workPath;
329 }
330
331 void CGUILabelControl::SetHighlight(unsigned int start, unsigned int end)
332 {
333   m_startHighlight = start;
334   m_endHighlight = end;
335 }
336
337 void CGUILabelControl::SetSelection(unsigned int start, unsigned int end)
338 {
339   m_startSelection = start;
340   m_endSelection = end;
341 }
342
343 CStdString CGUILabelControl::GetDescription() const
344 {
345   return m_infoLabel.GetLabel(m_parentID);
346 }