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