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