2 * Copyright (C) 2005-2013 Team XBMC
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)
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.
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/>.
21 #include "GUIMultiSelectText.h"
22 #include "GUIWindowManager.h"
24 #include "utils/log.h"
25 #include "utils/StringUtils.h"
29 CGUIMultiSelectTextControl::CSelectableString::CSelectableString(CGUIFont *font, const CStdString &text, bool selectable, const CStdString &clickAction)
32 m_selectable = selectable;
33 m_clickAction = clickAction;
34 StringUtils::TrimLeft(m_clickAction, " =");
35 StringUtils::TrimRight(m_clickAction);
38 m_text.GetTextExtent(m_length, height);
41 CGUIMultiSelectTextControl::CGUIMultiSelectTextControl(int parentID, int controlID, float posX, float posY, float width, float height, const CTextureInfo& textureFocus, const CTextureInfo& textureNoFocus, const CLabelInfo& labelInfo, const CGUIInfoLabel &content)
42 : CGUIControl(parentID, controlID, posX, posY, width, height)
45 , m_button(parentID, controlID, posX, posY, width, height, textureFocus, textureNoFocus, labelInfo)
54 m_label.align &= ~3; // we currently ignore all x alignment
57 CGUIMultiSelectTextControl::~CGUIMultiSelectTextControl(void)
61 bool CGUIMultiSelectTextControl::UpdateColors()
63 bool changed = CGUIControl::UpdateColors();
64 changed |= m_label.UpdateColors();
69 void CGUIMultiSelectTextControl::Process(unsigned int currentTime, CDirtyRegionList &dirtyregions)
71 m_renderTime = currentTime;
73 // check our selected item is in range
74 unsigned int numSelectable = GetNumSelectable();
77 else if (m_selectedItem >= numSelectable)
78 m_selectedItem = numSelectable - 1;
80 // and validate our offset
81 if (m_offset + m_width > m_totalWidth)
82 m_offset = m_totalWidth - m_width;
83 if (m_offset < 0) m_offset = 0;
86 m_scrollOffset += m_scrollSpeed * (m_renderTime - m_scrollLastTime);
87 if ((m_scrollSpeed < 0 && m_scrollOffset < m_offset) ||
88 (m_scrollSpeed > 0 && m_scrollOffset > m_offset))
90 m_scrollOffset = m_offset;
93 m_scrollLastTime = m_renderTime;
95 g_graphicsContext.SetOrigin(-m_scrollOffset, 0);
97 // process the buttons
98 for (unsigned int i = 0; i < m_buttons.size(); i++)
100 m_buttons[i].SetFocus(HasFocus() && i == m_selectedItem);
101 m_buttons[i].DoProcess(currentTime, dirtyregions);
104 g_graphicsContext.RestoreOrigin();
106 CGUIControl::Process(currentTime, dirtyregions);
109 void CGUIMultiSelectTextControl::Render()
111 // clip and set our scrolling origin
112 bool clip(m_width < m_totalWidth);
115 if (!g_graphicsContext.SetClipRegion(m_posX, m_posY, m_width, m_height))
116 return; // nothing to render??
118 g_graphicsContext.SetOrigin(-m_scrollOffset, 0);
120 // render the buttons
121 for (unsigned int i = 0; i < m_buttons.size(); i++)
122 m_buttons[i].DoRender();
124 // position the text - we center vertically if applicable, and use the offsets.
125 // all x-alignment is ignored for now (see constructor)
127 float posY = m_posY + m_label.offsetY;
128 if (m_label.align & XBFONT_CENTER_Y)
129 posY = m_posY + m_height * 0.5f;
131 if (m_items.size() && m_items[0].m_selectable)
132 posX += m_label.offsetX;
135 unsigned int num_selectable = 0;
136 for (unsigned int i = 0; i < m_items.size(); i++)
138 CSelectableString &string = m_items[i];
139 if (IsDisabled()) // all text is rendered with disabled color
140 string.m_text.Render(posX, posY, 0, m_label.disabledColor, m_label.shadowColor, m_label.align, 0, true);
141 else if (HasFocus() && string.m_selectable && num_selectable == m_selectedItem) // text is rendered with focusedcolor
142 string.m_text.Render(posX, posY, 0, m_label.focusedColor, m_label.shadowColor, m_label.align, 0);
143 else // text is rendered with textcolor
144 string.m_text.Render(posX, posY, 0, m_label.textColor, m_label.shadowColor, m_label.align, 0);
145 posX += string.m_length;
146 if (string.m_selectable)
150 g_graphicsContext.RestoreOrigin();
152 g_graphicsContext.RestoreClipRegion();
154 CGUIControl::Render();
157 void CGUIMultiSelectTextControl::UpdateInfo(const CGUIListItem *item)
159 if (m_info.IsEmpty())
160 return; // nothing to do
163 UpdateText(m_info.GetItemLabel(item));
165 UpdateText(m_info.GetLabel(m_parentID));
168 bool CGUIMultiSelectTextControl::OnAction(const CAction &action)
170 if (action.GetID() == ACTION_SELECT_ITEM)
172 // item is clicked - see if we have a clickaction
173 CStdString clickAction;
174 unsigned int selected = 0;
175 for (unsigned int i = 0; i < m_items.size(); i++)
177 if (m_items[i].m_selectable)
179 if (m_selectedItem == selected)
180 clickAction = m_items[i].m_clickAction;
184 if (!clickAction.empty())
185 { // have a click action -> perform it
186 CGUIMessage message(GUI_MSG_EXECUTE, m_controlID, m_parentID);
187 message.SetStringParam(clickAction);
188 g_windowManager.SendMessage(message);
191 { // no click action, just send a message to the window
192 CGUIMessage msg(GUI_MSG_CLICKED, m_controlID, m_parentID, m_selectedItem);
193 SendWindowMessage(msg);
197 return CGUIControl::OnAction(action);
200 void CGUIMultiSelectTextControl::OnLeft()
204 CGUIControl::OnLeft();
207 void CGUIMultiSelectTextControl::OnRight()
211 CGUIControl::OnRight();
214 // movement functions (callable from lists)
215 bool CGUIMultiSelectTextControl::MoveLeft()
217 if (m_selectedItem > 0)
218 ScrollToItem(m_selectedItem - 1);
219 else if (GetNumSelectable() && m_actionLeft.GetNavigation() && m_actionLeft.GetNavigation() == m_controlID)
220 ScrollToItem(GetNumSelectable() - 1);
226 bool CGUIMultiSelectTextControl::MoveRight()
228 if (GetNumSelectable() && m_selectedItem < GetNumSelectable() - 1)
229 ScrollToItem(m_selectedItem + 1);
230 else if (m_actionRight.GetNavigation() && m_actionRight.GetNavigation() == m_controlID)
237 void CGUIMultiSelectTextControl::SelectItemFromPoint(const CPoint &point)
239 int item = GetItemFromPoint(point);
249 bool CGUIMultiSelectTextControl::HitTest(const CPoint &point) const
251 return (GetItemFromPoint(point) != -1);
254 bool CGUIMultiSelectTextControl::OnMouseOver(const CPoint &point)
256 ScrollToItem(GetItemFromPoint(point));
257 return CGUIControl::OnMouseOver(point);
260 EVENT_RESULT CGUIMultiSelectTextControl::OnMouseEvent(const CPoint &point, const CMouseEvent &event)
262 if (event.m_id == ACTION_MOUSE_LEFT_CLICK)
264 m_selectedItem = GetItemFromPoint(point);
265 OnAction(CAction(ACTION_SELECT_ITEM));
266 return EVENT_RESULT_HANDLED;
268 return EVENT_RESULT_UNHANDLED;
271 int CGUIMultiSelectTextControl::GetItemFromPoint(const CPoint &point) const
273 if (!m_label.font) return -1;
275 unsigned int selectable = 0;
276 for (unsigned int i = 0; i < m_items.size(); i++)
278 const CSelectableString &string = m_items[i];
279 if (string.m_selectable)
281 CRect rect(posX, m_posY, posX + string.m_length, m_posY + m_height);
282 if (rect.PtInRect(point))
286 posX += string.m_length;
291 void CGUIMultiSelectTextControl::UpdateText(const CStdString &text)
293 if (text == m_oldText)
298 // parse our text into clickable blocks
299 // format is [ONCLICK <action>] [/ONCLICK]
300 size_t startClickable = text.find("[ONCLICK");
301 size_t startUnclickable = 0;
303 // add the first unclickable block
304 if (startClickable != CStdString::npos)
305 AddString(text.substr(startUnclickable, startClickable - startUnclickable), false);
307 AddString(text.substr(startUnclickable), false);
308 while (startClickable != CStdString::npos)
310 // grep out the action and the end of the string
311 size_t endAction = text.find(']', startClickable + 8);
312 size_t endClickable = text.find("[/ONCLICK]", startClickable + 8);
313 if (endAction != std::string::npos && endClickable != std::string::npos)
314 { // success - add the string, and move the start of our next unclickable portion along
315 AddString(text.substr(endAction + 1, endClickable - endAction - 1), true, text.substr(startClickable + 8, endAction - startClickable - 8));
316 startUnclickable = endClickable + 10;
320 CLog::Log(LOGERROR, "Invalid multiselect string %s", text.c_str());
323 startClickable = text.find("[ONCLICK", startUnclickable);
324 // add the unclickable portion
325 if (startClickable != CStdString::npos)
326 AddString(text.substr(startUnclickable, startClickable - startUnclickable), false);
328 AddString(text.substr(startUnclickable), false);
333 // finally, position our buttons
337 void CGUIMultiSelectTextControl::AddString(const CStdString &text, bool selectable, const CStdString &clickAction)
340 m_items.push_back(CSelectableString(m_label.font, text, selectable, clickAction));
343 void CGUIMultiSelectTextControl::PositionButtons()
349 if (m_items.size() && m_items.front().m_selectable)
350 m_totalWidth += m_label.offsetX;
352 for (unsigned int i = 0; i < m_items.size(); i++)
354 const CSelectableString &text = m_items[i];
355 if (text.m_selectable)
357 CGUIButtonControl button(m_button);
358 button.SetPosition(m_posX + m_totalWidth - m_label.offsetX, m_posY);
359 button.SetWidth(text.m_length + 2 * m_label.offsetX);
360 m_buttons.push_back(button);
362 m_totalWidth += text.m_length;
365 if (m_items.size() && m_items.back().m_selectable)
366 m_totalWidth += m_label.offsetX;
369 CStdString CGUIMultiSelectTextControl::GetDescription() const
371 // We currently just return the entire string - should we bother returning the
372 // particular subitems of this?
373 CStdString strLabel(m_info.GetLabel(m_parentID));
377 unsigned int CGUIMultiSelectTextControl::GetNumSelectable() const
379 unsigned int selectable = 0;
380 for (unsigned int i = 0; i < m_items.size(); i++)
381 if (m_items[i].m_selectable)
386 unsigned int CGUIMultiSelectTextControl::GetFocusedItem() const
388 if (GetNumSelectable())
389 return m_selectedItem + 1;
393 void CGUIMultiSelectTextControl::SetFocusedItem(unsigned int item)
397 ScrollToItem(item - 1);
400 bool CGUIMultiSelectTextControl::CanFocus() const
402 if (!GetNumSelectable()) return false;
403 return CGUIControl::CanFocus();
406 void CGUIMultiSelectTextControl::SetFocus(bool focus)
408 for (unsigned int i = 0; i < m_buttons.size(); i++)
409 m_buttons[i].SetFocus(focus);
410 CGUIControl::SetFocus(focus);
413 // overrides to allow anims to translate down to the focus image
414 void CGUIMultiSelectTextControl::SetAnimations(const vector<CAnimation> &animations)
416 // send any focus animations down to the focus image only
417 m_animations.clear();
418 vector<CAnimation> focusAnims;
419 for (unsigned int i = 0; i < animations.size(); i++)
421 const CAnimation &anim = animations[i];
422 if (anim.GetType() == ANIM_TYPE_FOCUS)
423 focusAnims.push_back(anim);
425 m_animations.push_back(anim);
427 m_button.SetAnimations(focusAnims);
430 void CGUIMultiSelectTextControl::ScrollToItem(unsigned int item)
432 static const unsigned int time_to_scroll = 200;
433 if (item >= m_buttons.size()) return;
435 const CGUIButtonControl &button = m_buttons[item];
436 float left = button.GetXPosition();
437 float right = left + button.GetWidth();
438 // make sure that we scroll so that this item is on screen
439 m_scrollOffset = m_offset;
440 if (left < m_posX + m_offset)
441 m_offset = left - m_posX;
442 else if (right > m_posX + m_offset + m_width)
443 m_offset = right - m_width - m_posX;
444 m_scrollSpeed = (m_offset - m_scrollOffset) / time_to_scroll;
445 m_selectedItem = item;