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"
29 #include "wselectlist.h"
40 #include "mediaoptions.h"
41 #include "mediaplayer.h"
43 #include "messagequeue.h"
45 const int VMediaView::EVENT_SLIDESHOW=100;
46 const int VMediaView::EVENT_DRAWINGDONE=101;
47 const int VMediaView::EVENT_DRAWINGERROR=102;
48 const int VMediaView::EVENT_DIRECTORYDONE=103;
52 * the combined user interface for pictures and audio
53 * has 2 surfaces to enable drawing in a separate thread
54 * the info display can either show a picture info or and audio info
55 * if the audio player comes on top it disables the picture info display
56 * basically we have 3 modes:
57 * - HIDDEN viewer is hidden (pictureEnabled=false, audioEnabled=false)
58 * if justPlaying=true the audioplayer is still running
59 * - PICTURE picture viewer on top ("slide show") - pictureEnabled=true, audioEnabled=false
60 * no AudioBanner visible
61 * if justPlaying=true the audio player runs in bg
62 * - AUDIO audioPlayer on top ("playlist mode") - pictureEnabled=true, audioEnabled=true
63 * the picture viewer is currently halted (should be able to continue if no AudioInfo)
64 * no picture banner (we should have an audio banner to indicate the audio player on top!)
65 * state transitions (via setPictureMode, setAudioMode)
66 * - show Picture -> pictureEnabled=true,
67 * only called from command handler if audioEnabled=false
68 * call from mediaList only possible if audioEnabled=false
69 * *** TODO: would be better to have separate function for external call/internal call
70 * - play [Audio] -> if activate is set -> audioEnabled=true (-> Mode AUDIO)
71 * used for calls from medialist
72 * internal calls do not set activate!
73 * - YELLOW key - toggle
74 * if audioActive==true -> audioActive=false (new mode depends on pictureEnabled - either HIDDEN or PICTURE)
75 * if audioActive==false -> audioActive=true (new mode AUDIO)
76 * *** open handling if no audio file there (ignore key???) - currently empty player
77 * - BACK if mode AUDIO -> audioEnabled=false, justPlaying=false
78 * - BACK if mode PICTURE -> pictureEnabled=false;
79 * - player StreamEnd -> audioEnabled=false (new mode depends on pciture - either HIDDEN or PICTURE)
80 * info/banner handling:
81 * - AudioInfo - alternativ to pictureInfo
82 * off when disabling audio or after timer or with OK in AUDIO mode
83 * on currently any command or player event (end/new song) when audioEnabled==true and retriggerAudioInfo=true
84 * on on OK in AUDIO mode
86 * off when disabling Picture or after timer, OK, green
87 * on only on green when pictureEnabled==true
89 * 2 modes: loading/normal
91 * off when disabling picture, OK, after timer
92 * on when enabling picture, with OK
94 * off when disabling picture and when loading done
96 * *** open: do not show when audio enabled and slide show running (currently slideshow is paused)
98 * always shown when audioEnabled=true
99 * on when enabling Audio
100 * off when disabling audio
102 * timers: 1 - slide show
105 * 4 - audio short update
108 #define DRAWING_THREAD
110 DrawStyle VMediaView::pictureBack=DrawStyle(140,140,140);
111 DrawStyle VMediaView::infoBack=DrawStyle(110,110,110);
112 DrawStyle VMediaView::audioBannerBack=DrawStyle(110,110,110,20);
115 //how long do we wait for a single picture chunk
116 //the unit is 100ms (the timeout on the server side)
118 class VPreader : public JpegReader {
125 VPreader(VMediaView *p,ImageReader *r){
131 virtual ULONG readChunk(ULONG offset,ULONG len,char ** buf) {
132 Log::getInstance()->log("VMediaView::jpegReader", Log::DEBUG, "read chunk o=%d,len=%d,buf=%p",
136 for (int trycount=0;trycount < MAXTRY && ! dobreak && numrec == 0 && rt == 0;trycount++) {
142 rt=reader->getImageChunk((ULLONG)offset,(UINT)len,&numrec,(UCHAR **)buf);
145 Log::getInstance()->log("VMediaView::jpegReader", Log::DEBUG, "got n=%d,buf=%p,rt=%d",
149 virtual int initRead(const MediaURI *uri,ULLONG *sz,ULONG factor=100) {
150 Log::getInstance()->log("VMediaView::jpegReader", Log::DEBUG, "load image %s",uri->getDisplayName());
151 Video* video = Video::getInstance();
153 int rt=MediaPlayer::getInstance()->openMedium(1,uri,sz,video->getScreenWidth()*factor/100, video->getScreenHeight()*factor/100);
156 Log::getInstance()->log("VMediaView::jpegReader", Log::DEBUG, "load image %s returned %llu",uri->getDisplayName(),size);
159 virtual ULONG getSize() {return size;}
160 //seems to be thread safe (more or less...)
167 * a separate thread for drawing pictures
168 * will be started with the sfc and the draw control
169 * will send a player event when done
171 class DrawingThread : public Thread_TYPE {
175 WJpegComplex::JpegControl *_ctl;
180 DrawingThread(VMediaView *parent) {
187 virtual ~DrawingThread(){}
189 return (threadIsActive()!=0);
191 bool start(WJpegComplex::JpegControl *ctl,Surface *sfc,VPreader *reader,DrawStyle &c) {
200 return (threadStart() == 1);
203 Log::getInstance()->log("DrawingThread",Log::DEBUG,"stop initiated");
204 if (threadIsActive()) {
206 if (_reader) _reader->setBreak();
209 Log::getInstance()->log("DrawingThread",Log::DEBUG,"stop done");
214 virtual void threadMethod() {
215 Log::getInstance()->log("DrawingThread",Log::DEBUG,"started");
216 bool rt=WJpegComplex::drawJpeg(_ctl,_sfc,_reader,_colour);
218 if (! _interrupted) {
219 Message* m = new Message();
220 //we misuse PLAYER_EVENT here
221 m->message = Message::PLAYER_EVENT;
224 m->parameter = rt?VMediaView::EVENT_DRAWINGDONE:VMediaView::EVENT_DRAWINGERROR;
225 MessageQueue::getInstance()->postMessage(m);
227 Log::getInstance()->log("DrawingThread",Log::DEBUG,"finishing interrupt=%d",(int)_interrupted);
229 virtual void threadPostStopCleanup() {}
234 VMediaView::VMediaView(VMediaList *p)
236 Log::getInstance()->log("VMediaView::VMediaView", Log::DEBUG, "p=%p", this);
238 pictureEnabled=false;
240 ireader=new ImageReader(1,MediaPlayer::getInstance());
241 reader=new VPreader(this,ireader);
242 //the surface handling
243 Video* video = Video::getInstance();
244 setSize(video->getScreenWidth(), video->getScreenHeight());
248 //create the second surface
253 //disable the surface
262 pictureLoading=false;
265 showtime=INITIAL_SHOWTIME;
266 rotate=WJpegComplex::ROT_0;
268 options=MediaOptions::getInstance();
269 VColourTuner::initFactors();
270 int st=options->getIntOption("SlideShowInterval");
271 if (st > 0) showtime=st;
272 ctl.area.w=originalw;
273 ctl.area.h=originalh;
275 ctl.scaleafter=options->getIntOption("ScaleFactor");
276 const char * mode=options->getStringOption("PictureMode");
277 if (strcmp(mode,"clip") == 0) ctl.mode=WJpegComplex::CROP;
278 else if (strcmp(mode,"letter") == 0) ctl.mode=WJpegComplex::LETTER;
279 else if (strcmp(mode,"clipfactor") == 0) ctl.mode=WJpegComplex::CROPPERCENT;
280 ctl.scaleAmount=options->getIntOption("PictureSize");
281 if (ctl.scaleAmount < 10) ctl.scaleAmount=10;
282 if (ctl.scaleAmount > 200) ctl.scaleAmount=200;
285 //current control is the one used for DISPLAY (not the one for drawing - this is the other one)
286 //this is closely coupled to the surface - i.e. ctl is for sfc1, ctl2 for sfc2
289 pictureShowing=false;
293 barBlue.set(0, 0, 150, 150);
298 retriggerAudioInfo=false;
300 drawingThread=new DrawingThread(this);
303 VMediaView::~VMediaView()
305 Log::getInstance()->log("VMediaView::~VMediaView", Log::DEBUG, "p=%p,secondSfc=%s", this,(secondSurface()?"true":"false"));
306 destroyPictureBanner();
307 if (currentPicture) delete currentPicture;
308 Timers::getInstance()->cancelTimer(this,1);
309 Timers::getInstance()->cancelTimer(this,2);
310 Timers::getInstance()->cancelTimer(this,3);
311 Timers::getInstance()->cancelTimer(this,4);
312 Timers::getInstance()->cancelTimer(this,5);
314 destroyAudioBanner();
315 if (getPlayer(false)) {
316 AudioPlayer::getInstance(NULL,false)->shutdown();
318 if (currentAudio) delete currentAudio;
319 drawingThread->stop();
323 if (secondSurface()) {
331 MediaPlayer::getInstance()->closeMediaChannel(1);
332 MediaPlayer::getInstance()->closeMediaChannel(2);
333 Log::getInstance()->log("VMediaView::~VMediaView", Log::DEBUG, "done p=%p", this);
336 void VMediaView::setPictureMode(bool act) {
337 Log::getInstance()->log("VMediaView", Log::DEBUG, "set pictureMode %d p=%d, a=%d", (int)act,(int)pictureEnabled,(int)audioEnabled);
339 if ( ! pictureEnabled && ! audioEnabled) {
345 if (! pictureEnabled) {
346 //we newly enable the picture - so clear the screen
348 BoxStack::getInstance()->update(this);
349 if (slideshow) startSlideshow();
354 if ( pictureEnabled) {
355 destroyPictureBanner();
356 stopSlideshow(false);
357 #ifdef DRAWING_THREAD
358 drawingThread->stop();
360 if (! audioEnabled) {
368 pictureShowing=false;
373 //we can disable audio without automatically hiding us
374 //this will become strange - we are visible but do not handle
375 //keys - so if you call setAudioMode(false,false) be sure
376 //to call setAudioMode(false,true) afterwards
377 //it is only here to give the list a chance to start a new play
378 //when we call directoryDone
379 void VMediaView::setAudioMode(bool act,bool doHiding) {
380 Log::getInstance()->log("VMediaView", Log::DEBUG, "setAudioMode %d p=%d, a=%d", (int)act,(int)pictureEnabled,(int)audioEnabled);
382 if (! audioEnabled) {
383 if (! pictureEnabled) {
386 draw(); //empty screen if no picture
389 destroyPictureBanner();
401 destroyAudioBanner();
403 if (! pictureEnabled && doHiding) {
411 if (havePictureBanner && ! pictureBanner) showPictureBanner();
418 void VMediaView::draw()
420 Log::getInstance()->log("VMediaView::draw", Log::DEBUG, "pictureError=%s,p=%p", pictureError,this);
422 if (pictureShowing ) fillColour(pictureBack);
423 else fillColour(DrawStyle::BLACK);
425 drawText(pictureError,100,area.h/2,DrawStyle::LIGHTTEXT);
431 int VMediaView::handleCommand(int command)
433 Log::getInstance()->log("VMediaView::handleCommand", Log::DEBUG, "cmd=%d,p=%p", command,this);
434 if ( !audioEnabled && ! pictureEnabled ) {
436 if (command == Input::YELLOW) {
442 if ( ! audioEnabled) {
443 //------------------------- command in mode PICTURE (i.e. picture is on top) ----------------
449 case Input::SKIPBACK:
450 rotate=WJpegComplex::ROT_0;
451 showPicture(VMediaList::MV_PREV,slideshow,true);
455 if (showtime > 1) showtime--;
456 updatePictureBanner(true);
459 case Input::SKIPFORWARD:
460 rotate=WJpegComplex::ROT_0;
461 showPicture(VMediaList::MV_NEXT,slideshow,true);
465 if (showtime < 50 ) showtime++;
466 updatePictureBanner(true);
471 destroyPictureBanner();
473 havePictureBanner=false;
476 havePictureBanner=true;
477 showPictureBanner(pictureLoading);
485 rotate=WJpegComplex::ROT_0;
486 showPicture(VMediaList::MV_NEXT,slideshow,true);
493 updatePictureBanner();
497 rotate=WJpegComplex::ROT_0;
498 showPicture(VMediaList::MV_NEXT,slideshow,true);
504 showtime=INITIAL_SHOWTIME;
505 updatePictureBanner();
510 case WJpegComplex::ROT_0:
511 rotate=WJpegComplex::ROT_90;
513 case WJpegComplex::ROT_90:
514 rotate=WJpegComplex::ROT_180;
516 case WJpegComplex::ROT_180:
517 rotate=WJpegComplex::ROT_270;
519 case WJpegComplex::ROT_270:
520 rotate=WJpegComplex::ROT_0;
523 showPicture(VMediaList::MV_NONE,slideshow,true);
527 if (info) destroyInfo();
528 else showPictureInfo();
533 case WJpegComplex::CROP:
534 cropmode=WJpegComplex::LETTER;
536 case WJpegComplex::LETTER:
537 cropmode=WJpegComplex::CROPPERCENT;
540 cropmode=WJpegComplex::CROP;
543 showPicture(VMediaList::MV_NONE,slideshow,true);
548 destroyPictureBanner();
550 VColourTuner *ct=new VColourTuner();
551 BoxStack::getInstance()->add(ct);
553 BoxStack::getInstance()->update(ct);
559 setPictureMode(false);
573 bool updateInfo=false;
574 //------------------------- command in mode AUDIO (i.e. audio is on top) ----------------
582 play(playall,false,VMediaList::MV_PREV);
586 if (! audioError) getPlayer()->fastForward();
591 play(playall,false,VMediaList::MV_NEXT);
594 case Input::SKIPFORWARD:
595 if (! audioError) getPlayer()->skipForward(10);
598 case Input::SKIPBACK:
599 if (! audioError) getPlayer()->skipBackward(10);
606 if (! audioError) getPlayer()->jumpToPercent(0);
610 if (! audioError) getPlayer()->jumpToPercent(10);
614 if (! audioError) getPlayer()->jumpToPercent(20);
618 if (! audioError) getPlayer()->jumpToPercent(30);
622 if (! audioError) getPlayer()->jumpToPercent(40);
626 if (! audioError) getPlayer()->jumpToPercent(50);
630 if (! audioError) getPlayer()->jumpToPercent(60);
634 if (! audioError) getPlayer()->jumpToPercent(70);
638 if (! audioError) getPlayer()->jumpToPercent(80);
642 if (! audioError) getPlayer()->jumpToPercent(90);
650 retriggerAudioInfo=false;
653 retriggerAudioInfo=true;
656 if (getPlayer()->getState() == AudioPlayer::S_ERROR) {
657 if (playall) play(playall,false,VMediaList::MV_NEXT);
664 if (! audioError) getPlayer()->unpause();
666 if (getPlayer()->getState() != AudioPlayer::S_ERROR) ;
667 else if (playall) play(playall,false,VMediaList::MV_NEXT);
672 if (! audioError) getPlayer()->pause();
686 retriggerAudioInfo=false;
689 if (! pictureShowing) setPictureMode(false); //could have been delayed
694 if (audioEnabled && updateInfo) updateAudioInfo();
699 void VMediaView::processMessage(Message* m)
701 Log::getInstance()->log("VMediaView::processMessage", Log::DEBUG, "cmd=%lu,p=%lu", m->message,m->parameter);
702 if (m->message == Message::MOUSE_MOVE)
706 else if (m->message == Message::MOUSE_LBDOWN)
709 //check if press is outside this view! then simulate cancel
710 int x=(m->parameter>>16)-getScreenX();
711 int y=(m->parameter&0xFFFF)-getScreenY();
712 if (x<0 || y <0 || x>(int)getWidth() || y>(int)getHeight())
714 BoxStack::getInstance()->handleCommand(Input::BACK); //simulate cancel press
717 else if (m->message = Message::PLAYER_EVENT) {
718 switch (m->parameter) {
719 case EVENT_SLIDESHOW:
720 if (! pictureEnabled) break; //old timer msg...
721 //if (! audioEnabled) {
724 rotate=WJpegComplex::ROT_0;
725 showPicture(VMediaList::MV_NEXT,true,false);
730 case EVENT_DRAWINGERROR:
733 case EVENT_DRAWINGDONE:
736 case EVENT_DIRECTORYDONE:
737 //just disable audio without (poetntially) hiding us
738 //this gives the list a chance to decide whether audio
739 //should be on top afterwards without showing the list
741 setAudioMode(false,false);
742 parent->directoryDone();
743 if (! pictureShowing) setPictureMode(false);
744 if (! justPlaying) setAudioMode(false);
746 case AudioPlayer::STREAM_ERR:
747 case AudioPlayer::STREAM_END:
748 if (playall) play(playall,false,VMediaList::MV_NEXT);
754 case AudioPlayer::SHORT_UPDATE:
755 if (info && audioEnabled ) {
757 BoxStack::getInstance()->update(info,&clocksRegion);
758 BoxStack::getInstance()->update(info,&barRegion);
759 Timers::getInstance()->setTimerD(this, 4, 1);
762 case AudioPlayer::NEW_SONG:
763 if (audioEnabled) updateAudioInfo();
765 case AudioPlayer::CONNECTION_LOST:
766 if (audioEnabled) destroyInfo();
767 if (AudioPlayer *player=getPlayer(false)) {
771 Command::getInstance()->connectionLost();
778 VMediaView * VMediaView::createViewer(VMediaList * mparent) {
779 Log::getInstance()->log("VMediaView::createViewer", Log::DEBUG, "p=%p",
781 VMediaView *vmn=new VMediaView(mparent);
782 BoxStack::getInstance()->add(vmn);
784 BoxStack::getInstance()->update(vmn);
788 void VMediaView::startSlideshow() {
790 if (! pictureLoading) Timers::getInstance()->setTimerD(this,1,showtime);
793 void VMediaView::stopSlideshow(bool hard) {
794 if (hard) slideshow=false;
795 Timers::getInstance()->cancelTimer(this,1);
799 void VMediaView::showPicture(ULONG move,bool bslideshow,bool activateBanner) {
801 setPictureMode(true);
803 #ifdef DRAWING_THREAD
804 drawingThread->stop();
806 slideshow=bslideshow;
807 Media *newPicture=parent->getMedia(MEDIA_TYPE_PICTURE,move);
809 pictureShowing=false;
811 //within the event handler first directoryDone is called
812 //and afterwards everything is stopped if nothing new
813 sendCommandMsg(EVENT_DIRECTORYDONE);
820 if (currentPicture) {
821 delete currentPicture;
824 currentPicture=newPicture;
825 loadPicture(currentPicture,activateBanner);
829 //the real picture drawing method
830 //will start drawing on a new surface and will switch it wehn done
832 int VMediaView::loadPicture(Media *md,bool activateBanner) {
835 if (! md->getURI()) {
836 pictureError=tr("No media found");
837 Log::getInstance()->log("VMediaView::load",Log::ERR,"no URI in media");
840 Log::getInstance()->log("VMediaView::load", Log::DEBUG, "filename=%s,p=%p",
841 md->getURI()->getName(),this);
842 //do we have a pictureBanner?
843 havePictureBanner=pictureBanner!=NULL || activateBanner;
845 #ifdef DRAWING_THREAD
846 if (!audioEnabled && havePictureBanner ) showPictureBanner(true);
847 drawingThread->stop();
849 showPictureBanner(true);
853 int rtok=reader->initRead(md->getURI(),&size,ctl.scaleAmount); //scaleAmount is the same for both...
855 //now we can really draw
856 //get the surface for drawing
857 Surface * drawSurface=NULL;
858 WJpegComplex::JpegControl *drawCtl=NULL;
859 getDrawingParam(drawSurface,drawCtl);
861 drawCtl->rotation=rotate;
862 drawCtl->mode=cropmode;
863 drawCtl->compressedSize=size;
864 #ifdef DRAWING_THREAD
865 bool ok=drawingThread->start(drawCtl,drawSurface,reader,pictureBack);
867 Log::getInstance()->log("VMediaView::load", Log::ERR, "unable to start drawing thread");
868 pictureError=tr("JpegError");
869 pictureLoading=false;
870 destroyPictureBanner();
875 //here we could hand this over to the drawing thread
876 bool ok=WJpegComplex::drawJpeg(drawCtl,drawSurface,reader,pictureBack);
881 pictureLoading=false;
885 void VMediaView::drawingDone(bool hasError) {
886 pictureLoading=false;
887 destroyPictureBanner(); //disable loading indication (or real banner)
890 Log::getInstance()->log("VMediaView::drawingDone", Log::DEBUG, "success: sfc now=%p",surface);
892 //only show the pictureBanner if it was there before
893 if (havePictureBanner && ! audioEnabled) showPictureBanner();
894 Log::getInstance()->log("VMediaView::load", Log::DEBUG, "success" );
895 updatePictureInfo(); //will only do somethng if pictureEnabled
899 pictureError=tr("JpegError");
900 if (pictureEnabled) {
905 MediaPlayer::getInstance()->closeMediaChannel(1);
906 #ifndef DRAWING_THREAD
907 if (audioEnabled) showAudioBanner();
909 if (slideshow) startSlideshow();
910 BoxStack::getInstance()->update(this);
913 void VMediaView::showPictureBanner(bool loading) {
914 //we are in the main thread - so we can (and must) safely hard destroy/create the banner
915 Timers::getInstance()->cancelTimer(this,2);
916 if (! currentPicture) {
918 destroyPictureBanner(false);
921 if (pictureBanner) destroyPictureBanner(false);
922 if (! pictureEnabled || ! bannerEnabled) return;
923 pictureBanner= new VPictureBanner(loading, slideshow);
924 pictureBanner->fillColour(infoBack);
926 int len=strlen(currentPicture->getFileName())+Media::TIMEBUFLEN+20;
927 char *buf=new char[len];
928 char tbuf[Media::TIMEBUFLEN];
929 SNPRINTF(buf,len,"%c%02ds%c %s %s " ,
933 currentPicture->getTimeString(tbuf),
934 currentPicture->getFileName()
936 pictureBanner->setText(buf);
940 char *buf=new char[strlen(currentPicture->getDisplayName())+50];
941 SNPRINTF(buf,50,"%s %s",tr("Loading"), currentPicture->getDisplayName());
942 pictureBanner->setText(buf);
945 pictureBanner->draw();
946 if (! loading ) Timers::getInstance()->setTimerD(this,2,8);
947 BoxStack::getInstance()->add(pictureBanner);
948 BoxStack::getInstance()->update(pictureBanner);
951 void VMediaView::destroyPictureBanner(bool fromTimer) {
953 if (fromTimer) sendViewMsg(pictureBanner);
954 else BoxStack::getInstance()->remove(pictureBanner);
956 if (! fromTimer) Timers::getInstance()->cancelTimer(this,2);
959 void VMediaView::updatePictureBanner(bool loading) {
960 if (pictureBanner ) {
961 showPictureBanner(loading);
964 void VMediaView::timercall(int clientref) {
965 Log::getInstance()->log("VMediaView::timercall", Log::DEBUG, "id=%d",clientref);
970 sendCommandMsg(AudioPlayer::SHORT_UPDATE);
975 Log::getInstance()->log("VMediaView::timercall", Log::DEBUG, "infoEnd");
978 //we only did show the audio error info if audio is enabled
979 bool stillError=false;
980 if (AudioPlayer * player=getPlayer(false)) {
981 stillError=player->getState()==AudioPlayer::S_ERROR;
984 sendCommandMsg(AudioPlayer::STREAM_END);
990 if (! slideshow) return;
991 Log::getInstance()->log("VMediaView::timercall", Log::DEBUG, "slideshow");
992 sendCommandMsg(EVENT_SLIDESHOW);
995 Log::getInstance()->log("VMediaView::timercall", Log::DEBUG, "pictureBannerEnd");
996 destroyPictureBanner(true);
1002 #define INFOBUF 2000
1003 void VMediaView::showPictureInfo(){
1004 if (! pictureEnabled || audioEnabled) return;
1005 if (info) destroyInfo();
1006 if (! currentPicture) return;
1009 info->setTitleText(currentPicture->getFileName());
1010 info->setDropThrough();
1011 info->setSize(500, 300);
1012 info->createBuffer();
1013 info->setBorderOn(1);
1014 info->setTitleBarOn(1);
1016 if (Video::getInstance()->getFormat() == Video::PAL)
1017 info->setPosition(100, 180);
1019 info->setPosition(100, 150);
1021 char tbuf[Media::TIMEBUFLEN];
1022 //modes should come from mediaoptions...
1023 const char *mode=NULL;
1024 switch (currentControl->mode) {
1025 case WJpegComplex::CROPPERCENT:
1028 case WJpegComplex::LETTER:
1035 const char *dirname=parent->getDirname(MEDIA_TYPE_PICTURE);
1037 const char *prfx="";
1038 if (dirname) ldir=strlen(dirname);
1040 dirname=dirname+ldir-35;
1043 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",
1044 tr("Directory"), prfx,dirname,
1045 tr("Format(px)"),currentControl->picw,currentControl->pich,
1046 tr("Filesize"),currentControl->compressedSize/1000,
1047 tr("Time"),currentPicture->getTimeString(tbuf),
1048 tr("Rotation"),90*currentControl->finalRotation,
1049 tr("Scale"),currentControl->scale,
1050 tr("Picture Mode"),mode );
1051 info->setMainText(buf);
1053 BoxStack::getInstance()->add(info);
1054 BoxStack::getInstance()->update(info);
1055 Timers::getInstance()->setTimerD(this,3,8);
1057 void VMediaView::updatePictureInfo(){
1062 void VMediaView::destroyInfo(bool fromTimer){
1065 Log::getInstance()->log("VMediaView",Log::DEBUG,"remove info %p",info);
1066 BoxStack::getInstance()->remove(info);
1069 Log::getInstance()->log("VMediaView",Log::DEBUG,"trigger remove info %p",info);
1074 if (! fromTimer) Timers::getInstance()->cancelTimer(this,3);
1075 if (! fromTimer) Timers::getInstance()->cancelTimer(this,4);
1078 void VMediaView::sendViewMsg(Boxx *v) {
1079 Message* m = new Message();
1080 m->message = Message::CLOSE_ME;
1081 m->to = BoxStack::getInstance();
1083 m->parameter=(ULONG)v;
1084 MessageQueue::getInstance()->postMessage(m);
1086 void VMediaView::sendCommandMsg(int command) {
1087 Message* m = new Message();
1088 //we misuse PLAYER_EVENT here
1089 m->message = Message::PLAYER_EVENT;
1092 m->parameter = command;
1093 MessageQueue::getInstance()->postMessage(m);
1096 void VMediaView::enableBanner(bool enable) {
1097 bannerEnabled=false;
1098 updatePictureBanner();
1101 void VMediaView::getDrawingParam(Surface *&sfc,WJpegComplex::JpegControl *&c){
1102 if (secondSurface()) {
1103 //we currently display on sfc2
1112 void VMediaView::switchSurface(){
1113 if (secondSurface()) {
1114 //now we switch to sfc1
1116 currentControl=&ctl;
1120 currentControl=&ctl2;
1124 AudioPlayer * VMediaView::getPlayer(bool createIfNeeded)
1126 AudioPlayer* rt=AudioPlayer::getInstance(this,false);
1127 if (! createIfNeeded && rt == NULL) return NULL;
1129 rt=AudioPlayer::getInstance(this);
1135 bool VMediaView::isAudioPlaying() {
1136 Log::getInstance()->log("VMediaView::isPlaying", Log::DEBUG, "rt=%s", justPlaying?"true":"false");
1145 int VMediaView::play(bool all,bool activate,ULONG move,bool showInfo) {
1147 if (getPlayer(false)) getPlayer(false)->stop();
1149 if (currentAudio) delete currentAudio;
1152 currentAudio=parent->getMedia(MEDIA_TYPE_AUDIO,move);
1154 if ( ! currentAudio || ! currentAudio->getURI()) {
1155 Log::getInstance()->log("VMediaView::load", Log::ERR, "no URI in media");
1156 audioError=tr("no audio file");
1157 if (audioEnabled) sendCommandMsg(EVENT_DIRECTORYDONE);
1161 Log::getInstance()->log("VMediaView::load", Log::DEBUG, "filename=%s,p=%p",
1162 currentAudio->getURI()->getName(),this);
1163 int wseq=getPlayer()->play(currentAudio->getURI());
1164 if (getPlayer()->waitForSequence(5,wseq)<0) {
1165 audioError=tr("unable to open audio file");
1172 Log::getInstance()->log("VMediaView", Log::DEBUG, "player started for %s",currentAudio->getURI()->getName());
1174 if (activate && ! audioEnabled){
1175 if (showInfo) retriggerAudioInfo=true;
1179 //avoid duplicate creation of banner and info
1183 if (activate && (! currentPicture || ! pictureShowing)) draw();
1184 BoxStack::getInstance()->update(this);
1188 void VMediaView::showAudioInfo() {
1189 if (! audioEnabled) return;
1190 Timers::getInstance()->cancelTimer(this,4);
1191 Timers::getInstance()->cancelTimer(this,3);
1192 if (info) destroyInfo();
1193 if (! retriggerAudioInfo) return;
1195 bool playerError=getPlayer()->getState()==AudioPlayer::S_ERROR || audioError;
1196 if (! playerError) Timers::getInstance()->setTimerD(this,3,AUDIOBANNER_TIME);
1197 else Timers::getInstance()->setTimerD(this,3,AUDIOERROR_TIME);
1198 if (! playerError) Timers::getInstance()->setTimerD(this,4, 1);
1199 BoxStack::getInstance()->update(info);
1202 void VMediaView::updateAudioInfo() {
1208 void VMediaView::drawAudioInfo(){
1209 Log::getInstance()->log("VMediaView",Log::DEBUG, "draw banner for %p",info);
1210 const char *title=NULL;
1211 char *playerTitle=NULL;
1212 bool playerError=false;
1216 const char *pl=tr("Playlist");
1217 const char *first=NULL;
1218 char *playerInfo=NULL;
1220 if (getPlayer()->getState() == AudioPlayer::S_PLAY) audioError=NULL;
1222 if (! currentAudio && ! audioError) audioError=tr("no audio file");
1224 title=tr("MediaError");
1227 playerTitle=getPlayer()->getTitle();
1228 if (playerTitle) title=playerTitle;
1229 else title=currentAudio->getDisplayName();
1230 num=parent->getNumEntries(MEDIA_TYPE_AUDIO,currentAudio->index);
1231 playerError=getPlayer()->getState() == AudioPlayer::S_ERROR;
1232 //1more line for long dirs
1233 numlines=playall?5:4;
1237 first=tr("Unable to play audio file");
1238 len=strlen(first)+3;
1240 else if (audioError) {
1243 len=strlen(first)+3;
1246 playerInfo=getPlayer()->getID3Info();drawText(tr("Loading"),5,3,DrawStyle::LIGHTTEXT);
1247 len=strlen(currentAudio->getDisplayName())+strlen(pl)+30+strlen(parent->getDirname(MEDIA_TYPE_AUDIO))+Media::TIMEBUFLEN+110;
1249 for (UINT i=0;i<strlen(playerInfo);i++)
1250 if (playerInfo[i] == '\n') numlines++;
1251 len+=strlen(playerInfo);
1260 UINT height=numlines*30+60;
1261 UINT vheight=Video::getInstance()->getScreenHeight();
1262 UINT vwidth=Video::getInstance()->getScreenWidth();
1263 if (height > vheight-2*AUDIOBANNER_BOTTOM_MARGIN)
1264 height=vheight-2*AUDIOBANNER_BOTTOM_MARGIN;
1265 info->setSize(vwidth -2*AUDIOBANNER_X_MARGIN, height);
1266 info->createBuffer();
1267 if (Video::getInstance()->getFormat() == Video::PAL)
1269 info->setPosition(AUDIOBANNER_X_MARGIN, vheight-height-AUDIOBANNER_BOTTOM_MARGIN);
1273 info->setPosition(AUDIOBANNER_X_MARGIN, vheight-height-AUDIOBANNER_BOTTOM_MARGIN);
1276 info->setTitleBarOn(0);
1277 info->setDropThrough();
1279 //set the regions for the closcks and bars on banner
1280 barRegion.x = info->getWidth()-AUDIOBARLEN-20;
1281 barRegion.y = info->getHeight() - 30; // FIXME, need to be - 1? and below?
1282 barRegion.w = info->getWidth()-AUDIOBARLEN+10;
1285 clocksRegion.x = 130;
1286 clocksRegion.y = barRegion.y + 3;
1287 clocksRegion.w = 190;
1288 clocksRegion.h = getFontHeight();
1289 Log::getInstance()->log("VMediaView",Log::DEBUG,"created AudioInfo %p",info);
1290 BoxStack::getInstance()->add(info);
1291 char *buf=new char [len];
1292 if (playerError || audioError) {
1293 SNPRINTF(buf,len,"%s",first);
1296 char tbuf[Media::TIMEBUFLEN];
1298 SNPRINTF(buf,len,"%s\n"
1304 tr("FileName"),currentAudio->getDisplayName(),
1305 pl,num,parent->getNumEntries(MEDIA_TYPE_AUDIO),
1306 tr("Directory"),parent->getDirname(MEDIA_TYPE_AUDIO),
1307 tr("Time"),currentAudio->getTimeString(tbuf));
1310 SNPRINTF(buf,len,"%s\n"
1315 tr("FileName"),currentAudio->getDisplayName(),
1316 tr("Directory"),parent->getDirname(MEDIA_TYPE_AUDIO),
1317 tr("Time"),currentAudio->getTimeString(tbuf));
1320 Log::getInstance()->log("VMediaView",Log::DEBUG,"info: (%d)%s",strlen(buf),buf);
1321 //now the real drawing functions
1324 info->rectangle(0, 0, info->getWidth(), 30, DrawStyle::TITLEBARBACKGROUND);
1325 info->drawText(title, 5, 5, DrawStyle::LIGHTTEXT);
1326 info->drawPara(buf,5,32,DrawStyle::LIGHTTEXT);
1332 int ybottom=info->getHeight();
1333 info->rectangle(0, ybottom - barRegion.h, info->getWidth(), barRegion.h, DrawStyle::TITLEBARBACKGROUND);
1334 bool drawSymbol=true;
1335 switch(getPlayer()->getState()) {
1336 case AudioPlayer::S_PAUSE:
1337 w.nextSymbol = WSymbol::PAUSE;
1339 case AudioPlayer::S_PLAY:
1340 w.nextSymbol = WSymbol::PLAY;
1342 case AudioPlayer::S_DONE:
1344 w.nextSymbol = WSymbol::PLAY;
1348 case AudioPlayer::S_BACK:
1349 w.nextSymbol = WSymbol::FBWD ;
1351 case AudioPlayer::S_FF:
1352 w.nextSymbol = WSymbol::FFWD ;
1359 w.setPosition(x, ybottom-24);
1364 info->rectangle(x, ybottom - 23, 18, 16, DrawStyle::LIGHTTEXT);
1369 if (playerInfo) delete playerInfo;
1370 if (playerTitle) delete playerTitle;
1373 void VMediaView::drawAudioClocks() {
1374 if (! info || ! audioEnabled) return;
1375 Log::getInstance()->log("VMediaView::drawAudioClocks", Log::DEBUG, "");
1376 //draw clocks and bar
1377 info->rectangle(clocksRegion, DrawStyle::TITLEBARBACKGROUND);
1379 time_t currentSec = (time_t)(getPlayer()->getCurrentTimes());
1380 time_t lengthSec=(time_t)(getPlayer()->getSonglen());
1383 /* gmtime_r(¤tSec,&cpos);
1384 gmtime_r(&lengthSec,&slen);*/
1385 cpos.tm_hour=currentSec/3600;
1386 cpos.tm_min=(currentSec-cpos.tm_hour*3600)/60;
1387 cpos.tm_sec=(currentSec-cpos.tm_hour*3600-cpos.tm_min*60);
1388 slen.tm_hour=lengthSec/3600;;
1389 slen.tm_min=(lengthSec-slen.tm_hour*3600)/60;
1390 slen.tm_sec=(lengthSec-slen.tm_hour*3600-slen.tm_min*60);
1393 if (currentSec >= lengthSec)
1395 SNPRINTF(buffer,99, "-:--:-- / -:--:-- %03dk",getPlayer()->getCurrentBitrate()/1000);
1399 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,
1400 getPlayer()->getCurrentBitrate()/1000);
1401 //Log::getInstance()->log("VMediaView", Log::DEBUG, buffer);
1404 info->drawText(buffer, clocksRegion.x, clocksRegion.y, DrawStyle::LIGHTTEXT);
1406 // Draw progress bar
1407 int progBarXbase = 0;
1410 info->rectangle(barRegion.x + progBarXbase, barRegion.y + 3, barlen+10, 24, DrawStyle::LIGHTTEXT);
1411 info->rectangle(barRegion.x + progBarXbase + 2, barRegion.y + 5, barlen+6, 20, barBlue);
1413 if (currentSec > lengthSec) currentSec=lengthSec;
1414 if (lengthSec == 0) return;
1416 // Draw yellow portion
1417 int progressWidth = (barlen+2) * currentSec / lengthSec;
1418 info->rectangle(barRegion.x + progBarXbase + 4, barRegion.y + 7, progressWidth, 16, DrawStyle::SELECTHIGHLIGHT);
1422 void VMediaView::showAudioBanner() {
1423 destroyAudioBanner();
1424 if (! audioEnabled) return;
1425 audioBanner=new VInfo();
1426 Log::getInstance()->log("VMediaView",Log::DEBUG,"creating AudioBanner %p", audioBanner);
1427 Video *v=Video::getInstance();
1428 audioBanner->setSize(v->getScreenWidth()-100, 36);
1429 audioBanner->createBuffer();
1430 audioBanner->setPosition(50, v->getScreenHeight()-50);
1431 audioBanner->fillColour(audioBannerBack);
1432 audioBanner->setTitleBarOn(0);
1433 audioBanner->setDropThrough();
1434 if ( ! currentAudio || ! currentAudio->getDisplayName() || audioError) {
1435 audioBanner->drawText(tr("AudioPlayer - not playing"),5,3,DrawStyle::LIGHTTEXT);
1438 char * buf=new char[strlen(currentAudio->getDisplayName())+50];
1439 SNPRINTF(buf,50,"%s %s",tr("AudioPlayer"),currentAudio->getDisplayName());
1440 audioBanner->drawText(buf,5,3,DrawStyle::LIGHTTEXT);
1443 BoxStack::getInstance()->add(audioBanner);
1444 BoxStack::getInstance()->update(audioBanner);
1447 void VMediaView::destroyAudioBanner() {
1449 Log::getInstance()->log("VMediaView",Log::DEBUG,"deleting AudioBanner %p",audioBanner);
1450 BoxStack::getInstance()->remove(audioBanner);