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.
34 #include "vchannellist.h"
35 #include "messagequeue.h"
38 #include "vepgsettimer.h"
49 static const char* TAG = "VEpg";
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 // setBackgroundColour(DrawStyle::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.c_str()); // 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(&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.c_str()); // then add the programme title
239 progTitle.setText(title); // sput this sring in our text box
240 length = event->description.length();
241 char* info = new char[length + 1]; // create programme detail string
242 strcpy(info, event->description.c_str());
243 progInfo.setText(info); // show programme detail string
244 // destroy dynamically allocated memory
251 // View::draw(); // draw pallet
253 fillColour(DrawStyle::TRANSPARENT);
256 // Moved all the dynamic data drawing to a seperate function
258 // Display the status and key stuff at the bottom
260 UINT keyx = chanListbox.getRootBoxOffsetX() + chanListbox.getColumn(0);
261 UINT keyy = chanListbox.getRootBoxOffsetY() + chanListbox.getHeight() + 2;
262 rectangle (0, keyy, Video::getInstance()->getScreenWidth() ,
263 Video::getInstance()->getScreenHeight()-keyy, DrawStyle::DARKGREY);
268 w.nextSymbol = WSymbol::LEFTARROW;
269 w.setPosition(keyx + 1, keyy + 20);
272 w.nextSymbol = WSymbol::UP;
273 w.setPosition(keyx + 26, keyy + 3);
276 w.nextSymbol = WSymbol::DOWN;
277 w.setPosition(keyx + 26, keyy + 36);
280 w.nextSymbol = WSymbol::RIGHTARROW;
281 w.setPosition(keyx + 50, keyy + 20);
284 drawTextCentre(tr("OK"), keyx + 35, keyy + 20, DrawStyle::LIGHTTEXT);
286 rectangle(keyx + 72, keyy + 4, 104, getFontHeight() + 2, DrawStyle::RED);
287 drawText(tr("Page up"), keyx + 74, keyy + 5, DrawStyle::LIGHTTEXT);
289 rectangle(keyx + 72, keyy + getFontHeight() + 8, 104, getFontHeight() + 2, DrawStyle::GREEN);
290 drawText(tr("Page down"), keyx + 74, keyy + getFontHeight() + 9, DrawStyle::LIGHTTEXT);
292 rectangle(keyx + 180, keyy + 4, 104, getFontHeight() + 2, DrawStyle::YELLOW);
293 drawText(tr("-24 hours"), keyx + 182, keyy + 5, DrawStyle::LIGHTTEXT);
295 rectangle(keyx + 180, keyy + getFontHeight() + 8, 104, getFontHeight() + 2, DrawStyle::BLUE);
296 drawText(tr("+24 hours"), keyx + 182, keyy + getFontHeight() + 9, DrawStyle::LIGHTTEXT);
298 rectangle(keyx + 290, keyy + 4, 180, getFontHeight() + 2, DrawStyle::GREY);
299 drawText(tr("Guide / Back: Close"), keyx + 292 , keyy + 5, DrawStyle::LIGHTTEXT);
301 rectangle(keyx + 290, keyy + getFontHeight() + 8, 180, getFontHeight() + 2, DrawStyle::GREY);
302 DrawStyle red = DrawStyle(130, 0, 0);
303 drawText(tr("Rec: Set timer"), keyx + 292, keyy + getFontHeight() + 9, red);
305 rectangle(keyx + 474, keyy + 4, 128, getFontHeight() + 2, DrawStyle::GREY);
306 w.nextSymbol = WSymbol::PLAY;
307 w.setPosition(keyx + 476, keyy + 5);
309 drawText(tr("Sel channel"), keyx + 496, keyy + 5, DrawStyle::LIGHTTEXT);
311 rectangle(keyx + 474, keyy + getFontHeight() + 8, 128, getFontHeight() + 2, DrawStyle::GREY);
312 drawText(tr("Go: Preview"), keyx + 476, keyy + getFontHeight() + 9, DrawStyle::LIGHTTEXT);
315 // Draw all the dynamic data
319 void VEpg::drawData()
321 // Not doing View::draw() every time causes
322 // things not to be cleared off the surface properly
323 // So, blank out the data area first
324 //int screenwidth=Video::getInstance()->getScreenWidth();
326 chanListbox.getRootBoxOffsetX(),
327 chanListbox.getRootBoxOffsetY() - getFontHeight() - 3,
328 window_width * MINUTE_SCALE,
329 chanListbox.getHeight() + getFontHeight() + 4,
334 chanName.draw(); // TODO this should be dealt with by vvideolive
339 // Set timer to redraw to move the current time bar
343 if (dt == 0) dt = 60;
345 Timers::getInstance()->setTimerT(this, 1, dt);
348 void VEpg::timercall(int /*clientReference*/)
351 boxstack->update(this);
354 int VEpg::handleCommand(int command)
359 { // cursor up the channel list
362 boxstack->update(this);
363 return BoxStack::COMMAND_HANDLED;
366 { // cursor down the channel list
367 LogNT::getInstance()->debug(TAG, "Down start");
371 boxstack->update(this);
372 LogNT::getInstance()->debug(TAG, "Down end");
374 return BoxStack::COMMAND_HANDLED;
377 { // cursor left through time
378 selTime = thisEvent.time - 1;
380 boxstack->update(this);
381 return BoxStack::COMMAND_HANDLED;
385 // cursor right through time
386 selTime = thisEvent.time + thisEvent.duration;
388 boxstack->update(this);
389 return BoxStack::COMMAND_HANDLED;
393 // cursor up one page
394 chanListbox.pageUp();
396 boxstack->update(this);
397 return BoxStack::COMMAND_HANDLED;
401 // cursor down one page
402 chanListbox.pageDown();
404 boxstack->update(this);
405 return BoxStack::COMMAND_HANDLED;
409 // step forward 24 hours
410 selTime += 24 * 60 * 60;
412 boxstack->update(this);
413 return BoxStack::COMMAND_HANDLED;
417 // step forward 24 hours
418 selTime -= 24 * 60 * 60;
420 boxstack->update(this);
421 return BoxStack::COMMAND_HANDLED;
425 if (!chanList) return 2;
426 LogNT::getInstance()->debug(TAG, "ID {} TIME {} DURATION {} TITLE {}", thisEvent.id, thisEvent.time, thisEvent.duration, thisEvent.title);
427 VEpgSetTimer* vs = new VEpgSetTimer(&thisEvent, (*chanList)[chanListbox.getCurrentOption()]);
430 boxstack->update(vs);
431 return BoxStack::COMMAND_HANDLED;
437 if (!chanList) return BoxStack::COMMAND_HANDLED;
438 // select programme and display menu TODO currently just changes to selected channel
440 currentChannelIndex = chanListbox.getCurrentOption();
444 Message* m = new Message(); // Must be done after this view deleted
447 m->message = Message::CHANNEL_CHANGE;
448 m->parameter = (*chanList)[currentChannelIndex]->number;
449 MessageQueue::getInstance()->postMessage(m);
454 if(command == Input::GO)
455 return BoxStack::COMMAND_HANDLED;
456 // GO just changes channel in preview, PLAY changes channel and returns to normal TV
461 return BoxStack::DELETE_ME;
463 case Input::CHANNELUP:
465 if (currentChannelIndex == (chanList->size() - 1)) // at the end
466 currentChannelIndex = 0;
468 ++currentChannelIndex;
472 Message* m = new Message(); // Must be done after this view deleted
475 m->message = Message::CHANNEL_CHANGE;
476 m->parameter = (*chanList)[currentChannelIndex]->number;
477 MessageQueue::getInstance()->postMessage(m);
482 return BoxStack::COMMAND_HANDLED;
484 case Input::CHANNELDOWN:
486 if (currentChannelIndex == 0) // at the start
487 currentChannelIndex = chanList->size() - 1; // so go to end
489 --currentChannelIndex;
493 Message* m = new Message(); // Must be done after this view deleted
496 m->message = Message::CHANNEL_CHANGE;
497 m->parameter = (*chanList)[currentChannelIndex]->number;
498 MessageQueue::getInstance()->postMessage(m);
503 return BoxStack::COMMAND_HANDLED;
506 // stop command getting to any more views
507 return BoxStack::ABANDON_COMMAND;
510 void VEpg::drawgrid() // redraws grid and select programme
512 // draw the grid of programmes
515 time(&t); // set t = now
517 selTime = t; // don't allow cursor in the past
518 if(listTop != chanListbox.getTopOption())
520 // chanListbox has scrolled TODO speed up by changing only rows that have changed
521 listTop = chanListbox.getTopOption();
524 if ((selTime >= ltime + (int)window_width * 60) || (selTime <= ltime))
526 // we have cursored back before left time of window
527 //TODO check that this and above don't happen together
528 ltime = prevHour(&selTime);
535 LOCALTIME_R(&t, &tms);
536 strftime(timeString, 19, "%a %d %b", &tms);
537 int timey = chanListbox.getRootBoxOffsetY() - getFontHeight() - 3;
539 drawTextRJ(timeString, timex - 10, timey, DrawStyle::LIGHTTEXT); // print date
540 strftime(timeString, 19, "%H:%M", &tms);
541 drawText(timeString, timex, timey, DrawStyle::LIGHTTEXT); // print left time
543 rectangle(155, timey + getFontHeight(), 2, 7, DrawStyle::WHITE);
545 LOCALTIME_R(&t, &tms);
546 strftime(timeString, 19, "%H:%M", &tms);
547 drawText(timeString, timex + 180, timey, DrawStyle::LIGHTTEXT); // print middle time
548 rectangle(335, timey + getFontHeight(), 2, 7, DrawStyle::WHITE);
550 LOCALTIME_R(&t, &tms);
551 strftime(timeString, 19, "%H:%M", &tms);
552 drawText(timeString, timex + 360, timey, DrawStyle::LIGHTTEXT); // print right time
553 rectangle(515, timey + getFontHeight(), 2, 7, DrawStyle::WHITE);
554 // pointer to selTime
555 //rectangle(155 + (selTime - ltime) / 20, timey + getFontHeight(), 2, 7, DrawStyle(255, 50, 50, 255));
559 if ((t >= ltime) && (t < (ltime + 9000)))
561 rectangle(155 + (t - ltime) / 20, timey + getFontHeight(), 2, ((getFontHeight() + 2) * gridRows) + 7 + 2, DrawStyle::RED);
564 // TODO should the above two comditional statements be combined to avoid calling updateEventList() twice?
566 Event noevent; // an event to use if there are gaps in the epg
567 thisEvent.description = tr("There are no programme details available for this period");
568 thisEvent.duration = window_width * 60;
569 thisEvent.time = ltime;
570 thisEvent.title = tr("No programme details");
572 bool swapColour = false; // alternate cell colour
573 bool currentRow = false;
574 int y = chanListbox.getRootBoxOffsetY() + 5; // vertical position of cell
575 DrawStyle bg, fg; // background colour of cells in grid
576 // for each displayed channel, find programmes that fall in 2.5 hour time window
577 for(UINT listIndex = 0; listIndex < gridRows; listIndex++)
579 if (listTop + (int)listIndex >= chanListbox.getBottomOption())
580 continue; // ensure nothing populates grid below last channel
582 currentRow = (listTop + (int)listIndex == chanListbox.getCurrentOption());
583 noevent.time = ltime;
584 noevent.duration = window_width * 60;
586 paintCell(&noevent, y, DrawStyle::NOPROGRAMME, DrawStyle::LIGHTTEXT); // fill row with no programme colour to be painted ove with valid programmes
589 thisEvent.description = tr("There are no programme details available for this period");
590 thisEvent.duration = window_width * 60;
591 thisEvent.time = ltime;
592 thisEvent.title = tr("No programme details");
595 if (eventLista[listIndex])
597 sort(eventLista[listIndex]->begin(), eventLista[listIndex]->end(), EventSorter());
598 for(e = 0; e < (eventLista[listIndex])->size(); e++) // step through events for this channel
600 fg = DrawStyle::LIGHTTEXT;
601 event = (*eventLista[listIndex])[e];
604 UINT end = event->time + event->duration; // programme end time
605 if(event->time >= UINT(ltime) + (window_width * 60)) // programme starts after RHS of window
606 continue; // that's enough of this channel's events
607 if(end <= UINT(ltime)) // programme ends before LHS of window
608 continue; // this event is before the window - let's try the next event
609 // this event is one we are interested in
610 bg = (swapColour)?DrawStyle::PROGRAMMEA:DrawStyle::PROGRAMMEB; // alternate cell colour
611 swapColour = !swapColour; // it wil be the other colour next time
612 if(event->time <= UINT(selTime) && end > UINT(selTime) && currentRow)
614 // this is the selected programme
615 thisEvent.description = event->description;
616 thisEvent.duration = event->duration;
617 thisEvent.time = event->time;
618 thisEvent.title = event->title;
619 thisEvent.id = event->id;
620 if(thisEvent.id == 0)
622 bg = DrawStyle::SELECTHIGHLIGHT; // highlight cell
623 fg = DrawStyle::DARKTEXT;
627 if (currentRow && thisEvent.id == 0)
629 if (end <= UINT(selTime) && end > UINT(thisEvent.time))
630 thisEvent.time = end;
631 if (event->time > UINT(selTime) && event->time < thisEvent.time + thisEvent.duration)
632 thisEvent.duration = event->time - thisEvent.time;
635 paintCell(event, y, bg, fg);
641 // no event list for this channel. Already painted noevent colour so just highlight if selected
644 bg = DrawStyle::SELECTHIGHLIGHT; // highlight cell
645 fg = DrawStyle::DARKTEXT;
646 paintCell(&thisEvent, y, bg, fg);
650 bg = DrawStyle::NOPROGRAMME;
651 fg = DrawStyle::LIGHTTEXT;
652 noevent.title = tr("No programme details");
653 paintCell(&noevent, y, bg, fg);
656 y += getFontHeight() + 2;
661 void VEpg::updateEventList()
663 if (!chanList) return;
665 for(UINT listIndex = 0; listIndex < gridRows; listIndex++)
667 if(listTop + listIndex >= UINT(chanListbox.getBottomOption()))
669 chan = (*chanList)[listTop + listIndex];
670 if (eventLista[listIndex])
672 (eventLista)[listIndex]->clear();
673 delete eventLista[listIndex];
675 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
679 void VEpg::setCurrentChannel()
681 chanName.setText((*chanList)[currentChannelIndex]->name);
684 chanName.getRootBoxRegion(&r);
685 boxstack->update(this, &r);
688 void VEpg::paintCell(Event* event, int yOffset, const DrawStyle& bg, const DrawStyle& fg)
691 w = x = 0; // keep compiler happy
693 h = getFontHeight(); // TODO if want border around text, need to increae this and wselectlist line height
694 UINT end = event->time + event->duration; // programme end time
695 if(event->time <= UINT(ltime) && end > UINT(ltime)) // spans start of displayed window
697 x = 155; // LHS of window
698 if (end > (UINT(ltime) + (window_width * 60)))
699 w = window_width * MINUTE_SCALE; // spans full 2 hour window
701 w = MINUTE_SCALE * (event->time + event->duration - ltime ) / 60; // get width of remaining programme
703 if((event->time >= UINT(ltime)) && (event->time <= UINT(ltime) + (window_width * 60))) // starts within window
705 x = 155 + (MINUTE_SCALE * (event->time - ltime) / 60);
706 w = MINUTE_SCALE * event->duration / 60;
707 //if (w > 155 + MINUTE_SCALE * WINDOW_WIDTH -x)
708 // w = w + x - 155 - MINUTE_SCALE * WINDOW_WIDTH; // ends outside window
710 if (w > 155 + (int)window_width * MINUTE_SCALE - x)
711 w = 155 + window_width * MINUTE_SCALE -x; // limit cells to RHS of window
712 rectangle(x, y, w, h, bg);
713 char* tt = new char[strlen(event->title.c_str()) + 1];
714 strcpy (tt, event->title.c_str());
716 unsigned int cur_length=1;
717 unsigned int text_max=strlen(tt);
719 if (Osd::getInstance()->charSet()!=1) mchar=true;
721 memset((void*)&state,0,sizeof(state));
724 for (textPos = 0; textPos <text_max; textPos+=cur_length)
728 cur_length = mbrtowc(&cur_char, tt + textPos, text_max-textPos, &state);
729 if (cur_length <= 0){
732 } else cur_char= *(tt+textPos);
733 float thisCharWidth = charWidth(cur_char);
734 if (textWidth + thisCharWidth > w) // text will not fit in cell
738 textWidth += thisCharWidth;
740 char* tT = new char[textPos+1];
743 strncpy(tT, tt, textPos );
745 surface->drawText(tT, x+2, y, fg);
751 time_t VEpg::prevHour(time_t* t)
754 LOCALTIME_R(t, &tms);
760 void VEpg::processMessage(Message* m)
762 if (m->message == Message::MOUSE_MOVE)
764 if (chanListbox.mouseMove(m->parameter - getScreenX(), m->tag - getScreenY()))
767 boxstack->update(this);
770 else if (m->message == Message::MOUSE_LBDOWN)
772 if (chanListbox.mouseLBDOWN(m->parameter - getScreenX(), m->tag - getScreenY()))
774 boxstack->handleCommand(Input::OK); //simulate OK press
778 //check if press is outside this view! then simulate cancel
779 int x = m->parameter - getScreenX();
780 int y = m->tag - getScreenY();
781 int keyx = chanListbox.getRootBoxOffsetX();
782 int keyy = chanListbox.getRootBoxOffsetY() + chanListbox.getHeight() + 2;
784 if (x<0 || y <0 || x>(int)getWidth() || y>(int)getHeight())
786 boxstack->handleCommand(Input::BACK); //simulate cancel press
788 else if (x>=(keyx+72) && y>=(keyy+4) &&x<=(keyx+72+104) &&y<=(keyy+4+getFontHeight() + 2))
790 boxstack->handleCommand(Input::RED);
792 else if (x>=(keyx+72) && y>=(keyy+ getFontHeight() + 8) &&x<=(keyx+72+104) &&y<=(keyy+8+2*getFontHeight() + 2))
794 boxstack->handleCommand(Input::GREEN);
796 else if (x>=(keyx+180) && y>=(keyy+4) &&x<=(keyx+180+104) &&y<=(keyy+4+getFontHeight() + 2))
798 boxstack->handleCommand(Input::YELLOW);
800 else if (x>=(keyx+180) && y>=(keyy+ getFontHeight() + 8) &&x<=(keyx+180+104) &&y<=(keyy+8+2*getFontHeight() + 2))
802 boxstack->handleCommand(Input::BLUE);
804 else if (x>=(keyx+290) && y>=(keyy+4) &&x<=(keyx+180+290) &&y<=(keyy+4+getFontHeight() + 2))
806 boxstack->handleCommand(Input::BACK);
808 else if (x>=(keyx+290) && y>=(keyy+ getFontHeight() + 8) &&x<=(keyx+290+180) &&y<=(keyy+8+2*getFontHeight() + 2))
810 boxstack->handleCommand(Input::RECORD);
812 else if (x>=(keyx+474) && y>=(keyy+4) &&x<=(keyx+128+474) &&y<=(keyy+4+getFontHeight() + 2))
814 boxstack->handleCommand(Input::PLAY);
816 else if (x>=(keyx+474) && y>=(keyy+ getFontHeight() + 8) &&x<=(keyx+238+474) &&y<=(keyy+8+2*getFontHeight() + 2))
818 boxstack->handleCommand(Input::GO);
820 else if ( x>=(chanListbox.getRootBoxOffsetX())
821 && y>=(chanListbox.getRootBoxOffsetY() + 5)
822 // &&x<=(chanListbox.getOffsetX()+155 + window_width * MINUTE_SCALE)
823 &&y<=(chanListbox.getRootBoxOffsetY() - getFontHeight()
824 - 3+(int)chanListbox.getHeight() + getFontHeight() + 3)
827 int cy=y-(chanListbox.getRootBoxOffsetY() + 5);
828 int row=cy/(getFontHeight()+2);
829 int clistTop = chanListbox.getTopOption();
830 chanListbox.hintSetCurrent(clistTop+row);
832 time_t ttime = cx*60/MINUTE_SCALE+ltime;
833 //x = 155 + (MINUTE_SCALE * (event->time - ltime) / 60);
837 boxstack->update(this);