2 Copyright 2004-2005 Chris Tallon, Andreas Vogel
4 This file is part of VOMP.
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.
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.
16 You should have received a copy of the GNU General Public License
17 along with VOMP. If not, see <https://www.gnu.org/licenses/>.
22 #include "vmediaview.h"
24 #include "vpicturebanner.h"
25 #include "vcolourtuner.h"
26 #include "audioplayer.h"
28 #include "wselectlist.h"
39 #include "mediaoptions.h"
40 #include "mediaplayer.h"
42 #include "messagequeue.h"
44 const int VMediaView::EVENT_SLIDESHOW=100;
45 const int VMediaView::EVENT_DRAWINGDONE=101;
46 const int VMediaView::EVENT_DRAWINGERROR=102;
47 const int VMediaView::EVENT_DIRECTORYDONE=103;
51 * the combined user interface for pictures and audio
52 * has 2 surfaces to enable drawing in a separate thread
53 * the info display can either show a picture info or and audio info
54 * if the audio player comes on top it disables the picture info display
55 * basically we have 3 modes:
56 * - HIDDEN viewer is hidden (pictureEnabled=false, audioEnabled=false)
57 * if justPlaying=true the audioplayer is still running
58 * - PICTURE picture viewer on top ("slide show") - pictureEnabled=true, audioEnabled=false
59 * no AudioBanner visible
60 * if justPlaying=true the audio player runs in bg
61 * - AUDIO audioPlayer on top ("playlist mode") - pictureEnabled=true, audioEnabled=true
62 * the picture viewer is currently halted (should be able to continue if no AudioInfo)
63 * no picture banner (we should have an audio banner to indicate the audio player on top!)
64 * state transitions (via setPictureMode, setAudioMode)
65 * - show Picture -> pictureEnabled=true,
66 * only called from command handler if audioEnabled=false
67 * call from mediaList only possible if audioEnabled=false
68 * *** TODO: would be better to have separate function for external call/internal call
69 * - play [Audio] -> if activate is set -> audioEnabled=true (-> Mode AUDIO)
70 * used for calls from medialist
71 * internal calls do not set activate!
72 * - YELLOW key - toggle
73 * if audioActive==true -> audioActive=false (new mode depends on pictureEnabled - either HIDDEN or PICTURE)
74 * if audioActive==false -> audioActive=true (new mode AUDIO)
75 * *** open handling if no audio file there (ignore key???) - currently empty player
76 * - BACK if mode AUDIO -> audioEnabled=false, justPlaying=false
77 * - BACK if mode PICTURE -> pictureEnabled=false;
78 * - player StreamEnd -> audioEnabled=false (new mode depends on pciture - either HIDDEN or PICTURE)
79 * info/banner handling:
80 * - AudioInfo - alternativ to pictureInfo
81 * off when disabling audio or after timer or with OK in AUDIO mode
82 * on currently any command or player event (end/new song) when audioEnabled==true and retriggerAudioInfo=true
83 * on on OK in AUDIO mode
85 * off when disabling Picture or after timer, OK, green
86 * on only on green when pictureEnabled==true
88 * 2 modes: loading/normal
90 * off when disabling picture, OK, after timer
91 * on when enabling picture, with OK
93 * off when disabling picture and when loading done
95 * *** open: do not show when audio enabled and slide show running (currently slideshow is paused)
97 * always shown when audioEnabled=true
98 * on when enabling Audio
99 * off when disabling audio
101 * timers: 1 - slide show
104 * 4 - audio short update
107 #define DRAWING_THREAD
109 DrawStyle VMediaView::pictureBack=DrawStyle(140,140,140);
110 DrawStyle VMediaView::infoBack=DrawStyle(110,110,110);
111 DrawStyle VMediaView::audioBannerBack=DrawStyle(110,110,110,20);
114 //how long do we wait for a single picture chunk
115 //the unit is 100ms (the timeout on the server side)
117 class VPreader : public JpegReader {
124 VPreader(VMediaView *p,ImageReader *r){
130 virtual u4 readChunk(u4 offset,u4 len,char ** buf) {
131 Log::getInstance()->log("VMediaView::jpegReader", Log::DEBUG, "read chunk o=%d,len=%d,buf=%p",
135 for (int trycount=0;trycount < MAXTRY && ! dobreak && numrec == 0 && rt == 0;trycount++) {
141 rt=reader->getImageChunk((u8)offset,(u4)len,&numrec,(u1 **)buf);
144 Log::getInstance()->log("VMediaView::jpegReader", Log::DEBUG, "got n=%d,buf=%p,rt=%d",
148 virtual int initRead(const MediaURI *uri,u8 *sz,u4 factor=100) {
149 Log::getInstance()->log("VMediaView::jpegReader", Log::DEBUG, "load image %s",uri->getDisplayName());
150 Video* video = Video::getInstance();
152 int rt=MediaPlayer::getInstance()->openMedium(1,uri,sz,video->getScreenWidth()*factor/100, video->getScreenHeight()*factor/100);
155 Log::getInstance()->log("VMediaView::jpegReader", Log::DEBUG, "load image %s returned %llu",uri->getDisplayName(),size);
158 virtual u4 getSize() {return size;}
159 //seems to be thread safe (more or less...)
166 * a separate thread for drawing pictures
167 * will be started with the sfc and the draw control
168 * will send a player event when done
170 class DrawingThread : public Thread_TYPE {
174 WJpegComplex::JpegControl *_ctl;
179 DrawingThread(VMediaView *parent) {
186 virtual ~DrawingThread(){}
188 return (threadIsActive()!=0);
190 bool start(WJpegComplex::JpegControl *ctl,Surface *sfc,VPreader *reader,DrawStyle &c) {
199 return (threadStart() == 1);
202 Log::getInstance()->log("DrawingThread",Log::DEBUG,"stop initiated");
203 if (threadIsActive()) {
205 if (_reader) _reader->setBreak();
208 Log::getInstance()->log("DrawingThread",Log::DEBUG,"stop done");
213 virtual void threadMethod() {
214 Log::getInstance()->log("DrawingThread",Log::DEBUG,"started");
215 bool rt=WJpegComplex::drawJpeg(_ctl,_sfc,_reader,_colour);
217 if (! _interrupted) {
218 Message* m = new Message();
219 //we misuse PLAYER_EVENT here
220 m->message = Message::PLAYER_EVENT;
223 m->parameter = rt?VMediaView::EVENT_DRAWINGDONE:VMediaView::EVENT_DRAWINGERROR;
224 MessageQueue::getInstance()->postMessage(m);
226 Log::getInstance()->log("DrawingThread",Log::DEBUG,"finishing interrupt=%d",(int)_interrupted);
231 VMediaView::VMediaView(VMediaList *p)
233 Log::getInstance()->log("VMediaView::VMediaView", Log::DEBUG, "p=%p", this);
235 pictureEnabled=false;
237 ireader=new ImageReader(1,MediaPlayer::getInstance());
238 reader=new VPreader(this,ireader);
239 //the surface handling
240 Video* video = Video::getInstance();
241 setSize(video->getScreenWidth(), video->getScreenHeight());
245 //create the second surface
250 //disable the surface
259 pictureLoading=false;
262 showtime=INITIAL_SHOWTIME;
263 rotate=WJpegComplex::ROT_0;
265 options=MediaOptions::getInstance();
266 VColourTuner::initFactors();
267 int st=options->getIntOption("SlideShowInterval");
268 if (st > 0) showtime=st;
269 ctl.area.w=originalw;
270 ctl.area.h=originalh;
272 ctl.scaleafter=options->getIntOption("ScaleFactor");
273 const char * mode=options->getStringOption("PictureMode");
274 if (strcmp(mode,"clip") == 0) ctl.mode=WJpegComplex::CROP;
275 else if (strcmp(mode,"letter") == 0) ctl.mode=WJpegComplex::LETTER;
276 else if (strcmp(mode,"clipfactor") == 0) ctl.mode=WJpegComplex::CROPPERCENT;
277 ctl.scaleAmount=options->getIntOption("PictureSize");
278 if (ctl.scaleAmount < 10) ctl.scaleAmount=10;
279 if (ctl.scaleAmount > 200) ctl.scaleAmount=200;
282 //current control is the one used for DISPLAY (not the one for drawing - this is the other one)
283 //this is closely coupled to the surface - i.e. ctl is for sfc1, ctl2 for sfc2
286 pictureShowing=false;
290 barBlue.set(0, 0, 150, 150);
295 retriggerAudioInfo=false;
297 drawingThread=new DrawingThread(this);
298 MessageQueue::getInstance()->addReceiver(this);
301 VMediaView::~VMediaView()
303 MessageQueue::getInstance()->removeReceiver(this);
304 Log::getInstance()->log("VMediaView::~VMediaView", Log::DEBUG, "p=%p,secondSfc=%s", this,(secondSurface()?"true":"false"));
305 destroyPictureBanner();
306 if (currentPicture) delete currentPicture;
307 Timers::getInstance()->cancelTimer(this,1);
308 Timers::getInstance()->cancelTimer(this,2);
309 Timers::getInstance()->cancelTimer(this,3);
310 Timers::getInstance()->cancelTimer(this,4);
311 Timers::getInstance()->cancelTimer(this,5);
313 destroyAudioBanner();
314 if (getPlayer(false)) {
315 AudioPlayer::getInstance(NULL,false)->shutdown();
317 if (currentAudio) delete currentAudio;
318 drawingThread->stop();
322 if (secondSurface()) {
330 MediaPlayer::getInstance()->closeMediaChannel(1);
331 MediaPlayer::getInstance()->closeMediaChannel(2);
332 Log::getInstance()->log("VMediaView::~VMediaView", Log::DEBUG, "done p=%p", this);
335 void VMediaView::setPictureMode(bool act) {
336 Log::getInstance()->log("VMediaView", Log::DEBUG, "set pictureMode %d p=%d, a=%d", (int)act,(int)pictureEnabled,(int)audioEnabled);
338 if ( ! pictureEnabled && ! audioEnabled) {
344 if (! pictureEnabled) {
345 //we newly enable the picture - so clear the screen
347 BoxStack::getInstance()->update(this);
348 if (slideshow) startSlideshow();
353 if ( pictureEnabled) {
354 destroyPictureBanner();
355 stopSlideshow(false);
356 #ifdef DRAWING_THREAD
357 drawingThread->stop();
359 if (! audioEnabled) {
367 pictureShowing=false;
372 //we can disable audio without automatically hiding us
373 //this will become strange - we are visible but do not handle
374 //keys - so if you call setAudioMode(false,false) be sure
375 //to call setAudioMode(false,true) afterwards
376 //it is only here to give the list a chance to start a new play
377 //when we call directoryDone
378 void VMediaView::setAudioMode(bool act,bool doHiding) {
379 Log::getInstance()->log("VMediaView", Log::DEBUG, "setAudioMode %d p=%d, a=%d", (int)act,(int)pictureEnabled,(int)audioEnabled);
381 if (! audioEnabled) {
382 if (! pictureEnabled) {
385 draw(); //empty screen if no picture
388 destroyPictureBanner();
400 destroyAudioBanner();
402 if (! pictureEnabled && doHiding) {
410 if (havePictureBanner && ! pictureBanner) showPictureBanner();
417 void VMediaView::draw()
419 Log::getInstance()->log("VMediaView::draw", Log::DEBUG, "pictureError=%s,p=%p", pictureError,this);
421 if (pictureShowing ) fillColour(pictureBack);
422 else fillColour(DrawStyle::BLACK);
424 drawText(pictureError,100,area.h/2,DrawStyle::LIGHTTEXT);
430 int VMediaView::handleCommand(int command)
432 Log::getInstance()->log("VMediaView::handleCommand", Log::DEBUG, "cmd=%d,p=%p", command,this);
433 if ( !audioEnabled && ! pictureEnabled ) {
435 if (command == Input::YELLOW) {
437 return BoxStack::ABANDON_COMMAND;
439 return BoxStack::DROP_THROUGH;
441 if ( ! audioEnabled) {
442 //------------------------- command in mode PICTURE (i.e. picture is on top) ----------------
444 int rt = BoxStack::ABANDON_COMMAND;
448 case Input::SKIPBACK:
449 rotate=WJpegComplex::ROT_0;
450 showPicture(VMediaList::MV_PREV,slideshow,true);
451 rt= BoxStack::COMMAND_HANDLED;
454 if (showtime > 1) showtime--;
455 updatePictureBanner(true);
458 case Input::SKIPFORWARD:
459 rotate=WJpegComplex::ROT_0;
460 showPicture(VMediaList::MV_NEXT,slideshow,true);
461 rt= BoxStack::COMMAND_HANDLED;
464 if (showtime < 50 ) showtime++;
465 updatePictureBanner(true);
470 destroyPictureBanner();
472 havePictureBanner=false;
475 havePictureBanner=true;
476 showPictureBanner(pictureLoading);
478 rt= BoxStack::COMMAND_HANDLED;
484 rotate=WJpegComplex::ROT_0;
485 showPicture(VMediaList::MV_NEXT,slideshow,true);
486 rt= BoxStack::COMMAND_HANDLED;
492 updatePictureBanner();
496 rotate=WJpegComplex::ROT_0;
497 showPicture(VMediaList::MV_NEXT,slideshow,true);
499 rt= BoxStack::COMMAND_HANDLED;
503 showtime=INITIAL_SHOWTIME;
504 updatePictureBanner();
505 rt= BoxStack::COMMAND_HANDLED;
509 case WJpegComplex::ROT_0:
510 rotate=WJpegComplex::ROT_90;
512 case WJpegComplex::ROT_90:
513 rotate=WJpegComplex::ROT_180;
515 case WJpegComplex::ROT_180:
516 rotate=WJpegComplex::ROT_270;
518 case WJpegComplex::ROT_270:
519 rotate=WJpegComplex::ROT_0;
522 showPicture(VMediaList::MV_NONE,slideshow,true);
523 rt=BoxStack::COMMAND_HANDLED;
526 if (info) destroyInfo();
527 else showPictureInfo();
528 rt=BoxStack::COMMAND_HANDLED;
532 case WJpegComplex::CROP:
533 cropmode=WJpegComplex::LETTER;
535 case WJpegComplex::LETTER:
536 cropmode=WJpegComplex::CROPPERCENT;
539 cropmode=WJpegComplex::CROP;
542 showPicture(VMediaList::MV_NONE,slideshow,true);
547 destroyPictureBanner();
549 VColourTuner *ct=new VColourTuner();
550 BoxStack::getInstance()->add(ct);
552 BoxStack::getInstance()->update(ct);
553 rt=BoxStack::COMMAND_HANDLED;
558 setPictureMode(false);
559 rt= BoxStack::COMMAND_HANDLED;
571 int rt = BoxStack::ABANDON_COMMAND;
572 bool updateInfo=false;
573 //------------------------- command in mode AUDIO (i.e. audio is on top) ----------------
578 rt=BoxStack::COMMAND_HANDLED;
581 play(playall,false,VMediaList::MV_PREV);
582 rt= BoxStack::COMMAND_HANDLED;
585 if (! audioError) getPlayer()->fastForward();
587 rt=BoxStack::COMMAND_HANDLED;
590 play(playall,false,VMediaList::MV_NEXT);
591 rt= BoxStack::COMMAND_HANDLED;
593 case Input::SKIPFORWARD:
594 if (! audioError) getPlayer()->skipForward(10);
595 rt=BoxStack::COMMAND_HANDLED;
597 case Input::SKIPBACK:
598 if (! audioError) getPlayer()->skipBackward(10);
599 rt=BoxStack::COMMAND_HANDLED;
602 rt=BoxStack::COMMAND_HANDLED;
605 if (! audioError) getPlayer()->jumpToPercent(0);
606 rt=BoxStack::COMMAND_HANDLED;
609 if (! audioError) getPlayer()->jumpToPercent(10);
610 rt=BoxStack::COMMAND_HANDLED;
613 if (! audioError) getPlayer()->jumpToPercent(20);
614 rt=BoxStack::COMMAND_HANDLED;
617 if (! audioError) getPlayer()->jumpToPercent(30);
618 rt=BoxStack::COMMAND_HANDLED;
621 if (! audioError) getPlayer()->jumpToPercent(40);
622 rt=BoxStack::COMMAND_HANDLED;
625 if (! audioError) getPlayer()->jumpToPercent(50);
626 rt=BoxStack::COMMAND_HANDLED;
629 if (! audioError) getPlayer()->jumpToPercent(60);
630 rt=BoxStack::COMMAND_HANDLED;
633 if (! audioError) getPlayer()->jumpToPercent(70);
634 rt=BoxStack::COMMAND_HANDLED;
637 if (! audioError) getPlayer()->jumpToPercent(80);
638 rt=BoxStack::COMMAND_HANDLED;
641 if (! audioError) getPlayer()->jumpToPercent(90);
642 rt=BoxStack::COMMAND_HANDLED;
649 retriggerAudioInfo=false;
652 retriggerAudioInfo=true;
655 if (getPlayer()->getState() == AudioPlayer::S_ERROR) {
656 if (playall) play(playall,false,VMediaList::MV_NEXT);
658 rt= BoxStack::COMMAND_HANDLED;
663 if (! audioError) getPlayer()->unpause();
665 if (getPlayer()->getState() != AudioPlayer::S_ERROR) ;
666 else if (playall) play(playall,false,VMediaList::MV_NEXT);
667 rt= BoxStack::COMMAND_HANDLED;
671 if (! audioError) getPlayer()->pause();
673 rt= BoxStack::COMMAND_HANDLED;
679 rt= BoxStack::COMMAND_HANDLED;
685 retriggerAudioInfo=false;
688 if (! pictureShowing) setPictureMode(false); //could have been delayed
689 rt= BoxStack::COMMAND_HANDLED;
693 if (audioEnabled && updateInfo) updateAudioInfo();
698 void VMediaView::processMessage(Message* m)
700 Log::getInstance()->log("VMediaView::processMessage", Log::DEBUG, "cmd=%lu,p=%lu", m->message,m->parameter);
701 if (m->message == Message::MOUSE_MOVE)
705 else if (m->message == Message::MOUSE_LBDOWN)
707 if (coordsOutsideBox(m))
709 Input::sendInputKey(Input::BACK);
712 else if (m->message = Message::PLAYER_EVENT) {
713 switch (m->parameter) {
714 case EVENT_SLIDESHOW:
715 if (! pictureEnabled) break; //old timer msg...
716 //if (! audioEnabled) {
719 rotate=WJpegComplex::ROT_0;
720 showPicture(VMediaList::MV_NEXT,true,false);
725 case EVENT_DRAWINGERROR:
728 case EVENT_DRAWINGDONE:
731 case EVENT_DIRECTORYDONE:
732 //just disable audio without (poetntially) hiding us
733 //this gives the list a chance to decide whether audio
734 //should be on top afterwards without showing the list
736 setAudioMode(false,false);
737 parent->directoryDone();
738 if (! pictureShowing) setPictureMode(false);
739 if (! justPlaying) setAudioMode(false);
741 case AudioPlayer::STREAM_ERR:
742 case AudioPlayer::STREAM_END:
743 if (playall) play(playall,false,VMediaList::MV_NEXT);
749 case AudioPlayer::SHORT_UPDATE:
750 if (info && audioEnabled ) {
752 BoxStack::getInstance()->update(info,&clocksRegion);
753 BoxStack::getInstance()->update(info,&barRegion);
754 Timers::getInstance()->setTimerD(this, 4, 1);
757 case AudioPlayer::NEW_SONG:
758 if (audioEnabled) updateAudioInfo();
760 case AudioPlayer::CONNECTION_LOST:
761 if (audioEnabled) destroyInfo();
762 if (AudioPlayer *player=getPlayer(false)) {
766 Control::getInstance()->connectionLost();
773 VMediaView * VMediaView::createViewer(VMediaList * mparent) {
774 Log::getInstance()->log("VMediaView::createViewer", Log::DEBUG, "p=%p",
776 VMediaView *vmn=new VMediaView(mparent);
777 BoxStack::getInstance()->add(vmn);
779 BoxStack::getInstance()->update(vmn);
783 void VMediaView::startSlideshow() {
785 if (! pictureLoading) Timers::getInstance()->setTimerD(this,1,showtime);
788 void VMediaView::stopSlideshow(bool hard) {
789 if (hard) slideshow=false;
790 Timers::getInstance()->cancelTimer(this,1);
794 void VMediaView::showPicture(u4 move,bool bslideshow,bool activateBanner) {
796 setPictureMode(true);
798 #ifdef DRAWING_THREAD
799 drawingThread->stop();
801 slideshow=bslideshow;
802 Media *newPicture=parent->getMedia(MEDIA_TYPE_PICTURE,move);
804 pictureShowing=false;
806 //within the event handler first directoryDone is called
807 //and afterwards everything is stopped if nothing new
808 sendCommandMsg(EVENT_DIRECTORYDONE);
815 if (currentPicture) {
816 delete currentPicture;
819 currentPicture=newPicture;
820 loadPicture(currentPicture,activateBanner);
824 //the real picture drawing method
825 //will start drawing on a new surface and will switch it wehn done
827 int VMediaView::loadPicture(Media *md,bool activateBanner) {
830 if (! md->getURI()) {
831 pictureError=tr("No media found");
832 Log::getInstance()->log("VMediaView::load",Log::ERR,"no URI in media");
835 Log::getInstance()->log("VMediaView::load", Log::DEBUG, "filename=%s,p=%p",
836 md->getURI()->getName(),this);
837 //do we have a pictureBanner?
838 havePictureBanner=pictureBanner!=NULL || activateBanner;
840 #ifdef DRAWING_THREAD
841 if (!audioEnabled && havePictureBanner ) showPictureBanner(true);
842 drawingThread->stop();
844 showPictureBanner(true);
848 int rtok=reader->initRead(md->getURI(),&size,ctl.scaleAmount); //scaleAmount is the same for both...
850 //now we can really draw
851 //get the surface for drawing
852 Surface * drawSurface=NULL;
853 WJpegComplex::JpegControl *drawCtl=NULL;
854 getDrawingParam(drawSurface,drawCtl);
856 drawCtl->rotation=rotate;
857 drawCtl->mode=cropmode;
858 drawCtl->compressedSize=size;
859 #ifdef DRAWING_THREAD
860 bool ok=drawingThread->start(drawCtl,drawSurface,reader,pictureBack);
862 Log::getInstance()->log("VMediaView::load", Log::ERR, "unable to start drawing thread");
863 pictureError=tr("JpegError");
864 pictureLoading=false;
865 destroyPictureBanner();
870 //here we could hand this over to the drawing thread
871 bool ok=WJpegComplex::drawJpeg(drawCtl,drawSurface,reader,pictureBack);
876 pictureLoading=false;
880 void VMediaView::drawingDone(bool hasError) {
881 pictureLoading=false;
882 destroyPictureBanner(); //disable loading indication (or real banner)
885 Log::getInstance()->log("VMediaView::drawingDone", Log::DEBUG, "success: sfc now=%p",surface);
887 //only show the pictureBanner if it was there before
888 if (havePictureBanner && ! audioEnabled) showPictureBanner();
889 Log::getInstance()->log("VMediaView::load", Log::DEBUG, "success" );
890 updatePictureInfo(); //will only do somethng if pictureEnabled
894 pictureError=tr("JpegError");
895 if (pictureEnabled) {
900 MediaPlayer::getInstance()->closeMediaChannel(1);
901 #ifndef DRAWING_THREAD
902 if (audioEnabled) showAudioBanner();
904 if (slideshow) startSlideshow();
905 BoxStack::getInstance()->update(this);
908 void VMediaView::showPictureBanner(bool loading) {
909 //we are in the main thread - so we can (and must) safely hard destroy/create the banner
910 Timers::getInstance()->cancelTimer(this,2);
911 if (! currentPicture) {
913 destroyPictureBanner(false);
916 if (pictureBanner) destroyPictureBanner(false);
917 if (! pictureEnabled || ! bannerEnabled) return;
918 pictureBanner= new VPictureBanner(loading, slideshow);
919 pictureBanner->fillColour(infoBack);
921 int len=strlen(currentPicture->getFileName())+Media::TIMEBUFLEN+20;
922 char *buf=new char[len];
923 char tbuf[Media::TIMEBUFLEN];
924 SNPRINTF(buf,len,"%c%02ds%c %s %s " ,
928 currentPicture->getTimeString(tbuf),
929 currentPicture->getFileName()
931 pictureBanner->setText(buf);
935 char *buf=new char[strlen(currentPicture->getDisplayName())+50];
936 SNPRINTF(buf,50,"%s %s",tr("Loading"), currentPicture->getDisplayName());
937 pictureBanner->setText(buf);
940 pictureBanner->draw();
941 if (! loading ) Timers::getInstance()->setTimerD(this,2,8);
942 BoxStack::getInstance()->add(pictureBanner);
943 BoxStack::getInstance()->update(pictureBanner);
946 void VMediaView::destroyPictureBanner(bool fromTimer) {
948 if (fromTimer) sendViewMsg(pictureBanner);
949 else BoxStack::getInstance()->remove(pictureBanner);
951 if (! fromTimer) Timers::getInstance()->cancelTimer(this,2);
954 void VMediaView::updatePictureBanner(bool loading) {
955 if (pictureBanner ) {
956 showPictureBanner(loading);
959 void VMediaView::timercall(int clientref) {
960 Log::getInstance()->log("VMediaView::timercall", Log::DEBUG, "id=%d",clientref);
965 sendCommandMsg(AudioPlayer::SHORT_UPDATE);
970 Log::getInstance()->log("VMediaView::timercall", Log::DEBUG, "infoEnd");
973 //we only did show the audio error info if audio is enabled
974 bool stillError=false;
975 if (AudioPlayer * player=getPlayer(false)) {
976 stillError=player->getState()==AudioPlayer::S_ERROR;
979 sendCommandMsg(AudioPlayer::STREAM_END);
985 if (! slideshow) return;
986 Log::getInstance()->log("VMediaView::timercall", Log::DEBUG, "slideshow");
987 sendCommandMsg(EVENT_SLIDESHOW);
990 Log::getInstance()->log("VMediaView::timercall", Log::DEBUG, "pictureBannerEnd");
991 destroyPictureBanner(true);
998 void VMediaView::showPictureInfo(){
999 if (! pictureEnabled || audioEnabled) return;
1000 if (info) destroyInfo();
1001 if (! currentPicture) return;
1004 info->setTitleText(currentPicture->getFileName());
1005 info->setDropThrough();
1006 info->setSize(500, 300);
1007 info->createBuffer();
1008 info->setBorderOn(1);
1009 info->setTitleBarOn(1);
1011 if (Video::getInstance()->getFormat() == Video::PAL)
1012 info->setPosition(100, 180);
1014 info->setPosition(100, 150);
1016 char tbuf[Media::TIMEBUFLEN];
1017 //modes should come from mediaoptions...
1018 const char *mode=NULL;
1019 switch (currentControl->mode) {
1020 case WJpegComplex::CROPPERCENT:
1023 case WJpegComplex::LETTER:
1030 const char *dirname=parent->getDirname(MEDIA_TYPE_PICTURE);
1032 const char *prfx="";
1033 if (dirname) ldir=strlen(dirname);
1035 dirname=dirname+ldir-35;
1038 SNPRINTF(buf,INFOBUF,"%s= %s%s\n%s= %u x %u\n%s= %lu kBytes\n%s= %s\n%s= %u\n%s= %u%%\n%s= %s",
1039 tr("Directory"), prfx,dirname,
1040 tr("Format(px)"),currentControl->picw,currentControl->pich,
1041 tr("Filesize"),currentControl->compressedSize/1000,
1042 tr("Time"),currentPicture->getTimeString(tbuf),
1043 tr("Rotation"),90*currentControl->finalRotation,
1044 tr("Scale"),currentControl->scale,
1045 tr("Picture Mode"),mode );
1046 info->setMainText(buf);
1048 BoxStack::getInstance()->add(info);
1049 BoxStack::getInstance()->update(info);
1050 Timers::getInstance()->setTimerD(this,3,8);
1052 void VMediaView::updatePictureInfo(){
1057 void VMediaView::destroyInfo(bool fromTimer){
1060 Log::getInstance()->log("VMediaView",Log::DEBUG,"remove info %p",info);
1061 BoxStack::getInstance()->remove(info);
1064 Log::getInstance()->log("VMediaView",Log::DEBUG,"trigger remove info %p",info);
1069 if (! fromTimer) Timers::getInstance()->cancelTimer(this,3);
1070 if (! fromTimer) Timers::getInstance()->cancelTimer(this,4);
1073 void VMediaView::sendViewMsg(Boxx *v) {
1074 Message* m = new Message();
1075 m->message = Message::CLOSE_ME;
1076 m->p_to = Message::BOXSTACK;
1079 MessageQueue::getInstance()->postMessage(m);
1081 void VMediaView::sendCommandMsg(int command) {
1082 Message* m = new Message();
1083 //we misuse PLAYER_EVENT here
1084 m->message = Message::PLAYER_EVENT;
1087 m->parameter = command;
1088 MessageQueue::getInstance()->postMessage(m);
1091 void VMediaView::enableBanner(bool enable) {
1092 bannerEnabled=false;
1093 updatePictureBanner();
1096 void VMediaView::getDrawingParam(Surface *&sfc,WJpegComplex::JpegControl *&c){
1097 if (secondSurface()) {
1098 //we currently display on sfc2
1107 void VMediaView::switchSurface(){
1108 if (secondSurface()) {
1109 //now we switch to sfc1
1111 currentControl=&ctl;
1115 currentControl=&ctl2;
1119 AudioPlayer * VMediaView::getPlayer(bool createIfNeeded)
1121 AudioPlayer* rt=AudioPlayer::getInstance(this,false);
1122 if (! createIfNeeded && rt == NULL) return NULL;
1124 rt=AudioPlayer::getInstance(this);
1130 bool VMediaView::isAudioPlaying() {
1131 Log::getInstance()->log("VMediaView::isPlaying", Log::DEBUG, "rt=%s", justPlaying?"true":"false");
1140 int VMediaView::play(bool all,bool activate,u4 move,bool showInfo) {
1142 if (getPlayer(false)) getPlayer(false)->stop();
1144 if (currentAudio) delete currentAudio;
1147 currentAudio=parent->getMedia(MEDIA_TYPE_AUDIO,move);
1149 if ( ! currentAudio || ! currentAudio->getURI()) {
1150 Log::getInstance()->log("VMediaView::load", Log::ERR, "no URI in media");
1151 audioError=tr("no audio file");
1152 if (audioEnabled) sendCommandMsg(EVENT_DIRECTORYDONE);
1156 Log::getInstance()->log("VMediaView::load", Log::DEBUG, "filename=%s,p=%p",
1157 currentAudio->getURI()->getName(),this);
1158 int wseq=getPlayer()->play(currentAudio->getURI());
1159 if (getPlayer()->waitForSequence(5,wseq)<0) {
1160 audioError=tr("unable to open audio file");
1167 Log::getInstance()->log("VMediaView", Log::DEBUG, "player started for %s",currentAudio->getURI()->getName());
1169 if (activate && ! audioEnabled){
1170 if (showInfo) retriggerAudioInfo=true;
1174 //avoid duplicate creation of banner and info
1178 if (activate && (! currentPicture || ! pictureShowing)) draw();
1179 BoxStack::getInstance()->update(this);
1183 void VMediaView::showAudioInfo() {
1184 if (! audioEnabled) return;
1185 Timers::getInstance()->cancelTimer(this,4);
1186 Timers::getInstance()->cancelTimer(this,3);
1187 if (info) destroyInfo();
1188 if (! retriggerAudioInfo) return;
1190 bool playerError=getPlayer()->getState()==AudioPlayer::S_ERROR || audioError;
1191 if (! playerError) Timers::getInstance()->setTimerD(this,3,AUDIOBANNER_TIME);
1192 else Timers::getInstance()->setTimerD(this,3,AUDIOERROR_TIME);
1193 if (! playerError) Timers::getInstance()->setTimerD(this,4, 1);
1194 BoxStack::getInstance()->update(info);
1197 void VMediaView::updateAudioInfo() {
1203 void VMediaView::drawAudioInfo(){
1204 Log::getInstance()->log("VMediaView",Log::DEBUG, "draw banner for %p",info);
1205 const char *title=NULL;
1206 char *playerTitle=NULL;
1207 bool playerError=false;
1211 const char *pl=tr("Playlist");
1212 const char *first=NULL;
1213 char *playerInfo=NULL;
1215 if (getPlayer()->getState() == AudioPlayer::S_PLAY) audioError=NULL;
1217 if (! currentAudio && ! audioError) audioError=tr("no audio file");
1219 title=tr("MediaError");
1222 playerTitle=getPlayer()->getTitle();
1223 if (playerTitle) title=playerTitle;
1224 else title=currentAudio->getDisplayName();
1225 num=parent->getNumEntries(MEDIA_TYPE_AUDIO,currentAudio->index);
1226 playerError=getPlayer()->getState() == AudioPlayer::S_ERROR;
1227 //1more line for long dirs
1228 numlines=playall?5:4;
1232 first=tr("Unable to play audio file");
1233 len=strlen(first)+3;
1235 else if (audioError) {
1238 len=strlen(first)+3;
1241 playerInfo=getPlayer()->getID3Info();drawText(tr("Loading"),5,3,DrawStyle::LIGHTTEXT);
1242 len=strlen(currentAudio->getDisplayName())+strlen(pl)+30+strlen(parent->getDirname(MEDIA_TYPE_AUDIO))+Media::TIMEBUFLEN+110;
1244 for (u4 i=0;i<strlen(playerInfo);i++)
1245 if (playerInfo[i] == '\n') numlines++;
1246 len+=strlen(playerInfo);
1255 u4 height=numlines*30+60;
1256 u4 vheight=Video::getInstance()->getScreenHeight();
1257 u4 vwidth=Video::getInstance()->getScreenWidth();
1258 if (height > vheight-2*AUDIOBANNER_BOTTOM_MARGIN)
1259 height=vheight-2*AUDIOBANNER_BOTTOM_MARGIN;
1260 info->setSize(vwidth -2*AUDIOBANNER_X_MARGIN, height);
1261 info->createBuffer();
1262 if (Video::getInstance()->getFormat() == Video::PAL)
1264 info->setPosition(AUDIOBANNER_X_MARGIN, vheight-height-AUDIOBANNER_BOTTOM_MARGIN);
1268 info->setPosition(AUDIOBANNER_X_MARGIN, vheight-height-AUDIOBANNER_BOTTOM_MARGIN);
1271 info->setTitleBarOn(0);
1272 info->setDropThrough();
1274 //set the regions for the closcks and bars on banner
1275 barRegion.x = info->getWidth()-AUDIOBARLEN-20;
1276 barRegion.y = info->getHeight() - 30; // FIXME, need to be - 1? and below?
1277 barRegion.w = info->getWidth()-AUDIOBARLEN+10;
1280 clocksRegion.x = 130;
1281 clocksRegion.y = barRegion.y + 3;
1282 clocksRegion.w = 190;
1283 clocksRegion.h = getFontHeight();
1284 Log::getInstance()->log("VMediaView",Log::DEBUG,"created AudioInfo %p",info);
1285 BoxStack::getInstance()->add(info);
1286 char *buf=new char [len];
1287 if (playerError || audioError) {
1288 SNPRINTF(buf,len,"%s",first);
1291 char tbuf[Media::TIMEBUFLEN];
1293 SNPRINTF(buf,len,"%s\n"
1299 tr("FileName"),currentAudio->getDisplayName(),
1300 pl,num,parent->getNumEntries(MEDIA_TYPE_AUDIO),
1301 tr("Directory"),parent->getDirname(MEDIA_TYPE_AUDIO),
1302 tr("Time"),currentAudio->getTimeString(tbuf));
1305 SNPRINTF(buf,len,"%s\n"
1310 tr("FileName"),currentAudio->getDisplayName(),
1311 tr("Directory"),parent->getDirname(MEDIA_TYPE_AUDIO),
1312 tr("Time"),currentAudio->getTimeString(tbuf));
1315 Log::getInstance()->log("VMediaView",Log::DEBUG,"info: (%d)%s",strlen(buf),buf);
1316 //now the real drawing functions
1319 info->rectangle(0, 0, info->getWidth(), 30, DrawStyle::TITLEBARBACKGROUND);
1320 info->drawText(title, 5, 5, DrawStyle::LIGHTTEXT);
1321 info->drawPara(buf,5,32,DrawStyle::LIGHTTEXT);
1327 int ybottom=info->getHeight();
1328 info->rectangle(0, ybottom - barRegion.h, info->getWidth(), barRegion.h, DrawStyle::TITLEBARBACKGROUND);
1329 bool drawSymbol=true;
1330 switch(getPlayer()->getState()) {
1331 case AudioPlayer::S_PAUSE:
1332 w.nextSymbol = WSymbol::PAUSE;
1334 case AudioPlayer::S_PLAY:
1335 w.nextSymbol = WSymbol::PLAY;
1337 case AudioPlayer::S_DONE:
1339 w.nextSymbol = WSymbol::PLAY;
1343 case AudioPlayer::S_BACK:
1344 w.nextSymbol = WSymbol::FBWD ;
1346 case AudioPlayer::S_FF:
1347 w.nextSymbol = WSymbol::FFWD ;
1354 w.setPosition(x, ybottom-24);
1359 info->rectangle(x, ybottom - 23, 18, 16, DrawStyle::LIGHTTEXT);
1364 if (playerInfo) delete playerInfo;
1365 if (playerTitle) delete playerTitle;
1368 void VMediaView::drawAudioClocks() {
1369 if (! info || ! audioEnabled) return;
1370 Log::getInstance()->log("VMediaView::drawAudioClocks", Log::DEBUG, "");
1371 //draw clocks and bar
1372 info->rectangle(clocksRegion, DrawStyle::TITLEBARBACKGROUND);
1374 time_t currentSec = (time_t)(getPlayer()->getCurrentTimes());
1375 time_t lengthSec=(time_t)(getPlayer()->getSonglen());
1378 /* gmtime_r(¤tSec,&cpos);
1379 gmtime_r(&lengthSec,&slen);*/
1380 cpos.tm_hour=currentSec/3600;
1381 cpos.tm_min=(currentSec-cpos.tm_hour*3600)/60;
1382 cpos.tm_sec=(currentSec-cpos.tm_hour*3600-cpos.tm_min*60);
1383 slen.tm_hour=lengthSec/3600;;
1384 slen.tm_min=(lengthSec-slen.tm_hour*3600)/60;
1385 slen.tm_sec=(lengthSec-slen.tm_hour*3600-slen.tm_min*60);
1388 if (currentSec >= lengthSec)
1390 SNPRINTF(buffer,99, "-:--:-- / -:--:-- %03dk",getPlayer()->getCurrentBitrate()/1000);
1394 SNPRINTF(buffer, 99, "%01i:%02i:%02i / %01i:%02i:%02i %03dk", cpos.tm_hour, cpos.tm_min, cpos.tm_sec, slen.tm_hour, slen.tm_min, slen.tm_sec,
1395 getPlayer()->getCurrentBitrate()/1000);
1396 //Log::getInstance()->log("VMediaView", Log::DEBUG, buffer);
1399 info->drawText(buffer, clocksRegion.x, clocksRegion.y, DrawStyle::LIGHTTEXT);
1401 // Draw progress bar
1402 int progBarXbase = 0;
1405 info->rectangle(barRegion.x + progBarXbase, barRegion.y + 3, barlen+10, 24, DrawStyle::LIGHTTEXT);
1406 info->rectangle(barRegion.x + progBarXbase + 2, barRegion.y + 5, barlen+6, 20, barBlue);
1408 if (currentSec > lengthSec) currentSec=lengthSec;
1409 if (lengthSec == 0) return;
1411 // Draw yellow portion
1412 int progressWidth = (barlen+2) * currentSec / lengthSec;
1413 info->rectangle(barRegion.x + progBarXbase + 4, barRegion.y + 7, progressWidth, 16, DrawStyle::SELECTHIGHLIGHT);
1417 void VMediaView::showAudioBanner() {
1418 destroyAudioBanner();
1419 if (! audioEnabled) return;
1420 audioBanner=new VInfo();
1421 Log::getInstance()->log("VMediaView",Log::DEBUG,"creating AudioBanner %p", audioBanner);
1422 Video *v=Video::getInstance();
1423 audioBanner->setSize(v->getScreenWidth()-100, 36);
1424 audioBanner->createBuffer();
1425 audioBanner->setPosition(50, v->getScreenHeight()-50);
1426 audioBanner->fillColour(audioBannerBack);
1427 audioBanner->setTitleBarOn(0);
1428 audioBanner->setDropThrough();
1429 if ( ! currentAudio || ! currentAudio->getDisplayName() || audioError) {
1430 audioBanner->drawText(tr("AudioPlayer - not playing"),5,3,DrawStyle::LIGHTTEXT);
1433 char * buf=new char[strlen(currentAudio->getDisplayName())+50];
1434 SNPRINTF(buf,50,"%s %s",tr("AudioPlayer"),currentAudio->getDisplayName());
1435 audioBanner->drawText(buf,5,3,DrawStyle::LIGHTTEXT);
1438 BoxStack::getInstance()->add(audioBanner);
1439 BoxStack::getInstance()->update(audioBanner);
1442 void VMediaView::destroyAudioBanner() {
1444 Log::getInstance()->log("VMediaView",Log::DEBUG,"deleting AudioBanner %p",audioBanner);
1445 BoxStack::getInstance()->remove(audioBanner);