]> git.vomp.tv Git - vompclient.git/blob - vepg.cc
Move EPG to the right a bit, timers fixes and improvements
[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 \r
36 VEpg::VEpg(VVideoLive* v, UINT currentChannel)\r
37 {\r
38   for(UINT listIndex = 0; listIndex < 7; listIndex++)\r
39   {\r
40     // initialise array of pointers to eventlist structures\r
41     eventLista[listIndex] = NULL;\r
42   }\r
43 \r
44 \r
45 // Create pallet on which to paint our epg view and position it in centre of screen.\r
46 // Need to reduce size to deal with overscanning TVs.\r
47   videoLive = v;\r
48 //  create(640, 530);\r
49 //TODO: have size for ntsc\r
50   if (Video::getInstance()->getFormat() == Video::PAL)\r
51   {\r
52     create(632, 520);\r
53     setScreenPos(60, 30);\r
54   }\r
55   else\r
56   {\r
57   // what can we do about poor ntsc?\r
58     create(512, 384);\r
59     setScreenPos(64, 51);\r
60   }\r
61 \r
62 // beautify\r
63   Colour transparent = Colour(0, 0, 0, 0);\r
64   setBackgroundColour(transparent);\r
65 // initialise variables and pointers\r
66   eventList = NULL;\r
67   chanList = VDR::getInstance()->getChannelsList(VDR::VIDEO); //TODO want to be able to display video and radio together\r
68   e = 0;\r
69   progTitle.setSurface(surface);\r
70   progTitle.setSurfaceOffset(0,0);\r
71   progTitle.setDimensions(300,(Surface::getFontHeight() + 6) * 2 + 2); //paragraph line seperation is 6 pixels\r
72   progTitle.setBackgroundColour(Colour::TITLEBARBACKGROUND);\r
73   progInfo.setSurface(surface);\r
74   progInfo.setSurfaceOffset(0, progTitle.getOffsetY() + progTitle.getHeight());\r
75   progInfo.setDimensions(300,(Surface::getFontHeight() + 6) * 8 + 2);\r
76   chanName.setSurface(surface);\r
77   chanName.setDimensions(510, (Surface::getFontHeight() + 4));\r
78   chanName.setSurfaceOffset(305,230);\r
79   chanName.setBackgroundColour(Colour(0, 0, 0, 90));\r
80 // create area to display list of channels\r
81   chanListbox.setSurface(surface); // add channel list\r
82   chanListbox.setSurfaceOffset(0, progInfo.getOffsetY() + progInfo.getHeight() + Surface::getFontHeight() + 8); // position channel list\r
83   chanListbox.setDimensions(150, (Surface::getFontHeight() + 1) * 7 + 5); //listbox line seperation is 1 pixel\r
84 // populate channel list\r
85   Channel* chan;\r
86   int first = 1;\r
87   for (UINT i = 0; i < chanList->size(); i++)\r
88   {\r
89     chan = (*chanList)[i];\r
90     if (i == currentChannel)\r
91       first = 1;\r
92     chan->index = chanListbox.addOption(chan->name, first);\r
93     first = 0;\r
94   }\r
95   listTop = chanListbox.getTopOption();\r
96   chanListbox.draw(); // doing this to allow chanListbox.getBottomOption() in updateEventList() to work\r
97   chanName.setText((*chanList)[chanListbox.getCurrentOption()]->name);\r
98   time(&ltime); // set ltime to now\r
99   ltime = prevHour(&ltime); // set ltime to previous hour TODO make this half hour?\r
100   time(&selTime); // set selTime to now\r
101   updateEventList(); // get list of programmes\r
102 }\r
103 \r
104 VEpg::~VEpg()\r
105 {\r
106    for(int listIndex = 0; listIndex < 7; listIndex++)\r
107    {\r
108      if (eventLista[listIndex])\r
109      {\r
110        (eventLista)[listIndex]->clear();\r
111        delete eventLista[listIndex];\r
112      }\r
113    }\r
114 //  delete [] eventLista;\r
115 \r
116 // destroy dynamically allocated memory\r
117 }\r
118 \r
119 void VEpg::setInfo(Event* event)\r
120 {\r
121   time_t t;\r
122   struct tm* btime; // to hold programme start and end time\r
123   char* timeString = new char[9]; // to hold programme start and end time\r
124   int length = strlen(event->title); // calculate length of programme title string\r
125   char* title = new char[length + 15]; // create string to hold start time, end time and programme title\r
126   btime = localtime((time_t*)&event->time); //get programme start time\r
127   strftime(timeString, 9, "%0H:%0M - ", btime); // and format it as hh:mm -\r
128   strcpy(title, timeString); // put it in our buffer\r
129   t = event->time + event->duration; //get programme end time\r
130   btime = localtime(&t);\r
131   strftime(timeString, 7, "%0H:%0M ", btime); // and format it as hh:mm -\r
132   strcat(title, timeString); // put it in our buffer\r
133   strcat(title, event->title); // then add the programme title\r
134   progTitle.setText(title); // sput this sring in our text box\r
135   length = strlen(event->description);\r
136   char* info = new char[length + 1]; // create programme detail string\r
137   strcpy(info, event->description);\r
138   progInfo.setText(info); // show programme detail string\r
139 // destroy dynamically allocated memory\r
140   delete info;\r
141   delete title;\r
142   delete timeString;\r
143 }\r
144 \r
145 void VEpg::draw()\r
146 {\r
147   View::draw(); // draw pallet\r
148   chanListbox.draw();\r
149   drawgrid();\r
150   chanName.draw(); // TODO this should be dealt with by vvideolive\r
151   progTitle.draw();\r
152   progInfo.draw();\r
153 \r
154 // Display the status and key stuff at the bottom\r
155   int keyx = chanListbox.getOffsetX();\r
156   int keyy = chanListbox.getOffsetY() + chanListbox.getHeight() + 2;\r
157   surface->fillblt(keyx, keyy, 610, Surface::getFontHeight() * 2 + 14, surface->rgba(100, 100, 100, 255));\r
158 \r
159   WSymbol w;\r
160   w.setSurface(surface);\r
161 \r
162   w.nextSymbol = WSymbol::LEFTARROW;\r
163   w.setSurfaceOffset(keyx + 1, keyy + 20);\r
164   w.draw();\r
165 \r
166   w.nextSymbol = WSymbol::UP;\r
167   w.setSurfaceOffset(keyx + 26, keyy + 3);\r
168   w.draw();\r
169 \r
170   w.nextSymbol = WSymbol::DOWN;\r
171   w.setSurfaceOffset(keyx + 26, keyy + 36);\r
172   w.draw();\r
173 \r
174   w.nextSymbol = WSymbol::RIGHTARROW;\r
175   w.setSurfaceOffset(keyx + 50, keyy + 20);\r
176   w.draw();\r
177 \r
178   drawText("OK", keyx + 18, keyy + 20, Colour::LIGHTTEXT);\r
179 \r
180   surface->fillblt(keyx + 72, keyy + 4, 104, Surface::getFontHeight() + 2, surface->rgba(200, 0, 0, 255));\r
181   drawText("Page up", keyx + 74, keyy + 5, Colour::LIGHTTEXT);\r
182 \r
183   surface->fillblt(keyx + 72, keyy + Surface::getFontHeight() + 8, 104, Surface::getFontHeight() + 2, surface->rgba(0, 200, 0, 255));\r
184   drawText("Page down", keyx + 74, keyy + Surface::getFontHeight() + 9, Colour::LIGHTTEXT);\r
185 \r
186   surface->fillblt(keyx + 180, keyy + 4, 104, Surface::getFontHeight() + 2, surface->rgba(200, 200, 0, 255));\r
187   drawText("-24 hours", keyx + 182, keyy + 5, Colour::LIGHTTEXT);\r
188 \r
189   surface->fillblt(keyx + 180, keyy + Surface::getFontHeight() + 8, 104, Surface::getFontHeight() + 2, surface->rgba( 0, 0, 200, 255));\r
190   drawText("+24 hours", keyx + 182, keyy + Surface::getFontHeight() + 9, Colour::LIGHTTEXT);\r
191 \r
192   surface->fillblt(keyx + 290, keyy + 4, 180, Surface::getFontHeight() + 2, surface->rgba( 180, 180, 180, 255));\r
193   drawText("Guide / Back: Close", keyx + 292 , keyy + 5, Colour::LIGHTTEXT);\r
194 \r
195   surface->fillblt(keyx + 290, keyy + Surface::getFontHeight() + 8, 180, Surface::getFontHeight() + 2, surface->rgba( 180, 180, 180, 255));\r
196   Colour red = Colour(130, 0, 0);\r
197   drawText("Rec: Set timer", keyx + 292, keyy + Surface::getFontHeight() + 9, red);\r
198 \r
199   surface->fillblt(keyx + 474, keyy + 4, 128, Surface::getFontHeight() + 2, surface->rgba( 180, 180, 180, 255));\r
200   w.nextSymbol = WSymbol::PLAY;\r
201   w.setSurfaceOffset(keyx + 476, keyy + 5);\r
202   w.draw();\r
203   drawText("Sel channel", keyx + 496, keyy + 5, Colour::LIGHTTEXT);\r
204 \r
205   surface->fillblt(keyx + 474, keyy + Surface::getFontHeight() + 8, 128, Surface::getFontHeight() + 2, surface->rgba( 180, 180, 180, 255));\r
206   drawText("Go: Preview", keyx + 476, keyy + Surface::getFontHeight() + 9, Colour::LIGHTTEXT);\r
207 }\r
208 \r
209 int VEpg::handleCommand(int command)\r
210 {\r
211   switch(command)\r
212   {\r
213     case Remote::DF_UP:\r
214     case Remote::UP:\r
215     { // cursor up the channel list\r
216       chanListbox.up();\r
217       draw();\r
218       show();\r
219       return 2;\r
220     }\r
221     case Remote::DF_DOWN:\r
222     case Remote::DOWN:\r
223     { // cursor down the channel list\r
224       chanListbox.down();\r
225       draw();\r
226       show();\r
227       return 2;\r
228     }\r
229     case Remote::DF_LEFT:\r
230     case Remote::LEFT:\r
231     { // cursor left through time\r
232       selTime = thisEvent.time - 1;\r
233       draw();\r
234       show();\r
235       return 2;\r
236     }\r
237     case Remote::DF_RIGHT:\r
238     case Remote::RIGHT:\r
239     {\r
240     // cursor right through time\r
241       selTime = thisEvent.time + thisEvent.duration;\r
242       draw();\r
243       show();\r
244       return 2;\r
245     }\r
246     case Remote::RED:\r
247     {\r
248     // cursor up one page\r
249       chanListbox.pageUp();\r
250       draw();\r
251       show();\r
252       return 2;\r
253     }\r
254     case Remote::GREEN:\r
255     {\r
256     // cursor down one page\r
257       chanListbox.pageDown();\r
258       draw();\r
259       show();\r
260       return 2;\r
261     }\r
262     case Remote::BLUE:\r
263     {\r
264     // step forward 24 hours\r
265       selTime += 24 * 60 * 60;\r
266       draw();\r
267       show();\r
268       return 2;\r
269     }\r
270     case Remote::YELLOW:\r
271     {\r
272     // step forward 24 hours\r
273       selTime -= 24 * 60 * 60;\r
274       draw();\r
275       show();\r
276       return 2;\r
277     }\r
278     case Remote::RECORD:\r
279     {\r
280       //TODO\r
281       return 2;\r
282     }\r
283     case Remote::PLAY:\r
284     case Remote::GO:\r
285     case Remote::OK:\r
286     { // select programme and display menu TODO currently just changes to selected channel\r
287       Message* m = new Message();\r
288       m->from = this;\r
289       m->to = videoLive;\r
290       m->message = Message::CHANNEL_CHANGE;\r
291       m->parameter = (*chanList)[chanListbox.getCurrentOption()]->number;\r
292       ViewMan::getInstance()->postMessage(m);\r
293       if(command == Remote::GO)\r
294         return 2;\r
295       // GO just changes channel in preview, PLAY changes channel and returns to normal TV\r
296     }\r
297     case Remote::BACK:\r
298     case Remote::GUIDE:\r
299     {\r
300     // return to normal TV mode TODO vvideolive should handle this (via message?)\r
301       Video::getInstance()->setMode(Video::NORMAL);\r
302       videoLive->setEpgMode(FALSE);\r
303       return 4;\r
304     }\r
305     case Remote::CHANNELUP:\r
306     {\r
307     // change up channel on live TV TODO vvideolive should handle this\r
308       Message* m = new Message();\r
309       m->from = this;\r
310       m->to = videoLive;\r
311       m->message = Message::CHANNEL_UP;\r
312       ViewMan::getInstance()->postMessage(m);\r
313       return 2;\r
314     }\r
315     case Remote::CHANNELDOWN:\r
316     { // change down channel on live TV TODO vvideolive should handle this\r
317       Message* m = new Message();\r
318       m->from = this;\r
319       m->to = videoLive;\r
320       m->message = Message::CHANNEL_DOWN;\r
321       ViewMan::getInstance()->postMessage(m);\r
322       return 2;\r
323     }\r
324   }\r
325   // stop command getting to any more views\r
326   return 1;\r
327 }\r
328 \r
329 void VEpg::drawgrid() // redraws grid and select programme\r
330 {\r
331 // draw the grid of programmes\r
332   char timeString[20];\r
333   time_t t;\r
334   time(&t); // set t = now\r
335   if(selTime < t)\r
336     selTime = t; // don't allow cursor in the past\r
337   if(listTop != chanListbox.getTopOption())\r
338   {\r
339   // chanListbox has scrolled TODO speed up by changing only rows that have changed\r
340     listTop = chanListbox.getTopOption();\r
341     updateEventList();\r
342   }\r
343   if ((selTime >= ltime + WINDOW_WIDTH * 60) || (selTime <= ltime))\r
344   {\r
345   // we have cursored back before left time of window\r
346   //TODO check that this and above don't happen together\r
347     ltime = prevHour(&selTime);\r
348     updateEventList();\r
349   }\r
350   // draw time scale\r
351   t = ltime;\r
352   struct tm* tms;\r
353   tms = localtime(&t);\r
354   strftime(timeString, 19, "%a %e %b", tms);\r
355   int timey = chanListbox.getOffsetY() - Surface::getFontHeight() - 3;\r
356   int timex = 135;\r
357   drawTextRJ(timeString, timex - 10, timey, Colour::LIGHTTEXT); // print date\r
358   strftime(timeString, 19, "%H:%M", tms);\r
359   drawText(timeString, timex, timey, Colour::LIGHTTEXT); // print left time\r
360   surface->fillblt(155, timey + Surface::getFontHeight(), 2, 7, surface->rgba(255, 255, 255, 255));\r
361   t = t + 3600;\r
362   tms = localtime(&t);\r
363   strftime(timeString, 19, "%H:%M", tms);\r
364   drawText(timeString, timex + 180, timey, Colour::LIGHTTEXT); // print middle time\r
365   surface->fillblt(335, timey + Surface::getFontHeight(), 2, 7, surface->rgba(255, 255, 255, 255));\r
366   t = t + 3600;\r
367   tms = localtime(&t);\r
368   strftime(timeString, 19, "%H:%M", tms);\r
369   drawText(timeString, timex + 360, timey, Colour::LIGHTTEXT); // print right time\r
370   surface->fillblt(515, timey + Surface::getFontHeight(), 2, 7, surface->rgba(255, 255, 255, 255));\r
371   // pointer to selTime\r
372   surface->fillblt(155 + (selTime - ltime) / 20, timey + Surface::getFontHeight(), 2, 7, surface->rgba(255, 50, 50, 255));\r
373 \r
374   // TODO should the above two comditional statements be combined to avoid calling updateEventList() twice?\r
375   Event* event;\r
376   Event noevent; // an event to use if there are gaps in the epg\r
377   thisEvent.setdescription("There are no programme details available for this period");\r
378   thisEvent.duration = WINDOW_WIDTH * 60;\r
379   thisEvent.time = ltime;\r
380   thisEvent.settitle("No programme details");\r
381   thisEvent.id = 0;\r
382   bool swapColour = FALSE; // alternate cell colour\r
383   bool currentRow = FALSE;\r
384   int y = chanListbox.getOffsetY() + 5; // vertical position of cell\r
385   Colour bg, fg; // background colour of cells in grid\r
386   // for each displayed channel, find programmes that fall in 2.5 hour time window\r
387   for(int listIndex = 0; listIndex < 7; listIndex++)\r
388   {\r
389     if (listTop + listIndex >= chanListbox.getBottomOption())\r
390       continue; // ensure nothing populates grid below last channel\r
391     currentRow = (listTop + listIndex == chanListbox.getCurrentOption());\r
392     noevent.time = ltime;\r
393     noevent.duration = WINDOW_WIDTH * 60;\r
394     noevent.settitle("");\r
395     paintCell(&noevent, y, Colour::NOPROGRAMME, Colour::LIGHTTEXT); // fill row with no programme colour to be painted ove with valid programmes\r
396     if (currentRow)\r
397     {\r
398       thisEvent.setdescription("There are no programme details available for this period");\r
399       thisEvent.duration = WINDOW_WIDTH * 60;\r
400       thisEvent.time = ltime;\r
401       thisEvent.settitle("No programme details");\r
402       thisEvent.id = 0;\r
403     }\r
404     if (eventLista[listIndex])\r
405     {\r
406       sort(eventLista[listIndex]->begin(), eventLista[listIndex]->end(), EventSorter());\r
407       for(e = 0; e < (eventLista[listIndex])->size(); e++) // step through events for this channel\r
408       {\r
409   fg = Colour::LIGHTTEXT;\r
410         event = (*eventLista[listIndex])[e];\r
411   if (event)\r
412   {\r
413     UINT end = event->time + event->duration; // programme end time\r
414     if(event->time >= UINT(ltime) + (WINDOW_WIDTH * 60)) // programme starts after RHS of window\r
415       continue; // that's enough of this channel's events\r
416     if(end <= UINT(ltime)) // programme ends before LHS of window\r
417       continue; // this event is before the window - let's try the next event\r
418     // this event is one we are interested in\r
419     bg = (swapColour)?Colour::PROGRAMMEA:Colour::PROGRAMMEB; // alternate cell colour\r
420     swapColour = !swapColour; // it wil be the other colour next time\r
421     if(event->time <= UINT(selTime) && end > UINT(selTime) && currentRow)\r
422     {\r
423     // this is the selected programme\r
424       thisEvent.setdescription(event->description);\r
425       thisEvent.duration = event->duration;\r
426       thisEvent.time = event->time;\r
427       thisEvent.settitle(event->title);\r
428       thisEvent.id = event->id;\r
429       if(thisEvent.id == 0)\r
430         thisEvent.id = 1;\r
431       bg = Colour::SELECTHIGHLIGHT; // highlight cell\r
432       fg = Colour::DARKTEXT;\r
433     }\r
434     else\r
435     {\r
436       if (currentRow && thisEvent.id == 0)\r
437       {\r
438         if (end <= UINT(selTime) && end > UINT(thisEvent.time))\r
439           thisEvent.time = end;\r
440         if (event->time > UINT(selTime) && event->time < thisEvent.time + thisEvent.duration)\r
441           thisEvent.duration = event->time - thisEvent.time;\r
442       }\r
443     }\r
444     paintCell(event, y, bg, fg);\r
445   }\r
446       }\r
447     }\r
448     else\r
449     {\r
450     // no event list for this channel. Already painted noevent colour so just highlight if selected\r
451       if (currentRow)\r
452       {\r
453         bg = Colour::SELECTHIGHLIGHT; // highlight cell\r
454         fg = Colour::DARKTEXT;\r
455         paintCell(&thisEvent, y, bg, fg);\r
456       }\r
457       else\r
458       {\r
459         bg = Colour::NOPROGRAMME;\r
460   fg = Colour::LIGHTTEXT;\r
461         noevent.settitle("No programme details");\r
462   paintCell(&noevent, y, bg, fg);\r
463       }\r
464     }\r
465     y += Surface::getFontHeight() + 1;\r
466   }\r
467   setInfo(&thisEvent);\r
468 }\r
469 \r
470 void VEpg::updateEventList()\r
471 {\r
472   Channel* chan;\r
473   for(UINT listIndex = 0; listIndex < 7; listIndex++)\r
474   {\r
475     if (eventLista[listIndex])\r
476     {\r
477       if ((eventLista[listIndex])->empty())\r
478         (eventLista)[listIndex]->clear();\r
479     }\r
480     if(listTop + listIndex >= UINT(chanListbox.getBottomOption()))\r
481       continue;\r
482     chan = (*chanList)[listTop + listIndex];\r
483     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
484   }\r
485 }\r
486 \r
487 void VEpg::setCurrentChannel(char* chname)\r
488 {\r
489   chanName.setText(chname);\r
490   chanName.draw();\r
491   show();\r
492 }\r
493 \r
494 void VEpg::paintCell(Event* event, int yOffset, Colour bg, Colour fg)\r
495 {\r
496   int w, x, y, h;\r
497   y =yOffset;\r
498   h = Surface::getFontHeight(); // TODO if want border around text, need to increae this and wselectlist line height\r
499   UINT end = event->time + event->duration; // programme end time\r
500   if(event->time <= UINT(ltime) && end > UINT(ltime)) // spans start of displayed window\r
501   {\r
502     x = 155; // LHS of window\r
503     if (end > (UINT(ltime) + (WINDOW_WIDTH * 60)))\r
504       w = WINDOW_WIDTH * MINUTE_SCALE; // spans full 2 hour window\r
505     else\r
506       w = MINUTE_SCALE * (event->time + event->duration - ltime ) / 60; // get width of remaining programme\r
507   }\r
508   if((event->time >= UINT(ltime)) && (event->time <= UINT(ltime) + (WINDOW_WIDTH * 60))) // starts within window\r
509   {\r
510     x = 155 + (MINUTE_SCALE * (event->time - ltime) / 60);\r
511     w = MINUTE_SCALE * event->duration / 60;\r
512     //if (w > 155 + MINUTE_SCALE * WINDOW_WIDTH -x)\r
513      // w = w + x - 155 - MINUTE_SCALE * WINDOW_WIDTH; // ends outside window\r
514   }\r
515   if (w > 155 + WINDOW_WIDTH * MINUTE_SCALE - x)\r
516     w = 155 + WINDOW_WIDTH * MINUTE_SCALE -x; // limit cells to RHS of window\r
517   surface->fillblt(x, y, w, h, surface->rgba(bg.red, bg.green, bg.blue, bg.alpha));\r
518   char* tt = new char[strlen(event->title) + 1];\r
519   strcpy (tt, event->title);\r
520   int textWidth = 0;\r
521   for (UINT textPos = 0; textPos < strlen(tt); textPos++)\r
522   {\r
523     int thisCharWidth = surface->getCharWidth(tt[textPos]);\r
524     if (textWidth + thisCharWidth > w) // text will not fit in cell\r
525     {\r
526       textWidth = textPos;\r
527       break;\r
528     }\r
529     textWidth += thisCharWidth;\r
530   }\r
531   char* tT = new char[textWidth];\r
532   if(textWidth > 1)\r
533   {\r
534     strncpy(tT, tt, textWidth - 1);\r
535     tT[textWidth - 1] =  '\0';\r
536     surface->drawText(tT, x+2, y, fg.red, fg.green, fg.blue);\r
537   }\r
538   delete tT;\r
539 \r
540 }\r
541 \r
542 time_t VEpg::prevHour(time_t* t)\r
543 {\r
544   struct tm* tms;\r
545   tms = localtime(t);\r
546   tms->tm_sec = 0;\r
547   tms->tm_min = 0;\r
548   return mktime(tms);\r
549 }\r
550 \r