]> git.vomp.tv Git - vompclient.git/blob - vvideolivetv.cc
Add recording option to VEpgSummary
[vompclient.git] / vvideolivetv.cc
1 /*
2     Copyright 2007-2008 Chris Tallon
3
4     This file is part of VOMP.
5
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.
10
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.
15
16     You should have received a copy of the GNU General Public License
17     along with VOMP; if not, write to the Free Software
18     Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
19 */
20
21 #include "vvideolivetv.h"
22
23 #include "vchannellist.h"
24 #include "video.h"
25 #include "audio.h"
26 #include "playerlive.h"
27 #include "playerlivetv.h"
28 #include "playerliveradio.h"
29 #include "channel.h"
30 #include "boxstack.h"
31 #include "colour.h"
32 #include "osd.h"
33 #include "command.h"
34 #include "i18n.h"
35 #include "wtextbox.h"
36 #include "remote.h"
37 #include "vaudioselector.h"
38 #include "colour.h"
39 #include "event.h"
40 #include "timers.h"
41 #include "vepg.h"
42 #include "bitmap.h"
43 #include "log.h"
44 #include "vteletextview.h"
45 #include "vepgsummary.h"
46
47 VVideoLiveTV::VVideoLiveTV(ChannelList* tchanList, ULONG initialChannelNumber, VChannelList* tvchannelList)
48 : osdBack(0, 0, 0, 128)
49 {
50   vdr = VDR::getInstance();
51   boxstack = BoxStack::getInstance();
52   video = Video::getInstance();
53   
54   vas = NULL;
55
56   chanList = tchanList;
57   vchannelList = tvchannelList;
58   numberWidth = (int)VDR::getInstance()->getChannelNumberWidth();
59
60   currentChannelIndex = 0;
61   previousChannelIndex = 0;
62   osdChannelIndex = 0;
63   keying = 0;
64   preBuffering = 0;
65
66   playing = false;
67
68   // Convert channel number to index
69   UINT i;
70   for(i = 0; i < chanList->size(); i++)
71   {
72     if ((*chanList)[i]->number == (UINT)initialChannelNumber)
73     {
74       currentChannelIndex = i;
75       osdChannelIndex = i;
76       break;
77     }
78   }
79
80   eventList = NULL;
81
82   videoMode = video->getMode();
83   
84   if ((*chanList)[currentChannelIndex]->type == VDR::VIDEO)
85   {
86     streamType = VDR::VIDEO;
87     player = new PlayerLiveTV(Command::getInstance(), this, this, chanList);
88   }
89   else
90   {
91     streamType = VDR::RADIO;
92     player = new PlayerLiveRadio(Command::getInstance(), this, chanList);
93   }
94   player->init();
95
96   setSize(video->getScreenWidth(), video->getScreenHeight());
97   createBuffer();
98   DrawStyle transparent(0, 0, 0, 0);
99   setBackgroundColour(transparent);
100 #ifdef PAL_WSS
101   dowss = false;
102   char* optionWSS = vdr->configLoad("General", "WSS");
103   if (optionWSS)
104   {
105     if (strstr(optionWSS, "Yes")) dowss = true;
106     delete[] optionWSS;
107   }
108   Log::getInstance()->log("VVideoLiveTV", Log::DEBUG, "Do WSS: %u", dowss);
109
110   if (dowss)
111   {
112     wss.setFormat(video->getFormat());
113     wss.setWide(true);
114     add(&wss);
115     
116     wssRegion.x = 0;
117     wssRegion.y = 6;
118     wssRegion.w = video->getScreenWidth();
119     wssRegion.h = 2;
120   }
121 #endif
122   // This variable is set to true if the user pressed OK to bring the OSD on screen
123   // This is only used on old remotes to stop up/down buttons being used for osd-epg scrolling
124   okTriggeredOSD = false;
125
126   osdposterbanner.setPosition(20,20);
127   osdposterbanner.setBackgroundColour(osdBack);
128   osdposterbanner.setSize(video->getScreenWidth()*4/10,video->getScreenHeight()*4/10);
129   osdposterbanner.setVisible(false);
130   add(&osdposterbanner);
131
132
133   osd.setBackgroundColour(osdBack);
134   osd.setPosition(0, video->getScreenHeight() - 150);
135   osd.setSize(video->getScreenWidth(), 150);
136   osd.setVisible(false);
137   add(&osd);
138   
139
140   clock.setBackgroundColour(osdBack);
141   clock.setPosition(osd.getWidth() - 100, 4);
142   clock.setSize(90, 30);
143   osd.add(&clock);
144
145   osdChanNum.setBackgroundColour(osdBack);
146   osdChanNum.setPosition(50, 4);
147   osdChanNum.setSize((numberWidth*10) + 22, 30); // 10 px = width of number chars in font
148   osd.add(&osdChanNum);  
149
150   osdChanName.setBackgroundColour(osdBack);
151   osdChanName.setPosition(osdChanNum.getX2() + 10, 4);
152   osdChanName.setSize(300, 30);
153   osd.add(&osdChanName);
154   
155   boxRed.setBackgroundColour(DrawStyle::RED);
156   boxRed.setPosition(54, 104);
157   boxRed.setSize(18, 16);
158   osd.add(&boxRed);
159
160   boxGreen.setBackgroundColour(DrawStyle::GREEN);
161   boxGreen.setPosition(220, 104);
162   boxGreen.setSize(18, 16);
163   osd.add(&boxGreen);
164
165   boxYellow.setBackgroundColour(DrawStyle::YELLOW);
166   boxYellow.setPosition(390, 104);
167   boxYellow.setSize(18, 16);
168   osd.add(&boxYellow);
169
170   boxBlue.setBackgroundColour(DrawStyle::BLUE);
171   boxBlue.setPosition(560, 104);
172   boxBlue.setSize(18, 16);
173   osd.add(&boxBlue);  
174   
175   textRed.setBackgroundColour(osdBack);
176   textRed.setPosition(boxRed.getX2(), 98);
177   textRed.setSize(boxGreen.getX() - boxRed.getX2(), 30);
178   textRed.setText(tr("Summary"));
179   osd.add(&textRed);  
180     
181   if (streamType == VDR::VIDEO)
182   {
183     textGreen.setBackgroundColour(osdBack);
184     textGreen.setPosition(boxGreen.getX2(), 98);
185     textGreen.setSize(boxYellow.getX() - boxGreen.getX2(), 30);
186     textGreen.setText(tr("Audio"));
187     osd.add(&textGreen);  
188   }
189     
190   textYellow.setBackgroundColour(osdBack);
191   textYellow.setPosition(boxYellow.getX2(), 98);
192   textYellow.setSize(boxBlue.getX() - boxYellow.getX2(), 30);
193   textYellow.setText(tr("Teletext"));
194   osd.add(&textYellow);  
195     
196   textBlue.setBackgroundColour(osdBack);
197   textBlue.setPosition(boxBlue.getX2(), 98);
198   textBlue.setSize(osd.getX2() - boxBlue.getX2(), 30);
199   textBlue.setText(tr("EPG"));
200   osd.add(&textBlue);  
201     
202   sl.setBackgroundColour(osdBack);
203   sl.setPosition(70, 36);
204   sl.setSize(500, 58);
205   sl.setNoLoop();
206   osd.add(&sl);
207   
208   // Summary Box
209   summary.setBackgroundColour(osdBack);
210   summary.setPosition(0, video->getScreenHeight() - 300);
211   summary.setSize(video->getScreenWidth(), 150);
212   summary.setVisible(false);
213   add(&summary);  
214   
215   textSummary.setBackgroundColour(osdBack);
216   textSummary.setPosition(40, 10);
217   textSummary.setSize(video->getScreenWidth() - 80, 130);
218   textSummary.setParaMode(true);
219   summary.add(&textSummary);
220   
221   summaryBlackLine.setBackgroundColour(DrawStyle::BLACK);
222   summaryBlackLine.setPosition(0, summary.getHeight() - 4);
223   summaryBlackLine.setSize(summary.getWidth(), 4);
224   summary.add(&summaryBlackLine);
225   
226   sAspectRatio.setPosition(osd.getWidth() - 90, 40);
227   sAspectRatio.nextColour = DrawStyle::SELECTHIGHLIGHT;
228   sAspectRatio.setVisible(false);
229   osd.add(&sAspectRatio);
230   
231   bufferBar.setPosition(osd.getWidth() - 90, 70);
232   bufferBar.setSize(40, 20);
233   bufferBar.setVisible(true);
234   osd.add(&bufferBar);
235   
236   sAudioChannels.setPosition(osd.getWidth() - 130, 40);
237   sAudioChannels.nextColour = DrawStyle::SELECTHIGHLIGHT;
238   sAudioChannels.setVisible(false);
239   osd.add(&sAudioChannels);
240   
241   textUnavailable.setBackgroundColour(osdBack);
242   textUnavailable.setPosition(60, 30);
243   textUnavailable.setSize(osd.getWidth() - 120, 30);
244   textUnavailable.setText(tr("Channel Unavailable"));
245   textUnavailable.setVisible(false);
246   add(&textUnavailable);
247   
248   // FIXME painful
249   Region r1 = summary.getRegionR();
250   Region r2 = osd.getRegionR();
251   osdSummaryRegion = r1 + r2;
252 }
253
254 void VVideoLiveTV::preDelete()
255 {
256   if (playing) stop();
257 }
258
259 VVideoLiveTV::~VVideoLiveTV()
260 {
261   delete player;
262   video->setDefaultAspect();
263   delData();
264 }
265
266 void VVideoLiveTV::delData()
267 {
268   if (eventList)
269   {
270     int eventListSize = eventList->size();
271     for(int i = 0; i < eventListSize; i++)
272     {
273       delete (*eventList)[i];
274     }
275     eventList->clear();
276     delete eventList;
277
278   }
279   sl.clear();
280 }
281
282 int VVideoLiveTV::handleCommand(int command)
283 {
284   switch(command)
285   {
286     case Remote::BACK:
287     {
288       if (osd.getVisible() && !textUnavailable.getVisible())
289       {
290         clearScreen();
291         return 2;
292       }
293       // else drop through to stop
294     }
295     case Remote::STOP:
296     {
297       stop();
298       vchannelList->highlightChannel((*chanList)[currentChannelIndex]);
299       return 4;
300     }
301     
302     // NEW REMOTE ONLY - navigate EPG, bring it onscreen if it's not there
303     case Remote::UP:
304     {
305         if (Remote::getInstance()->mayHaveFewButtons()) {
306               if (okTriggeredOSD) doUpDown(false);
307               else doChanUpDown(UP);
308         } else {
309                 doUpDown(false);
310         }
311       return 2;
312     }
313     case Remote::DOWN:
314     {
315       if (Remote::getInstance()->mayHaveFewButtons()) {
316           if (okTriggeredOSD) doUpDown(true);
317           else doChanUpDown(DOWN);
318       } else {
319           doUpDown(true);
320       }
321       return 2;
322     }
323     case Remote::LEFT:
324     {
325       doLeftRight(false);
326       return 2;
327     }
328     case Remote::RIGHT:
329     {
330       doLeftRight(true);
331       return 2;
332     }
333     // Continue new remote only...
334     case Remote::CHANNELUP:
335     {
336       doChanUpDown(UP);
337       return 2;
338     }
339     case Remote::CHANNELDOWN:
340     {
341       doChanUpDown(DOWN);
342       return 2;
343     }
344
345     // END NEW REMOTE ONLY, START OLD REMOTE ONLY
346     
347     // DF_LEFT and DF_RIGHT never get here because they are stolen
348     // by command as vol- and vol+
349     
350     // Old remote. Decide what to do based on whether
351     // OK was pressed - osd shown manually, use up/down for epg nav
352     // UP/DOWN was pressed to change channel, osd was shown auto, use up/down for ch+/ch-
353     
354     case Remote::DF_UP:
355     {
356       // Old remote, decide what to do based on okTriggeredOSD
357       if (okTriggeredOSD) doUpDown(false);
358       else doChanUpDown(UP);
359       return 2;
360     }
361     case Remote::DF_DOWN:
362     {
363       // Old remote, decide what to do based on okTriggeredOSD
364       if (okTriggeredOSD) doUpDown(true);
365       else doChanUpDown(DOWN);
366       return 2;
367     }
368
369     // END NEW/OLD REMOTE STUFF
370
371     case Remote::PREVCHANNEL:
372     {
373       channelChange(PREVIOUS, 0);
374       osdChannelIndex = currentChannelIndex;
375       displayOSD(true);
376       return 2;
377     }
378     case Remote::OK:
379     {
380       doOK();
381       return 2;
382     }
383     case Remote::RED:
384     case Remote::MENU:
385     {
386       doSummary();
387       return 2;
388     }
389     case Remote::FULL:
390     case Remote::TV:
391     {
392       toggleChopSides();
393       return 2;
394     }
395
396     case Remote::ZERO:
397     case Remote::ONE:
398     case Remote::TWO:
399     case Remote::THREE:
400     case Remote::FOUR:
401     case Remote::FIVE:
402     case Remote::SIX:
403     case Remote::SEVEN:
404     case Remote::EIGHT:
405     case Remote::NINE:
406     {
407       // key in channel number
408       doKey(command);
409       return 2;
410     }
411
412     case Remote::GREEN:
413     {
414       if (streamType == VDR::VIDEO) doAudioSelector();
415       return 2;   
416     }
417     case Remote::YELLOW:
418     {
419       if (streamType ==VDR::VIDEO) 
420       {
421         doTeletext(); //TODO: Add a selector for subtitles or teletext
422       }
423       return 2;
424     }
425     case Remote::GUIDE:
426     case Remote::BLUE:
427     {
428       doEPG();
429       return 2;
430     }
431     case Remote::RECORD:
432       if (streamType == VDR::VIDEO)
433         (static_cast<PlayerLiveTV*>(player))->toggleSubtitles();
434       return 2;
435   }
436
437   return 1;
438 }
439
440 void VVideoLiveTV::go()
441 {
442   playing = true;
443   draw();
444   boxstack->update(this);
445
446   setClock();
447   displayOSD(true);
448   
449   player->go(currentChannelIndex);
450 }
451
452 void VVideoLiveTV::stop()
453 {
454   Timers::getInstance()->cancelTimer(this, 1);
455   Timers::getInstance()->cancelTimer(this, 2);
456   player->stop();
457   playing = false;
458 }
459
460 void VVideoLiveTV::doLeftRight(bool right)
461 {
462   if (osd.getVisible())
463   {
464     if (right) osdChannelIndex = upChannel(osdChannelIndex);
465     else       osdChannelIndex = downChannel(osdChannelIndex);
466   }
467   else
468   {
469     osdChannelIndex = currentChannelIndex;
470   }
471   displayOSD(true);
472 }
473
474 void VVideoLiveTV::doUpDown(bool down)
475 {
476   if (osd.getVisible())
477   {
478     if (down) sl.down();
479     else      sl.up();
480     sl.draw();
481     
482     displayOSD(false);
483   }
484   else
485   {
486     displayOSD(true);
487   }
488 }
489
490 void VVideoLiveTV::doChanUpDown(int which)
491 {
492   channelChange(OFFSET, which);
493   osdChannelIndex = currentChannelIndex;
494   displayOSD(true);
495 }
496
497 void VVideoLiveTV::doOK()
498 {
499   if (osd.getVisible())
500   {
501     if (keying)
502     {
503       UINT newChannel = 0;
504       for(int i = keying - 1; i >= 0; i--) newChannel += keyingInput[i] * (ULONG)pow(10., i);
505       
506       channelChange(NUMBER, newChannel);
507       osdChannelIndex = currentChannelIndex;
508       displayOSD(true);
509     }
510     else if (osdChannelIndex == currentChannelIndex)
511     {
512       clearScreen();
513     }
514     else
515     {
516       channelChange(INDEX, osdChannelIndex);
517       displayOSD(true);
518     }
519   }
520   else
521   {
522     osdChannelIndex = currentChannelIndex;
523     displayOSD(true);
524     okTriggeredOSD = true;
525   }
526 }
527
528 void VVideoLiveTV::doSummary()
529 {
530         displayOSD(false);
531         Channel* currentChannel = (*chanList)[osdChannelIndex];
532         eventList = VDR::getInstance()->getChannelSchedule(currentChannel->number);
533         if (!eventList)
534         {
535                 return;
536         }
537         sort(eventList->begin(), eventList->end(), EventSorter());
538
539         if (eventList->size() < 1) return;
540         Event* event = (*eventList)[0];
541         event->loadinfos(currentChannel->number);
542
543         VEpgSummary* vr = new VEpgSummary(event,currentChannel);
544         vr->draw();
545         boxstack->add(vr);
546         boxstack->update(vr);
547 /*
548   if (summary.getVisible())
549   {
550     summary.setVisible(false);
551     draw();
552     boxstack->update(this, summary.getRegion());
553     Timers::getInstance()->setTimerD(this, 1, 8); // Restart a timer to get rid of osd
554     return;
555   }
556
557   summary.setVisible(true);
558
559   if (osd.getVisible())
560   {
561     Timers::getInstance()->cancelTimer(this, 1);
562     displayOSD(false);
563   }
564   else
565   {
566     displayOSD(true);
567   }*/
568 }
569
570 void VVideoLiveTV::doKey(int command)
571 {
572   if (!osd.getVisible()) // First key. prep the data
573   {
574     setNowNextData();
575     updatePosterBanner();
576     keying = 0;    
577   }
578
579   int i;
580   for (i = keying - 1; i >= 0; i--) keyingInput[i+1] = keyingInput[i];
581   keyingInput[0] = command;
582   keying++;
583
584   char* keyingString = new char[numberWidth + 1];
585   for (i = 0; i < numberWidth; i++) keyingString[i] = '_';
586   keyingString[numberWidth] = '\0';
587
588   for (i = 0; i < keying; i++) keyingString[i] = keyingInput[keying - 1 - i] + 48;
589   
590   if (keying == numberWidth)
591   {
592     UINT newChannel = 0;
593     for(i = keying - 1; i >= 0; i--) newChannel += keyingInput[i] * (ULONG)pow(10., i);
594     
595     channelChange(NUMBER, newChannel);
596     osdChannelIndex = currentChannelIndex;
597     Timers::getInstance()->cancelTimer(this, 1); // cancel the timer to react to keying input,
598     displayOSD(true); // this will put one back if required
599   }
600   else
601   {
602     osdChanNum.setText(keyingString);
603
604     if (!osd.getVisible())
605     {
606       osd.setVisible(true);
607       draw();
608     }
609     else
610     {
611       osdChanNum.draw();
612     }
613     boxstack->update(this, osd.getRegion());
614     Timers::getInstance()->setTimerD(this, 1, 3);  // 3s for keying input
615   }
616   delete[] keyingString;
617 }
618
619 void VVideoLiveTV::doTeletext(bool subtitlemode)
620 {
621   if (streamType !=VDR::VIDEO) return;
622   (static_cast<PlayerLiveTV*>(player))->tellSubtitlesOSDVisible(true);
623   bool exists=true;
624
625   // Cancel keying
626   if (keying)
627   {
628     keying = 0;
629     // and reset the display - this is a copy from setNowNextData
630     char formatChanNum[20];
631     SNPRINTF(formatChanNum, 19, "%0*lu", numberWidth, (*chanList)[osdChannelIndex]->number);
632     osdChanNum.setText(formatChanNum);
633     osdChanName.setText((*chanList)[osdChannelIndex]->name);
634   }
635   (static_cast<PlayerLiveTV*>(player))->tellSubtitlesOSDVisible(true);
636   if (osd.getVisible()) clearScreen();
637   // Draw the teletxt
638   VTeletextView *vtxv=((PlayerLiveTV*)player)->getTeletextDecoder()->getTeletxtView();
639   if (vtxv==NULL) {
640        vtxv= new VTeletextView(((PlayerLiveTV*)player)->getTeletextDecoder(),this, (PlayerLiveTV*)player);
641       ((PlayerLiveTV*)player)->getTeletextDecoder()->registerTeletextView(vtxv);
642       exists=false;
643   }
644   vtxv->setSubtitleMode(subtitlemode);
645   vtxv->draw();
646   draw();
647   
648   if (!exists) {
649       BoxStack::getInstance()->add(vtxv);
650   }
651   BoxStack::getInstance()->update(this);
652   BoxStack::getInstance()->update(vtxv); 
653 }
654
655 void VVideoLiveTV::doAudioSelector()
656 {
657   // If the osd is already visisble there might be a timer for it
658   Timers::getInstance()->cancelTimer(this, 1);
659   //This causes a deadlock with the timertrhread itself is locked
660
661
662   // Cancel keying
663   if (keying)
664   {
665     keying = 0;
666     // and reset the display - this is a copy from setNowNextData
667     char formatChanNum[20];
668     SNPRINTF(formatChanNum, 19, "%0*lu", numberWidth, (*chanList)[osdChannelIndex]->number);
669     osdChanNum.setText(formatChanNum);
670     osdChanName.setText((*chanList)[osdChannelIndex]->name);
671   }
672   int subtitleChannel=((PlayerLiveTV*)player)->getCurrentSubtitleChannel();
673   int subtitleType=0x10;
674   if (!(static_cast<PlayerLiveTV*>(player))->isSubtitlesOn()) {
675       if (((PlayerLiveTV*)player)->getTeletextDecoder()->getTeletxtView() &&
676           ((PlayerLiveTV*)player)->getTeletextDecoder()->getTeletxtView()->isInSubtitleMode() 
677           ) {
678               subtitleChannel=((PlayerLiveTV*)player)->getTeletextDecoder()->getPage();
679               subtitleType=0x11;
680           
681       } else {
682           subtitleType=0xFF; //turnedOff
683           subtitleChannel=0;
684       }
685   }
686
687   // Draw the selector
688   vas = new VAudioSelector(this, (*chanList)[currentChannelIndex], ((PlayerLiveTV*)player)->getCurrentAudioChannel(),
689       subtitleType,subtitleChannel,NULL);
690
691   vas->setBackgroundColour(osdBack);
692   vas->setPosition(0, osd.getScreenY() - vas->getHeight());
693   vas->draw();
694
695   // make vas != null and displayOSD will not set a timer or do any boxstack update
696   summary.setVisible(false);
697   if (osd.getVisible()) displayOSD(false);
698   else displayOSD(true);
699   draw();
700
701   BoxStack::getInstance()->add(vas);
702   BoxStack::getInstance()->update(this);        
703   BoxStack::getInstance()->update(vas);     
704 }      
705       
706 void VVideoLiveTV::doEPG()
707 {
708   if (osd.getVisible()) clearScreen();
709
710   video->setMode(Video::QUARTER);
711   video->setPosition(170, 5); //TODO need to deal with 4:3 switching
712
713   VEpg* vepg = new VEpg(this, currentChannelIndex, streamType);
714   vepg->draw();
715   boxstack->add(vepg);
716   boxstack->update(vepg);
717 }
718
719 void VVideoLiveTV::setNowNextData()
720 {
721   delData();
722   
723   Channel* currentChannel = (*chanList)[osdChannelIndex];
724
725   char formatChanNum[20];
726   SNPRINTF(formatChanNum, 19, "%0*lu", numberWidth, currentChannel->number);
727   osdChanNum.setText(formatChanNum);
728   osdChanName.setText(currentChannel->name);
729
730   eventList = VDR::getInstance()->getChannelSchedule(currentChannel->number);
731
732   if (!eventList)
733   {
734     sl.addOption(tr("No channel data available"), 0, 1);
735   }
736   else
737   {
738     sort(eventList->begin(), eventList->end(), EventSorter());
739
740     char tempString[300];
741     char tempString2[300];
742     struct tm* btime;
743     Event* event;
744     int eventListSize = eventList->size();
745     for(int i = 0; i < eventListSize; i++)
746     {
747       event = (*eventList)[i];
748
749       //btime = localtime((time_t*)&event->time);
750       time_t etime = event->time;
751       btime = localtime(&etime);
752 #ifndef _MSC_VER
753       strftime(tempString2, 299, "%0H:%0M ", btime);
754 #else
755       strftime(tempString2, 299, "%H:%M ", btime);
756 #endif
757       SNPRINTF(tempString, 299, "%s %s", tempString2, event->title);
758       
759       sl.addOption(tempString, (ULONG)event, (i==0));
760     }
761   }
762 }
763
764 void VVideoLiveTV::setSummaryData()
765 {
766   // If osd is not being displayed, sl will be filled with now, current channel
767   // If the display was already on, sl will have programme to show summary for, not necessarily current channel and now
768   Event* selectedEvent = (Event*)sl.getCurrentOptionData();
769   
770   if (!selectedEvent)
771   {
772     Log::getInstance()->log("VVideoLiveTV", Log::DEBUG, "No summary");  
773     textSummary.setText(tr("No summary available"));
774   }
775   else
776   {
777     Log::getInstance()->log("VVideoLiveTV", Log::DEBUG, "Summary: %s", selectedEvent->description);  
778     textSummary.setText(selectedEvent->description);
779   }
780 }
781
782 void VVideoLiveTV::displayOSD(bool newNowNextData)
783 {
784   (static_cast<PlayerLiveTV*>(player))->tellSubtitlesOSDVisible(true);
785   osd.setVisible(true);
786   if (newNowNextData)
787   {
788     setNowNextData();
789     updatePosterBanner();
790     keying = 0;
791   }
792   osd.draw();
793   if (osdposterbanner.getVisible()) osdposterbanner.draw();
794   
795
796   Region  toupdate;
797   if (summary.getVisible())
798   {
799     setSummaryData();
800     summary.draw();
801     toupdate=osdSummaryRegion;
802   }
803   else
804   {
805         toupdate=*osd.getRegion();
806   }
807
808   if (osdposterbanner.getVisible()) {
809           boxstack->update(this);
810   } else {
811           boxstack->update(this, &toupdate);
812   }
813   
814   bool setTimer = true;
815   if (vas) setTimer = false;
816   if (summary.getVisible()) setTimer = false;
817   if (textUnavailable.getVisible()) setTimer = false;
818
819   if (setTimer) Timers::getInstance()->setTimerD(this, 1, 4);
820 }
821
822 void VVideoLiveTV::clearScreen()
823 {  
824   if (!summary.getVisible()) Timers::getInstance()->cancelTimer(this, 1);
825
826   textUnavailable.setVisible(false);
827   osd.setVisible(false);
828   summary.setVisible(false);
829   osdposterbanner.setVisible(false);
830
831   okTriggeredOSD = false;
832
833   draw();
834   boxstack->update(this);
835
836   (static_cast<PlayerLiveTV*>(player))->tellSubtitlesOSDVisible(false);
837 }
838
839 void VVideoLiveTV::showUnavailable()
840 {
841   Log::getInstance()->log("VVideoLiveTV", Log::DEBUG, "Show unavailable called");  
842   textUnavailable.setVisible(true);
843   textUnavailable.draw();
844   
845   if (!osd.getVisible()) displayOSD(true);
846
847   boxstack->update(this, textUnavailable.getRegion());  
848 }
849
850 void VVideoLiveTV::setClock()
851 {
852   char timeString[20];
853   time_t t;
854   time(&t);
855   struct tm* tms = localtime(&t);
856   strftime(timeString, 19, "%H:%M", tms);
857   clock.setText(timeString);
858
859   time_t dt = 60 - (t % 60);  // seconds to the next minute
860   if (dt == 0) dt = 60; // advance a whole minute if necessary
861   dt += t;  // get a time_t value for it rather than using duration
862   // (so it will occur at the actual second and not second and a half)
863
864   Timers::getInstance()->setTimerT(this, 2, dt);
865 }
866
867 void VVideoLiveTV::timercall(int ref)
868 {
869   if (ref == 1)
870   {
871     if (keying)
872     {
873          Log::getInstance()->log("VVideoLiveTV", Log::DEBUG, "Timer Call 1 key start."); 
874       UINT newChannel = 0;
875       for(int i = keying - 1; i >= 0; i--) newChannel += keyingInput[i] * (ULONG)pow(10., i);
876       
877       Message* m = new Message();
878       m->message = Message::CHANNEL_CHANGE;
879       m->to = this;
880       m->parameter = newChannel;
881       m->tag = 1; // signal to call displayOSD();
882       Command::getInstance()->postMessageFromOuterSpace(m);
883       Log::getInstance()->log("VVideoLiveTV", Log::DEBUG, "Timer Call 1 key end."); 
884     }
885     else
886     {
887         Log::getInstance()->log("VVideoLiveTV", Log::DEBUG, "Timer Call 1 not key start."); 
888       // We have received a timer, we are not keying. If still prebuffering, don't remove the bar
889       if (preBuffering < 100)
890       {
891         Log::getInstance()->log("VVideoLiveTV", Log::DEBUG, "Still prebuffering, not removing osd...");  
892         Timers::getInstance()->setTimerD(this, 1, 2); // reset timer for another 2s
893         return;
894       }
895       bool osdpbvisible=osdposterbanner.getVisible();
896       Log::getInstance()->log("VVideoLiveTV", Log::DEBUG, "Timer Call 1 notkey 1."); 
897       osd.setVisible(false);
898       osdposterbanner.setVisible(false);
899       okTriggeredOSD = false;
900       Log::getInstance()->log("VVideoLiveTV", Log::DEBUG, "Timer Call 1 notkey 2."); 
901       draw();
902       Log::getInstance()->log("VVideoLiveTV", Log::DEBUG, "Timer Call 1 notkey 4."); 
903       if (osdpbvisible) boxstack->update(this);
904       else  boxstack->update(this, osd.getRegion());
905
906       Log::getInstance()->log("VVideoLiveTV", Log::DEBUG, "Timer Call 1 notkey 3."); 
907       (static_cast<PlayerLiveTV*>(player))->tellSubtitlesOSDVisible(false);
908       Log::getInstance()->log("VVideoLiveTV", Log::DEBUG, "Timer Call 1 notkey end."); 
909     }
910   }
911   else if (ref == 2)
912   {
913       Log::getInstance()->log("VVideoLiveTV", Log::DEBUG, "Timer Call 2  start."); 
914     setClock();
915     if (osd.getVisible())
916     {
917       clock.draw();
918       boxstack->update(this, osd.getRegion());
919     }
920     Log::getInstance()->log("VVideoLiveTV", Log::DEBUG, "Timer Call 2 end."); 
921   }
922 }
923
924 bool VVideoLiveTV::channelChange(UCHAR changeType, UINT newData)
925 {
926   UINT newChannel = 0;
927   if (streamType ==VDR::VIDEO) {
928   VTeletextView *vtxt=((PlayerLiveTV*)player)->getTeletextDecoder()->getTeletxtView();
929     if (vtxt ) {
930          BoxStack::getInstance()->remove(vtxt);
931
932     }
933   }
934   if (changeType == INDEX)
935   {
936     newChannel = newData;
937   }
938   else if (changeType == NUMBER)
939   {
940     UINT i;
941     for(i = 0; i < chanList->size(); i++)
942     {
943       if ((*chanList)[i]->number == (UINT)newData)
944       {
945         newChannel = i;
946         break;
947       }
948     }
949
950     if (i == chanList->size())
951     {
952       // no such channel
953       return false;
954     }
955   }
956   else if (changeType == OFFSET)
957   {
958     if (newData == UP) newChannel = upChannel(currentChannelIndex);
959     else newChannel = downChannel(currentChannelIndex);
960   }
961   else if (changeType == PREVIOUS)
962   {
963     newChannel = previousChannelIndex;
964   }
965   else
966   {
967     return false; // bad input
968   }
969
970   if (newChannel == currentChannelIndex) return true;
971
972   previousChannelIndex = currentChannelIndex;
973   currentChannelIndex = newChannel;
974   
975   preBuffering = 0;
976   
977   Log::getInstance()->log("VVideoLiveTV", Log::DEBUG, "Set player to channel %u", currentChannelIndex);
978   player->setChannel(currentChannelIndex);
979   Log::getInstance()->log("VVideoLiveTV", Log::DEBUG, "Done Set player to channel %u", currentChannelIndex);
980
981   // Blank out the symbols
982   sAspectRatio.setVisible(false);
983   bufferBar.setPercent(0);
984   sAudioChannels.setVisible(false);
985   
986   // Remove other stuff
987   if (textUnavailable.getVisible())
988   {
989     textUnavailable.setVisible(false);
990     
991   }
992
993     draw();
994     BoxStack::getInstance()->update(this);
995   
996   return true;
997 }
998
999 void VVideoLiveTV::processMessage(Message* m)
1000 {
1001   if (m->message == Message::MOUSE_LBDOWN)
1002   {
1003     //check if press is outside this view! then simulate cancel
1004     int x=(m->parameter>>16)-osd.getScreenX();
1005     int y=(m->parameter&0xFFFF)-osd.getScreenY();
1006     if (osd.getVisible()) {
1007     
1008         if ((boxRed.getX()<=x) && (boxRed.getX()+(int)boxRed.getWidth()>=x ) &&
1009             (boxRed.getY()<=y) && (boxRed.getY()+(int)boxRed.getHeight()>=y )) {
1010             BoxStack::getInstance()->handleCommand(Remote::RED);
1011         } else if ((boxGreen.getX()<=x) && (boxGreen.getX()+(int)boxGreen.getWidth()>=x ) &&
1012             (boxGreen.getY()<=y) && (boxGreen.getY()+(int)boxGreen.getHeight()>=y)){
1013             BoxStack::getInstance()->handleCommand(Remote::GREEN);
1014         } else if ((boxYellow.getX()<=x) && (boxYellow.getX()+(int)boxYellow.getWidth()>=x ) &&
1015             (boxYellow.getY()<=y) && (boxYellow.getY()+(int)boxYellow.getHeight()>=y )){
1016             BoxStack::getInstance()->handleCommand(Remote::YELLOW);
1017         } else if ((boxBlue.getX()<=x) && (boxBlue.getX()+(int)boxBlue.getWidth()>=x ) &&
1018             (boxBlue.getY()<=y) && (boxBlue.getY()+(int)boxBlue.getHeight()>=y )){
1019             BoxStack::getInstance()->handleCommand(Remote::BLUE);
1020         } else {
1021             BoxStack::getInstance()->handleCommand(Remote::OK); //simulate rok press
1022         }
1023
1024     } else {
1025         BoxStack::getInstance()->handleCommand(Remote::OK); //simulate rok press
1026     }
1027   }
1028   else if (m->message == Message::CHANNEL_CHANGE)
1029   {
1030     channelChange(NUMBER, m->parameter);
1031     osdChannelIndex = currentChannelIndex;
1032     if (m->tag == 1) displayOSD(true);
1033   }
1034   else if (m->message == Message::EPG_CLOSE)
1035   {
1036     video->setMode(videoMode);
1037   }
1038   else if (m->message == Message::CHILD_CLOSE)
1039   {
1040     if (m->from == vas)
1041     {
1042       vas = NULL;
1043       displayOSD(false);
1044     }
1045   }
1046   else if (m->message == Message::AUDIO_CHANGE_CHANNEL)
1047   {
1048     Log::getInstance()->log("VVideoLiveTV", Log::DEBUG, "Received change audio channel to %x", m->parameter);
1049     player->setAudioChannel((m->parameter & 0xFFFF),(m->parameter & 0xFF0000)>>16,(m->parameter & 0xFF000000)>>24);
1050   } 
1051   else if (m->message == Message::SUBTITLE_CHANGE_CHANNEL)
1052   {
1053     if (streamType !=VDR::VIDEO) return;
1054       Log::getInstance()->log("VVideoLiveTV", Log::DEBUG, "Received change subtitle channel to %x", m->parameter);
1055       int type=((m->parameter & 0xFF0000)>>16);
1056       switch (type) {
1057       case 0x10: { //dvbsubtitle
1058           if (streamType == VDR::VIDEO){
1059               player->setSubtitleChannel((m->parameter & 0xFFFF));
1060               (static_cast<PlayerLiveTV*>(player))->turnSubtitlesOn(true);
1061               VTeletextView *vtxt=((PlayerLiveTV*)player)->getTeletextDecoder()->getTeletxtView();
1062               if (vtxt && vtxt->isInSubtitleMode()) {
1063                   BoxStack::getInstance()->remove(vtxt);
1064               }
1065           }
1066                  } break;
1067       case 0xFF: { //nosubtitles
1068           if (streamType == VDR::VIDEO){
1069               (static_cast<PlayerLiveTV*>(player))->turnSubtitlesOn(false);
1070               VTeletextView *vtxt=((PlayerLiveTV*)player)->getTeletextDecoder()->getTeletxtView();
1071               if (vtxt && vtxt->isInSubtitleMode()) {
1072                   BoxStack::getInstance()->remove(vtxt);
1073               }  
1074           }
1075                  } break;
1076       case 0x11: { //videotext
1077           (static_cast<PlayerLiveTV*>(player))->turnSubtitlesOn(false);
1078           doTeletext(true);
1079           ((PlayerLiveTV*)player)->getTeletextDecoder()->setPage((m->parameter & 0xFFFF));
1080                  } break;
1081       };
1082       if (vas) {
1083         BoxStack::getInstance()->update(vas);
1084         //BoxStack::getInstance()->update(&osd); //eveil error
1085       }
1086       BoxStack::getInstance()->update(this, osd.getRegion());
1087
1088       
1089   }
1090   else if (m->message == Message::PLAYER_EVENT)
1091   {
1092     switch(m->parameter)
1093     {
1094       case PlayerLiveTV::CONNECTION_LOST: // connection lost detected
1095       {
1096         Log::getInstance()->log("VVideoLiveTV", Log::DEBUG, "Received connection lost from player");
1097         Command::getInstance()->connectionLost();
1098         break;
1099       }
1100       
1101       case PlayerLiveTV::STREAM_END:
1102       {
1103         // Message comes from playerlivetv through master mutex, so can do anything here
1104         showUnavailable();        
1105         break;
1106       }
1107       
1108       case PlayerLiveTV::ASPECT43:
1109       {
1110 #ifdef PAL_WSS
1111         if ((video->getTVsize() == Video::ASPECT16X9) && dowss)
1112         {
1113           Log::getInstance()->log("VVideoRec", Log::DEBUG, "Received do WSS 43");
1114           wss.setWide(false);
1115           wss.draw();
1116           BoxStack::getInstance()->update(this, &wssRegion);
1117         }
1118 #endif
1119         
1120         sAspectRatio.nextSymbol = WSymbol::VIDEOASPECT43;
1121         sAspectRatio.setVisible(true);
1122         
1123         if (osd.getVisible()) // don't wake up the whole osd just for a aspect change
1124         {
1125           osd.draw();
1126           BoxStack::getInstance()->update(this, osd.getRegion());
1127         }
1128         
1129         break;
1130       }
1131       case PlayerLiveTV::ASPECT169:
1132       {
1133 #ifdef PAL_WSS
1134         if ((video->getTVsize() == Video::ASPECT16X9) && dowss)
1135         {
1136           Log::getInstance()->log("VVideoRec", Log::DEBUG, "Received do WSS 169");
1137           wss.setWide(true);
1138           wss.draw();
1139           BoxStack::getInstance()->update(this, &wssRegion);
1140         }
1141 #endif
1142         
1143         sAspectRatio.nextSymbol = WSymbol::VIDEOASPECT169;
1144         sAspectRatio.setVisible(true);
1145
1146         if (osd.getVisible()) // don't wake up the whole osd just for a aspect change
1147         {
1148           osd.draw();
1149           BoxStack::getInstance()->update(this, osd.getRegion());
1150         }
1151                 
1152         break;
1153       }
1154       case PlayerLiveTV::PREBUFFERING:
1155       {
1156         preBuffering = m->tag;
1157         Log::getInstance()->log("VVideoRec", Log::DEBUG, "Prebuffering - %u", preBuffering);
1158         bufferBar.setPercent(preBuffering);
1159
1160         if (osd.getVisible())
1161         {
1162           bufferBar.setVisible(true);
1163           bufferBar.draw();
1164           Region r;
1165           bufferBar.getRootBoxRegion(&r);                       ///////// FIXME !!!
1166           BoxStack::getInstance()->update(this, &r);
1167
1168           if (preBuffering == 100)
1169           {
1170             doAudioChannelSymbol();
1171           }
1172         }
1173       }
1174     }
1175   }
1176 }
1177
1178 void VVideoLiveTV::doAudioChannelSymbol()
1179 {
1180   // get the doobery
1181   Channel* currentChannel = (*chanList)[osdChannelIndex];
1182     
1183   bool multiAudio = false;
1184   if (Audio::getInstance()->supportsAc3()) {
1185           if ((currentChannel->numDPids+currentChannel->numAPids) > 1) multiAudio = true;
1186   }
1187   if (currentChannel->numAPids > 1) multiAudio = true;
1188   
1189   // draw the doobery
1190   
1191   if (multiAudio) sAudioChannels.nextSymbol = WSymbol::MULTIAUDIO;
1192   else            sAudioChannels.nextSymbol = WSymbol::SINGLEAUDIO;
1193   sAudioChannels.setVisible(true);
1194   
1195   if (osd.getVisible())
1196   {
1197     sAudioChannels.draw();
1198     Region r;
1199     sAudioChannels.getRootBoxRegion(&r);                       ///////// FIXME !!!
1200     // Fix this n'all.
1201     r.w = 32;
1202     r.h = 16;
1203     BoxStack::getInstance()->update(this, &r);
1204   }
1205 }
1206
1207 UINT VVideoLiveTV::upChannel(UINT index)
1208 {
1209   if (index == (chanList->size() - 1)) // at the end
1210     return 0; // so go to start
1211   else
1212     return index + 1;
1213 }
1214
1215 UINT VVideoLiveTV::downChannel(UINT index)
1216 {
1217   if (index == 0) // at the start
1218     return chanList->size() - 1; // so go to end
1219   else
1220     return index - 1;
1221 }
1222
1223 void VVideoLiveTV::toggleChopSides()
1224 {
1225   if (video->getTVsize() == Video::ASPECT16X9) return; // Means nothing for 16:9 TVs
1226
1227   if (videoMode == Video::NORMAL)
1228   {
1229     videoMode = Video::LETTERBOX;
1230     video->setMode(Video::LETTERBOX);
1231   }
1232   else
1233   {
1234     videoMode = Video::NORMAL;
1235     video->setMode(Video::NORMAL);
1236   }
1237 }
1238
1239 void VVideoLiveTV::updatePosterBanner()
1240 {
1241         OsdVector *osdv=dynamic_cast<OsdVector*>(Osd::getInstance());
1242         if (!osdv) {
1243                 osdposterbanner.setVisible(false);
1244                 return;
1245         }
1246         Event* toShow = NULL;
1247         if (eventList && eventList->size()) {
1248                 toShow = (*eventList)[0];
1249                 Channel* currentChannel = (*chanList)[osdChannelIndex];
1250                 toShow->loadinfos(currentChannel->number);
1251         }
1252
1253         if (toShow)
1254         {
1255                 TVMedia poster;
1256                 poster.height=0;
1257                 bool posterscale=true;
1258                 if (toShow->movieInfo) {
1259                         poster=toShow->movieInfo->poster;
1260                 }
1261                 if (toShow->seriesInfo) {
1262                         if (toShow->seriesInfo->banners.size()) {
1263                                 poster=toShow->seriesInfo->banners[0];
1264                                 posterscale=false;
1265                         } else if (toShow->seriesInfo->seasonposter.height) {
1266                                 poster=toShow->seriesInfo->seasonposter;
1267                         } else if (toShow->seriesInfo->posters.size()) {
1268                                 poster=toShow->seriesInfo->posters[0];
1269                         }
1270                 }
1271                 if (poster.height) {
1272                         osdposterbanner.setTVMedia(poster.info, posterscale ? WTVMedia::ZoomVertical : WTVMedia::ZoomHorizontal);
1273                         osdposterbanner.setVisible(true);
1274                 } else {
1275                         osdposterbanner.setVisible(false);
1276                 }
1277         } else {
1278                 osdposterbanner.setVisible(false);
1279         }
1280
1281 }
1282
1283
1284 void VVideoLiveTV::drawOSDBitmap(UINT posX, UINT posY, const Bitmap& bm, const DisplayRegion& region)
1285 {
1286   drawBitmap(posX, posY, bm,region);
1287   Region r;
1288   r.x = posX; r.y = posY; r.w = bm.getWidth(); r.h = bm.getHeight();
1289   boxstack->update(this, &r);
1290 }
1291
1292 void VVideoLiveTV::clearOSD()
1293 {
1294   rectangle(area, DrawStyle(0,0,0,0));
1295   boxstack->update(this, &area);
1296 }
1297
1298 void VVideoLiveTV::clearOSDArea(UINT posX, UINT posY, UINT width, UINT height, const DisplayRegion& region)
1299 {
1300   Region r;
1301   r.x = posX+region.windowx; r.y = posY+region.windowy; r.w = width; r.h = height;
1302   //now convert to our display
1303   float scalex=720.f/((float) (region.framewidth+1));
1304   float scaley=576.f/((float) (region.frameheight+1));
1305   r.x=floor(scalex*((float)r.x));
1306   r.y=floor(scaley*((float)r.y));
1307   r.w=ceil(scalex*((float)r.w))+1.f;
1308   r.h=ceil(scaley*((float)r.h))+1.f;
1309   rectangle(r, DrawStyle(0,0,0,0));
1310   boxstack->update(this, &r);
1311 }