[guilib] fix labelcontrols with auto width always being marked as dirty if they speci...
[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, GetMaxWidth(), 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, GetMaxWidth(), 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, GetMaxWidth(), 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     return CLAMP(m_label.GetTextWidth(), m_minWidth, GetMaxWidth());
248   return m_width;
249 }
250
251 void CGUILabelControl::SetWidth(float width)
252 {
253   m_width = width;
254   m_label.SetMaxRect(m_posX, m_posY, m_width, m_height);
255   CGUIControl::SetWidth(m_width);
256 }
257
258 bool CGUILabelControl::OnMessage(CGUIMessage& message)
259 {
260   if ( message.GetControlId() == GetID() )
261   {
262     if (message.GetMessage() == GUI_MSG_LABEL_SET)
263     {
264       SetLabel(message.GetLabel());
265       return true;
266     }
267   }
268
269   return CGUIControl::OnMessage(message);
270 }
271
272 CStdString CGUILabelControl::ShortenPath(const CStdString &path)
273 {
274   if (m_width == 0 || path.empty())
275     return path;
276
277   char cDelim = '\0';
278   size_t nPos;
279
280   nPos = path.find_last_of( '\\' );
281   if ( nPos != std::string::npos )
282     cDelim = '\\';
283   else
284   {
285     nPos = path.find_last_of( '/' );
286     if ( nPos != std::string::npos )
287       cDelim = '/';
288   }
289   if ( cDelim == '\0' )
290     return path;
291
292   CStdString workPath(path);
293   // remove trailing slashes
294   if (workPath.size() > 3)
295     if (!StringUtils::EndsWith(workPath, "://") &&
296         !StringUtils::EndsWith(workPath, ":\\"))
297       if (nPos == workPath.size() - 1)
298       {
299         workPath.erase(workPath.size() - 1);
300         nPos = workPath.find_last_of( cDelim );
301       }
302
303   if (m_label.SetText(workPath))
304     MarkDirtyRegion();
305
306   float textWidth = m_label.GetTextWidth();
307
308   while ( textWidth > m_width )
309   {
310     size_t nGreaterDelim = workPath.find_last_of( cDelim, nPos );
311     if (nGreaterDelim == std::string::npos)
312       break;
313
314     nPos = workPath.find_last_of( cDelim, nGreaterDelim - 1 );
315     if ( nPos == std::string::npos )
316       break;
317
318     workPath.replace( nPos + 1, nGreaterDelim - nPos - 1, "..." );
319
320     if (m_label.SetText(workPath))
321       MarkDirtyRegion();
322
323     textWidth = m_label.GetTextWidth();
324   }
325   return workPath;
326 }
327
328 void CGUILabelControl::SetHighlight(unsigned int start, unsigned int end)
329 {
330   m_startHighlight = start;
331   m_endHighlight = end;
332 }
333
334 void CGUILabelControl::SetSelection(unsigned int start, unsigned int end)
335 {
336   m_startSelection = start;
337   m_endSelection = end;
338 }
339
340 CStdString CGUILabelControl::GetDescription() const
341 {
342   return m_infoLabel.GetLabel(m_parentID);
343 }