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