[Windows] Fix driver version detection of AMD RDNA+ GPU on Windows 10
[xbmc.git] / xbmc / pvr / guilib / GUIEPGGridContainer.cpp
blobffb9e4aa8b8305b3b953c0bbf3a8179aed0354a7
1 /*
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.
7 */
9 #include "GUIEPGGridContainer.h"
11 #include "FileItem.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"
31 #include <algorithm>
32 #include <memory>
33 #include <mutex>
34 #include <string>
35 #include <utility>
37 #include <tinyxml.h>
39 using namespace KODI;
40 using namespace PVR;
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,
46 int controlID,
47 float posX,
48 float posY,
49 float width,
50 float height,
51 ORIENTATION orientation,
52 int scrollTime,
53 int preloadItems,
54 int timeBlocks,
55 int rulerUnit,
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)
130 : nullptr),
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)
154 m_pageControl = id;
157 void CGUIEPGGridContainer::Process(unsigned int currentTime, CDirtyRegionList& dirtyregions)
159 ValidateOffset();
161 if (m_bInvalidated)
163 UpdateLayout();
165 if (m_pageControl)
167 int iItemsPerPage;
168 int iTotalItems;
170 if (m_orientation == VERTICAL)
172 iItemsPerPage = m_channelsPerPage;
173 iTotalItems = m_gridModel->ChannelItemsSize();
175 else
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);
193 if (m_pageControl)
195 int iItem =
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();
215 RenderRuler();
216 RenderRulerDate();
217 RenderChannels();
219 else
221 RenderChannels();
222 RenderRulerDate();
223 RenderRuler();
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())
286 return -1.0f;
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);
318 else
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)
341 return;
343 // set the origin
344 CServiceBroker::GetWinSystem()->GetGfxContext().SetOrigin(posX, posY);
346 if (m_bInvalidated)
347 item->SetInvalid();
349 if (focused)
351 if (!item->GetFocusedLayout())
353 item->SetFocusedLayout(std::make_unique<CGUIListItemLayout>(*focusedlayout, this));
356 if (resize != -1.0f)
358 if (m_orientation == VERTICAL)
359 item->GetFocusedLayout()->SetWidth(resize);
360 else
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);
379 lastitem = item;
381 else
383 if (!item->GetLayout())
385 item->SetLayout(std::make_unique<CGUIListItemLayout>(*normallayout, this));
388 if (resize != -1.0f)
390 if (m_orientation == VERTICAL)
391 item->GetLayout()->SetWidth(resize);
392 else
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);
401 else
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)
409 // set the origin
410 CServiceBroker::GetWinSystem()->GetGfxContext().SetOrigin(posX, posY);
412 if (focused)
414 if (item->GetFocusedLayout())
415 item->GetFocusedLayout()->Render(item, m_parentID);
417 else
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:
434 case ACTION_MOVE_UP:
435 case ACTION_NAV_BACK:
436 // use base class implementation
437 return CGUIControl::OnAction(action);
439 case ACTION_NEXT_ITEM:
440 // skip +12h
441 ScrollToBlockOffset(m_blockOffset + (12 * 60 / CGUIEPGGridContainerModel::MINSPERBLOCK));
442 SetBlock(m_blockCursor);
443 return true;
445 case ACTION_PREV_ITEM:
446 // skip -12h
447 ScrollToBlockOffset(m_blockOffset - (12 * 60 / CGUIEPGGridContainerModel::MINSPERBLOCK));
448 SetBlock(m_blockCursor);
449 return true;
451 case REMOTE_0:
452 GoToNow();
453 return true;
455 case ACTION_PAGE_UP:
456 if (m_orientation == VERTICAL)
458 if (m_channelOffset == 0)
460 // already on the first page, so move to the first item
461 SetChannel(0);
463 else
465 // scroll up to the previous page
466 ChannelScroll(-m_channelsPerPage);
469 else
471 if (m_blockOffset == 0)
473 // already on the first page, so move to the first item
474 SetBlock(0);
476 else
478 // scroll up to the previous page
479 ProgrammesScroll(-m_blocksPerPage);
482 return true;
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);
493 else
495 // scroll down to the next page
496 ChannelScroll(m_channelsPerPage);
499 else
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);
507 else
509 // scroll down to the next page
510 ProgrammesScroll(m_blocksPerPage);
514 return true;
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)
529 handled = true;
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);
537 return handled;
539 else
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)
548 handled = true;
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);
556 return handled;
558 break;
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)
572 handled = true;
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);
582 return handled;
584 else
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)
593 handled = true;
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);
601 return handled;
603 break;
605 default:
606 if (action.GetID())
607 return OnClick(action.GetID());
609 break;
612 return false;
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);
629 else
631 ScrollToBlockOffset(message.GetParam1());
632 SetBlock(m_blockCursor);
634 return true;
636 break;
638 case GUI_MSG_LABEL_BIND:
639 UpdateItems();
640 return true;
642 case GUI_MSG_REFRESH_LIST:
643 // update our list contents
644 m_gridModel->SetInvalid();
645 break;
649 return CGUIControl::OnMessage(message);
652 void CGUIEPGGridContainer::UpdateItems()
654 std::unique_lock<CCriticalSection> lock(m_critSection);
656 if (!m_updatedGridModel)
657 return;
659 // Save currently selected epg tag and grid coordinates. Selection shall be restored after update.
660 std::shared_ptr<CPVREpgInfoTag> prevSelectedEpgTag;
661 if (HasData())
662 prevSelectedEpgTag =
663 m_gridModel->GetGridItem(m_channelCursor + m_channelOffset, m_blockCursor + m_blockOffset)
664 ->GetEPGInfoTag();
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;
672 int channelUid = -1;
673 unsigned int broadcastUid = 0;
675 if (prevSelectedEpgTag)
677 // get the block offset relative to the first block of the selected event
678 eventOffset =
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;
688 else
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;
705 if (prevItem)
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;
715 else
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.
742 SetInvalid();
743 m_bEnableChannelScrolling = false;
744 GoToChannel(newChannelIndex);
745 m_bEnableProgrammeScrolling = false;
746 GoToNow();
747 return;
751 if (newChannelIndex >= m_gridModel->ChannelItemsSize() ||
752 newBlockIndex >= m_gridModel->GridItemsSize() ||
753 m_gridModel->GetGridItem(newChannelIndex, newBlockIndex)->GetEPGInfoTag() !=
754 prevSelectedEpgTag)
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())
766 // default to now
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
785 newChannelIndex = 0;
789 // restore previous selection.
790 if (newChannelIndex == oldChannelIndex && newBlockIndex == oldBlockIndex)
792 // same coordinates, keep current grid view port
793 UpdateItem();
795 else
797 // new coordinates, move grid view port accordingly
798 SetInvalid();
800 if (newBlockIndex != oldBlockIndex)
802 m_bEnableProgrammeScrolling = false;
803 GoToBlock(newBlockIndex);
806 if (newChannelIndex != oldChannelIndex)
808 m_bEnableChannelScrolling = false;
809 GoToChannel(newChannelIndex);
813 else
815 // no previous selection, goto now
816 SetInvalid();
817 m_bEnableProgrammeScrolling = false;
818 GoToNow();
822 float CGUIEPGGridContainer::GetChannelScrollOffsetPos() const
824 if (m_bEnableChannelScrolling)
825 return m_channelScrollOffset;
826 else
827 return m_channelOffset * m_channelLayout->Size(m_orientation);
830 float CGUIEPGGridContainer::GetProgrammeScrollOffsetPos() const
832 if (m_bEnableProgrammeScrolling)
833 return m_programmeScrollOffset;
834 else
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)));
843 else
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));
851 else
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;
863 if (offset < 0)
864 offset = 0;
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()
879 if (!HasData())
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);
892 SetChannel(0);
894 else if (action.GetNavigation() == GetID() || !action.HasActionsMeetingCondition()) // wrap around
896 int offset = m_gridModel->ChannelItemsSize() - m_channelsPerPage;
898 if (offset < 0)
899 offset = 0;
901 SetChannel(m_gridModel->GetLastChannel() - offset);
902 ScrollToChannelOffset(offset);
904 else
905 CGUIControl::OnUp();
907 else
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());
914 UpdateBlock();
915 return;
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);
921 UpdateBlock();
922 return;
925 CGUIControl::OnUp();
929 void CGUIEPGGridContainer::OnDown()
931 if (!HasData())
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);
943 else
945 ScrollToChannelOffset(m_channelOffset + 1);
946 SetChannel(m_channelsPerPage - 1);
949 else if (action.GetNavigation() == GetID() || !action.HasActionsMeetingCondition()) // wrap around
951 ScrollToChannelOffset(0);
952 SetChannel(0);
954 else
955 CGUIControl::OnDown();
957 else
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());
965 UpdateBlock();
966 return;
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);
974 UpdateBlock();
975 return;
978 CGUIControl::OnDown();
982 void CGUIEPGGridContainer::OnLeft()
984 if (!HasData())
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());
994 UpdateBlock();
995 return;
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);
1001 UpdateBlock();
1002 return;
1005 CGUIControl::OnLeft();
1007 else
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);
1017 SetChannel(0);
1019 else if (action.GetNavigation() == GetID() || !action.HasActionsMeetingCondition()) // wrap around
1021 int offset = m_gridModel->ChannelItemsSize() - m_channelsPerPage;
1023 if (offset < 0)
1024 offset = 0;
1026 SetChannel(m_gridModel->GetLastChannel() - offset);
1027 ScrollToChannelOffset(offset);
1029 else
1030 CGUIControl::OnLeft();
1034 void CGUIEPGGridContainer::OnRight()
1036 if (!HasData())
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());
1047 UpdateBlock();
1048 return;
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);
1056 UpdateBlock();
1057 return;
1060 CGUIControl::OnRight();
1062 else
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);
1071 else
1073 ScrollToChannelOffset(m_channelOffset + 1);
1074 SetChannel(m_channelsPerPage - 1);
1077 else if (action.GetNavigation() == GetID() || !action.HasActionsMeetingCondition()) // wrap around
1079 SetChannel(0);
1080 ScrollToChannelOffset(0);
1082 else
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);
1095 return true;
1098 return false;
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);
1109 return true;
1112 return false;
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);
1124 return true;
1127 return false;
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,
1139 m_blockTravelAxis))
1141 m_channelCursor = channel;
1142 MarkDirtyRegion();
1143 UpdateBlock(false);
1148 void CGUIEPGGridContainer::SetBlock(int block, bool bUpdateBlockTravelAxis /* = true */)
1150 std::unique_lock<CCriticalSection> lock(m_critSection);
1152 if (block < 0)
1153 m_blockCursor = 0;
1154 else if (block > m_blocksPerPage - 1)
1155 m_blockCursor = m_blocksPerPage - 1;
1156 else
1157 m_blockCursor = block;
1159 if (bUpdateBlockTravelAxis)
1160 m_blockTravelAxis = m_blockOffset + m_blockCursor;
1162 UpdateItem();
1163 MarkDirtyRegion();
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);
1175 if (item)
1176 return item->GetFocusedLayout();
1178 return nullptr;
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))
1185 return false;
1187 int channel;
1188 int block;
1190 if (m_orientation == VERTICAL)
1192 channel = point.y / m_channelHeight;
1193 block = point.x / m_blockSize;
1195 else
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();
1207 if (channel < 0)
1208 channel = 0;
1210 if (block > m_blocksPerPage)
1211 block = m_blocksPerPage - 1;
1213 if (block < 0)
1214 block = 0;
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())
1221 return false;
1223 // bail if block isn't occupied
1224 if (!m_gridModel->GetGridItem(channelIndex, blockIndex))
1225 return false;
1227 SetChannel(channel);
1228 SetBlock(block);
1229 return true;
1232 EVENT_RESULT CGUIEPGGridContainer::OnMouseEvent(const CPoint& point,
1233 const MOUSE::CMouseEvent& event)
1235 switch (event.m_id)
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)));
1283 m_blockOffset =
1284 MathUtils::round_int(static_cast<double>(m_programmeScrollOffset / m_blockSize));
1285 ValidateOffset();
1287 return EVENT_RESULT_HANDLED;
1289 default:
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);
1307 return true;
1309 return false;
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);
1318 return true;
1320 return false;
1323 bool CGUIEPGGridContainer::OnClick(int actionID)
1325 int subItem = 0;
1327 if (actionID == ACTION_SELECT_ITEM || actionID == ACTION_MOUSE_LEFT_CLICK)
1329 // grab the currently focused subitem (if applicable)
1330 CGUIListItemLayout* focusedLayout = GetFocusedLayout();
1332 if (focusedLayout)
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);
1345 return true;
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);
1357 if (fileItem)
1358 return fileItem->GetPVRChannelGroupMemberInfoTag();
1360 return {};
1363 CDateTime CGUIEPGGridContainer::GetSelectedDate() const
1365 return m_gridModel->GetStartTimeForBlock(m_blockOffset + m_blockCursor);
1368 CFileItemPtr CGUIEPGGridContainer::GetSelectedGridItem(int offset /*= 0*/) const
1370 CFileItemPtr item;
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);
1376 return item;
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();
1391 if (item < 0)
1392 item += m_gridModel->ChannelItemsSize();
1394 return m_gridModel->GetChannelItem(item);
1396 else
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
1406 std::string label;
1407 switch (info)
1409 case CONTAINER_NUM_PAGES:
1410 if (m_channelsPerPage > 0)
1411 label = std::to_string((m_gridModel->ChannelItemsSize() + m_channelsPerPage - 1) /
1412 m_channelsPerPage);
1413 else
1414 label = std::to_string(0);
1415 break;
1416 case CONTAINER_CURRENT_PAGE:
1417 if (m_channelsPerPage > 0)
1418 label = std::to_string(1 + (m_channelCursor + m_channelOffset) / m_channelsPerPage);
1419 else
1420 label = std::to_string(1);
1421 break;
1422 case CONTAINER_POSITION:
1423 label = std::to_string(1 + m_channelCursor + m_channelOffset);
1424 break;
1425 case CONTAINER_NUM_ITEMS:
1426 label = std::to_string(m_gridModel->ChannelItemsSize());
1427 break;
1428 default:
1429 break;
1431 return label;
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,
1440 int channelIndex,
1441 int blockIndex)
1443 if (item && channelIndex < m_gridModel->ChannelItemsSize() &&
1444 blockIndex < m_gridModel->GridItemsSize())
1446 m_itemStartBlock = m_gridModel->GetGridItemStartBlock(channelIndex, blockIndex);
1447 return true;
1449 else
1451 m_itemStartBlock = 0;
1452 return false;
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())
1462 return {};
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
1474 block += 1;
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);
1484 if (block > 0)
1486 // last block of previous event is one block before start block of selected event
1487 block -= 1;
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())
1501 SetInvalid();
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;
1513 if (range <= 0)
1514 range = 1;
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;
1530 MarkDirtyRegion();
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;
1543 if (range <= 0)
1544 range = 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;
1560 MarkDirtyRegion();
1563 void CGUIEPGGridContainer::ValidateOffset()
1565 std::unique_lock<CCriticalSection> lock(m_critSection);
1567 if (!m_programmeLayout)
1568 return;
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)
1596 m_blockOffset = 0;
1597 m_programmeScrollOffset = 0;
1601 void CGUIEPGGridContainer::LoadLayout(TiXmlElement* layout)
1603 /* layouts for the channel column */
1604 TiXmlElement* itemElement = layout->FirstChildElement("channellayout");
1605 while (itemElement)
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");
1612 while (itemElement)
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");
1621 while (itemElement)
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");
1628 while (itemElement)
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");
1637 while (itemElement)
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");
1646 while (itemElement)
1648 m_rulerLayouts.emplace_back();
1649 m_rulerLayouts.back().LoadLayout(itemElement, GetParentID(), false, m_width, m_height);
1650 itemElement = itemElement->NextSiblingElement("rulerlayout");
1653 UpdateLayout();
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);
1666 if (item)
1667 return item->GetLabel();
1670 return {};
1673 void CGUIEPGGridContainer::JumpToNow()
1675 m_bEnableProgrammeScrolling = false;
1676 GoToNow();
1679 void CGUIEPGGridContainer::JumpToDate(const CDateTime& date)
1681 m_bEnableProgrammeScrolling = false;
1682 GoToDate(date);
1685 void CGUIEPGGridContainer::GoToBegin()
1687 ScrollToBlockOffset(0);
1688 SetBlock(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)
1713 SetBlock(offset);
1715 else
1717 SetBlock(offset + 1);
1721 void CGUIEPGGridContainer::GoToFirstChannel()
1723 GoToChannel(0);
1726 void CGUIEPGGridContainer::GoToLastChannel()
1728 if (m_gridModel->ChannelItemsSize())
1729 GoToChannel(m_gridModel->GetLastChannel());
1730 else
1731 GoToChannel(0);
1734 void CGUIEPGGridContainer::GoToTop()
1736 if (m_orientation == VERTICAL)
1738 GoToChannel(0);
1740 else
1742 GoToBlock(0);
1746 void CGUIEPGGridContainer::GoToBottom()
1748 if (m_orientation == VERTICAL)
1750 if (m_gridModel->HasChannelItems())
1751 GoToChannel(m_gridModel->GetLastChannel());
1752 else
1753 GoToChannel(0);
1755 else
1757 if (m_gridModel->GridItemsSize())
1758 GoToBlock(m_gridModel->GetLastBlock());
1759 else
1760 GoToBlock(0);
1764 void CGUIEPGGridContainer::GoToMostLeft()
1766 if (m_orientation == VERTICAL)
1768 GoToBlock(0);
1770 else
1772 GoToChannel(0);
1776 void CGUIEPGGridContainer::GoToMostRight()
1778 if (m_orientation == VERTICAL)
1780 if (m_gridModel->GridItemsSize())
1781 GoToBlock(m_gridModel->GetLastBlock());
1782 else
1783 GoToBlock(0);
1785 else
1787 if (m_gridModel->HasChannelItems())
1788 GoToChannel(m_gridModel->GetLastChannel());
1789 else
1790 GoToChannel(0);
1794 void CGUIEPGGridContainer::SetTimelineItems(const std::unique_ptr<CFileItemList>& items,
1795 const CDateTime& gridStart,
1796 const CDateTime& gridEnd)
1798 int iRulerUnit;
1799 int iFirstChannel;
1800 int iChannelsPerPage;
1801 int iBlocksPerPage;
1802 int iFirstBlock;
1803 float fBlockSize;
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)
1840 // first page
1841 ScrollToChannelOffset(0);
1842 SetChannel(channelIndex);
1844 else if (channelIndex > m_gridModel->ChannelItemsSize() - m_channelsPerPage)
1846 // last page
1847 ScrollToChannelOffset(m_gridModel->ChannelItemsSize() - m_channelsPerPage);
1848 SetChannel(channelIndex - (m_gridModel->ChannelItemsSize() - m_channelsPerPage));
1850 else
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)
1862 // last page
1863 ScrollToBlockOffset(lastPage);
1864 SetBlock(blockIndex - lastPage);
1866 else
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)
1886 return;
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);
1920 else
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)
1948 return;
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)
1973 MarkDirtyRegion();
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];
1985 break;
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];
1999 break;
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];
2013 break;
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];
2027 break;
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];
2041 break;
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];
2055 break;
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)
2071 cacheBefore = 0;
2072 cacheAfter = m_cacheChannelItems;
2074 else if (m_channelScrollSpeed < 0)
2076 cacheBefore = m_cacheChannelItems;
2077 cacheAfter = 0;
2079 else
2081 cacheBefore = m_cacheChannelItems / 2;
2082 cacheAfter = m_cacheChannelItems / 2;
2086 void CGUIEPGGridContainer::GetProgrammeCacheOffsets(int& cacheBefore, int& cacheAfter)
2088 if (m_programmeScrollSpeed > 0)
2090 cacheBefore = 0;
2091 cacheAfter = m_cacheProgrammeItems;
2093 else if (m_programmeScrollSpeed < 0)
2095 cacheBefore = m_cacheProgrammeItems;
2096 cacheAfter = 0;
2098 else
2100 cacheBefore = m_cacheProgrammeItems / 2;
2101 cacheAfter = m_cacheProgrammeItems / 2;
2105 void CGUIEPGGridContainer::HandleChannels(bool bRender,
2106 unsigned int currentTime,
2107 CDirtyRegionList& dirtyregions,
2108 bool bAssignDepth)
2110 if (!m_focusedChannelLayout || !m_channelLayout)
2111 return;
2113 const int chanOffset = GetChannelScrollOffset(m_programmeLayout);
2115 int cacheBeforeChannel, cacheAfterChannel;
2116 GetChannelCacheOffsets(cacheBeforeChannel, cacheAfterChannel);
2118 if (bRender)
2120 if (m_orientation == VERTICAL)
2121 CServiceBroker::GetWinSystem()->GetGfxContext().SetClipRegion(m_channelPosX, m_channelPosY, m_channelWidth, m_gridHeight);
2122 else
2123 CServiceBroker::GetWinSystem()->GetGfxContext().SetClipRegion(m_channelPosX, m_channelPosY, m_gridWidth, m_channelHeight);
2125 else
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;
2134 float pos;
2135 float end;
2137 if (m_orientation == VERTICAL)
2139 pos = originChannel.y;
2140 end = m_posY + m_height;
2142 else
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);
2155 pos += drawOffset;
2156 end += cacheAfterChannel * m_channelLayout->Size(m_orientation);
2158 float focusedPos = 0;
2159 std::shared_ptr<CGUIListItem> focusedItem;
2161 CFileItemPtr item;
2162 int current = chanOffset - cacheBeforeChannel;
2163 while (pos < end && m_gridModel->HasChannelItems())
2165 int itemNo = current;
2166 if (itemNo >= m_gridModel->ChannelItemsSize())
2167 break;
2169 bool focused = (current == m_channelOffset + m_channelCursor);
2170 if (itemNo >= 0)
2172 item = m_gridModel->GetChannelItem(itemNo);
2173 if (bRender)
2175 // render our item
2176 if (focused)
2178 focusedPos = pos;
2179 focusedItem = item;
2181 else
2183 if (m_orientation == VERTICAL)
2184 RenderItem(originChannel.x, pos, item.get(), false);
2185 else
2186 RenderItem(pos, originChannel.y, item.get(), false);
2189 else if (bAssignDepth)
2191 if (focused)
2192 focusedItem = item;
2193 else
2194 AssignItemDepth(item.get(), false);
2196 else
2198 // process our item
2199 if (m_orientation == VERTICAL)
2200 ProcessItem(originChannel.x, pos, item, m_lastItem, focused, m_channelLayout, m_focusedChannelLayout, currentTime, dirtyregions);
2201 else
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);
2207 current++;
2210 if (bRender)
2212 // render focused item last so it can overlap other items
2213 if (focusedItem)
2215 if (m_orientation == VERTICAL)
2216 RenderItem(originChannel.x, focusedPos, focusedItem.get(), true);
2217 else
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,
2232 bool bAssignDepth)
2234 if (!m_rulerDateLayout || m_gridModel->RulerItemsSize() <= 1 || m_gridModel->IsZeroGridDuration())
2235 return;
2237 CFileItemPtr item(m_gridModel->GetRulerItem(0));
2239 if (bRender)
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);
2250 else
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,
2263 bool bAssignDepth)
2265 if (!m_rulerLayout || m_gridModel->RulerItemsSize() <= 1 || m_gridModel->IsZeroGridDuration())
2266 return;
2268 int rulerOffset = GetProgrammeScrollOffset();
2270 CFileItemPtr item(m_gridModel->GetRulerItem(0));
2271 CFileItemPtr lastitem;
2272 int cacheBeforeRuler, cacheAfterRuler;
2274 if (bRender)
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);
2289 else
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);
2298 else
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 +
2312 cacheAfterRuler);
2315 CPoint originRuler = CPoint(m_rulerPosX, m_rulerPosY) + m_renderOffset;
2316 float pos;
2317 float end;
2319 if (m_orientation == VERTICAL)
2321 pos = originRuler.x;
2322 end = m_posX + m_width;
2324 else
2326 pos = originRuler.y;
2327 end = m_posY + m_height;
2330 const float drawOffset =
2331 (rulerOffset - cacheBeforeRuler) * m_blockSize - GetProgrammeScrollOffsetPos();
2332 pos += drawOffset;
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)
2341 startBlock--;
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)
2354 if (bRender)
2355 RenderItem(pos, originRuler.y, item.get(), false);
2356 else if (bAssignDepth)
2357 AssignItemDepth(item.get(), false);
2358 else
2359 ProcessItem(pos, originRuler.y, item, lastitem, false, m_rulerLayout, m_rulerLayout, currentTime, dirtyregions, m_rulerWidth);
2361 pos += m_rulerWidth;
2363 else
2365 if (bRender)
2366 RenderItem(originRuler.x, pos, item.get(), false);
2367 else if (bAssignDepth)
2368 AssignItemDepth(item.get(), false);
2369 else
2370 ProcessItem(originRuler.x, pos, item, lastitem, false, m_rulerLayout, m_rulerLayout, currentTime, dirtyregions, m_rulerHeight);
2372 pos += m_rulerHeight;
2375 rulerOffset += m_rulerUnit;
2378 if (bRender)
2379 CServiceBroker::GetWinSystem()->GetGfxContext().RestoreClipRegion();
2382 void CGUIEPGGridContainer::HandleProgrammeGrid(bool bRender,
2383 unsigned int currentTime,
2384 CDirtyRegionList& dirtyregions,
2385 bool bAssignDepth)
2387 if (!m_focusedProgrammeLayout || !m_programmeLayout || m_gridModel->RulerItemsSize() <= 1 || m_gridModel->IsZeroGridDuration())
2388 return;
2390 const int blockOffset = GetProgrammeScrollOffset();
2391 const int chanOffset = GetChannelScrollOffset(m_programmeLayout);
2393 int cacheBeforeProgramme, cacheAfterProgramme;
2394 GetProgrammeCacheOffsets(cacheBeforeProgramme, cacheAfterProgramme);
2396 if (bRender)
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)
2408 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;
2413 if (firstBlock < 0)
2414 firstBlock = 0;
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;
2429 float posA;
2430 float endA;
2431 float posB;
2432 float endB;
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;
2441 else
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;
2463 CFileItemPtr item;
2465 const int lastChannel = m_gridModel->GetLastChannel();
2466 while (posB < endB && HasData() && channel <= lastChannel)
2468 if (channel >= 0)
2470 int block = blockOffset;
2471 float posA2 = posA;
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);
2491 if (bRender)
2493 // reset to grid start position if first item is out of grid view
2494 if (posA2 < posA)
2495 posA2 = posA;
2497 // render our item
2498 if (focused)
2500 focusedPosX = posA2;
2501 focusedPosY = posB;
2502 focusedItem = item;
2504 else
2506 if (m_orientation == VERTICAL)
2507 RenderItem(posA2, posB, item.get(), focused);
2508 else
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
2515 if (posA2 < posA)
2516 posA2 = posA;
2518 // render our item
2519 if (focused)
2521 focusedPosX = posA2;
2522 focusedPosY = posB;
2523 focusedItem = item;
2525 else
2527 AssignItemDepth(item.get(), focused);
2530 else
2532 // calculate the size to truncate if item is out of grid view
2533 float truncateSize = 0;
2534 if (posA2 < posA)
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));
2550 else
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
2565 channel++;
2566 posB += (m_orientation == VERTICAL) ? m_channelHeight : m_channelWidth;
2569 if (bRender)
2571 // and render the focused item last (for overlapping purposes)
2572 if (focusedItem)
2574 if (m_orientation == VERTICAL)
2575 RenderItem(focusedPosX, focusedPosY, focusedItem.get(), true);
2576 else
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)
2601 if (focused)
2603 if (item->GetFocusedLayout())
2604 item->GetFocusedLayout()->AssignDepth();
2606 else
2608 if (item->GetFocusedLayout() && item->GetFocusedLayout()->IsAnimating(ANIM_TYPE_UNFOCUS))
2609 item->GetFocusedLayout()->AssignDepth();
2610 else if (item->GetLayout())
2611 item->GetLayout()->AssignDepth();