2 * Copyright (C) 2012-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 "GUIEPGGridContainer.h"
12 #include "FileItemList.h"
13 #include "ServiceBroker.h"
14 #include "guilib/DirtyRegion.h"
15 #include "guilib/GUIAction.h"
16 #include "guilib/GUIMessage.h"
17 #include "guilib/guiinfo/GUIInfoLabels.h"
18 #include "input/actions/Action.h"
19 #include "input/actions/ActionIDs.h"
20 #include "input/mouse/MouseEvent.h"
21 #include "messaging/ApplicationMessenger.h"
22 #include "pvr/PVRManager.h"
23 #include "pvr/channels/PVRChannel.h"
24 #include "pvr/channels/PVRChannelGroupMember.h"
25 #include "pvr/epg/EpgInfoTag.h"
26 #include "pvr/guilib/GUIEPGGridContainerModel.h"
27 #include "utils/MathUtils.h"
28 #include "utils/StringUtils.h"
29 #include "utils/Variant.h"
42 #define BLOCKJUMP 4 // how many blocks are jumped with each analogue scroll action
43 static const int BLOCK_SCROLL_OFFSET
= 60 / CGUIEPGGridContainerModel::MINSPERBLOCK
; // how many blocks are jumped if we are at left/right edge of grid
45 CGUIEPGGridContainer::CGUIEPGGridContainer(int parentID
,
51 ORIENTATION orientation
,
56 const CTextureInfo
& progressIndicatorTexture
)
57 : IGUIContainer(parentID
, controlID
, posX
, posY
, width
, height
),
58 m_orientation(orientation
),
59 m_rulerUnit(rulerUnit
),
60 m_blocksPerPage(timeBlocks
),
61 m_cacheChannelItems(preloadItems
),
62 m_cacheProgrammeItems(preloadItems
),
63 m_cacheRulerItems(preloadItems
),
64 m_guiProgressIndicatorTexture(
65 CGUITexture::CreateTexture(posX
, posY
, width
, height
, progressIndicatorTexture
)),
66 m_scrollTime(scrollTime
? scrollTime
: 1),
67 m_gridModel(new CGUIEPGGridContainerModel
)
69 ControlType
= GUICONTAINER_EPGGRID
;
72 CGUIEPGGridContainer::CGUIEPGGridContainer(const CGUIEPGGridContainer
& other
)
73 : IGUIContainer(other
),
74 m_renderOffset(other
.m_renderOffset
),
75 m_orientation(other
.m_orientation
),
76 m_channelLayouts(other
.m_channelLayouts
),
77 m_focusedChannelLayouts(other
.m_focusedChannelLayouts
),
78 m_focusedProgrammeLayouts(other
.m_focusedProgrammeLayouts
),
79 m_programmeLayouts(other
.m_programmeLayouts
),
80 m_rulerLayouts(other
.m_rulerLayouts
),
81 m_rulerDateLayouts(other
.m_rulerDateLayouts
),
82 m_channelLayout(other
.m_channelLayout
),
83 m_focusedChannelLayout(other
.m_focusedChannelLayout
),
84 m_programmeLayout(other
.m_programmeLayout
),
85 m_focusedProgrammeLayout(other
.m_focusedProgrammeLayout
),
86 m_rulerLayout(other
.m_rulerLayout
),
87 m_rulerDateLayout(other
.m_rulerDateLayout
),
88 m_pageControl(other
.m_pageControl
),
89 m_rulerUnit(other
.m_rulerUnit
),
90 m_channelsPerPage(other
.m_channelsPerPage
),
91 m_programmesPerPage(other
.m_programmesPerPage
),
92 m_channelCursor(other
.m_channelCursor
),
93 m_channelOffset(other
.m_channelOffset
),
94 m_blocksPerPage(other
.m_blocksPerPage
),
95 m_blockCursor(other
.m_blockCursor
),
96 m_blockOffset(other
.m_blockOffset
),
97 m_blockTravelAxis(other
.m_blockTravelAxis
),
98 m_cacheChannelItems(other
.m_cacheChannelItems
),
99 m_cacheProgrammeItems(other
.m_cacheProgrammeItems
),
100 m_cacheRulerItems(other
.m_cacheRulerItems
),
101 m_rulerDateHeight(other
.m_rulerDateHeight
),
102 m_rulerDateWidth(other
.m_rulerDateWidth
),
103 m_rulerPosX(other
.m_rulerPosX
),
104 m_rulerPosY(other
.m_rulerPosY
),
105 m_rulerHeight(other
.m_rulerHeight
),
106 m_rulerWidth(other
.m_rulerWidth
),
107 m_channelPosX(other
.m_channelPosX
),
108 m_channelPosY(other
.m_channelPosY
),
109 m_channelHeight(other
.m_channelHeight
),
110 m_channelWidth(other
.m_channelWidth
),
111 m_gridPosX(other
.m_gridPosX
),
112 m_gridPosY(other
.m_gridPosY
),
113 m_gridWidth(other
.m_gridWidth
),
114 m_gridHeight(other
.m_gridHeight
),
115 m_blockSize(other
.m_blockSize
),
116 m_analogScrollCount(other
.m_analogScrollCount
),
117 m_guiProgressIndicatorTexture(other
.m_guiProgressIndicatorTexture
->Clone()),
118 m_lastItem(other
.m_lastItem
),
119 m_lastChannel(other
.m_lastChannel
),
120 m_scrollTime(other
.m_scrollTime
),
121 m_programmeScrollLastTime(other
.m_programmeScrollLastTime
),
122 m_programmeScrollSpeed(other
.m_programmeScrollSpeed
),
123 m_programmeScrollOffset(other
.m_programmeScrollOffset
),
124 m_channelScrollLastTime(other
.m_channelScrollLastTime
),
125 m_channelScrollSpeed(other
.m_channelScrollSpeed
),
126 m_channelScrollOffset(other
.m_channelScrollOffset
),
127 m_gridModel(new CGUIEPGGridContainerModel(*other
.m_gridModel
)),
128 m_updatedGridModel(other
.m_updatedGridModel
129 ? new CGUIEPGGridContainerModel(*other
.m_updatedGridModel
)
131 m_itemStartBlock(other
.m_itemStartBlock
)
135 bool CGUIEPGGridContainer::HasData() const
137 return m_gridModel
&& m_gridModel
->HasChannelItems();
140 void CGUIEPGGridContainer::AllocResources()
142 IGUIContainer::AllocResources();
143 m_guiProgressIndicatorTexture
->AllocResources();
146 void CGUIEPGGridContainer::FreeResources(bool immediately
)
148 m_guiProgressIndicatorTexture
->FreeResources(immediately
);
149 IGUIContainer::FreeResources(immediately
);
152 void CGUIEPGGridContainer::SetPageControl(int id
)
157 void CGUIEPGGridContainer::Process(unsigned int currentTime
, CDirtyRegionList
& dirtyregions
)
170 if (m_orientation
== VERTICAL
)
172 iItemsPerPage
= m_channelsPerPage
;
173 iTotalItems
= m_gridModel
->ChannelItemsSize();
177 iItemsPerPage
= m_blocksPerPage
;
178 iTotalItems
= m_gridModel
->GridItemsSize();
181 CGUIMessage
msg(GUI_MSG_LABEL_RESET
, GetID(), m_pageControl
, iItemsPerPage
, iTotalItems
);
182 SendWindowMessage(msg
);
186 UpdateScrollOffset(currentTime
);
187 ProcessChannels(currentTime
, dirtyregions
);
188 ProcessRulerDate(currentTime
, dirtyregions
);
189 ProcessRuler(currentTime
, dirtyregions
);
190 ProcessProgrammeGrid(currentTime
, dirtyregions
);
191 ProcessProgressIndicator(currentTime
, dirtyregions
);
196 (m_orientation
== VERTICAL
)
197 ? MathUtils::round_int(static_cast<double>(m_channelScrollOffset
/ m_channelHeight
))
198 : MathUtils::round_int(
199 static_cast<double>(m_programmeScrollOffset
/ (m_gridHeight
/ m_blocksPerPage
)));
201 CGUIMessage
msg(GUI_MSG_ITEM_SELECT
, GetID(), m_pageControl
, iItem
);
202 SendWindowMessage(msg
);
205 CGUIControl::Process(currentTime
, dirtyregions
);
208 void CGUIEPGGridContainer::Render()
210 if (CServiceBroker::GetWinSystem()->GetGfxContext().GetRenderOrder() ==
211 RENDER_ORDER_FRONT_TO_BACK
)
213 RenderProgressIndicator();
214 RenderProgrammeGrid();
224 RenderProgrammeGrid();
225 RenderProgressIndicator();
228 CGUIControl::Render();
231 void CGUIEPGGridContainer::ProcessChannels(unsigned int currentTime
, CDirtyRegionList
& dirtyregions
)
233 HandleChannels(false, currentTime
, dirtyregions
);
236 void CGUIEPGGridContainer::RenderChannels()
238 // params not needed for render.
239 unsigned int dummyTime
= 0;
240 CDirtyRegionList dummyRegions
;
241 HandleChannels(true, dummyTime
, dummyRegions
);
244 void CGUIEPGGridContainer::ProcessRulerDate(unsigned int currentTime
, CDirtyRegionList
& dirtyregions
)
246 HandleRulerDate(false, currentTime
, dirtyregions
);
249 void CGUIEPGGridContainer::RenderRulerDate()
251 // params not needed for render.
252 unsigned int dummyTime
= 0;
253 CDirtyRegionList dummyRegions
;
254 HandleRulerDate(true, dummyTime
, dummyRegions
);
257 void CGUIEPGGridContainer::ProcessRuler(unsigned int currentTime
, CDirtyRegionList
& dirtyregions
)
259 HandleRuler(false, currentTime
, dirtyregions
);
262 void CGUIEPGGridContainer::RenderRuler()
264 // params not needed for render.
265 unsigned int dummyTime
= 0;
266 CDirtyRegionList dummyRegions
;
267 HandleRuler(true, dummyTime
, dummyRegions
);
270 void CGUIEPGGridContainer::ProcessProgrammeGrid(unsigned int currentTime
, CDirtyRegionList
& dirtyregions
)
272 HandleProgrammeGrid(false, currentTime
, dirtyregions
);
275 void CGUIEPGGridContainer::RenderProgrammeGrid()
277 // params not needed for render.
278 unsigned int dummyTime
= 0;
279 CDirtyRegionList dummyRegions
;
280 HandleProgrammeGrid(true, dummyTime
, dummyRegions
);
283 float CGUIEPGGridContainer::GetCurrentTimePositionOnPage() const
285 if (!m_gridModel
->GetGridStart().IsValid())
288 const CDateTimeSpan
startDelta(CDateTime::GetUTCDateTime() - m_gridModel
->GetGridStart());
289 const float fPos
= (startDelta
.GetSecondsTotal() * m_blockSize
) /
290 (CGUIEPGGridContainerModel::MINSPERBLOCK
* 60) -
291 GetProgrammeScrollOffsetPos();
292 return std::min(fPos
, m_orientation
== VERTICAL
? m_gridWidth
: m_gridHeight
);
295 float CGUIEPGGridContainer::GetProgressIndicatorWidth() const
297 return (m_orientation
== VERTICAL
) ? GetCurrentTimePositionOnPage() : m_rulerWidth
+ m_gridWidth
;
300 float CGUIEPGGridContainer::GetProgressIndicatorHeight() const
302 return (m_orientation
== VERTICAL
) ? m_rulerHeight
+ m_gridHeight
: GetCurrentTimePositionOnPage();
305 void CGUIEPGGridContainer::ProcessProgressIndicator(unsigned int currentTime
, CDirtyRegionList
& dirtyregions
)
307 float width
= GetProgressIndicatorWidth();
308 float height
= GetProgressIndicatorHeight();
310 if (width
> 0 && height
> 0)
312 m_guiProgressIndicatorTexture
->SetVisible(true);
313 m_guiProgressIndicatorTexture
->SetPosition(m_rulerPosX
+ m_renderOffset
.x
,
314 m_rulerPosY
+ m_renderOffset
.y
);
315 m_guiProgressIndicatorTexture
->SetWidth(width
);
316 m_guiProgressIndicatorTexture
->SetHeight(height
);
320 m_guiProgressIndicatorTexture
->SetVisible(false);
323 m_guiProgressIndicatorTexture
->Process(currentTime
);
326 void CGUIEPGGridContainer::RenderProgressIndicator()
328 if (CServiceBroker::GetWinSystem()->GetGfxContext().SetClipRegion(m_rulerPosX
, m_rulerPosY
, GetProgressIndicatorWidth(), GetProgressIndicatorHeight()))
330 m_guiProgressIndicatorTexture
->SetDiffuseColor(m_diffuseColor
);
331 m_guiProgressIndicatorTexture
->Render(0, m_guiProgressIndicatorTextureDepth
);
332 CServiceBroker::GetWinSystem()->GetGfxContext().RestoreClipRegion();
336 void CGUIEPGGridContainer::ProcessItem(float posX
, float posY
, const CFileItemPtr
& item
, CFileItemPtr
& lastitem
,
337 bool focused
, CGUIListItemLayout
* normallayout
, CGUIListItemLayout
* focusedlayout
,
338 unsigned int currentTime
, CDirtyRegionList
& dirtyregions
, float resize
/* = -1.0f */)
340 if (!normallayout
|| !focusedlayout
)
344 CServiceBroker::GetWinSystem()->GetGfxContext().SetOrigin(posX
, posY
);
351 if (!item
->GetFocusedLayout())
353 item
->SetFocusedLayout(std::make_unique
<CGUIListItemLayout
>(*focusedlayout
, this));
358 if (m_orientation
== VERTICAL
)
359 item
->GetFocusedLayout()->SetWidth(resize
);
361 item
->GetFocusedLayout()->SetHeight(resize
);
364 if (item
!= lastitem
|| !HasFocus())
365 item
->GetFocusedLayout()->SetFocusedItem(0);
367 if (item
!= lastitem
&& HasFocus())
369 item
->GetFocusedLayout()->ResetAnimation(ANIM_TYPE_UNFOCUS
);
371 unsigned int subItem
= 1;
372 if (lastitem
&& lastitem
->GetFocusedLayout())
373 subItem
= lastitem
->GetFocusedLayout()->GetFocusedItem();
375 item
->GetFocusedLayout()->SetFocusedItem(subItem
? subItem
: 1);
378 item
->GetFocusedLayout()->Process(item
.get(), m_parentID
, currentTime
, dirtyregions
);
383 if (!item
->GetLayout())
385 item
->SetLayout(std::make_unique
<CGUIListItemLayout
>(*normallayout
, this));
390 if (m_orientation
== VERTICAL
)
391 item
->GetLayout()->SetWidth(resize
);
393 item
->GetLayout()->SetHeight(resize
);
396 if (item
->GetFocusedLayout())
397 item
->GetFocusedLayout()->SetFocusedItem(0);
399 if (item
->GetFocusedLayout() && item
->GetFocusedLayout()->IsAnimating(ANIM_TYPE_UNFOCUS
))
400 item
->GetFocusedLayout()->Process(item
.get(), m_parentID
, currentTime
, dirtyregions
);
402 item
->GetLayout()->Process(item
.get(), m_parentID
, currentTime
, dirtyregions
);
404 CServiceBroker::GetWinSystem()->GetGfxContext().RestoreOrigin();
407 void CGUIEPGGridContainer::RenderItem(float posX
, float posY
, CGUIListItem
* item
, bool focused
)
410 CServiceBroker::GetWinSystem()->GetGfxContext().SetOrigin(posX
, posY
);
414 if (item
->GetFocusedLayout())
415 item
->GetFocusedLayout()->Render(item
, m_parentID
);
419 if (item
->GetFocusedLayout() && item
->GetFocusedLayout()->IsAnimating(ANIM_TYPE_UNFOCUS
))
420 item
->GetFocusedLayout()->Render(item
, m_parentID
);
421 else if (item
->GetLayout())
422 item
->GetLayout()->Render(item
, m_parentID
);
424 CServiceBroker::GetWinSystem()->GetGfxContext().RestoreOrigin();
427 bool CGUIEPGGridContainer::OnAction(const CAction
& action
)
429 switch (action
.GetID())
431 case ACTION_MOVE_LEFT
:
432 case ACTION_MOVE_RIGHT
:
433 case ACTION_MOVE_DOWN
:
435 case ACTION_NAV_BACK
:
436 // use base class implementation
437 return CGUIControl::OnAction(action
);
439 case ACTION_NEXT_ITEM
:
441 ScrollToBlockOffset(m_blockOffset
+ (12 * 60 / CGUIEPGGridContainerModel::MINSPERBLOCK
));
442 SetBlock(m_blockCursor
);
445 case ACTION_PREV_ITEM
:
447 ScrollToBlockOffset(m_blockOffset
- (12 * 60 / CGUIEPGGridContainerModel::MINSPERBLOCK
));
448 SetBlock(m_blockCursor
);
456 if (m_orientation
== VERTICAL
)
458 if (m_channelOffset
== 0)
460 // already on the first page, so move to the first item
465 // scroll up to the previous page
466 ChannelScroll(-m_channelsPerPage
);
471 if (m_blockOffset
== 0)
473 // already on the first page, so move to the first item
478 // scroll up to the previous page
479 ProgrammesScroll(-m_blocksPerPage
);
484 case ACTION_PAGE_DOWN
:
485 if (m_orientation
== VERTICAL
)
487 if (m_channelOffset
== m_gridModel
->ChannelItemsSize() - m_channelsPerPage
||
488 m_gridModel
->ChannelItemsSize() < m_channelsPerPage
)
490 // already at the last page, so move to the last item.
491 SetChannel(m_gridModel
->GetLastChannel() - m_channelOffset
);
495 // scroll down to the next page
496 ChannelScroll(m_channelsPerPage
);
501 if (m_blockOffset
== m_gridModel
->GridItemsSize() - m_blocksPerPage
||
502 m_gridModel
->GridItemsSize() < m_blocksPerPage
)
504 // already at the last page, so move to the last item.
505 SetBlock(m_gridModel
->GetLastBlock() - m_blockOffset
);
509 // scroll down to the next page
510 ProgrammesScroll(m_blocksPerPage
);
516 // smooth scrolling (for analog controls)
517 case ACTION_TELETEXT_RED
:
518 case ACTION_TELETEXT_GREEN
:
519 case ACTION_SCROLL_UP
: // left horizontal scrolling
520 if (m_orientation
== VERTICAL
)
522 int blocksToJump
= action
.GetID() == ACTION_TELETEXT_RED
? m_blocksPerPage
/ 2 : m_blocksPerPage
/ 4;
524 m_analogScrollCount
+= action
.GetAmount() * action
.GetAmount();
525 bool handled
= false;
527 while (m_analogScrollCount
> 0.4f
)
530 m_analogScrollCount
-= 0.4f
;
532 if (m_blockOffset
> 0 && m_blockCursor
<= m_blocksPerPage
/ 2)
533 ProgrammesScroll(-blocksToJump
);
534 else if (m_blockCursor
> blocksToJump
)
535 SetBlock(m_blockCursor
- blocksToJump
);
541 int channelsToJump
= action
.GetID() == ACTION_TELETEXT_RED
? m_channelsPerPage
/ 2 : m_channelsPerPage
/ 4;
543 m_analogScrollCount
+= action
.GetAmount() * action
.GetAmount();
544 bool handled
= false;
546 while (m_analogScrollCount
> 0.4f
)
549 m_analogScrollCount
-= 0.4f
;
551 if (m_channelOffset
> 0 && m_channelCursor
<= m_channelsPerPage
/ 2)
552 ChannelScroll(-channelsToJump
);
553 else if (m_channelCursor
> channelsToJump
)
554 SetChannel(m_channelCursor
- channelsToJump
);
560 case ACTION_TELETEXT_BLUE
:
561 case ACTION_TELETEXT_YELLOW
:
562 case ACTION_SCROLL_DOWN
: // right horizontal scrolling
563 if (m_orientation
== VERTICAL
)
565 int blocksToJump
= action
.GetID() == ACTION_TELETEXT_BLUE
? m_blocksPerPage
/ 2 : m_blocksPerPage
/ 4;
567 m_analogScrollCount
+= action
.GetAmount() * action
.GetAmount();
568 bool handled
= false;
570 while (m_analogScrollCount
> 0.4f
)
573 m_analogScrollCount
-= 0.4f
;
575 if (m_blockOffset
+ m_blocksPerPage
< m_gridModel
->GridItemsSize() &&
576 m_blockCursor
>= m_blocksPerPage
/ 2)
577 ProgrammesScroll(blocksToJump
);
578 else if (m_blockCursor
< m_blocksPerPage
- blocksToJump
&&
579 m_blockOffset
+ m_blockCursor
< m_gridModel
->GridItemsSize() - blocksToJump
)
580 SetBlock(m_blockCursor
+ blocksToJump
);
586 int channelsToJump
= action
.GetID() == ACTION_TELETEXT_BLUE
? m_channelsPerPage
/ 2 : m_channelsPerPage
/ 4;
588 m_analogScrollCount
+= action
.GetAmount() * action
.GetAmount();
589 bool handled
= false;
591 while (m_analogScrollCount
> 0.4f
)
594 m_analogScrollCount
-= 0.4f
;
596 if (m_channelOffset
+ m_channelsPerPage
< m_gridModel
->ChannelItemsSize() && m_channelCursor
>= m_channelsPerPage
/ 2)
597 ChannelScroll(channelsToJump
);
598 else if (m_channelCursor
< m_channelsPerPage
- channelsToJump
&& m_channelOffset
+ m_channelCursor
< m_gridModel
->ChannelItemsSize() - channelsToJump
)
599 SetChannel(m_channelCursor
+ channelsToJump
);
607 return OnClick(action
.GetID());
615 bool CGUIEPGGridContainer::OnMessage(CGUIMessage
& message
)
617 if (message
.GetControlId() == GetID())
619 switch (message
.GetMessage())
621 case GUI_MSG_PAGE_CHANGE
:
622 if (message
.GetSenderId() == m_pageControl
&& IsVisible())
624 if (m_orientation
== VERTICAL
)
626 ScrollToChannelOffset(message
.GetParam1());
627 SetChannel(m_channelCursor
);
631 ScrollToBlockOffset(message
.GetParam1());
632 SetBlock(m_blockCursor
);
638 case GUI_MSG_LABEL_BIND
:
642 case GUI_MSG_REFRESH_LIST
:
643 // update our list contents
644 m_gridModel
->SetInvalid();
649 return CGUIControl::OnMessage(message
);
652 void CGUIEPGGridContainer::UpdateItems()
654 std::unique_lock
<CCriticalSection
> lock(m_critSection
);
656 if (!m_updatedGridModel
)
659 // Save currently selected epg tag and grid coordinates. Selection shall be restored after update.
660 std::shared_ptr
<CPVREpgInfoTag
> prevSelectedEpgTag
;
663 m_gridModel
->GetGridItem(m_channelCursor
+ m_channelOffset
, m_blockCursor
+ m_blockOffset
)
666 const int oldChannelIndex
= m_channelOffset
+ m_channelCursor
;
667 const int oldBlockIndex
= m_blockOffset
+ m_blockCursor
;
668 const CDateTime
oldGridStart(m_gridModel
->GetGridStart());
669 int eventOffset
= oldBlockIndex
;
670 int newChannelIndex
= oldChannelIndex
;
671 int newBlockIndex
= oldBlockIndex
;
673 unsigned int broadcastUid
= 0;
675 if (prevSelectedEpgTag
)
677 // get the block offset relative to the first block of the selected event
679 oldBlockIndex
- m_gridModel
->GetGridItemStartBlock(oldChannelIndex
, oldBlockIndex
);
681 if (!prevSelectedEpgTag
->IsGapTag()) // "normal" tag selected
683 if (oldGridStart
>= prevSelectedEpgTag
->StartAsUTC())
685 // start of previously selected event is before grid start
686 newBlockIndex
= eventOffset
;
690 newBlockIndex
= m_gridModel
->GetFirstEventBlock(prevSelectedEpgTag
) + eventOffset
;
693 channelUid
= prevSelectedEpgTag
->UniqueChannelID();
694 broadcastUid
= prevSelectedEpgTag
->UniqueBroadcastID();
696 else // "gap" tag selected
698 channelUid
= prevSelectedEpgTag
->UniqueChannelID();
700 // As gap tags do not have a unique broadcast id, we will look for the real tag preceding
701 // the gap tag and add the respective offset to restore the gap tag selection, assuming that
702 // the real tag is still the predecessor of the gap tag after the grid model update.
704 const std::shared_ptr
<CFileItem
> prevItem
= GetPrevItem().first
;
707 const std::shared_ptr
<const CPVREpgInfoTag
> tag
= prevItem
->GetEPGInfoTag();
708 if (tag
&& !tag
->IsGapTag())
710 if (oldGridStart
>= tag
->StartAsUTC())
712 // start of previously selected event is before grid start
713 newBlockIndex
= eventOffset
;
717 newBlockIndex
= m_gridModel
->GetFirstEventBlock(tag
);
718 eventOffset
+= m_gridModel
->GetFirstEventBlock(prevSelectedEpgTag
) - newBlockIndex
;
721 broadcastUid
= tag
->UniqueBroadcastID();
727 m_lastItem
= nullptr;
728 m_lastChannel
= nullptr;
730 // always use asynchronously precalculated grid data.
731 m_gridModel
= std::move(m_updatedGridModel
);
733 if (prevSelectedEpgTag
)
735 if (oldGridStart
!= m_gridModel
->GetGridStart())
737 // grid start changed. block offset for selected event might have changed.
738 newBlockIndex
+= m_gridModel
->GetBlock(oldGridStart
);
739 if (newBlockIndex
< 0 || newBlockIndex
> m_gridModel
->GetLastBlock())
741 // previous selection is no longer in grid.
743 m_bEnableChannelScrolling
= false;
744 GoToChannel(newChannelIndex
);
745 m_bEnableProgrammeScrolling
= false;
751 if (newChannelIndex
>= m_gridModel
->ChannelItemsSize() ||
752 newBlockIndex
>= m_gridModel
->GridItemsSize() ||
753 m_gridModel
->GetGridItem(newChannelIndex
, newBlockIndex
)->GetEPGInfoTag() !=
756 int iChannelIndex
= CGUIEPGGridContainerModel::INVALID_INDEX
;
757 int iBlockIndex
= CGUIEPGGridContainerModel::INVALID_INDEX
;
758 m_gridModel
->FindChannelAndBlockIndex(channelUid
, broadcastUid
, eventOffset
, iChannelIndex
, iBlockIndex
);
760 if (iBlockIndex
!= CGUIEPGGridContainerModel::INVALID_INDEX
)
762 newBlockIndex
= iBlockIndex
;
764 else if (newBlockIndex
> m_gridModel
->GetLastBlock())
767 newBlockIndex
= m_gridModel
->GetNowBlock();
769 if (newBlockIndex
> m_gridModel
->GetLastBlock())
771 // last block is in the past. default to last block
772 newBlockIndex
= m_gridModel
->GetLastBlock();
776 if (iChannelIndex
!= CGUIEPGGridContainerModel::INVALID_INDEX
)
778 newChannelIndex
= iChannelIndex
;
780 else if (newChannelIndex
>= m_gridModel
->ChannelItemsSize() ||
781 (m_gridModel
->GetGridItem(newChannelIndex
, newBlockIndex
)->GetEPGInfoTag()->UniqueChannelID() != prevSelectedEpgTag
->UniqueChannelID() &&
782 m_gridModel
->GetGridItem(newChannelIndex
, newBlockIndex
)->GetEPGInfoTag()->ClientID() != prevSelectedEpgTag
->ClientID()))
784 // default to first channel
789 // restore previous selection.
790 if (newChannelIndex
== oldChannelIndex
&& newBlockIndex
== oldBlockIndex
)
792 // same coordinates, keep current grid view port
797 // new coordinates, move grid view port accordingly
800 if (newBlockIndex
!= oldBlockIndex
)
802 m_bEnableProgrammeScrolling
= false;
803 GoToBlock(newBlockIndex
);
806 if (newChannelIndex
!= oldChannelIndex
)
808 m_bEnableChannelScrolling
= false;
809 GoToChannel(newChannelIndex
);
815 // no previous selection, goto now
817 m_bEnableProgrammeScrolling
= false;
822 float CGUIEPGGridContainer::GetChannelScrollOffsetPos() const
824 if (m_bEnableChannelScrolling
)
825 return m_channelScrollOffset
;
827 return m_channelOffset
* m_channelLayout
->Size(m_orientation
);
830 float CGUIEPGGridContainer::GetProgrammeScrollOffsetPos() const
832 if (m_bEnableProgrammeScrolling
)
833 return m_programmeScrollOffset
;
835 return m_blockOffset
* m_blockSize
;
838 int CGUIEPGGridContainer::GetChannelScrollOffset(CGUIListItemLayout
* layout
) const
840 if (m_bEnableChannelScrolling
)
841 return MathUtils::round_int(
842 static_cast<double>(m_channelScrollOffset
/ layout
->Size(m_orientation
)));
844 return m_channelOffset
;
847 int CGUIEPGGridContainer::GetProgrammeScrollOffset() const
849 if (m_bEnableProgrammeScrolling
)
850 return MathUtils::round_int(static_cast<double>(m_programmeScrollOffset
/ m_blockSize
));
852 return m_blockOffset
;
855 void CGUIEPGGridContainer::ChannelScroll(int amount
)
857 // increase or decrease the vertical offset
858 int offset
= m_channelOffset
+ amount
;
860 if (offset
> m_gridModel
->ChannelItemsSize() - m_channelsPerPage
)
861 offset
= m_gridModel
->ChannelItemsSize() - m_channelsPerPage
;
866 ScrollToChannelOffset(offset
);
867 SetChannel(m_channelCursor
);
870 void CGUIEPGGridContainer::ProgrammesScroll(int amount
)
872 // increase or decrease the horizontal offset
873 ScrollToBlockOffset(m_blockOffset
+ amount
);
874 SetBlock(m_blockCursor
);
877 void CGUIEPGGridContainer::OnUp()
880 return CGUIControl::OnUp();
882 if (m_orientation
== VERTICAL
)
884 CGUIAction action
= GetAction(ACTION_MOVE_UP
);
885 if (m_channelCursor
> 0)
887 SetChannel(m_channelCursor
- 1);
889 else if (m_channelCursor
== 0 && m_channelOffset
)
891 ScrollToChannelOffset(m_channelOffset
- 1);
894 else if (action
.GetNavigation() == GetID() || !action
.HasActionsMeetingCondition()) // wrap around
896 int offset
= m_gridModel
->ChannelItemsSize() - m_channelsPerPage
;
901 SetChannel(m_gridModel
->GetLastChannel() - offset
);
902 ScrollToChannelOffset(offset
);
909 if (m_gridModel
->GetGridItemStartBlock(m_channelCursor
+ m_channelOffset
,
910 m_blockCursor
+ m_blockOffset
) > m_blockOffset
)
912 // this is not first item on page
913 SetItem(GetPrevItem());
917 else if (m_blockCursor
<= 0 && m_blockOffset
&& m_blockOffset
- BLOCK_SCROLL_OFFSET
>= 0)
919 // this is the first item on page
920 ScrollToBlockOffset(m_blockOffset
- BLOCK_SCROLL_OFFSET
);
929 void CGUIEPGGridContainer::OnDown()
932 return CGUIControl::OnDown();
934 if (m_orientation
== VERTICAL
)
936 CGUIAction action
= GetAction(ACTION_MOVE_DOWN
);
937 if (m_channelOffset
+ m_channelCursor
< m_gridModel
->GetLastChannel())
939 if (m_channelCursor
+ 1 < m_channelsPerPage
)
941 SetChannel(m_channelCursor
+ 1);
945 ScrollToChannelOffset(m_channelOffset
+ 1);
946 SetChannel(m_channelsPerPage
- 1);
949 else if (action
.GetNavigation() == GetID() || !action
.HasActionsMeetingCondition()) // wrap around
951 ScrollToChannelOffset(0);
955 CGUIControl::OnDown();
959 if (m_gridModel
->GetGridItemEndBlock(m_channelCursor
+ m_channelOffset
,
960 m_blockCursor
+ m_blockOffset
) <
961 (m_blockOffset
+ m_blocksPerPage
- 1))
963 // this is not last item on page
964 SetItem(GetNextItem());
968 else if ((m_blockOffset
!= m_gridModel
->GridItemsSize() - m_blocksPerPage
) &&
969 m_gridModel
->GridItemsSize() > m_blocksPerPage
&&
970 m_blockOffset
+ BLOCK_SCROLL_OFFSET
< m_gridModel
->GetLastBlock())
972 // this is the last item on page
973 ScrollToBlockOffset(m_blockOffset
+ BLOCK_SCROLL_OFFSET
);
978 CGUIControl::OnDown();
982 void CGUIEPGGridContainer::OnLeft()
985 return CGUIControl::OnLeft();
987 if (m_orientation
== VERTICAL
)
989 if (m_gridModel
->GetGridItemStartBlock(m_channelCursor
+ m_channelOffset
,
990 m_blockCursor
+ m_blockOffset
) > m_blockOffset
)
992 // this is not first item on page
993 SetItem(GetPrevItem());
997 else if (m_blockCursor
<= 0 && m_blockOffset
&& m_blockOffset
- BLOCK_SCROLL_OFFSET
>= 0)
999 // this is the first item on page
1000 ScrollToBlockOffset(m_blockOffset
- BLOCK_SCROLL_OFFSET
);
1005 CGUIControl::OnLeft();
1009 CGUIAction action
= GetAction(ACTION_MOVE_LEFT
);
1010 if (m_channelCursor
> 0)
1012 SetChannel(m_channelCursor
- 1);
1014 else if (m_channelCursor
== 0 && m_channelOffset
)
1016 ScrollToChannelOffset(m_channelOffset
- 1);
1019 else if (action
.GetNavigation() == GetID() || !action
.HasActionsMeetingCondition()) // wrap around
1021 int offset
= m_gridModel
->ChannelItemsSize() - m_channelsPerPage
;
1026 SetChannel(m_gridModel
->GetLastChannel() - offset
);
1027 ScrollToChannelOffset(offset
);
1030 CGUIControl::OnLeft();
1034 void CGUIEPGGridContainer::OnRight()
1037 return CGUIControl::OnRight();
1039 if (m_orientation
== VERTICAL
)
1041 if (m_gridModel
->GetGridItemEndBlock(m_channelCursor
+ m_channelOffset
,
1042 m_blockCursor
+ m_blockOffset
) <
1043 (m_blockOffset
+ m_blocksPerPage
- 1))
1045 // this is not last item on page
1046 SetItem(GetNextItem());
1050 else if ((m_blockOffset
!= m_gridModel
->GridItemsSize() - m_blocksPerPage
) &&
1051 m_gridModel
->GridItemsSize() > m_blocksPerPage
&&
1052 m_blockOffset
+ BLOCK_SCROLL_OFFSET
< m_gridModel
->GetLastBlock())
1054 // this is the last item on page
1055 ScrollToBlockOffset(m_blockOffset
+ BLOCK_SCROLL_OFFSET
);
1060 CGUIControl::OnRight();
1064 CGUIAction action
= GetAction(ACTION_MOVE_RIGHT
);
1065 if (m_channelOffset
+ m_channelCursor
< m_gridModel
->GetLastChannel())
1067 if (m_channelCursor
+ 1 < m_channelsPerPage
)
1069 SetChannel(m_channelCursor
+ 1);
1073 ScrollToChannelOffset(m_channelOffset
+ 1);
1074 SetChannel(m_channelsPerPage
- 1);
1077 else if (action
.GetNavigation() == GetID() || !action
.HasActionsMeetingCondition()) // wrap around
1080 ScrollToChannelOffset(0);
1083 CGUIControl::OnRight();
1087 bool CGUIEPGGridContainer::SetChannel(const std::string
& channel
)
1089 for (int iIndex
= 0; iIndex
< m_gridModel
->ChannelItemsSize(); iIndex
++)
1091 std::string strPath
= m_gridModel
->GetChannelItem(iIndex
)->GetProperty("path").asString();
1092 if (strPath
== channel
)
1094 GoToChannel(iIndex
);
1101 bool CGUIEPGGridContainer::SetChannel(const std::shared_ptr
<CPVRChannel
>& channel
)
1103 for (int iIndex
= 0; iIndex
< m_gridModel
->ChannelItemsSize(); iIndex
++)
1105 int iChannelId
= static_cast<int>(m_gridModel
->GetChannelItem(iIndex
)->GetProperty("channelid").asInteger(-1));
1106 if (iChannelId
== channel
->ChannelID())
1108 GoToChannel(iIndex
);
1115 bool CGUIEPGGridContainer::SetChannel(const CPVRChannelNumber
& channelNumber
)
1117 for (int iIndex
= 0; iIndex
< m_gridModel
->ChannelItemsSize(); iIndex
++)
1119 const CPVRChannelNumber
& number
=
1120 m_gridModel
->GetChannelItem(iIndex
)->GetPVRChannelGroupMemberInfoTag()->ChannelNumber();
1121 if (number
== channelNumber
)
1123 GoToChannel(iIndex
);
1130 void CGUIEPGGridContainer::SetChannel(int channel
)
1132 std::unique_lock
<CCriticalSection
> lock(m_critSection
);
1134 int channelIndex
= channel
+ m_channelOffset
;
1135 int blockIndex
= m_blockCursor
+ m_blockOffset
;
1136 if (channelIndex
< m_gridModel
->ChannelItemsSize() && blockIndex
< m_gridModel
->GridItemsSize())
1138 if (SetItem(m_gridModel
->GetGridItem(channelIndex
, m_blockTravelAxis
), channelIndex
,
1141 m_channelCursor
= channel
;
1148 void CGUIEPGGridContainer::SetBlock(int block
, bool bUpdateBlockTravelAxis
/* = true */)
1150 std::unique_lock
<CCriticalSection
> lock(m_critSection
);
1154 else if (block
> m_blocksPerPage
- 1)
1155 m_blockCursor
= m_blocksPerPage
- 1;
1157 m_blockCursor
= block
;
1159 if (bUpdateBlockTravelAxis
)
1160 m_blockTravelAxis
= m_blockOffset
+ m_blockCursor
;
1166 void CGUIEPGGridContainer::UpdateBlock(bool bUpdateBlockTravelAxis
/* = true */)
1168 SetBlock(m_itemStartBlock
> 0 ? m_itemStartBlock
- m_blockOffset
: 0, bUpdateBlockTravelAxis
);
1171 CGUIListItemLayout
* CGUIEPGGridContainer::GetFocusedLayout() const
1173 std::shared_ptr
<CGUIListItem
> item
= GetListItem(0);
1176 return item
->GetFocusedLayout();
1181 bool CGUIEPGGridContainer::SelectItemFromPoint(const CPoint
& point
, bool justGrid
/* = false */)
1183 /* point has already had origin set to m_posX, m_posY */
1184 if (!m_focusedProgrammeLayout
|| !m_programmeLayout
|| (justGrid
&& point
.x
< 0))
1190 if (m_orientation
== VERTICAL
)
1192 channel
= point
.y
/ m_channelHeight
;
1193 block
= point
.x
/ m_blockSize
;
1197 channel
= point
.x
/ m_channelWidth
;
1198 block
= point
.y
/ m_blockSize
;
1201 if (channel
> m_channelsPerPage
)
1202 channel
= m_channelsPerPage
- 1;
1204 if (channel
>= m_gridModel
->ChannelItemsSize())
1205 channel
= m_gridModel
->GetLastChannel();
1210 if (block
> m_blocksPerPage
)
1211 block
= m_blocksPerPage
- 1;
1216 int channelIndex
= channel
+ m_channelOffset
;
1217 int blockIndex
= block
+ m_blockOffset
;
1219 // bail if out of range
1220 if (channelIndex
>= m_gridModel
->ChannelItemsSize() || blockIndex
>= m_gridModel
->GridItemsSize())
1223 // bail if block isn't occupied
1224 if (!m_gridModel
->GetGridItem(channelIndex
, blockIndex
))
1227 SetChannel(channel
);
1232 EVENT_RESULT
CGUIEPGGridContainer::OnMouseEvent(const CPoint
& point
,
1233 const MOUSE::CMouseEvent
& event
)
1237 case ACTION_MOUSE_LEFT_CLICK
:
1238 OnMouseClick(0, point
);
1239 return EVENT_RESULT_HANDLED
;
1240 case ACTION_MOUSE_RIGHT_CLICK
:
1241 OnMouseClick(1, point
);
1242 return EVENT_RESULT_HANDLED
;
1243 case ACTION_MOUSE_DOUBLE_CLICK
:
1244 OnMouseDoubleClick(0, point
);
1245 return EVENT_RESULT_HANDLED
;
1246 case ACTION_MOUSE_WHEEL_UP
:
1247 OnMouseWheel(-1, point
);
1248 return EVENT_RESULT_HANDLED
;
1249 case ACTION_MOUSE_WHEEL_DOWN
:
1250 OnMouseWheel(1, point
);
1251 return EVENT_RESULT_HANDLED
;
1252 case ACTION_GESTURE_BEGIN
:
1254 // we want exclusive access
1255 CGUIMessage
msg(GUI_MSG_EXCLUSIVE_MOUSE
, GetID(), GetParentID());
1256 SendWindowMessage(msg
);
1257 return EVENT_RESULT_HANDLED
;
1259 case ACTION_GESTURE_END
:
1260 case ACTION_GESTURE_ABORT
:
1262 // we're done with exclusive access
1263 CGUIMessage
msg(GUI_MSG_EXCLUSIVE_MOUSE
, 0, GetParentID());
1264 SendWindowMessage(msg
);
1265 ScrollToChannelOffset(MathUtils::round_int(
1266 static_cast<double>(m_channelScrollOffset
/ m_channelLayout
->Size(m_orientation
))));
1267 SetChannel(m_channelCursor
);
1268 ScrollToBlockOffset(
1269 MathUtils::round_int(static_cast<double>(m_programmeScrollOffset
/ m_blockSize
)));
1270 SetBlock(m_blockCursor
);
1271 return EVENT_RESULT_HANDLED
;
1273 case ACTION_GESTURE_PAN
:
1275 m_programmeScrollOffset
-= event
.m_offsetX
;
1276 m_channelScrollOffset
-= event
.m_offsetY
;
1279 std::unique_lock
<CCriticalSection
> lock(m_critSection
);
1281 m_channelOffset
= MathUtils::round_int(
1282 static_cast<double>(m_channelScrollOffset
/ m_channelLayout
->Size(m_orientation
)));
1284 MathUtils::round_int(static_cast<double>(m_programmeScrollOffset
/ m_blockSize
));
1287 return EVENT_RESULT_HANDLED
;
1290 return EVENT_RESULT_UNHANDLED
;
1294 bool CGUIEPGGridContainer::OnMouseOver(const CPoint
& point
)
1296 // select the item under the pointer
1297 SelectItemFromPoint(point
- CPoint(m_gridPosX
, m_gridPosY
), false);
1298 return CGUIControl::OnMouseOver(point
);
1301 bool CGUIEPGGridContainer::OnMouseClick(int dwButton
, const CPoint
& point
)
1303 if (SelectItemFromPoint(point
- CPoint(m_gridPosX
, m_gridPosY
)))
1305 // send click message to window
1306 OnClick(ACTION_MOUSE_LEFT_CLICK
+ dwButton
);
1312 bool CGUIEPGGridContainer::OnMouseDoubleClick(int dwButton
, const CPoint
& point
)
1314 if (SelectItemFromPoint(point
- CPoint(m_gridPosX
, m_gridPosY
)))
1316 // send double click message to window
1317 OnClick(ACTION_MOUSE_DOUBLE_CLICK
+ dwButton
);
1323 bool CGUIEPGGridContainer::OnClick(int actionID
)
1327 if (actionID
== ACTION_SELECT_ITEM
|| actionID
== ACTION_MOUSE_LEFT_CLICK
)
1329 // grab the currently focused subitem (if applicable)
1330 CGUIListItemLayout
* focusedLayout
= GetFocusedLayout();
1333 subItem
= focusedLayout
->GetFocusedItem();
1336 // Don't know what to do, so send to our parent window.
1337 CGUIMessage
msg(GUI_MSG_CLICKED
, GetID(), GetParentID(), actionID
, subItem
);
1338 return SendWindowMessage(msg
);
1341 bool CGUIEPGGridContainer::OnMouseWheel(char wheel
, const CPoint
& point
)
1343 // doesn't work while an item is selected?
1344 ProgrammesScroll(-wheel
);
1348 std::shared_ptr
<CPVRChannelGroupMember
> CGUIEPGGridContainer::GetSelectedChannelGroupMember() const
1350 CFileItemPtr fileItem
;
1352 std::unique_lock
<CCriticalSection
> lock(m_critSection
);
1353 if (m_channelCursor
+ m_channelOffset
< m_gridModel
->ChannelItemsSize())
1354 fileItem
= m_gridModel
->GetChannelItem(m_channelCursor
+ m_channelOffset
);
1358 return fileItem
->GetPVRChannelGroupMemberInfoTag();
1363 CDateTime
CGUIEPGGridContainer::GetSelectedDate() const
1365 return m_gridModel
->GetStartTimeForBlock(m_blockOffset
+ m_blockCursor
);
1368 CFileItemPtr
CGUIEPGGridContainer::GetSelectedGridItem(int offset
/*= 0*/) const
1372 if (m_channelCursor
+ m_channelOffset
+ offset
< m_gridModel
->ChannelItemsSize() &&
1373 m_blockCursor
+ m_blockOffset
< m_gridModel
->GridItemsSize())
1374 item
= m_gridModel
->GetGridItem(m_channelCursor
+ m_channelOffset
, m_blockCursor
+ m_blockOffset
);
1379 std::shared_ptr
<CGUIListItem
> CGUIEPGGridContainer::GetListItem(int offset
, unsigned int flag
) const
1381 if (!m_gridModel
->HasChannelItems())
1382 return std::shared_ptr
<CGUIListItem
>();
1384 int item
= m_channelCursor
+ m_channelOffset
+ offset
;
1385 if (flag
& INFOFLAG_LISTITEM_POSITION
)
1386 item
= GetChannelScrollOffset(m_channelLayout
);
1388 if (flag
& INFOFLAG_LISTITEM_WRAP
)
1390 item
%= m_gridModel
->ChannelItemsSize();
1392 item
+= m_gridModel
->ChannelItemsSize();
1394 return m_gridModel
->GetChannelItem(item
);
1398 if (item
>= 0 && item
< m_gridModel
->ChannelItemsSize())
1399 return m_gridModel
->GetChannelItem(item
);
1401 return std::shared_ptr
<CGUIListItem
>();
1404 std::string
CGUIEPGGridContainer::GetLabel(int info
) const
1409 case CONTAINER_NUM_PAGES
:
1410 if (m_channelsPerPage
> 0)
1411 label
= std::to_string((m_gridModel
->ChannelItemsSize() + m_channelsPerPage
- 1) /
1414 label
= std::to_string(0);
1416 case CONTAINER_CURRENT_PAGE
:
1417 if (m_channelsPerPage
> 0)
1418 label
= std::to_string(1 + (m_channelCursor
+ m_channelOffset
) / m_channelsPerPage
);
1420 label
= std::to_string(1);
1422 case CONTAINER_POSITION
:
1423 label
= std::to_string(1 + m_channelCursor
+ m_channelOffset
);
1425 case CONTAINER_NUM_ITEMS
:
1426 label
= std::to_string(m_gridModel
->ChannelItemsSize());
1434 void CGUIEPGGridContainer::SetItem(const std::pair
<std::shared_ptr
<CFileItem
>, int>& itemInfo
)
1436 SetItem(itemInfo
.first
, m_channelCursor
+ m_channelOffset
, itemInfo
.second
);
1439 bool CGUIEPGGridContainer::SetItem(const std::shared_ptr
<CFileItem
>& item
,
1443 if (item
&& channelIndex
< m_gridModel
->ChannelItemsSize() &&
1444 blockIndex
< m_gridModel
->GridItemsSize())
1446 m_itemStartBlock
= m_gridModel
->GetGridItemStartBlock(channelIndex
, blockIndex
);
1451 m_itemStartBlock
= 0;
1456 std::shared_ptr
<CFileItem
> CGUIEPGGridContainer::GetItem() const
1458 const int channelIndex
= m_channelCursor
+ m_channelOffset
;
1459 const int blockIndex
= m_blockCursor
+ m_blockOffset
;
1461 if (channelIndex
>= m_gridModel
->ChannelItemsSize() || blockIndex
>= m_gridModel
->GridItemsSize())
1464 return m_gridModel
->GetGridItem(m_channelCursor
+ m_channelOffset
, m_blockCursor
+ m_blockOffset
);
1467 std::pair
<std::shared_ptr
<CFileItem
>, int> CGUIEPGGridContainer::GetNextItem() const
1469 int block
= m_gridModel
->GetGridItemEndBlock(m_channelCursor
+ m_channelOffset
,
1470 m_blockCursor
+ m_blockOffset
);
1471 if (block
< m_gridModel
->GridItemsSize())
1473 // first block of next event is one block after end block of selected event
1477 return {m_gridModel
->GetGridItem(m_channelCursor
+ m_channelOffset
, block
), block
};
1480 std::pair
<std::shared_ptr
<CFileItem
>, int> CGUIEPGGridContainer::GetPrevItem() const
1482 int block
= m_gridModel
->GetGridItemStartBlock(m_channelCursor
+ m_channelOffset
,
1483 m_blockCursor
+ m_blockOffset
);
1486 // last block of previous event is one block before start block of selected event
1490 return {m_gridModel
->GetGridItem(m_channelCursor
+ m_channelOffset
, block
), block
};
1493 void CGUIEPGGridContainer::UpdateItem()
1495 SetItem(GetItem(), m_channelCursor
+ m_channelOffset
, m_blockCursor
+ m_blockOffset
);
1498 void CGUIEPGGridContainer::SetFocus(bool focus
)
1500 if (focus
!= HasFocus())
1503 CGUIControl::SetFocus(focus
);
1506 void CGUIEPGGridContainer::ScrollToChannelOffset(int offset
)
1508 std::unique_lock
<CCriticalSection
> lock(m_critSection
);
1510 float size
= m_programmeLayout
->Size(m_orientation
);
1511 int range
= m_channelsPerPage
/ 4;
1516 if (offset
* size
< m_channelScrollOffset
&& m_channelScrollOffset
- offset
* size
> size
* range
)
1518 // scrolling up, and we're jumping more than 0.5 of a screen
1519 m_channelScrollOffset
= (offset
+ range
) * size
;
1522 if (offset
* size
> m_channelScrollOffset
&& offset
* size
- m_channelScrollOffset
> size
* range
)
1524 // scrolling down, and we're jumping more than 0.5 of a screen
1525 m_channelScrollOffset
= (offset
- range
) * size
;
1528 m_channelScrollSpeed
= (offset
* size
- m_channelScrollOffset
) / m_scrollTime
;
1529 m_channelOffset
= offset
;
1533 void CGUIEPGGridContainer::ScrollToBlockOffset(int offset
)
1535 std::unique_lock
<CCriticalSection
> lock(m_critSection
);
1537 // make sure offset is in valid range
1538 offset
= std::max(0, std::min(offset
, m_gridModel
->GridItemsSize() - m_blocksPerPage
));
1540 float size
= m_blockSize
;
1541 int range
= m_blocksPerPage
/ 1;
1546 if (offset
* size
< m_programmeScrollOffset
&& m_programmeScrollOffset
- offset
* size
> size
* range
)
1548 // scrolling left, and we're jumping more than 0.5 of a screen
1549 m_programmeScrollOffset
= (offset
+ range
) * size
;
1552 if (offset
* size
> m_programmeScrollOffset
&& offset
* size
- m_programmeScrollOffset
> size
* range
)
1554 // scrolling right, and we're jumping more than 0.5 of a screen
1555 m_programmeScrollOffset
= (offset
- range
) * size
;
1558 m_programmeScrollSpeed
= (offset
* size
- m_programmeScrollOffset
) / m_scrollTime
;
1559 m_blockOffset
= offset
;
1563 void CGUIEPGGridContainer::ValidateOffset()
1565 std::unique_lock
<CCriticalSection
> lock(m_critSection
);
1567 if (!m_programmeLayout
)
1570 float pos
= (m_orientation
== VERTICAL
) ? m_channelHeight
: m_channelWidth
;
1572 if (m_gridModel
->ChannelItemsSize() &&
1573 (m_channelOffset
> m_gridModel
->ChannelItemsSize() - m_channelsPerPage
||
1574 m_channelScrollOffset
> (m_gridModel
->ChannelItemsSize() - m_channelsPerPage
) * pos
))
1576 m_channelOffset
= m_gridModel
->ChannelItemsSize() - m_channelsPerPage
;
1577 m_channelScrollOffset
= m_channelOffset
* pos
;
1580 if (m_channelOffset
< 0 || m_channelScrollOffset
< 0)
1582 m_channelOffset
= 0;
1583 m_channelScrollOffset
= 0;
1586 if (m_gridModel
->GridItemsSize() &&
1587 (m_blockOffset
> m_gridModel
->GridItemsSize() - m_blocksPerPage
||
1588 m_programmeScrollOffset
> (m_gridModel
->GridItemsSize() - m_blocksPerPage
) * m_blockSize
))
1590 m_blockOffset
= m_gridModel
->GridItemsSize() - m_blocksPerPage
;
1591 m_programmeScrollOffset
= m_blockOffset
* m_blockSize
;
1594 if (m_blockOffset
< 0 || m_programmeScrollOffset
< 0)
1597 m_programmeScrollOffset
= 0;
1601 void CGUIEPGGridContainer::LoadLayout(TiXmlElement
* layout
)
1603 /* layouts for the channel column */
1604 TiXmlElement
* itemElement
= layout
->FirstChildElement("channellayout");
1607 m_channelLayouts
.emplace_back();
1608 m_channelLayouts
.back().LoadLayout(itemElement
, GetParentID(), false, m_width
, m_height
);
1609 itemElement
= itemElement
->NextSiblingElement("channellayout");
1611 itemElement
= layout
->FirstChildElement("focusedchannellayout");
1614 m_focusedChannelLayouts
.emplace_back();
1615 m_focusedChannelLayouts
.back().LoadLayout(itemElement
, GetParentID(), true, m_width
, m_height
);
1616 itemElement
= itemElement
->NextSiblingElement("focusedchannellayout");
1619 /* layouts for the grid items */
1620 itemElement
= layout
->FirstChildElement("focusedlayout");
1623 m_focusedProgrammeLayouts
.emplace_back();
1624 m_focusedProgrammeLayouts
.back().LoadLayout(itemElement
, GetParentID(), true, m_width
, m_height
);
1625 itemElement
= itemElement
->NextSiblingElement("focusedlayout");
1627 itemElement
= layout
->FirstChildElement("itemlayout");
1630 m_programmeLayouts
.emplace_back();
1631 m_programmeLayouts
.back().LoadLayout(itemElement
, GetParentID(), false, m_width
, m_height
);
1632 itemElement
= itemElement
->NextSiblingElement("itemlayout");
1635 /* layout for the date label for the grid */
1636 itemElement
= layout
->FirstChildElement("rulerdatelayout");
1639 m_rulerDateLayouts
.emplace_back();
1640 m_rulerDateLayouts
.back().LoadLayout(itemElement
, GetParentID(), false, m_width
, m_height
);
1641 itemElement
= itemElement
->NextSiblingElement("rulerdatelayout");
1644 /* layout for the timeline for the grid */
1645 itemElement
= layout
->FirstChildElement("rulerlayout");
1648 m_rulerLayouts
.emplace_back();
1649 m_rulerLayouts
.back().LoadLayout(itemElement
, GetParentID(), false, m_width
, m_height
);
1650 itemElement
= itemElement
->NextSiblingElement("rulerlayout");
1656 std::string
CGUIEPGGridContainer::GetDescription() const
1658 std::unique_lock
<CCriticalSection
> lock(m_critSection
);
1660 const int channelIndex
= m_channelCursor
+ m_channelOffset
;
1661 const int blockIndex
= m_blockCursor
+ m_blockOffset
;
1663 if (channelIndex
< m_gridModel
->ChannelItemsSize() && blockIndex
< m_gridModel
->GridItemsSize())
1665 const std::shared_ptr
<CFileItem
> item
= m_gridModel
->GetGridItem(channelIndex
, blockIndex
);
1667 return item
->GetLabel();
1673 void CGUIEPGGridContainer::JumpToNow()
1675 m_bEnableProgrammeScrolling
= false;
1679 void CGUIEPGGridContainer::JumpToDate(const CDateTime
& date
)
1681 m_bEnableProgrammeScrolling
= false;
1685 void CGUIEPGGridContainer::GoToBegin()
1687 ScrollToBlockOffset(0);
1691 void CGUIEPGGridContainer::GoToEnd()
1693 ScrollToBlockOffset(m_gridModel
->GetLastBlock() - m_blocksPerPage
+ 1);
1694 SetBlock(m_blocksPerPage
- 1);
1697 void CGUIEPGGridContainer::GoToNow()
1699 GoToDate(CDateTime::GetUTCDateTime());
1702 void CGUIEPGGridContainer::GoToDate(const CDateTime
& date
)
1704 unsigned int offset
= m_gridModel
->GetPageNowOffset();
1705 ScrollToBlockOffset(m_gridModel
->GetBlock(date
) - offset
);
1707 // ensure we're selecting the active event, not its predecessor.
1708 const int iChannel
= m_channelOffset
+ m_channelCursor
;
1709 const int iBlock
= m_blockOffset
+ offset
;
1710 if (iChannel
>= m_gridModel
->ChannelItemsSize() || iBlock
>= m_gridModel
->GridItemsSize() ||
1711 m_gridModel
->GetGridItemEndTime(iChannel
, iBlock
) > date
)
1717 SetBlock(offset
+ 1);
1721 void CGUIEPGGridContainer::GoToFirstChannel()
1726 void CGUIEPGGridContainer::GoToLastChannel()
1728 if (m_gridModel
->ChannelItemsSize())
1729 GoToChannel(m_gridModel
->GetLastChannel());
1734 void CGUIEPGGridContainer::GoToTop()
1736 if (m_orientation
== VERTICAL
)
1746 void CGUIEPGGridContainer::GoToBottom()
1748 if (m_orientation
== VERTICAL
)
1750 if (m_gridModel
->HasChannelItems())
1751 GoToChannel(m_gridModel
->GetLastChannel());
1757 if (m_gridModel
->GridItemsSize())
1758 GoToBlock(m_gridModel
->GetLastBlock());
1764 void CGUIEPGGridContainer::GoToMostLeft()
1766 if (m_orientation
== VERTICAL
)
1776 void CGUIEPGGridContainer::GoToMostRight()
1778 if (m_orientation
== VERTICAL
)
1780 if (m_gridModel
->GridItemsSize())
1781 GoToBlock(m_gridModel
->GetLastBlock());
1787 if (m_gridModel
->HasChannelItems())
1788 GoToChannel(m_gridModel
->GetLastChannel());
1794 void CGUIEPGGridContainer::SetTimelineItems(const std::unique_ptr
<CFileItemList
>& items
,
1795 const CDateTime
& gridStart
,
1796 const CDateTime
& gridEnd
)
1800 int iChannelsPerPage
;
1805 std::unique_lock
<CCriticalSection
> lock(m_critSection
);
1807 iRulerUnit
= m_rulerUnit
;
1808 iFirstChannel
= m_channelOffset
;
1809 iChannelsPerPage
= m_channelsPerPage
;
1810 iFirstBlock
= m_blockOffset
;
1811 iBlocksPerPage
= m_blocksPerPage
;
1812 fBlockSize
= m_blockSize
;
1815 std::unique_ptr
<CGUIEPGGridContainerModel
> oldUpdatedGridModel
;
1816 std::unique_ptr
<CGUIEPGGridContainerModel
> newUpdatedGridModel(new CGUIEPGGridContainerModel
);
1818 newUpdatedGridModel
->Initialize(items
, gridStart
, gridEnd
, iFirstChannel
, iChannelsPerPage
,
1819 iFirstBlock
, iBlocksPerPage
, iRulerUnit
, fBlockSize
);
1821 std::unique_lock
<CCriticalSection
> lock(m_critSection
);
1823 // grid contains CFileItem instances. CFileItem dtor locks global graphics mutex.
1824 // by increasing its refcount make sure, old data are not deleted while we're holding own mutex.
1825 oldUpdatedGridModel
= std::move(m_updatedGridModel
);
1827 m_updatedGridModel
= std::move(newUpdatedGridModel
);
1831 std::unique_ptr
<CFileItemList
> CGUIEPGGridContainer::GetCurrentTimeLineItems() const
1833 return m_gridModel
->GetCurrentTimeLineItems(m_channelOffset
, m_channelsPerPage
);
1836 void CGUIEPGGridContainer::GoToChannel(int channelIndex
)
1838 if (channelIndex
< m_channelsPerPage
)
1841 ScrollToChannelOffset(0);
1842 SetChannel(channelIndex
);
1844 else if (channelIndex
> m_gridModel
->ChannelItemsSize() - m_channelsPerPage
)
1847 ScrollToChannelOffset(m_gridModel
->ChannelItemsSize() - m_channelsPerPage
);
1848 SetChannel(channelIndex
- (m_gridModel
->ChannelItemsSize() - m_channelsPerPage
));
1852 ScrollToChannelOffset(channelIndex
- m_channelCursor
);
1853 SetChannel(m_channelCursor
);
1857 void CGUIEPGGridContainer::GoToBlock(int blockIndex
)
1859 int lastPage
= m_gridModel
->GridItemsSize() - m_blocksPerPage
;
1860 if (blockIndex
> lastPage
)
1863 ScrollToBlockOffset(lastPage
);
1864 SetBlock(blockIndex
- lastPage
);
1868 ScrollToBlockOffset(blockIndex
- m_blockCursor
);
1869 SetBlock(m_blockCursor
);
1873 void CGUIEPGGridContainer::UpdateLayout()
1875 CGUIListItemLayout
* oldFocusedChannelLayout
= m_focusedChannelLayout
;
1876 CGUIListItemLayout
* oldChannelLayout
= m_channelLayout
;
1877 CGUIListItemLayout
* oldFocusedProgrammeLayout
= m_focusedProgrammeLayout
;
1878 CGUIListItemLayout
* oldProgrammeLayout
= m_programmeLayout
;
1879 CGUIListItemLayout
* oldRulerLayout
= m_rulerLayout
;
1880 CGUIListItemLayout
* oldRulerDateLayout
= m_rulerDateLayout
;
1882 GetCurrentLayouts();
1884 // Note: m_rulerDateLayout is optional
1885 if (!m_focusedProgrammeLayout
|| !m_programmeLayout
|| !m_focusedChannelLayout
|| !m_channelLayout
|| !m_rulerLayout
)
1888 if (oldChannelLayout
== m_channelLayout
&& oldFocusedChannelLayout
== m_focusedChannelLayout
&&
1889 oldProgrammeLayout
== m_programmeLayout
&& oldFocusedProgrammeLayout
== m_focusedProgrammeLayout
&&
1890 oldRulerLayout
== m_rulerLayout
&& oldRulerDateLayout
== m_rulerDateLayout
)
1891 return; // nothing has changed, so don't update stuff
1893 std::unique_lock
<CCriticalSection
> lock(m_critSection
);
1895 m_channelHeight
= m_channelLayout
->Size(VERTICAL
);
1896 m_channelWidth
= m_channelLayout
->Size(HORIZONTAL
);
1898 m_rulerDateHeight
= m_rulerDateLayout
? m_rulerDateLayout
->Size(VERTICAL
) : 0;
1899 m_rulerDateWidth
= m_rulerDateLayout
? m_rulerDateLayout
->Size(HORIZONTAL
) : 0;
1901 if (m_orientation
== VERTICAL
)
1903 m_rulerHeight
= m_rulerLayout
->Size(VERTICAL
);
1904 m_gridPosX
= m_posX
+ m_channelWidth
;
1905 m_gridPosY
= m_posY
+ m_rulerHeight
+ m_rulerDateHeight
;
1906 m_gridWidth
= m_width
- m_channelWidth
;
1907 m_gridHeight
= m_height
- m_rulerHeight
- m_rulerDateHeight
;
1908 m_blockSize
= m_gridWidth
/ m_blocksPerPage
;
1909 m_rulerWidth
= m_rulerUnit
* m_blockSize
;
1910 m_channelPosX
= m_posX
;
1911 m_channelPosY
= m_posY
+ m_rulerHeight
+ m_rulerDateHeight
;
1912 m_rulerPosX
= m_posX
+ m_channelWidth
;
1913 m_rulerPosY
= m_posY
+ m_rulerDateHeight
;
1914 m_channelsPerPage
= m_gridHeight
/ m_channelHeight
;
1915 m_programmesPerPage
= (m_gridWidth
/ m_blockSize
) + 1;
1917 m_programmeLayout
->SetHeight(m_channelHeight
);
1918 m_focusedProgrammeLayout
->SetHeight(m_channelHeight
);
1922 m_rulerWidth
= m_rulerLayout
->Size(HORIZONTAL
);
1923 m_gridPosX
= m_posX
+ m_rulerWidth
;
1924 m_gridPosY
= m_posY
+ m_channelHeight
+ m_rulerDateHeight
;
1925 m_gridWidth
= m_width
- m_rulerWidth
;
1926 m_gridHeight
= m_height
- m_channelHeight
- m_rulerDateHeight
;
1927 m_blockSize
= m_gridHeight
/ m_blocksPerPage
;
1928 m_rulerHeight
= m_rulerUnit
* m_blockSize
;
1929 m_channelPosX
= m_posX
+ m_rulerWidth
;
1930 m_channelPosY
= m_posY
+ m_rulerDateHeight
;
1931 m_rulerPosX
= m_posX
;
1932 m_rulerPosY
= m_posY
+ m_channelHeight
+ m_rulerDateHeight
;
1933 m_channelsPerPage
= m_gridWidth
/ m_channelWidth
;
1934 m_programmesPerPage
= (m_gridHeight
/ m_blockSize
) + 1;
1936 m_programmeLayout
->SetWidth(m_channelWidth
);
1937 m_focusedProgrammeLayout
->SetWidth(m_channelWidth
);
1940 // ensure that the scroll offsets are a multiple of our sizes
1941 m_channelScrollOffset
= m_channelOffset
* m_programmeLayout
->Size(m_orientation
);
1942 m_programmeScrollOffset
= m_blockOffset
* m_blockSize
;
1945 void CGUIEPGGridContainer::UpdateScrollOffset(unsigned int currentTime
)
1947 if (!m_programmeLayout
)
1950 m_channelScrollOffset
+= m_channelScrollSpeed
* (currentTime
- m_channelScrollLastTime
);
1951 if ((m_channelScrollSpeed
< 0 && m_channelScrollOffset
< m_channelOffset
* m_programmeLayout
->Size(m_orientation
)) ||
1952 (m_channelScrollSpeed
> 0 && m_channelScrollOffset
> m_channelOffset
* m_programmeLayout
->Size(m_orientation
)))
1954 m_channelScrollOffset
= m_channelOffset
* m_programmeLayout
->Size(m_orientation
);
1955 m_channelScrollSpeed
= 0;
1956 m_bEnableChannelScrolling
= true;
1959 m_channelScrollLastTime
= currentTime
;
1960 m_programmeScrollOffset
+= m_programmeScrollSpeed
* (currentTime
- m_programmeScrollLastTime
);
1962 if ((m_programmeScrollSpeed
< 0 && m_programmeScrollOffset
< m_blockOffset
* m_blockSize
) ||
1963 (m_programmeScrollSpeed
> 0 && m_programmeScrollOffset
> m_blockOffset
* m_blockSize
))
1965 m_programmeScrollOffset
= m_blockOffset
* m_blockSize
;
1966 m_programmeScrollSpeed
= 0;
1967 m_bEnableProgrammeScrolling
= true;
1970 m_programmeScrollLastTime
= currentTime
;
1972 if (m_channelScrollSpeed
|| m_programmeScrollSpeed
)
1976 void CGUIEPGGridContainer::GetCurrentLayouts()
1978 m_channelLayout
= nullptr;
1980 for (unsigned int i
= 0; i
< m_channelLayouts
.size(); i
++)
1982 if (m_channelLayouts
[i
].CheckCondition())
1984 m_channelLayout
= &m_channelLayouts
[i
];
1989 if (!m_channelLayout
&& !m_channelLayouts
.empty())
1990 m_channelLayout
= &m_channelLayouts
[0]; // failsafe
1992 m_focusedChannelLayout
= nullptr;
1994 for (unsigned int i
= 0; i
< m_focusedChannelLayouts
.size(); i
++)
1996 if (m_focusedChannelLayouts
[i
].CheckCondition())
1998 m_focusedChannelLayout
= &m_focusedChannelLayouts
[i
];
2003 if (!m_focusedChannelLayout
&& !m_focusedChannelLayouts
.empty())
2004 m_focusedChannelLayout
= &m_focusedChannelLayouts
[0]; // failsafe
2006 m_programmeLayout
= nullptr;
2008 for (unsigned int i
= 0; i
< m_programmeLayouts
.size(); i
++)
2010 if (m_programmeLayouts
[i
].CheckCondition())
2012 m_programmeLayout
= &m_programmeLayouts
[i
];
2017 if (!m_programmeLayout
&& !m_programmeLayouts
.empty())
2018 m_programmeLayout
= &m_programmeLayouts
[0]; // failsafe
2020 m_focusedProgrammeLayout
= nullptr;
2022 for (unsigned int i
= 0; i
< m_focusedProgrammeLayouts
.size(); i
++)
2024 if (m_focusedProgrammeLayouts
[i
].CheckCondition())
2026 m_focusedProgrammeLayout
= &m_focusedProgrammeLayouts
[i
];
2031 if (!m_focusedProgrammeLayout
&& !m_focusedProgrammeLayouts
.empty())
2032 m_focusedProgrammeLayout
= &m_focusedProgrammeLayouts
[0]; // failsafe
2034 m_rulerLayout
= nullptr;
2036 for (unsigned int i
= 0; i
< m_rulerLayouts
.size(); i
++)
2038 if (m_rulerLayouts
[i
].CheckCondition())
2040 m_rulerLayout
= &m_rulerLayouts
[i
];
2045 if (!m_rulerLayout
&& !m_rulerLayouts
.empty())
2046 m_rulerLayout
= &m_rulerLayouts
[0]; // failsafe
2048 m_rulerDateLayout
= nullptr;
2050 for (unsigned int i
= 0; i
< m_rulerDateLayouts
.size(); i
++)
2052 if (m_rulerDateLayouts
[i
].CheckCondition())
2054 m_rulerDateLayout
= &m_rulerDateLayouts
[i
];
2059 // Note: m_rulerDateLayout is optional; so no "failsafe" logic here (see above)
2062 void CGUIEPGGridContainer::SetRenderOffset(const CPoint
& offset
)
2064 m_renderOffset
= offset
;
2067 void CGUIEPGGridContainer::GetChannelCacheOffsets(int& cacheBefore
, int& cacheAfter
)
2069 if (m_channelScrollSpeed
> 0)
2072 cacheAfter
= m_cacheChannelItems
;
2074 else if (m_channelScrollSpeed
< 0)
2076 cacheBefore
= m_cacheChannelItems
;
2081 cacheBefore
= m_cacheChannelItems
/ 2;
2082 cacheAfter
= m_cacheChannelItems
/ 2;
2086 void CGUIEPGGridContainer::GetProgrammeCacheOffsets(int& cacheBefore
, int& cacheAfter
)
2088 if (m_programmeScrollSpeed
> 0)
2091 cacheAfter
= m_cacheProgrammeItems
;
2093 else if (m_programmeScrollSpeed
< 0)
2095 cacheBefore
= m_cacheProgrammeItems
;
2100 cacheBefore
= m_cacheProgrammeItems
/ 2;
2101 cacheAfter
= m_cacheProgrammeItems
/ 2;
2105 void CGUIEPGGridContainer::HandleChannels(bool bRender
,
2106 unsigned int currentTime
,
2107 CDirtyRegionList
& dirtyregions
,
2110 if (!m_focusedChannelLayout
|| !m_channelLayout
)
2113 const int chanOffset
= GetChannelScrollOffset(m_programmeLayout
);
2115 int cacheBeforeChannel
, cacheAfterChannel
;
2116 GetChannelCacheOffsets(cacheBeforeChannel
, cacheAfterChannel
);
2120 if (m_orientation
== VERTICAL
)
2121 CServiceBroker::GetWinSystem()->GetGfxContext().SetClipRegion(m_channelPosX
, m_channelPosY
, m_channelWidth
, m_gridHeight
);
2123 CServiceBroker::GetWinSystem()->GetGfxContext().SetClipRegion(m_channelPosX
, m_channelPosY
, m_gridWidth
, m_channelHeight
);
2127 // Free memory not used on screen
2128 if (m_gridModel
->ChannelItemsSize() > m_channelsPerPage
+ cacheBeforeChannel
+ cacheAfterChannel
)
2129 m_gridModel
->FreeChannelMemory(chanOffset
- cacheBeforeChannel
,
2130 chanOffset
+ m_channelsPerPage
- 1 + cacheAfterChannel
);
2133 CPoint originChannel
= CPoint(m_channelPosX
, m_channelPosY
) + m_renderOffset
;
2137 if (m_orientation
== VERTICAL
)
2139 pos
= originChannel
.y
;
2140 end
= m_posY
+ m_height
;
2144 pos
= originChannel
.x
;
2145 end
= m_posX
+ m_width
;
2148 // we offset our draw position to take into account scrolling and whether or not our focused
2149 // item is offscreen "above" the list.
2150 float drawOffset
= (chanOffset
- cacheBeforeChannel
) * m_channelLayout
->Size(m_orientation
) -
2151 GetChannelScrollOffsetPos();
2152 if (m_channelOffset
+ m_channelCursor
< chanOffset
)
2153 drawOffset
+= m_focusedChannelLayout
->Size(m_orientation
) - m_channelLayout
->Size(m_orientation
);
2156 end
+= cacheAfterChannel
* m_channelLayout
->Size(m_orientation
);
2158 float focusedPos
= 0;
2159 std::shared_ptr
<CGUIListItem
> focusedItem
;
2162 int current
= chanOffset
- cacheBeforeChannel
;
2163 while (pos
< end
&& m_gridModel
->HasChannelItems())
2165 int itemNo
= current
;
2166 if (itemNo
>= m_gridModel
->ChannelItemsSize())
2169 bool focused
= (current
== m_channelOffset
+ m_channelCursor
);
2172 item
= m_gridModel
->GetChannelItem(itemNo
);
2183 if (m_orientation
== VERTICAL
)
2184 RenderItem(originChannel
.x
, pos
, item
.get(), false);
2186 RenderItem(pos
, originChannel
.y
, item
.get(), false);
2189 else if (bAssignDepth
)
2194 AssignItemDepth(item
.get(), false);
2199 if (m_orientation
== VERTICAL
)
2200 ProcessItem(originChannel
.x
, pos
, item
, m_lastItem
, focused
, m_channelLayout
, m_focusedChannelLayout
, currentTime
, dirtyregions
);
2202 ProcessItem(pos
, originChannel
.y
, item
, m_lastItem
, focused
, m_channelLayout
, m_focusedChannelLayout
, currentTime
, dirtyregions
);
2205 // increment our position
2206 pos
+= focused
? m_focusedChannelLayout
->Size(m_orientation
) : m_channelLayout
->Size(m_orientation
);
2212 // render focused item last so it can overlap other items
2215 if (m_orientation
== VERTICAL
)
2216 RenderItem(originChannel
.x
, focusedPos
, focusedItem
.get(), true);
2218 RenderItem(focusedPos
, originChannel
.y
, focusedItem
.get(), true);
2221 CServiceBroker::GetWinSystem()->GetGfxContext().RestoreClipRegion();
2223 else if (bAssignDepth
&& focusedItem
)
2225 AssignItemDepth(focusedItem
.get(), true);
2229 void CGUIEPGGridContainer::HandleRulerDate(bool bRender
,
2230 unsigned int currentTime
,
2231 CDirtyRegionList
& dirtyregions
,
2234 if (!m_rulerDateLayout
|| m_gridModel
->RulerItemsSize() <= 1 || m_gridModel
->IsZeroGridDuration())
2237 CFileItemPtr
item(m_gridModel
->GetRulerItem(0));
2241 // Render single ruler item with date of selected programme
2242 CServiceBroker::GetWinSystem()->GetGfxContext().SetClipRegion(m_posX
, m_posY
, m_rulerDateWidth
, m_rulerDateHeight
);
2243 RenderItem(m_posX
, m_posY
, item
.get(), false);
2244 CServiceBroker::GetWinSystem()->GetGfxContext().RestoreClipRegion();
2246 else if (bAssignDepth
)
2248 AssignItemDepth(item
.get(), false);
2252 const int rulerOffset
= GetProgrammeScrollOffset();
2253 item
->SetLabel(m_gridModel
->GetRulerItem(rulerOffset
/ m_rulerUnit
+ 1)->GetLabel2());
2255 CFileItemPtr lastitem
;
2256 ProcessItem(m_posX
, m_posY
, item
, lastitem
, false, m_rulerDateLayout
, m_rulerDateLayout
, currentTime
, dirtyregions
);
2260 void CGUIEPGGridContainer::HandleRuler(bool bRender
,
2261 unsigned int currentTime
,
2262 CDirtyRegionList
& dirtyregions
,
2265 if (!m_rulerLayout
|| m_gridModel
->RulerItemsSize() <= 1 || m_gridModel
->IsZeroGridDuration())
2268 int rulerOffset
= GetProgrammeScrollOffset();
2270 CFileItemPtr
item(m_gridModel
->GetRulerItem(0));
2271 CFileItemPtr lastitem
;
2272 int cacheBeforeRuler
, cacheAfterRuler
;
2276 if (!m_rulerDateLayout
)
2278 // Render single ruler item with date of selected programme
2279 CServiceBroker::GetWinSystem()->GetGfxContext().SetClipRegion(m_posX
, m_posY
, m_width
, m_height
);
2280 RenderItem(m_posX
, m_posY
, item
.get(), false);
2281 CServiceBroker::GetWinSystem()->GetGfxContext().RestoreClipRegion();
2284 // render ruler items
2285 GetProgrammeCacheOffsets(cacheBeforeRuler
, cacheAfterRuler
);
2287 if (m_orientation
== VERTICAL
)
2288 CServiceBroker::GetWinSystem()->GetGfxContext().SetClipRegion(m_rulerPosX
, m_rulerPosY
, m_gridWidth
, m_rulerHeight
);
2290 CServiceBroker::GetWinSystem()->GetGfxContext().SetClipRegion(m_rulerPosX
, m_rulerPosY
, m_rulerWidth
, m_gridHeight
);
2292 else if (bAssignDepth
)
2294 if (!m_rulerDateLayout
)
2295 AssignItemDepth(item
.get(), false);
2296 GetProgrammeCacheOffsets(cacheBeforeRuler
, cacheAfterRuler
);
2300 if (!m_rulerDateLayout
)
2302 item
->SetLabel(m_gridModel
->GetRulerItem(rulerOffset
/ m_rulerUnit
+ 1)->GetLabel2());
2303 ProcessItem(m_posX
, m_posY
, item
, lastitem
, false, m_rulerLayout
, m_rulerLayout
, currentTime
, dirtyregions
, m_channelWidth
);
2306 GetProgrammeCacheOffsets(cacheBeforeRuler
, cacheAfterRuler
);
2308 // Free memory not used on screen
2309 if (m_gridModel
->RulerItemsSize() > m_blocksPerPage
+ cacheBeforeRuler
+ cacheAfterRuler
)
2310 m_gridModel
->FreeRulerMemory(rulerOffset
/ m_rulerUnit
+ 1 - cacheBeforeRuler
,
2311 rulerOffset
/ m_rulerUnit
+ 1 + m_blocksPerPage
- 1 +
2315 CPoint originRuler
= CPoint(m_rulerPosX
, m_rulerPosY
) + m_renderOffset
;
2319 if (m_orientation
== VERTICAL
)
2321 pos
= originRuler
.x
;
2322 end
= m_posX
+ m_width
;
2326 pos
= originRuler
.y
;
2327 end
= m_posY
+ m_height
;
2330 const float drawOffset
=
2331 (rulerOffset
- cacheBeforeRuler
) * m_blockSize
- GetProgrammeScrollOffsetPos();
2333 end
+= cacheAfterRuler
* m_rulerLayout
->Size(m_orientation
== VERTICAL
? HORIZONTAL
: VERTICAL
);
2335 if (rulerOffset
% m_rulerUnit
!= 0)
2337 /* first ruler marker starts before current view */
2338 int startBlock
= rulerOffset
- 1;
2340 while (startBlock
% m_rulerUnit
!= 0)
2343 int missingSection
= rulerOffset
- startBlock
;
2345 pos
-= missingSection
* m_blockSize
;
2348 while (pos
< end
&& (rulerOffset
/ m_rulerUnit
+ 1) < m_gridModel
->RulerItemsSize())
2350 item
= m_gridModel
->GetRulerItem(rulerOffset
/ m_rulerUnit
+ 1);
2352 if (m_orientation
== VERTICAL
)
2355 RenderItem(pos
, originRuler
.y
, item
.get(), false);
2356 else if (bAssignDepth
)
2357 AssignItemDepth(item
.get(), false);
2359 ProcessItem(pos
, originRuler
.y
, item
, lastitem
, false, m_rulerLayout
, m_rulerLayout
, currentTime
, dirtyregions
, m_rulerWidth
);
2361 pos
+= m_rulerWidth
;
2366 RenderItem(originRuler
.x
, pos
, item
.get(), false);
2367 else if (bAssignDepth
)
2368 AssignItemDepth(item
.get(), false);
2370 ProcessItem(originRuler
.x
, pos
, item
, lastitem
, false, m_rulerLayout
, m_rulerLayout
, currentTime
, dirtyregions
, m_rulerHeight
);
2372 pos
+= m_rulerHeight
;
2375 rulerOffset
+= m_rulerUnit
;
2379 CServiceBroker::GetWinSystem()->GetGfxContext().RestoreClipRegion();
2382 void CGUIEPGGridContainer::HandleProgrammeGrid(bool bRender
,
2383 unsigned int currentTime
,
2384 CDirtyRegionList
& dirtyregions
,
2387 if (!m_focusedProgrammeLayout
|| !m_programmeLayout
|| m_gridModel
->RulerItemsSize() <= 1 || m_gridModel
->IsZeroGridDuration())
2390 const int blockOffset
= GetProgrammeScrollOffset();
2391 const int chanOffset
= GetChannelScrollOffset(m_programmeLayout
);
2393 int cacheBeforeProgramme
, cacheAfterProgramme
;
2394 GetProgrammeCacheOffsets(cacheBeforeProgramme
, cacheAfterProgramme
);
2398 CServiceBroker::GetWinSystem()->GetGfxContext().SetClipRegion(m_gridPosX
, m_gridPosY
, m_gridWidth
, m_gridHeight
);
2400 else if (!bAssignDepth
)
2402 int cacheBeforeChannel
, cacheAfterChannel
;
2403 GetChannelCacheOffsets(cacheBeforeChannel
, cacheAfterChannel
);
2405 // Free memory not used on screen
2406 int firstChannel
= chanOffset
- cacheBeforeChannel
;
2407 if (firstChannel
< 0)
2409 int lastChannel
= chanOffset
+ m_channelsPerPage
- 1 + cacheAfterChannel
;
2410 if (lastChannel
> m_gridModel
->GetLastChannel())
2411 lastChannel
= m_gridModel
->GetLastChannel();
2412 int firstBlock
= blockOffset
- cacheBeforeProgramme
;
2415 int lastBlock
= blockOffset
+ m_programmesPerPage
- 1 + cacheAfterProgramme
;
2416 if (lastBlock
> m_gridModel
->GetLastBlock())
2417 lastBlock
= m_gridModel
->GetLastBlock();
2419 if (m_gridModel
->FreeProgrammeMemory(firstChannel
, lastChannel
, firstBlock
, lastBlock
))
2421 // announce changed viewport
2422 const CGUIMessage
msg(
2423 GUI_MSG_REFRESH_LIST
, GetParentID(), GetID(), static_cast<int>(PVREvent::Epg
));
2424 CServiceBroker::GetAppMessenger()->SendGUIMessage(msg
);
2428 CPoint originProgramme
= CPoint(m_gridPosX
, m_gridPosY
) + m_renderOffset
;
2434 if (m_orientation
== VERTICAL
)
2436 posA
= originProgramme
.x
;
2437 endA
= m_posX
+ m_width
;
2438 posB
= originProgramme
.y
;
2439 endB
= m_gridPosY
+ m_gridHeight
;
2443 posA
= originProgramme
.y
;
2444 endA
= m_posY
+ m_height
;
2445 posB
= originProgramme
.x
;
2446 endB
= m_gridPosX
+ m_gridWidth
;
2449 endA
+= cacheAfterProgramme
* m_blockSize
;
2451 const float drawOffsetA
= blockOffset
* m_blockSize
- GetProgrammeScrollOffsetPos();
2452 posA
+= drawOffsetA
;
2453 const float drawOffsetB
=
2454 (chanOffset
- cacheBeforeProgramme
) * m_channelLayout
->Size(m_orientation
) -
2455 GetChannelScrollOffsetPos();
2456 posB
+= drawOffsetB
;
2458 int channel
= chanOffset
- cacheBeforeProgramme
;
2460 float focusedPosX
= 0;
2461 float focusedPosY
= 0;
2462 CFileItemPtr focusedItem
;
2465 const int lastChannel
= m_gridModel
->GetLastChannel();
2466 while (posB
< endB
&& HasData() && channel
<= lastChannel
)
2470 int block
= blockOffset
;
2473 const int startBlock
= blockOffset
== 0 ? 0 : blockOffset
- 1;
2474 if (startBlock
== 0 || m_gridModel
->IsSameGridItem(channel
, block
, startBlock
))
2476 // First program starts before current view
2477 block
= m_gridModel
->GetGridItemStartBlock(channel
, startBlock
);
2478 const int missingSection
= blockOffset
- block
;
2479 posA2
-= missingSection
* m_blockSize
;
2482 const int lastBlock
= m_gridModel
->GetLastBlock();
2483 while (posA2
< endA
&& HasData() && block
<= lastBlock
)
2485 item
= m_gridModel
->GetGridItem(channel
, block
);
2487 bool focused
= (channel
== m_channelOffset
+ m_channelCursor
) &&
2488 m_gridModel
->IsSameGridItem(m_channelOffset
+ m_channelCursor
,
2489 m_blockOffset
+ m_blockCursor
, block
);
2493 // reset to grid start position if first item is out of grid view
2500 focusedPosX
= posA2
;
2506 if (m_orientation
== VERTICAL
)
2507 RenderItem(posA2
, posB
, item
.get(), focused
);
2509 RenderItem(posB
, posA2
, item
.get(), focused
);
2512 else if (bAssignDepth
)
2514 // reset to grid start position if first item is out of grid view
2521 focusedPosX
= posA2
;
2527 AssignItemDepth(item
.get(), focused
);
2532 // calculate the size to truncate if item is out of grid view
2533 float truncateSize
= 0;
2536 truncateSize
= posA
- posA2
;
2537 posA2
= posA
; // reset to grid start position
2541 std::unique_lock
<CCriticalSection
> lock(m_critSection
);
2542 // truncate item's width
2543 m_gridModel
->DecreaseGridItemWidth(channel
, block
, truncateSize
);
2546 if (m_orientation
== VERTICAL
)
2547 ProcessItem(posA2
, posB
, item
, m_lastChannel
, focused
, m_programmeLayout
,
2548 m_focusedProgrammeLayout
, currentTime
, dirtyregions
,
2549 m_gridModel
->GetGridItemWidth(channel
, block
));
2551 ProcessItem(posB
, posA2
, item
, m_lastChannel
, focused
, m_programmeLayout
,
2552 m_focusedProgrammeLayout
, currentTime
, dirtyregions
,
2553 m_gridModel
->GetGridItemWidth(channel
, block
));
2556 // increment our X position
2557 posA2
+= m_gridModel
->GetGridItemWidth(
2558 channel
, block
); // assumes focused & unfocused layouts have equal length
2559 block
+= MathUtils::round_int(
2560 static_cast<double>(m_gridModel
->GetGridItemOriginWidth(channel
, block
) / m_blockSize
));
2564 // increment our Y position
2566 posB
+= (m_orientation
== VERTICAL
) ? m_channelHeight
: m_channelWidth
;
2571 // and render the focused item last (for overlapping purposes)
2574 if (m_orientation
== VERTICAL
)
2575 RenderItem(focusedPosX
, focusedPosY
, focusedItem
.get(), true);
2577 RenderItem(focusedPosY
, focusedPosX
, focusedItem
.get(), true);
2580 CServiceBroker::GetWinSystem()->GetGfxContext().RestoreClipRegion();
2582 else if (bAssignDepth
&& focusedItem
)
2584 AssignItemDepth(focusedItem
.get(), true);
2588 void CGUIEPGGridContainer::AssignDepth()
2590 unsigned int dummyTime
= 0;
2591 CDirtyRegionList dummyRegions
;
2592 HandleChannels(false, dummyTime
, dummyRegions
, true);
2593 HandleRuler(false, dummyTime
, dummyRegions
, true);
2594 HandleRulerDate(false, dummyTime
, dummyRegions
, true);
2595 HandleProgrammeGrid(false, dummyTime
, dummyRegions
, true);
2596 m_guiProgressIndicatorTextureDepth
= CServiceBroker::GetWinSystem()->GetGfxContext().GetDepth();
2599 void CGUIEPGGridContainer::AssignItemDepth(CGUIListItem
* item
, bool focused
)
2603 if (item
->GetFocusedLayout())
2604 item
->GetFocusedLayout()->AssignDepth();
2608 if (item
->GetFocusedLayout() && item
->GetFocusedLayout()->IsAnimating(ANIM_TYPE_UNFOCUS
))
2609 item
->GetFocusedLayout()->AssignDepth();
2610 else if (item
->GetLayout())
2611 item
->GetLayout()->AssignDepth();