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 "GUISliderControl.h"
22 #include "GUIInfoManager.h"
24 #include "utils/MathUtils.h"
25 #include "utils/StringUtils.h"
26 #include "GUIWindowManager.h"
28 static const SliderAction actions[] = {
29 {"seek", "PlayerControl(SeekPercentage(%2f))", PLAYER_PROGRESS, false},
30 {"volume", "SetVolume(%2f)", PLAYER_VOLUME, true}
33 CGUISliderControl::CGUISliderControl(int parentID, int controlID, float posX, float posY, float width, float height, const CTextureInfo& backGroundTexture, const CTextureInfo& nibTexture, const CTextureInfo& nibTextureFocus, int iType)
34 : CGUIControl(parentID, controlID, posX, posY, width, height)
35 , m_guiBackground(posX, posY, width, height, backGroundTexture)
36 , m_guiSelectorLower(posX, posY, width, height, nibTexture)
37 , m_guiSelectorUpper(posX, posY, width, height, nibTexture)
38 , m_guiSelectorLowerFocus(posX, posY, width, height, nibTextureFocus)
39 , m_guiSelectorUpperFocus(posX, posY, width, height, nibTextureFocus)
42 m_rangeSelection = false;
43 m_currentSelector = RangeSelectorLower; // use lower selector by default
44 m_percentValues[0] = 0;
45 m_percentValues[1] = 100;
52 m_intValues[0] = m_iStart;
53 m_intValues[1] = m_iEnd;
54 m_floatValues[0] = m_fStart;
55 m_floatValues[1] = m_fEnd;
56 ControlType = GUICONTROL_SLIDER;
62 CGUISliderControl::~CGUISliderControl(void)
66 void CGUISliderControl::Process(unsigned int currentTime, CDirtyRegionList &dirtyregions)
70 dirty |= m_guiBackground.SetPosition( m_posX, m_posY );
71 int infoCode = m_iInfoCode;
72 if (m_action && (!m_dragging || m_action->fireOnDrag))
73 infoCode = m_action->infoCode;
77 if (g_infoManager.GetInt(val, infoCode))
81 float fScaleY = m_height == 0 ? 1.0f : m_height / m_guiBackground.GetTextureHeight();
83 dirty |= m_guiBackground.SetHeight(m_height);
84 dirty |= m_guiBackground.SetWidth(m_width);
85 dirty |= m_guiBackground.Process(currentTime);
87 CGUITexture &nibLower = (m_bHasFocus && !IsDisabled() && m_currentSelector == RangeSelectorLower) ? m_guiSelectorLowerFocus : m_guiSelectorLower;
88 dirty |= ProcessSelector(nibLower, currentTime, fScaleY, RangeSelectorLower);
91 CGUITexture &nibUpper = (m_bHasFocus && !IsDisabled() && m_currentSelector == RangeSelectorUpper) ? m_guiSelectorUpperFocus : m_guiSelectorUpper;
92 dirty |= ProcessSelector(nibUpper, currentTime, fScaleY, RangeSelectorUpper);
98 CGUIControl::Process(currentTime, dirtyregions);
101 bool CGUISliderControl::ProcessSelector(CGUITexture &nib, unsigned int currentTime, float fScaleY, RangeSelector selector)
104 // we render the nib centered at the appropriate percentage, except where the nib
105 // would overflow the background image
106 dirty |= nib.SetHeight(nib.GetTextureHeight() * fScaleY);
107 dirty |= nib.SetWidth(nib.GetHeight() * 2);
108 CAspectRatio ratio(CAspectRatio::AR_KEEP);
109 ratio.align = ASPECT_ALIGN_LEFT | ASPECT_ALIGNY_CENTER;
110 dirty |= nib.SetAspectRatio(ratio);
111 dirty |= nib.Process(currentTime);
112 CRect rect = nib.GetRenderRect();
114 float offset = GetProportion(selector) * m_width - rect.Width() / 2;
115 if (offset > m_width - rect.Width())
116 offset = m_width - rect.Width();
119 dirty |= nib.SetPosition(m_guiBackground.GetXPosition() + offset, m_guiBackground.GetYPosition());
120 dirty |= nib.Process(currentTime); // need to process again as the position may have changed
125 void CGUISliderControl::Render()
127 m_guiBackground.Render();
128 CGUITexture &nibLower = (m_bHasFocus && !IsDisabled() && m_currentSelector == RangeSelectorLower) ? m_guiSelectorLowerFocus : m_guiSelectorLower;
130 if (m_rangeSelection)
132 CGUITexture &nibUpper = (m_bHasFocus && !IsDisabled() && m_currentSelector == RangeSelectorUpper) ? m_guiSelectorUpperFocus : m_guiSelectorUpper;
135 CGUIControl::Render();
138 bool CGUISliderControl::OnMessage(CGUIMessage& message)
140 if (message.GetControlId() == GetID() )
142 switch (message.GetMessage())
144 case GUI_MSG_ITEM_SELECT:
145 SetPercentage( (float)message.GetParam1() );
149 case GUI_MSG_LABEL_RESET:
151 SetPercentage(0, RangeSelectorLower);
152 SetPercentage(100, RangeSelectorUpper);
159 return CGUIControl::OnMessage(message);
162 bool CGUISliderControl::OnAction(const CAction &action)
164 switch ( action.GetID() )
166 case ACTION_MOVE_LEFT:
167 //case ACTION_OSD_SHOW_VALUE_MIN:
171 case ACTION_MOVE_RIGHT:
172 //case ACTION_OSD_SHOW_VALUE_PLUS:
176 case ACTION_SELECT_ITEM:
177 // switch between the two sliders
178 if (m_rangeSelection)
179 SwitchRangeSelector();
186 return CGUIControl::OnAction(action);
189 void CGUISliderControl::Move(int iNumSteps)
191 bool rangeSwap = false;
194 case SPIN_CONTROL_TYPE_FLOAT:
196 float &value = m_floatValues[m_currentSelector];
197 value += m_fInterval * iNumSteps;
198 if (value < m_fStart) value = m_fStart;
199 if (value > m_fEnd) value = m_fEnd;
200 if (m_floatValues[0] > m_floatValues[1])
202 float valueLower = m_floatValues[0];
203 m_floatValues[0] = m_floatValues[1];
204 m_floatValues[1] = valueLower;
210 case SPIN_CONTROL_TYPE_INT:
212 int &value = m_intValues[m_currentSelector];
213 value += m_iInterval * iNumSteps;
214 if (value < m_iStart) value = m_iStart;
215 if (value > m_iEnd) value = m_iEnd;
216 if (m_intValues[0] > m_intValues[1])
218 int valueLower = m_intValues[0];
219 m_intValues[0] = m_intValues[1];
220 m_intValues[1] = valueLower;
228 float &value = m_percentValues[m_currentSelector];
229 value += m_iInterval * iNumSteps;
230 if (value < 0) value = 0;
231 if (value > 100) value = 100;
232 if (m_percentValues[0] > m_percentValues[1])
234 float valueLower = m_percentValues[0];
235 m_percentValues[0] = m_percentValues[1];
236 m_percentValues[1] = valueLower;
244 SwitchRangeSelector();
249 void CGUISliderControl::SendClick()
251 float percent = 100*GetProportion();
252 SEND_CLICK_MESSAGE(GetID(), GetParentID(), MathUtils::round_int(percent));
253 if (m_action && (!m_dragging || m_action->fireOnDrag))
255 CStdString action = StringUtils::Format(m_action->formatString, percent);
256 CGUIMessage message(GUI_MSG_EXECUTE, m_controlID, m_parentID);
257 message.SetStringParam(action);
258 g_windowManager.SendMessage(message);
262 void CGUISliderControl::SetRangeSelection(bool rangeSelection)
264 if (m_rangeSelection == rangeSelection)
267 m_rangeSelection = rangeSelection;
268 SetRangeSelector(RangeSelectorLower);
272 void CGUISliderControl::SetRangeSelector(RangeSelector selector)
274 if (m_currentSelector == selector)
277 m_currentSelector = selector;
281 void CGUISliderControl::SwitchRangeSelector()
283 if (m_currentSelector == RangeSelectorLower)
284 SetRangeSelector(RangeSelectorUpper);
286 SetRangeSelector(RangeSelectorLower);
289 void CGUISliderControl::SetPercentage(float percent, RangeSelector selector /* = RangeSelectorLower */, bool updateCurrent /* = false */)
291 if (percent > 100) percent = 100;
292 else if (percent < 0) percent = 0;
294 float percentLower = selector == RangeSelectorLower ? percent : m_percentValues[0];
295 float percentUpper = selector == RangeSelectorUpper ? percent : m_percentValues[1];
297 if (!m_rangeSelection || percentLower <= percentUpper)
299 m_percentValues[0] = percentLower;
300 m_percentValues[1] = percentUpper;
302 m_currentSelector = selector;
306 m_percentValues[0] = percentUpper;
307 m_percentValues[1] = percentLower;
309 m_currentSelector = (selector == RangeSelectorLower ? RangeSelectorUpper : RangeSelectorLower);
313 float CGUISliderControl::GetPercentage(RangeSelector selector /* = RangeSelectorLower */) const
315 return m_percentValues[selector];
318 void CGUISliderControl::SetIntValue(int iValue, RangeSelector selector /* = RangeSelectorLower */, bool updateCurrent /* = false */)
320 if (m_iType == SPIN_CONTROL_TYPE_FLOAT)
321 SetFloatValue((float)iValue);
322 else if (m_iType == SPIN_CONTROL_TYPE_INT)
324 if (iValue > m_iEnd) iValue = m_iEnd;
325 else if (iValue < m_iStart) iValue = m_iStart;
327 int iValueLower = selector == RangeSelectorLower ? iValue : m_intValues[0];
328 int iValueUpper = selector == RangeSelectorUpper ? iValue : m_intValues[1];
330 if (!m_rangeSelection || iValueLower <= iValueUpper)
332 m_intValues[0] = iValueLower;
333 m_intValues[1] = iValueUpper;
335 m_currentSelector = selector;
339 m_intValues[0] = iValueUpper;
340 m_intValues[1] = iValueLower;
342 m_currentSelector = (selector == RangeSelectorLower ? RangeSelectorUpper : RangeSelectorLower);
346 SetPercentage((float)iValue);
349 int CGUISliderControl::GetIntValue(RangeSelector selector /* = RangeSelectorLower */) const
351 if (m_iType == SPIN_CONTROL_TYPE_FLOAT)
352 return (int)m_floatValues[selector];
353 else if (m_iType == SPIN_CONTROL_TYPE_INT)
354 return m_intValues[selector];
356 return MathUtils::round_int(m_percentValues[selector]);
359 void CGUISliderControl::SetFloatValue(float fValue, RangeSelector selector /* = RangeSelectorLower */, bool updateCurrent /* = false */)
361 if (m_iType == SPIN_CONTROL_TYPE_FLOAT)
363 if (fValue > m_fEnd) fValue = m_fEnd;
364 else if (fValue < m_fStart) fValue = m_fStart;
366 float fValueLower = selector == RangeSelectorLower ? fValue : m_floatValues[0];
367 float fValueUpper = selector == RangeSelectorUpper ? fValue : m_floatValues[1];
369 if (!m_rangeSelection || fValueLower <= fValueUpper)
371 m_floatValues[0] = fValueLower;
372 m_floatValues[1] = fValueUpper;
374 m_currentSelector = selector;
378 m_floatValues[0] = fValueUpper;
379 m_floatValues[1] = fValueLower;
381 m_currentSelector = (selector == RangeSelectorLower ? RangeSelectorUpper : RangeSelectorLower);
384 else if (m_iType == SPIN_CONTROL_TYPE_INT)
385 SetIntValue((int)fValue);
387 SetPercentage(fValue);
390 float CGUISliderControl::GetFloatValue(RangeSelector selector /* = RangeSelectorLower */) const
392 if (m_iType == SPIN_CONTROL_TYPE_FLOAT)
393 return m_floatValues[selector];
394 else if (m_iType == SPIN_CONTROL_TYPE_INT)
395 return (float)m_intValues[selector];
397 return m_percentValues[selector];
400 void CGUISliderControl::SetIntInterval(int iInterval)
402 if (m_iType == SPIN_CONTROL_TYPE_FLOAT)
403 m_fInterval = (float)iInterval;
405 m_iInterval = iInterval;
408 void CGUISliderControl::SetFloatInterval(float fInterval)
410 if (m_iType == SPIN_CONTROL_TYPE_FLOAT)
411 m_fInterval = fInterval;
413 m_iInterval = (int)fInterval;
416 void CGUISliderControl::SetRange(int iStart, int iEnd)
418 if (m_iType == SPIN_CONTROL_TYPE_FLOAT)
419 SetFloatRange((float)iStart,(float)iEnd);
422 m_intValues[0] = m_iStart = iStart;
423 m_intValues[1] = m_iEnd = iEnd;
427 void CGUISliderControl::SetFloatRange(float fStart, float fEnd)
429 if (m_iType == SPIN_CONTROL_TYPE_INT)
430 SetRange((int)fStart, (int)fEnd);
433 m_floatValues[0] = m_fStart = fStart;
434 m_floatValues[1] = m_fEnd = fEnd;
438 void CGUISliderControl::FreeResources(bool immediately)
440 CGUIControl::FreeResources(immediately);
441 m_guiBackground.FreeResources(immediately);
442 m_guiSelectorLower.FreeResources(immediately);
443 m_guiSelectorUpper.FreeResources(immediately);
444 m_guiSelectorLowerFocus.FreeResources(immediately);
445 m_guiSelectorUpperFocus.FreeResources(immediately);
448 void CGUISliderControl::DynamicResourceAlloc(bool bOnOff)
450 CGUIControl::DynamicResourceAlloc(bOnOff);
451 m_guiBackground.DynamicResourceAlloc(bOnOff);
452 m_guiSelectorLower.DynamicResourceAlloc(bOnOff);
453 m_guiSelectorUpper.DynamicResourceAlloc(bOnOff);
454 m_guiSelectorLowerFocus.DynamicResourceAlloc(bOnOff);
455 m_guiSelectorUpperFocus.DynamicResourceAlloc(bOnOff);
458 void CGUISliderControl::AllocResources()
460 CGUIControl::AllocResources();
461 m_guiBackground.AllocResources();
462 m_guiSelectorLower.AllocResources();
463 m_guiSelectorUpper.AllocResources();
464 m_guiSelectorLowerFocus.AllocResources();
465 m_guiSelectorUpperFocus.AllocResources();
468 void CGUISliderControl::SetInvalid()
470 CGUIControl::SetInvalid();
471 m_guiBackground.SetInvalid();
472 m_guiSelectorLower.SetInvalid();
473 m_guiSelectorUpper.SetInvalid();
474 m_guiSelectorLowerFocus.SetInvalid();
475 m_guiSelectorUpperFocus.SetInvalid();
478 bool CGUISliderControl::HitTest(const CPoint &point) const
480 if (m_guiBackground.HitTest(point)) return true;
481 if (m_guiSelectorLower.HitTest(point)) return true;
482 if (m_rangeSelection && m_guiSelectorUpper.HitTest(point)) return true;
486 void CGUISliderControl::SetFromPosition(const CPoint &point, bool guessSelector /* = false */)
488 float fPercent = (point.x - m_guiBackground.GetXPosition()) / m_guiBackground.GetWidth();
489 if (fPercent < 0) fPercent = 0;
490 if (fPercent > 1) fPercent = 1;
492 if (m_rangeSelection && guessSelector)
494 // choose selector which value is closer to value calculated from position
495 if (fabs(GetPercentage(RangeSelectorLower) - 100 * fPercent) <= fabs(GetPercentage(RangeSelectorUpper) - 100 * fPercent))
496 m_currentSelector = RangeSelectorLower;
498 m_currentSelector = RangeSelectorUpper;
503 case SPIN_CONTROL_TYPE_FLOAT:
505 float fValue = m_fStart + (m_fEnd - m_fStart) * fPercent;
506 SetFloatValue(fValue, m_currentSelector, true);
510 case SPIN_CONTROL_TYPE_INT:
512 int iValue = (int)(m_iStart + (float)(m_iEnd - m_iStart) * fPercent + 0.49f);
513 SetIntValue(iValue, m_currentSelector, true);
519 SetPercentage(fPercent * 100, m_currentSelector, true);
526 EVENT_RESULT CGUISliderControl::OnMouseEvent(const CPoint &point, const CMouseEvent &event)
529 if (event.m_id == ACTION_MOUSE_DRAG)
532 bool guessSelector = false;
533 if (event.m_state == 1)
534 { // grab exclusive access
535 CGUIMessage msg(GUI_MSG_EXCLUSIVE_MOUSE, GetID(), GetParentID());
536 SendWindowMessage(msg);
537 guessSelector = true;
539 else if (event.m_state == 3)
540 { // release exclusive access
542 CGUIMessage msg(GUI_MSG_EXCLUSIVE_MOUSE, 0, GetParentID());
543 SendWindowMessage(msg);
545 SetFromPosition(point, guessSelector);
546 return EVENT_RESULT_HANDLED;
548 else if (event.m_id == ACTION_MOUSE_LEFT_CLICK && m_guiBackground.HitTest(point))
550 SetFromPosition(point, true);
551 return EVENT_RESULT_HANDLED;
553 else if (event.m_id == ACTION_MOUSE_WHEEL_UP)
556 return EVENT_RESULT_HANDLED;
558 else if (event.m_id == ACTION_MOUSE_WHEEL_DOWN)
561 return EVENT_RESULT_HANDLED;
563 else if (event.m_id == ACTION_GESTURE_NOTIFY)
565 return EVENT_RESULT_PAN_HORIZONTAL_WITHOUT_INERTIA;
567 else if (event.m_id == ACTION_GESTURE_BEGIN)
568 { // grab exclusive access
569 CGUIMessage msg(GUI_MSG_EXCLUSIVE_MOUSE, GetID(), GetParentID());
570 SendWindowMessage(msg);
571 return EVENT_RESULT_HANDLED;
573 else if (event.m_id == ACTION_GESTURE_PAN)
575 SetFromPosition(point);
576 return EVENT_RESULT_HANDLED;
578 else if (event.m_id == ACTION_GESTURE_END)
579 { // release exclusive access
580 CGUIMessage msg(GUI_MSG_EXCLUSIVE_MOUSE, 0, GetParentID());
581 SendWindowMessage(msg);
582 return EVENT_RESULT_HANDLED;
584 return EVENT_RESULT_UNHANDLED;
587 void CGUISliderControl::SetInfo(int iInfo)
592 CStdString CGUISliderControl::GetDescription() const
594 if (!m_textValue.IsEmpty())
596 CStdString description;
597 if (m_iType == SPIN_CONTROL_TYPE_FLOAT)
599 if (m_rangeSelection)
600 description = StringUtils::Format("[%2.2f, %2.2f]", m_floatValues[0], m_floatValues[1]);
602 description = StringUtils::Format("%2.2f", m_floatValues[0]);
604 else if (m_iType == SPIN_CONTROL_TYPE_INT)
606 if (m_rangeSelection)
607 description = StringUtils::Format("[%i, %i]", m_intValues[0], m_intValues[1]);
609 description = StringUtils::Format("%i", m_intValues[0]);
613 if (m_rangeSelection)
614 description = StringUtils::Format("[%i%%, %i%%]", MathUtils::round_int(m_percentValues[0]), MathUtils::round_int(m_percentValues[1]));
616 description = StringUtils::Format("%i%%", MathUtils::round_int(m_percentValues[0]));
621 bool CGUISliderControl::UpdateColors()
623 bool changed = CGUIControl::UpdateColors();
624 changed |= m_guiBackground.SetDiffuseColor(m_diffuseColor);
625 changed |= m_guiSelectorLower.SetDiffuseColor(m_diffuseColor);
626 changed |= m_guiSelectorUpper.SetDiffuseColor(m_diffuseColor);
627 changed |= m_guiSelectorLowerFocus.SetDiffuseColor(m_diffuseColor);
628 changed |= m_guiSelectorUpperFocus.SetDiffuseColor(m_diffuseColor);
633 float CGUISliderControl::GetProportion(RangeSelector selector /* = RangeSelectorLower */) const
635 if (m_iType == SPIN_CONTROL_TYPE_FLOAT)
636 return (GetFloatValue(selector) - m_fStart) / (m_fEnd - m_fStart);
637 else if (m_iType == SPIN_CONTROL_TYPE_INT)
638 return (float)(GetIntValue(selector) - m_iStart) / (float)(m_iEnd - m_iStart);
639 return 0.01f * GetPercentage(selector);
642 void CGUISliderControl::SetAction(const CStdString &action)
644 for (size_t i = 0; i < sizeof(actions)/sizeof(SliderAction); i++)
646 if (action.CompareNoCase(actions[i].action) == 0)
648 m_action = &actions[i];