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 VEpg* VEpg::instance = NULL;
51 VEpg::VEpg(void* tparent, UINT tcurrentChannelIndex, ChannelList* tchanList)
54 currentChannelIndex = tcurrentChannelIndex;
56 // PAL / NTSC sizes -----------------------
59 //int summaryLines, summaryLowerPadding;
61 int fontHeight=getFontHeight() + 4;
62 //UINT gridRows; // is a class member
65 if (Video::getInstance()->getFormat() == Video::PAL)
82 //summaryLowerPadding = 28;
86 int screenwidthhalf=Video::getInstance()->getScreenWidth()/2;
87 int screenheighthalf=Video::getInstance()->getScreenHeight()/2;
88 // summaryLines = ((float)screenheighthalf)/((float)fontHeight))-1;
89 // summaryLowerPadding = screenheighthalf-summaryLines*(fontHeight);
90 gridRows = (screenheighthalf-fontHeight*3-50)/fontHeight;
93 // initialise variables and pointers
94 boxstack = BoxStack::getInstance();
100 eventLista.resize(gridRows);
101 for(UINT listIndex = 0; listIndex < gridRows; listIndex++)
103 // initialise array of pointers to eventlist structures
104 eventLista[listIndex] = NULL;
107 // Create pallet on which to paint our epg view and position it in centre of screen.
108 // Need to reduce size to deal with overscanning TVs.
110 setSize(Video::getInstance()->getScreenWidth(), Video::getInstance()->getScreenHeight());
115 // setBackgroundColour(DrawStyle::TRANSPARENT);
117 // progTitle.setSurface(surface);
118 progTitle.setPosition(0,0);
119 progTitle.setSize(screenwidthhalf,(fontHeight) * 1 + ypos); //paragraph line seperation is 4 pixels
120 progTitle.setBackgroundColour(DrawStyle::TITLEBARBACKGROUND);
121 progTitle.setTextPos(xpos, ypos);
123 progTitle.setParaMode(false);
126 // progInfo.setSurface(surface);
127 progInfo.setBackgroundColour(DrawStyle::VIEWBACKGROUND);
128 progInfo.setPosition(0, progTitle.getY2());
129 progInfo.setSize(screenwidthhalf,screenheighthalf-progTitle.getY2()+10);
130 progInfo.setTextPos(xpos, 0);
134 // chanName.setSurface(surface);
135 chanName.setSize(510, (fontHeight));
136 chanName.setPosition(screenwidthhalf, screenheighthalf - 2*fontHeight);
137 DrawStyle t1(0, 0, 0, 90);
138 chanName.setBackgroundColour(t1);
139 chanName.setParaMode(false);
142 // create area to display list of channels
143 // chanListbox.setSurface(surface); // add channel list
144 chanListbox.setPosition(0, progInfo.getY2() + fontHeight + 4); // position channel list
145 chanListbox.setSize(150, ((getFontHeight() + 2) * gridRows) + 5); //listbox line seperation is 2 pixels
146 chanListbox.setGap(2);
147 chanListbox.addColumn(xpos);
150 // populate channel list
155 for (UINT i = 0; i < chanList->size(); i++)
157 chan = (*chanList)[i];
158 if (i == currentChannelIndex)
160 chan->index = chanListbox.addOption(chan->name, 0, first);
163 chanName.setText((*chanList)[chanListbox.getCurrentOption()]->name);
166 window_x= chanListbox.getRootBoxOffsetX() + chanListbox.getWidth() + 5;
167 window_width=(Video::getInstance()->getScreenWidth() - window_x +3)/3;
169 listTop = chanListbox.getTopOption();
170 chanListbox.draw(); // doing this to allow chanListbox.getBottomOption() in updateEventList() to work
171 time(<ime); // set ltime to now
172 ltime = prevHour(<ime); // set ltime to previous hour TODO make this half hour?
173 time(&selTime); // set selTime to now
174 updateEventList(); // get list of programmes
176 vdisplay.mode=Window;
177 vdisplay.fallbackMode=Quarter;
178 vdisplay.x=Video::getInstance()->getScreenWidth()/2;
180 vdisplay.width=Video::getInstance()->getScreenWidth()/2;
181 vdisplay.height=Video::getInstance()->getScreenHeight()/2;
184 void VEpg::preDelete()
186 Timers::getInstance()->cancelTimer(this, 1);
194 for(UINT listIndex = 0; listIndex < gridRows; listIndex++)
196 if (eventLista[listIndex])
198 (eventLista)[listIndex]->clear();
199 delete eventLista[listIndex];
202 // delete [] eventLista; // FIXME
204 // destroy dynamically allocated memory
207 VEpg* VEpg::getInstance()
212 void VEpg::setInfo(Event* event)
215 struct tm btime; // to hold programme start and end time
216 char timeString[9]; // to hold programme start and end time
217 int length = strlen(event->title.c_str()); // calculate length of programme title string
218 char* title = new char[length + 15]; // create string to hold start time, end time and programme title
219 time_t eventtime = event->time;
220 LOCALTIME_R(&eventtime, &btime); //get programme start time
222 strftime(timeString, 9, "%0H:%0M - ", &btime); // and format it as hh:mm -
224 strftime(timeString, 9, "%H:%M - ", &btime); // and format it as hh:mm -
226 strcpy(title, timeString); // put it in our buffer
227 t = event->time + event->duration; //get programme end time
228 LOCALTIME_R(&t, &btime);
230 strftime(timeString, 7, "%0H:%0M ", &btime); // and format it as hh:mm -
232 strftime(timeString, 7, "%H:%M ", &btime); // and format it as hh:mm -
234 strcat(title, timeString); // put it in our buffer
236 strcat(title, event->title.c_str()); // then add the programme title
237 progTitle.setText(title); // sput this sring in our text box
238 length = event->description.length();
239 char* info = new char[length + 1]; // create programme detail string
240 strcpy(info, event->description.c_str());
241 progInfo.setText(info); // show programme detail string
242 // destroy dynamically allocated memory
249 // View::draw(); // draw pallet
251 fillColour(DrawStyle::TRANSPARENT);
254 // Moved all the dynamic data drawing to a seperate function
256 // Display the status and key stuff at the bottom
258 UINT keyx = chanListbox.getRootBoxOffsetX() + chanListbox.getColumn(0);
259 UINT keyy = chanListbox.getRootBoxOffsetY() + chanListbox.getHeight() + 2;
260 rectangle (0, keyy, Video::getInstance()->getScreenWidth() ,
261 Video::getInstance()->getScreenHeight()-keyy, DrawStyle::DARKGREY);
266 w.nextSymbol = WSymbol::LEFTARROW;
267 w.setPosition(keyx + 1, keyy + 20);
270 w.nextSymbol = WSymbol::UP;
271 w.setPosition(keyx + 26, keyy + 3);
274 w.nextSymbol = WSymbol::DOWN;
275 w.setPosition(keyx + 26, keyy + 36);
278 w.nextSymbol = WSymbol::RIGHTARROW;
279 w.setPosition(keyx + 50, keyy + 20);
282 drawTextCentre(tr("OK"), keyx + 35, keyy + 20, DrawStyle::LIGHTTEXT);
284 rectangle(keyx + 72, keyy + 4, 104, getFontHeight() + 2, DrawStyle::RED);
285 drawText(tr("Page up"), keyx + 74, keyy + 5, DrawStyle::LIGHTTEXT);
287 rectangle(keyx + 72, keyy + getFontHeight() + 8, 104, getFontHeight() + 2, DrawStyle::GREEN);
288 drawText(tr("Page down"), keyx + 74, keyy + getFontHeight() + 9, DrawStyle::LIGHTTEXT);
290 rectangle(keyx + 180, keyy + 4, 104, getFontHeight() + 2, DrawStyle::YELLOW);
291 drawText(tr("-24 hours"), keyx + 182, keyy + 5, DrawStyle::LIGHTTEXT);
293 rectangle(keyx + 180, keyy + getFontHeight() + 8, 104, getFontHeight() + 2, DrawStyle::BLUE);
294 drawText(tr("+24 hours"), keyx + 182, keyy + getFontHeight() + 9, DrawStyle::LIGHTTEXT);
296 rectangle(keyx + 290, keyy + 4, 180, getFontHeight() + 2, DrawStyle::GREY);
297 drawText(tr("Guide / Back: Close"), keyx + 292 , keyy + 5, DrawStyle::LIGHTTEXT);
299 rectangle(keyx + 290, keyy + getFontHeight() + 8, 180, getFontHeight() + 2, DrawStyle::GREY);
300 DrawStyle red = DrawStyle(130, 0, 0);
301 drawText(tr("Rec: Set timer"), keyx + 292, keyy + getFontHeight() + 9, red);
303 rectangle(keyx + 474, keyy + 4, 128, getFontHeight() + 2, DrawStyle::GREY);
304 w.nextSymbol = WSymbol::PLAY;
305 w.setPosition(keyx + 476, keyy + 5);
307 drawText(tr("Sel channel"), keyx + 496, keyy + 5, DrawStyle::LIGHTTEXT);
309 rectangle(keyx + 474, keyy + getFontHeight() + 8, 128, getFontHeight() + 2, DrawStyle::GREY);
310 drawText(tr("Go: Preview"), keyx + 476, keyy + getFontHeight() + 9, DrawStyle::LIGHTTEXT);
313 // Draw all the dynamic data
317 void VEpg::drawData()
319 // Not doing View::draw() every time causes
320 // things not to be cleared off the surface properly
321 // So, blank out the data area first
322 //int screenwidth=Video::getInstance()->getScreenWidth();
324 chanListbox.getRootBoxOffsetX(),
325 chanListbox.getRootBoxOffsetY() - getFontHeight() - 3,
326 window_width * MINUTE_SCALE,
327 chanListbox.getHeight() + getFontHeight() + 4,
332 chanName.draw(); // TODO this should be dealt with by vvideolive
337 // Set timer to redraw to move the current time bar
341 if (dt == 0) dt = 60;
343 Timers::getInstance()->setTimerT(this, 1, dt);
346 void VEpg::timercall(int /*clientReference*/)
349 boxstack->update(this);
352 int VEpg::handleCommand(int command)
357 { // cursor up the channel list
360 boxstack->update(this);
364 { // cursor down the channel list
365 Log::getInstance()->log("VEPG", Log::DEBUG, "Down start");
369 boxstack->update(this);
370 Log::getInstance()->log("VEPG", Log::DEBUG, "Down end");
375 { // cursor left through time
376 selTime = thisEvent.time - 1;
378 boxstack->update(this);
383 // cursor right through time
384 selTime = thisEvent.time + thisEvent.duration;
386 boxstack->update(this);
391 // cursor up one page
392 chanListbox.pageUp();
394 boxstack->update(this);
399 // cursor down one page
400 chanListbox.pageDown();
402 boxstack->update(this);
407 // step forward 24 hours
408 selTime += 24 * 60 * 60;
410 boxstack->update(this);
415 // step forward 24 hours
416 selTime -= 24 * 60 * 60;
418 boxstack->update(this);
423 if (!chanList) return 2;
424 Log::getInstance()->log("VEPG", Log::DEBUG, "ID %lu TIME %lu DURATION %lu TITLE %s", thisEvent.id, thisEvent.time, thisEvent.duration, thisEvent.title);
425 VEpgSetTimer* vs = new VEpgSetTimer(&thisEvent, (*chanList)[chanListbox.getCurrentOption()]);
428 boxstack->update(vs);
435 if (!chanList) return 2;
437 // select programme and display menu TODO currently just changes to selected channel
439 currentChannelIndex = chanListbox.getCurrentOption();
443 Message* m = new Message(); // Must be done after this view deleted
446 m->message = Message::CHANNEL_CHANGE;
447 m->parameter = (*chanList)[currentChannelIndex]->number;
448 MessageQueue::getInstance()->postMessage(m);
453 if(command == Input::GO)
455 // GO just changes channel in preview, PLAY changes channel and returns to normal TV
462 case Input::CHANNELUP:
464 if (currentChannelIndex == (chanList->size() - 1)) // at the end
465 currentChannelIndex = 0;
467 ++currentChannelIndex;
471 Message* m = new Message(); // Must be done after this view deleted
474 m->message = Message::CHANNEL_CHANGE;
475 m->parameter = (*chanList)[currentChannelIndex]->number;
476 MessageQueue::getInstance()->postMessage(m);
483 case Input::CHANNELDOWN:
485 if (currentChannelIndex == 0) // at the start
486 currentChannelIndex = chanList->size() - 1; // so go to end
488 --currentChannelIndex;
492 Message* m = new Message(); // Must be done after this view deleted
495 m->message = Message::CHANNEL_CHANGE;
496 m->parameter = (*chanList)[currentChannelIndex]->number;
497 MessageQueue::getInstance()->postMessage(m);
505 // stop command getting to any more views
509 void VEpg::drawgrid() // redraws grid and select programme
511 // draw the grid of programmes
514 time(&t); // set t = now
516 selTime = t; // don't allow cursor in the past
517 if(listTop != chanListbox.getTopOption())
519 // chanListbox has scrolled TODO speed up by changing only rows that have changed
520 listTop = chanListbox.getTopOption();
523 if ((selTime >= ltime + (int)window_width * 60) || (selTime <= ltime))
525 // we have cursored back before left time of window
526 //TODO check that this and above don't happen together
527 ltime = prevHour(&selTime);
534 LOCALTIME_R(&t, &tms);
535 strftime(timeString, 19, "%a %d %b", &tms);
536 int timey = chanListbox.getRootBoxOffsetY() - getFontHeight() - 3;
538 drawTextRJ(timeString, timex - 10, timey, DrawStyle::LIGHTTEXT); // print date
539 strftime(timeString, 19, "%H:%M", &tms);
540 drawText(timeString, timex, timey, DrawStyle::LIGHTTEXT); // print left time
542 rectangle(155, timey + getFontHeight(), 2, 7, DrawStyle::WHITE);
544 LOCALTIME_R(&t, &tms);
545 strftime(timeString, 19, "%H:%M", &tms);
546 drawText(timeString, timex + 180, timey, DrawStyle::LIGHTTEXT); // print middle time
547 rectangle(335, timey + getFontHeight(), 2, 7, DrawStyle::WHITE);
549 LOCALTIME_R(&t, &tms);
550 strftime(timeString, 19, "%H:%M", &tms);
551 drawText(timeString, timex + 360, timey, DrawStyle::LIGHTTEXT); // print right time
552 rectangle(515, timey + getFontHeight(), 2, 7, DrawStyle::WHITE);
553 // pointer to selTime
554 //rectangle(155 + (selTime - ltime) / 20, timey + getFontHeight(), 2, 7, DrawStyle(255, 50, 50, 255));
558 if ((t >= ltime) && (t < (ltime + 9000)))
560 rectangle(155 + (t - ltime) / 20, timey + getFontHeight(), 2, ((getFontHeight() + 2) * gridRows) + 7 + 2, DrawStyle::RED);
563 // TODO should the above two comditional statements be combined to avoid calling updateEventList() twice?
565 Event noevent; // an event to use if there are gaps in the epg
566 thisEvent.description = tr("There are no programme details available for this period");
567 thisEvent.duration = window_width * 60;
568 thisEvent.time = ltime;
569 thisEvent.title = tr("No programme details");
571 bool swapColour = false; // alternate cell colour
572 bool currentRow = false;
573 int y = chanListbox.getRootBoxOffsetY() + 5; // vertical position of cell
574 DrawStyle bg, fg; // background colour of cells in grid
575 // for each displayed channel, find programmes that fall in 2.5 hour time window
576 for(UINT listIndex = 0; listIndex < gridRows; listIndex++)
578 if (listTop + (int)listIndex >= chanListbox.getBottomOption())
579 continue; // ensure nothing populates grid below last channel
581 currentRow = (listTop + (int)listIndex == chanListbox.getCurrentOption());
582 noevent.time = ltime;
583 noevent.duration = window_width * 60;
585 paintCell(&noevent, y, DrawStyle::NOPROGRAMME, DrawStyle::LIGHTTEXT); // fill row with no programme colour to be painted ove with valid programmes
588 thisEvent.description = tr("There are no programme details available for this period");
589 thisEvent.duration = window_width * 60;
590 thisEvent.time = ltime;
591 thisEvent.title = tr("No programme details");
594 if (eventLista[listIndex])
596 sort(eventLista[listIndex]->begin(), eventLista[listIndex]->end(), EventSorter());
597 for(e = 0; e < (eventLista[listIndex])->size(); e++) // step through events for this channel
599 fg = DrawStyle::LIGHTTEXT;
600 event = (*eventLista[listIndex])[e];
603 UINT end = event->time + event->duration; // programme end time
604 if(event->time >= UINT(ltime) + (window_width * 60)) // programme starts after RHS of window
605 continue; // that's enough of this channel's events
606 if(end <= UINT(ltime)) // programme ends before LHS of window
607 continue; // this event is before the window - let's try the next event
608 // this event is one we are interested in
609 bg = (swapColour)?DrawStyle::PROGRAMMEA:DrawStyle::PROGRAMMEB; // alternate cell colour
610 swapColour = !swapColour; // it wil be the other colour next time
611 if(event->time <= UINT(selTime) && end > UINT(selTime) && currentRow)
613 // this is the selected programme
614 thisEvent.description = event->description;
615 thisEvent.duration = event->duration;
616 thisEvent.time = event->time;
617 thisEvent.title = event->title;
618 thisEvent.id = event->id;
619 if(thisEvent.id == 0)
621 bg = DrawStyle::SELECTHIGHLIGHT; // highlight cell
622 fg = DrawStyle::DARKTEXT;
626 if (currentRow && thisEvent.id == 0)
628 if (end <= UINT(selTime) && end > UINT(thisEvent.time))
629 thisEvent.time = end;
630 if (event->time > UINT(selTime) && event->time < thisEvent.time + thisEvent.duration)
631 thisEvent.duration = event->time - thisEvent.time;
634 paintCell(event, y, bg, fg);
640 // no event list for this channel. Already painted noevent colour so just highlight if selected
643 bg = DrawStyle::SELECTHIGHLIGHT; // highlight cell
644 fg = DrawStyle::DARKTEXT;
645 paintCell(&thisEvent, y, bg, fg);
649 bg = DrawStyle::NOPROGRAMME;
650 fg = DrawStyle::LIGHTTEXT;
651 noevent.title = tr("No programme details");
652 paintCell(&noevent, y, bg, fg);
655 y += getFontHeight() + 2;
660 void VEpg::updateEventList()
662 if (!chanList) return;
664 for(UINT listIndex = 0; listIndex < gridRows; listIndex++)
666 if(listTop + listIndex >= UINT(chanListbox.getBottomOption()))
668 chan = (*chanList)[listTop + listIndex];
669 if (eventLista[listIndex])
671 (eventLista)[listIndex]->clear();
672 delete eventLista[listIndex];
674 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
678 void VEpg::setCurrentChannel()
680 chanName.setText((*chanList)[currentChannelIndex]->name);
683 chanName.getRootBoxRegion(&r);
684 boxstack->update(this, &r);
687 void VEpg::paintCell(Event* event, int yOffset, const DrawStyle& bg, const DrawStyle& fg)
690 w = x = 0; // keep compiler happy
692 h = getFontHeight(); // TODO if want border around text, need to increae this and wselectlist line height
693 UINT end = event->time + event->duration; // programme end time
694 if(event->time <= UINT(ltime) && end > UINT(ltime)) // spans start of displayed window
696 x = 155; // LHS of window
697 if (end > (UINT(ltime) + (window_width * 60)))
698 w = window_width * MINUTE_SCALE; // spans full 2 hour window
700 w = MINUTE_SCALE * (event->time + event->duration - ltime ) / 60; // get width of remaining programme
702 if((event->time >= UINT(ltime)) && (event->time <= UINT(ltime) + (window_width * 60))) // starts within window
704 x = 155 + (MINUTE_SCALE * (event->time - ltime) / 60);
705 w = MINUTE_SCALE * event->duration / 60;
706 //if (w > 155 + MINUTE_SCALE * WINDOW_WIDTH -x)
707 // w = w + x - 155 - MINUTE_SCALE * WINDOW_WIDTH; // ends outside window
709 if (w > 155 + (int)window_width * MINUTE_SCALE - x)
710 w = 155 + window_width * MINUTE_SCALE -x; // limit cells to RHS of window
711 rectangle(x, y, w, h, bg);
712 char* tt = new char[strlen(event->title.c_str()) + 1];
713 strcpy (tt, event->title.c_str());
715 unsigned int cur_length=1;
716 unsigned int text_max=strlen(tt);
718 if (Osd::getInstance()->charSet()!=1) mchar=true;
720 memset((void*)&state,0,sizeof(state));
723 for (textPos = 0; textPos <text_max; textPos+=cur_length)
727 cur_length = mbrtowc(&cur_char, tt + textPos, text_max-textPos, &state);
728 if (cur_length <= 0){
731 } else cur_char= *(tt+textPos);
732 float thisCharWidth = charWidth(cur_char);
733 if (textWidth + thisCharWidth > w) // text will not fit in cell
737 textWidth += thisCharWidth;
739 char* tT = new char[textPos+1];
742 strncpy(tT, tt, textPos );
744 surface->drawText(tT, x+2, y, fg);
750 time_t VEpg::prevHour(time_t* t)
753 LOCALTIME_R(t, &tms);
759 void VEpg::processMessage(Message* m)
761 if (m->message == Message::MOUSE_MOVE)
763 if (chanListbox.mouseMove(m->parameter - getScreenX(), m->tag - getScreenY()))
766 boxstack->update(this);
769 else if (m->message == Message::MOUSE_LBDOWN)
771 if (chanListbox.mouseLBDOWN(m->parameter - getScreenX(), m->tag - getScreenY()))
773 boxstack->handleCommand(Input::OK); //simulate OK press
777 //check if press is outside this view! then simulate cancel
778 int x = m->parameter - getScreenX();
779 int y = m->tag - getScreenY();
780 int keyx = chanListbox.getRootBoxOffsetX();
781 int keyy = chanListbox.getRootBoxOffsetY() + chanListbox.getHeight() + 2;
783 if (x<0 || y <0 || x>(int)getWidth() || y>(int)getHeight())
785 boxstack->handleCommand(Input::BACK); //simulate cancel press
787 else if (x>=(keyx+72) && y>=(keyy+4) &&x<=(keyx+72+104) &&y<=(keyy+4+getFontHeight() + 2))
789 boxstack->handleCommand(Input::RED);
791 else if (x>=(keyx+72) && y>=(keyy+ getFontHeight() + 8) &&x<=(keyx+72+104) &&y<=(keyy+8+2*getFontHeight() + 2))
793 boxstack->handleCommand(Input::GREEN);
795 else if (x>=(keyx+180) && y>=(keyy+4) &&x<=(keyx+180+104) &&y<=(keyy+4+getFontHeight() + 2))
797 boxstack->handleCommand(Input::YELLOW);
799 else if (x>=(keyx+180) && y>=(keyy+ getFontHeight() + 8) &&x<=(keyx+180+104) &&y<=(keyy+8+2*getFontHeight() + 2))
801 boxstack->handleCommand(Input::BLUE);
803 else if (x>=(keyx+290) && y>=(keyy+4) &&x<=(keyx+180+290) &&y<=(keyy+4+getFontHeight() + 2))
805 boxstack->handleCommand(Input::BACK);
807 else if (x>=(keyx+290) && y>=(keyy+ getFontHeight() + 8) &&x<=(keyx+290+180) &&y<=(keyy+8+2*getFontHeight() + 2))
809 boxstack->handleCommand(Input::RECORD);
811 else if (x>=(keyx+474) && y>=(keyy+4) &&x<=(keyx+128+474) &&y<=(keyy+4+getFontHeight() + 2))
813 boxstack->handleCommand(Input::PLAY);
815 else if (x>=(keyx+474) && y>=(keyy+ getFontHeight() + 8) &&x<=(keyx+238+474) &&y<=(keyy+8+2*getFontHeight() + 2))
817 boxstack->handleCommand(Input::GO);
819 else if ( x>=(chanListbox.getRootBoxOffsetX())
820 && y>=(chanListbox.getRootBoxOffsetY() + 5)
821 // &&x<=(chanListbox.getOffsetX()+155 + window_width * MINUTE_SCALE)
822 &&y<=(chanListbox.getRootBoxOffsetY() - getFontHeight()
823 - 3+(int)chanListbox.getHeight() + getFontHeight() + 3)
826 int cy=y-(chanListbox.getRootBoxOffsetY() + 5);
827 int row=cy/(getFontHeight()+2);
828 int clistTop = chanListbox.getTopOption();
829 chanListbox.hintSetCurrent(clistTop+row);
831 time_t ttime = cx*60/MINUTE_SCALE+ltime;
832 //x = 155 + (MINUTE_SCALE * (event->time - ltime) / 60);
836 boxstack->update(this);