trunk 20080912
[gitenigma.git] / src / enigma_epg.cpp
blob2e1785cfed1f75e0fccac3ad45d78ba11959b572
1 #include "enigma_epg.h"
2 #include "enigma.h"
3 #include "sselect.h"
4 #include "enigma_lcd.h"
5 #include "epgactions.h"
6 #include "timer.h"
7 #include "enigma_event.h"
9 #include <lib/dvb/epgcache.h>
10 #include <lib/dvb/service.h>
11 #include <lib/dvb/si.h>
12 #include <lib/dvb/serviceplaylist.h>
13 #include <lib/gui/eskin.h>
14 #include <lib/gui/elabel.h>
15 #include <lib/gui/guiactions.h>
16 #include <lib/gui/statusbar.h>
17 #include <lib/gdi/font.h>
18 #include <lib/gui/numberactions.h>
20 gPixmap *eZapEPG::entry::inTimer=0;
21 gPixmap *eZapEPG::entry::inTimerRec=0;
23 static eString buildShortName( const eString &str )
25 eString tmp;
26 static char stropen[3] = { 0xc2, 0x86, 0x00 };
27 static char strclose[3] = { 0xc2, 0x87, 0x00 };
28 unsigned int open=eString::npos-1;
30 while ( (open = str.find(stropen, open+2)) != eString::npos )
32 unsigned int close = str.find(strclose, open);
33 if ( close != eString::npos )
34 tmp+=str.mid( open+2, close-(open+2) );
36 return tmp;
39 eZapEPG::eZapEPG()
40 :eWidget(0,1), offs(0), focusColumn(0), hours(3)
41 ,numservices(8), eventWidget(0), NowTimeLineXPos(-1)
43 eConfig::getInstance()->getKey("/elitedvb/multiepg/hours", hours);
44 move(ePoint(70, 50));
45 resize(eSize(590, 470));
46 timeLine.setAutoDelete(true);
47 timeFont = eSkin::getActive()->queryFont("epg.time");
48 titleFont = eSkin::getActive()->queryFont("epg.title");
49 descrFont = eSkin::getActive()->queryFont("epg.description");
50 entryColor = eSkin::getActive()->queryColor("epg.entry.background");
51 entryColorSelected = eSkin::getActive()->queryColor("epg.entry.background.selected");
52 entry::inTimer = eSkin::getActive()->queryImage("timer_symbol");
53 entry::inTimerRec = eSkin::getActive()->queryImage("timer_rec_symbol");
54 addActionMap( &i_epgSelectorActions->map );
55 addActionMap( &i_focusActions->map );
56 addActionMap( &i_cursorActions->map );
57 addActionMap( &i_numberActions->map );
59 #ifndef DISABLE_FILE
60 addActionToHelpList( &i_epgSelectorActions->addDVRTimerEvent );
61 #endif
62 #ifndef DISABLE_NETWORK
63 addActionToHelpList( &i_epgSelectorActions->addNGRABTimerEvent );
64 #endif
65 addActionToHelpList( &i_epgSelectorActions->addSwitchTimerEvent );
66 addActionToHelpList( &i_epgSelectorActions->removeTimerEvent );
68 Signal1<void,const eServiceReference& > callback;
69 CONNECT( callback, eZapEPG::addToList );
70 eZap::getInstance()->getServiceSelector()->forEachServiceRef( callback, false );
71 curS = curE = services.begin();
72 sbar = new eStatusBar(this);
73 sbar->move( ePoint(0, clientrect.height()-50) );
74 sbar->resize( eSize( clientrect.width(), 50) );
75 sbar->loadDeco();
76 sbar->setFlags( eStatusBar::flagOwnerDraw|RS_WRAP);
78 eLabel *l = new eLabel(this);
79 l->move(ePoint(100, clientrect.height()-80) );
80 l->setFont( eSkin::getActive()->queryFont("eStatusBar") );
81 l->resize( eSize( clientrect.width()-100, 30) );
82 l->setText(_("press 1 ... 6 to select count of visible hours"));
83 l->setFlags( eLabel::flagVCenter );
85 setHelpID(13);
88 eZapEPG::~eZapEPG()
90 eConfig::getInstance()->setKey("/elitedvb/multiepg/hours", hours);
93 void eZapEPG::addToList( const eServiceReference& ref )
95 if ( ref.type == eServiceReference::idDVB )
96 services.push_back( (const eServiceReferenceDVB&) ref );
99 eZapEPG::entry::entry(eWidget *parent, gFont &timeFont, gFont &titleFont,
100 gFont &descrFont, gColor entryColor, gColor entryColorSelected, eWidget *sbar)
101 : eWidget(parent), timeFont(timeFont),
102 titleFont(titleFont), descrFont(descrFont), entryColor(entryColor),
103 entryColorSelected(entryColorSelected),
104 sbar(sbar), para(0), xOffs(0), yOffs(0)
106 setBackgroundColor(entryColor);
109 eZapEPG::entry::~entry()
111 if(para)
113 para->destroy();
114 para=0;
116 delete event;
119 void eZapEPG::entry::redrawWidget(gPainter *target, const eRect &area)
121 if ( !para )
123 para=new eTextPara( eRect(0, 0, size.width(), size.height() ) );
124 para->setFont(titleFont);
125 para->renderString(title, RS_WRAP);
126 int bboxHeight = para->getBoundBox().height();
127 int bboxWidth = para->getBoundBox().width();
128 yOffs = (bboxHeight < size.height()) ? (( size.height() - bboxHeight ) / 2) - para->getBoundBox().top() : 0;
129 xOffs = (bboxWidth < size.width()) ? (( size.width() - bboxWidth ) / 2) - para->getBoundBox().left() : 0;
132 target->renderPara(*para, ePoint( area.left()+xOffs, area.top()+yOffs) );
134 ePlaylistEntry* p=0;
135 if ( (p = eTimerManager::getInstance()->findEvent( &service->service, (EITEvent*)event )) )
137 if ( p->type & ePlaylistEntry::SwitchTimerEntry )
138 target->blit( *inTimer, ePoint( size.width()-inTimer->x-1, size.height()-inTimerRec->y-1 ), eRect(), gPixmap::blitAlphaTest);
139 else if ( p->type & ePlaylistEntry::RecTimerEntry )
140 target->blit( *inTimerRec, ePoint(size.width()-inTimerRec->x-1, size.height()-inTimerRec->y-1), eRect(), gPixmap::blitAlphaTest);
143 target->setForegroundColor(gColor(entryColorSelected));
144 target->fill(eRect(0, size.height()-1, size.width(), 1));
145 target->fill(eRect(size.width()-1, 0, 1, size.height()));
146 if (backgroundColor==entryColorSelected)
147 redrawed();
150 void eZapEPG::entry::gotFocus()
152 #ifndef DISABLE_LCD
153 eZapLCD* pLCD = eZapLCD::getInstance();
154 unsigned int pos = 0;
155 for (int i=0; i < 4; ++i)
156 pos = helptext.find(' ', pos+1);
157 if ( pos != eString::npos && (pos+1) < helptext.length() )
159 eString title =
160 helptext.left(pos);
161 title.removeChars(' ');
162 pLCD->lcdMenu->Title->setText(title);
163 pLCD->lcdMenu->Element->setText(helptext.mid(pos+1));
165 #endif
166 sbar->setText( helptext );
167 setBackgroundColor(entryColorSelected);
170 void eZapEPG::entry::lostFocus()
172 // setForegroundColor(normalF,false);
173 setBackgroundColor(entryColor);
176 int eZapEPG::eventHandler(const eWidgetEvent &event)
178 switch (event.type)
180 case eWidgetEvent::evtAction:
182 int addtype=-1;
183 int servicevalid = current_service != serviceentries.end();
184 int eventvalid = 0;
185 if ( servicevalid && current_service->current_entry != current_service->entries.end())
186 eventvalid = 1;
187 if ( (addtype = i_epgSelectorActions->checkTimerActions( event.action )) != -1 )
189 else if (event.action == &i_epgSelectorActions->removeTimerEvent)
191 if (eventvalid)
192 if ( eTimerManager::getInstance()->removeEventFromTimerList( this, &current_service->service, current_service->current_entry->event ) )
193 current_service->current_entry->invalidate();
195 else if (event.action == &i_focusActions->left)
196 selEntry(-1);
197 else if (event.action == &i_focusActions->right)
198 selEntry(+1);
199 else if (event.action == &i_focusActions->up)
200 selService(-1);
201 else if (event.action == &i_focusActions->down)
202 selService(+1);
203 else if (event.action == &i_cursorActions->ok)
204 close(eventvalid?0:-1);
205 else if (event.action == &i_cursorActions->cancel)
206 close(-1);
207 else if ((event.action == &i_epgSelectorActions->showExtendedInfo) && eventvalid)
209 eService *service=eDVB::getInstance()->settings->getTransponders()->searchService(current_service->service);
210 eEventDisplay ei(service ? service->service_name.c_str() : "", current_service->service, 0, (EITEvent*)current_service->current_entry->event);
212 #ifndef DISABLE_LCD
213 eZapLCD* pLCD = eZapLCD::getInstance();
214 pLCD->lcdMain->hide();
215 pLCD->lcdMenu->show();
216 ei.setLCD(pLCD->lcdMenu->Title, pLCD->lcdMenu->Element);
217 #endif
218 hide();
219 ei.show();
220 int ret;
221 while((ret = ei.exec()))
223 if (ret == 1)
224 selEntry(-1);
225 else if (ret == 2)
226 selEntry(+1);
227 else
228 break; // close EventDisplay
230 ei.setEvent((EITEvent*)current_service->current_entry->event);
232 ei.hide();
233 show();
234 drawTimeLines();
236 else if (event.action == &i_numberActions->key1)
238 hours=1;
239 close(5);
241 else if (event.action == &i_numberActions->key2)
243 hours=2;
244 close(5);
246 else if (event.action == &i_numberActions->key3)
248 hours=3;
249 close(5);
251 else if (event.action == &i_numberActions->key4)
253 hours=4;
254 close(5);
256 else if (event.action == &i_numberActions->key5)
258 hours=5;
259 close(5);
261 else if (event.action == &i_numberActions->key6)
263 hours=6;
264 close(5);
266 else
267 break;
268 if (eventvalid && (addtype != -1))
270 if ( !eTimerManager::getInstance()->eventAlreadyInList( this, *(EITEvent*)current_service->current_entry->event, current_service->service) )
272 hide();
273 eTimerEditView v( *(EITEvent*)current_service->current_entry->event, addtype, current_service->service);
274 v.show();
275 v.exec();
276 v.hide();
277 show();
280 return 1;
282 default:
283 break;
285 return eWidget::eventHandler(event);
288 void eZapEPG::buildService(serviceentry &service)
290 int width = service.pos.width();
291 service.entries.setAutoDelete(1);
292 eEPGCache *epgcache=eEPGCache::getInstance();
293 epgcache->Lock();
294 const timeMap *evmap = epgcache->getTimeMap(service.service);
295 if (!evmap)
297 epgcache->Unlock();
298 return;
301 timeMap::const_iterator ibegin = evmap->lower_bound(start);
302 if ((ibegin != evmap->end()) && (ibegin != evmap->begin()) )
304 if ( ibegin->first != start )
305 --ibegin;
307 else
308 ibegin=evmap->begin();
310 timeMap::const_iterator iend = evmap->lower_bound(end);
312 int tsidonid =
313 (service.service.getTransportStreamID().get()<<16) | service.service.getOriginalNetworkID().get();
315 for (timeMap::const_iterator event(ibegin); event != iend; ++event)
317 EITEvent *ev = new EITEvent(*event->second,tsidonid);
318 if (((ev->start_time+ev->duration)>= start) && (ev->start_time <= end))
320 eString description;
321 entry *e = new entry(eventWidget, timeFont, titleFont, descrFont, entryColor, entryColorSelected, sbar);
322 e->service = &service;
323 int xpos = (ev->start_time - start) * width / (end - start);
324 int ewidth = (ev->start_time + ev->duration - start) * width / (end - start);
325 ewidth -= xpos;
327 if (xpos < 0)
329 ewidth += xpos;
330 xpos = 0;
333 if ((xpos+ewidth) > width)
334 ewidth = width - xpos;
336 e->move(ePoint(service.pos.x() + xpos, service.pos.y()));
337 e->resize(eSize(ewidth, service.pos.height()));
338 CONNECT( e->redrawed, eZapEPG::drawTimeLines );
339 service.entries.push_back(e);
341 LocalEventData led;
342 led.getLocalData(ev, &e->title, &description);
343 tm *begin=localtime(&ev->start_time);
344 while ( description[0] == ' ' )
345 description.erase(0);
346 if ( description != e->title )
348 if ( description )
349 description = " - "+description;
351 else
352 description="";
353 eString tmp;
354 tmp.sprintf("%02d.%02d. %02d:%02d - ",
355 begin->tm_mday,
356 begin->tm_mon+1,
357 begin->tm_hour,
358 begin->tm_min);
360 time_t endTime = ev->start_time + ev->duration;
361 tm *end=localtime(&endTime);
362 tmp+=eString().sprintf("%02d:%02d %s%s",
363 end->tm_hour, end->tm_min,
364 e->title.c_str(), description.c_str());
366 e->setHelpText(tmp);
367 e->event = ev;
369 else
370 delete ev;
372 epgcache->Unlock();
375 void eZapEPG::selService(int dir)
377 if (serviceentries.begin() == serviceentries.end())
378 return;
379 int isok;
380 ePtrList<entry>::iterator l = current_service->current_entry;
381 isok = l != current_service->entries.end();
382 if (dir == +1)
386 ++current_service;
387 if (current_service == serviceentries.end())
389 focusColumn=0;
390 close(2);
391 return;
393 else
394 ++focusColumn;
396 while(current_service->entries.empty());
397 } else if (dir == -1)
401 if (current_service != serviceentries.begin())
403 --focusColumn;
404 --current_service;
406 else
408 close(1);
409 focusColumn=numservices-1;
410 return;
413 while(current_service->entries.empty());
415 time_t last_time=0;
417 if (isok)
419 l->lostFocus();
420 last_time = l->event->start_time;
423 if (current_service->current_entry != current_service->entries.end())
425 if (last_time)
427 int best_diff=0;
428 ePtrList<entry>::iterator best=current_service->entries.end();
429 for (ePtrList<entry>::iterator i(current_service->entries.begin());
430 i != current_service->entries.end(); ++i)
432 if ((best == current_service->entries.end()) || abs(i->event->start_time-last_time) < best_diff)
434 best = i;
435 best_diff = abs(i->event->start_time-last_time);
439 if (best != current_service->entries.end())
440 current_service->current_entry = best;
442 current_service->current_entry->gotFocus();
446 void eZapEPG::selEntry(int dir)
448 if (current_service == serviceentries.end() || current_service->entries.empty())
450 if ( dir == -1 && offs >= hours*3600 )
452 offs -= hours*3600;
453 close(3);
455 /* else
456 eDebug("invalid service");*/
457 return;
459 ePtrList<entry>::iterator l = current_service->current_entry;
460 if ( dir == +1)
462 ++current_service->current_entry;
463 if (current_service->current_entry == current_service->entries.end())
465 if ( eventWidget->isVisible() )
467 offs += hours*3600;
468 close(4);
470 else
471 --current_service->current_entry;
472 return;
474 } else
476 if (current_service->current_entry == current_service->entries.begin())
478 if ( offs >= hours*3600 )
480 offs -= hours*3600;
481 close(3);
483 return;
485 --current_service->current_entry;
487 if (l != current_service->entries.end())
488 l->lostFocus();
489 current_service->current_entry->gotFocus();
492 void eZapEPG::buildPage(int direction)
495 direction 1 -> up
496 direction 2 -> down
497 direction 3 -> left
498 direction 4 -> right */
499 NowTimeLineXPos = -1;
501 if ( eventWidget )
502 eventWidget->hide();
503 timeLine.clear();
505 serviceentries.clear();
506 current_service = serviceentries.end();
508 delete eventWidget;
509 eventWidget = new eWidget( this );
510 eventWidget->move(ePoint(0,0));
511 eSize tmps = clientrect.size();
512 tmps.setHeight( clientrect.height()-80 );
513 eventWidget->resize( tmps );
515 #ifndef DISABLE_LCD
516 eventWidget->setLCD( LCDTitle, LCDElement );
517 #endif
519 start=time(0)+eDVB::getInstance()->time_difference+offs;
520 unsigned int tmp = start % 900; // align to 15min
521 start -= tmp;
522 end=start+hours*3600;
524 if ( direction == 1 )
525 // go left.. we must count "numservices" back
527 std::list<eServiceReferenceDVB>::iterator s(curS);
528 unsigned int cnt=0;
531 if ( s == services.end() )
532 break;
533 if ( ++cnt > numservices )
534 break;
535 if ( s != services.begin() )
536 --s;
537 else
539 s = services.end();
540 if (s != services.begin())
541 --s;
542 else
543 break;
546 while( s != curS );
547 curS=curE=s;
549 else if ( direction == 2 ) // right
550 curS=curE;
551 else if ( direction > 2 ) // up or down
552 curE=curS;
554 int width = clientrect.width();
555 int serviceheight = (eventWidget->height()-40) / numservices;
557 time_t tmpTime=start;
558 int timeWidth = (width - 100) / (hours>3?hours:hours*2);
559 for (unsigned int i=0; i < (hours>3?hours:hours*2); ++i)
561 tmpTime+=i?(hours>3?3600:1800):0;
562 eLabel *l = new eLabel(eventWidget);
563 l->move(ePoint( i*timeWidth-(timeWidth/2)+100, 0));
564 l->resize(eSize(timeWidth,30));
565 l->setFlags(eLabel::flagVCenter);
566 l->setAlign(eTextPara::dirCenter);
567 tm *bla = localtime(&tmpTime);
568 l->setText(eString().sprintf("%d:%02d", bla->tm_hour, bla->tm_min));
569 timeLine.push_back(l);
572 int p=0;
573 std::set<int> nonEmptyServices;
577 if ( curE == services.end() )
578 break;
580 // const eventData *e = (const eventData*) eEPGCache::getInstance()->lookupEvent( *curE, (time_t)(start+tmp), true );
581 // if ( e )
583 serviceentries.push_back(serviceentry());
584 serviceentry &service = serviceentries.back();
585 service.header = new eLabel(eventWidget);
586 service.header->move(ePoint(0, p * serviceheight + 35));
587 service.header->resize(eSize(90, serviceheight));
588 service.pos = eRect(100, p * serviceheight + 35, width - 100, serviceheight );
590 eString stext;
591 if ( curE->descr ) // have changed service name?
592 stext=curE->descr; // use this...
593 else
595 eService *sv=eServiceInterface::getInstance()->addRef(*curE);
596 if ( sv )
598 eString shortname = buildShortName( sv->service_name );
599 stext=shortname?shortname:sv->service_name;
600 eServiceInterface::getInstance()->removeRef(*curE);
603 service.service = *curE;
605 // set column service name
606 service.header->setText(stext);
607 service.header->setFlags( eLabel::flagVCenter );
609 buildService(service);
611 if ( service.entries.empty() )
612 service.current_entry = service.entries.end();
613 else
615 // set focus line
616 if ( direction == 3 ) // up pressed
617 // set focus to last line
618 service.current_entry = --service.entries.end();
619 else // set focus to first line
620 service.current_entry = service.entries.begin();
621 nonEmptyServices.insert(p);
623 ++p;
625 if ( ++curE == services.end() ) // need wrap ?
626 curE = services.begin();
628 while( serviceentries.size() < numservices && curE != curS );
630 if (!p)
632 sbar->setText("");
633 return;
636 eventWidget->show();
638 if ( serviceentries.empty() )
640 drawTimeLines();
641 return;
644 std::set<int>::iterator it =
645 nonEmptyServices.lower_bound(focusColumn);
646 /* scroll back to the bottom non-empty service */
647 while ( it == nonEmptyServices.end() && it != nonEmptyServices.begin() ) --it;
648 if ( it != nonEmptyServices.end() )
649 focusColumn = *it;
650 else
651 focusColumn = 0;
653 // set column focus
654 current_service = serviceentries.begin();
655 for (unsigned int i=0; i < focusColumn; i++ )
657 if (current_service == --serviceentries.end())
658 break;
659 current_service++;
662 if (current_service->current_entry != current_service->entries.end())
663 current_service->current_entry->gotFocus();
665 if (nonEmptyServices.empty())
666 drawTimeLines();
669 void eZapEPG::drawTimeLines()
671 if ( eventWidget && eventWidget->isVisible() && timeLine.size() )
673 gPainter *p = getPainter(eRect(eventWidget->getPosition(),eventWidget->getSize()));
674 int incWidth=((eventWidget->width()-100)/(hours>3?hours:hours*2));
675 int pos=100;
676 int lineheight = eventWidget->height();
677 if ( NowTimeLineXPos != -1 )
679 int tmp=NowTimeLineXPos;
680 NowTimeLineXPos=-1;
681 invalidate( eRect( tmp,30,2,lineheight) );
683 p->setForegroundColor( eSkin::getActive()->queryColor("epg.timeline") );
684 for (ePtrList<eLabel>::iterator it(timeLine); it != timeLine.end(); ++it)
686 p->fill(eRect(pos,30,1,lineheight));
687 pos+=incWidth;
690 time_t now=time(0)+eDVB::getInstance()->time_difference;
691 if ( now >= start && now < end )
693 int bla = ((eventWidget->width()-100)*1000) / (hours*60);
694 NowTimeLineXPos = ((now/60) - (start/60)) * bla / 1000;
695 NowTimeLineXPos+=100;
696 p->setForegroundColor( eSkin::getActive()->queryColor("epg.timeline.now") );
697 p->fill(eRect(NowTimeLineXPos,30,2,lineheight));
699 delete p;