PageRenderTime 82ms CodeModel.GetById 17ms app.highlight 59ms RepoModel.GetById 1ms app.codeStats 0ms

/xbmc/guilib/GUISpinControl.cpp

http://github.com/xbmc/xbmc
C++ | 1025 lines | 892 code | 104 blank | 29 comment | 178 complexity | fdeaaa1f41963e62d1c2c3dd34cdb56d MD5 | raw file
   1/*
   2 *  Copyright (C) 2005-2018 Team Kodi
   3 *  This file is part of Kodi - https://kodi.tv
   4 *
   5 *  SPDX-License-Identifier: GPL-2.0-or-later
   6 *  See LICENSES/README.md for more information.
   7 */
   8
   9#include "GUISpinControl.h"
  10
  11#include "GUIMessage.h"
  12#include "input/Key.h"
  13#include "utils/StringUtils.h"
  14
  15#include <stdio.h>
  16
  17#define SPIN_BUTTON_DOWN 1
  18#define SPIN_BUTTON_UP   2
  19
  20CGUISpinControl::CGUISpinControl(int parentID, int controlID, float posX, float posY, float width, float height, const CTextureInfo& textureUp, const CTextureInfo& textureDown, const CTextureInfo& textureUpFocus, const CTextureInfo& textureDownFocus, const CTextureInfo& textureUpDisabled, const CTextureInfo& textureDownDisabled, const CLabelInfo &labelInfo, int iType)
  21    : CGUIControl(parentID, controlID, posX, posY, width, height)
  22    , m_imgspinUp(posX, posY, width, height, textureUp)
  23    , m_imgspinDown(posX, posY, width, height, textureDown)
  24    , m_imgspinUpFocus(posX, posY, width, height, textureUpFocus)
  25    , m_imgspinDownFocus(posX, posY, width, height, textureDownFocus)
  26    , m_imgspinUpDisabled(posX, posY, width, height, textureUpDisabled)
  27    , m_imgspinDownDisabled(posX, posY, width, height, textureDownDisabled)
  28    , m_label(posX, posY, width, height, labelInfo)
  29{
  30  m_bReverse = false;
  31  m_iStart = 0;
  32  m_iEnd = 100;
  33  m_fStart = 0.0f;
  34  m_fEnd = 1.0f;
  35  m_fInterval = 0.1f;
  36  m_iValue = 0;
  37  m_fValue = 0.0;
  38  m_iType = iType;
  39  m_iSelect = SPIN_BUTTON_DOWN;
  40  m_bShowRange = false;
  41  m_iTypedPos = 0;
  42  strcpy(m_szTyped, "");
  43  ControlType = GUICONTROL_SPIN;
  44  m_currentItem = 0;
  45  m_numItems = 10;
  46  m_itemsPerPage = 10;
  47  m_showOnePage = true;
  48}
  49
  50CGUISpinControl::~CGUISpinControl(void) = default;
  51
  52bool CGUISpinControl::OnAction(const CAction &action)
  53{
  54  switch (action.GetID())
  55  {
  56  case REMOTE_0:
  57  case REMOTE_1:
  58  case REMOTE_2:
  59  case REMOTE_3:
  60  case REMOTE_4:
  61  case REMOTE_5:
  62  case REMOTE_6:
  63  case REMOTE_7:
  64  case REMOTE_8:
  65  case REMOTE_9:
  66    {
  67      if (strlen(m_szTyped) >= 3)
  68      {
  69        m_iTypedPos = 0;
  70        strcpy(m_szTyped, "");
  71      }
  72      int iNumber = action.GetID() - REMOTE_0;
  73
  74      m_szTyped[m_iTypedPos] = iNumber + '0';
  75      m_iTypedPos++;
  76      m_szTyped[m_iTypedPos] = 0;
  77      int iValue;
  78      sscanf(m_szTyped, "%i", &iValue);
  79      switch (m_iType)
  80      {
  81      case SPIN_CONTROL_TYPE_INT:
  82        {
  83          if (iValue < m_iStart || iValue > m_iEnd)
  84          {
  85            m_iTypedPos = 0;
  86            m_szTyped[m_iTypedPos] = iNumber + '0';
  87            m_iTypedPos++;
  88            m_szTyped[m_iTypedPos] = 0;
  89            sscanf(m_szTyped, "%i", &iValue);
  90            if (iValue < m_iStart || iValue > m_iEnd)
  91            {
  92              m_iTypedPos = 0;
  93              strcpy(m_szTyped, "");
  94              return true;
  95            }
  96          }
  97          m_iValue = iValue;
  98          CGUIMessage msg(GUI_MSG_CLICKED, GetID(), GetParentID());
  99          SendWindowMessage(msg);
 100        }
 101        break;
 102
 103      case SPIN_CONTROL_TYPE_TEXT:
 104        {
 105          if (iValue < 0 || iValue >= (int)m_vecLabels.size())
 106          {
 107            m_iTypedPos = 0;
 108            m_szTyped[m_iTypedPos] = iNumber + '0';
 109            m_iTypedPos++;
 110            m_szTyped[m_iTypedPos] = 0;
 111            sscanf(m_szTyped, "%i", &iValue);
 112            if (iValue < 0 || iValue >= (int)m_vecLabels.size())
 113            {
 114              m_iTypedPos = 0;
 115              strcpy(m_szTyped, "");
 116              return true;
 117            }
 118          }
 119          m_iValue = iValue;
 120          CGUIMessage msg(GUI_MSG_CLICKED, GetID(), GetParentID());
 121          SendWindowMessage(msg);
 122        }
 123        break;
 124
 125      }
 126      return true;
 127    }
 128    break;
 129  case ACTION_PAGE_UP:
 130    if (!m_bReverse)
 131      PageDown();
 132    else
 133      PageUp();
 134    return true;
 135    break;
 136  case ACTION_PAGE_DOWN:
 137    if (!m_bReverse)
 138      PageUp();
 139    else
 140      PageDown();
 141    return true;
 142    break;
 143  case ACTION_SELECT_ITEM:
 144    if (m_iSelect == SPIN_BUTTON_UP)
 145    {
 146      MoveUp();
 147      return true;
 148    }
 149    if (m_iSelect == SPIN_BUTTON_DOWN)
 150    {
 151      MoveDown();
 152      return true;
 153    }
 154    break;
 155  }
 156/*  static float m_fSmoothScrollOffset = 0.0f;
 157  if (action.GetID() == ACTION_SCROLL_UP)
 158  {
 159    m_fSmoothScrollOffset += action.GetAmount() * action.GetAmount();
 160    bool handled = false;
 161    while (m_fSmoothScrollOffset > 0.4)
 162    {
 163      handled = true;
 164      m_fSmoothScrollOffset -= 0.4f;
 165      MoveDown();
 166    }
 167    return handled;
 168  }*/
 169  return CGUIControl::OnAction(action);
 170}
 171
 172void CGUISpinControl::OnLeft()
 173{
 174  if (m_iSelect == SPIN_BUTTON_UP)
 175  {
 176    // select the down button
 177    m_iSelect = SPIN_BUTTON_DOWN;
 178    MarkDirtyRegion();
 179  }
 180  else
 181  { // base class
 182    CGUIControl::OnLeft();
 183  }
 184}
 185
 186void CGUISpinControl::OnRight()
 187{
 188  if (m_iSelect == SPIN_BUTTON_DOWN)
 189  {
 190    // select the up button
 191    m_iSelect = SPIN_BUTTON_UP;
 192    MarkDirtyRegion();
 193  }
 194  else
 195  { // base class
 196    CGUIControl::OnRight();
 197  }
 198}
 199
 200void CGUISpinControl::Clear()
 201{
 202  m_vecLabels.clear();
 203  m_vecValues.clear();
 204  m_vecStrValues.clear();
 205  SetValue(0);
 206}
 207
 208bool CGUISpinControl::OnMessage(CGUIMessage& message)
 209{
 210  if (CGUIControl::OnMessage(message) )
 211    return true;
 212  if (message.GetControlId() == GetID() )
 213  {
 214    switch (message.GetMessage())
 215    {
 216    case GUI_MSG_ITEM_SELECT:
 217      if (SPIN_CONTROL_TYPE_PAGE == m_iType)
 218      {
 219        m_currentItem = message.GetParam1();
 220        return true;
 221      }
 222      SetValue( message.GetParam1());
 223      if (message.GetParam2() == SPIN_BUTTON_DOWN || message.GetParam2() == SPIN_BUTTON_UP)
 224        m_iSelect = message.GetParam2();
 225      return true;
 226      break;
 227
 228    case GUI_MSG_LABEL_RESET:
 229      if (SPIN_CONTROL_TYPE_PAGE == m_iType)
 230      {
 231        m_itemsPerPage = message.GetParam1();
 232        m_numItems = message.GetParam2();
 233        return true;
 234      }
 235      {
 236        Clear();
 237        return true;
 238      }
 239      break;
 240
 241    case GUI_MSG_SHOWRANGE:
 242      if (message.GetParam1() )
 243        m_bShowRange = true;
 244      else
 245        m_bShowRange = false;
 246      break;
 247
 248    case GUI_MSG_SET_LABELS:
 249      if (message.GetPointer())
 250      {
 251        auto labels =
 252            static_cast<const std::vector<std::pair<std::string, int>>*>(message.GetPointer());
 253        Clear();
 254        for (const auto& i : *labels)
 255          AddLabel(i.first, i.second);
 256        SetValue( message.GetParam1());
 257      }
 258      break;
 259
 260    case GUI_MSG_LABEL_ADD:
 261      {
 262        AddLabel(message.GetLabel(), message.GetParam1());
 263        return true;
 264      }
 265      break;
 266
 267    case GUI_MSG_ITEM_SELECTED:
 268      {
 269        message.SetParam1( GetValue() );
 270        message.SetParam2(m_iSelect);
 271
 272        if (m_iType == SPIN_CONTROL_TYPE_TEXT)
 273        {
 274          if ( m_iValue >= 0 && m_iValue < (int)m_vecLabels.size() )
 275            message.SetLabel( m_vecLabels[m_iValue]);
 276        }
 277        return true;
 278      }
 279
 280    case GUI_MSG_PAGE_UP:
 281      if (CanMoveUp())
 282        MoveUp();
 283      return true;
 284
 285    case GUI_MSG_PAGE_DOWN:
 286      if (CanMoveDown())
 287        MoveDown();
 288      return true;
 289
 290    case GUI_MSG_MOVE_OFFSET:
 291      {
 292        int count = message.GetParam1();
 293        while (count < 0)
 294        {
 295          MoveUp();
 296          count++;
 297        }
 298        while (count > 0)
 299        {
 300          MoveDown();
 301          count--;
 302        }
 303        return true;
 304      }
 305
 306    }
 307  }
 308  return false;
 309}
 310
 311void CGUISpinControl::AllocResources()
 312{
 313  CGUIControl::AllocResources();
 314  m_imgspinUp.AllocResources();
 315  m_imgspinUpFocus.AllocResources();
 316  m_imgspinDown.AllocResources();
 317  m_imgspinDownFocus.AllocResources();
 318  m_imgspinUpDisabled.AllocResources();
 319  m_imgspinDownDisabled.AllocResources();
 320
 321  m_imgspinDownFocus.SetPosition(m_posX, m_posY);
 322  m_imgspinDown.SetPosition(m_posX, m_posY);
 323  m_imgspinDownDisabled.SetPosition(m_posX, m_posY);
 324  m_imgspinUp.SetPosition(m_posX + m_imgspinDown.GetWidth(), m_posY);
 325  m_imgspinUpFocus.SetPosition(m_posX + m_imgspinDownFocus.GetWidth(), m_posY);
 326  m_imgspinUpDisabled.SetPosition(m_posX + m_imgspinDownDisabled.GetWidth(), m_posY);
 327}
 328
 329void CGUISpinControl::FreeResources(bool immediately)
 330{
 331  CGUIControl::FreeResources(immediately);
 332  m_imgspinUp.FreeResources(immediately);
 333  m_imgspinUpFocus.FreeResources(immediately);
 334  m_imgspinDown.FreeResources(immediately);
 335  m_imgspinDownFocus.FreeResources(immediately);
 336  m_imgspinUpDisabled.FreeResources(immediately);
 337  m_imgspinDownDisabled.FreeResources(immediately);
 338  m_iTypedPos = 0;
 339  strcpy(m_szTyped, "");
 340}
 341
 342void CGUISpinControl::DynamicResourceAlloc(bool bOnOff)
 343{
 344  CGUIControl::DynamicResourceAlloc(bOnOff);
 345  m_imgspinUp.DynamicResourceAlloc(bOnOff);
 346  m_imgspinUpFocus.DynamicResourceAlloc(bOnOff);
 347  m_imgspinDown.DynamicResourceAlloc(bOnOff);
 348  m_imgspinDownFocus.DynamicResourceAlloc(bOnOff);
 349  m_imgspinUpDisabled.DynamicResourceAlloc(bOnOff);
 350  m_imgspinDownDisabled.DynamicResourceAlloc(bOnOff);
 351}
 352
 353void CGUISpinControl::SetInvalid()
 354{
 355  CGUIControl::SetInvalid();
 356  m_label.SetInvalid();
 357  m_imgspinUp.SetInvalid();
 358  m_imgspinUpFocus.SetInvalid();
 359  m_imgspinDown.SetInvalid();
 360  m_imgspinDownFocus.SetInvalid();
 361  m_imgspinUpDisabled.SetInvalid();
 362  m_imgspinDownDisabled.SetInvalid();
 363}
 364
 365void CGUISpinControl::Process(unsigned int currentTime, CDirtyRegionList &dirtyregions)
 366{
 367  bool changed = false;
 368
 369  if (!HasFocus())
 370  {
 371    m_iTypedPos = 0;
 372    strcpy(m_szTyped, "");
 373  }
 374
 375  std::string text;
 376
 377  if (m_iType == SPIN_CONTROL_TYPE_INT)
 378  {
 379    if (m_bShowRange)
 380    {
 381      text = StringUtils::Format("%i/%i", m_iValue, m_iEnd);
 382    }
 383    else
 384    {
 385      text = StringUtils::Format("%i", m_iValue);
 386    }
 387  }
 388  else if (m_iType == SPIN_CONTROL_TYPE_PAGE)
 389  {
 390    // work out number of pages and current page
 391    int numPages = (m_numItems + m_itemsPerPage - 1) / m_itemsPerPage;
 392    int currentPage = m_currentItem / m_itemsPerPage + 1;
 393    if (m_currentItem >= m_numItems - m_itemsPerPage)
 394      currentPage = numPages;
 395    text = StringUtils::Format("%i/%i", currentPage, numPages);
 396  }
 397  else if (m_iType == SPIN_CONTROL_TYPE_FLOAT)
 398  {
 399    if (m_bShowRange)
 400    {
 401      text = StringUtils::Format("%02.2f/%02.2f", m_fValue, m_fEnd);
 402    }
 403    else
 404    {
 405      text = StringUtils::Format("%02.2f", m_fValue);
 406    }
 407  }
 408  else
 409  {
 410    if (m_iValue >= 0 && m_iValue < (int)m_vecLabels.size() )
 411    {
 412      if (m_bShowRange)
 413      {
 414        text = StringUtils::Format("(%i/%i) %s", m_iValue + 1, (int)m_vecLabels.size(), m_vecLabels[m_iValue].c_str() );
 415      }
 416      else
 417      {
 418        text = StringUtils::Format("%s", m_vecLabels[m_iValue].c_str() );
 419      }
 420    }
 421    else text = StringUtils::Format("?%i?", m_iValue);
 422
 423  }
 424
 425  changed |= m_label.SetText(text);
 426
 427  float textWidth = m_label.GetTextWidth() + 2 * m_label.GetLabelInfo().offsetX;
 428  // Position the arrows
 429  bool arrowsOnRight(0 != (m_label.GetLabelInfo().align & (XBFONT_RIGHT | XBFONT_CENTER_X)));
 430  if (!arrowsOnRight)
 431  {
 432    const float space = 5;
 433    changed |= m_imgspinDownFocus.SetPosition(m_posX + textWidth + space, m_posY);
 434    changed |= m_imgspinDown.SetPosition(m_posX + textWidth + space, m_posY);
 435    changed |= m_imgspinDownDisabled.SetPosition(m_posX + textWidth + space, m_posY);
 436    changed |= m_imgspinUpFocus.SetPosition(m_posX + textWidth + space + m_imgspinDown.GetWidth(), m_posY);
 437    changed |= m_imgspinUp.SetPosition(m_posX + textWidth + space + m_imgspinDown.GetWidth(), m_posY);
 438    changed |= m_imgspinUpDisabled.SetPosition(m_posX + textWidth + space + m_imgspinDownDisabled.GetWidth(), m_posY);
 439  }
 440
 441  changed |= m_imgspinDownFocus.Process(currentTime);
 442  changed |= m_imgspinDown.Process(currentTime);
 443  changed |= m_imgspinUp.Process(currentTime);
 444  changed |= m_imgspinUpFocus.Process(currentTime);
 445  changed |= m_imgspinUpDisabled.Process(currentTime);
 446  changed |= m_imgspinDownDisabled.Process(currentTime);
 447  changed |= m_label.Process(currentTime);
 448
 449  if (changed)
 450    MarkDirtyRegion();
 451
 452  CGUIControl::Process(currentTime, dirtyregions);
 453}
 454
 455void CGUISpinControl::Render()
 456{
 457  if ( HasFocus() )
 458  {
 459    if (m_iSelect == SPIN_BUTTON_UP)
 460      m_imgspinUpFocus.Render();
 461    else
 462      m_imgspinUp.Render();
 463
 464    if (m_iSelect == SPIN_BUTTON_DOWN)
 465      m_imgspinDownFocus.Render();
 466    else
 467      m_imgspinDown.Render();
 468  }
 469  else if ( !HasFocus() && !IsDisabled() )
 470  {
 471    m_imgspinUp.Render();
 472    m_imgspinDown.Render();
 473  }
 474  else
 475  {
 476    m_imgspinUpDisabled.Render();
 477    m_imgspinDownDisabled.Render();
 478  }
 479
 480  if (m_label.GetLabelInfo().font)
 481  {
 482    const float space = 5;
 483    float textWidth = m_label.GetTextWidth() + 2 * m_label.GetLabelInfo().offsetX;
 484    // Position the arrows
 485    bool arrowsOnRight(0 != (m_label.GetLabelInfo().align & (XBFONT_RIGHT | XBFONT_CENTER_X)));
 486
 487    if (arrowsOnRight)
 488      RenderText(m_posX - space - textWidth, m_posY, textWidth, m_height);
 489    else
 490      RenderText(m_posX + m_imgspinDown.GetWidth() + m_imgspinUp.GetWidth() + space, m_posY, textWidth, m_height);
 491
 492    // set our hit rectangle for MouseOver events
 493    m_hitRect = m_label.GetRenderRect();
 494  }
 495  CGUIControl::Render();
 496}
 497
 498void CGUISpinControl::RenderText(float posX, float posY, float width, float height)
 499{
 500  m_label.SetMaxRect(posX, posY, width, height);
 501  m_label.SetColor(GetTextColor());
 502  m_label.Render();
 503}
 504
 505CGUILabel::COLOR CGUISpinControl::GetTextColor() const
 506{
 507  if (IsDisabled())
 508    return CGUILabel::COLOR_DISABLED;
 509  else if (HasFocus())
 510    return CGUILabel::COLOR_FOCUSED;
 511  return CGUILabel::COLOR_TEXT;
 512}
 513
 514void CGUISpinControl::SetRange(int iStart, int iEnd)
 515{
 516  m_iStart = iStart;
 517  m_iEnd = iEnd;
 518}
 519
 520void CGUISpinControl::SetFloatRange(float fStart, float fEnd)
 521{
 522  m_fStart = fStart;
 523  m_fEnd = fEnd;
 524}
 525
 526void CGUISpinControl::SetValueFromLabel(const std::string &label)
 527{
 528  if (m_iType == SPIN_CONTROL_TYPE_TEXT)
 529  {
 530    m_iValue = 0;
 531    for (unsigned int i = 0; i < m_vecLabels.size(); i++)
 532      if (label == m_vecLabels[i])
 533        m_iValue = i;
 534  }
 535  else
 536    m_iValue = atoi(label.c_str());
 537
 538  MarkDirtyRegion();
 539  SetInvalid();
 540}
 541
 542void CGUISpinControl::SetValue(int iValue)
 543{
 544  if (m_iType == SPIN_CONTROL_TYPE_TEXT)
 545  {
 546    m_iValue = 0;
 547    for (unsigned int i = 0; i < m_vecValues.size(); i++)
 548      if (iValue == m_vecValues[i])
 549        m_iValue = i;
 550  }
 551  else
 552    m_iValue = iValue;
 553
 554  MarkDirtyRegion();
 555  SetInvalid();
 556}
 557
 558void CGUISpinControl::SetFloatValue(float fValue)
 559{
 560  m_fValue = fValue;
 561}
 562
 563void CGUISpinControl::SetStringValue(const std::string& strValue)
 564{
 565  if (m_iType == SPIN_CONTROL_TYPE_TEXT)
 566  {
 567    m_iValue = 0;
 568    for (unsigned int i = 0; i < m_vecStrValues.size(); i++)
 569      if (strValue == m_vecStrValues[i])
 570        m_iValue = i;
 571  }
 572
 573  SetInvalid();
 574}
 575
 576int CGUISpinControl::GetValue() const
 577{
 578  if (m_iType == SPIN_CONTROL_TYPE_TEXT)
 579  {
 580    if (m_iValue >= 0 && m_iValue < (int)m_vecValues.size())
 581      return m_vecValues[m_iValue];
 582  }
 583  return m_iValue;
 584}
 585
 586float CGUISpinControl::GetFloatValue() const
 587{
 588  return m_fValue;
 589}
 590
 591std::string CGUISpinControl::GetStringValue() const
 592{
 593  if (m_iType == SPIN_CONTROL_TYPE_TEXT && m_iValue >= 0 && m_iValue < (int)m_vecLabels.size())
 594  {
 595    if (m_iValue < (int)m_vecStrValues.size())
 596      return m_vecStrValues[m_iValue];
 597
 598    return m_vecLabels[m_iValue];
 599  }
 600  return "";
 601}
 602
 603void CGUISpinControl::AddLabel(const std::string& strLabel, int iValue)
 604{
 605  m_vecLabels.push_back(strLabel);
 606  m_vecValues.push_back(iValue);
 607}
 608
 609void CGUISpinControl::AddLabel(const std::string& strLabel, const std::string& strValue)
 610{
 611  m_vecLabels.push_back(strLabel);
 612  m_vecStrValues.push_back(strValue);
 613}
 614
 615const std::string CGUISpinControl::GetLabel() const
 616{
 617  if (m_iValue >= 0 && m_iValue < (int)m_vecLabels.size())
 618  {
 619    return m_vecLabels[m_iValue];
 620  }
 621  return "";
 622}
 623
 624void CGUISpinControl::SetPosition(float posX, float posY)
 625{
 626  CGUIControl::SetPosition(posX, posY);
 627
 628  m_imgspinDownFocus.SetPosition(posX, posY);
 629  m_imgspinDown.SetPosition(posX, posY);
 630  m_imgspinDownDisabled.SetPosition(posX, posY);
 631
 632  m_imgspinUp.SetPosition(m_posX + m_imgspinDown.GetWidth(), m_posY);
 633  m_imgspinUpFocus.SetPosition(m_posX + m_imgspinDownFocus.GetWidth(), m_posY);
 634  m_imgspinUpDisabled.SetPosition(m_posX + m_imgspinDownDisabled.GetWidth(), m_posY);
 635
 636}
 637
 638float CGUISpinControl::GetWidth() const
 639{
 640  return m_imgspinDown.GetWidth() * 2 ;
 641}
 642
 643bool CGUISpinControl::CanMoveUp(bool bTestReverse)
 644{
 645  // test for reverse...
 646  if (bTestReverse && m_bReverse) return CanMoveDown(false);
 647
 648  switch (m_iType)
 649  {
 650  case SPIN_CONTROL_TYPE_PAGE:
 651    return m_currentItem > 0;
 652  case SPIN_CONTROL_TYPE_INT:
 653    {
 654      if (m_iValue - 1 >= m_iStart)
 655        return true;
 656      return false;
 657    }
 658    break;
 659
 660  case SPIN_CONTROL_TYPE_FLOAT:
 661    {
 662      if (m_fValue - m_fInterval >= m_fStart)
 663        return true;
 664      return false;
 665    }
 666    break;
 667
 668  case SPIN_CONTROL_TYPE_TEXT:
 669    {
 670      if (m_iValue - 1 >= 0)
 671        return true;
 672      return false;
 673    }
 674    break;
 675  }
 676  return false;
 677}
 678
 679bool CGUISpinControl::CanMoveDown(bool bTestReverse)
 680{
 681  // test for reverse...
 682  if (bTestReverse && m_bReverse) return CanMoveUp(false);
 683  switch (m_iType)
 684  {
 685  case SPIN_CONTROL_TYPE_PAGE:
 686    return m_currentItem < m_numItems;
 687  case SPIN_CONTROL_TYPE_INT:
 688    {
 689      if (m_iValue + 1 <= m_iEnd)
 690        return true;
 691      return false;
 692    }
 693    break;
 694
 695  case SPIN_CONTROL_TYPE_FLOAT:
 696    {
 697      if (m_fValue + m_fInterval <= m_fEnd)
 698        return true;
 699      return false;
 700    }
 701    break;
 702
 703  case SPIN_CONTROL_TYPE_TEXT:
 704    {
 705      if (m_iValue + 1 < (int)m_vecLabels.size())
 706        return true;
 707      return false;
 708    }
 709    break;
 710  }
 711  return false;
 712}
 713
 714void CGUISpinControl::PageUp()
 715{
 716  switch (m_iType)
 717  {
 718  case SPIN_CONTROL_TYPE_INT:
 719    {
 720      if (m_iValue - 10 >= m_iStart)
 721        m_iValue -= 10;
 722      else
 723        m_iValue = m_iStart;
 724      CGUIMessage msg(GUI_MSG_CLICKED, GetID(), GetParentID());
 725      SendWindowMessage(msg);
 726      return ;
 727    }
 728    break;
 729  case SPIN_CONTROL_TYPE_PAGE:
 730    ChangePage(-10);
 731    break;
 732  case SPIN_CONTROL_TYPE_TEXT:
 733    {
 734      if (m_iValue - 10 >= 0)
 735        m_iValue -= 10;
 736      else
 737        m_iValue = 0;
 738      CGUIMessage msg(GUI_MSG_CLICKED, GetID(), GetParentID());
 739      SendWindowMessage(msg);
 740      return ;
 741    }
 742    break;
 743  }
 744
 745}
 746
 747void CGUISpinControl::PageDown()
 748{
 749  switch (m_iType)
 750  {
 751  case SPIN_CONTROL_TYPE_INT:
 752    {
 753      if (m_iValue + 10 <= m_iEnd)
 754        m_iValue += 10;
 755      else
 756        m_iValue = m_iEnd;
 757      CGUIMessage msg(GUI_MSG_CLICKED, GetID(), GetParentID());
 758      SendWindowMessage(msg);
 759      return ;
 760    }
 761    break;
 762  case SPIN_CONTROL_TYPE_PAGE:
 763    ChangePage(10);
 764    break;
 765  case SPIN_CONTROL_TYPE_TEXT:
 766    {
 767      if (m_iValue + 10 < (int)m_vecLabels.size() )
 768        m_iValue += 10;
 769      CGUIMessage msg(GUI_MSG_CLICKED, GetID(), GetParentID());
 770      SendWindowMessage(msg);
 771    }
 772    break;
 773  }
 774}
 775
 776void CGUISpinControl::MoveUp(bool bTestReverse)
 777{
 778  if (bTestReverse && m_bReverse)
 779  { // actually should move down.
 780    MoveDown(false);
 781    return ;
 782  }
 783  switch (m_iType)
 784  {
 785  case SPIN_CONTROL_TYPE_INT:
 786    {
 787      if (m_iValue - 1 >= m_iStart)
 788        m_iValue--;
 789      else if (m_iValue == m_iStart)
 790        m_iValue = m_iEnd;
 791      CGUIMessage msg(GUI_MSG_CLICKED, GetID(), GetParentID());
 792      SendWindowMessage(msg);
 793      return ;
 794    }
 795    break;
 796
 797  case SPIN_CONTROL_TYPE_PAGE:
 798    ChangePage(-1);
 799    break;
 800
 801  case SPIN_CONTROL_TYPE_FLOAT:
 802    {
 803      if (m_fValue - m_fInterval >= m_fStart)
 804        m_fValue -= m_fInterval;
 805      else if (m_fValue - m_fInterval < m_fStart)
 806        m_fValue = m_fEnd;
 807      CGUIMessage msg(GUI_MSG_CLICKED, GetID(), GetParentID());
 808      SendWindowMessage(msg);
 809      return ;
 810    }
 811    break;
 812
 813  case SPIN_CONTROL_TYPE_TEXT:
 814    {
 815      if (m_iValue - 1 >= 0)
 816        m_iValue--;
 817      else if (m_iValue == 0)
 818        m_iValue = (int)m_vecLabels.size() - 1;
 819      CGUIMessage msg(GUI_MSG_CLICKED, GetID(), GetParentID());
 820      SendWindowMessage(msg);
 821      return ;
 822    }
 823    break;
 824  }
 825}
 826
 827void CGUISpinControl::MoveDown(bool bTestReverse)
 828{
 829  if (bTestReverse && m_bReverse)
 830  { // actually should move up.
 831    MoveUp(false);
 832    return ;
 833  }
 834  switch (m_iType)
 835  {
 836  case SPIN_CONTROL_TYPE_INT:
 837    {
 838      if (m_iValue + 1 <= m_iEnd)
 839        m_iValue++;
 840      else if (m_iValue == m_iEnd)
 841        m_iValue = m_iStart;
 842      CGUIMessage msg(GUI_MSG_CLICKED, GetID(), GetParentID());
 843      SendWindowMessage(msg);
 844      return ;
 845    }
 846    break;
 847
 848  case SPIN_CONTROL_TYPE_PAGE:
 849    ChangePage(1);
 850    break;
 851
 852  case SPIN_CONTROL_TYPE_FLOAT:
 853    {
 854      if (m_fValue + m_fInterval <= m_fEnd)
 855        m_fValue += m_fInterval;
 856      else if (m_fValue + m_fInterval > m_fEnd)
 857        m_fValue = m_fStart;
 858      CGUIMessage msg(GUI_MSG_CLICKED, GetID(), GetParentID());
 859      SendWindowMessage(msg);
 860      return ;
 861    }
 862    break;
 863
 864  case SPIN_CONTROL_TYPE_TEXT:
 865    {
 866      if (m_iValue + 1 < (int)m_vecLabels.size() )
 867        m_iValue++;
 868      else if (m_iValue == (int)m_vecLabels.size() - 1)
 869        m_iValue = 0;
 870      CGUIMessage msg(GUI_MSG_CLICKED, GetID(), GetParentID());
 871      SendWindowMessage(msg);
 872      return ;
 873    }
 874    break;
 875  }
 876}
 877void CGUISpinControl::SetReverse(bool bReverse)
 878{
 879  m_bReverse = bReverse;
 880}
 881
 882void CGUISpinControl::SetFloatInterval(float fInterval)
 883{
 884  m_fInterval = fInterval;
 885}
 886
 887void CGUISpinControl::SetShowRange(bool bOnoff)
 888{
 889  m_bShowRange = bOnoff;
 890}
 891
 892int CGUISpinControl::GetMinimum() const
 893{
 894  switch (m_iType)
 895  {
 896  case SPIN_CONTROL_TYPE_PAGE:
 897    return 0;
 898  case SPIN_CONTROL_TYPE_INT:
 899    return m_iStart;
 900    break;
 901
 902  case SPIN_CONTROL_TYPE_TEXT:
 903    return 1;
 904    break;
 905
 906  case SPIN_CONTROL_TYPE_FLOAT:
 907    return (int)(m_fStart*10.0f);
 908    break;
 909  }
 910  return 0;
 911}
 912
 913int CGUISpinControl::GetMaximum() const
 914{
 915  switch (m_iType)
 916  {
 917  case SPIN_CONTROL_TYPE_PAGE:
 918    return m_numItems;
 919  case SPIN_CONTROL_TYPE_INT:
 920    return m_iEnd;
 921    break;
 922
 923  case SPIN_CONTROL_TYPE_TEXT:
 924    return (int)m_vecLabels.size();
 925    break;
 926
 927  case SPIN_CONTROL_TYPE_FLOAT:
 928    return (int)(m_fEnd*10.0f);
 929    break;
 930  }
 931  return 100;
 932}
 933
 934bool CGUISpinControl::HitTest(const CPoint &point) const
 935{
 936  if (m_imgspinUpFocus.HitTest(point) || m_imgspinDownFocus.HitTest(point))
 937    return true;
 938  return CGUIControl::HitTest(point);
 939}
 940
 941bool CGUISpinControl::OnMouseOver(const CPoint &point)
 942{
 943  int select = m_iSelect;
 944  if (m_imgspinDownFocus.HitTest(point))
 945    m_iSelect = SPIN_BUTTON_DOWN;
 946  else
 947    m_iSelect = SPIN_BUTTON_UP;
 948
 949  if (select != m_iSelect)
 950    MarkDirtyRegion();
 951
 952  return CGUIControl::OnMouseOver(point);
 953}
 954
 955EVENT_RESULT CGUISpinControl::OnMouseEvent(const CPoint &point, const CMouseEvent &event)
 956{
 957  if (event.m_id == ACTION_MOUSE_LEFT_CLICK)
 958  {
 959    if (m_imgspinUpFocus.HitTest(point))
 960      MoveUp();
 961    else if (m_imgspinDownFocus.HitTest(point))
 962      MoveDown();
 963    return EVENT_RESULT_HANDLED;
 964  }
 965  else if (event.m_id == ACTION_MOUSE_WHEEL_UP)
 966  {
 967    if (m_imgspinUpFocus.HitTest(point) || m_imgspinDownFocus.HitTest(point))
 968    {
 969      MoveUp();
 970      return EVENT_RESULT_HANDLED;
 971    }
 972  }
 973  else if (event.m_id == ACTION_MOUSE_WHEEL_DOWN)
 974  {
 975    if (m_imgspinUpFocus.HitTest(point) || m_imgspinDownFocus.HitTest(point))
 976    {
 977      MoveDown();
 978      return EVENT_RESULT_HANDLED;
 979    }
 980  }
 981  return EVENT_RESULT_UNHANDLED;
 982}
 983
 984std::string CGUISpinControl::GetDescription() const
 985{
 986  return StringUtils::Format("%i/%i", 1 + GetValue(), GetMaximum());
 987}
 988
 989bool CGUISpinControl::IsFocusedOnUp() const
 990{
 991  return (m_iSelect == SPIN_BUTTON_UP);
 992}
 993
 994void CGUISpinControl::ChangePage(int amount)
 995{
 996  m_currentItem += amount * m_itemsPerPage;
 997  if (m_currentItem > m_numItems - m_itemsPerPage)
 998    m_currentItem = m_numItems - m_itemsPerPage;
 999  if (m_currentItem < 0)
1000    m_currentItem = 0;
1001  CGUIMessage message(GUI_MSG_NOTIFY_ALL, GetParentID(), GetID(), GUI_MSG_PAGE_CHANGE, m_currentItem);
1002  SendWindowMessage(message);
1003}
1004
1005bool CGUISpinControl::UpdateColors()
1006{
1007  bool changed = CGUIControl::UpdateColors();
1008  changed |= m_label.UpdateColors();
1009  changed |= m_imgspinDownFocus.SetDiffuseColor(m_diffuseColor);
1010  changed |= m_imgspinDown.SetDiffuseColor(m_diffuseColor);
1011  changed |= m_imgspinUp.SetDiffuseColor(m_diffuseColor);
1012  changed |= m_imgspinUpFocus.SetDiffuseColor(m_diffuseColor);
1013  changed |= m_imgspinUpDisabled.SetDiffuseColor(m_diffuseColor);
1014  changed |= m_imgspinDownDisabled.SetDiffuseColor(m_diffuseColor);
1015
1016  return changed;
1017}
1018
1019bool CGUISpinControl::IsVisible() const
1020{
1021  // page controls can be optionally disabled if the number of pages is 1
1022  if (m_iType == SPIN_CONTROL_TYPE_PAGE && m_numItems <= m_itemsPerPage && !m_showOnePage)
1023    return false;
1024  return CGUIControl::IsVisible();
1025}