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.
9 #include "GUIBaseContainer.h"
12 #include "FileItemList.h"
13 #include "GUIInfoManager.h"
14 #include "GUIListItemLayout.h"
15 #include "GUIMessage.h"
16 #include "ServiceBroker.h"
17 #include "guilib/GUIListItem.h"
18 #include "guilib/guiinfo/GUIInfoLabels.h"
19 #include "guilib/listproviders/IListProvider.h"
20 #include "input/actions/Action.h"
21 #include "input/actions/ActionIDs.h"
22 #include "input/keyboard/KeyIDs.h"
23 #include "input/mouse/MouseEvent.h"
24 #include "settings/Settings.h"
25 #include "settings/SettingsComponent.h"
26 #include "utils/CharsetConverter.h"
27 #include "utils/MathUtils.h"
28 #include "utils/SortUtils.h"
29 #include "utils/StringUtils.h"
30 #include "utils/TimeUtils.h"
31 #include "utils/XBMCTinyXML.h"
32 #include "utils/log.h"
38 #define HOLD_TIME_START 100
39 #define HOLD_TIME_END 3000
40 #define SCROLLING_GAP 200U
41 #define SCROLLING_THRESHOLD 300U
43 CGUIBaseContainer::CGUIBaseContainer(int parentID
, int controlID
, float posX
, float posY
, float width
, float height
, ORIENTATION orientation
, const CScroller
& scroller
, int preloadItems
)
44 : IGUIContainer(parentID
, controlID
, posX
, posY
, width
, height
)
45 , m_scroller(scroller
)
52 m_orientation
= orientation
;
53 m_analogScrollCount
= 0;
56 m_focusedLayout
= NULL
;
57 m_cacheItems
= preloadItems
;
58 m_scrollItemsPerFrame
= 0.0f
;
59 m_type
= VIEW_TYPE_NONE
;
60 m_autoScrollMoveTime
= 0;
61 m_autoScrollDelayTime
= 0;
62 m_autoScrollIsReversed
= false;
66 CGUIBaseContainer::CGUIBaseContainer(const CGUIBaseContainer
& other
)
67 : IGUIContainer(other
),
68 m_renderOffset(other
.m_renderOffset
),
69 m_analogScrollCount(other
.m_analogScrollCount
),
70 m_lastHoldTime(other
.m_lastHoldTime
),
71 m_orientation(other
.m_orientation
),
72 m_itemsPerPage(other
.m_itemsPerPage
),
73 m_pageControl(other
.m_pageControl
),
74 m_layoutCondition(other
.m_layoutCondition
),
75 m_focusedLayoutCondition(other
.m_focusedLayoutCondition
),
76 m_scroller(other
.m_scroller
),
77 m_listProvider(other
.m_listProvider
? other
.m_listProvider
->Clone() : nullptr),
78 m_wasReset(other
.m_wasReset
),
79 m_letterOffsets(other
.m_letterOffsets
),
80 m_autoScrollCondition(other
.m_autoScrollCondition
),
81 m_autoScrollMoveTime(other
.m_autoScrollMoveTime
),
82 m_autoScrollDelayTime(other
.m_autoScrollDelayTime
),
83 m_autoScrollIsReversed(other
.m_autoScrollIsReversed
),
84 m_lastRenderTime(other
.m_lastRenderTime
),
85 m_cursor(other
.m_cursor
),
86 m_offset(other
.m_offset
),
87 m_cacheItems(other
.m_cacheItems
),
88 m_scrollTimer(other
.m_scrollTimer
),
89 m_lastScrollStartTimer(other
.m_lastScrollStartTimer
),
90 m_pageChangeTimer(other
.m_pageChangeTimer
),
91 m_clickActions(other
.m_clickActions
),
92 m_focusActions(other
.m_focusActions
),
93 m_unfocusActions(other
.m_unfocusActions
),
94 m_matchTimer(other
.m_matchTimer
),
95 m_match(other
.m_match
),
96 m_scrollItemsPerFrame(other
.m_scrollItemsPerFrame
),
97 m_gestureActive(other
.m_gestureActive
),
98 m_waitForScrollEnd(other
.m_waitForScrollEnd
),
99 m_lastScrollValue(other
.m_lastScrollValue
)
101 // Initialize CGUIControl
102 m_bInvalidated
= true;
104 for (const auto& item
: other
.m_items
)
105 m_items
.emplace_back(std::make_shared
<CGUIListItem
>(*item
));
107 for (const auto& layout
: other
.m_layouts
)
108 m_layouts
.emplace_back(layout
, this);
110 for (const auto& focusedLayout
: other
.m_focusedLayouts
)
111 m_focusedLayouts
.emplace_back(focusedLayout
, this);
114 CGUIBaseContainer::~CGUIBaseContainer(void)
116 // release the container from items
117 for (const auto& item
: m_items
)
121 void CGUIBaseContainer::DoProcess(unsigned int currentTime
, CDirtyRegionList
&dirtyregions
)
123 CGUIControl::DoProcess(currentTime
, dirtyregions
);
125 if (m_pageChangeTimer
.IsRunning() && m_pageChangeTimer
.GetElapsedMilliseconds() > 200)
126 m_pageChangeTimer
.Stop();
129 // if not visible, we reset the autoscroll timer
130 if (!IsVisible() && m_autoScrollMoveTime
)
132 ResetAutoScrolling();
136 void CGUIBaseContainer::Process(unsigned int currentTime
, CDirtyRegionList
&dirtyregions
)
138 // update our auto-scrolling as necessary
139 UpdateAutoScrolling(currentTime
);
141 if (!m_waitForScrollEnd
&& !m_gestureActive
)
147 if (!m_layout
|| !m_focusedLayout
) return;
149 UpdateScrollOffset(currentTime
);
151 if (m_scroller
.IsScrolling())
154 int offset
= (int)floorf(m_scroller
.GetValue() / m_layout
->Size(m_orientation
));
156 int cacheBefore
, cacheAfter
;
157 GetCacheOffsets(cacheBefore
, cacheAfter
);
159 // Free memory not used on screen
160 if ((int)m_items
.size() > m_itemsPerPage
+ cacheBefore
+ cacheAfter
)
161 FreeMemory(CorrectOffset(offset
- cacheBefore
, 0), CorrectOffset(offset
+ m_itemsPerPage
+ 1 + cacheAfter
, 0));
163 CPoint origin
= CPoint(m_posX
, m_posY
) + m_renderOffset
;
164 float pos
= (m_orientation
== VERTICAL
) ? origin
.y
: origin
.x
;
165 float end
= (m_orientation
== VERTICAL
) ? m_posY
+ m_height
: m_posX
+ m_width
;
167 // we offset our draw position to take into account scrolling and whether or not our focused
168 // item is offscreen "above" the list.
169 float drawOffset
= (offset
- cacheBefore
) * m_layout
->Size(m_orientation
) - m_scroller
.GetValue();
170 if (GetOffset() + GetCursor() < offset
)
171 drawOffset
+= m_focusedLayout
->Size(m_orientation
) - m_layout
->Size(m_orientation
);
173 end
+= cacheAfter
* m_layout
->Size(m_orientation
);
175 int current
= offset
- cacheBefore
;
176 while (pos
< end
&& m_items
.size())
178 int itemNo
= CorrectOffset(current
, 0);
179 if (itemNo
>= (int)m_items
.size())
181 bool focused
= (current
== GetOffset() + GetCursor());
184 std::shared_ptr
<CGUIListItem
> item
= m_items
[itemNo
];
185 item
->SetCurrentItem(itemNo
+ 1);
188 if (m_orientation
== VERTICAL
)
189 ProcessItem(origin
.x
, pos
, item
, focused
, currentTime
, dirtyregions
);
191 ProcessItem(pos
, origin
.y
, item
, focused
, currentTime
, dirtyregions
);
193 // increment our position
194 pos
+= focused
? m_focusedLayout
->Size(m_orientation
) : m_layout
->Size(m_orientation
);
198 // when we are scrolling up, offset will become lower (integer division, see offset calc)
199 // to have same behaviour when scrolling down, we need to set page control to offset+1
200 UpdatePageControl(offset
+ (m_scroller
.IsScrollingDown() ? 1 : 0));
202 m_lastRenderTime
= currentTime
;
204 CGUIControl::Process(currentTime
, dirtyregions
);
207 void CGUIBaseContainer::ProcessItem(float posX
,
209 std::shared_ptr
<CGUIListItem
>& item
,
211 unsigned int currentTime
,
212 CDirtyRegionList
& dirtyregions
)
214 if (!m_focusedLayout
|| !m_layout
) return;
217 CServiceBroker::GetWinSystem()->GetGfxContext().SetOrigin(posX
, posY
);
223 if (!item
->GetFocusedLayout())
225 item
->SetFocusedLayout(std::make_unique
<CGUIListItemLayout
>(*m_focusedLayout
, this));
227 if (item
->GetFocusedLayout())
229 if (item
!= m_lastItem
|| !HasFocus())
231 item
->GetFocusedLayout()->SetFocusedItem(0);
233 if (item
!= m_lastItem
&& HasFocus())
235 item
->GetFocusedLayout()->ResetAnimation(ANIM_TYPE_UNFOCUS
);
236 unsigned int subItem
= 1;
237 if (m_lastItem
&& m_lastItem
->GetFocusedLayout())
238 subItem
= m_lastItem
->GetFocusedLayout()->GetFocusedItem();
239 item
->GetFocusedLayout()->SetFocusedItem(subItem
? subItem
: 1);
241 item
->GetFocusedLayout()->Process(item
.get(), m_parentID
, currentTime
, dirtyregions
);
247 if (item
->GetFocusedLayout())
248 item
->GetFocusedLayout()->SetFocusedItem(0); // focus is not set
249 if (!item
->GetLayout())
251 auto layout
= std::make_unique
<CGUIListItemLayout
>(*m_layout
, this);
252 item
->SetLayout(std::move(layout
));
254 if (item
->GetFocusedLayout() && item
->GetFocusedLayout()->IsAnimating(ANIM_TYPE_UNFOCUS
))
255 item
->GetFocusedLayout()->Process(item
.get(), m_parentID
, currentTime
, dirtyregions
);
256 if (item
->GetLayout())
257 item
->GetLayout()->Process(item
.get(), m_parentID
, currentTime
, dirtyregions
);
260 CServiceBroker::GetWinSystem()->GetGfxContext().RestoreOrigin();
263 void CGUIBaseContainer::Render()
265 if (!m_layout
|| !m_focusedLayout
) return;
267 int offset
= (int)floorf(m_scroller
.GetValue() / m_layout
->Size(m_orientation
));
269 int cacheBefore
, cacheAfter
;
270 GetCacheOffsets(cacheBefore
, cacheAfter
);
272 if (CServiceBroker::GetWinSystem()->GetGfxContext().SetClipRegion(m_posX
, m_posY
, m_width
, m_height
))
274 CPoint origin
= CPoint(m_posX
, m_posY
) + m_renderOffset
;
275 float pos
= (m_orientation
== VERTICAL
) ? origin
.y
: origin
.x
;
276 float end
= (m_orientation
== VERTICAL
) ? m_posY
+ m_height
: m_posX
+ m_width
;
278 // we offset our draw position to take into account scrolling and whether or not our focused
279 // item is offscreen "above" the list.
280 float drawOffset
= (offset
- cacheBefore
) * m_layout
->Size(m_orientation
) - m_scroller
.GetValue();
281 if (GetOffset() + GetCursor() < offset
)
282 drawOffset
+= m_focusedLayout
->Size(m_orientation
) - m_layout
->Size(m_orientation
);
284 end
+= cacheAfter
* m_layout
->Size(m_orientation
);
286 float focusedPos
= 0;
287 std::shared_ptr
<CGUIListItem
> focusedItem
;
288 int current
= offset
- cacheBefore
;
290 std::vector
<RENDERITEM
> renderitems
;
291 while (pos
< end
&& m_items
.size())
293 int itemNo
= CorrectOffset(current
, 0);
294 if (itemNo
>= (int)m_items
.size())
296 bool focused
= (current
== GetOffset() + GetCursor());
299 std::shared_ptr
<CGUIListItem
> item
= m_items
[itemNo
];
308 if (m_orientation
== VERTICAL
)
309 renderitems
.emplace_back(RENDERITEM
{origin
.x
, pos
, item
, false});
311 renderitems
.emplace_back(RENDERITEM
{pos
, origin
.y
, item
, false});
314 // increment our position
315 pos
+= focused
? m_focusedLayout
->Size(m_orientation
) : m_layout
->Size(m_orientation
);
318 // render focused item last so it can overlap other items
321 if (m_orientation
== VERTICAL
)
322 renderitems
.emplace_back(RENDERITEM
{origin
.x
, focusedPos
, focusedItem
, true});
324 renderitems
.emplace_back(RENDERITEM
{focusedPos
, origin
.y
, focusedItem
, true});
327 if (CServiceBroker::GetWinSystem()->GetGfxContext().GetRenderOrder() ==
328 RENDER_ORDER_FRONT_TO_BACK
)
330 for (auto it
= std::crbegin(renderitems
); it
!= std::crend(renderitems
); it
++)
332 RenderItem(it
->posX
, it
->posY
, it
->item
.get(), it
->focused
);
337 for (const auto& renderitem
: renderitems
)
339 RenderItem(renderitem
.posX
, renderitem
.posY
, renderitem
.item
.get(), renderitem
.focused
);
343 CServiceBroker::GetWinSystem()->GetGfxContext().RestoreClipRegion();
346 CGUIControl::Render();
350 void CGUIBaseContainer::RenderItem(float posX
, float posY
, CGUIListItem
*item
, bool focused
)
352 if (!m_focusedLayout
|| !m_layout
) return;
355 CServiceBroker::GetWinSystem()->GetGfxContext().SetOrigin(posX
, posY
);
359 if (item
->GetFocusedLayout())
360 item
->GetFocusedLayout()->Render(item
, m_parentID
);
364 if (item
->GetFocusedLayout() && item
->GetFocusedLayout()->IsAnimating(ANIM_TYPE_UNFOCUS
))
365 item
->GetFocusedLayout()->Render(item
, m_parentID
);
366 else if (item
->GetLayout())
367 item
->GetLayout()->Render(item
, m_parentID
);
369 CServiceBroker::GetWinSystem()->GetGfxContext().RestoreOrigin();
372 bool CGUIBaseContainer::OnAction(const CAction
&action
)
374 if (action
.GetID() == KEY_UNICODE
)
377 g_charsetConverter
.wToUTF8({action
.GetUnicode()}, letter
);
378 OnJumpLetter(letter
);
381 // stop the timer on any other action
384 switch (action
.GetID())
386 case ACTION_MOVE_LEFT
:
387 case ACTION_MOVE_RIGHT
:
388 case ACTION_MOVE_DOWN
:
390 case ACTION_NAV_BACK
:
391 case ACTION_PREVIOUS_MENU
:
393 if (!HasFocus()) return false;
395 if (action
.GetHoldTime() > HOLD_TIME_START
&&
396 ((m_orientation
== VERTICAL
&& (action
.GetID() == ACTION_MOVE_UP
|| action
.GetID() == ACTION_MOVE_DOWN
)) ||
397 (m_orientation
== HORIZONTAL
&& (action
.GetID() == ACTION_MOVE_LEFT
|| action
.GetID() == ACTION_MOVE_RIGHT
))))
398 { // action is held down - repeat a number of times
399 float speed
= std::min(1.0f
, (float)(action
.GetHoldTime() - HOLD_TIME_START
) / (HOLD_TIME_END
- HOLD_TIME_START
));
400 unsigned int frameDuration
= std::min(CTimeUtils::GetFrameTime() - m_lastHoldTime
, 50u); // max 20fps
402 // maximal scroll rate is at least 30 items per second, and at most (item_rows/7) items per second
403 // i.e. timed to take 7 seconds to traverse the list at full speed.
404 // minimal scroll rate is at least 10 items per second
405 float maxSpeed
= std::max(frameDuration
* 0.001f
* 30, frameDuration
* 0.001f
* GetRows() / 7);
406 float minSpeed
= frameDuration
* 0.001f
* 10;
407 m_scrollItemsPerFrame
+= std::max(minSpeed
, speed
*maxSpeed
); // accelerate to max speed
408 m_lastHoldTime
= CTimeUtils::GetFrameTime();
410 if(m_scrollItemsPerFrame
< 1.0f
)//not enough hold time accumulated for one step
413 while (m_scrollItemsPerFrame
>= 1)
415 if (action
.GetID() == ACTION_MOVE_LEFT
|| action
.GetID() == ACTION_MOVE_UP
)
419 m_scrollItemsPerFrame
--;
425 //if HOLD_TIME_START is reached we need
426 //a sane initial value for calculating m_scrollItemsPerPage
427 m_lastHoldTime
= CTimeUtils::GetFrameTime();
428 m_scrollItemsPerFrame
= 0.0f
;
429 return CGUIControl::OnAction(action
);
432 case ACTION_CONTEXT_MENU
:
436 case ACTION_SHOW_INFO
:
439 const int selected
= GetSelectedItem();
440 if (selected
>= 0 && selected
< static_cast<int>(m_items
.size()))
442 if (m_listProvider
->OnInfo(m_items
[selected
]))
448 else if (action
.GetID())
449 return OnClick(action
.GetID());
453 case ACTION_PLAYER_PLAY
:
456 const int selected
= GetSelectedItem();
457 if (selected
>= 0 && selected
< static_cast<int>(m_items
.size()))
459 if (m_listProvider
->OnPlay(m_items
[selected
]))
465 case ACTION_FIRST_PAGE
:
469 case ACTION_LAST_PAGE
:
471 SelectItem(m_items
.size() - 1);
474 case ACTION_NEXT_LETTER
:
477 case ACTION_PREV_LETTER
:
480 case ACTION_JUMP_SMS2
:
481 case ACTION_JUMP_SMS3
:
482 case ACTION_JUMP_SMS4
:
483 case ACTION_JUMP_SMS5
:
484 case ACTION_JUMP_SMS6
:
485 case ACTION_JUMP_SMS7
:
486 case ACTION_JUMP_SMS8
:
487 case ACTION_JUMP_SMS9
:
488 OnJumpSMS(action
.GetID() - ACTION_JUMP_SMS2
+ 2);
494 return action
.GetID() && OnClick(action
.GetID());
497 bool CGUIBaseContainer::OnMessage(CGUIMessage
& message
)
499 if (message
.GetControlId() == GetID() )
503 if (message
.GetMessage() == GUI_MSG_LABEL_BIND
&& message
.GetPointer())
506 CFileItemList
*items
= static_cast<CFileItemList
*>(message
.GetPointer());
507 for (int i
= 0; i
< items
->Size(); i
++)
508 m_items
.push_back(items
->Get(i
));
509 UpdateLayout(true); // true to refresh all items
510 UpdateScrollByLetter();
511 SelectItem(message
.GetParam1());
514 else if (message
.GetMessage() == GUI_MSG_LABEL_RESET
)
517 SetPageControlRange();
521 if (message
.GetMessage() == GUI_MSG_ITEM_SELECT
)
523 SelectItem(message
.GetParam1());
526 else if (message
.GetMessage() == GUI_MSG_SETFOCUS
)
528 if (message
.GetParam1()) // subfocus item is specified, so set the offset appropriately
530 int offset
= GetOffset();
531 if (message
.GetParam2() && message
.GetParam2() == 1)
533 int item
= std::min(offset
+ message
.GetParam1() - 1, (int)m_items
.size() - 1);
537 else if (message
.GetMessage() == GUI_MSG_ITEM_SELECTED
)
539 message
.SetParam1(GetSelectedItem());
542 else if (message
.GetMessage() == GUI_MSG_PAGE_CHANGE
)
544 if (message
.GetSenderId() == m_pageControl
&& IsVisible())
545 { // update our page if we're visible - not much point otherwise
546 if (message
.GetParam1() != GetOffset())
547 m_pageChangeTimer
.StartZero();
548 ScrollToOffset(message
.GetParam1());
552 else if (message
.GetMessage() == GUI_MSG_REFRESH_LIST
)
553 { // update our list contents
554 for (unsigned int i
= 0; i
< m_items
.size(); ++i
)
555 m_items
[i
]->SetInvalid();
557 else if (message
.GetMessage() == GUI_MSG_REFRESH_THUMBS
)
560 m_listProvider
->FreeResources(true);
562 else if (message
.GetMessage() == GUI_MSG_MOVE_OFFSET
)
564 int count
= message
.GetParam1();
578 return CGUIControl::OnMessage(message
);
581 void CGUIBaseContainer::OnUp()
583 CGUIAction action
= GetAction(ACTION_MOVE_UP
);
584 bool wrapAround
= action
.GetNavigation() == GetID() || !action
.HasActionsMeetingCondition();
585 if (m_orientation
== VERTICAL
&& MoveUp(wrapAround
))
587 // with horizontal lists it doesn't make much sense to have multiselect labels
591 void CGUIBaseContainer::OnDown()
593 CGUIAction action
= GetAction(ACTION_MOVE_DOWN
);
594 bool wrapAround
= action
.GetNavigation() == GetID() || !action
.HasActionsMeetingCondition();
595 if (m_orientation
== VERTICAL
&& MoveDown(wrapAround
))
597 // with horizontal lists it doesn't make much sense to have multiselect labels
598 CGUIControl::OnDown();
601 void CGUIBaseContainer::OnLeft()
603 CGUIAction action
= GetAction(ACTION_MOVE_LEFT
);
604 bool wrapAround
= action
.GetNavigation() == GetID() || !action
.HasActionsMeetingCondition();
605 if (m_orientation
== HORIZONTAL
&& MoveUp(wrapAround
))
607 else if (m_orientation
== VERTICAL
)
609 CGUIListItemLayout
*focusedLayout
= GetFocusedLayout();
610 if (focusedLayout
&& focusedLayout
->MoveLeft())
613 CGUIControl::OnLeft();
616 void CGUIBaseContainer::OnRight()
618 CGUIAction action
= GetAction(ACTION_MOVE_RIGHT
);
619 bool wrapAround
= action
.GetNavigation() == GetID() || !action
.HasActionsMeetingCondition();
620 if (m_orientation
== HORIZONTAL
&& MoveDown(wrapAround
))
622 else if (m_orientation
== VERTICAL
)
624 CGUIListItemLayout
*focusedLayout
= GetFocusedLayout();
625 if (focusedLayout
&& focusedLayout
->MoveRight())
628 CGUIControl::OnRight();
631 void CGUIBaseContainer::OnNextLetter()
633 int offset
= CorrectOffset(GetOffset(), GetCursor());
634 for (unsigned int i
= 0; i
< m_letterOffsets
.size(); i
++)
636 if (m_letterOffsets
[i
].first
> offset
)
638 SelectItem(m_letterOffsets
[i
].first
);
644 void CGUIBaseContainer::OnPrevLetter()
646 int offset
= CorrectOffset(GetOffset(), GetCursor());
647 if (!m_letterOffsets
.size())
649 for (int i
= (int)m_letterOffsets
.size() - 1; i
>= 0; i
--)
651 if (m_letterOffsets
[i
].first
< offset
)
653 SelectItem(m_letterOffsets
[i
].first
);
659 void CGUIBaseContainer::OnJumpLetter(const std::string
& letter
, bool skip
/*=false*/)
661 if (m_matchTimer
.GetElapsedMilliseconds() < letter_match_timeout
)
666 m_matchTimer
.StartZero();
668 // we can't jump through letters if we have none
669 if (0 == m_letterOffsets
.size())
672 // find the current letter we're focused on
673 unsigned int offset
= CorrectOffset(GetOffset(), GetCursor());
674 unsigned int i
= (offset
+ ((skip
) ? 1 : 0)) % m_items
.size();
677 std::shared_ptr
<CGUIListItem
> item
= m_items
[i
];
678 std::string label
= item
->GetLabel();
679 if (CServiceBroker::GetSettingsComponent()->GetSettings()->GetBool(CSettings::SETTING_FILELISTS_IGNORETHEWHENSORTING
))
680 label
= SortUtils::RemoveArticles(label
);
681 if (0 == StringUtils::CompareNoCase(label
, m_match
, m_match
.size()))
686 i
= (i
+1) % m_items
.size();
687 } while (i
!= offset
);
689 // no match found - repeat with a single letter
691 g_charsetConverter
.utf8ToW(m_match
, wmatch
);
692 if (wmatch
.length() > 1)
695 OnJumpLetter(letter
, true);
699 void CGUIBaseContainer::OnJumpSMS(int letter
)
701 static const char letterMap
[8][6] = { "ABC2", "DEF3", "GHI4", "JKL5", "MNO6", "PQRS7", "TUV8", "WXYZ9" };
703 // only 2..9 supported
704 if (letter
< 2 || letter
> 9 || !m_letterOffsets
.size())
707 const std::string letters
= letterMap
[letter
- 2];
708 // find where we currently are
709 int offset
= CorrectOffset(GetOffset(), GetCursor());
710 unsigned int currentLetter
= 0;
711 while (currentLetter
+ 1 < m_letterOffsets
.size() && m_letterOffsets
[currentLetter
+ 1].first
<= offset
)
714 // now switch to the next letter
715 std::string current
= m_letterOffsets
[currentLetter
].second
;
716 size_t startPos
= (letters
.find(current
) + 1) % letters
.size();
717 // now jump to letters[startPos], or another one in the same range if possible
718 size_t pos
= startPos
;
721 // check if we can jump to this letter
722 for (size_t i
= 0; i
< m_letterOffsets
.size(); i
++)
724 if (m_letterOffsets
[i
].second
== letters
.substr(pos
, 1))
726 SelectItem(m_letterOffsets
[i
].first
);
730 pos
= (pos
+ 1) % letters
.size();
736 bool CGUIBaseContainer::MoveUp(bool wrapAround
)
741 bool CGUIBaseContainer::MoveDown(bool wrapAround
)
746 // scrolls the said amount
747 void CGUIBaseContainer::Scroll(int amount
)
749 ResetAutoScrolling();
750 ScrollToOffset(GetOffset() + amount
);
753 int CGUIBaseContainer::GetSelectedItem() const
755 return CorrectOffset(GetOffset(), GetCursor());
758 std::shared_ptr
<CGUIListItem
> CGUIBaseContainer::GetListItem(int offset
, unsigned int flag
) const
760 if (!m_items
.size() || !m_layout
)
761 return std::shared_ptr
<CGUIListItem
>();
762 int item
= GetSelectedItem() + offset
;
763 if (flag
& INFOFLAG_LISTITEM_POSITION
) // use offset from the first item displayed, taking into account scrolling
764 item
= CorrectOffset((int)(m_scroller
.GetValue() / m_layout
->Size(m_orientation
)), offset
);
766 if (flag
& INFOFLAG_LISTITEM_ABSOLUTE
) // use offset from the first item
767 item
= CorrectOffset(0, offset
);
769 if (flag
& INFOFLAG_LISTITEM_WRAP
)
771 item
%= ((int)m_items
.size());
772 if (item
< 0) item
+= m_items
.size();
773 return m_items
[item
];
777 if (item
>= 0 && item
< (int)m_items
.size())
778 return m_items
[item
];
780 return std::shared_ptr
<CGUIListItem
>();
783 CGUIListItemLayout
*CGUIBaseContainer::GetFocusedLayout() const
785 std::shared_ptr
<CGUIListItem
> item
= GetListItem(0);
786 if (item
.get()) return item
->GetFocusedLayout();
790 bool CGUIBaseContainer::OnMouseOver(const CPoint
&point
)
792 // select the item under the pointer
793 if (!m_waitForScrollEnd
)
794 SelectItemFromPoint(point
- CPoint(m_posX
, m_posY
));
795 return CGUIControl::OnMouseOver(point
);
798 EVENT_RESULT
CGUIBaseContainer::OnMouseEvent(const CPoint
& point
, const MOUSE::CMouseEvent
& event
)
800 if (event
.m_id
== ACTION_MOUSE_LEFT_CLICK
||
801 event
.m_id
== ACTION_MOUSE_DOUBLE_CLICK
||
802 event
.m_id
== ACTION_MOUSE_RIGHT_CLICK
)
805 m_waitForScrollEnd
= false;
806 int select
= GetSelectedItem();
807 if (SelectItemFromPoint(point
- CPoint(m_posX
, m_posY
)))
809 if (event
.m_id
!= ACTION_MOUSE_RIGHT_CLICK
|| select
== GetSelectedItem())
811 return EVENT_RESULT_HANDLED
;
814 else if (event
.m_id
== ACTION_MOUSE_WHEEL_UP
)
817 return EVENT_RESULT_HANDLED
;
819 else if (event
.m_id
== ACTION_MOUSE_WHEEL_DOWN
)
822 return EVENT_RESULT_HANDLED
;
824 else if (event
.m_id
== ACTION_GESTURE_NOTIFY
)
826 m_waitForScrollEnd
= true;
827 m_lastScrollValue
= m_scroller
.GetValue();
828 return (m_orientation
== HORIZONTAL
) ? EVENT_RESULT_PAN_HORIZONTAL
: EVENT_RESULT_PAN_VERTICAL
;
830 else if (event
.m_id
== ACTION_GESTURE_BEGIN
)
831 { // grab exclusive access
832 m_gestureActive
= true;
833 CGUIMessage
msg(GUI_MSG_EXCLUSIVE_MOUSE
, GetID(), GetParentID());
834 SendWindowMessage(msg
);
835 return EVENT_RESULT_HANDLED
;
837 else if (event
.m_id
== ACTION_GESTURE_PAN
)
838 { // do the drag and validate our offset (corrects for end of scroll)
839 m_scroller
.SetValue(m_scroller
.GetValue() - ((m_orientation
== HORIZONTAL
) ? event
.m_offsetX
: event
.m_offsetY
));
840 float size
= (m_layout
) ? m_layout
->Size(m_orientation
) : 10.0f
;
841 int offset
= MathUtils::round_int(static_cast<double>(m_scroller
.GetValue() / size
));
842 m_lastScrollStartTimer
.Stop();
843 m_scrollTimer
.Start();
844 const int absCursor
= CorrectOffset(GetOffset(), GetCursor());
847 // Notify Application if Inertial scrolling reaches lists end
848 if (m_waitForScrollEnd
)
850 if (fabs(m_scroller
.GetValue() - m_lastScrollValue
) < 0.001f
)
852 m_waitForScrollEnd
= false;
853 return EVENT_RESULT_UNHANDLED
;
856 m_lastScrollValue
= m_scroller
.GetValue();
860 CGUIBaseContainer::SetCursor(absCursor
- CorrectOffset(GetOffset(), 0));
862 return EVENT_RESULT_HANDLED
;
864 else if (event
.m_id
== ACTION_GESTURE_END
|| event
.m_id
== ACTION_GESTURE_ABORT
)
865 { // release exclusive access
866 CGUIMessage
msg(GUI_MSG_EXCLUSIVE_MOUSE
, 0, GetParentID());
867 SendWindowMessage(msg
);
868 m_scrollTimer
.Stop();
869 // and compute the nearest offset from this and scroll there
870 float size
= (m_layout
) ? m_layout
->Size(m_orientation
) : 10.0f
;
871 float offset
= m_scroller
.GetValue() / size
;
872 int toOffset
= MathUtils::round_int(static_cast<double>(offset
));
873 if (toOffset
< offset
)
874 SetOffset(toOffset
+1);
876 SetOffset(toOffset
-1);
877 ScrollToOffset(toOffset
);
879 SetCursor(GetCursor());
881 m_waitForScrollEnd
= false;
882 m_gestureActive
= false;
883 return EVENT_RESULT_HANDLED
;
885 return EVENT_RESULT_UNHANDLED
;
888 bool CGUIBaseContainer::OnClick(int actionID
)
891 if (actionID
== ACTION_SELECT_ITEM
|| actionID
== ACTION_MOUSE_LEFT_CLICK
)
895 int selected
= GetSelectedItem();
896 if (selected
>= 0 && selected
< static_cast<int>(m_items
.size()))
898 // One of the actions could trigger a reload of the GUI which destroys
899 // this CGUIBaseContainer and therefore the m_items[selected] we are
900 // going to process. The shared_ptr ensures that item survives until
901 // it has been processed.
902 std::shared_ptr
<CGUIListItem
> item
= m_items
[selected
];
904 if (m_clickActions
.HasActionsMeetingCondition())
905 m_clickActions
.ExecuteActions(0, GetParentID(), item
);
907 m_listProvider
->OnClick(item
);
911 // grab the currently focused subitem (if applicable)
912 CGUIListItemLayout
*focusedLayout
= GetFocusedLayout();
914 subItem
= focusedLayout
->GetFocusedItem();
916 else if (actionID
== ACTION_MOUSE_RIGHT_CLICK
)
921 // Don't know what to do, so send to our parent window.
922 CGUIMessage
msg(GUI_MSG_CLICKED
, GetID(), GetParentID(), actionID
, subItem
);
923 return SendWindowMessage(msg
);
926 bool CGUIBaseContainer::OnContextMenu()
930 int selected
= GetSelectedItem();
931 if (selected
>= 0 && selected
< static_cast<int>(m_items
.size()))
933 m_listProvider
->OnContextMenu(m_items
[selected
]);
940 std::string
CGUIBaseContainer::GetDescription() const
942 std::string strLabel
;
943 int item
= GetSelectedItem();
944 if (item
>= 0 && item
< (int)m_items
.size())
946 std::shared_ptr
<CGUIListItem
> pItem
= m_items
[item
];
947 if (pItem
->m_bIsFolder
)
948 strLabel
= StringUtils::Format("[{}]", pItem
->GetLabel());
950 strLabel
= pItem
->GetLabel();
955 void CGUIBaseContainer::SetFocus(bool bOnOff
)
957 if (bOnOff
!= HasFocus())
962 CGUIControl::SetFocus(bOnOff
);
965 void CGUIBaseContainer::SaveStates(std::vector
<CControlState
> &states
)
967 if (!m_listProvider
|| !m_listProvider
->AlwaysFocusDefaultItem())
968 states
.emplace_back(GetID(), GetSelectedItem());
971 void CGUIBaseContainer::SetPageControl(int id
)
976 bool CGUIBaseContainer::GetOffsetRange(int &minOffset
, int &maxOffset
) const
979 maxOffset
= GetRows() - m_itemsPerPage
;
983 void CGUIBaseContainer::ValidateOffset()
987 void CGUIBaseContainer::AllocResources()
989 CGUIControl::AllocResources();
993 UpdateListProvider(true);
997 void CGUIBaseContainer::FreeResources(bool immediately
)
999 CGUIControl::FreeResources(immediately
);
1005 m_listProvider
->Reset();
1011 void CGUIBaseContainer::UpdateLayout(bool updateAllItems
)
1014 { // free memory of items
1015 for (iItems it
= m_items
.begin(); it
!= m_items
.end(); ++it
)
1016 (*it
)->FreeMemory();
1018 // and recalculate the layout
1020 SetPageControlRange();
1024 void CGUIBaseContainer::SetPageControlRange()
1028 CGUIMessage
msg(GUI_MSG_LABEL_RESET
, GetID(), m_pageControl
, m_itemsPerPage
, GetRows());
1029 SendWindowMessage(msg
);
1033 void CGUIBaseContainer::UpdatePageControl(int offset
)
1036 { // tell our pagecontrol (scrollbar or whatever) to update (offset it by our cursor position)
1037 CGUIMessage
msg(GUI_MSG_ITEM_SELECT
, GetID(), m_pageControl
, offset
);
1038 SendWindowMessage(msg
);
1042 void CGUIBaseContainer::UpdateVisibility(const CGUIListItem
*item
)
1044 CGUIControl::UpdateVisibility(item
);
1046 if (!IsVisible() && !CGUIControl::CanFocus())
1047 return; // no need to update the content if we're not visible and we can't focus
1049 // update layouts in case of condition changed
1050 if ((m_layout
&& m_layout
->CheckCondition() != m_layoutCondition
) ||
1051 (m_focusedLayout
&& m_focusedLayout
->CheckCondition() != m_focusedLayoutCondition
))
1054 m_layoutCondition
= m_layout
->CheckCondition();
1055 if (m_focusedLayout
)
1056 m_focusedLayoutCondition
= m_focusedLayout
->CheckCondition();
1058 int itemIndex
= GetSelectedItem();
1059 UpdateLayout(true); // true to refresh all items
1060 SelectItem(itemIndex
);
1063 UpdateListProvider();
1066 void CGUIBaseContainer::AssignDepth()
1068 std::shared_ptr
<CGUIListItem
> focusedItem
= nullptr;
1069 int32_t current
= 0;
1071 for (const auto& item
: m_items
)
1073 bool focused
= (current
== GetOffset() + GetCursor());
1080 if (item
->GetFocusedLayout())
1081 item
->GetFocusedLayout()->AssignDepth();
1082 if (item
->GetLayout())
1083 item
->GetLayout()->AssignDepth();
1090 if (focusedItem
->GetFocusedLayout())
1091 focusedItem
->GetFocusedLayout()->AssignDepth();
1092 if (focusedItem
->GetLayout())
1093 focusedItem
->GetLayout()->AssignDepth();
1097 void CGUIBaseContainer::UpdateListProvider(bool forceRefresh
/* = false */)
1101 if (m_listProvider
->Update(forceRefresh
))
1103 // save the current item
1104 int currentItem
= GetSelectedItem();
1105 CGUIListItem
*current
= (currentItem
>= 0 && currentItem
< (int)m_items
.size()) ? m_items
[currentItem
].get() : NULL
;
1106 const std::string
prevSelectedPath((current
&& current
->IsFileItem()) ? static_cast<CFileItem
*>(current
)->GetPath() : "");
1109 m_listProvider
->Fetch(m_items
);
1110 SetPageControlRange();
1111 // update the newly selected item
1114 // first, try to re-identify selected item by comparing item pointers, though it is not guaranteed that item instances got not recreated on update.
1115 for (int i
= 0; i
< (int)m_items
.size(); i
++)
1117 if (m_items
[i
].get() == current
)
1120 if (i
!= currentItem
)
1127 if (!found
&& !prevSelectedPath
.empty())
1129 // as fallback, try to re-identify selected item by comparing item paths.
1130 for (int i
= 0; i
< static_cast<int>(m_items
.size()); i
++)
1132 const std::shared_ptr
<CGUIListItem
> c(m_items
[i
]);
1133 if (c
->IsFileItem())
1135 const std::string
&selectedPath
= static_cast<CFileItem
*>(c
.get())->GetPath();
1136 if (selectedPath
== prevSelectedPath
)
1139 if (i
!= currentItem
)
1148 if (!found
&& currentItem
>= (int)m_items
.size())
1149 SelectItem(m_items
.size()-1);
1152 // always update the scroll by letter, as the list provider may have altered labels
1153 // while not actually changing the list items.
1154 UpdateScrollByLetter();
1158 void CGUIBaseContainer::CalculateLayout()
1160 CGUIListItemLayout
*oldFocusedLayout
= m_focusedLayout
;
1161 CGUIListItemLayout
*oldLayout
= m_layout
;
1162 GetCurrentLayouts();
1164 // calculate the number of items to display
1165 if (!m_focusedLayout
|| !m_layout
)
1168 if (oldLayout
== m_layout
&& oldFocusedLayout
== m_focusedLayout
)
1169 return; // nothing has changed, so don't update stuff
1171 m_itemsPerPage
= std::max((int)((Size() - m_focusedLayout
->Size(m_orientation
)) / m_layout
->Size(m_orientation
)) + 1, 1);
1173 // ensure that the scroll offset is a multiple of our size
1174 m_scroller
.SetValue(GetOffset() * m_layout
->Size(m_orientation
));
1177 void CGUIBaseContainer::UpdateScrollByLetter()
1179 m_letterOffsets
.clear();
1181 // for scrolling by letter we have an offset table into our vector.
1182 std::string currentMatch
;
1183 for (unsigned int i
= 0; i
< m_items
.size(); i
++)
1185 std::shared_ptr
<CGUIListItem
> item
= m_items
[i
];
1186 // The letter offset jumping is only for ASCII characters at present, and
1187 // our checks are all done in uppercase
1188 std::string nextLetter
;
1189 std::wstring character
= item
->GetSortLabel().substr(0, 1);
1190 StringUtils::ToUpper(character
);
1191 g_charsetConverter
.wToUTF8(character
, nextLetter
);
1192 if (currentMatch
!= nextLetter
)
1194 currentMatch
= nextLetter
;
1195 m_letterOffsets
.emplace_back(static_cast<int>(i
), currentMatch
);
1200 unsigned int CGUIBaseContainer::GetRows() const
1202 return m_items
.size();
1205 inline float CGUIBaseContainer::Size() const
1207 return (m_orientation
== HORIZONTAL
) ? m_width
: m_height
;
1210 int CGUIBaseContainer::ScrollCorrectionRange() const
1212 int range
= m_itemsPerPage
/ 4;
1213 if (range
<= 0) range
= 1;
1217 void CGUIBaseContainer::ScrollToOffset(int offset
)
1219 int minOffset
, maxOffset
;
1220 if(GetOffsetRange(minOffset
, maxOffset
))
1221 offset
= std::max(minOffset
, std::min(offset
, maxOffset
));
1222 float size
= (m_layout
) ? m_layout
->Size(m_orientation
) : 10.0f
;
1223 int range
= ScrollCorrectionRange();
1224 if (offset
* size
< m_scroller
.GetValue() && m_scroller
.GetValue() - offset
* size
> size
* range
)
1225 { // scrolling up, and we're jumping more than 0.5 of a screen
1226 m_scroller
.SetValue((offset
+ range
) * size
);
1228 if (offset
* size
> m_scroller
.GetValue() && offset
* size
- m_scroller
.GetValue() > size
* range
)
1229 { // scrolling down, and we're jumping more than 0.5 of a screen
1230 m_scroller
.SetValue((offset
- range
) * size
);
1232 m_scroller
.ScrollTo(offset
* size
);
1233 m_lastScrollStartTimer
.StartZero();
1236 SetContainerMoving(offset
- GetOffset());
1237 if (m_scroller
.IsScrolling())
1238 m_scrollTimer
.Start();
1240 m_scrollTimer
.Stop();
1244 m_scrollTimer
.Stop();
1245 m_scroller
.Update(~0U);
1250 void CGUIBaseContainer::SetAutoScrolling(const TiXmlNode
*node
)
1253 const TiXmlElement
*scroll
= node
->FirstChildElement("autoscroll");
1256 scroll
->Attribute("time", &m_autoScrollMoveTime
);
1257 if (scroll
->Attribute("reverse"))
1258 m_autoScrollIsReversed
= true;
1259 if (scroll
->FirstChild())
1260 m_autoScrollCondition
= CServiceBroker::GetGUI()->GetInfoManager().Register(scroll
->FirstChild()->ValueStr(), GetParentID());
1264 void CGUIBaseContainer::ResetAutoScrolling()
1266 m_autoScrollDelayTime
= 0;
1269 void CGUIBaseContainer::UpdateAutoScrolling(unsigned int currentTime
)
1271 if (m_autoScrollCondition
&& m_autoScrollCondition
->Get(INFO::DEFAULT_CONTEXT
))
1273 if (m_lastRenderTime
)
1274 m_autoScrollDelayTime
+= currentTime
- m_lastRenderTime
;
1275 if (m_autoScrollDelayTime
> (unsigned int)m_autoScrollMoveTime
&& !m_scroller
.IsScrolling())
1276 { // delay is finished - start moving
1277 m_autoScrollDelayTime
= 0;
1278 // Move up or down whether reversed moving is true or false
1279 m_autoScrollIsReversed
? MoveUp(true) : MoveDown(true);
1283 ResetAutoScrolling();
1286 void CGUIBaseContainer::SetContainerMoving(int direction
)
1289 CServiceBroker::GetGUI()->GetInfoManager().GetInfoProviders().GetGUIControlsInfoProvider().SetContainerMoving(GetID(), direction
> 0, m_scroller
.IsScrolling());
1292 void CGUIBaseContainer::UpdateScrollOffset(unsigned int currentTime
)
1294 if (m_scroller
.Update(currentTime
))
1296 else if (m_lastScrollStartTimer
.IsRunning() && m_lastScrollStartTimer
.GetElapsedMilliseconds() >= SCROLLING_GAP
)
1298 m_scrollTimer
.Stop();
1299 m_lastScrollStartTimer
.Stop();
1300 SetCursor(GetCursor());
1304 int CGUIBaseContainer::CorrectOffset(int offset
, int cursor
) const
1306 return offset
+ cursor
;
1309 void CGUIBaseContainer::Reset()
1314 ResetAutoScrolling();
1317 void CGUIBaseContainer::LoadLayout(TiXmlElement
*layout
)
1319 TiXmlElement
*itemElement
= layout
->FirstChildElement("itemlayout");
1321 { // we have a new item layout
1322 m_layouts
.emplace_back();
1323 m_layouts
.back().LoadLayout(itemElement
, GetParentID(), false, m_width
, m_height
);
1324 itemElement
= itemElement
->NextSiblingElement("itemlayout");
1325 m_layouts
.back().SetParentControl(this);
1327 itemElement
= layout
->FirstChildElement("focusedlayout");
1329 { // we have a new item layout
1330 m_focusedLayouts
.emplace_back();
1331 m_focusedLayouts
.back().LoadLayout(itemElement
, GetParentID(), true, m_width
, m_height
);
1332 itemElement
= itemElement
->NextSiblingElement("focusedlayout");
1333 m_focusedLayouts
.back().SetParentControl(this);
1337 void CGUIBaseContainer::LoadListProvider(TiXmlElement
*content
, int defaultItem
, bool defaultAlways
)
1339 m_listProvider
= IListProvider::Create(content
, GetParentID());
1341 m_listProvider
->SetDefaultItem(defaultItem
, defaultAlways
);
1344 void CGUIBaseContainer::SetListProvider(std::unique_ptr
<IListProvider
> provider
)
1346 m_listProvider
= std::move(provider
);
1347 UpdateListProvider(true);
1350 void CGUIBaseContainer::SetRenderOffset(const CPoint
&offset
)
1352 m_renderOffset
= offset
;
1355 void CGUIBaseContainer::FreeMemory(int keepStart
, int keepEnd
)
1357 if (keepStart
< keepEnd
)
1358 { // remove before keepStart and after keepEnd
1359 for (int i
= 0; i
< keepStart
&& i
< (int)m_items
.size(); ++i
)
1360 m_items
[i
]->FreeMemory();
1361 for (int i
= std::max(keepEnd
+ 1, 0); i
< (int)m_items
.size(); ++i
)
1362 m_items
[i
]->FreeMemory();
1366 for (int i
= std::max(keepEnd
+ 1, 0); i
< keepStart
&& i
< (int)m_items
.size(); ++i
)
1367 m_items
[i
]->FreeMemory();
1371 bool CGUIBaseContainer::InsideLayout(const CGUIListItemLayout
*layout
, const CPoint
&point
) const
1373 if (!layout
) return false;
1374 if ((m_orientation
== VERTICAL
&& (layout
->Size(HORIZONTAL
) > 1) && point
.x
> layout
->Size(HORIZONTAL
)) ||
1375 (m_orientation
== HORIZONTAL
&& (layout
->Size(VERTICAL
) > 1)&& point
.y
> layout
->Size(VERTICAL
)))
1381 void CGUIBaseContainer::DumpTextureUse()
1383 CLog::Log(LOGDEBUG
, "{} for container {}", __FUNCTION__
, GetID());
1384 for (unsigned int i
= 0; i
< m_items
.size(); ++i
)
1386 std::shared_ptr
<CGUIListItem
> item
= m_items
[i
];
1387 if (item
->GetFocusedLayout()) item
->GetFocusedLayout()->DumpTextureUse();
1388 if (item
->GetLayout()) item
->GetLayout()->DumpTextureUse();
1393 bool CGUIBaseContainer::GetCondition(int condition
, int data
) const
1398 return (m_orientation
== VERTICAL
) ? (GetCursor() == data
) : true;
1399 case CONTAINER_COLUMN
:
1400 return (m_orientation
== HORIZONTAL
) ? (GetCursor() == data
) : true;
1401 case CONTAINER_POSITION
:
1402 return (GetCursor() == data
);
1403 case CONTAINER_HAS_NEXT
:
1404 return (HasNextPage());
1405 case CONTAINER_HAS_PREVIOUS
:
1406 return (HasPreviousPage());
1407 case CONTAINER_HAS_PARENT_ITEM
:
1408 return (m_items
.size() && m_items
[0]->IsFileItem() && (std::static_pointer_cast
<CFileItem
>(m_items
[0]))->IsParentFolder());
1409 case CONTAINER_SUBITEM
:
1411 CGUIListItemLayout
*layout
= GetFocusedLayout();
1412 return layout
? (layout
->GetFocusedItem() == (unsigned int)data
) : false;
1414 case CONTAINER_SCROLLING
:
1415 return ((m_scrollTimer
.IsRunning() && m_scrollTimer
.GetElapsedMilliseconds() > std::max(m_scroller
.GetDuration(), SCROLLING_THRESHOLD
)) || m_pageChangeTimer
.IsRunning());
1416 case CONTAINER_ISUPDATING
:
1417 return (m_listProvider
) ? m_listProvider
->IsUpdating() : false;
1423 void CGUIBaseContainer::GetCurrentLayouts()
1426 for (auto &layout
: m_layouts
)
1428 if (layout
.CheckCondition())
1434 if (!m_layout
&& !m_layouts
.empty())
1435 m_layout
= &m_layouts
.front(); // failsafe
1437 m_focusedLayout
= NULL
;
1438 for (auto &layout
: m_focusedLayouts
)
1440 if (layout
.CheckCondition())
1442 m_focusedLayout
= &layout
;
1446 if (!m_focusedLayout
&& !m_focusedLayouts
.empty())
1447 m_focusedLayout
= &m_focusedLayouts
.front(); // failsafe
1450 bool CGUIBaseContainer::HasNextPage() const
1455 bool CGUIBaseContainer::HasPreviousPage() const
1460 std::string
CGUIBaseContainer::GetLabel(int info
) const
1465 case CONTAINER_NUM_PAGES
:
1466 label
= std::to_string((GetRows() + m_itemsPerPage
- 1) / m_itemsPerPage
);
1468 case CONTAINER_CURRENT_PAGE
:
1469 label
= std::to_string(GetCurrentPage());
1471 case CONTAINER_POSITION
:
1472 label
= std::to_string(GetCursor());
1474 case CONTAINER_CURRENT_ITEM
:
1476 if (m_items
.size() && m_items
[0]->IsFileItem() && (std::static_pointer_cast
<CFileItem
>(m_items
[0]))->IsParentFolder())
1477 label
= std::to_string(GetSelectedItem());
1479 label
= std::to_string(GetSelectedItem() + 1);
1482 case CONTAINER_NUM_ALL_ITEMS
:
1483 case CONTAINER_NUM_ITEMS
:
1485 unsigned int numItems
= GetNumItems();
1486 if (info
== CONTAINER_NUM_ITEMS
&& numItems
&& m_items
[0]->IsFileItem() && (std::static_pointer_cast
<CFileItem
>(m_items
[0]))->IsParentFolder())
1487 label
= std::to_string(numItems
- 1);
1489 label
= std::to_string(numItems
);
1492 case CONTAINER_NUM_NONFOLDER_ITEMS
:
1495 for (const auto& item
: m_items
)
1497 if (!item
->m_bIsFolder
)
1500 label
= std::to_string(numItems
);
1509 int CGUIBaseContainer::GetCurrentPage() const
1511 if (GetOffset() + m_itemsPerPage
>= (int)GetRows()) // last page
1512 return (GetRows() + m_itemsPerPage
- 1) / m_itemsPerPage
;
1513 return GetOffset() / m_itemsPerPage
+ 1;
1516 void CGUIBaseContainer::GetCacheOffsets(int &cacheBefore
, int &cacheAfter
) const
1518 if (m_scroller
.IsScrollingDown())
1521 cacheAfter
= m_cacheItems
;
1523 else if (m_scroller
.IsScrollingUp())
1525 cacheBefore
= m_cacheItems
;
1530 cacheBefore
= m_cacheItems
/ 2;
1531 cacheAfter
= m_cacheItems
/ 2;
1535 void CGUIBaseContainer::SetCursor(int cursor
)
1537 if (m_cursor
!= cursor
)
1542 void CGUIBaseContainer::SetOffset(int offset
)
1544 if (m_offset
!= offset
)
1549 bool CGUIBaseContainer::CanFocus() const
1551 if (CGUIControl::CanFocus())
1554 We allow focus if we have items available or if we have a list provider
1555 that's in the process of updating.
1557 return !m_items
.empty() || (m_listProvider
&& m_listProvider
->IsUpdating());
1562 void CGUIBaseContainer::OnFocus()
1564 if (m_listProvider
&& m_listProvider
->AlwaysFocusDefaultItem())
1565 SelectItem(m_listProvider
->GetDefaultItem());
1567 if (m_focusActions
.HasAnyActions())
1568 m_focusActions
.ExecuteActions(GetID(), GetParentID());
1570 CGUIControl::OnFocus();
1573 void CGUIBaseContainer::OnUnFocus()
1575 if (m_unfocusActions
.HasAnyActions())
1576 m_unfocusActions
.ExecuteActions(GetID(), GetParentID());
1578 CGUIControl::OnUnFocus();