]> git.vomp.tv Git - vompclient.git/blob - vepg.cc
Multiple options pages
[vompclient.git] / vepg.cc
1 /*\r
2     Copyright 2005 Brian Walton\r
3 \r
4     This file is part of VOMP.\r
5 \r
6     VOMP is free software; you can redistribute it and/or modify\r
7     it under the terms of the GNU General Public License as published by\r
8     the Free Software Foundation; either version 2 of the License, or\r
9     (at your option) any later version.\r
10 \r
11     VOMP is distributed in the hope that it will be useful,\r
12     but WITHOUT ANY WARRANTY; without even the implied warranty of\r
13     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
14     GNU General Public License for more details.\r
15 \r
16     You should have received a copy of the GNU General Public License\r
17     along with VOMP; if not, write to the Free Software\r
18     Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA\r
19 */\r
20 /*\r
21     vepg presents a 2 dimensional electronic programme guide with channels down\r
22     the y axis and time along the x axis.\r
23     Programmes are layed on the x axis as alterate coloured blocks with as much\r
24     of the programme title as will fit inside the block shown as text.\r
25     Up and down commands step through the channels whilst left and right commands\r
26     move through the programmes of the currently selected channel.\r
27     When a programme is selected, it highlights in the grid and full programe details\r
28     (start time, title and description) are displayed in an area at te top left of the screen.\r
29     Any currently programmed timers will display in the grid and in the orogramme detail window as red\r
30     It is possible to select a programme to be recorded by pressing the record button.\r
31     The video stream currently being viewed is shown as quarter screen in the top right.\r
32 */\r
33 \r
34 #include "vepg.h"\r
35
36 VEpg* VEpg::instance = NULL;
37 \r
38 VEpg::VEpg(VVideoLive* v, UINT currentChannel)\r
39 {\r
40   instance = this;
41
42   // PAL / NTSC sizes -----------------------
43
44   int xsize, ysize;
45   int xpos, ypos;
46   int summaryLines, summaryLowerPadding;
47   int chanNameYpos;
48   //UINT gridRows; // is a class member
49
50   if (Video::getInstance()->getFormat() == Video::PAL)
51   {
52     xsize = 632;
53     ysize = 541;
54     xpos = 60;
55     ypos = 16;
56     summaryLines = 8;
57     summaryLowerPadding = 16;
58     chanNameYpos = 244;
59     gridRows = 7;
60   }
61   else
62   {
63     xsize = 632;
64     ysize = 452;
65     xpos = 50;
66     ypos = 10;
67     summaryLines = 6;
68     summaryLowerPadding = 26;
69     chanNameYpos = 206;
70     gridRows = 5;
71   }
72
73   // initialise variables and pointers
74   viewman = ViewMan::getInstance();\r
75   videoLive = v;
76   eventList = NULL;
77   chanList = VDR::getInstance()->getChannelsList(VDR::VIDEO); //TODO want to be able to display video and radio together
78   e = 0;
79 \r
80   for(UINT listIndex = 0; listIndex < gridRows; listIndex++)\r
81   {\r
82     // initialise array of pointers to eventlist structures\r
83     eventLista[listIndex] = NULL;\r
84   }\r
85 \r
86   // Create pallet on which to paint our epg view and position it in centre of screen.\r
87   // Need to reduce size to deal with overscanning TVs.\r
88
89   create(xsize, ysize);\r
90   setScreenPos(xpos, ypos);\r
91 \r
92   // beautify\r
93   Colour transparent = Colour(0, 0, 0, 0);\r
94   setBackgroundColour(transparent);\r
95
96   progTitle.setSurface(surface);\r
97   progTitle.setSurfaceOffset(0,0);\r
98   progTitle.setDimensions(300,(Surface::getFontHeight() + 4) * 2 + 16); //paragraph line seperation is 4 pixels\r
99   progTitle.setBackgroundColour(Colour::TITLEBARBACKGROUND);\r
100   progTitle.setTextPos(5, 16);
101   progTitle.setGap(4);
102
103   progInfo.setSurface(surface);\r
104   progInfo.setSurfaceOffset(0, progTitle.getOffsetY() + progTitle.getHeight());\r
105   progInfo.setDimensions(300,((Surface::getFontHeight() + 4) * summaryLines) + summaryLowerPadding);\r
106   progInfo.setGap(4);
107
108   chanName.setSurface(surface);\r
109   chanName.setDimensions(510, (Surface::getFontHeight() + 4));\r
110   chanName.setSurfaceOffset(305, chanNameYpos);\r
111   chanName.setBackgroundColour(Colour(0, 0, 0, 90));\r
112
113   // create area to display list of channels\r
114   chanListbox.setSurface(surface); // add channel list\r
115   chanListbox.setSurfaceOffset(0, progInfo.getOffsetY() + progInfo.getHeight() + Surface::getFontHeight() + 8); // position channel list\r
116   chanListbox.setDimensions(150, ((Surface::getFontHeight() + 2) * gridRows) + 5); //listbox line seperation is 2 pixels\r
117   chanListbox.setGap(2);
118
119
120   // populate channel list\r
121   Channel* chan;\r
122   int first = 1;\r
123   for (UINT i = 0; i < chanList->size(); i++)\r
124   {\r
125     chan = (*chanList)[i];\r
126     if (i == currentChannel)\r
127       first = 1;\r
128     chan->index = chanListbox.addOption(chan->name, first);\r
129     first = 0;\r
130   }\r
131   listTop = chanListbox.getTopOption();\r
132   chanListbox.draw(); // doing this to allow chanListbox.getBottomOption() in updateEventList() to work\r
133   chanName.setText((*chanList)[chanListbox.getCurrentOption()]->name);\r
134   time(&ltime); // set ltime to now\r
135   ltime = prevHour(&ltime); // set ltime to previous hour TODO make this half hour?\r
136   time(&selTime); // set selTime to now\r
137   updateEventList(); // get list of programmes\r
138 }\r
139 \r
140 VEpg::~VEpg()\r
141 {\r
142   instance = NULL;
143
144   for(UINT listIndex = 0; listIndex < gridRows; listIndex++)\r
145   {\r
146     if (eventLista[listIndex])\r
147     {\r
148       (eventLista)[listIndex]->clear();\r
149       delete eventLista[listIndex];\r
150     }\r
151   }\r
152   //  delete [] eventLista;\r
153 \r
154   // destroy dynamically allocated memory\r
155 }\r
156
157 VEpg* VEpg::getInstance()
158 {
159   return instance;
160 }
161 \r
162 void VEpg::setInfo(Event* event)\r
163 {\r
164   time_t t;\r
165   struct tm* btime; // to hold programme start and end time\r
166   char* timeString = new char[9]; // to hold programme start and end time\r
167   int length = strlen(event->title); // calculate length of programme title string\r
168   char* title = new char[length + 15]; // create string to hold start time, end time and programme title\r
169   btime = localtime((time_t*)&event->time); //get programme start time\r
170   strftime(timeString, 9, "%0H:%0M - ", btime); // and format it as hh:mm -\r
171   strcpy(title, timeString); // put it in our buffer\r
172   t = event->time + event->duration; //get programme end time\r
173   btime = localtime(&t);\r
174   strftime(timeString, 7, "%0H:%0M ", btime); // and format it as hh:mm -\r
175   strcat(title, timeString); // put it in our buffer\r
176   strcat(title, event->title); // then add the programme title\r
177   progTitle.setText(title); // sput this sring in our text box\r
178   length = strlen(event->description);\r
179   char* info = new char[length + 1]; // create programme detail string\r
180   strcpy(info, event->description);\r
181   progInfo.setText(info); // show programme detail string\r
182 // destroy dynamically allocated memory\r
183   delete info;\r
184   delete title;\r
185   delete timeString;\r
186 }\r
187 \r
188 void VEpg::draw()\r
189 {\r
190   View::draw(); // draw pallet\r
191 \r
192   // Moved all the dynamic data drawing to a seperate function\r
193 \r
194   // Display the status and key stuff at the bottom\r
195   int keyx = chanListbox.getOffsetX();\r
196   int keyy = chanListbox.getOffsetY() + chanListbox.getHeight() + 2;\r
197   surface->fillblt(keyx, keyy, 605, Surface::getFontHeight() * 2 + 14, surface->rgba(100, 100, 100, 255));\r
198
199   WSymbol w;\r
200   w.setSurface(surface);\r
201 \r
202   w.nextSymbol = WSymbol::LEFTARROW;\r
203   w.setSurfaceOffset(keyx + 1, keyy + 20);\r
204   w.draw();\r
205 \r
206   w.nextSymbol = WSymbol::UP;\r
207   w.setSurfaceOffset(keyx + 26, keyy + 3);\r
208   w.draw();\r
209 \r
210   w.nextSymbol = WSymbol::DOWN;\r
211   w.setSurfaceOffset(keyx + 26, keyy + 36);\r
212   w.draw();\r
213 \r
214   w.nextSymbol = WSymbol::RIGHTARROW;\r
215   w.setSurfaceOffset(keyx + 50, keyy + 20);\r
216   w.draw();\r
217 \r
218   drawText(tr("OK"), keyx + 18, keyy + 20, Colour::LIGHTTEXT);\r
219 \r
220   surface->fillblt(keyx + 72, keyy + 4, 104, Surface::getFontHeight() + 2, surface->rgba(200, 0, 0, 255));\r
221   drawText(tr("Page up"), keyx + 74, keyy + 5, Colour::LIGHTTEXT);\r
222 \r
223   surface->fillblt(keyx + 72, keyy + Surface::getFontHeight() + 8, 104, Surface::getFontHeight() + 2, surface->rgba(0, 200, 0, 255));\r
224   drawText(tr("Page down"), keyx + 74, keyy + Surface::getFontHeight() + 9, Colour::LIGHTTEXT);\r
225 \r
226   surface->fillblt(keyx + 180, keyy + 4, 104, Surface::getFontHeight() + 2, surface->rgba(200, 200, 0, 255));\r
227   drawText(tr("-24 hours"), keyx + 182, keyy + 5, Colour::LIGHTTEXT);\r
228 \r
229   surface->fillblt(keyx + 180, keyy + Surface::getFontHeight() + 8, 104, Surface::getFontHeight() + 2, surface->rgba( 0, 0, 200, 255));\r
230   drawText(tr("+24 hours"), keyx + 182, keyy + Surface::getFontHeight() + 9, Colour::LIGHTTEXT);\r
231 \r
232   surface->fillblt(keyx + 290, keyy + 4, 180, Surface::getFontHeight() + 2, surface->rgba( 180, 180, 180, 255));\r
233   drawText(tr("Guide / Back: Close"), keyx + 292 , keyy + 5, Colour::LIGHTTEXT);\r
234 \r
235   surface->fillblt(keyx + 290, keyy + Surface::getFontHeight() + 8, 180, Surface::getFontHeight() + 2, surface->rgba( 180, 180, 180, 255));\r
236 //  Colour red = Colour(130, 0, 0);\r
237 //  drawText(tr("Rec: Set timer"), keyx + 292, keyy + Surface::getFontHeight() + 9, red);\r
238 \r
239   surface->fillblt(keyx + 474, keyy + 4, 128, Surface::getFontHeight() + 2, surface->rgba( 180, 180, 180, 255));\r
240   w.nextSymbol = WSymbol::PLAY;\r
241   w.setSurfaceOffset(keyx + 476, keyy + 5);\r
242   w.draw();\r
243   drawText(tr("Sel channel"), keyx + 496, keyy + 5, Colour::LIGHTTEXT);\r
244 \r
245   surface->fillblt(keyx + 474, keyy + Surface::getFontHeight() + 8, 128, Surface::getFontHeight() + 2, surface->rgba( 180, 180, 180, 255));\r
246   drawText(tr("Go: Preview"), keyx + 476, keyy + Surface::getFontHeight() + 9, Colour::LIGHTTEXT);\r
247 \r
248   // Draw all the dynamic data\r
249   drawData();\r
250 }\r
251 \r
252 void VEpg::drawData()\r
253 {\r
254   // Not doing View::draw() every time causes\r
255   // things not to be cleared off the surface properly\r
256   // So, blank out the data area first\r
257 \r
258   rectangle(\r
259     chanListbox.getOffsetX(),\r
260     chanListbox.getOffsetY() - Surface::getFontHeight() - 3,\r
261     155 + WINDOW_WIDTH * MINUTE_SCALE,\r
262     chanListbox.getHeight() + Surface::getFontHeight() + 3,\r
263     Colour::BLACK);\r
264 \r
265   chanListbox.draw();\r
266   drawgrid();\r
267   chanName.draw(); // TODO this should be dealt with by vvideolive\r
268   progTitle.draw();\r
269   progInfo.draw();\r
270 }
271
272 \r
273 int VEpg::handleCommand(int command)\r
274 {\r
275   switch(command)\r
276   {\r
277     case Remote::DF_UP:\r
278     case Remote::UP:\r
279     { // cursor up the channel list\r
280       chanListbox.up();\r
281       drawData();\r
282       viewman->updateView(this);\r
283       return 2;\r
284     }\r
285     case Remote::DF_DOWN:\r
286     case Remote::DOWN:\r
287     { // cursor down the channel list\r
288       chanListbox.down();\r
289       drawData();\r
290       viewman->updateView(this);\r
291       return 2;\r
292     }\r
293     case Remote::DF_LEFT:\r
294     case Remote::LEFT:\r
295     { // cursor left through time\r
296       selTime = thisEvent.time - 1;\r
297       drawData();\r
298       viewman->updateView(this);\r
299       return 2;\r
300     }\r
301     case Remote::DF_RIGHT:\r
302     case Remote::RIGHT:\r
303     {\r
304     // cursor right through time\r
305       selTime = thisEvent.time + thisEvent.duration;\r
306       drawData();\r
307       viewman->updateView(this);\r
308       return 2;\r
309     }\r
310     case Remote::RED:\r
311     {\r
312     // cursor up one page\r
313       chanListbox.pageUp();\r
314       drawData();\r
315       viewman->updateView(this);\r
316       return 2;\r
317     }\r
318     case Remote::GREEN:\r
319     {\r
320     // cursor down one page\r
321       chanListbox.pageDown();\r
322       drawData();\r
323       viewman->updateView(this);\r
324       return 2;\r
325     }\r
326     case Remote::BLUE:\r
327     {\r
328     // step forward 24 hours\r
329       selTime += 24 * 60 * 60;\r
330       drawData();\r
331       viewman->updateView(this);\r
332       return 2;\r
333     }\r
334     case Remote::YELLOW:\r
335     {\r
336     // step forward 24 hours\r
337       selTime -= 24 * 60 * 60;\r
338       drawData();\r
339       viewman->updateView(this);\r
340       return 2;\r
341     }\r
342     case Remote::RECORD:\r
343     {\r
344       Log::getInstance()->log("VEPG", Log::DEBUG, "ID %lu TIME %lu DURATION %lu TITLE %s\n", thisEvent.id, thisEvent.time, thisEvent.duration, thisEvent.title);
345
346       //TODO FIXME\r
347       return 2;\r
348     }\r
349     case Remote::PLAY:\r
350     case Remote::GO:\r
351     case Remote::OK:\r
352     { // select programme and display menu TODO currently just changes to selected channel\r
353       videoLive->channelChange(VVideoLive::NUMBER, (*chanList)[chanListbox.getCurrentOption()]->number);
354
355       if(command == Remote::GO)\r
356         return 2;\r
357       // GO just changes channel in preview, PLAY changes channel and returns to normal TV\r
358     }\r
359     case Remote::BACK:\r
360     case Remote::GUIDE:\r
361     {\r
362       // return to normal TV mode\r
363       if (videoLive) // ptr check done in case being tested from videorec\r
364       {\r
365         Message* m = new Message(); // Must be done after this view deleted
366         m->from = this;
367         m->to = videoLive;
368         m->message = Message::EPG_CLOSE;
369         ViewMan::getInstance()->postMessage(m);
370       }
371       return 4;\r
372     }\r
373     case Remote::CHANNELUP:
374     {
375       videoLive->channelChange(VVideoLive::OFFSET, VVideoLive::UP);
376       return 2;
377     }
378     case Remote::CHANNELDOWN:
379     {
380       videoLive->channelChange(VVideoLive::OFFSET, VVideoLive::DOWN);
381       return 2;
382     }
383   }\r
384   // stop command getting to any more views\r
385   return 1;\r
386 }\r
387 \r
388 void VEpg::drawgrid() // redraws grid and select programme\r
389 {\r
390   // draw the grid of programmes\r
391   char timeString[20];\r
392   time_t t;\r
393   time(&t); // set t = now\r
394   if(selTime < t)\r
395     selTime = t; // don't allow cursor in the past\r
396   if(listTop != chanListbox.getTopOption())\r
397   {\r
398   // chanListbox has scrolled TODO speed up by changing only rows that have changed\r
399     listTop = chanListbox.getTopOption();\r
400     updateEventList();\r
401   }\r
402   if ((selTime >= ltime + WINDOW_WIDTH * 60) || (selTime <= ltime))\r
403   {\r
404   // we have cursored back before left time of window\r
405   //TODO check that this and above don't happen together\r
406     ltime = prevHour(&selTime);\r
407     updateEventList();\r
408   }\r
409   // draw time scale\r
410   t = ltime;\r
411   struct tm* tms;\r
412   tms = localtime(&t);\r
413   strftime(timeString, 19, "%a %e %b", tms);\r
414   int timey = chanListbox.getOffsetY() - Surface::getFontHeight() - 3;\r
415   int timex = 135;\r
416   drawTextRJ(timeString, timex - 10, timey, Colour::LIGHTTEXT); // print date\r
417   strftime(timeString, 19, "%H:%M", tms);\r
418   drawText(timeString, timex, timey, Colour::LIGHTTEXT); // print left time\r
419   surface->fillblt(155, timey + Surface::getFontHeight(), 2, 7, surface->rgba(255, 255, 255, 255));\r
420   t = t + 3600;\r
421   tms = localtime(&t);\r
422   strftime(timeString, 19, "%H:%M", tms);\r
423   drawText(timeString, timex + 180, timey, Colour::LIGHTTEXT); // print middle time\r
424   surface->fillblt(335, timey + Surface::getFontHeight(), 2, 7, surface->rgba(255, 255, 255, 255));\r
425   t = t + 3600;\r
426   tms = localtime(&t);\r
427   strftime(timeString, 19, "%H:%M", tms);\r
428   drawText(timeString, timex + 360, timey, Colour::LIGHTTEXT); // print right time\r
429   surface->fillblt(515, timey + Surface::getFontHeight(), 2, 7, surface->rgba(255, 255, 255, 255));\r
430   // pointer to selTime\r
431   surface->fillblt(155 + (selTime - ltime) / 20, timey + Surface::getFontHeight(), 2, 7, surface->rgba(255, 50, 50, 255));\r
432 \r
433   // TODO should the above two comditional statements be combined to avoid calling updateEventList() twice?\r
434   Event* event;\r
435   Event noevent; // an event to use if there are gaps in the epg\r
436   thisEvent.setdescription(tr("There are no programme details available for this period"));\r
437   thisEvent.duration = WINDOW_WIDTH * 60;\r
438   thisEvent.time = ltime;\r
439   thisEvent.settitle(tr("No programme details"));\r
440   thisEvent.id = 0;\r
441   bool swapColour = FALSE; // alternate cell colour\r
442   bool currentRow = FALSE;\r
443   int y = chanListbox.getOffsetY() + 5; // vertical position of cell\r
444   Colour bg, fg; // background colour of cells in grid\r
445   // for each displayed channel, find programmes that fall in 2.5 hour time window\r
446   for(UINT listIndex = 0; listIndex < gridRows; listIndex++)\r
447   {\r
448     if (listTop + (int)listIndex >= chanListbox.getBottomOption())\r
449       continue; // ensure nothing populates grid below last channel\r
450     currentRow = (listTop + (int)listIndex == chanListbox.getCurrentOption());\r
451     noevent.time = ltime;\r
452     noevent.duration = WINDOW_WIDTH * 60;\r
453     noevent.settitle("");\r
454     paintCell(&noevent, y, Colour::NOPROGRAMME, Colour::LIGHTTEXT); // fill row with no programme colour to be painted ove with valid programmes\r
455     if (currentRow)\r
456     {\r
457       thisEvent.setdescription(tr("There are no programme details available for this period"));\r
458       thisEvent.duration = WINDOW_WIDTH * 60;\r
459       thisEvent.time = ltime;\r
460       thisEvent.settitle(tr("No programme details"));\r
461       thisEvent.id = 0;\r
462     }\r
463     if (eventLista[listIndex])\r
464     {\r
465       sort(eventLista[listIndex]->begin(), eventLista[listIndex]->end(), EventSorter());\r
466       for(e = 0; e < (eventLista[listIndex])->size(); e++) // step through events for this channel\r
467       {\r
468         fg = Colour::LIGHTTEXT;\r
469         event = (*eventLista[listIndex])[e];\r
470         if (event)\r
471         {\r
472           UINT end = event->time + event->duration; // programme end time\r
473           if(event->time >= UINT(ltime) + (WINDOW_WIDTH * 60)) // programme starts after RHS of window\r
474             continue; // that's enough of this channel's events\r
475           if(end <= UINT(ltime)) // programme ends before LHS of window\r
476             continue; // this event is before the window - let's try the next event\r
477           // this event is one we are interested in\r
478           bg = (swapColour)?Colour::PROGRAMMEA:Colour::PROGRAMMEB; // alternate cell colour\r
479           swapColour = !swapColour; // it wil be the other colour next time\r
480           if(event->time <= UINT(selTime) && end > UINT(selTime) && currentRow)\r
481           {\r
482             // this is the selected programme\r
483             thisEvent.setdescription(event->description);\r
484             thisEvent.duration = event->duration;\r
485             thisEvent.time = event->time;\r
486             thisEvent.settitle(event->title);\r
487             thisEvent.id = event->id;\r
488             if(thisEvent.id == 0)\r
489               thisEvent.id = 1;\r
490             bg = Colour::SELECTHIGHLIGHT; // highlight cell\r
491             fg = Colour::DARKTEXT;\r
492           }\r
493           else\r
494           {\r
495             if (currentRow && thisEvent.id == 0)\r
496             {\r
497               if (end <= UINT(selTime) && end > UINT(thisEvent.time))\r
498                 thisEvent.time = end;\r
499               if (event->time > UINT(selTime) && event->time < thisEvent.time + thisEvent.duration)\r
500                 thisEvent.duration = event->time - thisEvent.time;\r
501             }\r
502           }\r
503           paintCell(event, y, bg, fg);\r
504         }\r
505       }\r
506     }\r
507     else\r
508     {\r
509       // no event list for this channel. Already painted noevent colour so just highlight if selected\r
510       if (currentRow)\r
511       {\r
512         bg = Colour::SELECTHIGHLIGHT; // highlight cell\r
513         fg = Colour::DARKTEXT;\r
514         paintCell(&thisEvent, y, bg, fg);\r
515       }\r
516       else\r
517       {\r
518         bg = Colour::NOPROGRAMME;\r
519         fg = Colour::LIGHTTEXT;\r
520         noevent.settitle(tr("No programme details"));\r
521         paintCell(&noevent, y, bg, fg);\r
522       }\r
523     }\r
524     y += Surface::getFontHeight() + 2;\r
525   }\r
526   setInfo(&thisEvent);\r
527 }\r
528 \r
529 void VEpg::updateEventList()\r
530 {\r
531   Channel* chan;\r
532   for(UINT listIndex = 0; listIndex < gridRows; listIndex++)\r
533   {\r
534     if (eventLista[listIndex])\r
535     {\r
536       if ((eventLista[listIndex])->empty())\r
537         (eventLista)[listIndex]->clear();\r
538     }\r
539     if(listTop + listIndex >= UINT(chanListbox.getBottomOption()))\r
540       continue;\r
541     chan = (*chanList)[listTop + listIndex];\r
542     eventLista[listIndex] = VDR::getInstance()->getChannelSchedule(chan->number, ltime - 1, WINDOW_WIDTH * 60 + 2); // ltime - 1 to get prog before window (allows cursor left past ltime). + 2 to get prog after window\r
543   }\r
544 }\r
545 \r
546 void VEpg::setCurrentChannel(char* chname)\r
547 {\r
548   chanName.setText(chname);\r
549   chanName.draw();\r
550   viewman->updateView(this);\r
551 }\r
552 \r
553 void VEpg::paintCell(Event* event, int yOffset, Colour bg, Colour fg)\r
554 {\r
555   int w, x, y, h;\r
556   w = x = 0; // keep compiler happy
557
558   y =yOffset;\r
559   h = Surface::getFontHeight(); // TODO if want border around text, need to increae this and wselectlist line height\r
560   UINT end = event->time + event->duration; // programme end time\r
561   if(event->time <= UINT(ltime) && end > UINT(ltime)) // spans start of displayed window\r
562   {\r
563     x = 155; // LHS of window\r
564     if (end > (UINT(ltime) + (WINDOW_WIDTH * 60)))\r
565       w = WINDOW_WIDTH * MINUTE_SCALE; // spans full 2 hour window\r
566     else\r
567       w = MINUTE_SCALE * (event->time + event->duration - ltime ) / 60; // get width of remaining programme\r
568   }\r
569   if((event->time >= UINT(ltime)) && (event->time <= UINT(ltime) + (WINDOW_WIDTH * 60))) // starts within window\r
570   {\r
571     x = 155 + (MINUTE_SCALE * (event->time - ltime) / 60);\r
572     w = MINUTE_SCALE * event->duration / 60;\r
573     //if (w > 155 + MINUTE_SCALE * WINDOW_WIDTH -x)\r
574      // w = w + x - 155 - MINUTE_SCALE * WINDOW_WIDTH; // ends outside window\r
575   }\r
576   if (w > 155 + WINDOW_WIDTH * MINUTE_SCALE - x)\r
577     w = 155 + WINDOW_WIDTH * MINUTE_SCALE -x; // limit cells to RHS of window\r
578   surface->fillblt(x, y, w, h, surface->rgba(bg.red, bg.green, bg.blue, bg.alpha));\r
579   char* tt = new char[strlen(event->title) + 1];\r
580   strcpy (tt, event->title);\r
581   int textWidth = 0;\r
582   for (UINT textPos = 0; textPos < strlen(tt); textPos++)\r
583   {\r
584     int thisCharWidth = surface->getCharWidth(tt[textPos]);\r
585     if (textWidth + thisCharWidth > w) // text will not fit in cell\r
586     {\r
587       textWidth = textPos;\r
588       break;\r
589     }\r
590     textWidth += thisCharWidth;\r
591   }\r
592   char* tT = new char[textWidth];\r
593   if(textWidth > 1)\r
594   {\r
595     strncpy(tT, tt, textWidth - 1);\r
596     tT[textWidth - 1] =  '\0';\r
597     surface->drawText(tT, x+2, y, fg.red, fg.green, fg.blue);\r
598   }\r
599   delete tT;\r
600 \r
601 }\r
602 \r
603 time_t VEpg::prevHour(time_t* t)\r
604 {\r
605   struct tm* tms;\r
606   tms = localtime(t);\r
607   tms->tm_sec = 0;\r
608   tms->tm_min = 0;\r
609   return mktime(tms);\r
610 }\r
611 \r