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