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 "GUIWindowManager.h"
27 static const SliderAction actions[] = {
28 {"seek", "PlayerControl(SeekPercentage(%2d))", PLAYER_PROGRESS, false},
29 {"volume", "SetVolume(%2d)", PLAYER_VOLUME, true}
32 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)
33 : CGUIControl(parentID, controlID, posX, posY, width, height)
34 , m_guiBackground(posX, posY, width, height, backGroundTexture)
35 , m_guiSelectorLower(posX, posY, width, height, nibTexture)
36 , m_guiSelectorUpper(posX, posY, width, height, nibTexture)
37 , m_guiSelectorLowerFocus(posX, posY, width, height, nibTextureFocus)
38 , m_guiSelectorUpperFocus(posX, posY, width, height, nibTextureFocus)
41 m_rangeSelection = false;
42 m_currentSelector = RangeSelectorLower; // use lower selector by default
43 m_percentValues[0] = 0;
44 m_percentValues[1] = 100;
51 m_intValues[0] = m_iStart;
52 m_intValues[1] = m_iEnd;
53 m_floatValues[0] = m_fStart;
54 m_floatValues[1] = m_fEnd;
55 ControlType = GUICONTROL_SLIDER;
61 CGUISliderControl::~CGUISliderControl(void)
65 void CGUISliderControl::Process(unsigned int currentTime, CDirtyRegionList &dirtyregions)
69 dirty |= m_guiBackground.SetPosition( m_posX, m_posY );
70 int infoCode = m_iInfoCode;
71 if (m_action && (!m_dragging || m_action->fireOnDrag))
72 infoCode = m_action->infoCode;
76 if (g_infoManager.GetInt(val, infoCode))
80 float fScaleY = m_height == 0 ? 1.0f : m_height / m_guiBackground.GetTextureHeight();
82 dirty |= m_guiBackground.SetHeight(m_height);
83 dirty |= m_guiBackground.SetWidth(m_width);
84 dirty |= m_guiBackground.Process(currentTime);
86 CGUITexture &nibLower = (m_bHasFocus && !IsDisabled() && m_currentSelector == RangeSelectorLower) ? m_guiSelectorLowerFocus : m_guiSelectorLower;
87 dirty |= ProcessSelector(nibLower, currentTime, fScaleY, RangeSelectorLower);
90 CGUITexture &nibUpper = (m_bHasFocus && !IsDisabled() && m_currentSelector == RangeSelectorUpper) ? m_guiSelectorUpperFocus : m_guiSelectorUpper;
91 dirty |= ProcessSelector(nibUpper, currentTime, fScaleY, RangeSelectorUpper);
97 CGUIControl::Process(currentTime, dirtyregions);
100 bool CGUISliderControl::ProcessSelector(CGUITexture &nib, unsigned int currentTime, float fScaleY, RangeSelector selector)
103 // we render the nib centered at the appropriate percentage, except where the nib
104 // would overflow the background image
105 dirty |= nib.SetHeight(nib.GetTextureHeight() * fScaleY);
106 dirty |= nib.SetWidth(nib.GetHeight() * 2);
107 CAspectRatio ratio(CAspectRatio::AR_KEEP);
108 ratio.align = ASPECT_ALIGN_LEFT | ASPECT_ALIGNY_CENTER;
109 dirty |= nib.SetAspectRatio(ratio);
110 dirty |= nib.Process(currentTime);
111 CRect rect = nib.GetRenderRect();
113 float offset = GetProportion(selector) * m_width - rect.Width() / 2;
114 if (offset > m_width - rect.Width())
115 offset = m_width - rect.Width();
118 dirty |= nib.SetPosition(m_guiBackground.GetXPosition() + offset, m_guiBackground.GetYPosition());
119 dirty |= nib.Process(currentTime); // need to process again as the position may have changed
124 void CGUISliderControl::Render()
126 m_guiBackground.Render();
127 CGUITexture &nibLower = (m_bHasFocus && !IsDisabled() && m_currentSelector == RangeSelectorLower) ? m_guiSelectorLowerFocus : m_guiSelectorLower;
129 if (m_rangeSelection)
131 CGUITexture &nibUpper = (m_bHasFocus && !IsDisabled() && m_currentSelector == RangeSelectorUpper) ? m_guiSelectorUpperFocus : m_guiSelectorUpper;
134 CGUIControl::Render();
137 bool CGUISliderControl::OnMessage(CGUIMessage& message)
139 if (message.GetControlId() == GetID() )
141 switch (message.GetMessage())
143 case GUI_MSG_ITEM_SELECT:
144 SetPercentage( message.GetParam1() );
148 case GUI_MSG_LABEL_RESET:
150 SetPercentage(0, RangeSelectorLower);
151 SetPercentage(100, RangeSelectorUpper);
158 return CGUIControl::OnMessage(message);
161 bool CGUISliderControl::OnAction(const CAction &action)
163 switch ( action.GetID() )
165 case ACTION_MOVE_LEFT:
166 //case ACTION_OSD_SHOW_VALUE_MIN:
170 case ACTION_MOVE_RIGHT:
171 //case ACTION_OSD_SHOW_VALUE_PLUS:
175 case ACTION_SELECT_ITEM:
176 // switch between the two sliders
177 SwitchRangeSelector();
184 return CGUIControl::OnAction(action);
187 void CGUISliderControl::Move(int iNumSteps)
189 bool rangeSwap = false;
192 case SPIN_CONTROL_TYPE_FLOAT:
194 float &value = m_floatValues[m_currentSelector];
195 value += m_fInterval * iNumSteps;
196 if (value < m_fStart) value = m_fStart;
197 if (value > m_fEnd) value = m_fEnd;
198 if (m_floatValues[0] > m_floatValues[1])
200 float valueLower = m_floatValues[0];
201 m_floatValues[0] = m_floatValues[1];
202 m_floatValues[1] = valueLower;
208 case SPIN_CONTROL_TYPE_INT:
210 int &value = m_intValues[m_currentSelector];
211 value += m_iInterval * iNumSteps;
212 if (value < m_iStart) value = m_iStart;
213 if (value > m_iEnd) value = m_iEnd;
214 if (m_intValues[0] > m_intValues[1])
216 int valueLower = m_intValues[0];
217 m_intValues[0] = m_intValues[1];
218 m_intValues[1] = valueLower;
226 int &value = m_percentValues[m_currentSelector];
227 value += m_iInterval * iNumSteps;
228 if (value < 0) value = 0;
229 if (value > 100) value = 100;
230 if (m_percentValues[0] > m_percentValues[1])
232 int valueLower = m_percentValues[0];
233 m_percentValues[0] = m_percentValues[1];
234 m_percentValues[1] = valueLower;
242 SwitchRangeSelector();
247 void CGUISliderControl::SendClick()
249 int percent = MathUtils::round_int(100*GetProportion());
250 SEND_CLICK_MESSAGE(GetID(), GetParentID(), percent);
251 if (m_action && (!m_dragging || m_action->fireOnDrag))
254 action.Format(m_action->formatString, percent);
255 CGUIMessage message(GUI_MSG_EXECUTE, m_controlID, m_parentID);
256 message.SetStringParam(action);
257 g_windowManager.SendMessage(message);
261 void CGUISliderControl::SetRangeSelection(bool rangeSelection)
263 if (m_rangeSelection == rangeSelection)
266 m_rangeSelection = rangeSelection;
267 SetRangeSelector(RangeSelectorLower);
271 void CGUISliderControl::SetRangeSelector(RangeSelector selector)
273 if (m_currentSelector == selector)
276 m_currentSelector = selector;
280 void CGUISliderControl::SwitchRangeSelector()
282 if (m_currentSelector == RangeSelectorLower)
283 SetRangeSelector(RangeSelectorUpper);
285 SetRangeSelector(RangeSelectorLower);
288 void CGUISliderControl::SetPercentage(int iPercent, RangeSelector selector /* = RangeSelectorLower */, bool updateCurrent /* = false */)
290 if (iPercent > 100) iPercent = 100;
291 else if (iPercent < 0) iPercent = 0;
293 int iPercentLower = selector == RangeSelectorLower ? iPercent : m_percentValues[0];
294 int iPercentUpper = selector == RangeSelectorUpper ? iPercent : m_percentValues[1];
296 if (!m_rangeSelection || iPercentLower <= iPercentUpper)
298 m_percentValues[0] = iPercentLower;
299 m_percentValues[1] = iPercentUpper;
301 m_currentSelector = selector;
305 m_percentValues[0] = iPercentUpper;
306 m_percentValues[1] = iPercentLower;
308 m_currentSelector = (selector == RangeSelectorLower ? RangeSelectorUpper : RangeSelectorLower);
312 int CGUISliderControl::GetPercentage(RangeSelector selector /* = RangeSelectorLower */) const
314 return m_percentValues[selector];
317 void CGUISliderControl::SetIntValue(int iValue, RangeSelector selector /* = RangeSelectorLower */, bool updateCurrent /* = false */)
319 if (m_iType == SPIN_CONTROL_TYPE_FLOAT)
320 SetFloatValue((float)iValue);
321 else if (m_iType == SPIN_CONTROL_TYPE_INT)
323 if (iValue > m_iEnd) iValue = m_iEnd;
324 else if (iValue < m_iStart) iValue = m_iStart;
326 int iValueLower = selector == RangeSelectorLower ? iValue : m_intValues[0];
327 int iValueUpper = selector == RangeSelectorUpper ? iValue : m_intValues[1];
329 if (!m_rangeSelection || iValueLower <= iValueUpper)
331 m_intValues[0] = iValueLower;
332 m_intValues[1] = iValueUpper;
334 m_currentSelector = selector;
338 m_intValues[0] = iValueUpper;
339 m_intValues[1] = iValueLower;
341 m_currentSelector = (selector == RangeSelectorLower ? RangeSelectorUpper : RangeSelectorLower);
345 SetPercentage(iValue);
348 int CGUISliderControl::GetIntValue(RangeSelector selector /* = RangeSelectorLower */) const
350 if (m_iType == SPIN_CONTROL_TYPE_FLOAT)
351 return (int)m_floatValues[selector];
352 else if (m_iType == SPIN_CONTROL_TYPE_INT)
353 return m_intValues[selector];
355 return m_percentValues[selector];
358 void CGUISliderControl::SetFloatValue(float fValue, RangeSelector selector /* = RangeSelectorLower */, bool updateCurrent /* = false */)
360 if (m_iType == SPIN_CONTROL_TYPE_FLOAT)
362 if (fValue > m_fEnd) fValue = m_fEnd;
363 else if (fValue < m_fStart) fValue = m_fStart;
365 float fValueLower = selector == RangeSelectorLower ? fValue : m_floatValues[0];
366 float fValueUpper = selector == RangeSelectorUpper ? fValue : m_floatValues[1];
368 if (!m_rangeSelection || fValueLower <= fValueUpper)
370 m_floatValues[0] = fValueLower;
371 m_floatValues[1] = fValueUpper;
373 m_currentSelector = selector;
377 m_floatValues[0] = fValueUpper;
378 m_floatValues[1] = fValueLower;
380 m_currentSelector = (selector == RangeSelectorLower ? RangeSelectorUpper : RangeSelectorLower);
383 else if (m_iType == SPIN_CONTROL_TYPE_INT)
384 SetIntValue((int)fValue);
386 SetPercentage((int)fValue);
389 float CGUISliderControl::GetFloatValue(RangeSelector selector /* = RangeSelectorLower */) const
391 if (m_iType == SPIN_CONTROL_TYPE_FLOAT)
392 return m_floatValues[selector];
393 else if (m_iType == SPIN_CONTROL_TYPE_INT)
394 return (float)m_intValues[selector];
396 return (float)m_percentValues[selector];
399 void CGUISliderControl::SetIntInterval(int iInterval)
401 if (m_iType == SPIN_CONTROL_TYPE_FLOAT)
402 m_fInterval = (float)iInterval;
404 m_iInterval = iInterval;
407 void CGUISliderControl::SetFloatInterval(float fInterval)
409 if (m_iType == SPIN_CONTROL_TYPE_FLOAT)
410 m_fInterval = fInterval;
412 m_iInterval = (int)fInterval;
415 void CGUISliderControl::SetRange(int iStart, int iEnd)
417 if (m_iType == SPIN_CONTROL_TYPE_FLOAT)
418 SetFloatRange((float)iStart,(float)iEnd);
421 m_intValues[0] = m_iStart = iStart;
422 m_intValues[1] = m_iEnd = iEnd;
426 void CGUISliderControl::SetFloatRange(float fStart, float fEnd)
428 if (m_iType == SPIN_CONTROL_TYPE_INT)
429 SetRange((int)fStart, (int)fEnd);
432 m_floatValues[0] = m_fStart = fStart;
433 m_floatValues[1] = m_fEnd = fEnd;
437 void CGUISliderControl::FreeResources(bool immediately)
439 CGUIControl::FreeResources(immediately);
440 m_guiBackground.FreeResources(immediately);
441 m_guiSelectorLower.FreeResources(immediately);
442 m_guiSelectorUpper.FreeResources(immediately);
443 m_guiSelectorLowerFocus.FreeResources(immediately);
444 m_guiSelectorUpperFocus.FreeResources(immediately);
447 void CGUISliderControl::DynamicResourceAlloc(bool bOnOff)
449 CGUIControl::DynamicResourceAlloc(bOnOff);
450 m_guiBackground.DynamicResourceAlloc(bOnOff);
451 m_guiSelectorLower.DynamicResourceAlloc(bOnOff);
452 m_guiSelectorUpper.DynamicResourceAlloc(bOnOff);
453 m_guiSelectorLowerFocus.DynamicResourceAlloc(bOnOff);
454 m_guiSelectorUpperFocus.DynamicResourceAlloc(bOnOff);
457 void CGUISliderControl::AllocResources()
459 CGUIControl::AllocResources();
460 m_guiBackground.AllocResources();
461 m_guiSelectorLower.AllocResources();
462 m_guiSelectorUpper.AllocResources();
463 m_guiSelectorLowerFocus.AllocResources();
464 m_guiSelectorUpperFocus.AllocResources();
467 void CGUISliderControl::SetInvalid()
469 CGUIControl::SetInvalid();
470 m_guiBackground.SetInvalid();
471 m_guiSelectorLower.SetInvalid();
472 m_guiSelectorUpper.SetInvalid();
473 m_guiSelectorLowerFocus.SetInvalid();
474 m_guiSelectorUpperFocus.SetInvalid();
477 bool CGUISliderControl::HitTest(const CPoint &point) const
479 if (m_guiBackground.HitTest(point)) return true;
480 if (m_guiSelectorLower.HitTest(point)) return true;
481 if (m_rangeSelection && m_guiSelectorUpper.HitTest(point)) return true;
485 void CGUISliderControl::SetFromPosition(const CPoint &point, bool guessSelector /* = false */)
487 float fPercent = (point.x - m_guiBackground.GetXPosition()) / m_guiBackground.GetWidth();
488 if (fPercent < 0) fPercent = 0;
489 if (fPercent > 1) fPercent = 1;
491 if (m_rangeSelection && guessSelector)
493 // choose selector which value is closer to value calculated from position
494 if (fabs(GetPercentage(RangeSelectorLower) - 100 * fPercent) <= fabs(GetPercentage(RangeSelectorUpper) - 100 * fPercent))
495 m_currentSelector = RangeSelectorLower;
497 m_currentSelector = RangeSelectorUpper;
502 case SPIN_CONTROL_TYPE_FLOAT:
504 float fValue = m_fStart + (m_fEnd - m_fStart) * fPercent;
505 SetFloatValue(fValue, m_currentSelector, true);
509 case SPIN_CONTROL_TYPE_INT:
511 int iValue = (int)(m_iStart + (float)(m_iEnd - m_iStart) * fPercent + 0.49f);
512 SetIntValue(iValue, m_currentSelector, true);
518 int iValue = (int)(fPercent * 100 + 0.49f);
519 SetPercentage(iValue, 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.Format("[%2.2f, %2.2f]", m_floatValues[0], m_floatValues[1]);
602 description.Format("%2.2f", m_floatValues[0]);
604 else if (m_iType == SPIN_CONTROL_TYPE_INT)
606 if (m_rangeSelection)
607 description.Format("[%i, %i]", m_intValues[0], m_intValues[1]);
609 description.Format("%i", m_intValues[0]);
613 if (m_rangeSelection)
614 description.Format("[%i%%, %i%%]", m_percentValues[0], m_percentValues[1]);
616 description.Format("%i%%", 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];