2b798380d38f7e9201ed9e753b39a497f3c9d7bd
[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 #include "utils/StringUtils.h"
24
25 using namespace std;
26
27 CGUILabelControl::CGUILabelControl(int parentID, int controlID, float posX, float posY, float width, float height, const CLabelInfo& labelInfo, bool wrapMultiLine, bool bHasPath)
28     : CGUIControl(parentID, controlID, posX, posY, width, height)
29     , m_label(posX, posY, width, height, labelInfo, wrapMultiLine ? CGUILabel::OVER_FLOW_WRAP : CGUILabel::OVER_FLOW_TRUNCATE)
30 {
31   m_bHasPath = bHasPath;
32   m_iCursorPos = 0;
33   m_bShowCursor = false;
34   m_dwCounter = 0;
35   ControlType = GUICONTROL_LABEL;
36   m_startHighlight = m_endHighlight = 0;
37   m_startSelection = m_endSelection = 0;
38   m_minWidth = 0;
39 }
40
41 CGUILabelControl::~CGUILabelControl(void)
42 {
43 }
44
45 void CGUILabelControl::ShowCursor(bool bShow)
46 {
47   m_bShowCursor = bShow;
48 }
49
50 void CGUILabelControl::SetCursorPos(int iPos)
51 {
52   CStdString labelUTF8 = m_infoLabel.GetLabel(m_parentID);
53   CStdStringW label;
54   g_charsetConverter.utf8ToW(labelUTF8, label);
55   if (iPos > (int)label.length()) iPos = label.length();
56   if (iPos < 0) iPos = 0;
57
58   if (m_iCursorPos != iPos)
59     MarkDirtyRegion();
60
61   m_iCursorPos = iPos;
62 }
63
64 void CGUILabelControl::SetInfo(const CGUIInfoLabel &infoLabel)
65 {
66   m_infoLabel = infoLabel;
67 }
68
69 bool CGUILabelControl::UpdateColors()
70 {
71   bool changed = CGUIControl::UpdateColors();
72   changed |= m_label.UpdateColors();
73
74   return changed;
75 }
76
77 typedef struct InsertPointInfo
78 {
79   typedef enum Type
80   {
81     HEAD = 0,
82     HEAD_AND_TAIL,
83     TAIL,
84   } Type;
85
86   bool operator < (const InsertPointInfo& info) const
87   {
88     if (pos < info.pos)
89       return true;
90     if (pos > info.pos)
91       return false;
92     // TAIL always ahead HEAD at same pos.
93     if (type > info.type)
94       return true;
95     if (type < info.type)
96       return false;
97     if (type == HEAD)
98     {
99       // HEADs in same pos, larger block's HEAD ahead
100       if (blockLength > info.blockLength)
101         return true;
102       if (blockLength < info.blockLength)
103         return false;
104       // same pos, type, blocklength, then lower priority HEAD ahead.
105       // then the higher priority block will nested in the lower priority one.
106       return priority < info.priority;
107     }
108     else if (type == TAIL)
109     {
110       // TAILs in same pos, smaill block's TAIL ahead
111       if (blockLength < info.blockLength)
112         return true;
113       if (blockLength > info.blockLength)
114         return false;
115       // same pos, type, blocklength, then higher priority TAIL ahead.
116       return priority > info.priority;
117     }
118     else
119       // order type HEAD_AND_TAIL by priority, higher ahead.
120       return priority > info.priority;
121   }
122
123   int pos;
124   Type type;
125   int blockLength;
126   int priority; // only used when same pos, same type, same blocklength
127   CStdStringW text;
128 } InsertPointInfo;
129
130 void CGUILabelControl::UpdateInfo(const CGUIListItem *item)
131 {
132   CStdString label(m_infoLabel.GetLabel(m_parentID));
133
134   bool changed = false;
135   if (m_startHighlight < m_endHighlight || m_startSelection < m_endSelection || m_bShowCursor)
136   {
137     CStdStringW utf16;
138     g_charsetConverter.utf8ToW(label, utf16);
139     vecText text; text.reserve(utf16.size()+1);
140     vecColors colors;
141     colors.push_back(m_label.GetLabelInfo().textColor);
142     colors.push_back(m_label.GetLabelInfo().disabledColor);
143     color_t select = m_label.GetLabelInfo().selectedColor;
144     if (!select)
145       select = 0xFFFF0000;
146     colors.push_back(select);
147     colors.push_back(0xFF000000);
148     for (unsigned int i = 0; i < utf16.size(); i++)
149     {
150       unsigned int ch = utf16[i];
151       if ((m_startSelection < m_endSelection) && (m_startSelection <= i && i < m_endSelection))
152         ch |= (2 << 16);
153       else if ((m_startHighlight < m_endHighlight) && (i < m_startHighlight || i >= m_endHighlight))
154         ch |= (1 << 16);
155       text.push_back(ch);
156     }
157     if (m_bShowCursor && m_iCursorPos >= 0 && (unsigned int)m_iCursorPos <= utf16.size())
158     {
159       unsigned int ch = L'|';
160       if ((++m_dwCounter % 50) <= 25)
161         ch |= (3 << 16);
162       text.insert(text.begin() + m_iCursorPos, ch);
163     }
164     changed |= m_label.SetMaxRect(m_posX, m_posY, GetWidth(), m_height);
165     changed |= m_label.SetStyledText(text, colors);
166   }
167   else
168   {
169     if (m_bHasPath)
170       label = ShortenPath(label);
171
172     changed |= m_label.SetMaxRect(m_posX, m_posY, GetWidth(), m_height);
173     changed |= m_label.SetText(label);
174   }
175   if (changed)
176     MarkDirtyRegion();
177 }
178
179 void CGUILabelControl::Process(unsigned int currentTime, CDirtyRegionList &dirtyregions)
180 {
181   bool changed = false;
182
183   changed |= m_label.SetColor(IsDisabled() ? CGUILabel::COLOR_DISABLED : CGUILabel::COLOR_TEXT);
184   changed |= m_label.SetMaxRect(m_posX, m_posY, GetWidth(), m_height);
185   changed |= m_label.Process(currentTime);
186
187   if (changed)
188     MarkDirtyRegion();
189
190   CGUIControl::Process(currentTime, dirtyregions);
191 }
192
193 CRect CGUILabelControl::CalcRenderRegion() const
194 {
195   return m_label.GetRenderRect();
196 }
197
198 void CGUILabelControl::Render()
199 {
200   m_label.Render();
201   CGUIControl::Render();
202 }
203
204
205 bool CGUILabelControl::CanFocus() const
206 {
207   return false;
208 }
209
210 void CGUILabelControl::SetLabel(const string &strLabel)
211 {
212   // NOTE: this optimization handles fixed labels only (i.e. not info labels).
213   // One way it might be extended to all labels would be for GUIInfoLabel ( or here )
214   // to store the label prior to parsing, and then compare that against what you're setting.
215   if (m_infoLabel.GetLabel(GetParentID(), false) != strLabel)
216   {
217     m_infoLabel.SetLabel(strLabel, "", GetParentID());
218     if (m_iCursorPos > (int)strLabel.size())
219       m_iCursorPos = strLabel.size();
220
221     SetInvalid();
222   }
223 }
224
225 void CGUILabelControl::SetWidthControl(float minWidth, bool bScroll)
226 {
227   if (m_label.SetScrolling(bScroll) || m_minWidth != minWidth)
228     MarkDirtyRegion();
229
230   m_minWidth = minWidth;
231 }
232
233 void CGUILabelControl::SetAlignment(uint32_t align)
234 {
235   if (m_label.GetLabelInfo().align != align)
236   {
237     MarkDirtyRegion();
238     m_label.GetLabelInfo().align = align;
239   }
240 }
241
242 #define CLAMP(x, low, high)  (((x) > (high)) ? (high) : (((x) < (low)) ? (low) : (x)))
243
244 float CGUILabelControl::GetWidth() const
245 {
246   if (m_minWidth && m_minWidth != m_width)
247   {
248     float maxWidth = m_width ? m_width : m_label.GetTextWidth();
249     return CLAMP(m_label.GetTextWidth(), m_minWidth, maxWidth);
250   }
251   return m_width;
252 }
253
254 void CGUILabelControl::SetWidth(float width)
255 {
256   m_width = width;
257   m_label.SetMaxRect(m_posX, m_posY, m_width, m_height);
258   CGUIControl::SetWidth(m_width);
259 }
260
261 bool CGUILabelControl::OnMessage(CGUIMessage& message)
262 {
263   if ( message.GetControlId() == GetID() )
264   {
265     if (message.GetMessage() == GUI_MSG_LABEL_SET)
266     {
267       SetLabel(message.GetLabel());
268       return true;
269     }
270   }
271
272   return CGUIControl::OnMessage(message);
273 }
274
275 CStdString CGUILabelControl::ShortenPath(const CStdString &path)
276 {
277   if (m_width == 0 || path.empty())
278     return path;
279
280   char cDelim = '\0';
281   size_t nPos;
282
283   nPos = path.find_last_of( '\\' );
284   if ( nPos != std::string::npos )
285     cDelim = '\\';
286   else
287   {
288     nPos = path.find_last_of( '/' );
289     if ( nPos != std::string::npos )
290       cDelim = '/';
291   }
292   if ( cDelim == '\0' )
293     return path;
294
295   CStdString workPath(path);
296   // remove trailing slashes
297   if (workPath.size() > 3)
298     if (!StringUtils::EndsWith(workPath, "://") &&
299         !StringUtils::EndsWith(workPath, ":\\"))
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 }