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, see <https://www.gnu.org/licenses/>.
20 vepg presents a 2 dimensional electronic programme guide with channels down
21 the y axis and time along the x axis.
22 Programmes are layed on the x axis as alterate coloured blocks with as much
23 of the programme title as will fit inside the block shown as text.
24 Up and down commands step through the channels whilst left and right commands
25 move through the programmes of the currently selected channel.
26 When a programme is selected, it highlights in the grid and full programe details
27 (start time, title and description) are displayed in an area at te top left of the screen.
28 Any currently programmed timers will display in the grid and in the orogramme detail window as red
29 It is possible to select a programme to be recorded by pressing the record button.
30 The video stream currently being viewed is shown as quarter screen in the top right.
36 #include "vchannellist.h"
37 #include "messagequeue.h"
39 #include "vepgsettimer.h"
50 VEpg* VEpg::instance = NULL;
52 VEpg::VEpg(void* tparent, UINT tcurrentChannelIndex, ChannelList* tchanList)
55 currentChannelIndex = tcurrentChannelIndex;
57 // PAL / NTSC sizes -----------------------
60 //int summaryLines, summaryLowerPadding;
62 int fontHeight=getFontHeight() + 4;
63 //UINT gridRows; // is a class member
66 if (Video::getInstance()->getFormat() == Video::PAL)
83 //summaryLowerPadding = 28;
87 int screenwidthhalf=Video::getInstance()->getScreenWidth()/2;
88 int screenheighthalf=Video::getInstance()->getScreenHeight()/2;
89 // summaryLines = ((float)screenheighthalf)/((float)fontHeight))-1;
90 // summaryLowerPadding = screenheighthalf-summaryLines*(fontHeight);
91 gridRows = (screenheighthalf-fontHeight*3-50)/fontHeight;
94 // initialise variables and pointers
95 boxstack = BoxStack::getInstance();
101 eventLista.resize(gridRows);
102 for(UINT listIndex = 0; listIndex < gridRows; listIndex++)
104 // initialise array of pointers to eventlist structures
105 eventLista[listIndex] = NULL;
108 // Create pallet on which to paint our epg view and position it in centre of screen.
109 // Need to reduce size to deal with overscanning TVs.
111 setSize(Video::getInstance()->getScreenWidth(), Video::getInstance()->getScreenHeight());
116 // DrawStyle transparent = DrawStyle(0, 0, 0, 0);
117 // setBackgroundColour(transparent);
119 // progTitle.setSurface(surface);
120 progTitle.setPosition(0,0);
121 progTitle.setSize(screenwidthhalf,(fontHeight) * 1 + ypos); //paragraph line seperation is 4 pixels
122 progTitle.setBackgroundColour(DrawStyle::TITLEBARBACKGROUND);
123 progTitle.setTextPos(xpos, ypos);
125 progTitle.setParaMode(false);
128 // progInfo.setSurface(surface);
129 progInfo.setBackgroundColour(DrawStyle::VIEWBACKGROUND);
130 progInfo.setPosition(0, progTitle.getY2());
131 progInfo.setSize(screenwidthhalf,screenheighthalf-progTitle.getY2()+10);
132 progInfo.setTextPos(xpos, 0);
136 // chanName.setSurface(surface);
137 chanName.setSize(510, (fontHeight));
138 chanName.setPosition(screenwidthhalf, screenheighthalf - 2*fontHeight);
139 DrawStyle t1(0, 0, 0, 90);
140 chanName.setBackgroundColour(t1);
141 chanName.setParaMode(false);
144 // create area to display list of channels
145 // chanListbox.setSurface(surface); // add channel list
146 chanListbox.setPosition(0, progInfo.getY2() + fontHeight + 4); // position channel list
147 chanListbox.setSize(150, ((getFontHeight() + 2) * gridRows) + 5); //listbox line seperation is 2 pixels
148 chanListbox.setGap(2);
149 chanListbox.addColumn(xpos);
152 // populate channel list
157 for (UINT i = 0; i < chanList->size(); i++)
159 chan = (*chanList)[i];
160 if (i == currentChannelIndex)
162 chan->index = chanListbox.addOption(chan->name, 0, first);
165 chanName.setText((*chanList)[chanListbox.getCurrentOption()]->name);
168 window_x= chanListbox.getRootBoxOffsetX() + chanListbox.getWidth() + 5;
169 window_width=(Video::getInstance()->getScreenWidth() - window_x +3)/3;
171 listTop = chanListbox.getTopOption();
172 chanListbox.draw(); // doing this to allow chanListbox.getBottomOption() in updateEventList() to work
173 time(<ime); // set ltime to now
174 ltime = prevHour(<ime); // set ltime to previous hour TODO make this half hour?
175 time(&selTime); // set selTime to now
176 updateEventList(); // get list of programmes
178 vdisplay.mode=Window;
179 vdisplay.fallbackMode=Quarter;
180 vdisplay.x=Video::getInstance()->getScreenWidth()/2;
182 vdisplay.width=Video::getInstance()->getScreenWidth()/2;
183 vdisplay.height=Video::getInstance()->getScreenHeight()/2;
186 void VEpg::preDelete()
188 Timers::getInstance()->cancelTimer(this, 1);
196 for(UINT listIndex = 0; listIndex < gridRows; listIndex++)
198 if (eventLista[listIndex])
200 (eventLista)[listIndex]->clear();
201 delete eventLista[listIndex];
204 // delete [] eventLista; // FIXME
206 // destroy dynamically allocated memory
209 VEpg* VEpg::getInstance()
214 void VEpg::setInfo(Event* event)
217 struct tm btime; // to hold programme start and end time
218 char timeString[9]; // to hold programme start and end time
219 int length = strlen(event->title); // calculate length of programme title string
220 char* title = new char[length + 15]; // create string to hold start time, end time and programme title
221 time_t eventtime = event->time;
222 LOCALTIME_R((time_t*)&eventtime, &btime); //get programme start time
224 strftime(timeString, 9, "%0H:%0M - ", &btime); // and format it as hh:mm -
226 strftime(timeString, 9, "%H:%M - ", &btime); // and format it as hh:mm -
228 strcpy(title, timeString); // put it in our buffer
229 t = event->time + event->duration; //get programme end time
230 LOCALTIME_R(&t, &btime);
232 strftime(timeString, 7, "%0H:%0M ", &btime); // and format it as hh:mm -
234 strftime(timeString, 7, "%H:%M ", &btime); // and format it as hh:mm -
236 strcat(title, timeString); // put it in our buffer
238 strcat(title, event->title); // then add the programme title
239 progTitle.setText(title); // sput this sring in our text box
240 length = strlen(event->description);
241 char* info = new char[length + 1]; // create programme detail string
242 strcpy(info, event->description);
243 progInfo.setText(info); // show programme detail string
244 // destroy dynamically allocated memory
251 // View::draw(); // draw pallet
253 DrawStyle transparent = DrawStyle(0, 0, 0, 0);
254 fillColour(transparent);
257 // Moved all the dynamic data drawing to a seperate function
259 // Display the status and key stuff at the bottom
261 UINT keyx = chanListbox.getRootBoxOffsetX() + chanListbox.getColumn(0);
262 UINT keyy = chanListbox.getRootBoxOffsetY() + chanListbox.getHeight() + 2;
263 rectangle (0, keyy, Video::getInstance()->getScreenWidth() ,
264 Video::getInstance()->getScreenHeight()-keyy, DrawStyle::DARKGREY);
269 w.nextSymbol = WSymbol::LEFTARROW;
270 w.setPosition(keyx + 1, keyy + 20);
273 w.nextSymbol = WSymbol::UP;
274 w.setPosition(keyx + 26, keyy + 3);
277 w.nextSymbol = WSymbol::DOWN;
278 w.setPosition(keyx + 26, keyy + 36);
281 w.nextSymbol = WSymbol::RIGHTARROW;
282 w.setPosition(keyx + 50, keyy + 20);
285 drawTextCentre(tr("OK"), keyx + 35, keyy + 20, DrawStyle::LIGHTTEXT);
287 rectangle(keyx + 72, keyy + 4, 104, getFontHeight() + 2, DrawStyle::RED);
288 drawText(tr("Page up"), keyx + 74, keyy + 5, DrawStyle::LIGHTTEXT);
290 rectangle(keyx + 72, keyy + getFontHeight() + 8, 104, getFontHeight() + 2, DrawStyle::GREEN);
291 drawText(tr("Page down"), keyx + 74, keyy + getFontHeight() + 9, DrawStyle::LIGHTTEXT);
293 rectangle(keyx + 180, keyy + 4, 104, getFontHeight() + 2, DrawStyle::YELLOW);
294 drawText(tr("-24 hours"), keyx + 182, keyy + 5, DrawStyle::LIGHTTEXT);
296 rectangle(keyx + 180, keyy + getFontHeight() + 8, 104, getFontHeight() + 2, DrawStyle::BLUE);
297 drawText(tr("+24 hours"), keyx + 182, keyy + getFontHeight() + 9, DrawStyle::LIGHTTEXT);
299 rectangle(keyx + 290, keyy + 4, 180, getFontHeight() + 2, DrawStyle::GREY);
300 drawText(tr("Guide / Back: Close"), keyx + 292 , keyy + 5, DrawStyle::LIGHTTEXT);
302 rectangle(keyx + 290, keyy + getFontHeight() + 8, 180, getFontHeight() + 2, DrawStyle::GREY);
303 DrawStyle red = DrawStyle(130, 0, 0);
304 drawText(tr("Rec: Set timer"), keyx + 292, keyy + getFontHeight() + 9, red);
306 rectangle(keyx + 474, keyy + 4, 128, getFontHeight() + 2, DrawStyle::GREY);
307 w.nextSymbol = WSymbol::PLAY;
308 w.setPosition(keyx + 476, keyy + 5);
310 drawText(tr("Sel channel"), keyx + 496, keyy + 5, DrawStyle::LIGHTTEXT);
312 rectangle(keyx + 474, keyy + getFontHeight() + 8, 128, getFontHeight() + 2, DrawStyle::GREY);
313 drawText(tr("Go: Preview"), keyx + 476, keyy + getFontHeight() + 9, DrawStyle::LIGHTTEXT);
316 // Draw all the dynamic data
320 void VEpg::drawData()
322 // Not doing View::draw() every time causes
323 // things not to be cleared off the surface properly
324 // So, blank out the data area first
325 //int screenwidth=Video::getInstance()->getScreenWidth();
327 chanListbox.getRootBoxOffsetX(),
328 chanListbox.getRootBoxOffsetY() - getFontHeight() - 3,
329 window_width * MINUTE_SCALE,
330 chanListbox.getHeight() + getFontHeight() + 4,
335 chanName.draw(); // TODO this should be dealt with by vvideolive
340 // Set timer to redraw to move the current time bar
344 if (dt == 0) dt = 60;
346 Timers::getInstance()->setTimerT(this, 1, dt);
349 void VEpg::timercall(int clientReference)
352 boxstack->update(this);
355 int VEpg::handleCommand(int command)
360 { // cursor up the channel list
363 boxstack->update(this);
367 { // cursor down the channel list
368 Log::getInstance()->log("VEPG", Log::DEBUG, "Down start");
372 boxstack->update(this);
373 Log::getInstance()->log("VEPG", Log::DEBUG, "Down end");
378 { // cursor left through time
379 selTime = thisEvent.time - 1;
381 boxstack->update(this);
386 // cursor right through time
387 selTime = thisEvent.time + thisEvent.duration;
389 boxstack->update(this);
394 // cursor up one page
395 chanListbox.pageUp();
397 boxstack->update(this);
402 // cursor down one page
403 chanListbox.pageDown();
405 boxstack->update(this);
410 // step forward 24 hours
411 selTime += 24 * 60 * 60;
413 boxstack->update(this);
418 // step forward 24 hours
419 selTime -= 24 * 60 * 60;
421 boxstack->update(this);
426 if (!chanList) return 2;
427 Log::getInstance()->log("VEPG", Log::DEBUG, "ID %lu TIME %lu DURATION %lu TITLE %s", thisEvent.id, thisEvent.time, thisEvent.duration, thisEvent.title);
428 VEpgSetTimer* vs = new VEpgSetTimer(&thisEvent, (*chanList)[chanListbox.getCurrentOption()]);
431 boxstack->update(vs);
438 if (!chanList) return 2;
440 // select programme and display menu TODO currently just changes to selected channel
442 currentChannelIndex = chanListbox.getCurrentOption();
446 Message* m = new Message(); // Must be done after this view deleted
449 m->message = Message::CHANNEL_CHANGE;
450 m->parameter = (*chanList)[currentChannelIndex]->number;
451 MessageQueue::getInstance()->postMessage(m);
456 if(command == Input::GO)
458 // GO just changes channel in preview, PLAY changes channel and returns to normal TV
465 case Input::CHANNELUP:
467 if (currentChannelIndex == (chanList->size() - 1)) // at the end
468 currentChannelIndex = 0;
470 ++currentChannelIndex;
474 Message* m = new Message(); // Must be done after this view deleted
477 m->message = Message::CHANNEL_CHANGE;
478 m->parameter = (*chanList)[currentChannelIndex]->number;
479 MessageQueue::getInstance()->postMessage(m);
486 case Input::CHANNELDOWN:
488 if (currentChannelIndex == 0) // at the start
489 currentChannelIndex = chanList->size() - 1; // so go to end
491 --currentChannelIndex;
495 Message* m = new Message(); // Must be done after this view deleted
498 m->message = Message::CHANNEL_CHANGE;
499 m->parameter = (*chanList)[currentChannelIndex]->number;
500 MessageQueue::getInstance()->postMessage(m);
508 // stop command getting to any more views
512 void VEpg::drawgrid() // redraws grid and select programme
514 // draw the grid of programmes
517 time(&t); // set t = now
519 selTime = t; // don't allow cursor in the past
520 if(listTop != chanListbox.getTopOption())
522 // chanListbox has scrolled TODO speed up by changing only rows that have changed
523 listTop = chanListbox.getTopOption();
526 if ((selTime >= ltime + (int)window_width * 60) || (selTime <= ltime))
528 // we have cursored back before left time of window
529 //TODO check that this and above don't happen together
530 ltime = prevHour(&selTime);
534 DrawStyle white = DrawStyle(255, 255, 255, 255);
539 LOCALTIME_R(&t, &tms);
540 strftime(timeString, 19, "%a %d %b", &tms);
541 int timey = chanListbox.getRootBoxOffsetY() - getFontHeight() - 3;
543 drawTextRJ(timeString, timex - 10, timey, DrawStyle::LIGHTTEXT); // print date
544 strftime(timeString, 19, "%H:%M", &tms);
545 drawText(timeString, timex, timey, DrawStyle::LIGHTTEXT); // print left time
547 rectangle(155, timey + getFontHeight(), 2, 7, white);
549 LOCALTIME_R(&t, &tms);
550 strftime(timeString, 19, "%H:%M", &tms);
551 drawText(timeString, timex + 180, timey, DrawStyle::LIGHTTEXT); // print middle time
552 rectangle(335, timey + getFontHeight(), 2, 7, white);
554 LOCALTIME_R(&t, &tms);
555 strftime(timeString, 19, "%H:%M", &tms);
556 drawText(timeString, timex + 360, timey, DrawStyle::LIGHTTEXT); // print right time
557 rectangle(515, timey + getFontHeight(), 2, 7, white);
558 // pointer to selTime
559 //rectangle(155 + (selTime - ltime) / 20, timey + getFontHeight(), 2, 7, DrawStyle(255, 50, 50, 255));
563 if ((t >= ltime) && (t < (ltime + 9000)))
565 rectangle(155 + (t - ltime) / 20, timey + getFontHeight(), 2, ((getFontHeight() + 2) * gridRows) + 7 + 2, DrawStyle::RED);
568 // TODO should the above two comditional statements be combined to avoid calling updateEventList() twice?
570 Event noevent; // an event to use if there are gaps in the epg
571 thisEvent.setdescription(tr("There are no programme details available for this period"));
572 thisEvent.duration = window_width * 60;
573 thisEvent.time = ltime;
574 thisEvent.settitle(tr("No programme details"));
576 bool swapColour = false; // alternate cell colour
577 bool currentRow = false;
578 int y = chanListbox.getRootBoxOffsetY() + 5; // vertical position of cell
579 DrawStyle bg, fg; // background colour of cells in grid
580 // for each displayed channel, find programmes that fall in 2.5 hour time window
581 for(UINT listIndex = 0; listIndex < gridRows; listIndex++)
583 if (listTop + (int)listIndex >= chanListbox.getBottomOption())
584 continue; // ensure nothing populates grid below last channel
586 currentRow = (listTop + (int)listIndex == chanListbox.getCurrentOption());
587 noevent.time = ltime;
588 noevent.duration = window_width * 60;
589 noevent.settitle("");
590 paintCell(&noevent, y, DrawStyle::NOPROGRAMME, DrawStyle::LIGHTTEXT); // fill row with no programme colour to be painted ove with valid programmes
593 thisEvent.setdescription(tr("There are no programme details available for this period"));
594 thisEvent.duration = window_width * 60;
595 thisEvent.time = ltime;
596 thisEvent.settitle(tr("No programme details"));
599 if (eventLista[listIndex])
601 sort(eventLista[listIndex]->begin(), eventLista[listIndex]->end(), EventSorter());
602 for(e = 0; e < (eventLista[listIndex])->size(); e++) // step through events for this channel
604 fg = DrawStyle::LIGHTTEXT;
605 event = (*eventLista[listIndex])[e];
608 UINT end = event->time + event->duration; // programme end time
609 if(event->time >= UINT(ltime) + (window_width * 60)) // programme starts after RHS of window
610 continue; // that's enough of this channel's events
611 if(end <= UINT(ltime)) // programme ends before LHS of window
612 continue; // this event is before the window - let's try the next event
613 // this event is one we are interested in
614 bg = (swapColour)?DrawStyle::PROGRAMMEA:DrawStyle::PROGRAMMEB; // alternate cell colour
615 swapColour = !swapColour; // it wil be the other colour next time
616 if(event->time <= UINT(selTime) && end > UINT(selTime) && currentRow)
618 // this is the selected programme
619 thisEvent.setdescription(event->description);
620 thisEvent.duration = event->duration;
621 thisEvent.time = event->time;
622 thisEvent.settitle(event->title);
623 thisEvent.id = event->id;
624 if(thisEvent.id == 0)
626 bg = DrawStyle::SELECTHIGHLIGHT; // highlight cell
627 fg = DrawStyle::DARKTEXT;
631 if (currentRow && thisEvent.id == 0)
633 if (end <= UINT(selTime) && end > UINT(thisEvent.time))
634 thisEvent.time = end;
635 if (event->time > UINT(selTime) && event->time < thisEvent.time + thisEvent.duration)
636 thisEvent.duration = event->time - thisEvent.time;
639 paintCell(event, y, bg, fg);
645 // no event list for this channel. Already painted noevent colour so just highlight if selected
648 bg = DrawStyle::SELECTHIGHLIGHT; // highlight cell
649 fg = DrawStyle::DARKTEXT;
650 paintCell(&thisEvent, y, bg, fg);
654 bg = DrawStyle::NOPROGRAMME;
655 fg = DrawStyle::LIGHTTEXT;
656 noevent.settitle(tr("No programme details"));
657 paintCell(&noevent, y, bg, fg);
660 y += getFontHeight() + 2;
665 void VEpg::updateEventList()
667 if (!chanList) return;
669 for(UINT listIndex = 0; listIndex < gridRows; listIndex++)
671 if(listTop + listIndex >= UINT(chanListbox.getBottomOption()))
673 chan = (*chanList)[listTop + listIndex];
674 if (eventLista[listIndex])
676 (eventLista)[listIndex]->clear();
677 delete eventLista[listIndex];
679 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
683 void VEpg::setCurrentChannel()
685 chanName.setText((*chanList)[currentChannelIndex]->name);
688 chanName.getRootBoxRegion(&r);
689 boxstack->update(this, &r);
692 void VEpg::paintCell(Event* event, int yOffset, const DrawStyle& bg, const DrawStyle& fg)
695 w = x = 0; // keep compiler happy
697 h = getFontHeight(); // TODO if want border around text, need to increae this and wselectlist line height
698 UINT end = event->time + event->duration; // programme end time
699 if(event->time <= UINT(ltime) && end > UINT(ltime)) // spans start of displayed window
701 x = 155; // LHS of window
702 if (end > (UINT(ltime) + (window_width * 60)))
703 w = window_width * MINUTE_SCALE; // spans full 2 hour window
705 w = MINUTE_SCALE * (event->time + event->duration - ltime ) / 60; // get width of remaining programme
707 if((event->time >= UINT(ltime)) && (event->time <= UINT(ltime) + (window_width * 60))) // starts within window
709 x = 155 + (MINUTE_SCALE * (event->time - ltime) / 60);
710 w = MINUTE_SCALE * event->duration / 60;
711 //if (w > 155 + MINUTE_SCALE * WINDOW_WIDTH -x)
712 // w = w + x - 155 - MINUTE_SCALE * WINDOW_WIDTH; // ends outside window
714 if (w > 155 + (int)window_width * MINUTE_SCALE - x)
715 w = 155 + window_width * MINUTE_SCALE -x; // limit cells to RHS of window
716 rectangle(x, y, w, h, bg);
717 char* tt = new char[strlen(event->title) + 1];
718 strcpy (tt, event->title);
720 unsigned int cur_length=1;
721 unsigned int text_max=strlen(tt);
723 if (Osd::getInstance()->charSet()!=1) mchar=true;
725 memset((void*)&state,0,sizeof(state));
728 for (textPos = 0; textPos <text_max; textPos+=cur_length)
732 cur_length = mbrtowc(&cur_char, tt + textPos, text_max-textPos, &state);
733 if (cur_length <= 0){
736 } else cur_char= *(tt+textPos);
737 float thisCharWidth = charWidth(cur_char);
738 if (textWidth + thisCharWidth > w) // text will not fit in cell
742 textWidth += thisCharWidth;
744 char* tT = new char[textPos+1];
747 strncpy(tT, tt, textPos );
749 surface->drawText(tT, x+2, y, fg);
755 time_t VEpg::prevHour(time_t* t)
758 LOCALTIME_R(t, &tms);
764 void VEpg::processMessage(Message* m)
766 if (m->message == Message::MOUSE_MOVE)
768 if (chanListbox.mouseMove((m->parameter>>16)-getScreenX(),(m->parameter&0xFFFF)-getScreenY()))
771 boxstack->update(this);
774 else if (m->message == Message::MOUSE_LBDOWN)
776 if (chanListbox.mouseLBDOWN((m->parameter>>16)-getScreenX(),(m->parameter&0xFFFF)-getScreenY()))
778 boxstack->handleCommand(Input::OK); //simulate OK press
782 //check if press is outside this view! then simulate cancel
783 int x=(m->parameter>>16)-getScreenX();
784 int y=(m->parameter&0xFFFF)-getScreenY();
785 int keyx = chanListbox.getRootBoxOffsetX();
786 int keyy = chanListbox.getRootBoxOffsetY() + chanListbox.getHeight() + 2;
788 if (x<0 || y <0 || x>(int)getWidth() || y>(int)getHeight())
790 boxstack->handleCommand(Input::BACK); //simulate cancel press
792 else if (x>=(keyx+72) && y>=(keyy+4) &&x<=(keyx+72+104) &&y<=(keyy+4+getFontHeight() + 2))
794 boxstack->handleCommand(Input::RED);
796 else if (x>=(keyx+72) && y>=(keyy+ getFontHeight() + 8) &&x<=(keyx+72+104) &&y<=(keyy+8+2*getFontHeight() + 2))
798 boxstack->handleCommand(Input::GREEN);
800 else if (x>=(keyx+180) && y>=(keyy+4) &&x<=(keyx+180+104) &&y<=(keyy+4+getFontHeight() + 2))
802 boxstack->handleCommand(Input::YELLOW);
804 else if (x>=(keyx+180) && y>=(keyy+ getFontHeight() + 8) &&x<=(keyx+180+104) &&y<=(keyy+8+2*getFontHeight() + 2))
806 boxstack->handleCommand(Input::BLUE);
808 else if (x>=(keyx+290) && y>=(keyy+4) &&x<=(keyx+180+290) &&y<=(keyy+4+getFontHeight() + 2))
810 boxstack->handleCommand(Input::BACK);
812 else if (x>=(keyx+290) && y>=(keyy+ getFontHeight() + 8) &&x<=(keyx+290+180) &&y<=(keyy+8+2*getFontHeight() + 2))
814 boxstack->handleCommand(Input::RECORD);
816 else if (x>=(keyx+474) && y>=(keyy+4) &&x<=(keyx+128+474) &&y<=(keyy+4+getFontHeight() + 2))
818 boxstack->handleCommand(Input::PLAY);
820 else if (x>=(keyx+474) && y>=(keyy+ getFontHeight() + 8) &&x<=(keyx+238+474) &&y<=(keyy+8+2*getFontHeight() + 2))
822 boxstack->handleCommand(Input::GO);
824 else if ( x>=(chanListbox.getRootBoxOffsetX())
825 && y>=(chanListbox.getRootBoxOffsetY() + 5)
826 // &&x<=(chanListbox.getOffsetX()+155 + window_width * MINUTE_SCALE)
827 &&y<=(chanListbox.getRootBoxOffsetY() - getFontHeight()
828 - 3+(int)chanListbox.getHeight() + getFontHeight() + 3)
831 int cy=y-(chanListbox.getRootBoxOffsetY() + 5);
832 int row=cy/(getFontHeight()+2);
833 int clistTop = chanListbox.getTopOption();
834 chanListbox.hintSetCurrent(clistTop+row);
836 time_t ttime = cx*60/MINUTE_SCALE+ltime;
837 //x = 155 + (MINUTE_SCALE * (event->time - ltime) / 60);
841 boxstack->update(this);