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