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