]> git.vomp.tv Git - vompclient.git/blob - vepg.cc
Internationalise EPG, add timed input code to vchannelselect
[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(tr("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(tr("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(tr("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(tr("-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(tr("+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(tr("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(tr("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(tr("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(tr("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\r
301       videoLive->setEpgMode(FALSE);\r
302       return 4;\r
303     }\r
304     case Remote::CHANNELUP:\r
305     {\r
306     // change up channel on live TV\r
307       Message* m = new Message();\r
308       m->from = this;\r
309       m->to = videoLive;\r
310       m->message = Message::CHANNEL_UP;\r
311       ViewMan::getInstance()->postMessage(m);\r
312       return 2;\r
313     }\r
314     case Remote::CHANNELDOWN:\r
315     { // change down channel on live TV\r
316       Message* m = new Message();\r
317       m->from = this;\r
318       m->to = videoLive;\r
319       m->message = Message::CHANNEL_DOWN;\r
320       ViewMan::getInstance()->postMessage(m);\r
321       return 2;\r
322     }\r
323   }\r
324   // stop command getting to any more views\r
325   return 1;\r
326 }\r
327 \r
328 void VEpg::drawgrid() // redraws grid and select programme\r
329 {\r
330 // draw the grid of programmes\r
331   char timeString[20];\r
332   time_t t;\r
333   time(&t); // set t = now\r
334   if(selTime < t)\r
335     selTime = t; // don't allow cursor in the past\r
336   if(listTop != chanListbox.getTopOption())\r
337   {\r
338   // chanListbox has scrolled TODO speed up by changing only rows that have changed\r
339     listTop = chanListbox.getTopOption();\r
340     updateEventList();\r
341   }\r
342   if ((selTime >= ltime + WINDOW_WIDTH * 60) || (selTime <= ltime))\r
343   {\r
344   // we have cursored back before left time of window\r
345   //TODO check that this and above don't happen together\r
346     ltime = prevHour(&selTime);\r
347     updateEventList();\r
348   }\r
349   // draw time scale\r
350   t = ltime;\r
351   struct tm* tms;\r
352   tms = localtime(&t);\r
353   strftime(timeString, 19, "%a %e %b", tms);\r
354   int timey = chanListbox.getOffsetY() - Surface::getFontHeight() - 3;\r
355   int timex = 135;\r
356   drawTextRJ(timeString, timex - 10, timey, Colour::LIGHTTEXT); // print date\r
357   strftime(timeString, 19, "%H:%M", tms);\r
358   drawText(timeString, timex, timey, Colour::LIGHTTEXT); // print left time\r
359   surface->fillblt(155, timey + Surface::getFontHeight(), 2, 7, surface->rgba(255, 255, 255, 255));\r
360   t = t + 3600;\r
361   tms = localtime(&t);\r
362   strftime(timeString, 19, "%H:%M", tms);\r
363   drawText(timeString, timex + 180, timey, Colour::LIGHTTEXT); // print middle time\r
364   surface->fillblt(335, timey + Surface::getFontHeight(), 2, 7, surface->rgba(255, 255, 255, 255));\r
365   t = t + 3600;\r
366   tms = localtime(&t);\r
367   strftime(timeString, 19, "%H:%M", tms);\r
368   drawText(timeString, timex + 360, timey, Colour::LIGHTTEXT); // print right time\r
369   surface->fillblt(515, timey + Surface::getFontHeight(), 2, 7, surface->rgba(255, 255, 255, 255));\r
370   // pointer to selTime\r
371   surface->fillblt(155 + (selTime - ltime) / 20, timey + Surface::getFontHeight(), 2, 7, surface->rgba(255, 50, 50, 255));\r
372 \r
373   // TODO should the above two comditional statements be combined to avoid calling updateEventList() twice?\r
374   Event* event;\r
375   Event noevent; // an event to use if there are gaps in the epg\r
376   thisEvent.setdescription(tr("There are no programme details available for this period"));\r
377   thisEvent.duration = WINDOW_WIDTH * 60;\r
378   thisEvent.time = ltime;\r
379   thisEvent.settitle(tr("No programme details"));\r
380   thisEvent.id = 0;\r
381   bool swapColour = FALSE; // alternate cell colour\r
382   bool currentRow = FALSE;\r
383   int y = chanListbox.getOffsetY() + 5; // vertical position of cell\r
384   Colour bg, fg; // background colour of cells in grid\r
385   // for each displayed channel, find programmes that fall in 2.5 hour time window\r
386   for(int listIndex = 0; listIndex < 7; listIndex++)\r
387   {\r
388     if (listTop + listIndex >= chanListbox.getBottomOption())\r
389       continue; // ensure nothing populates grid below last channel\r
390     currentRow = (listTop + listIndex == chanListbox.getCurrentOption());\r
391     noevent.time = ltime;\r
392     noevent.duration = WINDOW_WIDTH * 60;\r
393     noevent.settitle("");\r
394     paintCell(&noevent, y, Colour::NOPROGRAMME, Colour::LIGHTTEXT); // fill row with no programme colour to be painted ove with valid programmes\r
395     if (currentRow)\r
396     {\r
397       thisEvent.setdescription(tr("There are no programme details available for this period"));\r
398       thisEvent.duration = WINDOW_WIDTH * 60;\r
399       thisEvent.time = ltime;\r
400       thisEvent.settitle(tr("No programme details"));\r
401       thisEvent.id = 0;\r
402     }\r
403     if (eventLista[listIndex])\r
404     {\r
405       sort(eventLista[listIndex]->begin(), eventLista[listIndex]->end(), EventSorter());\r
406       for(e = 0; e < (eventLista[listIndex])->size(); e++) // step through events for this channel\r
407       {\r
408         fg = Colour::LIGHTTEXT;\r
409         event = (*eventLista[listIndex])[e];\r
410         if (event)\r
411         {\r
412           UINT end = event->time + event->duration; // programme end time\r
413           if(event->time >= UINT(ltime) + (WINDOW_WIDTH * 60)) // programme starts after RHS of window\r
414             continue; // that's enough of this channel's events\r
415           if(end <= UINT(ltime)) // programme ends before LHS of window\r
416             continue; // this event is before the window - let's try the next event\r
417           // this event is one we are interested in\r
418           bg = (swapColour)?Colour::PROGRAMMEA:Colour::PROGRAMMEB; // alternate cell colour\r
419           swapColour = !swapColour; // it wil be the other colour next time\r
420           if(event->time <= UINT(selTime) && end > UINT(selTime) && currentRow)\r
421           {\r
422             // this is the selected programme\r
423             thisEvent.setdescription(event->description);\r
424             thisEvent.duration = event->duration;\r
425             thisEvent.time = event->time;\r
426             thisEvent.settitle(event->title);\r
427             thisEvent.id = event->id;\r
428             if(thisEvent.id == 0)\r
429               thisEvent.id = 1;\r
430             bg = Colour::SELECTHIGHLIGHT; // highlight cell\r
431             fg = Colour::DARKTEXT;\r
432           }\r
433           else\r
434           {\r
435             if (currentRow && thisEvent.id == 0)\r
436             {\r
437               if (end <= UINT(selTime) && end > UINT(thisEvent.time))\r
438                 thisEvent.time = end;\r
439               if (event->time > UINT(selTime) && event->time < thisEvent.time + thisEvent.duration)\r
440                 thisEvent.duration = event->time - thisEvent.time;\r
441             }\r
442           }\r
443           paintCell(event, y, bg, fg);\r
444         }\r
445       }\r
446     }\r
447     else\r
448     {\r
449       // no event list for this channel. Already painted noevent colour so just highlight if selected\r
450       if (currentRow)\r
451       {\r
452         bg = Colour::SELECTHIGHLIGHT; // highlight cell\r
453         fg = Colour::DARKTEXT;\r
454         paintCell(&thisEvent, y, bg, fg);\r
455       }\r
456       else\r
457       {\r
458         bg = Colour::NOPROGRAMME;\r
459         fg = Colour::LIGHTTEXT;\r
460         noevent.settitle(tr("No programme details"));\r
461         paintCell(&noevent, y, bg, fg);\r
462       }\r
463     }\r
464     y += Surface::getFontHeight() + 1;\r
465   }\r
466   setInfo(&thisEvent);\r
467 }\r
468 \r
469 void VEpg::updateEventList()\r
470 {\r
471   Channel* chan;\r
472   for(UINT listIndex = 0; listIndex < 7; listIndex++)\r
473   {\r
474     if (eventLista[listIndex])\r
475     {\r
476       if ((eventLista[listIndex])->empty())\r
477         (eventLista)[listIndex]->clear();\r
478     }\r
479     if(listTop + listIndex >= UINT(chanListbox.getBottomOption()))\r
480       continue;\r
481     chan = (*chanList)[listTop + listIndex];\r
482     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
483   }\r
484 }\r
485 \r
486 void VEpg::setCurrentChannel(char* chname)\r
487 {\r
488   chanName.setText(chname);\r
489   chanName.draw();\r
490   show();\r
491 }\r
492 \r
493 void VEpg::paintCell(Event* event, int yOffset, Colour bg, Colour fg)\r
494 {\r
495   int w, x, y, h;\r
496   y =yOffset;\r
497   h = Surface::getFontHeight(); // TODO if want border around text, need to increae this and wselectlist line height\r
498   UINT end = event->time + event->duration; // programme end time\r
499   if(event->time <= UINT(ltime) && end > UINT(ltime)) // spans start of displayed window\r
500   {\r
501     x = 155; // LHS of window\r
502     if (end > (UINT(ltime) + (WINDOW_WIDTH * 60)))\r
503       w = WINDOW_WIDTH * MINUTE_SCALE; // spans full 2 hour window\r
504     else\r
505       w = MINUTE_SCALE * (event->time + event->duration - ltime ) / 60; // get width of remaining programme\r
506   }\r
507   if((event->time >= UINT(ltime)) && (event->time <= UINT(ltime) + (WINDOW_WIDTH * 60))) // starts within window\r
508   {\r
509     x = 155 + (MINUTE_SCALE * (event->time - ltime) / 60);\r
510     w = MINUTE_SCALE * event->duration / 60;\r
511     //if (w > 155 + MINUTE_SCALE * WINDOW_WIDTH -x)\r
512      // w = w + x - 155 - MINUTE_SCALE * WINDOW_WIDTH; // ends outside window\r
513   }\r
514   if (w > 155 + WINDOW_WIDTH * MINUTE_SCALE - x)\r
515     w = 155 + WINDOW_WIDTH * MINUTE_SCALE -x; // limit cells to RHS of window\r
516   surface->fillblt(x, y, w, h, surface->rgba(bg.red, bg.green, bg.blue, bg.alpha));\r
517   char* tt = new char[strlen(event->title) + 1];\r
518   strcpy (tt, event->title);\r
519   int textWidth = 0;\r
520   for (UINT textPos = 0; textPos < strlen(tt); textPos++)\r
521   {\r
522     int thisCharWidth = surface->getCharWidth(tt[textPos]);\r
523     if (textWidth + thisCharWidth > w) // text will not fit in cell\r
524     {\r
525       textWidth = textPos;\r
526       break;\r
527     }\r
528     textWidth += thisCharWidth;\r
529   }\r
530   char* tT = new char[textWidth];\r
531   if(textWidth > 1)\r
532   {\r
533     strncpy(tT, tt, textWidth - 1);\r
534     tT[textWidth - 1] =  '\0';\r
535     surface->drawText(tT, x+2, y, fg.red, fg.green, fg.blue);\r
536   }\r
537   delete tT;\r
538 \r
539 }\r
540 \r
541 time_t VEpg::prevHour(time_t* t)\r
542 {\r
543   struct tm* tms;\r
544   tms = localtime(t);\r
545   tms->tm_sec = 0;\r
546   tms->tm_min = 0;\r
547   return mktime(tms);\r
548 }\r
549 \r