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