b9087cc03afccdeb5ad5ec427218dc288fa55bb9
[vuplus_xbmc] / xbmc / guilib / GUIListContainer.cpp
1 /*
2  *      Copyright (C) 2005-2013 Team XBMC
3  *      http://xbmc.org
4  *
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)
8  *  any later version.
9  *
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.
14  *
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/>.
18  *
19  */
20
21 #include "system.h"
22 #include "GUIListContainer.h"
23 #include "GUIListItem.h"
24 #include "Key.h"
25
26 CGUIListContainer::CGUIListContainer(int parentID, int controlID, float posX, float posY, float width, float height, ORIENTATION orientation, const CScroller& scroller, int preloadItems)
27     : CGUIBaseContainer(parentID, controlID, posX, posY, width, height, orientation, scroller, preloadItems)
28 {
29   ControlType = GUICONTAINER_LIST;
30   m_type = VIEW_TYPE_LIST;
31 }
32
33 CGUIListContainer::~CGUIListContainer(void)
34 {
35 }
36
37 bool CGUIListContainer::OnAction(const CAction &action)
38 {
39   switch (action.GetID())
40   {
41   case ACTION_PAGE_UP:
42     {
43       if (GetOffset() == 0)
44       { // already on the first page, so move to the first item
45         SetCursor(0);
46       }
47       else
48       { // scroll up to the previous page
49         Scroll( -m_itemsPerPage);
50       }
51       return true;
52     }
53     break;
54   case ACTION_PAGE_DOWN:
55     {
56       if (GetOffset() == (int)m_items.size() - m_itemsPerPage || (int)m_items.size() < m_itemsPerPage)
57       { // already at the last page, so move to the last item.
58         SetCursor(m_items.size() - GetOffset() - 1);
59       }
60       else
61       { // scroll down to the next page
62         Scroll(m_itemsPerPage);
63       }
64       return true;
65     }
66     break;
67     // smooth scrolling (for analog controls)
68   case ACTION_SCROLL_UP:
69     {
70       m_analogScrollCount += action.GetAmount() * action.GetAmount();
71       bool handled = false;
72       while (m_analogScrollCount > 0.4)
73       {
74         handled = true;
75         m_analogScrollCount -= 0.4f;
76         if (GetOffset() > 0 && GetCursor() <= m_itemsPerPage / 2)
77         {
78           Scroll(-1);
79         }
80         else if (GetCursor() > 0)
81         {
82           SetCursor(GetCursor() - 1);
83         }
84       }
85       return handled;
86     }
87     break;
88   case ACTION_SCROLL_DOWN:
89     {
90       m_analogScrollCount += action.GetAmount() * action.GetAmount();
91       bool handled = false;
92       while (m_analogScrollCount > 0.4)
93       {
94         handled = true;
95         m_analogScrollCount -= 0.4f;
96         if (GetOffset() + m_itemsPerPage < (int)m_items.size() && GetCursor() >= m_itemsPerPage / 2)
97         {
98           Scroll(1);
99         }
100         else if (GetCursor() < m_itemsPerPage - 1 && GetOffset() + GetCursor() < (int)m_items.size() - 1)
101         {
102           SetCursor(GetCursor() + 1);
103         }
104       }
105       return handled;
106     }
107     break;
108   }
109   return CGUIBaseContainer::OnAction(action);
110 }
111
112 bool CGUIListContainer::OnMessage(CGUIMessage& message)
113 {
114   if (message.GetControlId() == GetID() )
115   {
116     if (message.GetMessage() == GUI_MSG_LABEL_RESET)
117     {
118       SetCursor(0);
119     }
120     else if (message.GetMessage() == GUI_MSG_SETFOCUS)
121     {
122       if (message.GetParam1()) // subfocus item is specified, so set the offset appropriately
123       {
124         int item = std::min(GetOffset() + (int)message.GetParam1() - 1, (int)m_items.size() - 1);
125         SelectItem(item);
126       }
127     }
128   }
129   return CGUIBaseContainer::OnMessage(message);
130 }
131
132 bool CGUIListContainer::MoveUp(bool wrapAround)
133 {
134   if (GetCursor() > 0)
135   {
136     SetCursor(GetCursor() - 1);
137   }
138   else if (GetCursor() == 0 && GetOffset())
139   {
140     ScrollToOffset(GetOffset() - 1);
141   }
142   else if (wrapAround)
143   {
144     if (m_items.size() > 0)
145     { // move 2 last item in list, and set our container moving up
146       int offset = m_items.size() - m_itemsPerPage;
147       if (offset < 0) offset = 0;
148       SetCursor(m_items.size() - offset - 1);
149       ScrollToOffset(offset);
150       SetContainerMoving(-1);
151     }
152   }
153   else
154     return false;
155   return true;
156 }
157
158 bool CGUIListContainer::MoveDown(bool wrapAround)
159 {
160   if (GetOffset() + GetCursor() + 1 < (int)m_items.size())
161   {
162     if (GetCursor() + 1 < m_itemsPerPage)
163     {
164       SetCursor(GetCursor() + 1);
165     }
166     else
167     {
168       ScrollToOffset(GetOffset() + 1);
169     }
170   }
171   else if(wrapAround)
172   { // move first item in list, and set our container moving in the "down" direction
173     SetCursor(0);
174     ScrollToOffset(0);
175     SetContainerMoving(1);
176   }
177   else
178     return false;
179   return true;
180 }
181
182 // scrolls the said amount
183 void CGUIListContainer::Scroll(int amount)
184 {
185   // increase or decrease the offset
186   int offset = GetOffset() + amount;
187   if (offset > (int)m_items.size() - m_itemsPerPage)
188   {
189     offset = m_items.size() - m_itemsPerPage;
190   }
191   if (offset < 0) offset = 0;
192   ScrollToOffset(offset);
193 }
194
195 void CGUIListContainer::ValidateOffset()
196 {
197   if (!m_layout) return;
198   // first thing is we check the range of our offset
199   // don't validate offset if we are scrolling in case the tween image exceed <0, 1> range
200   int minOffset, maxOffset;
201   GetOffsetRange(minOffset, maxOffset);
202   if (GetOffset() > maxOffset || (!m_scroller.IsScrolling() && m_scroller.GetValue() > maxOffset * m_layout->Size(m_orientation)))
203   {
204     SetOffset(std::max(0, maxOffset));
205     m_scroller.SetValue(GetOffset() * m_layout->Size(m_orientation));
206   }
207   if (GetOffset() < 0 || (!m_scroller.IsScrolling() && m_scroller.GetValue() < 0))
208   {
209     SetOffset(0);
210     m_scroller.SetValue(0);
211   }
212 }
213
214 void CGUIListContainer::SetCursor(int cursor)
215 {
216   if (cursor > m_itemsPerPage - 1) cursor = m_itemsPerPage - 1;
217   if (cursor < 0) cursor = 0;
218   if (!m_wasReset)
219     SetContainerMoving(cursor - GetCursor());
220   CGUIBaseContainer::SetCursor(cursor);
221 }
222
223 void CGUIListContainer::SelectItem(int item)
224 {
225   // Check that our offset is valid
226   ValidateOffset();
227   // only select an item if it's in a valid range
228   if (item >= 0 && item < (int)m_items.size())
229   {
230     // Select the item requested
231     if (item >= GetOffset() && item < GetOffset() + m_itemsPerPage)
232     { // the item is on the current page, so don't change it.
233       SetCursor(item - GetOffset());
234     }
235     else if (item < GetOffset())
236     { // item is on a previous page - make it the first item on the page
237       SetCursor(0);
238       ScrollToOffset(item);
239     }
240     else // (item >= GetOffset()+m_itemsPerPage)
241     { // item is on a later page - make it the last item on the page
242       SetCursor(m_itemsPerPage - 1);
243       ScrollToOffset(item - GetCursor());
244     }
245   }
246 }
247
248 int CGUIListContainer::GetCursorFromPoint(const CPoint &point, CPoint *itemPoint) const
249 {
250   if (!m_focusedLayout || !m_layout)
251     return -1;
252
253   int row = 0;
254   float pos = (m_orientation == VERTICAL) ? point.y : point.x;
255   while (row < m_itemsPerPage + 1)  // 1 more to ensure we get the (possible) half item at the end.
256   {
257     const CGUIListItemLayout *layout = (row == GetCursor()) ? m_focusedLayout : m_layout;
258     if (pos < layout->Size(m_orientation) && row + GetOffset() < (int)m_items.size())
259     { // found correct "row" -> check horizontal
260       if (!InsideLayout(layout, point))
261         return -1;
262
263       if (itemPoint)
264         *itemPoint = m_orientation == VERTICAL ? CPoint(point.x, pos) : CPoint(pos, point.y);
265       return row;
266     }
267     row++;
268     pos -= layout->Size(m_orientation);
269   }
270   return -1;
271 }
272
273 bool CGUIListContainer::SelectItemFromPoint(const CPoint &point)
274 {
275   CPoint itemPoint;
276   int row = GetCursorFromPoint(point, &itemPoint);
277   if (row < 0)
278     return false;
279
280   SetCursor(row);
281   CGUIListItemLayout *focusedLayout = GetFocusedLayout();
282   if (focusedLayout)
283     focusedLayout->SelectItemFromPoint(itemPoint);
284   return true;
285 }
286
287 //#ifdef PRE_SKIN_VERSION_9_10_COMPATIBILITY
288 CGUIListContainer::CGUIListContainer(int parentID, int controlID, float posX, float posY, float width, float height,
289                                  const CLabelInfo& labelInfo, const CLabelInfo& labelInfo2,
290                                  const CTextureInfo& textureButton, const CTextureInfo& textureButtonFocus,
291                                  float textureHeight, float itemWidth, float itemHeight, float spaceBetweenItems)
292 : CGUIBaseContainer(parentID, controlID, posX, posY, width, height, VERTICAL, 200, 0)
293 {
294   CGUIListItemLayout layout;
295   layout.CreateListControlLayouts(width, textureHeight + spaceBetweenItems, false, labelInfo, labelInfo2, textureButton, textureButtonFocus, textureHeight, itemWidth, itemHeight, "", "");
296   m_layouts.push_back(layout);
297   CStdString condition;
298   condition.Format("control.hasfocus(%i)", controlID);
299   CStdString condition2 = "!" + condition;
300   CGUIListItemLayout focusLayout;
301   focusLayout.CreateListControlLayouts(width, textureHeight + spaceBetweenItems, true, labelInfo, labelInfo2, textureButton, textureButtonFocus, textureHeight, itemWidth, itemHeight, condition2, condition);
302   m_focusedLayouts.push_back(focusLayout);
303   m_height = floor(m_height / (textureHeight + spaceBetweenItems)) * (textureHeight + spaceBetweenItems);
304   ControlType = GUICONTAINER_LIST;
305 }
306 //#endif
307
308 bool CGUIListContainer::HasNextPage() const
309 {
310   return (GetOffset() != (int)m_items.size() - m_itemsPerPage && (int)m_items.size() >= m_itemsPerPage);
311 }
312
313 bool CGUIListContainer::HasPreviousPage() const
314 {
315   return (GetOffset() > 0);
316 }