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 // DrawStyle transparent = DrawStyle(0, 0, 0, 0);
116 // setBackgroundColour(transparent);
118 // progTitle.setSurface(surface);
119 progTitle.setPosition(0,0);
120 progTitle.setSize(screenwidthhalf,(fontHeight) * 1 + ypos); //paragraph line seperation is 4 pixels
121 progTitle.setBackgroundColour(DrawStyle::TITLEBARBACKGROUND);
122 progTitle.setTextPos(xpos, ypos);
124 progTitle.setParaMode(false);
127 // progInfo.setSurface(surface);
128 progInfo.setBackgroundColour(DrawStyle::VIEWBACKGROUND);
129 progInfo.setPosition(0, progTitle.getY2());
130 progInfo.setSize(screenwidthhalf,screenheighthalf-progTitle.getY2()+10);
131 progInfo.setTextPos(xpos, 0);
135 // chanName.setSurface(surface);
136 chanName.setSize(510, (fontHeight));
137 chanName.setPosition(screenwidthhalf, screenheighthalf - 2*fontHeight);
138 DrawStyle t1(0, 0, 0, 90);
139 chanName.setBackgroundColour(t1);
140 chanName.setParaMode(false);
143 // create area to display list of channels
144 // chanListbox.setSurface(surface); // add channel list
145 chanListbox.setPosition(0, progInfo.getY2() + fontHeight + 4); // position channel list
146 chanListbox.setSize(150, ((getFontHeight() + 2) * gridRows) + 5); //listbox line seperation is 2 pixels
147 chanListbox.setGap(2);
148 chanListbox.addColumn(xpos);
151 // populate channel list
156 for (UINT i = 0; i < chanList->size(); i++)
158 chan = (*chanList)[i];
159 if (i == currentChannelIndex)
161 chan->index = chanListbox.addOption(chan->name, 0, first);
164 chanName.setText((*chanList)[chanListbox.getCurrentOption()]->name);
167 window_x= chanListbox.getRootBoxOffsetX() + chanListbox.getWidth() + 5;
168 window_width=(Video::getInstance()->getScreenWidth() - window_x +3)/3;
170 listTop = chanListbox.getTopOption();
171 chanListbox.draw(); // doing this to allow chanListbox.getBottomOption() in updateEventList() to work
172 time(<ime); // set ltime to now
173 ltime = prevHour(<ime); // set ltime to previous hour TODO make this half hour?
174 time(&selTime); // set selTime to now
175 updateEventList(); // get list of programmes
177 vdisplay.mode=Window;
178 vdisplay.fallbackMode=Quarter;
179 vdisplay.x=Video::getInstance()->getScreenWidth()/2;
181 vdisplay.width=Video::getInstance()->getScreenWidth()/2;
182 vdisplay.height=Video::getInstance()->getScreenHeight()/2;
185 void VEpg::preDelete()
187 Timers::getInstance()->cancelTimer(this, 1);
195 for(UINT listIndex = 0; listIndex < gridRows; listIndex++)
197 if (eventLista[listIndex])
199 (eventLista)[listIndex]->clear();
200 delete eventLista[listIndex];
203 // delete [] eventLista; // FIXME
205 // destroy dynamically allocated memory
208 VEpg* VEpg::getInstance()
213 void VEpg::setInfo(Event* event)
216 struct tm btime; // to hold programme start and end time
217 char timeString[9]; // to hold programme start and end time
218 int length = strlen(event->title.c_str()); // calculate length of programme title string
219 char* title = new char[length + 15]; // create string to hold start time, end time and programme title
220 time_t eventtime = event->time;
221 LOCALTIME_R(&eventtime, &btime); //get programme start time
223 strftime(timeString, 9, "%0H:%0M - ", &btime); // and format it as hh:mm -
225 strftime(timeString, 9, "%H:%M - ", &btime); // and format it as hh:mm -
227 strcpy(title, timeString); // put it in our buffer
228 t = event->time + event->duration; //get programme end time
229 LOCALTIME_R(&t, &btime);
231 strftime(timeString, 7, "%0H:%0M ", &btime); // and format it as hh:mm -
233 strftime(timeString, 7, "%H:%M ", &btime); // and format it as hh:mm -
235 strcat(title, timeString); // put it in our buffer
237 strcat(title, event->title.c_str()); // then add the programme title
238 progTitle.setText(title); // sput this sring in our text box
239 length = event->description.length();
240 char* info = new char[length + 1]; // create programme detail string
241 strcpy(info, event->description.c_str());
242 progInfo.setText(info); // show programme detail string
243 // destroy dynamically allocated memory
250 // View::draw(); // draw pallet
252 DrawStyle transparent = DrawStyle(0, 0, 0, 0);
253 fillColour(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 Log::getInstance()->log("VEPG", Log::DEBUG, "Down start");
371 boxstack->update(this);
372 Log::getInstance()->log("VEPG", Log::DEBUG, "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 Log::getInstance()->log("VEPG", Log::DEBUG, "ID %lu TIME %lu DURATION %lu TITLE %s", 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);
533 DrawStyle white = DrawStyle(255, 255, 255, 255);
538 LOCALTIME_R(&t, &tms);
539 strftime(timeString, 19, "%a %d %b", &tms);
540 int timey = chanListbox.getRootBoxOffsetY() - getFontHeight() - 3;
542 drawTextRJ(timeString, timex - 10, timey, DrawStyle::LIGHTTEXT); // print date
543 strftime(timeString, 19, "%H:%M", &tms);
544 drawText(timeString, timex, timey, DrawStyle::LIGHTTEXT); // print left time
546 rectangle(155, timey + getFontHeight(), 2, 7, white);
548 LOCALTIME_R(&t, &tms);
549 strftime(timeString, 19, "%H:%M", &tms);
550 drawText(timeString, timex + 180, timey, DrawStyle::LIGHTTEXT); // print middle time
551 rectangle(335, timey + getFontHeight(), 2, 7, white);
553 LOCALTIME_R(&t, &tms);
554 strftime(timeString, 19, "%H:%M", &tms);
555 drawText(timeString, timex + 360, timey, DrawStyle::LIGHTTEXT); // print right time
556 rectangle(515, timey + getFontHeight(), 2, 7, white);
557 // pointer to selTime
558 //rectangle(155 + (selTime - ltime) / 20, timey + getFontHeight(), 2, 7, DrawStyle(255, 50, 50, 255));
562 if ((t >= ltime) && (t < (ltime + 9000)))
564 rectangle(155 + (t - ltime) / 20, timey + getFontHeight(), 2, ((getFontHeight() + 2) * gridRows) + 7 + 2, DrawStyle::RED);
567 // TODO should the above two comditional statements be combined to avoid calling updateEventList() twice?
569 Event noevent; // an event to use if there are gaps in the epg
570 thisEvent.description = tr("There are no programme details available for this period");
571 thisEvent.duration = window_width * 60;
572 thisEvent.time = ltime;
573 thisEvent.title = tr("No programme details");
575 bool swapColour = false; // alternate cell colour
576 bool currentRow = false;
577 int y = chanListbox.getRootBoxOffsetY() + 5; // vertical position of cell
578 DrawStyle bg, fg; // background colour of cells in grid
579 // for each displayed channel, find programmes that fall in 2.5 hour time window
580 for(UINT listIndex = 0; listIndex < gridRows; listIndex++)
582 if (listTop + (int)listIndex >= chanListbox.getBottomOption())
583 continue; // ensure nothing populates grid below last channel
585 currentRow = (listTop + (int)listIndex == chanListbox.getCurrentOption());
586 noevent.time = ltime;
587 noevent.duration = window_width * 60;
589 paintCell(&noevent, y, DrawStyle::NOPROGRAMME, DrawStyle::LIGHTTEXT); // fill row with no programme colour to be painted ove with valid programmes
592 thisEvent.description = tr("There are no programme details available for this period");
593 thisEvent.duration = window_width * 60;
594 thisEvent.time = ltime;
595 thisEvent.title = tr("No programme details");
598 if (eventLista[listIndex])
600 sort(eventLista[listIndex]->begin(), eventLista[listIndex]->end(), EventSorter());
601 for(e = 0; e < (eventLista[listIndex])->size(); e++) // step through events for this channel
603 fg = DrawStyle::LIGHTTEXT;
604 event = (*eventLista[listIndex])[e];
607 UINT end = event->time + event->duration; // programme end time
608 if(event->time >= UINT(ltime) + (window_width * 60)) // programme starts after RHS of window
609 continue; // that's enough of this channel's events
610 if(end <= UINT(ltime)) // programme ends before LHS of window
611 continue; // this event is before the window - let's try the next event
612 // this event is one we are interested in
613 bg = (swapColour)?DrawStyle::PROGRAMMEA:DrawStyle::PROGRAMMEB; // alternate cell colour
614 swapColour = !swapColour; // it wil be the other colour next time
615 if(event->time <= UINT(selTime) && end > UINT(selTime) && currentRow)
617 // this is the selected programme
618 thisEvent.description = event->description;
619 thisEvent.duration = event->duration;
620 thisEvent.time = event->time;
621 thisEvent.title = event->title;
622 thisEvent.id = event->id;
623 if(thisEvent.id == 0)
625 bg = DrawStyle::SELECTHIGHLIGHT; // highlight cell
626 fg = DrawStyle::DARKTEXT;
630 if (currentRow && thisEvent.id == 0)
632 if (end <= UINT(selTime) && end > UINT(thisEvent.time))
633 thisEvent.time = end;
634 if (event->time > UINT(selTime) && event->time < thisEvent.time + thisEvent.duration)
635 thisEvent.duration = event->time - thisEvent.time;
638 paintCell(event, y, bg, fg);
644 // no event list for this channel. Already painted noevent colour so just highlight if selected
647 bg = DrawStyle::SELECTHIGHLIGHT; // highlight cell
648 fg = DrawStyle::DARKTEXT;
649 paintCell(&thisEvent, y, bg, fg);
653 bg = DrawStyle::NOPROGRAMME;
654 fg = DrawStyle::LIGHTTEXT;
655 noevent.title = tr("No programme details");
656 paintCell(&noevent, y, bg, fg);
659 y += getFontHeight() + 2;
664 void VEpg::updateEventList()
666 if (!chanList) return;
668 for(UINT listIndex = 0; listIndex < gridRows; listIndex++)
670 if(listTop + listIndex >= UINT(chanListbox.getBottomOption()))
672 chan = (*chanList)[listTop + listIndex];
673 if (eventLista[listIndex])
675 (eventLista)[listIndex]->clear();
676 delete eventLista[listIndex];
678 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
682 void VEpg::setCurrentChannel()
684 chanName.setText((*chanList)[currentChannelIndex]->name);
687 chanName.getRootBoxRegion(&r);
688 boxstack->update(this, &r);
691 void VEpg::paintCell(Event* event, int yOffset, const DrawStyle& bg, const DrawStyle& fg)
694 w = x = 0; // keep compiler happy
696 h = getFontHeight(); // TODO if want border around text, need to increae this and wselectlist line height
697 UINT end = event->time + event->duration; // programme end time
698 if(event->time <= UINT(ltime) && end > UINT(ltime)) // spans start of displayed window
700 x = 155; // LHS of window
701 if (end > (UINT(ltime) + (window_width * 60)))
702 w = window_width * MINUTE_SCALE; // spans full 2 hour window
704 w = MINUTE_SCALE * (event->time + event->duration - ltime ) / 60; // get width of remaining programme
706 if((event->time >= UINT(ltime)) && (event->time <= UINT(ltime) + (window_width * 60))) // starts within window
708 x = 155 + (MINUTE_SCALE * (event->time - ltime) / 60);
709 w = MINUTE_SCALE * event->duration / 60;
710 //if (w > 155 + MINUTE_SCALE * WINDOW_WIDTH -x)
711 // w = w + x - 155 - MINUTE_SCALE * WINDOW_WIDTH; // ends outside window
713 if (w > 155 + (int)window_width * MINUTE_SCALE - x)
714 w = 155 + window_width * MINUTE_SCALE -x; // limit cells to RHS of window
715 rectangle(x, y, w, h, bg);
716 char* tt = new char[strlen(event->title.c_str()) + 1];
717 strcpy (tt, event->title.c_str());
719 unsigned int cur_length=1;
720 unsigned int text_max=strlen(tt);
722 if (Osd::getInstance()->charSet()!=1) mchar=true;
724 memset((void*)&state,0,sizeof(state));
727 for (textPos = 0; textPos <text_max; textPos+=cur_length)
731 cur_length = mbrtowc(&cur_char, tt + textPos, text_max-textPos, &state);
732 if (cur_length <= 0){
735 } else cur_char= *(tt+textPos);
736 float thisCharWidth = charWidth(cur_char);
737 if (textWidth + thisCharWidth > w) // text will not fit in cell
741 textWidth += thisCharWidth;
743 char* tT = new char[textPos+1];
746 strncpy(tT, tt, textPos );
748 surface->drawText(tT, x+2, y, fg);
754 time_t VEpg::prevHour(time_t* t)
757 LOCALTIME_R(t, &tms);
763 void VEpg::processMessage(Message* m)
765 if (m->message == Message::MOUSE_MOVE)
767 if (chanListbox.mouseMove(m->parameter - getScreenX(), m->tag - getScreenY()))
770 boxstack->update(this);
773 else if (m->message == Message::MOUSE_LBDOWN)
775 if (chanListbox.mouseLBDOWN(m->parameter - getScreenX(), m->tag - getScreenY()))
777 boxstack->handleCommand(Input::OK); //simulate OK press
781 //check if press is outside this view! then simulate cancel
782 int x = m->parameter - getScreenX();
783 int y = m->tag - getScreenY();
784 int keyx = chanListbox.getRootBoxOffsetX();
785 int keyy = chanListbox.getRootBoxOffsetY() + chanListbox.getHeight() + 2;
787 if (x<0 || y <0 || x>(int)getWidth() || y>(int)getHeight())
789 boxstack->handleCommand(Input::BACK); //simulate cancel press
791 else if (x>=(keyx+72) && y>=(keyy+4) &&x<=(keyx+72+104) &&y<=(keyy+4+getFontHeight() + 2))
793 boxstack->handleCommand(Input::RED);
795 else if (x>=(keyx+72) && y>=(keyy+ getFontHeight() + 8) &&x<=(keyx+72+104) &&y<=(keyy+8+2*getFontHeight() + 2))
797 boxstack->handleCommand(Input::GREEN);
799 else if (x>=(keyx+180) && y>=(keyy+4) &&x<=(keyx+180+104) &&y<=(keyy+4+getFontHeight() + 2))
801 boxstack->handleCommand(Input::YELLOW);
803 else if (x>=(keyx+180) && y>=(keyy+ getFontHeight() + 8) &&x<=(keyx+180+104) &&y<=(keyy+8+2*getFontHeight() + 2))
805 boxstack->handleCommand(Input::BLUE);
807 else if (x>=(keyx+290) && y>=(keyy+4) &&x<=(keyx+180+290) &&y<=(keyy+4+getFontHeight() + 2))
809 boxstack->handleCommand(Input::BACK);
811 else if (x>=(keyx+290) && y>=(keyy+ getFontHeight() + 8) &&x<=(keyx+290+180) &&y<=(keyy+8+2*getFontHeight() + 2))
813 boxstack->handleCommand(Input::RECORD);
815 else if (x>=(keyx+474) && y>=(keyy+4) &&x<=(keyx+128+474) &&y<=(keyy+4+getFontHeight() + 2))
817 boxstack->handleCommand(Input::PLAY);
819 else if (x>=(keyx+474) && y>=(keyy+ getFontHeight() + 8) &&x<=(keyx+238+474) &&y<=(keyy+8+2*getFontHeight() + 2))
821 boxstack->handleCommand(Input::GO);
823 else if ( x>=(chanListbox.getRootBoxOffsetX())
824 && y>=(chanListbox.getRootBoxOffsetY() + 5)
825 // &&x<=(chanListbox.getOffsetX()+155 + window_width * MINUTE_SCALE)
826 &&y<=(chanListbox.getRootBoxOffsetY() - getFontHeight()
827 - 3+(int)chanListbox.getHeight() + getFontHeight() + 3)
830 int cy=y-(chanListbox.getRootBoxOffsetY() + 5);
831 int row=cy/(getFontHeight()+2);
832 int clistTop = chanListbox.getTopOption();
833 chanListbox.hintSetCurrent(clistTop+row);
835 time_t ttime = cx*60/MINUTE_SCALE+ltime;
836 //x = 155 + (MINUTE_SCALE * (event->time - ltime) / 60);
840 boxstack->update(this);