[WASAPI] fix stream types and frequencies enumeration
[xbmc.git] / xbmc / guilib / GUIPanelContainer.cpp
blob3c757dc15730115d1380f9a2c63e77eaca363227
1 /*
2 * Copyright (C) 2005-2018 Team Kodi
3 * This file is part of Kodi - https://kodi.tv
5 * SPDX-License-Identifier: GPL-2.0-or-later
6 * See LICENSES/README.md for more information.
7 */
9 #include "GUIPanelContainer.h"
11 #include "FileItem.h"
12 #include "GUIListItemLayout.h"
13 #include "GUIMessage.h"
14 #include "guilib/guiinfo/GUIInfoLabels.h"
15 #include "input/actions/Action.h"
16 #include "input/actions/ActionIDs.h"
17 #include "utils/StringUtils.h"
19 #include <cassert>
21 CGUIPanelContainer::CGUIPanelContainer(int parentID, int controlID, float posX, float posY, float width, float height, ORIENTATION orientation, const CScroller& scroller, int preloadItems)
22 : CGUIBaseContainer(parentID, controlID, posX, posY, width, height, orientation, scroller, preloadItems)
24 ControlType = GUICONTAINER_PANEL;
25 m_type = VIEW_TYPE_ICON;
26 m_itemsPerRow = 1;
29 CGUIPanelContainer::~CGUIPanelContainer(void) = default;
31 void CGUIPanelContainer::Process(unsigned int currentTime, CDirtyRegionList &dirtyregions)
33 ValidateOffset();
35 if (m_bInvalidated)
36 UpdateLayout();
38 if (!m_layout || !m_focusedLayout)
39 return;
41 UpdateScrollOffset(currentTime);
43 int offset = (int)(m_scroller.GetValue() / m_layout->Size(m_orientation));
45 int cacheBefore, cacheAfter;
46 GetCacheOffsets(cacheBefore, cacheAfter);
48 // Free memory not used on screen
49 if ((int)m_items.size() > m_itemsPerPage + cacheBefore + cacheAfter)
50 FreeMemory(CorrectOffset(offset - cacheBefore, 0), CorrectOffset(offset + m_itemsPerPage + 1 + cacheAfter, 0));
52 CPoint origin = CPoint(m_posX, m_posY) + m_renderOffset;
53 float pos = (m_orientation == VERTICAL) ? origin.y : origin.x;
54 float end = (m_orientation == VERTICAL) ? m_posY + m_height : m_posX + m_width;
55 pos += (offset - cacheBefore) * m_layout->Size(m_orientation) - m_scroller.GetValue();
56 end += cacheAfter * m_layout->Size(m_orientation);
58 int current = (offset - cacheBefore) * m_itemsPerRow;
59 int col = 0;
60 while (pos < end && m_items.size())
62 if (current >= (int)m_items.size())
63 break;
64 if (current >= 0)
66 std::shared_ptr<CGUIListItem> item = m_items[current];
67 item->SetCurrentItem(current + 1);
68 bool focused = (current == GetOffset() * m_itemsPerRow + GetCursor()) && m_bHasFocus;
70 if (m_orientation == VERTICAL)
71 ProcessItem(origin.x + col * m_layout->Size(HORIZONTAL), pos, item, focused, currentTime, dirtyregions);
72 else
73 ProcessItem(pos, origin.y + col * m_layout->Size(VERTICAL), item, focused, currentTime, dirtyregions);
75 // increment our position
76 if (col < m_itemsPerRow - 1)
77 col++;
78 else
80 pos += m_layout->Size(m_orientation);
81 col = 0;
83 current++;
86 // when we are scrolling up, offset will become lower (integer division, see offset calc)
87 // to have same behaviour when scrolling down, we need to set page control to offset+1
88 UpdatePageControl(offset + (m_scroller.IsScrollingDown() ? 1 : 0));
90 CGUIControl::Process(currentTime, dirtyregions);
94 void CGUIPanelContainer::Render()
96 if (!m_layout || !m_focusedLayout)
97 return;
99 int offset = (int)(m_scroller.GetValue() / m_layout->Size(m_orientation));
101 int cacheBefore, cacheAfter;
102 GetCacheOffsets(cacheBefore, cacheAfter);
104 if (CServiceBroker::GetWinSystem()->GetGfxContext().SetClipRegion(m_posX, m_posY, m_width, m_height))
106 CPoint origin = CPoint(m_posX, m_posY) + m_renderOffset;
107 float pos = (m_orientation == VERTICAL) ? origin.y : origin.x;
108 float end = (m_orientation == VERTICAL) ? m_posY + m_height : m_posX + m_width;
109 pos += (offset - cacheBefore) * m_layout->Size(m_orientation) - m_scroller.GetValue();
110 end += cacheAfter * m_layout->Size(m_orientation);
112 float focusedPos = 0;
113 int focusedCol = 0;
114 std::shared_ptr<CGUIListItem> focusedItem;
115 int current = (offset - cacheBefore) * m_itemsPerRow;
116 int col = 0;
117 std::vector<RENDERITEM> renderitems;
118 while (pos < end && m_items.size())
120 if (current >= (int)m_items.size())
121 break;
122 if (current >= 0)
124 std::shared_ptr<CGUIListItem> item = m_items[current];
125 bool focused = (current == GetOffset() * m_itemsPerRow + GetCursor()) && m_bHasFocus;
126 // render our item
127 if (focused)
129 focusedPos = pos;
130 focusedCol = col;
131 focusedItem = item;
133 else
135 if (m_orientation == VERTICAL)
136 renderitems.emplace_back(
137 RENDERITEM{origin.x + col * m_layout->Size(HORIZONTAL), pos, item, false});
138 else
139 renderitems.emplace_back(
140 RENDERITEM{pos, origin.y + col * m_layout->Size(VERTICAL), item, false});
143 // increment our position
144 if (col < m_itemsPerRow - 1)
145 col++;
146 else
148 pos += m_layout->Size(m_orientation);
149 col = 0;
151 current++;
153 // and render the focused item last (for overlapping purposes)
154 if (focusedItem)
156 if (m_orientation == VERTICAL)
157 renderitems.emplace_back(RENDERITEM{origin.x + focusedCol * m_layout->Size(HORIZONTAL),
158 focusedPos, focusedItem, true});
159 else
160 renderitems.emplace_back(RENDERITEM{
161 focusedPos, origin.y + focusedCol * m_layout->Size(VERTICAL), focusedItem, true});
164 if (CServiceBroker::GetWinSystem()->GetGfxContext().GetRenderOrder() ==
165 RENDER_ORDER_FRONT_TO_BACK)
167 for (auto it = std::crbegin(renderitems); it != std::crend(renderitems); it++)
169 RenderItem(it->posX, it->posY, it->item.get(), it->focused);
172 else
174 for (const auto& renderitem : renderitems)
176 RenderItem(renderitem.posX, renderitem.posY, renderitem.item.get(), renderitem.focused);
180 CServiceBroker::GetWinSystem()->GetGfxContext().RestoreClipRegion();
182 CGUIControl::Render();
185 bool CGUIPanelContainer::OnAction(const CAction &action)
187 switch (action.GetID())
189 case ACTION_PAGE_UP:
191 if (GetOffset() == 0)
192 { // already on the first page, so move to the first item
193 SetCursor(0);
195 else
196 { // scroll up to the previous page
197 Scroll( -m_itemsPerPage);
199 return true;
201 break;
202 case ACTION_PAGE_DOWN:
204 if ((GetOffset() + m_itemsPerPage) * m_itemsPerRow >= (int)m_items.size() || (int)m_items.size() < m_itemsPerPage)
205 { // already at the last page, so move to the last item.
206 SetCursor(m_items.size() - GetOffset() * m_itemsPerRow - 1);
208 else
209 { // scroll down to the next page
210 Scroll(m_itemsPerPage);
212 return true;
214 break;
215 // smooth scrolling (for analog controls)
216 case ACTION_SCROLL_UP:
218 m_analogScrollCount += action.GetAmount() * action.GetAmount();
219 bool handled = false;
220 while (m_analogScrollCount > AnalogScrollSpeed())
222 handled = true;
223 m_analogScrollCount -= AnalogScrollSpeed();
224 if (GetOffset() > 0)// && GetCursor() <= m_itemsPerPage * m_itemsPerRow / 2)
226 Scroll(-1);
228 else if (GetCursor() > 0)
230 SetCursor(GetCursor() - 1);
233 return handled;
235 break;
236 case ACTION_SCROLL_DOWN:
238 m_analogScrollCount += action.GetAmount() * action.GetAmount();
239 bool handled = false;
240 while (m_analogScrollCount > AnalogScrollSpeed())
242 handled = true;
243 m_analogScrollCount -= AnalogScrollSpeed();
244 if ((GetOffset() + m_itemsPerPage) * m_itemsPerRow < (int)m_items.size())// && GetCursor() >= m_itemsPerPage * m_itemsPerRow / 2)
246 Scroll(1);
248 else if (GetCursor() < m_itemsPerPage * m_itemsPerRow - 1 && GetOffset() * m_itemsPerRow + GetCursor() < (int)m_items.size() - 1)
250 SetCursor(GetCursor() + 1);
253 return handled;
255 break;
257 return CGUIBaseContainer::OnAction(action);
260 bool CGUIPanelContainer::OnMessage(CGUIMessage& message)
262 if (message.GetControlId() == GetID() )
264 if (message.GetMessage() == GUI_MSG_LABEL_RESET)
266 SetCursor(0);
267 // fall through to base class
270 return CGUIBaseContainer::OnMessage(message);
273 void CGUIPanelContainer::OnLeft()
275 CGUIAction action = GetAction(ACTION_MOVE_LEFT);
276 bool wrapAround = action.GetNavigation() == GetID() || !action.HasActionsMeetingCondition();
277 if (m_orientation == VERTICAL && MoveLeft(wrapAround))
278 return;
279 if (m_orientation == HORIZONTAL && MoveUp(wrapAround))
280 return;
281 CGUIControl::OnLeft();
284 void CGUIPanelContainer::OnRight()
286 CGUIAction action = GetAction(ACTION_MOVE_RIGHT);
287 bool wrapAround = action.GetNavigation() == GetID() || !action.HasActionsMeetingCondition();
288 if (m_orientation == VERTICAL && MoveRight(wrapAround))
289 return;
290 if (m_orientation == HORIZONTAL && MoveDown(wrapAround))
291 return;
292 return CGUIControl::OnRight();
295 void CGUIPanelContainer::OnUp()
297 CGUIAction action = GetAction(ACTION_MOVE_UP);
298 bool wrapAround = action.GetNavigation() == GetID() || !action.HasActionsMeetingCondition();
299 if (m_orientation == VERTICAL && MoveUp(wrapAround))
300 return;
301 if (m_orientation == HORIZONTAL && MoveLeft(wrapAround))
302 return;
303 CGUIControl::OnUp();
306 void CGUIPanelContainer::OnDown()
308 CGUIAction action = GetAction(ACTION_MOVE_DOWN);
309 bool wrapAround = action.GetNavigation() == GetID() || !action.HasActionsMeetingCondition();
310 if (m_orientation == VERTICAL && MoveDown(wrapAround))
311 return;
312 if (m_orientation == HORIZONTAL && MoveRight(wrapAround))
313 return;
314 return CGUIControl::OnDown();
317 bool CGUIPanelContainer::MoveDown(bool wrapAround)
319 if (GetCursor() + m_itemsPerRow < m_itemsPerPage * m_itemsPerRow && (GetOffset() + 1 + GetCursor() / m_itemsPerRow) * m_itemsPerRow < (int)m_items.size())
320 { // move to last item if necessary
321 if ((GetOffset() + 1)*m_itemsPerRow + GetCursor() >= (int)m_items.size())
322 SetCursor((int)m_items.size() - 1 - GetOffset()*m_itemsPerRow);
323 else
324 SetCursor(GetCursor() + m_itemsPerRow);
326 else if ((GetOffset() + 1 + GetCursor() / m_itemsPerRow) * m_itemsPerRow < (int)m_items.size())
327 { // we scroll to the next row, and move to last item if necessary
328 if ((GetOffset() + 1)*m_itemsPerRow + GetCursor() >= (int)m_items.size())
329 SetCursor((int)m_items.size() - 1 - (GetOffset() + 1)*m_itemsPerRow);
330 ScrollToOffset(GetOffset() + 1);
332 else if (wrapAround)
333 { // move first item in list
334 SetCursor(GetCursor() % m_itemsPerRow);
335 ScrollToOffset(0);
336 SetContainerMoving(1);
338 else
339 return false;
340 return true;
343 bool CGUIPanelContainer::MoveUp(bool wrapAround)
345 if (GetCursor() >= m_itemsPerRow)
346 SetCursor(GetCursor() - m_itemsPerRow);
347 else if (GetOffset() > 0)
348 ScrollToOffset(GetOffset() - 1);
349 else if (wrapAround)
350 { // move last item in list in this column
351 SetCursor((GetCursor() % m_itemsPerRow) + (m_itemsPerPage - 1) * m_itemsPerRow);
352 int offset = std::max((int)GetRows() - m_itemsPerPage, 0);
353 // should check here whether cursor is actually allowed here, and reduce accordingly
354 if (offset * m_itemsPerRow + GetCursor() >= (int)m_items.size())
355 SetCursor((int)m_items.size() - offset * m_itemsPerRow - 1);
356 ScrollToOffset(offset);
357 SetContainerMoving(-1);
359 else
360 return false;
361 return true;
364 bool CGUIPanelContainer::MoveLeft(bool wrapAround)
366 int col = GetCursor() % m_itemsPerRow;
367 if (col > 0)
368 SetCursor(GetCursor() - 1);
369 else if (wrapAround)
370 { // wrap around
371 SetCursor(GetCursor() + m_itemsPerRow - 1);
372 if (GetOffset() * m_itemsPerRow + GetCursor() >= (int)m_items.size())
373 SetCursor((int)m_items.size() - GetOffset() * m_itemsPerRow - 1);
375 else
376 return false;
377 return true;
380 bool CGUIPanelContainer::MoveRight(bool wrapAround)
382 int col = GetCursor() % m_itemsPerRow;
383 if (col + 1 < m_itemsPerRow && GetOffset() * m_itemsPerRow + GetCursor() + 1 < (int)m_items.size())
384 SetCursor(GetCursor() + 1);
385 else if (wrapAround) // move first item in row
386 SetCursor(GetCursor() - col);
387 else
388 return false;
389 return true;
392 // scrolls the said amount
393 void CGUIPanelContainer::Scroll(int amount)
395 // increase or decrease the offset
396 int offset = GetOffset() + amount;
397 if (offset > ((int)GetRows() - m_itemsPerPage) * m_itemsPerRow)
399 offset = ((int)GetRows() - m_itemsPerPage) * m_itemsPerRow;
401 if (offset < 0) offset = 0;
402 ScrollToOffset(offset);
405 void CGUIPanelContainer::ValidateOffset()
407 if (!m_layout) return;
408 // first thing is we check the range of our offset
409 // don't validate offset if we are scrolling in case the tween image exceed <0, 1> range
410 if (GetOffset() > (int)GetRows() - m_itemsPerPage || (!m_scroller.IsScrolling() && m_scroller.GetValue() > ((int)GetRows() - m_itemsPerPage) * m_layout->Size(m_orientation)))
412 SetOffset(std::max(0, (int)GetRows() - m_itemsPerPage));
413 m_scroller.SetValue(GetOffset() * m_layout->Size(m_orientation));
415 if (GetOffset() < 0 || (!m_scroller.IsScrolling() && m_scroller.GetValue() < 0))
417 SetOffset(0);
418 m_scroller.SetValue(0);
422 void CGUIPanelContainer::SetCursor(int cursor)
424 // exceeds the number of items the panel can hold
425 if (cursor > m_itemsPerPage * m_itemsPerRow - 1)
426 cursor = m_itemsPerPage * m_itemsPerRow - 1;
428 // exceeds the number of items being displayed
429 const int itemsOn = m_items.size() - 1 - GetOffset() * m_itemsPerRow;
430 if (cursor > itemsOn)
431 cursor = itemsOn;
433 if (cursor < 0)
434 cursor = 0;
436 if (!m_wasReset)
437 SetContainerMoving(cursor - GetCursor());
438 CGUIBaseContainer::SetCursor(cursor);
441 void CGUIPanelContainer::CalculateLayout()
443 GetCurrentLayouts();
445 if (!m_layout || !m_focusedLayout) return;
446 // calculate the number of items to display
447 if (m_orientation == HORIZONTAL)
449 m_itemsPerRow = (int)(m_height / m_layout->Size(VERTICAL));
450 m_itemsPerPage = (int)(m_width / m_layout->Size(HORIZONTAL));
452 else
454 m_itemsPerRow = (int)(m_width / m_layout->Size(HORIZONTAL));
455 m_itemsPerPage = (int)(m_height / m_layout->Size(VERTICAL));
457 if (m_itemsPerRow < 1) m_itemsPerRow = 1;
458 if (m_itemsPerPage < 1) m_itemsPerPage = 1;
460 // ensure that the scroll offset is a multiple of our size
461 m_scroller.SetValue(GetOffset() * m_layout->Size(m_orientation));
464 unsigned int CGUIPanelContainer::GetRows() const
466 assert(m_itemsPerRow > 0);
467 return (m_items.size() + m_itemsPerRow - 1) / m_itemsPerRow;
470 float CGUIPanelContainer::AnalogScrollSpeed() const
472 return 10.0f / m_itemsPerPage;
475 int CGUIPanelContainer::CorrectOffset(int offset, int cursor) const
477 return offset * m_itemsPerRow + cursor;
480 int CGUIPanelContainer::GetCursorFromPoint(const CPoint &point, CPoint *itemPoint) const
482 if (!m_layout)
483 return -1;
485 float sizeX = m_orientation == VERTICAL ? m_layout->Size(HORIZONTAL) : m_layout->Size(VERTICAL);
486 float sizeY = m_orientation == VERTICAL ? m_layout->Size(VERTICAL) : m_layout->Size(HORIZONTAL);
488 float posY = m_orientation == VERTICAL ? point.y : point.x;
489 for (int y = 0; y < m_itemsPerPage + 1; y++) // +1 to ensure if we have a half item we can select it
491 float posX = m_orientation == VERTICAL ? point.x : point.y;
492 for (int x = 0; x < m_itemsPerRow; x++)
494 int item = x + y * m_itemsPerRow;
495 if (posX < sizeX && posY < sizeY && item + GetOffset() < (int)m_items.size())
496 { // found
497 return item;
499 posX -= sizeX;
501 posY -= sizeY;
503 return -1;
506 bool CGUIPanelContainer::SelectItemFromPoint(const CPoint &point)
508 int cursor = GetCursorFromPoint(point);
509 if (cursor < 0)
510 return false;
511 SetCursor(cursor);
512 return true;
515 int CGUIPanelContainer::GetCurrentRow() const
517 return m_itemsPerRow > 0 ? GetCursor() / m_itemsPerRow : 0;
520 int CGUIPanelContainer::GetCurrentColumn() const
522 return GetCursor() % m_itemsPerRow;
525 bool CGUIPanelContainer::GetCondition(int condition, int data) const
527 int row = GetCurrentRow();
528 int col = GetCurrentColumn();
530 if (m_orientation == HORIZONTAL)
531 std::swap(row, col);
533 switch (condition)
535 case CONTAINER_ROW:
536 return (row == data);
537 case CONTAINER_COLUMN:
538 return (col == data);
539 default:
540 return CGUIBaseContainer::GetCondition(condition, data);
544 std::string CGUIPanelContainer::GetLabel(int info) const
546 int row = GetCurrentRow();
547 int col = GetCurrentColumn();
549 if (m_orientation == HORIZONTAL)
550 std::swap(row, col);
552 switch (info)
554 case CONTAINER_ROW:
555 return std::to_string(row);
556 case CONTAINER_COLUMN:
557 return std::to_string(col);
558 default:
559 return CGUIBaseContainer::GetLabel(info);
561 return StringUtils::Empty;
564 void CGUIPanelContainer::SelectItem(int item)
566 // Check that our offset is valid
567 ValidateOffset();
568 // only select an item if it's in a valid range
569 if (item >= 0 && item < (int)m_items.size())
571 // Select the item requested
572 if (item >= GetOffset() * m_itemsPerRow && item < (GetOffset() + m_itemsPerPage) * m_itemsPerRow)
573 { // the item is on the current page, so don't change it.
574 SetCursor(item - GetOffset() * m_itemsPerRow);
576 else if (item < GetOffset() * m_itemsPerRow)
577 { // item is on a previous page - make it the first item on the page
578 SetCursor(item % m_itemsPerRow);
579 ScrollToOffset((item - GetCursor()) / m_itemsPerRow);
581 else // (item >= GetOffset()+m_itemsPerPage)
582 { // item is on a later page - make it the last row on the page
583 SetCursor(item % m_itemsPerRow + m_itemsPerRow * (m_itemsPerPage - 1));
584 ScrollToOffset((item - GetCursor()) / m_itemsPerRow);
589 bool CGUIPanelContainer::HasPreviousPage() const
591 return (GetOffset() > 0);
594 bool CGUIPanelContainer::HasNextPage() const
596 return (GetOffset() != (int)GetRows() - m_itemsPerPage && (int)GetRows() > m_itemsPerPage);
599 void CGUIPanelContainer::ScrollToOffset(int offset)
601 CGUIBaseContainer::ScrollToOffset(offset);
602 SetCursor(GetCursor());