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