2 Copyright 2005 Brian Walton
4 This file is part of VOMP.
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.
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.
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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
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.
37 #include "vchannellist.h"
40 #include "vepgsettimer.h"
51 VEpg* VEpg::instance = NULL;
53 VEpg::VEpg(void* tparent, UINT tcurrentChannelIndex, ChannelList* tchanList)
56 currentChannelIndex = tcurrentChannelIndex;
58 // PAL / NTSC sizes -----------------------
61 //int summaryLines, summaryLowerPadding;
63 int fontHeight=getFontHeight() + 4;
64 //UINT gridRows; // is a class member
67 if (Video::getInstance()->getFormat() == Video::PAL)
84 //summaryLowerPadding = 28;
88 int screenwidthhalf=Video::getInstance()->getScreenWidth()/2;
89 int screenheighthalf=Video::getInstance()->getScreenHeight()/2;
90 // summaryLines = ((float)screenheighthalf)/((float)fontHeight))-1;
91 // summaryLowerPadding = screenheighthalf-summaryLines*(fontHeight);
92 gridRows = (screenheighthalf-fontHeight*3-50)/fontHeight;
95 // initialise variables and pointers
96 boxstack = BoxStack::getInstance();
102 eventLista.resize(gridRows);
103 for(UINT listIndex = 0; listIndex < gridRows; listIndex++)
105 // initialise array of pointers to eventlist structures
106 eventLista[listIndex] = NULL;
109 // Create pallet on which to paint our epg view and position it in centre of screen.
110 // Need to reduce size to deal with overscanning TVs.
112 setSize(Video::getInstance()->getScreenWidth(), Video::getInstance()->getScreenHeight());
117 // DrawStyle transparent = DrawStyle(0, 0, 0, 0);
118 // setBackgroundColour(transparent);
120 // progTitle.setSurface(surface);
121 progTitle.setPosition(0,0);
122 progTitle.setSize(screenwidthhalf,(fontHeight) * 1 + ypos); //paragraph line seperation is 4 pixels
123 progTitle.setBackgroundColour(DrawStyle::TITLEBARBACKGROUND);
124 progTitle.setTextPos(xpos, ypos);
126 progTitle.setParaMode(false);
129 // progInfo.setSurface(surface);
130 progInfo.setBackgroundColour(DrawStyle::VIEWBACKGROUND);
131 progInfo.setPosition(0, progTitle.getY2());
132 progInfo.setSize(screenwidthhalf,screenheighthalf-progTitle.getY2()+10);
133 progInfo.setTextPos(xpos, 0);
137 // chanName.setSurface(surface);
138 chanName.setSize(510, (fontHeight));
139 chanName.setPosition(screenwidthhalf, screenheighthalf - 2*fontHeight);
140 DrawStyle t1(0, 0, 0, 90);
141 chanName.setBackgroundColour(t1);
142 chanName.setParaMode(false);
145 // create area to display list of channels
146 // chanListbox.setSurface(surface); // add channel list
147 chanListbox.setPosition(0, progInfo.getY2() + fontHeight + 4); // position channel list
148 chanListbox.setSize(150, ((getFontHeight() + 2) * gridRows) + 5); //listbox line seperation is 2 pixels
149 chanListbox.setGap(2);
150 chanListbox.addColumn(xpos);
153 // populate channel list
158 for (UINT i = 0; i < chanList->size(); i++)
160 chan = (*chanList)[i];
161 if (i == currentChannelIndex)
163 chan->index = chanListbox.addOption(chan->name, 0, first);
166 chanName.setText((*chanList)[chanListbox.getCurrentOption()]->name);
169 window_x= chanListbox.getRootBoxOffsetX() + chanListbox.getWidth() + 5;
170 window_width=(Video::getInstance()->getScreenWidth() - window_x +3)/3;
172 listTop = chanListbox.getTopOption();
173 chanListbox.draw(); // doing this to allow chanListbox.getBottomOption() in updateEventList() to work
174 time(<ime); // set ltime to now
175 ltime = prevHour(<ime); // set ltime to previous hour TODO make this half hour?
176 time(&selTime); // set selTime to now
177 updateEventList(); // get list of programmes
179 vdisplay.mode=Window;
180 vdisplay.fallbackMode=Quarter;
181 vdisplay.x=Video::getInstance()->getScreenWidth()/2;
183 vdisplay.width=Video::getInstance()->getScreenWidth()/2;
184 vdisplay.height=Video::getInstance()->getScreenHeight()/2;
187 void VEpg::preDelete()
189 Timers::getInstance()->cancelTimer(this, 1);
197 for(UINT listIndex = 0; listIndex < gridRows; listIndex++)
199 if (eventLista[listIndex])
201 (eventLista)[listIndex]->clear();
202 delete eventLista[listIndex];
205 // delete [] eventLista; // FIXME
207 // destroy dynamically allocated memory
210 VEpg* VEpg::getInstance()
215 void VEpg::setInfo(Event* event)
218 struct tm btime; // to hold programme start and end time
219 char timeString[9]; // to hold programme start and end time
220 int length = strlen(event->title); // calculate length of programme title string
221 char* title = new char[length + 15]; // create string to hold start time, end time and programme title
222 time_t eventtime = event->time;
223 LOCALTIME_R((time_t*)&eventtime, &btime); //get programme start time
225 strftime(timeString, 9, "%0H:%0M - ", &btime); // and format it as hh:mm -
227 strftime(timeString, 9, "%H:%M - ", &btime); // and format it as hh:mm -
229 strcpy(title, timeString); // put it in our buffer
230 t = event->time + event->duration; //get programme end time
231 LOCALTIME_R(&t, &btime);
233 strftime(timeString, 7, "%0H:%0M ", &btime); // and format it as hh:mm -
235 strftime(timeString, 7, "%H:%M ", &btime); // and format it as hh:mm -
237 strcat(title, timeString); // put it in our buffer
239 strcat(title, event->title); // then add the programme title
240 progTitle.setText(title); // sput this sring in our text box
241 length = strlen(event->description);
242 char* info = new char[length + 1]; // create programme detail string
243 strcpy(info, event->description);
244 progInfo.setText(info); // show programme detail string
245 // destroy dynamically allocated memory
252 // View::draw(); // draw pallet
254 DrawStyle transparent = DrawStyle(0, 0, 0, 0);
255 fillColour(transparent);
258 // Moved all the dynamic data drawing to a seperate function
260 // Display the status and key stuff at the bottom
262 UINT keyx = chanListbox.getRootBoxOffsetX() + chanListbox.getColumn(0);
263 UINT keyy = chanListbox.getRootBoxOffsetY() + chanListbox.getHeight() + 2;
264 rectangle (0, keyy, Video::getInstance()->getScreenWidth() ,
265 Video::getInstance()->getScreenHeight()-keyy, DrawStyle::DARKGREY);
270 w.nextSymbol = WSymbol::LEFTARROW;
271 w.setPosition(keyx + 1, keyy + 20);
274 w.nextSymbol = WSymbol::UP;
275 w.setPosition(keyx + 26, keyy + 3);
278 w.nextSymbol = WSymbol::DOWN;
279 w.setPosition(keyx + 26, keyy + 36);
282 w.nextSymbol = WSymbol::RIGHTARROW;
283 w.setPosition(keyx + 50, keyy + 20);
286 drawTextCentre(tr("OK"), keyx + 35, keyy + 20, DrawStyle::LIGHTTEXT);
288 rectangle(keyx + 72, keyy + 4, 104, getFontHeight() + 2, DrawStyle::RED);
289 drawText(tr("Page up"), keyx + 74, keyy + 5, DrawStyle::LIGHTTEXT);
291 rectangle(keyx + 72, keyy + getFontHeight() + 8, 104, getFontHeight() + 2, DrawStyle::GREEN);
292 drawText(tr("Page down"), keyx + 74, keyy + getFontHeight() + 9, DrawStyle::LIGHTTEXT);
294 rectangle(keyx + 180, keyy + 4, 104, getFontHeight() + 2, DrawStyle::YELLOW);
295 drawText(tr("-24 hours"), keyx + 182, keyy + 5, DrawStyle::LIGHTTEXT);
297 rectangle(keyx + 180, keyy + getFontHeight() + 8, 104, getFontHeight() + 2, DrawStyle::BLUE);
298 drawText(tr("+24 hours"), keyx + 182, keyy + getFontHeight() + 9, DrawStyle::LIGHTTEXT);
300 rectangle(keyx + 290, keyy + 4, 180, getFontHeight() + 2, DrawStyle::GREY);
301 drawText(tr("Guide / Back: Close"), keyx + 292 , keyy + 5, DrawStyle::LIGHTTEXT);
303 rectangle(keyx + 290, keyy + getFontHeight() + 8, 180, getFontHeight() + 2, DrawStyle::GREY);
304 DrawStyle red = DrawStyle(130, 0, 0);
305 drawText(tr("Rec: Set timer"), keyx + 292, keyy + getFontHeight() + 9, red);
307 rectangle(keyx + 474, keyy + 4, 128, getFontHeight() + 2, DrawStyle::GREY);
308 w.nextSymbol = WSymbol::PLAY;
309 w.setPosition(keyx + 476, keyy + 5);
311 drawText(tr("Sel channel"), keyx + 496, keyy + 5, DrawStyle::LIGHTTEXT);
313 rectangle(keyx + 474, keyy + getFontHeight() + 8, 128, getFontHeight() + 2, DrawStyle::GREY);
314 drawText(tr("Go: Preview"), keyx + 476, keyy + getFontHeight() + 9, DrawStyle::LIGHTTEXT);
317 // Draw all the dynamic data
321 void VEpg::drawData()
323 // Not doing View::draw() every time causes
324 // things not to be cleared off the surface properly
325 // So, blank out the data area first
326 //int screenwidth=Video::getInstance()->getScreenWidth();
328 chanListbox.getRootBoxOffsetX(),
329 chanListbox.getRootBoxOffsetY() - getFontHeight() - 3,
330 window_width * MINUTE_SCALE,
331 chanListbox.getHeight() + getFontHeight() + 4,
336 chanName.draw(); // TODO this should be dealt with by vvideolive
341 // Set timer to redraw to move the current time bar
345 if (dt == 0) dt = 60;
347 Timers::getInstance()->setTimerT(this, 1, dt);
350 void VEpg::timercall(int clientReference)
353 boxstack->update(this);
356 int VEpg::handleCommand(int command)
362 { // cursor up the channel list
365 boxstack->update(this);
368 case Remote::DF_DOWN:
370 { // cursor down the channel list
371 Log::getInstance()->log("VEPG", Log::DEBUG, "Down start");
375 boxstack->update(this);
376 Log::getInstance()->log("VEPG", Log::DEBUG, "Down end");
380 case Remote::DF_LEFT:
382 { // cursor left through time
383 selTime = thisEvent.time - 1;
385 boxstack->update(this);
388 case Remote::DF_RIGHT:
391 // cursor right through time
392 selTime = thisEvent.time + thisEvent.duration;
394 boxstack->update(this);
399 // cursor up one page
400 chanListbox.pageUp();
402 boxstack->update(this);
407 // cursor down one page
408 chanListbox.pageDown();
410 boxstack->update(this);
415 // step forward 24 hours
416 selTime += 24 * 60 * 60;
418 boxstack->update(this);
423 // step forward 24 hours
424 selTime -= 24 * 60 * 60;
426 boxstack->update(this);
431 if (!chanList) return 2;
432 Log::getInstance()->log("VEPG", Log::DEBUG, "ID %lu TIME %lu DURATION %lu TITLE %s", thisEvent.id, thisEvent.time, thisEvent.duration, thisEvent.title);
433 VEpgSetTimer* vs = new VEpgSetTimer(&thisEvent, (*chanList)[chanListbox.getCurrentOption()]);
436 boxstack->update(vs);
443 if (!chanList) return 2;
445 // select programme and display menu TODO currently just changes to selected channel
447 currentChannelIndex = chanListbox.getCurrentOption();
451 Message* m = new Message(); // Must be done after this view deleted
454 m->message = Message::CHANNEL_CHANGE;
455 m->parameter.num = (*chanList)[currentChannelIndex]->number;
456 Command::getInstance()->postMessageNoLock(m);
461 if(command == Remote::GO)
463 // GO just changes channel in preview, PLAY changes channel and returns to normal TV
469 #ifdef VOMP_PLATTFORM_MVP
471 new video modes system doesn't work properly on MVP
472 It seems to always set wanted mode to 1 which is "full screen"
473 but translates to Video::LETERBOX on MVP
475 Mode 0 in new system is "None", but is Video::NORMAL for MVP
477 VideoMVP::setMode(LETTERBOX) while in 16x9 mode is invalid
479 Anyway, bool Video::setVideoDisplay(VideoDisplay display) calls with Video::mode
480 not applyMode, so wouldn't work for two reasons
482 VVideoLiveTV has the memory of what mode, NORMAL / LETTERBOX we were in before
483 we went to QUARTER. Hack this in to get a message there.
486 if (parent) // ptr check done in case being tested from videorec
488 Message* m = new Message(); // Must be done after this view deleted
491 m->message = Message::HACK_MVP_RETURN_FROM_QUARTER;
492 Command::getInstance()->postMessageNoLock(m);
498 case Remote::CHANNELUP:
500 if (currentChannelIndex == (chanList->size() - 1)) // at the end
501 currentChannelIndex = 0;
503 ++currentChannelIndex;
507 Message* m = new Message(); // Must be done after this view deleted
510 m->message = Message::CHANNEL_CHANGE;
511 m->parameter.num = (*chanList)[currentChannelIndex]->number;
512 Command::getInstance()->postMessageNoLock(m);
519 case Remote::CHANNELDOWN:
521 if (currentChannelIndex == 0) // at the start
522 currentChannelIndex = chanList->size() - 1; // so go to end
524 --currentChannelIndex;
528 Message* m = new Message(); // Must be done after this view deleted
531 m->message = Message::CHANNEL_CHANGE;
532 m->parameter.num = (*chanList)[currentChannelIndex]->number;
533 Command::getInstance()->postMessageNoLock(m);
541 // stop command getting to any more views
545 void VEpg::drawgrid() // redraws grid and select programme
547 // draw the grid of programmes
550 time(&t); // set t = now
552 selTime = t; // don't allow cursor in the past
553 if(listTop != chanListbox.getTopOption())
555 // chanListbox has scrolled TODO speed up by changing only rows that have changed
556 listTop = chanListbox.getTopOption();
559 if ((selTime >= ltime + (int)window_width * 60) || (selTime <= ltime))
561 // we have cursored back before left time of window
562 //TODO check that this and above don't happen together
563 ltime = prevHour(&selTime);
567 DrawStyle white = DrawStyle(255, 255, 255, 255);
572 LOCALTIME_R(&t, &tms);
573 strftime(timeString, 19, "%a %d %b", &tms);
574 int timey = chanListbox.getRootBoxOffsetY() - getFontHeight() - 3;
576 drawTextRJ(timeString, timex - 10, timey, DrawStyle::LIGHTTEXT); // print date
577 strftime(timeString, 19, "%H:%M", &tms);
578 drawText(timeString, timex, timey, DrawStyle::LIGHTTEXT); // print left time
580 rectangle(155, timey + getFontHeight(), 2, 7, white);
582 LOCALTIME_R(&t, &tms);
583 strftime(timeString, 19, "%H:%M", &tms);
584 drawText(timeString, timex + 180, timey, DrawStyle::LIGHTTEXT); // print middle time
585 rectangle(335, timey + getFontHeight(), 2, 7, white);
587 LOCALTIME_R(&t, &tms);
588 strftime(timeString, 19, "%H:%M", &tms);
589 drawText(timeString, timex + 360, timey, DrawStyle::LIGHTTEXT); // print right time
590 rectangle(515, timey + getFontHeight(), 2, 7, white);
591 // pointer to selTime
592 //rectangle(155 + (selTime - ltime) / 20, timey + getFontHeight(), 2, 7, DrawStyle(255, 50, 50, 255));
596 if ((t >= ltime) && (t < (ltime + 9000)))
598 rectangle(155 + (t - ltime) / 20, timey + getFontHeight(), 2, ((getFontHeight() + 2) * gridRows) + 7 + 2, DrawStyle::RED);
601 // TODO should the above two comditional statements be combined to avoid calling updateEventList() twice?
603 Event noevent; // an event to use if there are gaps in the epg
604 thisEvent.setdescription(tr("There are no programme details available for this period"));
605 thisEvent.duration = window_width * 60;
606 thisEvent.time = ltime;
607 thisEvent.settitle(tr("No programme details"));
609 bool swapColour = false; // alternate cell colour
610 bool currentRow = false;
611 int y = chanListbox.getRootBoxOffsetY() + 5; // vertical position of cell
612 DrawStyle bg, fg; // background colour of cells in grid
613 // for each displayed channel, find programmes that fall in 2.5 hour time window
614 for(UINT listIndex = 0; listIndex < gridRows; listIndex++)
616 if (listTop + (int)listIndex >= chanListbox.getBottomOption())
617 continue; // ensure nothing populates grid below last channel
619 currentRow = (listTop + (int)listIndex == chanListbox.getCurrentOption());
620 noevent.time = ltime;
621 noevent.duration = window_width * 60;
622 noevent.settitle("");
623 paintCell(&noevent, y, DrawStyle::NOPROGRAMME, DrawStyle::LIGHTTEXT); // fill row with no programme colour to be painted ove with valid programmes
626 thisEvent.setdescription(tr("There are no programme details available for this period"));
627 thisEvent.duration = window_width * 60;
628 thisEvent.time = ltime;
629 thisEvent.settitle(tr("No programme details"));
632 if (eventLista[listIndex])
634 sort(eventLista[listIndex]->begin(), eventLista[listIndex]->end(), EventSorter());
635 for(e = 0; e < (eventLista[listIndex])->size(); e++) // step through events for this channel
637 fg = DrawStyle::LIGHTTEXT;
638 event = (*eventLista[listIndex])[e];
641 UINT end = event->time + event->duration; // programme end time
642 if(event->time >= UINT(ltime) + (window_width * 60)) // programme starts after RHS of window
643 continue; // that's enough of this channel's events
644 if(end <= UINT(ltime)) // programme ends before LHS of window
645 continue; // this event is before the window - let's try the next event
646 // this event is one we are interested in
647 bg = (swapColour)?DrawStyle::PROGRAMMEA:DrawStyle::PROGRAMMEB; // alternate cell colour
648 swapColour = !swapColour; // it wil be the other colour next time
649 if(event->time <= UINT(selTime) && end > UINT(selTime) && currentRow)
651 // this is the selected programme
652 thisEvent.setdescription(event->description);
653 thisEvent.duration = event->duration;
654 thisEvent.time = event->time;
655 thisEvent.settitle(event->title);
656 thisEvent.id = event->id;
657 if(thisEvent.id == 0)
659 bg = DrawStyle::SELECTHIGHLIGHT; // highlight cell
660 fg = DrawStyle::DARKTEXT;
664 if (currentRow && thisEvent.id == 0)
666 if (end <= UINT(selTime) && end > UINT(thisEvent.time))
667 thisEvent.time = end;
668 if (event->time > UINT(selTime) && event->time < thisEvent.time + thisEvent.duration)
669 thisEvent.duration = event->time - thisEvent.time;
672 paintCell(event, y, bg, fg);
678 // no event list for this channel. Already painted noevent colour so just highlight if selected
681 bg = DrawStyle::SELECTHIGHLIGHT; // highlight cell
682 fg = DrawStyle::DARKTEXT;
683 paintCell(&thisEvent, y, bg, fg);
687 bg = DrawStyle::NOPROGRAMME;
688 fg = DrawStyle::LIGHTTEXT;
689 noevent.settitle(tr("No programme details"));
690 paintCell(&noevent, y, bg, fg);
693 y += getFontHeight() + 2;
698 void VEpg::updateEventList()
700 if (!chanList) return;
702 for(UINT listIndex = 0; listIndex < gridRows; listIndex++)
704 if(listTop + listIndex >= UINT(chanListbox.getBottomOption()))
706 chan = (*chanList)[listTop + listIndex];
707 if (eventLista[listIndex])
709 (eventLista)[listIndex]->clear();
710 delete eventLista[listIndex];
712 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
716 void VEpg::setCurrentChannel()
718 chanName.setText((*chanList)[currentChannelIndex]->name);
721 chanName.getRootBoxRegion(&r);
722 boxstack->update(this, &r);
725 void VEpg::paintCell(Event* event, int yOffset, const DrawStyle& bg, const DrawStyle& fg)
728 w = x = 0; // keep compiler happy
730 h = getFontHeight(); // TODO if want border around text, need to increae this and wselectlist line height
731 UINT end = event->time + event->duration; // programme end time
732 if(event->time <= UINT(ltime) && end > UINT(ltime)) // spans start of displayed window
734 x = 155; // LHS of window
735 if (end > (UINT(ltime) + (window_width * 60)))
736 w = window_width * MINUTE_SCALE; // spans full 2 hour window
738 w = MINUTE_SCALE * (event->time + event->duration - ltime ) / 60; // get width of remaining programme
740 if((event->time >= UINT(ltime)) && (event->time <= UINT(ltime) + (window_width * 60))) // starts within window
742 x = 155 + (MINUTE_SCALE * (event->time - ltime) / 60);
743 w = MINUTE_SCALE * event->duration / 60;
744 //if (w > 155 + MINUTE_SCALE * WINDOW_WIDTH -x)
745 // w = w + x - 155 - MINUTE_SCALE * WINDOW_WIDTH; // ends outside window
747 if (w > 155 + (int)window_width * MINUTE_SCALE - x)
748 w = 155 + window_width * MINUTE_SCALE -x; // limit cells to RHS of window
749 rectangle(x, y, w, h, bg);
750 char* tt = new char[strlen(event->title) + 1];
751 strcpy (tt, event->title);
753 unsigned int cur_length=1;
754 unsigned int text_max=strlen(tt);
756 if (Osd::getInstance()->charSet()!=1) mchar=true;
758 memset((void*)&state,0,sizeof(state));
761 for (textPos = 0; textPos <text_max; textPos+=cur_length)
765 cur_length = mbrtowc(&cur_char, tt + textPos, text_max-textPos, &state);
766 if (cur_length <= 0){
769 } else cur_char= *(tt+textPos);
770 float thisCharWidth = charWidth(cur_char);
771 if (textWidth + thisCharWidth > w) // text will not fit in cell
775 textWidth += thisCharWidth;
777 char* tT = new char[textPos+1];
780 strncpy(tT, tt, textPos );
782 surface->drawText(tT, x+2, y, fg);
788 time_t VEpg::prevHour(time_t* t)
791 LOCALTIME_R(t, &tms);
797 void VEpg::processMessage(Message* m)
799 if (m->message == Message::MOUSE_MOVE)
801 if (chanListbox.mouseMove((m->parameter.num>>16)-getScreenX(),(m->parameter.num&0xFFFF)-getScreenY()))
804 boxstack->update(this);
807 else if (m->message == Message::MOUSE_LBDOWN)
809 if (chanListbox.mouseLBDOWN((m->parameter.num>>16)-getScreenX(),(m->parameter.num&0xFFFF)-getScreenY()))
811 boxstack->handleCommand(Remote::OK); //simulate OK press
815 //check if press is outside this view! then simulate cancel
816 int x=(m->parameter.num>>16)-getScreenX();
817 int y=(m->parameter.num&0xFFFF)-getScreenY();
818 int keyx = chanListbox.getRootBoxOffsetX();
819 int keyy = chanListbox.getRootBoxOffsetY() + chanListbox.getHeight() + 2;
821 if (x<0 || y <0 || x>(int)getWidth() || y>(int)getHeight())
823 boxstack->handleCommand(Remote::BACK); //simulate cancel press
825 else if (x>=(keyx+72) && y>=(keyy+4) &&x<=(keyx+72+104) &&y<=(keyy+4+getFontHeight() + 2))
827 boxstack->handleCommand(Remote::RED);
829 else if (x>=(keyx+72) && y>=(keyy+ getFontHeight() + 8) &&x<=(keyx+72+104) &&y<=(keyy+8+2*getFontHeight() + 2))
831 boxstack->handleCommand(Remote::GREEN);
833 else if (x>=(keyx+180) && y>=(keyy+4) &&x<=(keyx+180+104) &&y<=(keyy+4+getFontHeight() + 2))
835 boxstack->handleCommand(Remote::YELLOW);
837 else if (x>=(keyx+180) && y>=(keyy+ getFontHeight() + 8) &&x<=(keyx+180+104) &&y<=(keyy+8+2*getFontHeight() + 2))
839 boxstack->handleCommand(Remote::BLUE);
841 else if (x>=(keyx+290) && y>=(keyy+4) &&x<=(keyx+180+290) &&y<=(keyy+4+getFontHeight() + 2))
843 boxstack->handleCommand(Remote::BACK);
845 else if (x>=(keyx+290) && y>=(keyy+ getFontHeight() + 8) &&x<=(keyx+290+180) &&y<=(keyy+8+2*getFontHeight() + 2))
847 boxstack->handleCommand(Remote::RECORD);
849 else if (x>=(keyx+474) && y>=(keyy+4) &&x<=(keyx+128+474) &&y<=(keyy+4+getFontHeight() + 2))
851 boxstack->handleCommand(Remote::PLAY);
853 else if (x>=(keyx+474) && y>=(keyy+ getFontHeight() + 8) &&x<=(keyx+238+474) &&y<=(keyy+8+2*getFontHeight() + 2))
855 boxstack->handleCommand(Remote::GO);
857 else if ( x>=(chanListbox.getRootBoxOffsetX())
858 && y>=(chanListbox.getRootBoxOffsetY() + 5)
859 // &&x<=(chanListbox.getOffsetX()+155 + window_width * MINUTE_SCALE)
860 &&y<=(chanListbox.getRootBoxOffsetY() - getFontHeight()
861 - 3+(int)chanListbox.getHeight() + getFontHeight() + 3)
864 int cy=y-(chanListbox.getRootBoxOffsetY() + 5);
865 int row=cy/(getFontHeight()+2);
866 int clistTop = chanListbox.getTopOption();
867 chanListbox.hintSetCurrent(clistTop+row);
869 time_t ttime = cx*60/MINUTE_SCALE+ltime;
870 //x = 155 + (MINUTE_SCALE * (event->time - ltime) / 60);
874 boxstack->update(this);