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);
366 { // cursor down the channel list
367 LogNT::getInstance()->debug(TAG, "Down start");
371 boxstack->update(this);
372 LogNT::getInstance()->debug(TAG, "Down end");
377 { // cursor left through time
378 selTime = thisEvent.time - 1;
380 boxstack->update(this);
385 // cursor right through time
386 selTime = thisEvent.time + thisEvent.duration;
388 boxstack->update(this);
393 // cursor up one page
394 chanListbox.pageUp();
396 boxstack->update(this);
401 // cursor down one page
402 chanListbox.pageDown();
404 boxstack->update(this);
409 // step forward 24 hours
410 selTime += 24 * 60 * 60;
412 boxstack->update(this);
417 // step forward 24 hours
418 selTime -= 24 * 60 * 60;
420 boxstack->update(this);
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);
437 if (!chanList) return 2;
439 // select programme and display menu TODO currently just changes to selected channel
441 currentChannelIndex = chanListbox.getCurrentOption();
445 Message* m = new Message(); // Must be done after this view deleted
448 m->message = Message::CHANNEL_CHANGE;
449 m->parameter = (*chanList)[currentChannelIndex]->number;
450 MessageQueue::getInstance()->postMessage(m);
455 if(command == Input::GO)
457 // GO just changes channel in preview, PLAY changes channel and returns to normal TV
464 case Input::CHANNELUP:
466 if (currentChannelIndex == (chanList->size() - 1)) // at the end
467 currentChannelIndex = 0;
469 ++currentChannelIndex;
473 Message* m = new Message(); // Must be done after this view deleted
476 m->message = Message::CHANNEL_CHANGE;
477 m->parameter = (*chanList)[currentChannelIndex]->number;
478 MessageQueue::getInstance()->postMessage(m);
485 case Input::CHANNELDOWN:
487 if (currentChannelIndex == 0) // at the start
488 currentChannelIndex = chanList->size() - 1; // so go to end
490 --currentChannelIndex;
494 Message* m = new Message(); // Must be done after this view deleted
497 m->message = Message::CHANNEL_CHANGE;
498 m->parameter = (*chanList)[currentChannelIndex]->number;
499 MessageQueue::getInstance()->postMessage(m);
507 // stop command getting to any more views
511 void VEpg::drawgrid() // redraws grid and select programme
513 // draw the grid of programmes
516 time(&t); // set t = now
518 selTime = t; // don't allow cursor in the past
519 if(listTop != chanListbox.getTopOption())
521 // chanListbox has scrolled TODO speed up by changing only rows that have changed
522 listTop = chanListbox.getTopOption();
525 if ((selTime >= ltime + (int)window_width * 60) || (selTime <= ltime))
527 // we have cursored back before left time of window
528 //TODO check that this and above don't happen together
529 ltime = prevHour(&selTime);
536 LOCALTIME_R(&t, &tms);
537 strftime(timeString, 19, "%a %d %b", &tms);
538 int timey = chanListbox.getRootBoxOffsetY() - getFontHeight() - 3;
540 drawTextRJ(timeString, timex - 10, timey, DrawStyle::LIGHTTEXT); // print date
541 strftime(timeString, 19, "%H:%M", &tms);
542 drawText(timeString, timex, timey, DrawStyle::LIGHTTEXT); // print left time
544 rectangle(155, timey + getFontHeight(), 2, 7, DrawStyle::WHITE);
546 LOCALTIME_R(&t, &tms);
547 strftime(timeString, 19, "%H:%M", &tms);
548 drawText(timeString, timex + 180, timey, DrawStyle::LIGHTTEXT); // print middle time
549 rectangle(335, timey + getFontHeight(), 2, 7, DrawStyle::WHITE);
551 LOCALTIME_R(&t, &tms);
552 strftime(timeString, 19, "%H:%M", &tms);
553 drawText(timeString, timex + 360, timey, DrawStyle::LIGHTTEXT); // print right time
554 rectangle(515, timey + getFontHeight(), 2, 7, DrawStyle::WHITE);
555 // pointer to selTime
556 //rectangle(155 + (selTime - ltime) / 20, timey + getFontHeight(), 2, 7, DrawStyle(255, 50, 50, 255));
560 if ((t >= ltime) && (t < (ltime + 9000)))
562 rectangle(155 + (t - ltime) / 20, timey + getFontHeight(), 2, ((getFontHeight() + 2) * gridRows) + 7 + 2, DrawStyle::RED);
565 // TODO should the above two comditional statements be combined to avoid calling updateEventList() twice?
567 Event noevent; // an event to use if there are gaps in the epg
568 thisEvent.description = tr("There are no programme details available for this period");
569 thisEvent.duration = window_width * 60;
570 thisEvent.time = ltime;
571 thisEvent.title = tr("No programme details");
573 bool swapColour = false; // alternate cell colour
574 bool currentRow = false;
575 int y = chanListbox.getRootBoxOffsetY() + 5; // vertical position of cell
576 DrawStyle bg, fg; // background colour of cells in grid
577 // for each displayed channel, find programmes that fall in 2.5 hour time window
578 for(UINT listIndex = 0; listIndex < gridRows; listIndex++)
580 if (listTop + (int)listIndex >= chanListbox.getBottomOption())
581 continue; // ensure nothing populates grid below last channel
583 currentRow = (listTop + (int)listIndex == chanListbox.getCurrentOption());
584 noevent.time = ltime;
585 noevent.duration = window_width * 60;
587 paintCell(&noevent, y, DrawStyle::NOPROGRAMME, DrawStyle::LIGHTTEXT); // fill row with no programme colour to be painted ove with valid programmes
590 thisEvent.description = tr("There are no programme details available for this period");
591 thisEvent.duration = window_width * 60;
592 thisEvent.time = ltime;
593 thisEvent.title = tr("No programme details");
596 if (eventLista[listIndex])
598 sort(eventLista[listIndex]->begin(), eventLista[listIndex]->end(), EventSorter());
599 for(e = 0; e < (eventLista[listIndex])->size(); e++) // step through events for this channel
601 fg = DrawStyle::LIGHTTEXT;
602 event = (*eventLista[listIndex])[e];
605 UINT end = event->time + event->duration; // programme end time
606 if(event->time >= UINT(ltime) + (window_width * 60)) // programme starts after RHS of window
607 continue; // that's enough of this channel's events
608 if(end <= UINT(ltime)) // programme ends before LHS of window
609 continue; // this event is before the window - let's try the next event
610 // this event is one we are interested in
611 bg = (swapColour)?DrawStyle::PROGRAMMEA:DrawStyle::PROGRAMMEB; // alternate cell colour
612 swapColour = !swapColour; // it wil be the other colour next time
613 if(event->time <= UINT(selTime) && end > UINT(selTime) && currentRow)
615 // this is the selected programme
616 thisEvent.description = event->description;
617 thisEvent.duration = event->duration;
618 thisEvent.time = event->time;
619 thisEvent.title = event->title;
620 thisEvent.id = event->id;
621 if(thisEvent.id == 0)
623 bg = DrawStyle::SELECTHIGHLIGHT; // highlight cell
624 fg = DrawStyle::DARKTEXT;
628 if (currentRow && thisEvent.id == 0)
630 if (end <= UINT(selTime) && end > UINT(thisEvent.time))
631 thisEvent.time = end;
632 if (event->time > UINT(selTime) && event->time < thisEvent.time + thisEvent.duration)
633 thisEvent.duration = event->time - thisEvent.time;
636 paintCell(event, y, bg, fg);
642 // no event list for this channel. Already painted noevent colour so just highlight if selected
645 bg = DrawStyle::SELECTHIGHLIGHT; // highlight cell
646 fg = DrawStyle::DARKTEXT;
647 paintCell(&thisEvent, y, bg, fg);
651 bg = DrawStyle::NOPROGRAMME;
652 fg = DrawStyle::LIGHTTEXT;
653 noevent.title = tr("No programme details");
654 paintCell(&noevent, y, bg, fg);
657 y += getFontHeight() + 2;
662 void VEpg::updateEventList()
664 if (!chanList) return;
666 for(UINT listIndex = 0; listIndex < gridRows; listIndex++)
668 if(listTop + listIndex >= UINT(chanListbox.getBottomOption()))
670 chan = (*chanList)[listTop + listIndex];
671 if (eventLista[listIndex])
673 (eventLista)[listIndex]->clear();
674 delete eventLista[listIndex];
676 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
680 void VEpg::setCurrentChannel()
682 chanName.setText((*chanList)[currentChannelIndex]->name);
685 chanName.getRootBoxRegion(&r);
686 boxstack->update(this, &r);
689 void VEpg::paintCell(Event* event, int yOffset, const DrawStyle& bg, const DrawStyle& fg)
692 w = x = 0; // keep compiler happy
694 h = getFontHeight(); // TODO if want border around text, need to increae this and wselectlist line height
695 UINT end = event->time + event->duration; // programme end time
696 if(event->time <= UINT(ltime) && end > UINT(ltime)) // spans start of displayed window
698 x = 155; // LHS of window
699 if (end > (UINT(ltime) + (window_width * 60)))
700 w = window_width * MINUTE_SCALE; // spans full 2 hour window
702 w = MINUTE_SCALE * (event->time + event->duration - ltime ) / 60; // get width of remaining programme
704 if((event->time >= UINT(ltime)) && (event->time <= UINT(ltime) + (window_width * 60))) // starts within window
706 x = 155 + (MINUTE_SCALE * (event->time - ltime) / 60);
707 w = MINUTE_SCALE * event->duration / 60;
708 //if (w > 155 + MINUTE_SCALE * WINDOW_WIDTH -x)
709 // w = w + x - 155 - MINUTE_SCALE * WINDOW_WIDTH; // ends outside window
711 if (w > 155 + (int)window_width * MINUTE_SCALE - x)
712 w = 155 + window_width * MINUTE_SCALE -x; // limit cells to RHS of window
713 rectangle(x, y, w, h, bg);
714 char* tt = new char[strlen(event->title.c_str()) + 1];
715 strcpy (tt, event->title.c_str());
717 unsigned int cur_length=1;
718 unsigned int text_max=strlen(tt);
720 if (Osd::getInstance()->charSet()!=1) mchar=true;
722 memset((void*)&state,0,sizeof(state));
725 for (textPos = 0; textPos <text_max; textPos+=cur_length)
729 cur_length = mbrtowc(&cur_char, tt + textPos, text_max-textPos, &state);
730 if (cur_length <= 0){
733 } else cur_char= *(tt+textPos);
734 float thisCharWidth = charWidth(cur_char);
735 if (textWidth + thisCharWidth > w) // text will not fit in cell
739 textWidth += thisCharWidth;
741 char* tT = new char[textPos+1];
744 strncpy(tT, tt, textPos );
746 surface->drawText(tT, x+2, y, fg);
752 time_t VEpg::prevHour(time_t* t)
755 LOCALTIME_R(t, &tms);
761 void VEpg::processMessage(Message* m)
763 if (m->message == Message::MOUSE_MOVE)
765 if (chanListbox.mouseMove(m->parameter - getScreenX(), m->tag - getScreenY()))
768 boxstack->update(this);
771 else if (m->message == Message::MOUSE_LBDOWN)
773 if (chanListbox.mouseLBDOWN(m->parameter - getScreenX(), m->tag - getScreenY()))
775 boxstack->handleCommand(Input::OK); //simulate OK press
779 //check if press is outside this view! then simulate cancel
780 int x = m->parameter - getScreenX();
781 int y = m->tag - getScreenY();
782 int keyx = chanListbox.getRootBoxOffsetX();
783 int keyy = chanListbox.getRootBoxOffsetY() + chanListbox.getHeight() + 2;
785 if (x<0 || y <0 || x>(int)getWidth() || y>(int)getHeight())
787 boxstack->handleCommand(Input::BACK); //simulate cancel press
789 else if (x>=(keyx+72) && y>=(keyy+4) &&x<=(keyx+72+104) &&y<=(keyy+4+getFontHeight() + 2))
791 boxstack->handleCommand(Input::RED);
793 else if (x>=(keyx+72) && y>=(keyy+ getFontHeight() + 8) &&x<=(keyx+72+104) &&y<=(keyy+8+2*getFontHeight() + 2))
795 boxstack->handleCommand(Input::GREEN);
797 else if (x>=(keyx+180) && y>=(keyy+4) &&x<=(keyx+180+104) &&y<=(keyy+4+getFontHeight() + 2))
799 boxstack->handleCommand(Input::YELLOW);
801 else if (x>=(keyx+180) && y>=(keyy+ getFontHeight() + 8) &&x<=(keyx+180+104) &&y<=(keyy+8+2*getFontHeight() + 2))
803 boxstack->handleCommand(Input::BLUE);
805 else if (x>=(keyx+290) && y>=(keyy+4) &&x<=(keyx+180+290) &&y<=(keyy+4+getFontHeight() + 2))
807 boxstack->handleCommand(Input::BACK);
809 else if (x>=(keyx+290) && y>=(keyy+ getFontHeight() + 8) &&x<=(keyx+290+180) &&y<=(keyy+8+2*getFontHeight() + 2))
811 boxstack->handleCommand(Input::RECORD);
813 else if (x>=(keyx+474) && y>=(keyy+4) &&x<=(keyx+128+474) &&y<=(keyy+4+getFontHeight() + 2))
815 boxstack->handleCommand(Input::PLAY);
817 else if (x>=(keyx+474) && y>=(keyy+ getFontHeight() + 8) &&x<=(keyx+238+474) &&y<=(keyy+8+2*getFontHeight() + 2))
819 boxstack->handleCommand(Input::GO);
821 else if ( x>=(chanListbox.getRootBoxOffsetX())
822 && y>=(chanListbox.getRootBoxOffsetY() + 5)
823 // &&x<=(chanListbox.getOffsetX()+155 + window_width * MINUTE_SCALE)
824 &&y<=(chanListbox.getRootBoxOffsetY() - getFontHeight()
825 - 3+(int)chanListbox.getHeight() + getFontHeight() + 3)
828 int cy=y-(chanListbox.getRootBoxOffsetY() + 5);
829 int row=cy/(getFontHeight()+2);
830 int clistTop = chanListbox.getTopOption();
831 chanListbox.hintSetCurrent(clistTop+row);
833 time_t ttime = cx*60/MINUTE_SCALE+ltime;
834 //x = 155 + (MINUTE_SCALE * (event->time - ltime) / 60);
838 boxstack->update(this);