]> git.vomp.tv Git - vompclient-marten.git/blob - vepg.cc
Minor cleanup changes
[vompclient-marten.git] / vepg.cc
1 /*
2     Copyright 2005 Brian Walton
3
4     This file is part of VOMP.
5
6     VOMP is free software; you can redistribute it and/or modify
7     it under the terms of the GNU General Public License as published by
8     the Free Software Foundation; either version 2 of the License, or
9     (at your option) any later version.
10
11     VOMP is distributed in the hope that it will be useful,
12     but WITHOUT ANY WARRANTY; without even the implied warranty of
13     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14     GNU General Public License for more details.
15
16     You should have received a copy of the GNU General Public License
17     along with VOMP; if not, write to the Free Software
18     Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
19 */
20 /*
21     vepg presents a 2 dimensional electronic programme guide with channels down
22     the y axis and time along the x axis.
23     Programmes are layed on the x axis as alterate coloured blocks with as much
24     of the programme title as will fit inside the block shown as text.
25     Up and down commands step through the channels whilst left and right commands
26     move through the programmes of the currently selected channel.
27     When a programme is selected, it highlights in the grid and full programe details
28     (start time, title and description) are displayed in an area at te top left of the screen.
29     Any currently programmed timers will display in the grid and in the orogramme detail window as red
30     It is possible to select a programme to be recorded by pressing the record button.
31     The video stream currently being viewed is shown as quarter screen in the top right.
32 */
33
34 #include "vepg.h"
35
36 VEpg* VEpg::instance = NULL;
37
38 VEpg::VEpg(VVideoLive* v, UINT currentChannel)
39 {
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();
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
80   for(UINT listIndex = 0; listIndex < gridRows; listIndex++)
81   {
82     // initialise array of pointers to eventlist structures
83     eventLista[listIndex] = NULL;
84   }
85
86   // Create pallet on which to paint our epg view and position it in centre of screen.
87   // Need to reduce size to deal with overscanning TVs.
88
89   create(xsize, ysize);
90   setScreenPos(xpos, ypos);
91
92   // beautify
93   Colour transparent = Colour(0, 0, 0, 0);
94   setBackgroundColour(transparent);
95
96   progTitle.setSurface(surface);
97   progTitle.setSurfaceOffset(0,0);
98   progTitle.setDimensions(300,(Surface::getFontHeight() + 4) * 2 + 16); //paragraph line seperation is 4 pixels
99   progTitle.setBackgroundColour(Colour::TITLEBARBACKGROUND);
100   progTitle.setTextPos(5, 16);
101   progTitle.setGap(4);
102
103   progInfo.setSurface(surface);
104   progInfo.setSurfaceOffset(0, progTitle.getOffsetY() + progTitle.getHeight());
105   progInfo.setDimensions(300,((Surface::getFontHeight() + 4) * summaryLines) + summaryLowerPadding);
106   progInfo.setGap(4);
107
108   chanName.setSurface(surface);
109   chanName.setDimensions(510, (Surface::getFontHeight() + 4));
110   chanName.setSurfaceOffset(305, chanNameYpos);
111   chanName.setBackgroundColour(Colour(0, 0, 0, 90));
112
113   // create area to display list of channels
114   chanListbox.setSurface(surface); // add channel list
115   chanListbox.setSurfaceOffset(0, progInfo.getOffsetY() + progInfo.getHeight() + Surface::getFontHeight() + 8); // position channel list
116   chanListbox.setDimensions(150, ((Surface::getFontHeight() + 2) * gridRows) + 5); //listbox line seperation is 2 pixels
117   chanListbox.setGap(2);
118
119
120   // populate channel list
121   if (chanList)
122   {
123     Channel* chan;
124     int first = 1;
125     for (UINT i = 0; i < chanList->size(); i++)
126     {
127       chan = (*chanList)[i];
128       if (i == currentChannel)
129         first = 1;
130       chan->index = chanListbox.addOption(chan->name, 0, first);
131       first = 0;
132     }
133     chanName.setText((*chanList)[chanListbox.getCurrentOption()]->name);
134   }
135
136   listTop = chanListbox.getTopOption();
137   chanListbox.draw(); // doing this to allow chanListbox.getBottomOption() in updateEventList() to work
138   time(&ltime); // set ltime to now
139   ltime = prevHour(&ltime); // set ltime to previous hour TODO make this half hour?
140   time(&selTime); // set selTime to now
141   updateEventList(); // get list of programmes
142 }
143
144 VEpg::~VEpg()
145 {
146   instance = NULL;
147
148   for(UINT listIndex = 0; listIndex < gridRows; listIndex++)
149   {
150     if (eventLista[listIndex])
151     {
152       (eventLista)[listIndex]->clear();
153       delete eventLista[listIndex];
154     }
155   }
156   //  delete [] eventLista;
157
158   // destroy dynamically allocated memory
159 }
160
161 VEpg* VEpg::getInstance()
162 {
163   return instance;
164 }
165
166 void VEpg::setInfo(Event* event)
167 {
168   time_t t;
169   struct tm* btime; // to hold programme start and end time
170   char timeString[9]; // to hold programme start and end time
171   int length = strlen(event->title); // calculate length of programme title string
172   char* title = new char[length + 15]; // create string to hold start time, end time and programme title
173   btime = localtime((time_t*)&event->time); //get programme start time
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
180   t = event->time + event->duration; //get programme end time
181   btime = localtime(&t);
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
188   strcat(title, event->title); // then add the programme title
189   progTitle.setText(title); // sput this sring in our text box
190   length = strlen(event->description);
191   char* info = new char[length + 1]; // create programme detail string
192   strcpy(info, event->description);
193   progInfo.setText(info); // show programme detail string
194 // destroy dynamically allocated memory
195   delete[] info;
196   delete[] title;
197 }
198
199 void VEpg::draw()
200 {
201   View::draw(); // draw pallet
202
203   // Moved all the dynamic data drawing to a seperate function
204
205   // Display the status and key stuff at the bottom
206   int keyx = chanListbox.getOffsetX();
207   int keyy = chanListbox.getOffsetY() + chanListbox.getHeight() + 2;
208   rectangle(keyx, keyy, 605, Surface::getFontHeight() * 2 + 14, Colour(100, 100, 100, 255));
209
210   WSymbol w;
211   w.setSurface(surface);
212
213   w.nextSymbol = WSymbol::LEFTARROW;
214   w.setSurfaceOffset(keyx + 1, keyy + 20);
215   w.draw();
216
217   w.nextSymbol = WSymbol::UP;
218   w.setSurfaceOffset(keyx + 26, keyy + 3);
219   w.draw();
220
221   w.nextSymbol = WSymbol::DOWN;
222   w.setSurfaceOffset(keyx + 26, keyy + 36);
223   w.draw();
224
225   w.nextSymbol = WSymbol::RIGHTARROW;
226   w.setSurfaceOffset(keyx + 50, keyy + 20);
227   w.draw();
228
229   drawText(tr("OK"), keyx + 18, keyy + 20, Colour::LIGHTTEXT);
230
231   rectangle(keyx + 72, keyy + 4, 104, Surface::getFontHeight() + 2, Colour(200, 0, 0, 255));
232   drawText(tr("Page up"), keyx + 74, keyy + 5, Colour::LIGHTTEXT);
233
234   rectangle(keyx + 72, keyy + Surface::getFontHeight() + 8, 104, Surface::getFontHeight() + 2, Colour(0, 200, 0, 255));
235   drawText(tr("Page down"), keyx + 74, keyy + Surface::getFontHeight() + 9, Colour::LIGHTTEXT);
236
237   rectangle(keyx + 180, keyy + 4, 104, Surface::getFontHeight() + 2, Colour(200, 200, 0, 255));
238   drawText(tr("-24 hours"), keyx + 182, keyy + 5, Colour::LIGHTTEXT);
239
240   rectangle(keyx + 180, keyy + Surface::getFontHeight() + 8, 104, Surface::getFontHeight() + 2, Colour(0, 0, 200, 255));
241   drawText(tr("+24 hours"), keyx + 182, keyy + Surface::getFontHeight() + 9, Colour::LIGHTTEXT);
242
243   rectangle(keyx + 290, keyy + 4, 180, Surface::getFontHeight() + 2, Colour(180, 180, 180, 255));
244   drawText(tr("Guide / Back: Close"), keyx + 292 , keyy + 5, Colour::LIGHTTEXT);
245
246   rectangle(keyx + 290, keyy + Surface::getFontHeight() + 8, 180, Surface::getFontHeight() + 2, Colour(180, 180, 180, 255));
247   Colour red = Colour(130, 0, 0);
248   drawText(tr("Rec: Set timer"), keyx + 292, keyy + Surface::getFontHeight() + 9, red);
249
250   rectangle(keyx + 474, keyy + 4, 128, Surface::getFontHeight() + 2, Colour(180, 180, 180, 255));
251   w.nextSymbol = WSymbol::PLAY;
252   w.setSurfaceOffset(keyx + 476, keyy + 5);
253   w.draw();
254   drawText(tr("Sel channel"), keyx + 496, keyy + 5, Colour::LIGHTTEXT);
255
256   rectangle(keyx + 474, keyy + Surface::getFontHeight() + 8, 128, Surface::getFontHeight() + 2, Colour(180, 180, 180, 255));
257   drawText(tr("Go: Preview"), keyx + 476, keyy + Surface::getFontHeight() + 9, Colour::LIGHTTEXT);
258
259   // Draw all the dynamic data
260   drawData();
261 }
262
263 void VEpg::drawData()
264 {
265   // Not doing View::draw() every time causes
266   // things not to be cleared off the surface properly
267   // So, blank out the data area first
268
269   rectangle(
270     chanListbox.getOffsetX(),
271     chanListbox.getOffsetY() - Surface::getFontHeight() - 3,
272     155 + WINDOW_WIDTH * MINUTE_SCALE,
273     chanListbox.getHeight() + Surface::getFontHeight() + 3,
274     Colour::BLACK);
275
276   chanListbox.draw();
277   drawgrid();
278   chanName.draw(); // TODO this should be dealt with by vvideolive
279   progTitle.draw();
280   progInfo.draw();
281 }
282
283
284 int VEpg::handleCommand(int command)
285 {
286   switch(command)
287   {
288     case Remote::DF_UP:
289     case Remote::UP:
290     { // cursor up the channel list
291       chanListbox.up();
292       drawData();
293       viewman->updateView(this);
294       return 2;
295     }
296     case Remote::DF_DOWN:
297     case Remote::DOWN:
298     { // cursor down the channel list
299       chanListbox.down();
300       drawData();
301       viewman->updateView(this);
302       return 2;
303     }
304     case Remote::DF_LEFT:
305     case Remote::LEFT:
306     { // cursor left through time
307       selTime = thisEvent.time - 1;
308       drawData();
309       viewman->updateView(this);
310       return 2;
311     }
312     case Remote::DF_RIGHT:
313     case Remote::RIGHT:
314     {
315     // cursor right through time
316       selTime = thisEvent.time + thisEvent.duration;
317       drawData();
318       viewman->updateView(this);
319       return 2;
320     }
321     case Remote::RED:
322     {
323     // cursor up one page
324       chanListbox.pageUp();
325       drawData();
326       viewman->updateView(this);
327       return 2;
328     }
329     case Remote::GREEN:
330     {
331     // cursor down one page
332       chanListbox.pageDown();
333       drawData();
334       viewman->updateView(this);
335       return 2;
336     }
337     case Remote::BLUE:
338     {
339     // step forward 24 hours
340       selTime += 24 * 60 * 60;
341       drawData();
342       viewman->updateView(this);
343       return 2;
344     }
345     case Remote::YELLOW:
346     {
347     // step forward 24 hours
348       selTime -= 24 * 60 * 60;
349       drawData();
350       viewman->updateView(this);
351       return 2;
352     }
353     case Remote::RECORD:
354     {
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;
362     }
363     case Remote::PLAY:
364     case Remote::GO:
365     case Remote::OK:
366     {
367       if (!chanList) return 2;
368
369       // select programme and display menu TODO currently just changes to selected channel
370       videoLive->channelChange(VVideoLive::NUMBER, (*chanList)[chanListbox.getCurrentOption()]->number);
371
372       if(command == Remote::GO)
373         return 2;
374       // GO just changes channel in preview, PLAY changes channel and returns to normal TV
375     }
376     case Remote::BACK:
377     case Remote::GUIDE:
378     {
379       // return to normal TV mode
380       if (videoLive) // ptr check done in case being tested from videorec
381       {
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         Command::getInstance()->postMessageNoLock(m);
387       }
388       return 4;
389     }
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   }
401   // stop command getting to any more views
402   return 1;
403 }
404
405 void VEpg::drawgrid() // redraws grid and select programme
406 {
407   // draw the grid of programmes
408   char timeString[20];
409   time_t t;
410   time(&t); // set t = now
411   if(selTime < t)
412     selTime = t; // don't allow cursor in the past
413   if(listTop != chanListbox.getTopOption())
414   {
415   // chanListbox has scrolled TODO speed up by changing only rows that have changed
416     listTop = chanListbox.getTopOption();
417     updateEventList();
418   }
419   if ((selTime >= ltime + WINDOW_WIDTH * 60) || (selTime <= ltime))
420   {
421   // we have cursored back before left time of window
422   //TODO check that this and above don't happen together
423     ltime = prevHour(&selTime);
424     updateEventList();
425   }
426   // draw time scale
427   t = ltime;
428   struct tm* tms;
429   tms = localtime(&t);
430   strftime(timeString, 19, "%a %e %b", tms);
431   int timey = chanListbox.getOffsetY() - Surface::getFontHeight() - 3;
432   int timex = 135;
433   drawTextRJ(timeString, timex - 10, timey, Colour::LIGHTTEXT); // print date
434   strftime(timeString, 19, "%H:%M", tms);
435   drawText(timeString, timex, timey, Colour::LIGHTTEXT); // print left time
436   rectangle(155, timey + Surface::getFontHeight(), 2, 7, Colour(255, 255, 255, 255));
437   t = t + 3600;
438   tms = localtime(&t);
439   strftime(timeString, 19, "%H:%M", tms);
440   drawText(timeString, timex + 180, timey, Colour::LIGHTTEXT); // print middle time
441   rectangle(335, timey + Surface::getFontHeight(), 2, 7, Colour(255, 255, 255, 255));
442   t = t + 3600;
443   tms = localtime(&t);
444   strftime(timeString, 19, "%H:%M", tms);
445   drawText(timeString, timex + 360, timey, Colour::LIGHTTEXT); // print right time
446   rectangle(515, timey + Surface::getFontHeight(), 2, 7, Colour(255, 255, 255, 255));
447   // pointer to selTime
448   rectangle(155 + (selTime - ltime) / 20, timey + Surface::getFontHeight(), 2, 7, Colour(255, 50, 50, 255));
449
450   // TODO should the above two comditional statements be combined to avoid calling updateEventList() twice?
451   Event* event;
452   Event noevent; // an event to use if there are gaps in the epg
453   thisEvent.setdescription(tr("There are no programme details available for this period"));
454   thisEvent.duration = WINDOW_WIDTH * 60;
455   thisEvent.time = ltime;
456   thisEvent.settitle(tr("No programme details"));
457   thisEvent.id = 0;
458   bool swapColour = FALSE; // alternate cell colour
459   bool currentRow = FALSE;
460   int y = chanListbox.getOffsetY() + 5; // vertical position of cell
461   Colour bg, fg; // background colour of cells in grid
462   // for each displayed channel, find programmes that fall in 2.5 hour time window
463   for(UINT listIndex = 0; listIndex < gridRows; listIndex++)
464   {
465     if (listTop + (int)listIndex >= chanListbox.getBottomOption())
466       continue; // ensure nothing populates grid below last channel
467     currentRow = (listTop + (int)listIndex == chanListbox.getCurrentOption());
468     noevent.time = ltime;
469     noevent.duration = WINDOW_WIDTH * 60;
470     noevent.settitle("");
471     paintCell(&noevent, y, Colour::NOPROGRAMME, Colour::LIGHTTEXT); // fill row with no programme colour to be painted ove with valid programmes
472     if (currentRow)
473     {
474       thisEvent.setdescription(tr("There are no programme details available for this period"));
475       thisEvent.duration = WINDOW_WIDTH * 60;
476       thisEvent.time = ltime;
477       thisEvent.settitle(tr("No programme details"));
478       thisEvent.id = 0;
479     }
480     if (eventLista[listIndex])
481     {
482       sort(eventLista[listIndex]->begin(), eventLista[listIndex]->end(), EventSorter());
483       for(e = 0; e < (eventLista[listIndex])->size(); e++) // step through events for this channel
484       {
485         fg = Colour::LIGHTTEXT;
486         event = (*eventLista[listIndex])[e];
487         if (event)
488         {
489           UINT end = event->time + event->duration; // programme end time
490           if(event->time >= UINT(ltime) + (WINDOW_WIDTH * 60)) // programme starts after RHS of window
491             continue; // that's enough of this channel's events
492           if(end <= UINT(ltime)) // programme ends before LHS of window
493             continue; // this event is before the window - let's try the next event
494           // this event is one we are interested in
495           bg = (swapColour)?Colour::PROGRAMMEA:Colour::PROGRAMMEB; // alternate cell colour
496           swapColour = !swapColour; // it wil be the other colour next time
497           if(event->time <= UINT(selTime) && end > UINT(selTime) && currentRow)
498           {
499             // this is the selected programme
500             thisEvent.setdescription(event->description);
501             thisEvent.duration = event->duration;
502             thisEvent.time = event->time;
503             thisEvent.settitle(event->title);
504             thisEvent.id = event->id;
505             if(thisEvent.id == 0)
506               thisEvent.id = 1;
507             bg = Colour::SELECTHIGHLIGHT; // highlight cell
508             fg = Colour::DARKTEXT;
509           }
510           else
511           {
512             if (currentRow && thisEvent.id == 0)
513             {
514               if (end <= UINT(selTime) && end > UINT(thisEvent.time))
515                 thisEvent.time = end;
516               if (event->time > UINT(selTime) && event->time < thisEvent.time + thisEvent.duration)
517                 thisEvent.duration = event->time - thisEvent.time;
518             }
519           }
520           paintCell(event, y, bg, fg);
521         }
522       }
523     }
524     else
525     {
526       // no event list for this channel. Already painted noevent colour so just highlight if selected
527       if (currentRow)
528       {
529         bg = Colour::SELECTHIGHLIGHT; // highlight cell
530         fg = Colour::DARKTEXT;
531         paintCell(&thisEvent, y, bg, fg);
532       }
533       else
534       {
535         bg = Colour::NOPROGRAMME;
536         fg = Colour::LIGHTTEXT;
537         noevent.settitle(tr("No programme details"));
538         paintCell(&noevent, y, bg, fg);
539       }
540     }
541     y += Surface::getFontHeight() + 2;
542   }
543   setInfo(&thisEvent);
544 }
545
546 void VEpg::updateEventList()
547 {
548   if (!chanList) return;
549   Channel* chan;
550   for(UINT listIndex = 0; listIndex < gridRows; listIndex++)
551   {
552     if(listTop + listIndex >= UINT(chanListbox.getBottomOption()))
553       continue;
554     chan = (*chanList)[listTop + listIndex];
555
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
557   }
558 }
559
560 void VEpg::setCurrentChannel(char* chname)
561 {
562   chanName.setText(chname);
563   chanName.draw();
564   viewman->updateView(this);
565 }
566
567 void VEpg::paintCell(Event* event, int yOffset, Colour bg, Colour fg)
568 {
569   int w, x, y, h;
570   w = x = 0; // keep compiler happy
571
572   y =yOffset;
573   h = Surface::getFontHeight(); // TODO if want border around text, need to increae this and wselectlist line height
574   UINT end = event->time + event->duration; // programme end time
575   if(event->time <= UINT(ltime) && end > UINT(ltime)) // spans start of displayed window
576   {
577     x = 155; // LHS of window
578     if (end > (UINT(ltime) + (WINDOW_WIDTH * 60)))
579       w = WINDOW_WIDTH * MINUTE_SCALE; // spans full 2 hour window
580     else
581       w = MINUTE_SCALE * (event->time + event->duration - ltime ) / 60; // get width of remaining programme
582   }
583   if((event->time >= UINT(ltime)) && (event->time <= UINT(ltime) + (WINDOW_WIDTH * 60))) // starts within window
584   {
585     x = 155 + (MINUTE_SCALE * (event->time - ltime) / 60);
586     w = MINUTE_SCALE * event->duration / 60;
587     //if (w > 155 + MINUTE_SCALE * WINDOW_WIDTH -x)
588      // w = w + x - 155 - MINUTE_SCALE * WINDOW_WIDTH; // ends outside window
589   }
590   if (w > 155 + WINDOW_WIDTH * MINUTE_SCALE - x)
591     w = 155 + WINDOW_WIDTH * MINUTE_SCALE -x; // limit cells to RHS of window
592   rectangle(x, y, w, h, bg);
593   char* tt = new char[strlen(event->title) + 1];
594   strcpy (tt, event->title);
595   int textWidth = 0;
596   for (UINT textPos = 0; textPos < strlen(tt); textPos++)
597   {
598     int thisCharWidth = surface->getCharWidth(tt[textPos]);
599     if (textWidth + thisCharWidth > w) // text will not fit in cell
600     {
601       textWidth = textPos;
602       break;
603     }
604     textWidth += thisCharWidth;
605   }
606   char* tT = new char[textWidth];
607   if(textWidth > 1)
608   {
609     strncpy(tT, tt, textWidth - 1);
610     tT[textWidth - 1] =  '\0';
611     surface->drawText(tT, x+2, y, fg.rgba());
612   }
613   delete tT;
614
615 }
616
617 time_t VEpg::prevHour(time_t* t)
618 {
619   struct tm* tms;
620   tms = localtime(t);
621   tms->tm_sec = 0;
622   tms->tm_min = 0;
623   return mktime(tms);
624 }
625
626 void VEpg::processMessage(Message* m)
627 {
628   if (m->message == Message::MOUSE_MOVE)
629   {
630     if (chanListbox.mouseMove((m->parameter>>16)-getScreenX(),(m->parameter&0xFFFF)-getScreenY()))
631     {
632       drawData();
633       ViewMan::getInstance()->updateView(this);
634     }
635   }
636   else if (m->message == Message::MOUSE_LBDOWN)
637   {
638     if (chanListbox.mouseLBDOWN((m->parameter>>16)-getScreenX(),(m->parameter&0xFFFF)-getScreenY()))
639     {
640       ViewMan::getInstance()->handleCommand(Remote::OK); //simulate OK press
641     }
642     else
643     {
644       //check if press is outside this view! then simulate cancel
645       int x=(m->parameter>>16)-getScreenX();
646       int y=(m->parameter&0xFFFF)-getScreenY();
647       int keyx = chanListbox.getOffsetX();
648       int keyy = chanListbox.getOffsetY() + chanListbox.getHeight() + 2;
649
650       if (x<0 || y <0 || x>getWidth() || y>getHeight())
651       {
652         ViewMan::getInstance()->handleCommand(Remote::BACK); //simulate cancel press
653       }
654       else if (x>=(keyx+72) && y>=(keyy+4) &&x<=(keyx+72+104) &&y<=(keyy+4+Surface::getFontHeight() + 2))
655       {
656         ViewMan::getInstance()->handleCommand(Remote::RED);
657       }
658       else if (x>=(keyx+72) && y>=(keyy+ Surface::getFontHeight() + 8) &&x<=(keyx+72+104) &&y<=(keyy+8+2*Surface::getFontHeight() + 2))
659       {
660         ViewMan::getInstance()->handleCommand(Remote::GREEN);
661       }
662       else if (x>=(keyx+180) && y>=(keyy+4) &&x<=(keyx+180+104) &&y<=(keyy+4+Surface::getFontHeight() + 2))
663       {
664         ViewMan::getInstance()->handleCommand(Remote::YELLOW);
665       }
666       else if (x>=(keyx+180) && y>=(keyy+ Surface::getFontHeight() + 8) &&x<=(keyx+180+104) &&y<=(keyy+8+2*Surface::getFontHeight() + 2))
667       {
668         ViewMan::getInstance()->handleCommand(Remote::BLUE);
669       }
670       else if (x>=(keyx+290) && y>=(keyy+4) &&x<=(keyx+180+290) &&y<=(keyy+4+Surface::getFontHeight() + 2))
671       {
672         ViewMan::getInstance()->handleCommand(Remote::BACK);
673       }
674       else if (x>=(keyx+290) && y>=(keyy+ Surface::getFontHeight() + 8) &&x<=(keyx+290+180) &&y<=(keyy+8+2*Surface::getFontHeight() + 2))
675       {
676         ViewMan::getInstance()->handleCommand(Remote::RECORD);
677       }
678       else if (x>=(keyx+474) && y>=(keyy+4) &&x<=(keyx+128+474) &&y<=(keyy+4+Surface::getFontHeight() + 2))
679       {
680         ViewMan::getInstance()->handleCommand(Remote::PLAY);
681       }
682       else if (x>=(keyx+474) && y>=(keyy+ Surface::getFontHeight() + 8) &&x<=(keyx+238+474) &&y<=(keyy+8+2*Surface::getFontHeight() + 2))
683       {
684         ViewMan::getInstance()->handleCommand(Remote::GO);
685       }
686       else if ( x>=(chanListbox.getOffsetX())
687                 && y>=(chanListbox.getOffsetY() + 5)
688                 // &&x<=(chanListbox.getOffsetX()+155 + WINDOW_WIDTH * MINUTE_SCALE)
689                 &&y<=(chanListbox.getOffsetY() - Surface::getFontHeight()
690                 - 3+chanListbox.getHeight() + Surface::getFontHeight() + 3)
691               )
692       {
693         int cy=y-(chanListbox.getOffsetY() + 5);
694         int row=cy/(Surface::getFontHeight()+2);
695         int clistTop = chanListbox.getTopOption();
696         chanListbox.hintSetCurrent(clistTop+row);
697         int cx=x-155;
698         time_t ttime = cx*60/MINUTE_SCALE+ltime;
699         //x = 155 + (MINUTE_SCALE * (event->time - ltime) / 60);
700
701         selTime = ttime;
702         drawData();
703         viewman->updateView(this);
704       }
705     }
706   }
707 }