aboutsummaryrefslogtreecommitdiff
path: root/guilib/GUIControlGroupList.cpp
diff options
context:
space:
mode:
authorAlTheKiller <AlTheKiller@svn>2009-09-23 01:49:50 +0000
committerAlTheKiller <AlTheKiller@svn>2009-09-23 01:49:50 +0000
commit45285e8a9300cd754a760560640b75b09f98035e (patch)
treead9f093885ad5c98e9dd4156674e7691c22ed0a2 /guilib/GUIControlGroupList.cpp
step 3/4: Move linuxport to trunk. How'd I get roped into this?
git-svn-id: https://xbmc.svn.sourceforge.net/svnroot/xbmc/trunk@23097 568bbfeb-2a22-0410-94d2-cc84cf5bfa90
Diffstat (limited to 'guilib/GUIControlGroupList.cpp')
-rw-r--r--guilib/GUIControlGroupList.cpp405
1 files changed, 405 insertions, 0 deletions
diff --git a/guilib/GUIControlGroupList.cpp b/guilib/GUIControlGroupList.cpp
new file mode 100644
index 0000000000..02c093f6ac
--- /dev/null
+++ b/guilib/GUIControlGroupList.cpp
@@ -0,0 +1,405 @@
+/*
+ * Copyright (C) 2005-2008 Team XBMC
+ * http://www.xbmc.org
+ *
+ * This Program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This Program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with XBMC; see the file COPYING. If not, write to
+ * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ * http://www.gnu.org/copyleft/gpl.html
+ *
+ */
+
+#include "GUIControlGroupList.h"
+#include "utils/GUIInfoManager.h"
+#include "GUIControlProfiler.h"
+
+#define TIME_TO_SCROLL 200;
+
+CGUIControlGroupList::CGUIControlGroupList(int parentID, int controlID, float posX, float posY, float width, float height, float itemGap, int pageControl, ORIENTATION orientation, bool useControlPositions, uint32_t alignment)
+: CGUIControlGroup(parentID, controlID, posX, posY, width, height)
+{
+ m_itemGap = itemGap;
+ m_pageControl = pageControl;
+ m_offset = 0;
+ m_totalSize = 10;
+ m_orientation = orientation;
+ m_alignment = alignment;
+ m_scrollOffset = 0;
+ m_scrollSpeed = 0;
+ m_scrollTime = 0;
+ m_renderTime = 0;
+ m_useControlPositions = useControlPositions;
+ ControlType = GUICONTROL_GROUPLIST;
+}
+
+CGUIControlGroupList::~CGUIControlGroupList(void)
+{
+}
+
+void CGUIControlGroupList::Render()
+{
+ if (m_scrollSpeed != 0)
+ {
+ m_offset += m_scrollSpeed * (m_renderTime - m_scrollTime);
+ if ((m_scrollSpeed < 0 && m_offset < m_scrollOffset) ||
+ (m_scrollSpeed > 0 && m_offset > m_scrollOffset))
+ {
+ m_offset = m_scrollOffset;
+ m_scrollSpeed = 0;
+ }
+ }
+ m_scrollTime = m_renderTime;
+
+ // first we update visibility of all our items, to ensure our size and
+ // alignment computations are correct.
+ for (iControls it = m_children.begin(); it != m_children.end(); ++it)
+ {
+ CGUIControl *control = *it;
+ GUIPROFILER_VISIBILITY_BEGIN(control);
+ control->UpdateVisibility();
+ GUIPROFILER_VISIBILITY_END(control);
+ }
+
+ ValidateOffset();
+ if (m_pageControl)
+ {
+ CGUIMessage message(GUI_MSG_LABEL_RESET, GetParentID(), m_pageControl, (int)m_height, (int)m_totalSize);
+ SendWindowMessage(message);
+ CGUIMessage message2(GUI_MSG_ITEM_SELECT, GetParentID(), m_pageControl, (int)m_offset);
+ SendWindowMessage(message2);
+ }
+ // we run through the controls, rendering as we go
+ bool render(g_graphicsContext.SetClipRegion(m_posX, m_posY, m_width, m_height));
+ float pos = GetAlignOffset();
+ float focusedPos = 0;
+ CGUIControl *focusedControl = NULL;
+ for (iControls it = m_children.begin(); it != m_children.end(); ++it)
+ {
+ // note we render all controls, even if they're offscreen, as then they'll be updated
+ // with respect to animations
+ CGUIControl *control = *it;
+ if (m_renderFocusedLast && control->HasFocus())
+ {
+ focusedControl = control;
+ focusedPos = pos;
+ }
+ else
+ {
+ if (m_orientation == VERTICAL)
+ g_graphicsContext.SetOrigin(m_posX, m_posY + pos - m_offset);
+ else
+ g_graphicsContext.SetOrigin(m_posX + pos - m_offset, m_posY);
+ control->DoRender(m_renderTime);
+ }
+ if (control->IsVisible())
+ pos += Size(control) + m_itemGap;
+ g_graphicsContext.RestoreOrigin();
+ }
+ if (focusedControl)
+ {
+ if (m_orientation == VERTICAL)
+ g_graphicsContext.SetOrigin(m_posX, m_posY + focusedPos - m_offset);
+ else
+ g_graphicsContext.SetOrigin(m_posX + focusedPos - m_offset, m_posY);
+ focusedControl->DoRender(m_renderTime);
+ }
+ if (render) g_graphicsContext.RestoreClipRegion();
+ CGUIControl::Render();
+}
+
+bool CGUIControlGroupList::OnMessage(CGUIMessage& message)
+{
+ switch (message.GetMessage() )
+ {
+ case GUI_MSG_FOCUSED:
+ { // a control has been focused
+ // scroll if we need to and update our page control
+ ValidateOffset();
+ float offset = 0;
+ for (iControls it = m_children.begin(); it != m_children.end(); ++it)
+ {
+ CGUIControl *control = *it;
+ if (!control->IsVisible())
+ continue;
+ if (control->HasID(message.GetControlId()))
+ {
+ // find out whether this is the first or last control
+ if (IsFirstFocusableControl(control))
+ ScrollTo(0);
+ else if (IsLastFocusableControl(control))
+ ScrollTo(m_totalSize - Size());
+ else if (offset < m_offset)
+ ScrollTo(offset);
+ else if (offset + Size(control) > m_offset + Size())
+ ScrollTo(offset + Size(control) - Size());
+ break;
+ }
+ offset += Size(control) + m_itemGap;
+ }
+ }
+ break;
+ case GUI_MSG_SETFOCUS:
+ {
+ // we've been asked to focus. We focus the last control if it's on this page,
+ // else we'll focus the first focusable control from our offset (after verifying it)
+ ValidateOffset();
+ // now check the focusControl's offset
+ float offset = 0;
+ for (iControls it = m_children.begin(); it != m_children.end(); ++it)
+ {
+ CGUIControl *control = *it;
+ if (!control->IsVisible())
+ continue;
+ if (control->HasID(m_focusedControl))
+ {
+ if (offset >= m_offset && offset + Size(control) <= m_offset + Size())
+ return CGUIControlGroup::OnMessage(message);
+ break;
+ }
+ offset += Size(control) + m_itemGap;
+ }
+ // find the first control on this page
+ offset = 0;
+ for (iControls it = m_children.begin(); it != m_children.end(); ++it)
+ {
+ CGUIControl *control = *it;
+ if (!control->IsVisible())
+ continue;
+ if (control->CanFocus() && offset >= m_offset && offset + Size(control) <= m_offset + Size())
+ {
+ m_focusedControl = control->GetID();
+ break;
+ }
+ offset += Size(control) + m_itemGap;
+ }
+ }
+ break;
+ case GUI_MSG_PAGE_CHANGE:
+ {
+ if (message.GetSenderId() == m_pageControl)
+ { // it's from our page control
+ ScrollTo((float)message.GetParam1());
+ return true;
+ }
+ }
+ break;
+ }
+ return CGUIControlGroup::OnMessage(message);
+}
+
+void CGUIControlGroupList::ValidateOffset()
+{
+ // calculate how many items we have on this page
+ m_totalSize = 0;
+ for (iControls it = m_children.begin(); it != m_children.end(); ++it)
+ {
+ CGUIControl *control = *it;
+ if (!control->IsVisible()) continue;
+ m_totalSize += Size(control) + m_itemGap;
+ }
+ if (m_totalSize > 0) m_totalSize -= m_itemGap;
+ // check our m_offset range
+ if (m_offset > m_totalSize - Size())
+ m_offset = m_totalSize - Size();
+ if (m_offset < 0) m_offset = 0;
+}
+
+void CGUIControlGroupList::AddControl(CGUIControl *control, int position /*= -1*/)
+{
+ // NOTE: We override control navigation here, but we don't override the <onleft> etc. builtins
+ // if specified.
+ if (position < 0 || position > (int)m_children.size()) // add at the end
+ position = (int)m_children.size();
+
+ if (control)
+ { // set the navigation of items so that they form a list
+ int beforeID = (m_orientation == VERTICAL) ? GetControlIdUp() : GetControlIdLeft();
+ int afterID = (m_orientation == VERTICAL) ? GetControlIdDown() : GetControlIdRight();
+ if (m_children.size())
+ {
+ // we're inserting at the given position, so grab the items above and below and alter
+ // their navigation accordingly
+ CGUIControl *before = NULL;
+ CGUIControl *after = NULL;
+ if (position == 0)
+ { // inserting at the beginning
+ after = m_children[0];
+ if (afterID == GetID()) // we're wrapping around bottom->top, so we have to update the last item
+ before = m_children[m_children.size() - 1];
+ if (beforeID == GetID()) // we're wrapping around top->bottom
+ beforeID = m_children[m_children.size() - 1]->GetID();
+ afterID = after->GetID();
+ }
+ else if (position == (int)m_children.size())
+ { // inserting at the end
+ before = m_children[m_children.size() - 1];
+ if (beforeID == GetID()) // we're wrapping around top->bottom, so we have to update the first item
+ after = m_children[0];
+ if (afterID == GetID()) // we're wrapping around bottom->top
+ afterID = m_children[0]->GetID();
+ beforeID = before->GetID();
+ }
+ else
+ { // inserting somewhere in the middle
+ before = m_children[position - 1];
+ after = m_children[position];
+ beforeID = before->GetID();
+ afterID = after->GetID();
+ }
+ if (m_orientation == VERTICAL)
+ {
+ if (before) // update the DOWN action to point to us
+ before->SetNavigation(before->GetControlIdUp(), control->GetID(), GetControlIdLeft(), GetControlIdRight());
+ if (after) // update the UP action to point to us
+ after->SetNavigation(control->GetID(), after->GetControlIdDown(), GetControlIdLeft(), GetControlIdRight());
+ }
+ else
+ {
+ if (before) // update the RIGHT action to point to us
+ before->SetNavigation(GetControlIdUp(), GetControlIdDown(), before->GetControlIdLeft(), control->GetID());
+ if (after) // update the LEFT action to point to us
+ after->SetNavigation(GetControlIdUp(), GetControlIdDown(), control->GetID(), after->GetControlIdRight());
+ }
+ }
+ // now the control's nav
+ if (m_orientation == VERTICAL)
+ control->SetNavigation(beforeID, afterID, GetControlIdLeft(), GetControlIdRight());
+ else
+ control->SetNavigation(GetControlIdUp(), GetControlIdDown(), beforeID, afterID);
+
+ if (!m_useControlPositions)
+ control->SetPosition(0,0);
+ CGUIControlGroup::AddControl(control, position);
+ }
+}
+
+void CGUIControlGroupList::ClearAll()
+{
+ CGUIControlGroup::ClearAll();
+ m_offset = 0;
+}
+
+inline float CGUIControlGroupList::Size(const CGUIControl *control) const
+{
+ return (m_orientation == VERTICAL) ? control->GetYPosition() + control->GetHeight() : control->GetXPosition() + control->GetWidth();
+}
+
+inline float CGUIControlGroupList::Size() const
+{
+ return (m_orientation == VERTICAL) ? m_height : m_width;
+}
+
+void CGUIControlGroupList::ScrollTo(float offset)
+{
+ m_scrollOffset = offset;
+ m_scrollSpeed = (m_scrollOffset - m_offset) / TIME_TO_SCROLL;
+}
+
+bool CGUIControlGroupList::CanFocusFromPoint(const CPoint &point, CGUIControl **control, CPoint &controlPoint) const
+{
+ if (!CGUIControl::CanFocus()) return false;
+ float pos = 0;
+ CPoint controlCoords(point);
+ m_transform.InverseTransformPosition(controlCoords.x, controlCoords.y);
+ float alignOffset = GetAlignOffset();
+ for (ciControls it = m_children.begin(); it != m_children.end(); ++it)
+ {
+ const CGUIControl *child = *it;
+ if (child->IsVisible())
+ {
+ if (pos + Size(child) > m_offset && pos < m_offset + Size())
+ { // we're on screen
+ float offsetX = m_orientation == VERTICAL ? m_posX : m_posX + alignOffset + pos - m_offset;
+ float offsetY = m_orientation == VERTICAL ? m_posY + alignOffset + pos - m_offset : m_posY;
+ if (child->CanFocusFromPoint(controlCoords - CPoint(offsetX, offsetY), control, controlPoint))
+ return true;
+ }
+ pos += Size(child) + m_itemGap;
+ }
+ }
+ *control = NULL;
+ return false;
+}
+
+void CGUIControlGroupList::UnfocusFromPoint(const CPoint &point)
+{
+ float pos = 0;
+ CPoint controlCoords(point);
+ m_transform.InverseTransformPosition(controlCoords.x, controlCoords.y);
+ float alignOffset = GetAlignOffset();
+ for (iControls it = m_children.begin(); it != m_children.end(); ++it)
+ {
+ CGUIControl *child = *it;
+ if (child->IsVisible())
+ {
+ if (pos + Size(child) > m_offset && pos < m_offset + Size())
+ { // we're on screen
+ CPoint offset = (m_orientation == VERTICAL) ? CPoint(m_posX, m_posY + alignOffset + pos - m_offset) : CPoint(m_posX + alignOffset + pos - m_offset, m_posY);
+ child->UnfocusFromPoint(controlCoords - offset);
+ }
+ pos += Size(child) + m_itemGap;
+ }
+ }
+ CGUIControl::UnfocusFromPoint(point);
+}
+
+bool CGUIControlGroupList::GetCondition(int condition, int data) const
+{
+ switch (condition)
+ {
+ case CONTAINER_HAS_NEXT:
+ return (m_totalSize >= Size() && m_offset < m_totalSize - Size());
+ case CONTAINER_HAS_PREVIOUS:
+ return (m_offset > 0);
+ default:
+ return false;
+ }
+}
+
+bool CGUIControlGroupList::IsFirstFocusableControl(const CGUIControl *control) const
+{
+ for (ciControls it = m_children.begin(); it != m_children.end(); ++it)
+ {
+ CGUIControl *child = *it;
+ if (child->IsVisible() && child->CanFocus())
+ { // found first focusable
+ return child == control;
+ }
+ }
+ return false;
+}
+
+bool CGUIControlGroupList::IsLastFocusableControl(const CGUIControl *control) const
+{
+ for (crControls it = m_children.rbegin(); it != m_children.rend(); ++it)
+ {
+ CGUIControl *child = *it;
+ if (child->IsVisible() && child->CanFocus())
+ { // found first focusable
+ return child == control;
+ }
+ }
+ return false;
+}
+
+float CGUIControlGroupList::GetAlignOffset() const
+{
+ if (m_totalSize < Size())
+ {
+ if (m_alignment & XBFONT_RIGHT)
+ return Size() - m_totalSize;
+ if (m_alignment & XBFONT_CENTER_X)
+ return (Size() - m_totalSize)*0.5f;
+ }
+ return 0.0f;
+}