]> git.vomp.tv Git - vompclient.git/blob - vepg.cc
Message tweaks
[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       //TODO FIXME\r
345       return 2;\r
346     }\r
347     case Remote::PLAY:\r
348     case Remote::GO:\r
349     case Remote::OK:\r
350     { // select programme and display menu TODO currently just changes to selected channel\r
351       Message* m = new Message();\r
352       m->from = this;\r
353       m->to = videoLive;\r
354       m->message = Message::CHANNEL_CHANGE;\r
355       m->parameter = (*chanList)[chanListbox.getCurrentOption()]->number;\r
356       ViewMan::getInstance()->postMessage(m);\r
357       if(command == Remote::GO)\r
358         return 2;\r
359       // GO just changes channel in preview, PLAY changes channel and returns to normal TV\r
360     }\r
361     case Remote::BACK:\r
362     case Remote::GUIDE:\r
363     {\r
364       // return to normal TV mode\r
365       if (videoLive) // ptr check done in case being tested from videorec\r
366       {\r
367         Message* m = new Message();
368         m->from = this;
369         m->to = videoLive;
370         m->message = Message::EPG_CLOSE;
371         ViewMan::getInstance()->postMessage(m);
372       }
373       return 4;\r
374     }\r
375     case Remote::CHANNELUP:\r
376     {\r
377     // change up channel on live TV\r
378       Message* m = new Message();\r
379       m->from = this;\r
380       m->to = videoLive;\r
381       m->message = Message::CHANNEL_UP;\r
382       ViewMan::getInstance()->postMessage(m);\r
383       return 2;\r
384     }\r
385     case Remote::CHANNELDOWN:\r
386     { // change down channel on live TV\r
387       Message* m = new Message();\r
388       m->from = this;\r
389       m->to = videoLive;\r
390       m->message = Message::CHANNEL_DOWN;\r
391       ViewMan::getInstance()->postMessage(m);\r
392       return 2;\r
393     }\r
394   }\r
395   // stop command getting to any more views\r
396   return 1;\r
397 }\r
398 \r
399 void VEpg::drawgrid() // redraws grid and select programme\r
400 {\r
401   // draw the grid of programmes\r
402   char timeString[20];\r
403   time_t t;\r
404   time(&t); // set t = now\r
405   if(selTime < t)\r
406     selTime = t; // don't allow cursor in the past\r
407   if(listTop != chanListbox.getTopOption())\r
408   {\r
409   // chanListbox has scrolled TODO speed up by changing only rows that have changed\r
410     listTop = chanListbox.getTopOption();\r
411     updateEventList();\r
412   }\r
413   if ((selTime >= ltime + WINDOW_WIDTH * 60) || (selTime <= ltime))\r
414   {\r
415   // we have cursored back before left time of window\r
416   //TODO check that this and above don't happen together\r
417     ltime = prevHour(&selTime);\r
418     updateEventList();\r
419   }\r
420   // draw time scale\r
421   t = ltime;\r
422   struct tm* tms;\r
423   tms = localtime(&t);\r
424   strftime(timeString, 19, "%a %e %b", tms);\r
425   int timey = chanListbox.getOffsetY() - Surface::getFontHeight() - 3;\r
426   int timex = 135;\r
427   drawTextRJ(timeString, timex - 10, timey, Colour::LIGHTTEXT); // print date\r
428   strftime(timeString, 19, "%H:%M", tms);\r
429   drawText(timeString, timex, timey, Colour::LIGHTTEXT); // print left time\r
430   surface->fillblt(155, timey + Surface::getFontHeight(), 2, 7, surface->rgba(255, 255, 255, 255));\r
431   t = t + 3600;\r
432   tms = localtime(&t);\r
433   strftime(timeString, 19, "%H:%M", tms);\r
434   drawText(timeString, timex + 180, timey, Colour::LIGHTTEXT); // print middle time\r
435   surface->fillblt(335, timey + Surface::getFontHeight(), 2, 7, surface->rgba(255, 255, 255, 255));\r
436   t = t + 3600;\r
437   tms = localtime(&t);\r
438   strftime(timeString, 19, "%H:%M", tms);\r
439   drawText(timeString, timex + 360, timey, Colour::LIGHTTEXT); // print right time\r
440   surface->fillblt(515, timey + Surface::getFontHeight(), 2, 7, surface->rgba(255, 255, 255, 255));\r
441   // pointer to selTime\r
442   surface->fillblt(155 + (selTime - ltime) / 20, timey + Surface::getFontHeight(), 2, 7, surface->rgba(255, 50, 50, 255));\r
443 \r
444   // TODO should the above two comditional statements be combined to avoid calling updateEventList() twice?\r
445   Event* event;\r
446   Event noevent; // an event to use if there are gaps in the epg\r
447   thisEvent.setdescription(tr("There are no programme details available for this period"));\r
448   thisEvent.duration = WINDOW_WIDTH * 60;\r
449   thisEvent.time = ltime;\r
450   thisEvent.settitle(tr("No programme details"));\r
451   thisEvent.id = 0;\r
452   bool swapColour = FALSE; // alternate cell colour\r
453   bool currentRow = FALSE;\r
454   int y = chanListbox.getOffsetY() + 5; // vertical position of cell\r
455   Colour bg, fg; // background colour of cells in grid\r
456   // for each displayed channel, find programmes that fall in 2.5 hour time window\r
457   for(UINT listIndex = 0; listIndex < gridRows; listIndex++)\r
458   {\r
459     if (listTop + (int)listIndex >= chanListbox.getBottomOption())\r
460       continue; // ensure nothing populates grid below last channel\r
461     currentRow = (listTop + (int)listIndex == chanListbox.getCurrentOption());\r
462     noevent.time = ltime;\r
463     noevent.duration = WINDOW_WIDTH * 60;\r
464     noevent.settitle("");\r
465     paintCell(&noevent, y, Colour::NOPROGRAMME, Colour::LIGHTTEXT); // fill row with no programme colour to be painted ove with valid programmes\r
466     if (currentRow)\r
467     {\r
468       thisEvent.setdescription(tr("There are no programme details available for this period"));\r
469       thisEvent.duration = WINDOW_WIDTH * 60;\r
470       thisEvent.time = ltime;\r
471       thisEvent.settitle(tr("No programme details"));\r
472       thisEvent.id = 0;\r
473     }\r
474     if (eventLista[listIndex])\r
475     {\r
476       sort(eventLista[listIndex]->begin(), eventLista[listIndex]->end(), EventSorter());\r
477       for(e = 0; e < (eventLista[listIndex])->size(); e++) // step through events for this channel\r
478       {\r
479         fg = Colour::LIGHTTEXT;\r
480         event = (*eventLista[listIndex])[e];\r
481         if (event)\r
482         {\r
483           UINT end = event->time + event->duration; // programme end time\r
484           if(event->time >= UINT(ltime) + (WINDOW_WIDTH * 60)) // programme starts after RHS of window\r
485             continue; // that's enough of this channel's events\r
486           if(end <= UINT(ltime)) // programme ends before LHS of window\r
487             continue; // this event is before the window - let's try the next event\r
488           // this event is one we are interested in\r
489           bg = (swapColour)?Colour::PROGRAMMEA:Colour::PROGRAMMEB; // alternate cell colour\r
490           swapColour = !swapColour; // it wil be the other colour next time\r
491           if(event->time <= UINT(selTime) && end > UINT(selTime) && currentRow)\r
492           {\r
493             // this is the selected programme\r
494             thisEvent.setdescription(event->description);\r
495             thisEvent.duration = event->duration;\r
496             thisEvent.time = event->time;\r
497             thisEvent.settitle(event->title);\r
498             thisEvent.id = event->id;\r
499             if(thisEvent.id == 0)\r
500               thisEvent.id = 1;\r
501             bg = Colour::SELECTHIGHLIGHT; // highlight cell\r
502             fg = Colour::DARKTEXT;\r
503           }\r
504           else\r
505           {\r
506             if (currentRow && thisEvent.id == 0)\r
507             {\r
508               if (end <= UINT(selTime) && end > UINT(thisEvent.time))\r
509                 thisEvent.time = end;\r
510               if (event->time > UINT(selTime) && event->time < thisEvent.time + thisEvent.duration)\r
511                 thisEvent.duration = event->time - thisEvent.time;\r
512             }\r
513           }\r
514           paintCell(event, y, bg, fg);\r
515         }\r
516       }\r
517     }\r
518     else\r
519     {\r
520       // no event list for this channel. Already painted noevent colour so just highlight if selected\r
521       if (currentRow)\r
522       {\r
523         bg = Colour::SELECTHIGHLIGHT; // highlight cell\r
524         fg = Colour::DARKTEXT;\r
525         paintCell(&thisEvent, y, bg, fg);\r
526       }\r
527       else\r
528       {\r
529         bg = Colour::NOPROGRAMME;\r
530         fg = Colour::LIGHTTEXT;\r
531         noevent.settitle(tr("No programme details"));\r
532         paintCell(&noevent, y, bg, fg);\r
533       }\r
534     }\r
535     y += Surface::getFontHeight() + 2;\r
536   }\r
537   setInfo(&thisEvent);\r
538 }\r
539 \r
540 void VEpg::updateEventList()\r
541 {\r
542   Channel* chan;\r
543   for(UINT listIndex = 0; listIndex < gridRows; listIndex++)\r
544   {\r
545     if (eventLista[listIndex])\r
546     {\r
547       if ((eventLista[listIndex])->empty())\r
548         (eventLista)[listIndex]->clear();\r
549     }\r
550     if(listTop + listIndex >= UINT(chanListbox.getBottomOption()))\r
551       continue;\r
552     chan = (*chanList)[listTop + listIndex];\r
553     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
554   }\r
555 }\r
556 \r
557 void VEpg::setCurrentChannel(char* chname)\r
558 {\r
559   chanName.setText(chname);\r
560   chanName.draw();\r
561   viewman->updateView(this);\r
562 }\r
563 \r
564 void VEpg::paintCell(Event* event, int yOffset, Colour bg, Colour fg)\r
565 {\r
566   int w, x, y, h;\r
567   w = x = 0; // keep compiler happy
568
569   y =yOffset;\r
570   h = Surface::getFontHeight(); // TODO if want border around text, need to increae this and wselectlist line height\r
571   UINT end = event->time + event->duration; // programme end time\r
572   if(event->time <= UINT(ltime) && end > UINT(ltime)) // spans start of displayed window\r
573   {\r
574     x = 155; // LHS of window\r
575     if (end > (UINT(ltime) + (WINDOW_WIDTH * 60)))\r
576       w = WINDOW_WIDTH * MINUTE_SCALE; // spans full 2 hour window\r
577     else\r
578       w = MINUTE_SCALE * (event->time + event->duration - ltime ) / 60; // get width of remaining programme\r
579   }\r
580   if((event->time >= UINT(ltime)) && (event->time <= UINT(ltime) + (WINDOW_WIDTH * 60))) // starts within window\r
581   {\r
582     x = 155 + (MINUTE_SCALE * (event->time - ltime) / 60);\r
583     w = MINUTE_SCALE * event->duration / 60;\r
584     //if (w > 155 + MINUTE_SCALE * WINDOW_WIDTH -x)\r
585      // w = w + x - 155 - MINUTE_SCALE * WINDOW_WIDTH; // ends outside window\r
586   }\r
587   if (w > 155 + WINDOW_WIDTH * MINUTE_SCALE - x)\r
588     w = 155 + WINDOW_WIDTH * MINUTE_SCALE -x; // limit cells to RHS of window\r
589   surface->fillblt(x, y, w, h, surface->rgba(bg.red, bg.green, bg.blue, bg.alpha));\r
590   char* tt = new char[strlen(event->title) + 1];\r
591   strcpy (tt, event->title);\r
592   int textWidth = 0;\r
593   for (UINT textPos = 0; textPos < strlen(tt); textPos++)\r
594   {\r
595     int thisCharWidth = surface->getCharWidth(tt[textPos]);\r
596     if (textWidth + thisCharWidth > w) // text will not fit in cell\r
597     {\r
598       textWidth = textPos;\r
599       break;\r
600     }\r
601     textWidth += thisCharWidth;\r
602   }\r
603   char* tT = new char[textWidth];\r
604   if(textWidth > 1)\r
605   {\r
606     strncpy(tT, tt, textWidth - 1);\r
607     tT[textWidth - 1] =  '\0';\r
608     surface->drawText(tT, x+2, y, fg.red, fg.green, fg.blue);\r
609   }\r
610   delete tT;\r
611 \r
612 }\r
613 \r
614 time_t VEpg::prevHour(time_t* t)\r
615 {\r
616   struct tm* tms;\r
617   tms = localtime(t);\r
618   tms->tm_sec = 0;\r
619   tms->tm_min = 0;\r
620   return mktime(tms);\r
621 }\r
622 \r