2 Copyright 2005 Brian Walton
\r
4 This file is part of VOMP.
\r
6 VOMP is free software; you can redistribute it and/or modify
\r
7 it under the terms of the GNU General Public License as published by
\r
8 the Free Software Foundation; either version 2 of the License, or
\r
9 (at your option) any later version.
\r
11 VOMP is distributed in the hope that it will be useful,
\r
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
\r
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
\r
14 GNU General Public License for more details.
\r
16 You should have received a copy of the GNU General Public License
\r
17 along with VOMP; if not, write to the Free Software
\r
18 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
\r
21 vepg presents a 2 dimensional electronic programme guide with channels down
\r
22 the y axis and time along the x axis.
\r
23 Programmes are layed on the x axis as alterate coloured blocks with as much
\r
24 of the programme title as will fit inside the block shown as text.
\r
25 Up and down commands step through the channels whilst left and right commands
\r
26 move through the programmes of the currently selected channel.
\r
27 When a programme is selected, it highlights in the grid and full programe details
\r
28 (start time, title and description) are displayed in an area at te top left of the screen.
\r
29 Any currently programmed timers will display in the grid and in the orogramme detail window as red
\r
30 It is possible to select a programme to be recorded by pressing the record button.
\r
31 The video stream currently being viewed is shown as quarter screen in the top right.
\r
37 #include "vchannellist.h"
\r
38 #include "command.h"
\r
40 #include "vepgsettimer.h"
\r
42 #include "wsymbol.h"
\r
43 #include "message.h"
\r
45 #include "boxstack.h"
\r
46 #include "channel.h"
\r
50 VEpg* VEpg::instance = NULL;
\r
52 VEpg::VEpg(void* tparent, UINT tcurrentChannelIndex, ULONG streamType)
\r
55 currentChannelIndex = tcurrentChannelIndex;
\r
57 // PAL / NTSC sizes -----------------------
\r
61 int summaryLines, summaryLowerPadding;
\r
63 //UINT gridRows; // is a class member
\r
65 if (Video::getInstance()->getFormat() == Video::PAL)
\r
72 summaryLowerPadding = 18;
\r
83 summaryLowerPadding = 28;
\r
88 // initialise variables and pointers
\r
89 boxstack = BoxStack::getInstance();
\r
92 chanList = VDR::getInstance()->getChannelsList(streamType); //TODO want to be able to display video and radio together
\r
95 for(UINT listIndex = 0; listIndex < gridRows; listIndex++)
\r
97 // initialise array of pointers to eventlist structures
\r
98 eventLista[listIndex] = NULL;
\r
101 // Create pallet on which to paint our epg view and position it in centre of screen.
\r
102 // Need to reduce size to deal with overscanning TVs.
\r
104 setSize(xsize, ysize);
\r
106 setPosition(xpos, ypos);
\r
109 // DrawStyle transparent = DrawStyle(0, 0, 0, 0);
\r
110 // setBackgroundColour(transparent);
\r
112 // progTitle.setSurface(surface);
\r
113 progTitle.setPosition(0,0);
\r
114 progTitle.setSize(300,(getFontHeight() + 4) * 2 + 16); //paragraph line seperation is 4 pixels
\r
115 progTitle.setBackgroundColour(DrawStyle::TITLEBARBACKGROUND);
\r
116 progTitle.setTextPos(5, 16);
\r
117 progTitle.setGap(4);
\r
120 // progInfo.setSurface(surface);
\r
121 progInfo.setBackgroundColour(DrawStyle::VIEWBACKGROUND);
\r
122 progInfo.setPosition(0, progTitle.getY2());
\r
123 progInfo.setSize(300,((getFontHeight() + 4) * summaryLines) + summaryLowerPadding);
\r
124 progInfo.setGap(4);
\r
127 // chanName.setSurface(surface);
\r
128 chanName.setSize(510, (getFontHeight() + 4));
\r
129 chanName.setPosition(305, chanNameYpos);
\r
130 DrawStyle t1(0, 0, 0, 90);
\r
131 chanName.setBackgroundColour(t1);
\r
134 // create area to display list of channels
\r
135 // chanListbox.setSurface(surface); // add channel list
\r
136 chanListbox.setPosition(0, progInfo.getY2() + getFontHeight() + 8); // position channel list
\r
137 chanListbox.setSize(150, ((getFontHeight() + 2) * gridRows) + 5); //listbox line seperation is 2 pixels
\r
138 chanListbox.setGap(2);
\r
141 // populate channel list
\r
146 for (UINT i = 0; i < chanList->size(); i++)
\r
148 chan = (*chanList)[i];
\r
149 if (i == currentChannelIndex)
\r
151 chan->index = chanListbox.addOption(chan->name, 0, first);
\r
154 chanName.setText((*chanList)[chanListbox.getCurrentOption()]->name);
\r
157 listTop = chanListbox.getTopOption();
\r
158 chanListbox.draw(); // doing this to allow chanListbox.getBottomOption() in updateEventList() to work
\r
159 time(<ime); // set ltime to now
\r
160 ltime = prevHour(<ime); // set ltime to previous hour TODO make this half hour?
\r
161 time(&selTime); // set selTime to now
\r
162 updateEventList(); // get list of programmes
\r
165 void VEpg::preDelete()
\r
167 Timers::getInstance()->cancelTimer(this, 1);
\r
175 for(UINT listIndex = 0; listIndex < gridRows; listIndex++)
\r
177 if (eventLista[listIndex])
\r
179 (eventLista)[listIndex]->clear();
\r
180 delete eventLista[listIndex];
\r
183 // delete [] eventLista; // FIXME
\r
185 // destroy dynamically allocated memory
\r
188 VEpg* VEpg::getInstance()
\r
193 void VEpg::setInfo(Event* event)
\r
196 struct tm* btime; // to hold programme start and end time
\r
197 char timeString[9]; // to hold programme start and end time
\r
198 int length = strlen(event->title); // calculate length of programme title string
\r
199 char* title = new char[length + 15]; // create string to hold start time, end time and programme title
\r
200 btime = localtime((time_t*)&event->time); //get programme start time
\r
202 strftime(timeString, 9, "%0H:%0M - ", btime); // and format it as hh:mm -
\r
204 strftime(timeString, 9, "%H:%M - ", btime); // and format it as hh:mm -
\r
206 strcpy(title, timeString); // put it in our buffer
\r
207 t = event->time + event->duration; //get programme end time
\r
208 btime = localtime(&t);
\r
210 strftime(timeString, 7, "%0H:%0M ", btime); // and format it as hh:mm -
\r
212 strftime(timeString, 7, "%H:%M ", btime); // and format it as hh:mm -
\r
214 strcat(title, timeString); // put it in our buffer
\r
215 strcat(title, event->title); // then add the programme title
\r
216 progTitle.setText(title); // sput this sring in our text box
\r
217 length = strlen(event->description);
\r
218 char* info = new char[length + 1]; // create programme detail string
\r
219 strcpy(info, event->description);
\r
220 progInfo.setText(info); // show programme detail string
\r
221 // destroy dynamically allocated memory
\r
228 // View::draw(); // draw pallet
\r
230 DrawStyle transparent = DrawStyle(0, 0, 0, 0);
\r
231 fillColour(transparent);
\r
234 // Moved all the dynamic data drawing to a seperate function
\r
236 // Display the status and key stuff at the bottom
\r
238 UINT keyx = chanListbox.getRootBoxOffsetX();
\r
239 UINT keyy = chanListbox.getRootBoxOffsetY() + chanListbox.getHeight() + 2;
\r
240 DrawStyle ref1 = DrawStyle(100, 100, 100, 255); //to do global definition for skin
\r
241 rectangle(keyx, keyy, 605, getFontHeight() * 2 + 14, ref1);
\r
246 w.nextSymbol = WSymbol::LEFTARROW;
\r
247 w.setPosition(keyx + 1, keyy + 20);
\r
250 w.nextSymbol = WSymbol::UP;
\r
251 w.setPosition(keyx + 26, keyy + 3);
\r
254 w.nextSymbol = WSymbol::DOWN;
\r
255 w.setPosition(keyx + 26, keyy + 36);
\r
258 w.nextSymbol = WSymbol::RIGHTARROW;
\r
259 w.setPosition(keyx + 50, keyy + 20);
\r
262 drawText(tr("OK"), keyx + 18, keyy + 20, DrawStyle::LIGHTTEXT);
\r
264 DrawStyle ref2 = DrawStyle(200, 0, 0, 255);
\r
265 rectangle(keyx + 72, keyy + 4, 104, getFontHeight() + 2, ref2);
\r
266 drawText(tr("Page up"), keyx + 74, keyy + 5, DrawStyle::LIGHTTEXT);
\r
268 DrawStyle ref3 = DrawStyle(0, 200, 0, 255);
\r
269 rectangle(keyx + 72, keyy + getFontHeight() + 8, 104, getFontHeight() + 2, ref3);
\r
270 drawText(tr("Page down"), keyx + 74, keyy + getFontHeight() + 9, DrawStyle::LIGHTTEXT);
\r
272 DrawStyle ref4 = DrawStyle(200, 200, 0, 255);
\r
273 rectangle(keyx + 180, keyy + 4, 104, getFontHeight() + 2, ref4);
\r
274 drawText(tr("-24 hours"), keyx + 182, keyy + 5, DrawStyle::LIGHTTEXT);
\r
276 DrawStyle ref5 = DrawStyle(0, 0, 200, 255);
\r
277 rectangle(keyx + 180, keyy + getFontHeight() + 8, 104, getFontHeight() + 2, ref5);
\r
278 drawText(tr("+24 hours"), keyx + 182, keyy + getFontHeight() + 9, DrawStyle::LIGHTTEXT);
\r
280 DrawStyle ref6 = DrawStyle(180, 180, 180, 255);
\r
281 rectangle(keyx + 290, keyy + 4, 180, getFontHeight() + 2, ref6);
\r
282 drawText(tr("Guide / Back: Close"), keyx + 292 , keyy + 5, DrawStyle::LIGHTTEXT);
\r
284 DrawStyle ref7 = DrawStyle(180, 180, 180, 255);
\r
285 rectangle(keyx + 290, keyy + getFontHeight() + 8, 180, getFontHeight() + 2, ref7);
\r
286 DrawStyle red = DrawStyle(130, 0, 0);
\r
287 drawText(tr("Rec: Set timer"), keyx + 292, keyy + getFontHeight() + 9, red);
\r
289 DrawStyle ref8 = DrawStyle(180, 180, 180, 255);
\r
290 rectangle(keyx + 474, keyy + 4, 128, getFontHeight() + 2, ref8);
\r
291 w.nextSymbol = WSymbol::PLAY;
\r
292 w.setPosition(keyx + 476, keyy + 5);
\r
294 drawText(tr("Sel channel"), keyx + 496, keyy + 5, DrawStyle::LIGHTTEXT);
\r
296 DrawStyle ref9 = DrawStyle(180, 180, 180, 255);
\r
297 rectangle(keyx + 474, keyy + getFontHeight() + 8, 128, getFontHeight() + 2, ref9);
\r
298 drawText(tr("Go: Preview"), keyx + 476, keyy + getFontHeight() + 9, DrawStyle::LIGHTTEXT);
\r
301 // Draw all the dynamic data
\r
305 void VEpg::drawData()
\r
307 // Not doing View::draw() every time causes
\r
308 // things not to be cleared off the surface properly
\r
309 // So, blank out the data area first
\r
312 chanListbox.getRootBoxOffsetX(),
\r
313 chanListbox.getRootBoxOffsetY() - getFontHeight() - 3,
\r
314 155 + WINDOW_WIDTH * MINUTE_SCALE,
\r
315 chanListbox.getHeight() + getFontHeight() + 4,
\r
318 chanListbox.draw();
\r
320 chanName.draw(); // TODO this should be dealt with by vvideolive
\r
325 // Set timer to redraw to move the current time bar
\r
328 dt = 60 - (t % 60);
\r
329 if (dt == 0) dt = 60;
\r
331 Timers::getInstance()->setTimerT(this, 1, dt);
\r
334 void VEpg::timercall(int clientReference)
\r
337 boxstack->update(this);
\r
340 int VEpg::handleCommand(int command)
\r
344 case Remote::DF_UP:
\r
346 { // cursor up the channel list
\r
349 boxstack->update(this);
\r
352 case Remote::DF_DOWN:
\r
354 { // cursor down the channel list
\r
355 Log::getInstance()->log("VEPG", Log::DEBUG, "Down start");
\r
357 chanListbox.down();
\r
359 boxstack->update(this);
\r
360 Log::getInstance()->log("VEPG", Log::DEBUG, "Down end");
\r
364 case Remote::DF_LEFT:
\r
366 { // cursor left through time
\r
367 selTime = thisEvent.time - 1;
\r
369 boxstack->update(this);
\r
372 case Remote::DF_RIGHT:
\r
373 case Remote::RIGHT:
\r
375 // cursor right through time
\r
376 selTime = thisEvent.time + thisEvent.duration;
\r
378 boxstack->update(this);
\r
383 // cursor up one page
\r
384 chanListbox.pageUp();
\r
386 boxstack->update(this);
\r
389 case Remote::GREEN:
\r
391 // cursor down one page
\r
392 chanListbox.pageDown();
\r
394 boxstack->update(this);
\r
399 // step forward 24 hours
\r
400 selTime += 24 * 60 * 60;
\r
402 boxstack->update(this);
\r
405 case Remote::YELLOW:
\r
407 // step forward 24 hours
\r
408 selTime -= 24 * 60 * 60;
\r
410 boxstack->update(this);
\r
413 case Remote::RECORD:
\r
415 if (!chanList) return 2;
\r
416 Log::getInstance()->log("VEPG", Log::DEBUG, "ID %lu TIME %lu DURATION %lu TITLE %s", thisEvent.id, thisEvent.time, thisEvent.duration, thisEvent.title);
\r
417 VEpgSetTimer* vs = new VEpgSetTimer(&thisEvent, (*chanList)[chanListbox.getCurrentOption()]);
\r
420 boxstack->update(vs);
\r
427 if (!chanList) return 2;
\r
429 // select programme and display menu TODO currently just changes to selected channel
\r
431 currentChannelIndex = chanListbox.getCurrentOption();
\r
435 Message* m = new Message(); // Must be done after this view deleted
\r
438 m->message = Message::CHANNEL_CHANGE;
\r
439 m->parameter = (*chanList)[currentChannelIndex]->number;
\r
440 Command::getInstance()->postMessageNoLock(m);
\r
443 setCurrentChannel();
\r
445 if(command == Remote::GO)
\r
447 // GO just changes channel in preview, PLAY changes channel and returns to normal TV
\r
450 case Remote::GUIDE:
\r
452 // return to normal TV mode
\r
453 if (parent) // ptr check done in case being tested from videorec
\r
455 Message* m = new Message(); // Must be done after this view deleted
\r
458 m->message = Message::EPG_CLOSE;
\r
459 Command::getInstance()->postMessageNoLock(m);
\r
463 case Remote::CHANNELUP:
\r
465 if (currentChannelIndex == (chanList->size() - 1)) // at the end
\r
466 currentChannelIndex = 0;
\r
468 ++currentChannelIndex;
\r
472 Message* m = new Message(); // Must be done after this view deleted
\r
475 m->message = Message::CHANNEL_CHANGE;
\r
476 m->parameter = (*chanList)[currentChannelIndex]->number;
\r
477 Command::getInstance()->postMessageNoLock(m);
\r
480 setCurrentChannel();
\r
484 case Remote::CHANNELDOWN:
\r
486 if (currentChannelIndex == 0) // at the start
\r
487 currentChannelIndex = chanList->size() - 1; // so go to end
\r
489 --currentChannelIndex;
\r
493 Message* m = new Message(); // Must be done after this view deleted
\r
496 m->message = Message::CHANNEL_CHANGE;
\r
497 m->parameter = (*chanList)[currentChannelIndex]->number;
\r
498 Command::getInstance()->postMessageNoLock(m);
\r
501 setCurrentChannel();
\r
506 // stop command getting to any more views
\r
510 void VEpg::drawgrid() // redraws grid and select programme
\r
512 // draw the grid of programmes
\r
513 char timeString[20];
\r
515 time(&t); // set t = now
\r
517 selTime = t; // don't allow cursor in the past
\r
518 if(listTop != chanListbox.getTopOption())
\r
520 // chanListbox has scrolled TODO speed up by changing only rows that have changed
\r
521 listTop = chanListbox.getTopOption();
\r
524 if ((selTime >= ltime + WINDOW_WIDTH * 60) || (selTime <= ltime))
\r
526 // we have cursored back before left time of window
\r
527 //TODO check that this and above don't happen together
\r
528 ltime = prevHour(&selTime);
\r
532 DrawStyle white = DrawStyle(255, 255, 255, 255);
\r
536 tms = localtime(&t);
\r
537 strftime(timeString, 19, "%a %d %b", tms);
\r
538 int timey = chanListbox.getRootBoxOffsetY() - getFontHeight() - 3;
\r
540 drawTextRJ(timeString, timex - 10, timey, DrawStyle::LIGHTTEXT); // print date
\r
541 strftime(timeString, 19, "%H:%M", tms);
\r
542 drawText(timeString, timex, timey, DrawStyle::LIGHTTEXT); // print left time
\r
544 rectangle(155, timey + getFontHeight(), 2, 7, white);
\r
546 tms = localtime(&t);
\r
547 strftime(timeString, 19, "%H:%M", tms);
\r
548 drawText(timeString, timex + 180, timey, DrawStyle::LIGHTTEXT); // print middle time
\r
549 rectangle(335, timey + getFontHeight(), 2, 7, white);
\r
551 tms = localtime(&t);
\r
552 strftime(timeString, 19, "%H:%M", tms);
\r
553 drawText(timeString, timex + 360, timey, DrawStyle::LIGHTTEXT); // print right time
\r
554 rectangle(515, timey + getFontHeight(), 2, 7, white);
\r
555 // pointer to selTime
\r
556 //rectangle(155 + (selTime - ltime) / 20, timey + getFontHeight(), 2, 7, DrawStyle(255, 50, 50, 255));
\r
558 // current time line
\r
560 if ((t >= ltime) && (t < (ltime + 9000)))
\r
562 rectangle(155 + (t - ltime) / 20, timey + getFontHeight(), 2, ((getFontHeight() + 2) * 7) + 7 + 2, DrawStyle::RED);
\r
565 // TODO should the above two comditional statements be combined to avoid calling updateEventList() twice?
\r
567 Event noevent; // an event to use if there are gaps in the epg
\r
568 thisEvent.setdescription(tr("There are no programme details available for this period"));
\r
569 thisEvent.duration = WINDOW_WIDTH * 60;
\r
570 thisEvent.time = ltime;
\r
571 thisEvent.settitle(tr("No programme details"));
\r
573 bool swapColour = false; // alternate cell colour
\r
574 bool currentRow = false;
\r
575 int y = chanListbox.getRootBoxOffsetY() + 5; // vertical position of cell
\r
576 DrawStyle bg, fg; // background colour of cells in grid
\r
577 // for each displayed channel, find programmes that fall in 2.5 hour time window
\r
578 for(UINT listIndex = 0; listIndex < gridRows; listIndex++)
\r
580 if (listTop + (int)listIndex >= chanListbox.getBottomOption())
\r
581 continue; // ensure nothing populates grid below last channel
\r
582 currentRow = (listTop + (int)listIndex == chanListbox.getCurrentOption());
\r
583 noevent.time = ltime;
\r
584 noevent.duration = WINDOW_WIDTH * 60;
\r
585 noevent.settitle("");
\r
586 paintCell(&noevent, y, DrawStyle::NOPROGRAMME, DrawStyle::LIGHTTEXT); // fill row with no programme colour to be painted ove with valid programmes
\r
589 thisEvent.setdescription(tr("There are no programme details available for this period"));
\r
590 thisEvent.duration = WINDOW_WIDTH * 60;
\r
591 thisEvent.time = ltime;
\r
592 thisEvent.settitle(tr("No programme details"));
\r
595 if (eventLista[listIndex])
\r
597 sort(eventLista[listIndex]->begin(), eventLista[listIndex]->end(), EventSorter());
\r
598 for(e = 0; e < (eventLista[listIndex])->size(); e++) // step through events for this channel
\r
600 fg = DrawStyle::LIGHTTEXT;
\r
601 event = (*eventLista[listIndex])[e];
\r
604 UINT end = event->time + event->duration; // programme end time
\r
605 if(event->time >= UINT(ltime) + (WINDOW_WIDTH * 60)) // programme starts after RHS of window
\r
606 continue; // that's enough of this channel's events
\r
607 if(end <= UINT(ltime)) // programme ends before LHS of window
\r
608 continue; // this event is before the window - let's try the next event
\r
609 // this event is one we are interested in
\r
610 bg = (swapColour)?DrawStyle::PROGRAMMEA:DrawStyle::PROGRAMMEB; // alternate cell colour
\r
611 swapColour = !swapColour; // it wil be the other colour next time
\r
612 if(event->time <= UINT(selTime) && end > UINT(selTime) && currentRow)
\r
614 // this is the selected programme
\r
615 thisEvent.setdescription(event->description);
\r
616 thisEvent.duration = event->duration;
\r
617 thisEvent.time = event->time;
\r
618 thisEvent.settitle(event->title);
\r
619 thisEvent.id = event->id;
\r
620 if(thisEvent.id == 0)
\r
622 bg = DrawStyle::SELECTHIGHLIGHT; // highlight cell
\r
623 fg = DrawStyle::DARKTEXT;
\r
627 if (currentRow && thisEvent.id == 0)
\r
629 if (end <= UINT(selTime) && end > UINT(thisEvent.time))
\r
630 thisEvent.time = end;
\r
631 if (event->time > UINT(selTime) && event->time < thisEvent.time + thisEvent.duration)
\r
632 thisEvent.duration = event->time - thisEvent.time;
\r
635 paintCell(event, y, bg, fg);
\r
641 // no event list for this channel. Already painted noevent colour so just highlight if selected
\r
644 bg = DrawStyle::SELECTHIGHLIGHT; // highlight cell
\r
645 fg = DrawStyle::DARKTEXT;
\r
646 paintCell(&thisEvent, y, bg, fg);
\r
650 bg = DrawStyle::NOPROGRAMME;
\r
651 fg = DrawStyle::LIGHTTEXT;
\r
652 noevent.settitle(tr("No programme details"));
\r
653 paintCell(&noevent, y, bg, fg);
\r
656 y += getFontHeight() + 2;
\r
658 setInfo(&thisEvent);
\r
661 void VEpg::updateEventList()
\r
663 if (!chanList) return;
\r
665 for(UINT listIndex = 0; listIndex < gridRows; listIndex++)
\r
667 if(listTop + listIndex >= UINT(chanListbox.getBottomOption()))
\r
669 chan = (*chanList)[listTop + listIndex];
\r
671 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
\r
675 void VEpg::setCurrentChannel()
\r
677 chanName.setText((*chanList)[currentChannelIndex]->name);
\r
680 chanName.getRootBoxRegion(&r);
\r
681 boxstack->update(this, &r);
\r
684 void VEpg::paintCell(Event* event, int yOffset, const DrawStyle& bg, const DrawStyle& fg)
\r
687 w = x = 0; // keep compiler happy
\r
690 h = getFontHeight(); // TODO if want border around text, need to increae this and wselectlist line height
\r
691 UINT end = event->time + event->duration; // programme end time
\r
692 if(event->time <= UINT(ltime) && end > UINT(ltime)) // spans start of displayed window
\r
694 x = 155; // LHS of window
\r
695 if (end > (UINT(ltime) + (WINDOW_WIDTH * 60)))
\r
696 w = WINDOW_WIDTH * MINUTE_SCALE; // spans full 2 hour window
\r
698 w = MINUTE_SCALE * (event->time + event->duration - ltime ) / 60; // get width of remaining programme
\r
700 if((event->time >= UINT(ltime)) && (event->time <= UINT(ltime) + (WINDOW_WIDTH * 60))) // starts within window
\r
702 x = 155 + (MINUTE_SCALE * (event->time - ltime) / 60);
\r
703 w = MINUTE_SCALE * event->duration / 60;
\r
704 //if (w > 155 + MINUTE_SCALE * WINDOW_WIDTH -x)
\r
705 // w = w + x - 155 - MINUTE_SCALE * WINDOW_WIDTH; // ends outside window
\r
707 if (w > 155 + WINDOW_WIDTH * MINUTE_SCALE - x)
\r
708 w = 155 + WINDOW_WIDTH * MINUTE_SCALE -x; // limit cells to RHS of window
\r
709 rectangle(x, y, w, h, bg);
\r
710 char* tt = new char[strlen(event->title) + 1];
\r
711 strcpy (tt, event->title);
\r
712 float textWidth = 0;
\r
713 unsigned int cur_length=1;
\r
714 unsigned int text_max=strlen(tt);
\r
716 for (textPos = 0; textPos <text_max; textPos+=cur_length)
\r
718 wchar_t cur_char=getWChar(tt+textPos,&cur_length);
\r
719 float thisCharWidth = charWidth(cur_char);
\r
720 if (textWidth + thisCharWidth > w) // text will not fit in cell
\r
724 textWidth += thisCharWidth;
\r
726 char* tT = new char[textPos+1];
\r
729 strncpy(tT, tt, textPos );
\r
730 tT[textPos ] = '\0';
\r
731 surface->drawText(tT, x+2, y, fg);
\r
737 time_t VEpg::prevHour(time_t* t)
\r
740 tms = localtime(t);
\r
743 return mktime(tms);
\r
746 void VEpg::processMessage(Message* m)
\r
748 if (m->message == Message::MOUSE_MOVE)
\r
750 if (chanListbox.mouseMove((m->parameter>>16)-getScreenX(),(m->parameter&0xFFFF)-getScreenY()))
\r
753 boxstack->update(this);
\r
756 else if (m->message == Message::MOUSE_LBDOWN)
\r
758 if (chanListbox.mouseLBDOWN((m->parameter>>16)-getScreenX(),(m->parameter&0xFFFF)-getScreenY()))
\r
760 boxstack->handleCommand(Remote::OK); //simulate OK press
\r
764 //check if press is outside this view! then simulate cancel
\r
765 int x=(m->parameter>>16)-getScreenX();
\r
766 int y=(m->parameter&0xFFFF)-getScreenY();
\r
767 int keyx = chanListbox.getRootBoxOffsetX();
\r
768 int keyy = chanListbox.getRootBoxOffsetY() + chanListbox.getHeight() + 2;
\r
770 if (x<0 || y <0 || x>(int)getWidth() || y>(int)getHeight())
\r
772 boxstack->handleCommand(Remote::BACK); //simulate cancel press
\r
774 else if (x>=(keyx+72) && y>=(keyy+4) &&x<=(keyx+72+104) &&y<=(keyy+4+getFontHeight() + 2))
\r
776 boxstack->handleCommand(Remote::RED);
\r
778 else if (x>=(keyx+72) && y>=(keyy+ getFontHeight() + 8) &&x<=(keyx+72+104) &&y<=(keyy+8+2*getFontHeight() + 2))
\r
780 boxstack->handleCommand(Remote::GREEN);
\r
782 else if (x>=(keyx+180) && y>=(keyy+4) &&x<=(keyx+180+104) &&y<=(keyy+4+getFontHeight() + 2))
\r
784 boxstack->handleCommand(Remote::YELLOW);
\r
786 else if (x>=(keyx+180) && y>=(keyy+ getFontHeight() + 8) &&x<=(keyx+180+104) &&y<=(keyy+8+2*getFontHeight() + 2))
\r
788 boxstack->handleCommand(Remote::BLUE);
\r
790 else if (x>=(keyx+290) && y>=(keyy+4) &&x<=(keyx+180+290) &&y<=(keyy+4+getFontHeight() + 2))
\r
792 boxstack->handleCommand(Remote::BACK);
\r
794 else if (x>=(keyx+290) && y>=(keyy+ getFontHeight() + 8) &&x<=(keyx+290+180) &&y<=(keyy+8+2*getFontHeight() + 2))
\r
796 boxstack->handleCommand(Remote::RECORD);
\r
798 else if (x>=(keyx+474) && y>=(keyy+4) &&x<=(keyx+128+474) &&y<=(keyy+4+getFontHeight() + 2))
\r
800 boxstack->handleCommand(Remote::PLAY);
\r
802 else if (x>=(keyx+474) && y>=(keyy+ getFontHeight() + 8) &&x<=(keyx+238+474) &&y<=(keyy+8+2*getFontHeight() + 2))
\r
804 boxstack->handleCommand(Remote::GO);
\r
806 else if ( x>=(chanListbox.getRootBoxOffsetX())
\r
807 && y>=(chanListbox.getRootBoxOffsetY() + 5)
\r
808 // &&x<=(chanListbox.getOffsetX()+155 + WINDOW_WIDTH * MINUTE_SCALE)
\r
809 &&y<=(chanListbox.getRootBoxOffsetY() - getFontHeight()
\r
810 - 3+(int)chanListbox.getHeight() + getFontHeight() + 3)
\r
813 int cy=y-(chanListbox.getRootBoxOffsetY() + 5);
\r
814 int row=cy/(getFontHeight()+2);
\r
815 int clistTop = chanListbox.getTopOption();
\r
816 chanListbox.hintSetCurrent(clistTop+row);
\r
818 time_t ttime = cx*60/MINUTE_SCALE+ltime;
\r
819 //x = 155 + (MINUTE_SCALE * (event->time - ltime) / 60);
\r
823 boxstack->update(this);
\r