]> git.vomp.tv Git - vompclient.git/blob - vepg.cc
Options menu rearrangement, set timer from EPG
[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[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 }\r
186 \r
187 void VEpg::draw()\r
188 {\r
189   View::draw(); // draw pallet\r
190 \r
191   // Moved all the dynamic data drawing to a seperate function\r
192 \r
193   // Display the status and key stuff at the bottom\r
194   int keyx = chanListbox.getOffsetX();\r
195   int keyy = chanListbox.getOffsetY() + chanListbox.getHeight() + 2;\r
196   surface->fillblt(keyx, keyy, 605, Surface::getFontHeight() * 2 + 14, surface->rgba(100, 100, 100, 255));\r
197
198   WSymbol w;\r
199   w.setSurface(surface);\r
200 \r
201   w.nextSymbol = WSymbol::LEFTARROW;\r
202   w.setSurfaceOffset(keyx + 1, keyy + 20);\r
203   w.draw();\r
204 \r
205   w.nextSymbol = WSymbol::UP;\r
206   w.setSurfaceOffset(keyx + 26, keyy + 3);\r
207   w.draw();\r
208 \r
209   w.nextSymbol = WSymbol::DOWN;\r
210   w.setSurfaceOffset(keyx + 26, keyy + 36);\r
211   w.draw();\r
212 \r
213   w.nextSymbol = WSymbol::RIGHTARROW;\r
214   w.setSurfaceOffset(keyx + 50, keyy + 20);\r
215   w.draw();\r
216 \r
217   drawText(tr("OK"), keyx + 18, keyy + 20, Colour::LIGHTTEXT);\r
218 \r
219   surface->fillblt(keyx + 72, keyy + 4, 104, Surface::getFontHeight() + 2, surface->rgba(200, 0, 0, 255));\r
220   drawText(tr("Page up"), keyx + 74, keyy + 5, Colour::LIGHTTEXT);\r
221 \r
222   surface->fillblt(keyx + 72, keyy + Surface::getFontHeight() + 8, 104, Surface::getFontHeight() + 2, surface->rgba(0, 200, 0, 255));\r
223   drawText(tr("Page down"), keyx + 74, keyy + Surface::getFontHeight() + 9, Colour::LIGHTTEXT);\r
224 \r
225   surface->fillblt(keyx + 180, keyy + 4, 104, Surface::getFontHeight() + 2, surface->rgba(200, 200, 0, 255));\r
226   drawText(tr("-24 hours"), keyx + 182, keyy + 5, Colour::LIGHTTEXT);\r
227 \r
228   surface->fillblt(keyx + 180, keyy + Surface::getFontHeight() + 8, 104, Surface::getFontHeight() + 2, surface->rgba( 0, 0, 200, 255));\r
229   drawText(tr("+24 hours"), keyx + 182, keyy + Surface::getFontHeight() + 9, Colour::LIGHTTEXT);\r
230 \r
231   surface->fillblt(keyx + 290, keyy + 4, 180, Surface::getFontHeight() + 2, surface->rgba( 180, 180, 180, 255));\r
232   drawText(tr("Guide / Back: Close"), keyx + 292 , keyy + 5, Colour::LIGHTTEXT);\r
233 \r
234   surface->fillblt(keyx + 290, keyy + Surface::getFontHeight() + 8, 180, Surface::getFontHeight() + 2, surface->rgba( 180, 180, 180, 255));\r
235   Colour red = Colour(130, 0, 0);\r
236   drawText(tr("Rec: Set timer"), keyx + 292, keyy + Surface::getFontHeight() + 9, red);\r
237 \r
238   surface->fillblt(keyx + 474, keyy + 4, 128, Surface::getFontHeight() + 2, surface->rgba( 180, 180, 180, 255));\r
239   w.nextSymbol = WSymbol::PLAY;\r
240   w.setSurfaceOffset(keyx + 476, keyy + 5);\r
241   w.draw();\r
242   drawText(tr("Sel channel"), keyx + 496, keyy + 5, Colour::LIGHTTEXT);\r
243 \r
244   surface->fillblt(keyx + 474, keyy + Surface::getFontHeight() + 8, 128, Surface::getFontHeight() + 2, surface->rgba( 180, 180, 180, 255));\r
245   drawText(tr("Go: Preview"), keyx + 476, keyy + Surface::getFontHeight() + 9, Colour::LIGHTTEXT);\r
246 \r
247   // Draw all the dynamic data\r
248   drawData();\r
249 }\r
250 \r
251 void VEpg::drawData()\r
252 {\r
253   // Not doing View::draw() every time causes\r
254   // things not to be cleared off the surface properly\r
255   // So, blank out the data area first\r
256 \r
257   rectangle(\r
258     chanListbox.getOffsetX(),\r
259     chanListbox.getOffsetY() - Surface::getFontHeight() - 3,\r
260     155 + WINDOW_WIDTH * MINUTE_SCALE,\r
261     chanListbox.getHeight() + Surface::getFontHeight() + 3,\r
262     Colour::BLACK);\r
263 \r
264   chanListbox.draw();\r
265   drawgrid();\r
266   chanName.draw(); // TODO this should be dealt with by vvideolive\r
267   progTitle.draw();\r
268   progInfo.draw();\r
269 }
270
271 \r
272 int VEpg::handleCommand(int command)\r
273 {\r
274   switch(command)\r
275   {\r
276     case Remote::DF_UP:\r
277     case Remote::UP:\r
278     { // cursor up the channel list\r
279       chanListbox.up();\r
280       drawData();\r
281       viewman->updateView(this);\r
282       return 2;\r
283     }\r
284     case Remote::DF_DOWN:\r
285     case Remote::DOWN:\r
286     { // cursor down the channel list\r
287       chanListbox.down();\r
288       drawData();\r
289       viewman->updateView(this);\r
290       return 2;\r
291     }\r
292     case Remote::DF_LEFT:\r
293     case Remote::LEFT:\r
294     { // cursor left through time\r
295       selTime = thisEvent.time - 1;\r
296       drawData();\r
297       viewman->updateView(this);\r
298       return 2;\r
299     }\r
300     case Remote::DF_RIGHT:\r
301     case Remote::RIGHT:\r
302     {\r
303     // cursor right through time\r
304       selTime = thisEvent.time + thisEvent.duration;\r
305       drawData();\r
306       viewman->updateView(this);\r
307       return 2;\r
308     }\r
309     case Remote::RED:\r
310     {\r
311     // cursor up one page\r
312       chanListbox.pageUp();\r
313       drawData();\r
314       viewman->updateView(this);\r
315       return 2;\r
316     }\r
317     case Remote::GREEN:\r
318     {\r
319     // cursor down one page\r
320       chanListbox.pageDown();\r
321       drawData();\r
322       viewman->updateView(this);\r
323       return 2;\r
324     }\r
325     case Remote::BLUE:\r
326     {\r
327     // step forward 24 hours\r
328       selTime += 24 * 60 * 60;\r
329       drawData();\r
330       viewman->updateView(this);\r
331       return 2;\r
332     }\r
333     case Remote::YELLOW:\r
334     {\r
335     // step forward 24 hours\r
336       selTime -= 24 * 60 * 60;\r
337       drawData();\r
338       viewman->updateView(this);\r
339       return 2;\r
340     }\r
341     case Remote::RECORD:\r
342     {\r
343       Log::getInstance()->log("VEPG", Log::DEBUG, "ID %lu TIME %lu DURATION %lu TITLE %s", thisEvent.id, thisEvent.time, thisEvent.duration, thisEvent.title);
344       VEpgSetTimer* vs = new VEpgSetTimer(&thisEvent, (*chanList)[chanListbox.getCurrentOption()]);
345       vs->draw();
346       viewman->add(vs);
347       viewman->updateView(vs);
348       return 2;\r
349     }\r
350     case Remote::PLAY:\r
351     case Remote::GO:\r
352     case Remote::OK:\r
353     { // select programme and display menu TODO currently just changes to selected channel\r
354       videoLive->channelChange(VVideoLive::NUMBER, (*chanList)[chanListbox.getCurrentOption()]->number);
355
356       if(command == Remote::GO)\r
357         return 2;\r
358       // GO just changes channel in preview, PLAY changes channel and returns to normal TV\r
359     }\r
360     case Remote::BACK:\r
361     case Remote::GUIDE:\r
362     {\r
363       // return to normal TV mode\r
364       if (videoLive) // ptr check done in case being tested from videorec\r
365       {\r
366         Message* m = new Message(); // Must be done after this view deleted
367         m->from = this;
368         m->to = videoLive;
369         m->message = Message::EPG_CLOSE;
370         ViewMan::getInstance()->postMessage(m);
371       }
372       return 4;\r
373     }\r
374     case Remote::CHANNELUP:
375     {
376       videoLive->channelChange(VVideoLive::OFFSET, VVideoLive::UP);
377       return 2;
378     }
379     case Remote::CHANNELDOWN:
380     {
381       videoLive->channelChange(VVideoLive::OFFSET, VVideoLive::DOWN);
382       return 2;
383     }
384   }\r
385   // stop command getting to any more views\r
386   return 1;\r
387 }\r
388 \r
389 void VEpg::drawgrid() // redraws grid and select programme\r
390 {\r
391   // draw the grid of programmes\r
392   char timeString[20];\r
393   time_t t;\r
394   time(&t); // set t = now\r
395   if(selTime < t)\r
396     selTime = t; // don't allow cursor in the past\r
397   if(listTop != chanListbox.getTopOption())\r
398   {\r
399   // chanListbox has scrolled TODO speed up by changing only rows that have changed\r
400     listTop = chanListbox.getTopOption();\r
401     updateEventList();\r
402   }\r
403   if ((selTime >= ltime + WINDOW_WIDTH * 60) || (selTime <= ltime))\r
404   {\r
405   // we have cursored back before left time of window\r
406   //TODO check that this and above don't happen together\r
407     ltime = prevHour(&selTime);\r
408     updateEventList();\r
409   }\r
410   // draw time scale\r
411   t = ltime;\r
412   struct tm* tms;\r
413   tms = localtime(&t);\r
414   strftime(timeString, 19, "%a %e %b", tms);\r
415   int timey = chanListbox.getOffsetY() - Surface::getFontHeight() - 3;\r
416   int timex = 135;\r
417   drawTextRJ(timeString, timex - 10, timey, Colour::LIGHTTEXT); // print date\r
418   strftime(timeString, 19, "%H:%M", tms);\r
419   drawText(timeString, timex, timey, Colour::LIGHTTEXT); // print left time\r
420   surface->fillblt(155, timey + Surface::getFontHeight(), 2, 7, surface->rgba(255, 255, 255, 255));\r
421   t = t + 3600;\r
422   tms = localtime(&t);\r
423   strftime(timeString, 19, "%H:%M", tms);\r
424   drawText(timeString, timex + 180, timey, Colour::LIGHTTEXT); // print middle time\r
425   surface->fillblt(335, timey + Surface::getFontHeight(), 2, 7, surface->rgba(255, 255, 255, 255));\r
426   t = t + 3600;\r
427   tms = localtime(&t);\r
428   strftime(timeString, 19, "%H:%M", tms);\r
429   drawText(timeString, timex + 360, timey, Colour::LIGHTTEXT); // print right time\r
430   surface->fillblt(515, timey + Surface::getFontHeight(), 2, 7, surface->rgba(255, 255, 255, 255));\r
431   // pointer to selTime\r
432   surface->fillblt(155 + (selTime - ltime) / 20, timey + Surface::getFontHeight(), 2, 7, surface->rgba(255, 50, 50, 255));\r
433 \r
434   // TODO should the above two comditional statements be combined to avoid calling updateEventList() twice?\r
435   Event* event;\r
436   Event noevent; // an event to use if there are gaps in the epg\r
437   thisEvent.setdescription(tr("There are no programme details available for this period"));\r
438   thisEvent.duration = WINDOW_WIDTH * 60;\r
439   thisEvent.time = ltime;\r
440   thisEvent.settitle(tr("No programme details"));\r
441   thisEvent.id = 0;\r
442   bool swapColour = FALSE; // alternate cell colour\r
443   bool currentRow = FALSE;\r
444   int y = chanListbox.getOffsetY() + 5; // vertical position of cell\r
445   Colour bg, fg; // background colour of cells in grid\r
446   // for each displayed channel, find programmes that fall in 2.5 hour time window\r
447   for(UINT listIndex = 0; listIndex < gridRows; listIndex++)\r
448   {\r
449     if (listTop + (int)listIndex >= chanListbox.getBottomOption())\r
450       continue; // ensure nothing populates grid below last channel\r
451     currentRow = (listTop + (int)listIndex == chanListbox.getCurrentOption());\r
452     noevent.time = ltime;\r
453     noevent.duration = WINDOW_WIDTH * 60;\r
454     noevent.settitle("");\r
455     paintCell(&noevent, y, Colour::NOPROGRAMME, Colour::LIGHTTEXT); // fill row with no programme colour to be painted ove with valid programmes\r
456     if (currentRow)\r
457     {\r
458       thisEvent.setdescription(tr("There are no programme details available for this period"));\r
459       thisEvent.duration = WINDOW_WIDTH * 60;\r
460       thisEvent.time = ltime;\r
461       thisEvent.settitle(tr("No programme details"));\r
462       thisEvent.id = 0;\r
463     }\r
464     if (eventLista[listIndex])\r
465     {\r
466       sort(eventLista[listIndex]->begin(), eventLista[listIndex]->end(), EventSorter());\r
467       for(e = 0; e < (eventLista[listIndex])->size(); e++) // step through events for this channel\r
468       {\r
469         fg = Colour::LIGHTTEXT;\r
470         event = (*eventLista[listIndex])[e];\r
471         if (event)\r
472         {\r
473           UINT end = event->time + event->duration; // programme end time\r
474           if(event->time >= UINT(ltime) + (WINDOW_WIDTH * 60)) // programme starts after RHS of window\r
475             continue; // that's enough of this channel's events\r
476           if(end <= UINT(ltime)) // programme ends before LHS of window\r
477             continue; // this event is before the window - let's try the next event\r
478           // this event is one we are interested in\r
479           bg = (swapColour)?Colour::PROGRAMMEA:Colour::PROGRAMMEB; // alternate cell colour\r
480           swapColour = !swapColour; // it wil be the other colour next time\r
481           if(event->time <= UINT(selTime) && end > UINT(selTime) && currentRow)\r
482           {\r
483             // this is the selected programme\r
484             thisEvent.setdescription(event->description);\r
485             thisEvent.duration = event->duration;\r
486             thisEvent.time = event->time;\r
487             thisEvent.settitle(event->title);\r
488             thisEvent.id = event->id;\r
489             if(thisEvent.id == 0)\r
490               thisEvent.id = 1;\r
491             bg = Colour::SELECTHIGHLIGHT; // highlight cell\r
492             fg = Colour::DARKTEXT;\r
493           }\r
494           else\r
495           {\r
496             if (currentRow && thisEvent.id == 0)\r
497             {\r
498               if (end <= UINT(selTime) && end > UINT(thisEvent.time))\r
499                 thisEvent.time = end;\r
500               if (event->time > UINT(selTime) && event->time < thisEvent.time + thisEvent.duration)\r
501                 thisEvent.duration = event->time - thisEvent.time;\r
502             }\r
503           }\r
504           paintCell(event, y, bg, fg);\r
505         }\r
506       }\r
507     }\r
508     else\r
509     {\r
510       // no event list for this channel. Already painted noevent colour so just highlight if selected\r
511       if (currentRow)\r
512       {\r
513         bg = Colour::SELECTHIGHLIGHT; // highlight cell\r
514         fg = Colour::DARKTEXT;\r
515         paintCell(&thisEvent, y, bg, fg);\r
516       }\r
517       else\r
518       {\r
519         bg = Colour::NOPROGRAMME;\r
520         fg = Colour::LIGHTTEXT;\r
521         noevent.settitle(tr("No programme details"));\r
522         paintCell(&noevent, y, bg, fg);\r
523       }\r
524     }\r
525     y += Surface::getFontHeight() + 2;\r
526   }\r
527   setInfo(&thisEvent);\r
528 }\r
529 \r
530 void VEpg::updateEventList()\r
531 {\r
532   Channel* chan;\r
533   for(UINT listIndex = 0; listIndex < gridRows; listIndex++)\r
534   {\r
535     if (eventLista[listIndex])\r
536     {\r
537       if ((eventLista[listIndex])->empty())\r
538         (eventLista)[listIndex]->clear();\r
539     }\r
540     if(listTop + listIndex >= UINT(chanListbox.getBottomOption()))\r
541       continue;\r
542     chan = (*chanList)[listTop + listIndex];\r
543     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
544   }\r
545 }\r
546 \r
547 void VEpg::setCurrentChannel(char* chname)\r
548 {\r
549   chanName.setText(chname);\r
550   chanName.draw();\r
551   viewman->updateView(this);\r
552 }\r
553 \r
554 void VEpg::paintCell(Event* event, int yOffset, Colour bg, Colour fg)\r
555 {\r
556   int w, x, y, h;\r
557   w = x = 0; // keep compiler happy
558
559   y =yOffset;\r
560   h = Surface::getFontHeight(); // TODO if want border around text, need to increae this and wselectlist line height\r
561   UINT end = event->time + event->duration; // programme end time\r
562   if(event->time <= UINT(ltime) && end > UINT(ltime)) // spans start of displayed window\r
563   {\r
564     x = 155; // LHS of window\r
565     if (end > (UINT(ltime) + (WINDOW_WIDTH * 60)))\r
566       w = WINDOW_WIDTH * MINUTE_SCALE; // spans full 2 hour window\r
567     else\r
568       w = MINUTE_SCALE * (event->time + event->duration - ltime ) / 60; // get width of remaining programme\r
569   }\r
570   if((event->time >= UINT(ltime)) && (event->time <= UINT(ltime) + (WINDOW_WIDTH * 60))) // starts within window\r
571   {\r
572     x = 155 + (MINUTE_SCALE * (event->time - ltime) / 60);\r
573     w = MINUTE_SCALE * event->duration / 60;\r
574     //if (w > 155 + MINUTE_SCALE * WINDOW_WIDTH -x)\r
575      // w = w + x - 155 - MINUTE_SCALE * WINDOW_WIDTH; // ends outside window\r
576   }\r
577   if (w > 155 + WINDOW_WIDTH * MINUTE_SCALE - x)\r
578     w = 155 + WINDOW_WIDTH * MINUTE_SCALE -x; // limit cells to RHS of window\r
579   surface->fillblt(x, y, w, h, surface->rgba(bg.red, bg.green, bg.blue, bg.alpha));\r
580   char* tt = new char[strlen(event->title) + 1];\r
581   strcpy (tt, event->title);\r
582   int textWidth = 0;\r
583   for (UINT textPos = 0; textPos < strlen(tt); textPos++)\r
584   {\r
585     int thisCharWidth = surface->getCharWidth(tt[textPos]);\r
586     if (textWidth + thisCharWidth > w) // text will not fit in cell\r
587     {\r
588       textWidth = textPos;\r
589       break;\r
590     }\r
591     textWidth += thisCharWidth;\r
592   }\r
593   char* tT = new char[textWidth];\r
594   if(textWidth > 1)\r
595   {\r
596     strncpy(tT, tt, textWidth - 1);\r
597     tT[textWidth - 1] =  '\0';\r
598     surface->drawText(tT, x+2, y, fg.red, fg.green, fg.blue);\r
599   }\r
600   delete tT;\r
601 \r
602 }\r
603 \r
604 time_t VEpg::prevHour(time_t* t)\r
605 {\r
606   struct tm* tms;\r
607   tms = localtime(t);\r
608   tms->tm_sec = 0;\r
609   tms->tm_min = 0;\r
610   return mktime(tms);\r
611 }\r
612 \r