Merge pull request #4539 from Matricom/amcodec
[vuplus_xbmc] / xbmc / epg / GUIEPGGridContainer.cpp
1 /*
2  *      Copyright (C) 2012-2013 Team XBMC
3  *      http://xbmc.org
4  *
5  *  This Program is free software; you can redistribute it and/or modify
6  *  it under the terms of the GNU General Public License as published by
7  *  the Free Software Foundation; either version 2, or (at your option)
8  *  any later version.
9  *
10  *  This Program is distributed in the hope that it will be useful,
11  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
12  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13  *  GNU General Public License for more details.
14  *
15  *  You should have received a copy of the GNU General Public License
16  *  along with XBMC; see the file COPYING.  If not, see
17  *  <http://www.gnu.org/licenses/>.
18  *
19  */
20
21 #include "guilib/Key.h"
22 #include "guilib/GUIControlFactory.h"
23 #include "guilib/GUIListItem.h"
24 #include "guilib/GUIFontManager.h"
25 #include "guilib/LocalizeStrings.h"
26 #include "guilib/DirtyRegion.h"
27 #include <tinyxml.h>
28 #include "utils/log.h"
29 #include "utils/MathUtils.h"
30 #include "utils/Variant.h"
31 #include "threads/SystemClock.h"
32 #include "GUIInfoManager.h"
33
34 #include "epg/Epg.h"
35 #include "pvr/channels/PVRChannel.h"
36
37 #include "GUIEPGGridContainer.h"
38
39 using namespace PVR;
40 using namespace EPG;
41 using namespace std;
42
43 #define SHORTGAP     5 // how many blocks is considered a short-gap in nav logic
44 #define MINSPERBLOCK 5 /// would be nice to offer zooming of busy schedules /// performance cost to increase resolution 5 fold?
45 #define BLOCKJUMP    4 // how many blocks are jumped with each analogue scroll action
46 #define BLOCK_SCROLL_OFFSET 60 / MINSPERBLOCK // how many blocks are jumped if we are at left/right edge of grid
47
48 CGUIEPGGridContainer::CGUIEPGGridContainer(int parentID, int controlID, float posX, float posY, float width,
49                                            float height, ORIENTATION orientation, int scrollTime,
50                                            int preloadItems, int timeBlocks, int rulerUnit, const CTextureInfo& progressIndicatorTexture)
51     : IGUIContainer(parentID, controlID, posX, posY, width, height)
52     , m_guiProgressIndicatorTexture(posX, posY, width, height, progressIndicatorTexture)
53 {
54   ControlType             = GUICONTAINER_EPGGRID;
55   m_blocksPerPage         = timeBlocks;
56   m_rulerUnit             = rulerUnit;
57   m_channelCursor         = 0;
58   m_blockCursor           = 0;
59   m_channelOffset         = 0;
60   m_blockOffset           = 0;
61   m_channelScrollOffset   = 0;
62   m_channelScrollSpeed    = 0;
63   m_channelScrollLastTime = 0;
64   m_programmeScrollOffset = 0;
65   m_programmeScrollSpeed  = 0;
66   m_programmeScrollLastTime  = 0;
67   m_scrollTime            = scrollTime ? scrollTime : 1;
68   m_item                  = NULL;
69   m_lastItem              = NULL;
70   m_lastChannel           = NULL;
71   m_orientation           = orientation;
72   m_programmeLayout       = NULL;
73   m_focusedProgrammeLayout= NULL;
74   m_channelLayout         = NULL;
75   m_focusedChannelLayout  = NULL;
76   m_rulerLayout           = NULL;
77   m_rulerPosX             = 0;
78   m_rulerPosY             = 0;
79   m_rulerHeight           = 0;
80   m_rulerWidth            = 0;
81   m_channelPosX           = 0;
82   m_channelPosY           = 0;
83   m_channelHeight         = 0;
84   m_channelWidth          = 0;
85   m_gridPosX              = 0;
86   m_gridPosY              = 0;
87   m_gridWidth             = 0;
88   m_gridHeight            = 0;
89   m_blockSize             = 0;
90   m_analogScrollCount     = 0;
91   m_cacheChannelItems     = preloadItems;
92   m_cacheRulerItems       = preloadItems;
93   m_cacheProgrammeItems   = preloadItems;
94 }
95
96 CGUIEPGGridContainer::~CGUIEPGGridContainer(void)
97 {
98   Reset();
99 }
100
101 void CGUIEPGGridContainer::Process(unsigned int currentTime, CDirtyRegionList &dirtyregions)
102 {
103   ValidateOffset();
104
105   if (m_bInvalidated)
106     UpdateLayout();
107
108   UpdateScrollOffset(currentTime);
109   ProcessChannels(currentTime, dirtyregions);
110   ProcessRuler(currentTime, dirtyregions);
111   ProcessProgrammeGrid(currentTime, dirtyregions);
112   ProcessProgressIndicator(currentTime, dirtyregions);
113
114   CGUIControl::Process(currentTime, dirtyregions);
115 }
116
117 void CGUIEPGGridContainer::Render()
118 {
119   RenderChannels();
120   RenderRuler();
121   RenderProgrammeGrid();
122   RenderProgressIndicator();
123
124   CGUIControl::Render();
125 }
126
127 void CGUIEPGGridContainer::ProcessChannels(unsigned int currentTime, CDirtyRegionList &dirtyregions)
128 {
129   if (!m_focusedChannelLayout || !m_channelLayout)
130     return;
131
132   int chanOffset  = (int)floorf(m_channelScrollOffset / m_programmeLayout->Size(m_orientation));
133
134   int cacheBeforeChannel, cacheAfterChannel;
135   GetChannelCacheOffsets(cacheBeforeChannel, cacheAfterChannel);
136
137   // Free memory not used on screen
138   if ((int)m_channelItems.size() > m_channelsPerPage + cacheBeforeChannel + cacheAfterChannel)
139     FreeChannelMemory(CorrectOffset(chanOffset - cacheBeforeChannel, 0), CorrectOffset(chanOffset + m_channelsPerPage + 1 + cacheAfterChannel, 0));
140
141   CPoint originChannel = CPoint(m_channelPosX, m_channelPosY) + m_renderOffset;
142   float pos = (m_orientation == VERTICAL) ? originChannel.y : originChannel.x;
143   float end = (m_orientation == VERTICAL) ? m_posY + m_height : m_posX + m_width;
144
145   // we offset our draw position to take into account scrolling and whether or not our focused
146   // item is offscreen "above" the list.
147   float drawOffset = (chanOffset - cacheBeforeChannel) * m_channelLayout->Size(m_orientation) - m_channelScrollOffset;
148   if (m_channelOffset + m_channelCursor < chanOffset)
149     drawOffset += m_focusedChannelLayout->Size(m_orientation) - m_channelLayout->Size(m_orientation);
150   pos += drawOffset;
151   end += cacheAfterChannel * m_channelLayout->Size(m_orientation);
152
153   int current = chanOffset;// - cacheBeforeChannel;
154   while (pos < end && !m_channelItems.empty())
155   {
156     int itemNo = CorrectOffset(current, 0);
157     if (itemNo >= (int)m_channelItems.size())
158       break;
159     bool focused = (current == m_channelOffset + m_channelCursor);
160     if (itemNo >= 0)
161     {
162       CGUIListItemPtr item = m_channelItems[itemNo];
163       // process our item
164       if (m_orientation == VERTICAL)
165         ProcessItem(originChannel.x, pos, item.get(), m_lastItem, focused, m_channelLayout, m_focusedChannelLayout, currentTime, dirtyregions);
166       else
167         ProcessItem(pos, originChannel.y, item.get(), m_lastItem, focused, m_channelLayout, m_focusedChannelLayout, currentTime, dirtyregions);
168     }
169     // increment our position
170     pos += focused ? m_focusedChannelLayout->Size(m_orientation) : m_channelLayout->Size(m_orientation);
171     current++;
172   }
173 }
174
175 void CGUIEPGGridContainer::RenderChannels()
176 {
177   if (!m_focusedChannelLayout || !m_channelLayout)
178     return;
179
180   int chanOffset  = (int)floorf(m_channelScrollOffset / m_programmeLayout->Size(m_orientation));
181
182   /// Render channel names
183   int cacheBeforeChannel, cacheAfterChannel;
184   GetChannelCacheOffsets(cacheBeforeChannel, cacheAfterChannel);
185
186   if (m_orientation == VERTICAL)
187     g_graphicsContext.SetClipRegion(m_channelPosX, m_channelPosY, m_channelWidth, m_gridHeight);
188   else
189     g_graphicsContext.SetClipRegion(m_channelPosX, m_channelPosY, m_gridWidth, m_channelHeight);
190
191   CPoint originChannel = CPoint(m_channelPosX, m_channelPosY) + m_renderOffset;
192   float pos = (m_orientation == VERTICAL) ? originChannel.y : originChannel.x;
193   float end = (m_orientation == VERTICAL) ? m_posY + m_height : m_posX + m_width;
194
195   // we offset our draw position to take into account scrolling and whether or not our focused
196   // item is offscreen "above" the list.
197   float drawOffset = (chanOffset - cacheBeforeChannel) * m_channelLayout->Size(m_orientation) - m_channelScrollOffset;
198   if (m_channelOffset + m_channelCursor < chanOffset)
199     drawOffset += m_focusedChannelLayout->Size(m_orientation) - m_channelLayout->Size(m_orientation);
200   pos += drawOffset;
201   end += cacheAfterChannel * m_channelLayout->Size(m_orientation);
202
203   float focusedPos = 0;
204   CGUIListItemPtr focusedItem;
205   int current = chanOffset;// - cacheBeforeChannel;
206   while (pos < end && !m_channelItems.empty())
207   {
208     int itemNo = CorrectOffset(current, 0);
209     if (itemNo >= (int)m_channelItems.size())
210       break;
211     bool focused = (current == m_channelOffset + m_channelCursor);
212     if (itemNo >= 0)
213     {
214       CGUIListItemPtr item = m_channelItems[itemNo];
215       // render our item
216       if (focused)
217       {
218         focusedPos = pos;
219         focusedItem = item;
220       }
221       else
222       {
223         if (m_orientation == VERTICAL)
224           RenderItem(originChannel.x, pos, item.get(), false);
225         else
226           RenderItem(pos, originChannel.y, item.get(), false);
227       }
228     }
229     // increment our position
230     pos += focused ? m_focusedChannelLayout->Size(m_orientation) : m_channelLayout->Size(m_orientation);
231     current++;
232   }
233   // render focused item last so it can overlap other items
234   if (focusedItem)
235   {
236     if (m_orientation == VERTICAL)
237       RenderItem(originChannel.x, focusedPos, focusedItem.get(), true);
238     else
239       RenderItem(focusedPos, originChannel.y, focusedItem.get(), true);
240   }
241   g_graphicsContext.RestoreClipRegion();
242 }
243
244 void CGUIEPGGridContainer::ProcessRuler(unsigned int currentTime, CDirtyRegionList &dirtyregions)
245 {
246   if (!m_rulerLayout || m_rulerItems.size()<=1 || (m_gridEnd - m_gridStart) == CDateTimeSpan(0, 0, 0, 0))
247     return;
248
249   int rulerOffset = (int)floorf(m_programmeScrollOffset / m_blockSize);
250   CGUIListItemPtr item = m_rulerItems[0];
251   item->SetLabel(m_rulerItems[rulerOffset/m_rulerUnit+1]->GetLabel2());
252   CGUIListItem* lastitem = NULL; // dummy pointer needed to be passed as reference to ProcessItem() method
253   ProcessItem(m_posX, m_posY, item.get(), lastitem, false, m_rulerLayout, m_rulerLayout, currentTime, dirtyregions, (m_orientation == VERTICAL ? m_channelWidth : m_channelHeight));
254
255   // render ruler items
256   int cacheBeforeRuler, cacheAfterRuler;
257   GetProgrammeCacheOffsets(cacheBeforeRuler, cacheAfterRuler);
258
259   // Free memory not used on screen
260   if ((int)m_rulerItems.size() > m_blocksPerPage + cacheBeforeRuler + cacheAfterRuler)
261     FreeRulerMemory(CorrectOffset(rulerOffset/m_rulerUnit+1 - cacheBeforeRuler, 0), CorrectOffset(rulerOffset/m_rulerUnit+1 + m_blocksPerPage + 1 + cacheAfterRuler, 0));
262
263   CPoint originRuler = CPoint(m_rulerPosX, m_rulerPosY) + m_renderOffset;
264   float pos = (m_orientation == VERTICAL) ? originRuler.x : originRuler.y;
265   float end = (m_orientation == VERTICAL) ? m_posX + m_width : m_posY + m_height;
266   float drawOffset = (rulerOffset - cacheBeforeRuler) * m_blockSize - m_programmeScrollOffset;
267   pos += drawOffset;
268   end += cacheAfterRuler * m_rulerLayout->Size(m_orientation == VERTICAL ? HORIZONTAL : VERTICAL);
269
270   if (rulerOffset % m_rulerUnit != 0)
271   {
272     /* first ruler marker starts before current view */
273     int startBlock = rulerOffset - 1;
274
275     while (startBlock % m_rulerUnit != 0)
276       startBlock--;
277
278     int missingSection = rulerOffset - startBlock;
279
280     pos -= missingSection * m_blockSize;
281   }
282   while (pos < end && (rulerOffset/m_rulerUnit+1) < (int)m_rulerItems.size())
283   {
284     item = m_rulerItems[rulerOffset/m_rulerUnit+1];
285     if (m_orientation == VERTICAL)
286     {
287       ProcessItem(pos, originRuler.y, item.get(), lastitem, false, m_rulerLayout, m_rulerLayout, currentTime, dirtyregions, m_rulerWidth);
288       pos += m_rulerWidth;
289     }
290     else
291     {
292       ProcessItem(originRuler.x, pos, item.get(), lastitem, false, m_rulerLayout, m_rulerLayout, currentTime, dirtyregions, m_rulerWidth);
293       pos += m_rulerHeight;
294     }
295     rulerOffset += m_rulerUnit;
296   }
297 }
298
299 void CGUIEPGGridContainer::RenderRuler()
300 {
301   if (!m_rulerLayout || m_rulerItems.size()<=1 || (m_gridEnd - m_gridStart) == CDateTimeSpan(0, 0, 0, 0))
302     return;
303
304   int rulerOffset = (int)floorf(m_programmeScrollOffset / m_blockSize);
305
306   /// Render single ruler item with date of selected programme
307   g_graphicsContext.SetClipRegion(m_posX, m_posY, m_width, m_height);
308   CGUIListItemPtr item = m_rulerItems[0];
309   RenderItem(m_posX, m_posY, item.get(), false);
310   g_graphicsContext.RestoreClipRegion();
311
312   // render ruler items
313   int cacheBeforeRuler, cacheAfterRuler;
314   GetProgrammeCacheOffsets(cacheBeforeRuler, cacheAfterRuler);
315
316   if (m_orientation == VERTICAL)
317     g_graphicsContext.SetClipRegion(m_rulerPosX, m_rulerPosY, m_gridWidth, m_rulerHeight);
318   else
319     g_graphicsContext.SetClipRegion(m_rulerPosX, m_rulerPosY, m_rulerWidth, m_gridHeight);
320
321   CPoint originRuler = CPoint(m_rulerPosX, m_rulerPosY) + m_renderOffset;
322   float pos = (m_orientation == VERTICAL) ? originRuler.x : originRuler.y;
323   float end = (m_orientation == VERTICAL) ? m_posX + m_width : m_posY + m_height;
324   float drawOffset = (rulerOffset - cacheBeforeRuler) * m_blockSize - m_programmeScrollOffset;
325   pos += drawOffset;
326   end += cacheAfterRuler * m_rulerLayout->Size(m_orientation == VERTICAL ? HORIZONTAL : VERTICAL);
327
328   if (rulerOffset % m_rulerUnit != 0)
329   {
330     /* first ruler marker starts before current view */
331     int startBlock = rulerOffset - 1;
332
333     while (startBlock % m_rulerUnit != 0)
334       startBlock--;
335
336     int missingSection = rulerOffset - startBlock;
337
338     pos -= missingSection * m_blockSize;
339   }
340   while (pos < end && (rulerOffset/m_rulerUnit+1) < (int)m_rulerItems.size())
341   {
342     item = m_rulerItems[rulerOffset/m_rulerUnit+1];
343     if (m_orientation == VERTICAL)
344     {
345       RenderItem(pos, originRuler.y, item.get(), false);
346       pos += m_rulerWidth;
347     }
348     else
349     {
350       RenderItem(originRuler.x, pos, item.get(), false);
351       pos += m_rulerHeight;
352     }
353     rulerOffset += m_rulerUnit;
354   }
355   g_graphicsContext.RestoreClipRegion();
356 }
357
358 void CGUIEPGGridContainer::ProcessProgrammeGrid(unsigned int currentTime, CDirtyRegionList &dirtyregions)
359 {
360   if (!m_focusedProgrammeLayout || !m_programmeLayout || m_rulerItems.size()<=1 || (m_gridEnd - m_gridStart) == CDateTimeSpan(0, 0, 0, 0))
361     return;
362
363   int blockOffset = (int)floorf(m_programmeScrollOffset / m_blockSize);
364   int chanOffset  = (int)floorf(m_channelScrollOffset / m_programmeLayout->Size(m_orientation));
365
366   int cacheBeforeProgramme, cacheAfterProgramme;
367   GetProgrammeCacheOffsets(cacheBeforeProgramme, cacheAfterProgramme);
368
369   CPoint originProgramme = CPoint(m_gridPosX, m_gridPosY) + m_renderOffset;
370   float posA = (m_orientation != VERTICAL) ? originProgramme.y : originProgramme.x;
371   float endA = (m_orientation != VERTICAL) ? m_posY + m_height : m_posX + m_width;
372   float posB = (m_orientation == VERTICAL) ? originProgramme.y : originProgramme.x;
373   float endB = (m_orientation == VERTICAL) ? m_gridPosY + m_gridHeight : m_posX + m_width;
374   endA += cacheAfterProgramme * m_blockSize;
375
376   float DrawOffsetA = blockOffset * m_blockSize - m_programmeScrollOffset;
377   posA += DrawOffsetA;
378   float DrawOffsetB = (chanOffset - cacheBeforeProgramme) * m_channelLayout->Size(m_orientation) - m_channelScrollOffset;
379   posB += DrawOffsetB;
380
381   int channel = chanOffset;
382
383   while (posB < endB && !m_channelItems.empty())
384   {
385     if (channel >= (int)m_channelItems.size())
386       break;
387
388     // Free memory not used on screen
389     FreeProgrammeMemory(channel, CorrectOffset(blockOffset - cacheBeforeProgramme, 0), CorrectOffset(blockOffset + m_ProgrammesPerPage + 1 + cacheAfterProgramme, 0));
390
391     int block = blockOffset;
392     float posA2 = posA;
393
394     CGUIListItemPtr item = m_gridIndex[channel][block].item;
395     if (blockOffset > 0 && item == m_gridIndex[channel][blockOffset-1].item)
396     {
397       /* first program starts before current view */
398       int startBlock = blockOffset - 1;
399       while (startBlock >= 0 && m_gridIndex[channel][startBlock].item == item)
400         startBlock--;
401
402       block = startBlock + 1;
403       int missingSection = blockOffset - block;
404       posA2 -= missingSection * m_blockSize;
405     }
406
407     while (posA2 < endA && !m_programmeItems.empty())   // FOR EACH ITEM ///////////////
408     {
409       item = m_gridIndex[channel][block].item;
410       if (!item || !item.get()->IsFileItem())
411         break;
412
413       bool focused = (channel == m_channelOffset + m_channelCursor) && (item == m_gridIndex[m_channelOffset + m_channelCursor][m_blockOffset + m_blockCursor].item);
414
415       // calculate the size to truncate if item is out of grid view
416       float truncateSize = 0;
417       if (posA2 < posA)
418       {
419         truncateSize = posA - posA2;
420         posA2 = posA; // reset to grid start position
421       }
422
423       if (m_orientation == VERTICAL)
424       {
425         // truncate item's width
426         m_gridIndex[channel][block].width = m_gridIndex[channel][block].originWidth - truncateSize;
427
428         ProcessItem(posA2, posB, item.get(), m_lastChannel, focused, m_programmeLayout, m_focusedProgrammeLayout, currentTime, dirtyregions, m_gridIndex[channel][block].width);
429
430         // increment our X position
431         posA2 += m_gridIndex[channel][block].width; // assumes focused & unfocused layouts have equal length
432         block += (int)(m_gridIndex[channel][block].originWidth / m_blockSize);
433       }
434       else
435       {
436         // truncate item's height
437         m_gridIndex[channel][block].height = m_gridIndex[channel][block].originHeight - truncateSize;
438
439         ProcessItem(posB, posA2, item.get(), m_lastChannel, focused, m_programmeLayout, m_focusedProgrammeLayout, currentTime, dirtyregions, m_gridIndex[channel][block].height);
440
441         // increment our X position
442         posA2 += m_gridIndex[channel][block].height; // assumes focused & unfocused layouts have equal length
443         block += (int)(m_gridIndex[channel][block].originHeight / m_blockSize);
444       }
445     }
446
447     // increment our Y position
448     channel++;
449     posB += m_orientation == VERTICAL ? m_channelHeight : m_channelWidth;
450   }
451 }
452
453 void CGUIEPGGridContainer::RenderProgrammeGrid()
454 {
455   if (!m_focusedProgrammeLayout || !m_programmeLayout || m_rulerItems.size()<=1 || (m_gridEnd - m_gridStart) == CDateTimeSpan(0, 0, 0, 0))
456     return;
457
458   int blockOffset = (int)floorf(m_programmeScrollOffset / m_blockSize);
459   int chanOffset  = (int)floorf(m_channelScrollOffset / m_programmeLayout->Size(m_orientation));
460
461   /// Render programmes
462   int cacheBeforeProgramme, cacheAfterProgramme;
463   GetProgrammeCacheOffsets(cacheBeforeProgramme, cacheAfterProgramme);
464
465   g_graphicsContext.SetClipRegion(m_gridPosX, m_gridPosY, m_gridWidth, m_gridHeight);
466   CPoint originProgramme = CPoint(m_gridPosX, m_gridPosY) + m_renderOffset;
467   float posA = (m_orientation != VERTICAL) ? originProgramme.y : originProgramme.x;
468   float endA = (m_orientation != VERTICAL) ? m_posY + m_height : m_posX + m_width;
469   float posB = (m_orientation == VERTICAL) ? originProgramme.y : originProgramme.x;
470   float endB = (m_orientation == VERTICAL) ? m_gridPosY + m_gridHeight : m_posX + m_width;
471   endA += cacheAfterProgramme * m_blockSize;
472
473   float DrawOffsetA = blockOffset * m_blockSize - m_programmeScrollOffset;
474   posA += DrawOffsetA;
475   float DrawOffsetB = (chanOffset - cacheBeforeProgramme) * m_channelLayout->Size(m_orientation) - m_channelScrollOffset;
476   posB += DrawOffsetB;
477
478   int channel = chanOffset;
479
480   float focusedPosX = 0;
481   float focusedPosY = 0;
482   CGUIListItemPtr focusedItem;
483   while (posB < endB && !m_channelItems.empty())
484   {
485     if (channel >= (int)m_channelItems.size())
486       break;
487
488     int block = blockOffset;
489     float posA2 = posA;
490
491     CGUIListItemPtr item = m_gridIndex[channel][block].item;
492     if (blockOffset > 0 && item == m_gridIndex[channel][blockOffset-1].item)
493     {
494       /* first program starts before current view */
495       int startBlock = blockOffset - 1;
496       while (startBlock >= 0 && m_gridIndex[channel][startBlock].item == item)
497         startBlock--;
498
499       block = startBlock + 1;
500       int missingSection = blockOffset - block;
501       posA2 -= missingSection * m_blockSize;
502     }
503
504     while (posA2 < endA && !m_programmeItems.empty())   // FOR EACH ITEM ///////////////
505     {
506       item = m_gridIndex[channel][block].item;
507       if (!item || !item.get()->IsFileItem())
508         break;
509
510       bool focused = (channel == m_channelOffset + m_channelCursor) && (item == m_gridIndex[m_channelOffset + m_channelCursor][m_blockOffset + m_blockCursor].item);
511
512       // reset to grid start position if first item is out of grid view
513       if (posA2 < posA)
514         posA2 = posA;
515
516       // render our item
517       if (focused)
518       {
519         if (m_orientation == VERTICAL)
520         {
521           focusedPosX = posA2;
522           focusedPosY = posB;
523         }
524         else
525         {
526           focusedPosX = posB;
527           focusedPosY = posA2;
528         }
529         focusedItem = item;
530       }
531       else
532       {
533         if (m_orientation == VERTICAL)
534           RenderItem(posA2, posB, item.get(), focused);
535         else
536           RenderItem(posB, posA2, item.get(), focused);
537       }
538
539       // increment our X position
540       if (m_orientation == VERTICAL)
541       {
542         posA2 += m_gridIndex[channel][block].width; // assumes focused & unfocused layouts have equal length
543         block += (int)(m_gridIndex[channel][block].originWidth / m_blockSize);
544       }
545       else
546       {
547         posA2 += m_gridIndex[channel][block].height; // assumes focused & unfocused layouts have equal length
548         block += (int)(m_gridIndex[channel][block].originHeight / m_blockSize);
549       }
550     }
551
552     // increment our Y position
553     channel++;
554     posB += m_orientation == VERTICAL ? m_channelHeight : m_channelWidth;
555   }
556
557   // and render the focused item last (for overlapping purposes)
558   if (focusedItem)
559     RenderItem(focusedPosX, focusedPosY, focusedItem.get(), true);
560
561   g_graphicsContext.RestoreClipRegion();
562 }
563
564 void CGUIEPGGridContainer::ProcessProgressIndicator(unsigned int currentTime, CDirtyRegionList &dirtyregions)
565 {
566   CPoint originRuler = CPoint(m_rulerPosX, m_rulerPosY) + m_renderOffset;
567   float width = ((CDateTime::GetUTCDateTime() - m_gridStart).GetSecondsTotal() * m_blockSize) / (MINSPERBLOCK * 60) - m_programmeScrollOffset;
568
569   if (width > 0)
570   {
571     m_guiProgressIndicatorTexture.SetVisible(true);
572     m_guiProgressIndicatorTexture.SetPosition(originRuler.x, originRuler.y);
573     if (m_orientation == VERTICAL)
574       m_guiProgressIndicatorTexture.SetWidth(width);
575     else
576       m_guiProgressIndicatorTexture.SetHeight(width);
577   }
578   else
579   {
580     m_guiProgressIndicatorTexture.SetVisible(false);
581   }
582   
583   m_guiProgressIndicatorTexture.Process(currentTime);
584 }
585
586 void CGUIEPGGridContainer::RenderProgressIndicator()
587 {
588   bool render = false;
589   
590   if (m_orientation == VERTICAL)
591     render = g_graphicsContext.SetClipRegion(m_rulerPosX, m_rulerPosY, m_gridWidth, m_height);
592   else
593     render = g_graphicsContext.SetClipRegion(m_rulerPosX, m_rulerPosY, m_width, m_gridHeight);
594   
595   if(render)
596   {
597     m_guiProgressIndicatorTexture.Render();
598     g_graphicsContext.RestoreClipRegion();
599   }
600 }
601
602 void CGUIEPGGridContainer::ProcessItem(float posX, float posY, CGUIListItem* item, CGUIListItem *&lastitem,
603   bool focused, CGUIListItemLayout* normallayout, CGUIListItemLayout* focusedlayout,
604   unsigned int currentTime, CDirtyRegionList &dirtyregions, float resize /* = -1.0f */)
605 {
606   if (!normallayout || !focusedlayout) return;
607
608   // set the origin
609   g_graphicsContext.SetOrigin(posX, posY);
610
611   if (m_bInvalidated)
612     item->SetInvalid();
613   if (focused)
614   {
615     if (!item->GetFocusedLayout())
616     {
617       CGUIListItemLayout *layout = new CGUIListItemLayout(*focusedlayout);
618       item->SetFocusedLayout(layout);
619     }
620
621     if (resize != -1.0f)
622     {
623       if (m_orientation == VERTICAL)
624         item->GetFocusedLayout()->SetWidth(resize);
625       else
626         item->GetFocusedLayout()->SetHeight(resize);
627     }
628
629     if (item != lastitem || !HasFocus())
630       item->GetFocusedLayout()->SetFocusedItem(0);
631
632     if (item != lastitem && HasFocus())
633     {
634       item->GetFocusedLayout()->ResetAnimation(ANIM_TYPE_UNFOCUS);
635       unsigned int subItem = 1;
636       if (lastitem && lastitem->GetFocusedLayout())
637         subItem = lastitem->GetFocusedLayout()->GetFocusedItem();
638       item->GetFocusedLayout()->SetFocusedItem(subItem ? subItem : 1);
639     }
640
641     item->GetFocusedLayout()->Process(item, m_parentID, currentTime, dirtyregions);
642     lastitem = item;
643   }
644   else
645   {
646     if (!item->GetLayout())
647     {
648       CGUIListItemLayout *layout = new CGUIListItemLayout(*normallayout);
649       item->SetLayout(layout);
650     }
651
652     if (resize != -1.0f)
653     {
654       if (m_orientation == VERTICAL)
655         item->GetLayout()->SetWidth(resize);
656       else
657         item->GetLayout()->SetHeight(resize);
658     }
659
660     if (item->GetFocusedLayout())
661       item->GetFocusedLayout()->SetFocusedItem(0);
662
663     if (item->GetFocusedLayout() && item->GetFocusedLayout()->IsAnimating(ANIM_TYPE_UNFOCUS))
664       item->GetFocusedLayout()->Process(item, m_parentID, currentTime, dirtyregions);
665     else
666       item->GetLayout()->Process(item, m_parentID, currentTime, dirtyregions);
667   }
668   g_graphicsContext.RestoreOrigin();
669 }
670
671 void CGUIEPGGridContainer::RenderItem(float posX, float posY, CGUIListItem *item, bool focused)
672 {
673   // set the origin
674   g_graphicsContext.SetOrigin(posX, posY);
675
676   if (focused)
677   {
678     if (item->GetFocusedLayout())
679       item->GetFocusedLayout()->Render(item, m_parentID);
680   }
681   else
682   {
683     if (item->GetFocusedLayout() && item->GetFocusedLayout()->IsAnimating(ANIM_TYPE_UNFOCUS))
684       item->GetFocusedLayout()->Render(item, m_parentID);
685     else if (item->GetLayout())
686       item->GetLayout()->Render(item, m_parentID);
687   }
688   g_graphicsContext.RestoreOrigin();
689 }
690
691 bool CGUIEPGGridContainer::OnAction(const CAction &action)
692 {
693   switch (action.GetID())
694   {
695   case ACTION_MOVE_LEFT:
696   case ACTION_MOVE_RIGHT:
697   case ACTION_MOVE_DOWN:
698   case ACTION_MOVE_UP:
699   case ACTION_NAV_BACK:
700     { // use base class implementation
701
702       return CGUIControl::OnAction(action);
703     }
704
705     break;
706   case ACTION_PAGE_UP:
707     {
708       if (m_orientation == VERTICAL)
709       {
710         if (m_channelOffset == 0)
711         { // already on the first page, so move to the first item
712           SetChannel(0);
713         }
714         else
715         { // scroll up to the previous page
716           ChannelScroll(-m_channelsPerPage);
717         }
718       }
719       else
720         ProgrammesScroll(-m_blocksPerPage/4);
721
722       return true;
723     }
724
725     break;
726   case ACTION_PAGE_DOWN:
727     {
728       if (m_orientation == VERTICAL)
729       {
730         if (m_channelOffset == m_channels - m_channelsPerPage || m_channels < m_channelsPerPage)
731         { // already at the last page, so move to the last item.
732           SetChannel(m_channels - m_channelOffset - 1);
733         }
734         else
735         { // scroll down to the next page
736           ChannelScroll(m_channelsPerPage);
737         }
738       }
739       else
740         ProgrammesScroll(m_blocksPerPage/4);
741
742       return true;
743     }
744
745     break;
746
747     // smooth scrolling (for analog controls)
748   case ACTION_TELETEXT_RED:
749   case ACTION_TELETEXT_GREEN:
750   case ACTION_SCROLL_UP: // left horizontal scrolling
751     {
752       int blocksToJump = action.GetID() == ACTION_TELETEXT_RED ? m_blocksPerPage/2 : m_blocksPerPage/4;
753
754       m_analogScrollCount += action.GetAmount() * action.GetAmount();
755       bool handled = false;
756
757       while (m_analogScrollCount > 0.4)
758       {
759         handled = true;
760         m_analogScrollCount -= 0.4f;
761
762         if (m_blockOffset > 0 && m_blockCursor <= m_blocksPerPage / 2)
763         {
764           ProgrammesScroll(-blocksToJump);
765         }
766         else if (m_blockCursor > blocksToJump)
767         {
768           SetBlock(m_blockCursor - blocksToJump);
769         }
770       }
771
772       return handled;
773     }
774
775     break;
776
777   case ACTION_TELETEXT_BLUE:
778   case ACTION_TELETEXT_YELLOW:
779   case ACTION_SCROLL_DOWN: // right horizontal scrolling
780     {
781       int blocksToJump = action.GetID() == ACTION_TELETEXT_BLUE ? m_blocksPerPage/2 : m_blocksPerPage/4;
782
783       m_analogScrollCount += action.GetAmount() * action.GetAmount();
784       bool handled = false;
785
786       while (m_analogScrollCount > 0.4)
787       {
788         handled = true;
789         m_analogScrollCount -= 0.4f;
790
791         if (m_blockOffset + m_blocksPerPage < m_blocks && m_blockCursor >= m_blocksPerPage / 2)
792         {
793           ProgrammesScroll(blocksToJump);
794         }
795         else if (m_blockCursor < m_blocksPerPage - blocksToJump && m_blockOffset + m_blockCursor < m_blocks - blocksToJump)
796         {
797           SetBlock(m_blockCursor + blocksToJump);
798         }
799       }
800
801       return handled;
802     }
803
804     break;
805
806   default:
807     if (action.GetID())
808       return OnClick(action.GetID());
809     break;
810   }
811
812   return false;
813 }
814
815 bool CGUIEPGGridContainer::OnMessage(CGUIMessage& message)
816 {
817   if (message.GetControlId() == GetID())
818   {
819     if (message.GetMessage() == GUI_MSG_ITEM_SELECTED)
820     {
821       message.SetParam1(GetSelectedItem());
822       return true;
823     }
824     else if (message.GetMessage() == GUI_MSG_LABEL_BIND && message.GetPointer())
825     {
826       Reset();
827       CFileItemList *items = (CFileItemList *)message.GetPointer();
828
829       /* Create programme items */
830       m_programmeItems.reserve(items->Size());
831       for (int i = 0; i < items->Size(); i++)
832       {
833         CFileItemPtr fileItem = items->Get(i);
834         if (fileItem->HasEPGInfoTag() && fileItem->GetEPGInfoTag()->HasPVRChannel())
835           m_programmeItems.push_back(fileItem);
836       }
837
838       /* Create Channel items */
839       int iLastChannelNumber = -1;
840       ItemsPtr itemsPointer;
841       itemsPointer.start = 0;
842       for (unsigned int i = 0; i < m_programmeItems.size(); ++i)
843       {
844         const CEpgInfoTag* tag = ((CFileItem*)m_programmeItems[i].get())->GetEPGInfoTag();
845         int iCurrentChannelNumber = tag->PVRChannelNumber();
846         if (iCurrentChannelNumber != iLastChannelNumber)
847         {
848           CPVRChannelPtr channel = tag->ChannelTag();
849           if (!channel)
850             continue;
851
852           if (i > 0)
853           {
854             itemsPointer.stop = i-1;
855             m_epgItemsPtr.push_back(itemsPointer);
856             itemsPointer.start = i;
857           }
858           iLastChannelNumber = iCurrentChannelNumber;
859           CGUIListItemPtr item(new CFileItem(*channel));
860           m_channelItems.push_back(item);
861         }
862       }
863       if (!m_programmeItems.empty())
864       {
865         itemsPointer.stop = m_programmeItems.size()-1;
866         m_epgItemsPtr.push_back(itemsPointer);
867       }
868
869       ClearGridIndex();
870       m_gridIndex.reserve(m_channelItems.size());
871       for (unsigned int i = 0; i < m_channelItems.size(); i++)
872       {
873         std::vector<GridItemsPtr> blocks(MAXBLOCKS);
874         m_gridIndex.push_back(blocks);
875       }
876
877       UpdateLayout(true); // true to refresh all items
878
879       /* Create Ruler items */
880       CDateTime ruler; ruler.SetFromUTCDateTime(m_gridStart);
881       CDateTime rulerEnd; rulerEnd.SetFromUTCDateTime(m_gridEnd);
882       CDateTimeSpan unit(0, 0, m_rulerUnit * MINSPERBLOCK, 0);
883       CGUIListItemPtr rulerItem(new CFileItem(ruler.GetAsLocalizedDate(true, true)));
884       rulerItem->SetProperty("DateLabel", true);
885       m_rulerItems.push_back(rulerItem);
886
887       for (; ruler < rulerEnd; ruler += unit)
888       {
889         CGUIListItemPtr rulerItem(new CFileItem(ruler.GetAsLocalizedTime("", false)));
890         rulerItem->SetLabel2(ruler.GetAsLocalizedDate(true, true));
891         m_rulerItems.push_back(rulerItem);
892       }
893
894       UpdateItems();
895       //SelectItem(message.GetParam1());
896       return true;
897     }
898     else if (message.GetMessage() == GUI_MSG_REFRESH_LIST)
899     { // update our list contents
900       for (unsigned int i = 0; i < m_channelItems.size(); ++i)
901         m_channelItems[i]->SetInvalid();
902       for (unsigned int i = 0; i < m_programmeItems.size(); ++i)
903         m_programmeItems[i]->SetInvalid();
904       for (unsigned int i = 0; i < m_rulerItems.size(); ++i)
905         m_rulerItems[i]->SetInvalid();
906     }
907   }
908
909   return CGUIControl::OnMessage(message);
910 }
911
912 void CGUIEPGGridContainer::UpdateItems()
913 {
914   CDateTimeSpan blockDuration, gridDuration;
915
916   /* check for invalid start and end time */
917   if (m_gridStart >= m_gridEnd)
918   {
919     CLog::Log(LOGERROR, "CGUIEPGGridContainer - %s - invalid start and end time set", __FUNCTION__);
920     CGUIMessage msg(GUI_MSG_LABEL_RESET, GetID(), GetParentID()); // message the window
921     SendWindowMessage(msg);
922     return;
923   }
924
925   gridDuration = m_gridEnd - m_gridStart;
926
927   m_blocks = (gridDuration.GetDays()*24*60 + gridDuration.GetHours()*60 + gridDuration.GetMinutes()) / MINSPERBLOCK;
928   if (m_blocks >= MAXBLOCKS)
929     m_blocks = MAXBLOCKS;
930
931   /* if less than one page, can't display grid */
932   if (m_blocks < m_blocksPerPage)
933   {
934     CLog::Log(LOGERROR, "(%s) - Less than one page of data available.", __FUNCTION__);
935     CGUIMessage msg(GUI_MSG_LABEL_RESET, GetID(), GetParentID()); // message the window
936     SendWindowMessage(msg);
937     return;
938   }
939
940   blockDuration.SetDateTimeSpan(0, 0, MINSPERBLOCK, 0);
941
942   long tick(XbmcThreads::SystemClockMillis());
943
944   for (unsigned int row = 0; row < m_epgItemsPtr.size(); ++row)
945   {
946     CDateTime gridCursor  = m_gridStart; //reset cursor for new channel
947     unsigned long progIdx = m_epgItemsPtr[row].start;
948     unsigned long lastIdx = m_epgItemsPtr[row].stop;
949     int iEpgId            = ((CFileItem *)m_programmeItems[progIdx].get())->GetEPGInfoTag()->EpgID();
950
951     /** FOR EACH BLOCK **********************************************************************/
952
953     for (int block = 0; block < m_blocks; block++)
954     {
955       while (progIdx <= lastIdx)
956       {
957         CGUIListItemPtr item = m_programmeItems[progIdx];
958         const CEpgInfoTag* tag = ((CFileItem *)item.get())->GetEPGInfoTag();
959         if (tag == NULL)
960         {
961           progIdx++;
962           continue;
963         }
964
965         if (tag->EpgID() != iEpgId || gridCursor < tag->StartAsUTC() || m_gridEnd <= tag->StartAsUTC())
966           break;
967
968         if (gridCursor < tag->EndAsUTC())
969         {
970           m_gridIndex[row][block].item = item;
971           break;
972         }
973         
974         progIdx++;
975       }
976
977       gridCursor += blockDuration;
978     }
979
980     /** FOR EACH BLOCK **********************************************************************/
981     int itemSize = 1; // size of the programme in blocks
982     int savedBlock = 0;
983
984     for (int block = 0; block < m_blocks; block++)
985     {
986       CGUIListItemPtr item = m_gridIndex[row][block].item;
987
988       if (item != m_gridIndex[row][block+1].item)
989       {
990         if (!item)
991         {
992           CEpgInfoTag gapTag;
993           CFileItemPtr gapItem(new CFileItem(gapTag));
994           for (int i = block ; i > block - itemSize; i--)
995           {
996             m_gridIndex[row][i].item = gapItem;
997           }
998         }
999         else
1000         {
1001           const CEpgInfoTag* tag = ((CFileItem *)item.get())->GetEPGInfoTag();
1002           m_gridIndex[row][savedBlock].item->SetProperty("GenreType", tag->GenreType());
1003         }
1004
1005         if (m_orientation == VERTICAL)
1006         {
1007           m_gridIndex[row][savedBlock].originWidth = itemSize*m_blockSize;
1008           m_gridIndex[row][savedBlock].originHeight = m_channelHeight;
1009         }
1010         else
1011         {
1012           m_gridIndex[row][savedBlock].originWidth = m_channelWidth;
1013           m_gridIndex[row][savedBlock].originHeight = itemSize*m_blockSize;
1014         }
1015
1016         m_gridIndex[row][savedBlock].width = m_gridIndex[row][savedBlock].originWidth;
1017         m_gridIndex[row][savedBlock].height = m_gridIndex[row][savedBlock].originHeight;
1018
1019         itemSize = 1;
1020         savedBlock = block+1;
1021       }
1022       else
1023       {
1024         itemSize++;
1025       }
1026     }
1027   }
1028
1029   /******************************************* END ******************************************/
1030
1031   CLog::Log(LOGDEBUG, "%s completed successfully in %u ms", __FUNCTION__, (unsigned int)(XbmcThreads::SystemClockMillis()-tick));
1032
1033   m_channels = (int)m_epgItemsPtr.size();
1034   m_item = GetItem(m_channelCursor);
1035   if (m_item)
1036     SetBlock(GetBlock(m_item->item, m_channelCursor));
1037
1038   SetInvalid();
1039   GoToNow();
1040 }
1041
1042 void CGUIEPGGridContainer::ChannelScroll(int amount)
1043 {
1044   // increase or decrease the vertical offset
1045   int offset = m_channelOffset + amount;
1046
1047   if (offset > m_channels - m_channelsPerPage)
1048   {
1049     offset = m_channels - m_channelsPerPage;
1050   }
1051
1052   if (offset < 0) offset = 0;
1053
1054   ScrollToChannelOffset(offset);
1055 }
1056
1057 void CGUIEPGGridContainer::ProgrammesScroll(int amount)
1058 {
1059   // increase or decrease the horizontal offset
1060   ScrollToBlockOffset(m_blockOffset + amount);
1061 }
1062
1063 bool CGUIEPGGridContainer::MoveChannel(bool direction, bool wrapAround)
1064 {
1065   if (direction)
1066   {
1067     if (m_channelCursor > 0)
1068     {
1069       SetChannel(m_channelCursor - 1);
1070     }
1071     else if (m_channelCursor == 0 && m_channelOffset)
1072     {
1073       ScrollToChannelOffset(m_channelOffset - 1);
1074       SetChannel(0);
1075     }
1076     else if (wrapAround)
1077     {
1078       int offset = m_channels - m_channelsPerPage;
1079
1080       if (offset < 0) offset = 0;
1081
1082       SetChannel(m_channels - offset - 1);
1083
1084       ScrollToChannelOffset(offset);
1085     }
1086     else
1087       return false;
1088   }
1089   else
1090   {
1091     if (m_channelOffset + m_channelCursor + 1 < m_channels)
1092     {
1093       if (m_channelCursor + 1 < m_channelsPerPage)
1094       {
1095         SetChannel(m_channelCursor + 1);
1096       }
1097       else
1098       {
1099         ScrollToChannelOffset(m_channelOffset + 1);
1100         SetChannel(m_channelsPerPage - 1);
1101       }
1102     }
1103     else if (wrapAround)
1104     {
1105       SetChannel(0);
1106       ScrollToChannelOffset(0);
1107     }
1108     else
1109       return false;
1110   }
1111   return true;
1112 }
1113
1114 bool CGUIEPGGridContainer::MoveProgrammes(bool direction)
1115 {
1116   if (m_gridIndex.empty() || !m_item)
1117     return false;
1118
1119   if (direction)
1120   {
1121     if (m_channelCursor + m_channelOffset < 0 || m_blockOffset < 0)
1122       return false;
1123
1124     if (m_item->item != m_gridIndex[m_channelCursor + m_channelOffset][m_blockOffset].item)
1125     {
1126       // this is not first item on page
1127       m_item = GetPrevItem(m_channelCursor);
1128       SetBlock(GetBlock(m_item->item, m_channelCursor));
1129     }
1130     else if (m_blockCursor <= 0 && m_blockOffset)
1131     {
1132       if (m_blockOffset - BLOCK_SCROLL_OFFSET < 0)
1133         return false;
1134
1135       // this is the first item on page
1136       ScrollToBlockOffset(m_blockOffset - BLOCK_SCROLL_OFFSET);
1137       SetBlock(GetBlock(m_item->item, m_channelCursor));
1138     }
1139     else
1140       return false;
1141   }
1142   else
1143   {
1144     if (m_item->item != m_gridIndex[m_channelCursor + m_channelOffset][m_blocksPerPage + m_blockOffset - 1].item)
1145     {
1146       // this is not last item on page
1147       m_item = GetNextItem(m_channelCursor);
1148       SetBlock(GetBlock(m_item->item, m_channelCursor));
1149     }
1150     else if ((m_blockOffset != m_blocks - m_blocksPerPage) && m_blocks > m_blocksPerPage)
1151     {
1152       if (m_blockOffset + BLOCK_SCROLL_OFFSET > m_blocks)
1153         return false;
1154       
1155       // this is the last item on page
1156       ScrollToBlockOffset(m_blockOffset + BLOCK_SCROLL_OFFSET);
1157       SetBlock(GetBlock(m_item->item, m_channelCursor));
1158     }
1159     else
1160       return false;
1161   }
1162   return true;
1163 }
1164
1165 void CGUIEPGGridContainer::OnUp()
1166 {
1167   bool wrapAround = m_actionUp.GetNavigation() == GetID() || !m_actionUp.HasActionsMeetingCondition();
1168   if (m_orientation == VERTICAL)
1169   {
1170     if (!MoveChannel(true, wrapAround))
1171       CGUIControl::OnUp();
1172   }
1173   else
1174   {
1175     if (!MoveProgrammes(true))
1176       CGUIControl::OnUp();
1177   }
1178 }
1179
1180 void CGUIEPGGridContainer::OnDown()
1181 {
1182   bool wrapAround = m_actionDown.GetNavigation() == GetID() || !m_actionDown.HasActionsMeetingCondition();
1183   if (m_orientation == VERTICAL)
1184   {
1185     if (!MoveChannel(false, wrapAround))
1186       CGUIControl::OnDown();
1187   }
1188   else
1189   {
1190     if (!MoveProgrammes(false))
1191       CGUIControl::OnDown();
1192   }
1193 }
1194
1195 void CGUIEPGGridContainer::OnLeft()
1196 {
1197   bool wrapAround = m_actionLeft.GetNavigation() == GetID() || !m_actionLeft.HasActionsMeetingCondition();
1198   if (m_orientation == VERTICAL)
1199   {
1200     if (!MoveProgrammes(true))
1201       CGUIControl::OnLeft();
1202   }
1203   else
1204   {
1205     if (!MoveChannel(true, wrapAround))
1206       CGUIControl::OnLeft();
1207   }
1208 }
1209
1210 void CGUIEPGGridContainer::OnRight()
1211 {
1212   bool wrapAround = m_actionRight.GetNavigation() == GetID() || !m_actionRight.HasActionsMeetingCondition();
1213   if (m_orientation == VERTICAL)
1214   {
1215     if (!MoveProgrammes(false))
1216       CGUIControl::OnRight();
1217   }
1218   else
1219   {
1220     if (!MoveChannel(false, wrapAround))
1221       CGUIControl::OnRight();
1222   }
1223 }
1224
1225 void CGUIEPGGridContainer::SetChannel(const CStdString &channel)
1226 {
1227   int iChannelIndex(-1);
1228   for (unsigned int iIndex = 0; iIndex < m_channelItems.size(); iIndex++)
1229   {
1230     CStdString strPath = m_channelItems[iIndex]->GetProperty("path").asString(StringUtils::EmptyString);
1231     if (strPath == channel)
1232     {
1233       iChannelIndex = iIndex;
1234       break;
1235     }
1236   }
1237
1238   if (iChannelIndex >= 0)
1239     ScrollToChannelOffset(iChannelIndex);
1240 }
1241
1242 void CGUIEPGGridContainer::SetChannel(const CPVRChannel &channel)
1243 {
1244   int iChannelIndex(-1);
1245   for (unsigned int iIndex = 0; iIndex < m_channelItems.size(); iIndex++)
1246   {
1247     int iChannelId = (int)m_channelItems[iIndex]->GetProperty("channelid").asInteger(-1);
1248     if (iChannelId == channel.ChannelID())
1249     {
1250       iChannelIndex = iIndex;
1251       break;
1252     }
1253   }
1254
1255   if (iChannelIndex >= 0)
1256     ScrollToChannelOffset(iChannelIndex);
1257 }
1258
1259 void CGUIEPGGridContainer::SetChannel(int channel)
1260 {
1261   if (m_blockCursor + m_blockOffset == 0 || m_blockOffset + m_blockCursor + GetItemSize(m_item) == m_blocks)
1262   {
1263     m_item          = GetItem(channel);
1264     if (m_item)
1265     {
1266       m_channelCursor = channel;
1267       SetBlock(GetBlock(m_item->item, channel));
1268     }
1269     return;
1270   }
1271
1272   /* basic checks failed, need to correctly identify nearest item */
1273   m_item          = GetClosestItem(channel);
1274   if (m_item)
1275   {
1276     m_channelCursor = channel;
1277     SetBlock(GetBlock(m_item->item, m_channelCursor));
1278   }
1279 }
1280
1281 void CGUIEPGGridContainer::SetBlock(int block)
1282 {
1283   if (block < 0)
1284     m_blockCursor = 0;
1285   else if (block > m_blocksPerPage - 1)
1286     m_blockCursor = m_blocksPerPage - 1;
1287   else
1288     m_blockCursor = block;
1289   m_item        = GetItem(m_channelCursor);
1290 }
1291
1292 CGUIListItemLayout *CGUIEPGGridContainer::GetFocusedLayout() const
1293 {
1294   CGUIListItemPtr item = GetListItem(0);
1295
1296   if (item.get()) return item->GetFocusedLayout();
1297
1298   return NULL;
1299 }
1300
1301 bool CGUIEPGGridContainer::SelectItemFromPoint(const CPoint &point, bool justGrid /* = false */)
1302 {
1303   /* point has already had origin set to m_posX, m_posY */
1304   if (!m_focusedProgrammeLayout || !m_programmeLayout || (justGrid && point.x < 0))
1305     return false;
1306
1307   int channel = (int)(point.y / m_channelHeight);
1308   int block   = (int)(point.x / m_blockSize);
1309
1310   if (channel > m_channelsPerPage) channel = m_channelsPerPage - 1;
1311   if (channel >= m_channels) channel = m_channels - 1;
1312   if (channel < 0) channel = 0;
1313   if (block > m_blocksPerPage) block = m_blocksPerPage - 1;
1314   if (block < 0) block = 0;
1315
1316   int channelIndex = channel + m_channelOffset;
1317   int blockIndex = block + m_blockOffset;
1318
1319   // bail if out of range
1320   if (channelIndex >= m_channels || blockIndex >= m_blocks)
1321     return false;
1322   // bail if block isn't occupied
1323   if (!m_gridIndex[channelIndex][blockIndex].item)
1324     return false;
1325
1326   SetChannel(channel);
1327   SetBlock(block);
1328   return true;
1329 }
1330
1331 EVENT_RESULT CGUIEPGGridContainer::OnMouseEvent(const CPoint &point, const CMouseEvent &event)
1332 {
1333   switch (event.m_id)
1334   {
1335   case ACTION_MOUSE_LEFT_CLICK:
1336     OnMouseClick(0, point);
1337     return EVENT_RESULT_HANDLED;
1338   case ACTION_MOUSE_RIGHT_CLICK:
1339     OnMouseClick(1, point);
1340     return EVENT_RESULT_HANDLED;
1341   case ACTION_MOUSE_DOUBLE_CLICK:
1342     OnMouseDoubleClick(0, point);
1343     return EVENT_RESULT_HANDLED;
1344   case ACTION_MOUSE_WHEEL_UP:
1345     OnMouseWheel(-1, point);
1346     return EVENT_RESULT_HANDLED;
1347   case ACTION_MOUSE_WHEEL_DOWN:
1348     OnMouseWheel(1, point);
1349     return EVENT_RESULT_HANDLED;
1350   case ACTION_GESTURE_BEGIN:
1351     {
1352       // we want exclusive access
1353       CGUIMessage msg(GUI_MSG_EXCLUSIVE_MOUSE, GetID(), GetParentID());
1354       SendWindowMessage(msg);
1355       return EVENT_RESULT_HANDLED;
1356     }
1357   case ACTION_GESTURE_END:
1358     {
1359       // we're done with exclusive access
1360       CGUIMessage msg(GUI_MSG_EXCLUSIVE_MOUSE, 0, GetParentID());
1361       SendWindowMessage(msg);
1362       ScrollToChannelOffset(MathUtils::round_int(m_channelScrollOffset / m_channelLayout->Size(m_orientation)));
1363       ScrollToBlockOffset(MathUtils::round_int(m_programmeScrollOffset / m_blockSize));
1364       return EVENT_RESULT_HANDLED;
1365     }
1366   case ACTION_GESTURE_PAN:
1367     {
1368       if (m_orientation == VERTICAL)
1369       {
1370         m_programmeScrollOffset -= event.m_offsetX;
1371         m_channelScrollOffset -= event.m_offsetY;
1372       }
1373       else
1374       {
1375         m_channelScrollOffset -= event.m_offsetX;
1376         m_programmeScrollOffset -= event.m_offsetY;
1377       }
1378
1379       m_channelOffset = MathUtils::round_int(m_channelScrollOffset / m_channelLayout->Size(m_orientation));
1380       m_blockOffset = MathUtils::round_int(m_programmeScrollOffset / m_blockSize);
1381       ValidateOffset();
1382       return EVENT_RESULT_HANDLED;
1383     }
1384   default:
1385     return EVENT_RESULT_UNHANDLED;
1386   }
1387 }
1388
1389 bool CGUIEPGGridContainer::OnMouseOver(const CPoint &point)
1390 {
1391   // select the item under the pointer
1392   SelectItemFromPoint(point - CPoint(m_gridPosX, m_posY + m_rulerHeight), false);
1393   return CGUIControl::OnMouseOver(point);
1394 }
1395
1396 bool CGUIEPGGridContainer::OnMouseClick(int dwButton, const CPoint &point)
1397 {
1398   if (SelectItemFromPoint(point - CPoint(m_gridPosX, m_posY + m_rulerHeight)))
1399   { // send click message to window
1400     OnClick(ACTION_MOUSE_LEFT_CLICK + dwButton);
1401     return true;
1402   }
1403
1404   return false;
1405 }
1406
1407 bool CGUIEPGGridContainer::OnMouseDoubleClick(int dwButton, const CPoint &point)
1408 {
1409   if (SelectItemFromPoint(point - CPoint(m_gridPosX, m_posY + m_rulerHeight)))
1410   { // send double click message to window
1411     OnClick(ACTION_MOUSE_DOUBLE_CLICK + dwButton);
1412     return true;
1413   }
1414
1415   return false;
1416 }
1417
1418 bool CGUIEPGGridContainer::OnClick(int actionID)
1419 {
1420   int subItem = 0;
1421
1422   if (actionID == ACTION_SELECT_ITEM || actionID == ACTION_MOUSE_LEFT_CLICK)
1423   {
1424     // grab the currently focused subitem (if applicable)
1425     CGUIListItemLayout *focusedLayout = GetFocusedLayout();
1426
1427     if (focusedLayout)
1428       subItem = focusedLayout->GetFocusedItem();
1429   }
1430
1431   // Don't know what to do, so send to our parent window.
1432   CGUIMessage msg(GUI_MSG_CLICKED, GetID(), GetParentID(), actionID, subItem);
1433   return SendWindowMessage(msg);
1434 }
1435
1436 bool CGUIEPGGridContainer::OnMouseWheel(char wheel, const CPoint &point)
1437 {
1438   ///doesn't work while an item is selected?
1439   ProgrammesScroll(-wheel);
1440   return true;
1441 }
1442
1443 int CGUIEPGGridContainer::GetSelectedItem() const
1444 {
1445   if (m_gridIndex.empty() ||
1446       m_epgItemsPtr.empty() ||
1447       m_channelCursor + m_channelOffset >= m_channels ||
1448       m_blockCursor + m_blockOffset >= m_blocks)
1449     return -1;
1450
1451   CGUIListItemPtr currentItem = m_gridIndex[m_channelCursor + m_channelOffset][m_blockCursor + m_blockOffset].item;
1452   if (!currentItem)
1453     return -1;
1454
1455   for (int i = 0; i < (int)m_programmeItems.size(); i++)
1456   {
1457     if (currentItem == m_programmeItems[i])
1458       return i;
1459   }
1460   return -1;
1461 }
1462
1463 CGUIListItemPtr CGUIEPGGridContainer::GetListItem(int offset, unsigned int flag) const
1464 {
1465   if (m_channelItems.empty())
1466     return CGUIListItemPtr();
1467
1468   int item = m_channelCursor + m_channelOffset + offset;
1469   if (flag & INFOFLAG_LISTITEM_POSITION)
1470     item = (int)(m_channelScrollOffset / m_channelLayout->Size(VERTICAL));
1471
1472   if (flag & INFOFLAG_LISTITEM_WRAP)
1473   {
1474     item %= (int)m_channelItems.size();
1475     if (item < 0) item += m_channelItems.size();
1476     return m_channelItems[item];
1477   }
1478   else
1479   {
1480     if (item >= 0 && item < (int)m_channelItems.size())
1481       return m_channelItems[item];
1482   }
1483   return CGUIListItemPtr();
1484 }
1485
1486 CStdString CGUIEPGGridContainer::GetLabel(int info) const
1487 {
1488   CStdString label;
1489   switch (info)
1490   {
1491   case CONTAINER_NUM_PAGES:
1492     label = StringUtils::Format("%u", (m_channels + m_channelsPerPage - 1) / m_channelsPerPage);
1493     break;
1494   case CONTAINER_CURRENT_PAGE:
1495     label = StringUtils::Format("%u", 1 + (m_channelCursor + m_channelOffset) / m_channelsPerPage );
1496     break;
1497   case CONTAINER_POSITION:
1498     label = StringUtils::Format("%i", 1 + m_channelCursor + m_channelOffset);
1499     break;
1500   case CONTAINER_NUM_ITEMS:
1501     label = StringUtils::Format("%u", m_channels);
1502     break;
1503   default:
1504       break;
1505   }
1506   return label;
1507 }
1508
1509 GridItemsPtr *CGUIEPGGridContainer::GetClosestItem(const int &channel)
1510 {
1511   GridItemsPtr *closest = GetItem(channel);
1512
1513   if(!closest)
1514     return NULL;
1515
1516   int block = GetBlock(closest->item, channel);
1517   int left;   // num blocks to start of previous item
1518   int right;  // num blocks to start of next item
1519
1520   if (block == m_blockCursor)
1521     return closest; // item & m_item start together
1522
1523   if (block + GetItemSize(closest) == m_blockCursor + GetItemSize(m_item))
1524     return closest; // closest item ends when current does
1525
1526   if (block > m_blockCursor)  // item starts after m_item
1527   {
1528     left = m_blockCursor - GetBlock(closest->item, channel);
1529     right = block - m_blockCursor;
1530   }
1531   else
1532   {
1533     left  = m_blockCursor - block;
1534     right = GetBlock(GetNextItem(channel)->item, channel) - m_blockCursor;
1535   }
1536
1537   if (right <= SHORTGAP && right <= left && m_blockCursor + right < m_blocksPerPage)
1538     return &m_gridIndex[channel + m_channelOffset][m_blockCursor + right + m_blockOffset];
1539
1540   return &m_gridIndex[channel + m_channelOffset][m_blockCursor - left  + m_blockOffset];
1541 }
1542
1543 int CGUIEPGGridContainer::GetItemSize(GridItemsPtr *item)
1544 {
1545   if (!item)
1546     return (int) m_blockSize; /// stops it crashing
1547
1548   return (int) ((m_orientation == VERTICAL ? item->width : item->height) / m_blockSize);
1549 }
1550
1551 int CGUIEPGGridContainer::GetBlock(const CGUIListItemPtr &item, const int &channel)
1552 {
1553   if (!item)
1554     return 0;
1555
1556   return GetRealBlock(item, channel) - m_blockOffset;
1557 }
1558
1559 int CGUIEPGGridContainer::GetRealBlock(const CGUIListItemPtr &item, const int &channel)
1560 {
1561   int channelIndex = channel + m_channelOffset;
1562   int block = 0;
1563
1564   while (m_gridIndex[channelIndex][block].item != item && block < m_blocks)
1565     block++;
1566
1567   return block;
1568 }
1569
1570 GridItemsPtr *CGUIEPGGridContainer::GetNextItem(const int &channel)
1571 {
1572   int channelIndex = channel + m_channelOffset;
1573   int blockIndex = m_blockCursor + m_blockOffset;
1574   if (channelIndex >= m_channels || blockIndex >= m_blocks)
1575     return NULL;
1576
1577   int i = m_blockCursor;
1578
1579   while (i < m_blocksPerPage && m_gridIndex[channelIndex][i + m_blockOffset].item == m_gridIndex[channelIndex][blockIndex].item)
1580     i++;
1581
1582   return &m_gridIndex[channelIndex][i + m_blockOffset];
1583 }
1584
1585 GridItemsPtr *CGUIEPGGridContainer::GetPrevItem(const int &channel)
1586 {
1587   int channelIndex = channel + m_channelOffset;
1588   int blockIndex = m_blockCursor + m_blockOffset;
1589   if (channelIndex >= m_channels || blockIndex >= m_blocks)
1590     return NULL;
1591
1592   int i = m_blockCursor;
1593
1594   while (i > 0 && m_gridIndex[channelIndex][i + m_blockOffset].item == m_gridIndex[channelIndex][blockIndex].item)
1595     i--;
1596
1597   return &m_gridIndex[channelIndex][i + m_blockOffset];
1598 }
1599
1600 GridItemsPtr *CGUIEPGGridContainer::GetItem(const int &channel)
1601 {
1602   int channelIndex = channel + m_channelOffset;
1603   int blockIndex = m_blockCursor + m_blockOffset;
1604   if (channelIndex >= m_channels || blockIndex >= m_blocks)
1605     return NULL;
1606
1607   return &m_gridIndex[channelIndex][blockIndex];
1608 }
1609
1610 void CGUIEPGGridContainer::SetFocus(bool bOnOff)
1611 {
1612   if (bOnOff != HasFocus())
1613   {
1614     SetInvalid();
1615     /*m_lastItem.reset();
1616     m_lastChannel.reset();*/
1617   }
1618
1619   CGUIControl::SetFocus(bOnOff);
1620 }
1621
1622 void CGUIEPGGridContainer::DoRender()
1623 {
1624   CGUIControl::DoRender();
1625   m_wasReset = false;
1626 }
1627
1628 void CGUIEPGGridContainer::ScrollToChannelOffset(int offset)
1629 {
1630   float size = m_programmeLayout->Size(VERTICAL);
1631   int range = m_channelsPerPage / 4;
1632
1633   if (range <= 0) range = 1;
1634
1635   if (offset * size < m_channelScrollOffset &&  m_channelScrollOffset - offset * size > size * range)
1636   { // scrolling up, and we're jumping more than 0.5 of a screen
1637     m_channelScrollOffset = (offset + range) * size;
1638   }
1639
1640   if (offset * size > m_channelScrollOffset && offset * size - m_channelScrollOffset > size * range)
1641   { // scrolling down, and we're jumping more than 0.5 of a screen
1642     m_channelScrollOffset = (offset - range) * size;
1643   }
1644
1645   m_channelScrollSpeed = (offset * size - m_channelScrollOffset) / m_scrollTime;
1646
1647   m_channelOffset = offset;
1648 }
1649
1650 void CGUIEPGGridContainer::ScrollToBlockOffset(int offset)
1651 {
1652   // make sure offset is in valid range
1653   offset = std::max(0, std::min(offset, m_blocks - m_blocksPerPage));
1654
1655   float size = m_blockSize;
1656   int range = m_blocksPerPage / 1;
1657
1658   if (range <= 0) range = 1;
1659
1660   if (offset * size < m_programmeScrollOffset &&  m_programmeScrollOffset - offset * size > size * range)
1661   { // scrolling left, and we're jumping more than 0.5 of a screen
1662     m_programmeScrollOffset = (offset + range) * size;
1663   }
1664
1665   if (offset * size > m_programmeScrollOffset && offset * size - m_programmeScrollOffset > size * range)
1666   { // scrolling right, and we're jumping more than 0.5 of a screen
1667     m_programmeScrollOffset = (offset - range) * size;
1668   }
1669
1670   m_programmeScrollSpeed = (offset * size - m_programmeScrollOffset) / m_scrollTime;
1671
1672   m_blockOffset = offset;
1673 }
1674
1675 void CGUIEPGGridContainer::ValidateOffset()
1676 {
1677   if (!m_programmeLayout)
1678     return;
1679
1680   if (m_channelOffset > m_channels - m_channelsPerPage || m_channelScrollOffset > (m_channels - m_channelsPerPage) * m_channelHeight)
1681   {
1682     m_channelOffset = m_channels - m_channelsPerPage;
1683     m_channelScrollOffset = m_channelOffset * m_channelHeight;
1684   }
1685
1686   if (m_channelOffset < 0 || m_channelScrollOffset < 0)
1687   {
1688     m_channelOffset = 0;
1689     m_channelScrollOffset = 0;
1690   }
1691
1692   if (m_blockOffset > m_blocks - m_blocksPerPage || m_programmeScrollOffset > (m_blocks - m_blocksPerPage) * m_blockSize)
1693   {
1694     m_blockOffset = m_blocks - m_blocksPerPage;
1695     m_programmeScrollOffset = m_blockOffset * m_blockSize;
1696   }
1697
1698   if (m_blockOffset < 0 || m_programmeScrollOffset < 0)
1699   {
1700     m_blockOffset = 0;
1701     m_programmeScrollOffset = 0;
1702   }
1703 }
1704
1705 void CGUIEPGGridContainer::LoadLayout(TiXmlElement *layout)
1706 {
1707   /* layouts for the channel column */
1708   TiXmlElement *itemElement = layout->FirstChildElement("channellayout");
1709   while (itemElement)
1710   { // we have a new item layout
1711     CGUIListItemLayout itemLayout;
1712     itemLayout.LoadLayout(itemElement, GetParentID(), false);
1713     m_channelLayouts.push_back(itemLayout);
1714     itemElement = itemElement->NextSiblingElement("channellayout");
1715   }
1716   itemElement = layout->FirstChildElement("focusedchannellayout");
1717   while (itemElement)
1718   { // we have a new item layout
1719     CGUIListItemLayout itemLayout;
1720     itemLayout.LoadLayout(itemElement, GetParentID(), true);
1721     m_focusedChannelLayouts.push_back(itemLayout);
1722     itemElement = itemElement->NextSiblingElement("focusedchannellayout");
1723   }
1724
1725   /* layouts for the grid items */
1726   itemElement = layout->FirstChildElement("focusedlayout");
1727   while (itemElement)
1728   {
1729     CGUIListItemLayout itemLayout;
1730     itemLayout.LoadLayout(itemElement, GetParentID(), true);
1731     m_focusedProgrammeLayouts.push_back(itemLayout);
1732     itemElement = itemElement->NextSiblingElement("focusedlayout");
1733   }
1734   itemElement = layout->FirstChildElement("itemlayout");
1735   while (itemElement)
1736   {
1737     CGUIListItemLayout itemLayout;
1738     itemLayout.LoadLayout(itemElement, GetParentID(), false);
1739     m_programmeLayouts.push_back(itemLayout);
1740     itemElement = itemElement->NextSiblingElement("itemlayout");
1741   }
1742
1743   /* layout for the timeline above the grid */
1744   itemElement = layout->FirstChildElement("rulerlayout");
1745   while (itemElement)
1746   {
1747     CGUIListItemLayout itemLayout;
1748     itemLayout.LoadLayout(itemElement, GetParentID(), false);
1749     m_rulerLayouts.push_back(itemLayout);
1750     itemElement = itemElement->NextSiblingElement("rulerlayout");
1751   }
1752 }
1753
1754 void CGUIEPGGridContainer::UpdateLayout(bool updateAllItems)
1755 {
1756   // if container is invalid, either new data has arrived, or m_blockSize has changed
1757   //  need to run UpdateItems rather than CalculateLayout?
1758   if (updateAllItems)
1759   { // free memory of items
1760     for (iItems it = m_channelItems.begin(); it != m_channelItems.end(); it++)
1761       (*it)->FreeMemory();
1762     for (iItems it = m_rulerItems.begin(); it != m_rulerItems.end(); it++)
1763       (*it)->FreeMemory();
1764     for (iItems it = m_programmeItems.begin(); it != m_programmeItems.end(); it++)
1765       (*it)->FreeMemory();
1766   }
1767
1768   // and recalculate the layout
1769   CalculateLayout();
1770 }
1771
1772 CStdString CGUIEPGGridContainer::GetDescription() const
1773 {
1774   CStdString strLabel;
1775   int item = GetSelectedItem();
1776   if (item >= 0 && item < (int)m_programmeItems.size())
1777   {
1778     CGUIListItemPtr pItem = m_programmeItems[item];
1779     strLabel = pItem->GetLabel();
1780   }
1781   return strLabel;
1782 }
1783
1784 void CGUIEPGGridContainer::ClearGridIndex(void)
1785 {
1786   for (unsigned int i = 0; i < m_gridIndex.size(); i++)
1787   {
1788     for (int block = 0; block < m_blocks; block++)
1789     {
1790       if (m_gridIndex[i][block].item)
1791         m_gridIndex[i][block].item.get()->ClearProperties();
1792     }
1793     m_gridIndex[i].clear();
1794   }
1795   m_gridIndex.clear();
1796 }
1797
1798 void CGUIEPGGridContainer::Reset()
1799 {
1800   ClearGridIndex();
1801
1802   m_wasReset = true;
1803   m_channelItems.clear();
1804   m_programmeItems.clear();
1805   m_rulerItems.clear();
1806   m_epgItemsPtr.clear();
1807
1808   m_lastItem    = NULL;
1809   m_lastChannel = NULL;
1810 }
1811
1812 void CGUIEPGGridContainer::GoToBegin()
1813 {
1814   ScrollToBlockOffset(0);
1815   SetBlock(0);
1816 }
1817
1818 void CGUIEPGGridContainer::GoToEnd()
1819 {
1820   int blocksEnd = 0;   // the end block of the last epg element for the selected channel
1821   int blocksStart = 0; // the start block of the last epg element for the selected channel
1822   int blockOffset = 0; // the block offset to scroll to
1823   for (int blockIndex = m_blocks; blockIndex >= 0 && (!blocksEnd || !blocksStart); blockIndex--)
1824   {
1825     if (!blocksEnd && m_gridIndex[m_channelCursor + m_channelOffset][blockIndex].item != NULL)
1826       blocksEnd = blockIndex;
1827     if (blocksEnd && m_gridIndex[m_channelCursor + m_channelOffset][blocksEnd].item != 
1828                      m_gridIndex[m_channelCursor + m_channelOffset][blockIndex].item)
1829       blocksStart = blockIndex + 1;
1830   }
1831   if (blocksEnd - blocksStart > m_blocksPerPage)
1832     blockOffset = blocksStart;
1833   else if (blocksEnd > m_blocksPerPage)
1834     blockOffset = blocksEnd - m_blocksPerPage;
1835
1836   ScrollToBlockOffset(blockOffset); // scroll to the start point of the last epg element
1837   SetBlock(m_blocksPerPage - 1);    // select the last epg element
1838 }
1839
1840 void CGUIEPGGridContainer::GoToNow()
1841 {
1842   CDateTime currentDate = CDateTime::GetCurrentDateTime().GetAsUTCDateTime();
1843   int offset = ((currentDate - m_gridStart).GetSecondsTotal() / 60 - 30) / MINSPERBLOCK;
1844   ScrollToBlockOffset(offset);
1845 }
1846
1847 void CGUIEPGGridContainer::SetStartEnd(CDateTime start, CDateTime end)
1848 {
1849   m_gridStart = CDateTime(start.GetYear(), start.GetMonth(), start.GetDay(), start.GetHour(), start.GetMinute() >= 30 ? 30 : 0, 0);
1850   m_gridEnd = CDateTime(end.GetYear(), end.GetMonth(), end.GetDay(), end.GetHour(), end.GetMinute() >= 30 ? 30 : 0, 0);
1851
1852   CLog::Log(LOGDEBUG, "CGUIEPGGridContainer - %s - start=%s end=%s",
1853       __FUNCTION__, m_gridStart.GetAsLocalizedDateTime(false, true).c_str(), m_gridEnd.GetAsLocalizedDateTime(false, true).c_str());
1854 }
1855
1856 void CGUIEPGGridContainer::CalculateLayout()
1857 {
1858   CGUIListItemLayout *oldFocusedChannelLayout   = m_focusedChannelLayout;
1859   CGUIListItemLayout *oldChannelLayout          = m_channelLayout;
1860   CGUIListItemLayout *oldFocusedProgrammeLayout = m_focusedProgrammeLayout;
1861   CGUIListItemLayout *oldProgrammeLayout        = m_programmeLayout;
1862   CGUIListItemLayout *oldRulerLayout            = m_rulerLayout;
1863   GetCurrentLayouts();
1864
1865   if (!m_focusedProgrammeLayout || !m_programmeLayout || !m_focusedChannelLayout || !m_channelLayout || !m_rulerLayout)
1866     return;
1867
1868   if (oldChannelLayout   == m_channelLayout   && oldFocusedChannelLayout   == m_focusedChannelLayout   &&
1869       oldProgrammeLayout == m_programmeLayout && oldFocusedProgrammeLayout == m_focusedProgrammeLayout &&
1870       oldRulerLayout     == m_rulerLayout)
1871     return; // nothing has changed, so don't update stuff
1872
1873   m_channelHeight       = m_channelLayout->Size(VERTICAL);
1874   m_channelWidth        = m_channelLayout->Size(HORIZONTAL);
1875   if (m_orientation == VERTICAL)
1876   {
1877     m_rulerHeight       = m_rulerLayout->Size(VERTICAL);
1878     m_gridPosX          = m_posX + m_channelWidth;
1879     m_gridPosY          = m_posY + m_rulerHeight;
1880     m_gridWidth         = m_width - m_channelWidth;
1881     m_gridHeight        = m_height - m_rulerHeight;
1882     m_blockSize         = m_gridWidth / m_blocksPerPage;
1883     m_rulerWidth        = m_rulerUnit * m_blockSize;
1884     m_channelPosX       = m_posX;
1885     m_channelPosY       = m_posY + m_rulerHeight;
1886     m_rulerPosX         = m_posX + m_channelWidth;
1887     m_rulerPosY         = m_posY;
1888     m_channelsPerPage   = (int)(m_gridHeight / m_channelHeight);
1889     m_ProgrammesPerPage = (int)(m_gridWidth / m_blockSize) + 1;
1890   }
1891   else
1892   {
1893     m_rulerWidth        = m_rulerLayout->Size(HORIZONTAL);
1894     m_gridPosX          = m_posX + m_rulerWidth;
1895     m_gridPosY          = m_posY + m_channelHeight;
1896     m_gridWidth         = m_width - m_rulerWidth;
1897     m_gridHeight        = m_height - m_channelHeight;
1898     m_blockSize         = m_gridHeight / m_blocksPerPage;
1899     m_rulerHeight       = m_rulerUnit * m_blockSize;
1900     m_channelPosX       = m_posX + m_rulerWidth;
1901     m_channelPosY       = m_posY;
1902     m_rulerPosX         = m_posX;
1903     m_rulerPosY         = m_posY + m_channelHeight;
1904     m_channelsPerPage   = (int)(m_gridWidth / m_channelWidth);
1905     m_ProgrammesPerPage = (int)(m_gridHeight / m_blockSize) + 1;
1906   }
1907
1908   // ensure that the scroll offsets are a multiple of our sizes
1909   m_channelScrollOffset   = m_channelOffset * m_programmeLayout->Size(m_orientation);
1910   m_programmeScrollOffset = m_blockOffset * m_blockSize;
1911 }
1912
1913 void CGUIEPGGridContainer::UpdateScrollOffset(unsigned int currentTime)
1914 {
1915   if (!m_programmeLayout)
1916     return;
1917   m_channelScrollOffset += m_channelScrollSpeed * (currentTime - m_channelScrollLastTime);
1918   if ((m_channelScrollSpeed < 0 && m_channelScrollOffset < m_channelOffset * m_programmeLayout->Size(m_orientation)) ||
1919       (m_channelScrollSpeed > 0 && m_channelScrollOffset > m_channelOffset * m_programmeLayout->Size(m_orientation)))
1920   {
1921     m_channelScrollOffset = m_channelOffset * m_programmeLayout->Size(m_orientation);
1922     m_channelScrollSpeed = 0;
1923   }
1924   m_channelScrollLastTime = currentTime;
1925
1926   m_programmeScrollOffset += m_programmeScrollSpeed * (currentTime - m_programmeScrollLastTime);
1927   if ((m_programmeScrollSpeed < 0 && m_programmeScrollOffset < m_blockOffset * m_blockSize) ||
1928       (m_programmeScrollSpeed > 0 && m_programmeScrollOffset > m_blockOffset * m_blockSize))
1929   {
1930     m_programmeScrollOffset = m_blockOffset * m_blockSize;
1931     m_programmeScrollSpeed = 0;
1932   }
1933   m_programmeScrollLastTime = currentTime;
1934 }
1935
1936 void CGUIEPGGridContainer::GetCurrentLayouts()
1937 {
1938   m_channelLayout = NULL;
1939   for (unsigned int i = 0; i < m_channelLayouts.size(); i++)
1940   {
1941     if (m_channelLayouts[i].CheckCondition())
1942     {
1943       m_channelLayout = &m_channelLayouts[i];
1944       break;
1945     }
1946   }
1947   if (!m_channelLayout && !m_channelLayouts.empty())
1948     m_channelLayout = &m_channelLayouts[0];  // failsafe
1949
1950   m_focusedChannelLayout = NULL;
1951   for (unsigned int i = 0; i < m_focusedChannelLayouts.size(); i++)
1952   {
1953     if (m_focusedChannelLayouts[i].CheckCondition())
1954     {
1955       m_focusedChannelLayout = &m_focusedChannelLayouts[i];
1956       break;
1957     }
1958   }
1959   if (!m_focusedChannelLayout && !m_focusedChannelLayouts.empty())
1960     m_focusedChannelLayout = &m_focusedChannelLayouts[0];  // failsafe
1961
1962   m_programmeLayout = NULL;
1963   for (unsigned int i = 0; i < m_programmeLayouts.size(); i++)
1964   {
1965     if (m_programmeLayouts[i].CheckCondition())
1966     {
1967       m_programmeLayout = &m_programmeLayouts[i];
1968       break;
1969     }
1970   }
1971   if (!m_programmeLayout && !m_programmeLayouts.empty())
1972     m_programmeLayout = &m_programmeLayouts[0];  // failsafe
1973
1974   m_focusedProgrammeLayout = NULL;
1975   for (unsigned int i = 0; i < m_focusedProgrammeLayouts.size(); i++)
1976   {
1977     if (m_focusedProgrammeLayouts[i].CheckCondition())
1978     {
1979       m_focusedProgrammeLayout = &m_focusedProgrammeLayouts[i];
1980       break;
1981     }
1982   }
1983   if (!m_focusedProgrammeLayout && !m_focusedProgrammeLayouts.empty())
1984     m_focusedProgrammeLayout = &m_focusedProgrammeLayouts[0];  // failsafe
1985
1986   m_rulerLayout = NULL;
1987   for (unsigned int i = 0; i < m_rulerLayouts.size(); i++)
1988   {
1989     if (m_rulerLayouts[i].CheckCondition())
1990     {
1991       m_rulerLayout = &m_rulerLayouts[i];
1992       break;
1993     }
1994   }
1995   if (!m_rulerLayout && !m_rulerLayouts.empty())
1996     m_rulerLayout = &m_rulerLayouts[0];  // failsafe
1997 }
1998
1999 int CGUIEPGGridContainer::CorrectOffset(int offset, int cursor) const
2000 {
2001   return offset + cursor;
2002 }
2003
2004 void CGUIEPGGridContainer::SetRenderOffset(const CPoint &offset)
2005 {
2006   m_renderOffset = offset;
2007 }
2008
2009 void CGUIEPGGridContainer::FreeChannelMemory(int keepStart, int keepEnd)
2010 {
2011   if (keepStart < keepEnd)
2012   { // remove before keepStart and after keepEnd
2013     for (int i = 0; i < keepStart && i < (int)m_channelItems.size(); ++i)
2014       m_channelItems[i]->FreeMemory();
2015     for (int i = keepEnd + 1; i < (int)m_channelItems.size(); ++i)
2016       m_channelItems[i]->FreeMemory();
2017   }
2018   else
2019   { // wrapping
2020     for (int i = keepEnd + 1; i < keepStart && i < (int)m_channelItems.size(); ++i)
2021       m_channelItems[i]->FreeMemory();
2022   }
2023 }
2024
2025 void CGUIEPGGridContainer::FreeProgrammeMemory(int channel, int keepStart, int keepEnd)
2026 {
2027   if (keepStart < keepEnd)
2028   { // remove before keepStart and after keepEnd
2029     if (keepStart > 0 && keepStart < m_blocks)
2030     {
2031       // if item exist and block is not part of visible item
2032       CGUIListItemPtr last = m_gridIndex[channel][keepStart].item;
2033       for (int i = keepStart - 1 ; i > 0 ; i--)
2034       {
2035         if (m_gridIndex[channel][i].item && m_gridIndex[channel][i].item != last)
2036         {
2037           m_gridIndex[channel][i].item->FreeMemory();
2038           // FreeMemory() is smart enough to not cause any problems when called multiple times on same item
2039           // but we can make use of condition needed to not call FreeMemory() on item that is partially visible
2040           // to avoid calling FreeMemory() multiple times on item that ocupy few blocks in a row
2041           last = m_gridIndex[channel][i].item;
2042         }
2043       }
2044     }
2045
2046     if (keepEnd > 0 && keepEnd < m_blocks)
2047     {
2048       CGUIListItemPtr last = m_gridIndex[channel][keepEnd].item;
2049       for (int i = keepEnd + 1 ; i < m_blocks ; i++)
2050       {
2051         // if item exist and block is not part of visible item
2052         if (m_gridIndex[channel][i].item && m_gridIndex[channel][i].item != last)
2053         {
2054           m_gridIndex[channel][i].item->FreeMemory();
2055           // FreeMemory() is smart enough to not cause any problems when called multiple times on same item
2056           // but we can make use of condition needed to not call FreeMemory() on item that is partially visible
2057           // to avoid calling FreeMemory() multiple times on item that ocupy few blocks in a row
2058           last = m_gridIndex[channel][i].item;
2059         }
2060       }
2061     }
2062   }
2063 }
2064
2065 void CGUIEPGGridContainer::FreeRulerMemory(int keepStart, int keepEnd)
2066 {
2067   if (keepStart < keepEnd)
2068   { // remove before keepStart and after keepEnd
2069     for (int i = 1; i < keepStart && i < (int)m_rulerItems.size(); ++i)
2070       m_rulerItems[i]->FreeMemory();
2071     for (int i = keepEnd + 1; i < (int)m_rulerItems.size(); ++i)
2072       m_rulerItems[i]->FreeMemory();
2073   }
2074   else
2075   { // wrapping
2076     for (int i = keepEnd + 1; i < keepStart && i < (int)m_rulerItems.size(); ++i)
2077     {
2078       if (i == 0)
2079         continue;
2080       m_rulerItems[i]->FreeMemory();
2081     }
2082   }
2083 }
2084
2085 void CGUIEPGGridContainer::GetChannelCacheOffsets(int &cacheBefore, int &cacheAfter)
2086 {
2087   if (m_channelScrollSpeed > 0)
2088   {
2089     cacheBefore = 0;
2090     cacheAfter = m_cacheChannelItems;
2091   }
2092   else if (m_channelScrollSpeed < 0)
2093   {
2094     cacheBefore = m_cacheChannelItems;
2095     cacheAfter = 0;
2096   }
2097   else
2098   {
2099     cacheBefore = m_cacheChannelItems / 2;
2100     cacheAfter = m_cacheChannelItems / 2;
2101   }
2102 }
2103
2104 void CGUIEPGGridContainer::GetProgrammeCacheOffsets(int &cacheBefore, int &cacheAfter)
2105 {
2106   if (m_programmeScrollSpeed > 0)
2107   {
2108     cacheBefore = 0;
2109     cacheAfter = m_cacheProgrammeItems;
2110   }
2111   else if (m_programmeScrollSpeed < 0)
2112   {
2113     cacheBefore = m_cacheProgrammeItems;
2114     cacheAfter = 0;
2115   }
2116   else
2117   {
2118     cacheBefore = m_cacheProgrammeItems / 2;
2119     cacheAfter = m_cacheProgrammeItems / 2;
2120   }
2121 }