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, write to the Free Software
18 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
23 #include "vmediaview.h"
25 #include "vpicturebanner.h"
26 #include "vcolourtuner.h"
27 #include "audioplayer.h"
30 #include "wselectlist.h"
41 #include "mediaoptions.h"
42 #include "mediaplayer.h"
46 * the combined user interface for pictures and audio
47 * has 2 surfaces to enable drawing in a separate thread
48 * the info display can either show a picture info or and audio info
49 * if the audio player comes on top it disables the picture info display
50 * basically we have 3 modes:
51 * - HIDDEN viewer is hidden (pictureEnabled=false, audioEnabled=false)
52 * if justPlaying=true the audioplayer is still running
53 * - PICTURE picture viewer on top ("slide show") - pictureEnabled=true, audioEnabled=false
54 * no AudioBanner visible
55 * if justPlaying=true the audio player runs in bg
56 * - AUDIO audioPlayer on top ("playlist mode") - pictureEnabled=true, audioEnabled=true
57 * the picture viewer is currently halted (should be able to continue if no AudioInfo)
58 * no picture banner (we should have an audio banner to indicate the audio player on top!)
59 * state transitions (via setPictureMode, setAudioMode)
60 * - show Picture -> pictureEnabled=true,
61 * only called from command handler if audioEnabled=false
62 * call from mediaList only possible if audioEnabled=false
63 * *** TODO: would be better to have separate function for external call/internal call
64 * - play [Audio] -> if activate is set -> audioEnabled=true (-> Mode AUDIO)
65 * used for calls from medialist
66 * internal calls do not set activate!
67 * - YELLOW key - toggle
68 * if audioActive==true -> audioActive=false (new mode depends on pictureEnabled - either HIDDEN or PICTURE)
69 * if audioActive==false -> audioActive=true (new mode AUDIO)
70 * *** open handling if no audio file there (ignore key???) - currently empty player
71 * - BACK if mode AUDIO -> audioEnabled=false, justPlaying=false
72 * - BACK if mode PICTURE -> pictureEnabled=false;
73 * - player StreamEnd -> audioEnabled=false (new mode depends on pciture - either HIDDEN or PICTURE)
74 * info/banner handling:
75 * - AudioInfo - alternativ to pictureInfo
76 * off when disabling audio or after timer or with OK in AUDIO mode
77 * on currently any command or player event (end/new song) when audioEnabled==true and retriggerAudioInfo=true
78 * on on OK in AUDIO mode
80 * off when disabling Picture or after timer, OK, green
81 * on only on green when pictureEnabled==true
83 * 2 modes: loading/normal
85 * off when disabling picture, OK, after timer
86 * on when enabling picture, with OK
88 * off when disabling picture and when loading done
90 * *** open: do not show when audio enabled and slide show running (currently slideshow is paused)
92 * always shown when audioEnabled=true
93 * on when enabling Audio
94 * off when disabling audio
96 * timers: 1 - slide show
99 * 4 - audio short update
102 #define DRAWING_THREAD
104 Colour VMediaView::pictureBack=Colour(140,140,140);
105 Colour VMediaView::infoBack=Colour(110,110,110);
106 Colour VMediaView::audioBannerBack=Colour(110,110,110,20);
109 //how long do we wait for a single picture chunk
110 //the unit is 100ms (the timeout on the server side)
112 class VPreader : public JpegReader {
119 VPreader(VMediaView *p,ImageReader *r){
125 virtual ULONG readChunk(ULONG offset,ULONG len,char ** buf) {
126 Log::getInstance()->log("VMediaView::jpegReader", Log::DEBUG, "read chunk o=%d,len=%d,buf=%p",
130 for (int trycount=0;trycount < MAXTRY && ! dobreak && numrec == 0 && rt == 0;trycount++) {
136 rt=reader->getImageChunk((ULLONG)offset,(UINT)len,&numrec,(UCHAR **)buf);
139 Log::getInstance()->log("VMediaView::jpegReader", Log::DEBUG, "got n=%d,buf=%p,rt=%d",
143 virtual int initRead(const MediaURI *uri,ULLONG *sz,ULONG factor=100) {
144 Log::getInstance()->log("VMediaView::jpegReader", Log::DEBUG, "load image %s",uri->getDisplayName());
145 Video* video = Video::getInstance();
147 int rt=MediaPlayer::getInstance()->openMedium(1,uri,sz,video->getScreenWidth()*factor/100, video->getScreenHeight()*factor/100);
150 Log::getInstance()->log("VMediaView::jpegReader", Log::DEBUG, "load image %s returned %llu",uri->getDisplayName(),size);
153 virtual ULONG getSize() {return size;}
154 //seems to be thread safe (more or less...)
161 * a separate thread for drawing pictures
162 * will be started with the sfc and the draw control
163 * will send a player event when done
165 class DrawingThread : public Thread_TYPE {
169 WJpeg::JpegControl *_ctl;
174 DrawingThread(VMediaView *parent) {
181 virtual ~DrawingThread(){}
183 return (threadIsActive()!=0);
185 bool start(WJpeg::JpegControl *ctl,Surface *sfc,VPreader *reader,Colour &c) {
194 return (threadStart() == 1);
197 Log::getInstance()->log("DrawingThread",Log::DEBUG,"stop initiated");
198 if (threadIsActive()) {
200 if (_reader) _reader->setBreak();
203 Log::getInstance()->log("DrawingThread",Log::DEBUG,"stop done");
208 virtual void threadMethod() {
209 Log::getInstance()->log("DrawingThread",Log::DEBUG,"started");
210 bool rt=WJpeg::drawJpeg(_ctl,_sfc,_reader,_colour);
212 if (! _interrupted) {
213 Message* m = new Message();
214 //we misuse PLAYER_EVENT here
215 m->message = Message::PLAYER_EVENT;
218 m->parameter= rt?VMediaView::EVENT_DRAWINGDONE:VMediaView::EVENT_DRAWINGERROR;
219 Command::getInstance()->postMessageFromOuterSpace(m);
221 Log::getInstance()->log("DrawingThread",Log::DEBUG,"finishing interrupt=%d",(int)_interrupted);
223 virtual void threadPostStopCleanup() {}
228 VMediaView::VMediaView(VMediaList *p)
230 Log::getInstance()->log("VMediaView::VMediaView", Log::DEBUG, "p=%p", this);
232 pictureEnabled=false;
234 ireader=new ImageReader(1,MediaPlayer::getInstance());
235 reader=new VPreader(this,ireader);
236 //the surface handling
237 Video* video = Video::getInstance();
238 setSize(video->getScreenWidth(), video->getScreenHeight());
242 //create the second surface
247 //disable the surface
256 pictureLoading=false;
259 showtime=INITIAL_SHOWTIME;
262 options=MediaOptions::getInstance();
263 VColourTuner::initFactors();
264 int st=options->getIntOption("SlideShowInterval");
265 if (st > 0) showtime=st;
266 ctl.area.w=originalw;
267 ctl.area.h=originalh;
269 ctl.scaleafter=options->getIntOption("ScaleFactor");
270 const char * mode=options->getStringOption("PictureMode");
271 if (strcmp(mode,"clip") == 0) ctl.mode=WJpeg::CROP;
272 else if (strcmp(mode,"letter") == 0) ctl.mode=WJpeg::LETTER;
273 else if (strcmp(mode,"clipfactor") == 0) ctl.mode=WJpeg::CROPPERCENT;
274 ctl.scaleAmount=options->getIntOption("PictureSize");
275 if (ctl.scaleAmount < 10) ctl.scaleAmount=10;
276 if (ctl.scaleAmount > 200) ctl.scaleAmount=200;
279 //current control is the one used for DISPLAY (not the one for drawing - this is the other one)
280 //this is closely coupled to the surface - i.e. ctl is for sfc1, ctl2 for sfc2
283 pictureShowing=false;
287 barBlue.set(0, 0, 150, 150);
292 retriggerAudioInfo=false;
294 drawingThread=new DrawingThread(this);
297 VMediaView::~VMediaView()
299 Log::getInstance()->log("VMediaView::~VMediaView", Log::DEBUG, "p=%p,secondSfc=%s", this,(secondSurface()?"true":"false"));
300 destroyPictureBanner();
301 if (currentPicture) delete currentPicture;
302 Timers::getInstance()->cancelTimer(this,1);
303 Timers::getInstance()->cancelTimer(this,2);
304 Timers::getInstance()->cancelTimer(this,3);
305 Timers::getInstance()->cancelTimer(this,4);
306 Timers::getInstance()->cancelTimer(this,5);
308 destroyAudioBanner();
309 if (getPlayer(false)) {
310 AudioPlayer::getInstance(NULL,false)->shutdown();
312 if (currentAudio) delete currentAudio;
313 drawingThread->stop();
317 if (secondSurface()) {
325 MediaPlayer::getInstance()->closeMediaChannel(1);
326 MediaPlayer::getInstance()->closeMediaChannel(2);
327 Log::getInstance()->log("VMediaView::~VMediaView", Log::DEBUG, "done p=%p", this);
330 void VMediaView::setPictureMode(bool act) {
331 Log::getInstance()->log("VMediaView", Log::DEBUG, "set pictureMode %d p=%d, a=%d", (int)act,(int)pictureEnabled,(int)audioEnabled);
333 if ( ! pictureEnabled && ! audioEnabled) {
339 if (! pictureEnabled) {
340 //we newly enable the picture - so clear the screen
342 BoxStack::getInstance()->update(this);
343 if (slideshow) startSlideshow();
348 if ( pictureEnabled) {
349 destroyPictureBanner();
350 stopSlideshow(false);
351 #ifdef DRAWING_THREAD
352 drawingThread->stop();
354 if (! audioEnabled) {
362 pictureShowing=false;
367 //we can disable audio without automatically hiding us
368 //this will become strange - we are visible but do not handle
369 //keys - so if you call setAudioMode(false,false) be sure
370 //to call setAudioMode(false,true) afterwards
371 //it is only here to give the list a chance to start a new play
372 //when we call directoryDone
373 void VMediaView::setAudioMode(bool act,bool doHiding) {
374 Log::getInstance()->log("VMediaView", Log::DEBUG, "setAudioMode %d p=%d, a=%d", (int)act,(int)pictureEnabled,(int)audioEnabled);
376 if (! audioEnabled) {
377 if (! pictureEnabled) {
380 draw(); //empty screen if no picture
383 destroyPictureBanner();
395 destroyAudioBanner();
397 if (! pictureEnabled && doHiding) {
405 if (havePictureBanner && ! pictureBanner) showPictureBanner();
412 void VMediaView::draw()
414 Log::getInstance()->log("VMediaView::draw", Log::DEBUG, "pictureError=%s,p=%p", pictureError,this);
416 if (pictureShowing ) fillColour(pictureBack);
417 else fillColour(Colour::BLACK);
419 drawText(pictureError,100,area.h/2,Colour::LIGHTTEXT);
425 int VMediaView::handleCommand(int command)
427 Log::getInstance()->log("VMediaView::handleCommand", Log::DEBUG, "cmd=%d,p=%p", command,this);
428 if ( !audioEnabled && ! pictureEnabled ) {
430 if (command == Remote::YELLOW) {
436 if ( ! audioEnabled) {
437 //------------------------- command in mode PICTURE (i.e. picture is on top) ----------------
444 case Remote::SKIPBACK:
446 showPicture(VMediaList::MV_PREV,slideshow,true);
449 case Remote::FORWARD:
450 if (showtime > 1) showtime--;
451 updatePictureBanner(true);
453 case Remote::DF_DOWN:
455 case Remote::SKIPFORWARD:
457 showPicture(VMediaList::MV_NEXT,slideshow,true);
460 case Remote::REVERSE:
461 if (showtime < 50 ) showtime++;
462 updatePictureBanner(true);
467 destroyPictureBanner();
469 havePictureBanner=false;
472 havePictureBanner=true;
473 showPictureBanner(pictureLoading);
482 showPicture(VMediaList::MV_NEXT,slideshow,true);
489 updatePictureBanner();
494 showPicture(VMediaList::MV_NEXT,slideshow,true);
500 showtime=INITIAL_SHOWTIME;
501 updatePictureBanner();
507 rotate=WJpeg::ROT_90;
510 rotate=WJpeg::ROT_180;
513 rotate=WJpeg::ROT_270;
519 showPicture(VMediaList::MV_NONE,slideshow,true);
523 if (info) destroyInfo();
524 else showPictureInfo();
530 cropmode=WJpeg::LETTER;
533 cropmode=WJpeg::CROPPERCENT;
536 cropmode=WJpeg::CROP;
539 showPicture(VMediaList::MV_NONE,slideshow,true);
544 destroyPictureBanner();
546 VColourTuner *ct=new VColourTuner();
547 BoxStack::getInstance()->add(ct);
549 BoxStack::getInstance()->update(ct);
554 setPictureMode(false);
568 bool updateInfo=false;
569 //------------------------- command in mode AUDIO (i.e. audio is on top) ----------------
578 play(playall,false,VMediaList::MV_PREV);
581 case Remote::FORWARD:
582 if (! audioError) getPlayer()->fastForward();
586 case Remote::DF_DOWN:
588 play(playall,false,VMediaList::MV_NEXT);
591 case Remote::SKIPFORWARD:
592 if (! audioError) getPlayer()->skipForward(10);
595 case Remote::SKIPBACK:
596 if (! audioError) getPlayer()->skipBackward(10);
599 case Remote::REVERSE:
603 if (! audioError) getPlayer()->jumpToPercent(0);
607 if (! audioError) getPlayer()->jumpToPercent(10);
611 if (! audioError) getPlayer()->jumpToPercent(20);
615 if (! audioError) getPlayer()->jumpToPercent(30);
619 if (! audioError) getPlayer()->jumpToPercent(40);
623 if (! audioError) getPlayer()->jumpToPercent(50);
627 if (! audioError) getPlayer()->jumpToPercent(60);
631 if (! audioError) getPlayer()->jumpToPercent(70);
635 if (! audioError) getPlayer()->jumpToPercent(80);
639 if (! audioError) getPlayer()->jumpToPercent(90);
647 retriggerAudioInfo=false;
650 retriggerAudioInfo=true;
653 if (getPlayer()->getState() == AudioPlayer::S_ERROR) {
654 if (playall) play(playall,false,VMediaList::MV_NEXT);
661 if (! audioError) getPlayer()->unpause();
663 if (getPlayer()->getState() != AudioPlayer::S_ERROR) ;
664 else if (playall) play(playall,false,VMediaList::MV_NEXT);
669 if (! audioError) getPlayer()->pause();
683 retriggerAudioInfo=false;
686 if (! pictureShowing) setPictureMode(false); //could have been delayed
691 if (audioEnabled && updateInfo) updateAudioInfo();
696 void VMediaView::processMessage(Message* m)
698 Log::getInstance()->log("VMediaView::processMessage", Log::DEBUG, "cmd=%lu,p=%lu", m->message,m->parameter);
699 if (m->message == Message::MOUSE_MOVE)
703 else if (m->message == Message::MOUSE_LBDOWN)
706 //check if press is outside this view! then simulate cancel
707 int x=(m->parameter>>16)-getScreenX();
708 int y=(m->parameter&0xFFFF)-getScreenY();
709 if (x<0 || y <0 || x>(int)getWidth() || y>(int)getHeight())
711 BoxStack::getInstance()->handleCommand(Remote::BACK); //simulate cancel press
714 else if (m->message = Message::PLAYER_EVENT) {
715 switch (m->parameter) {
716 case EVENT_SLIDESHOW:
717 if (! pictureEnabled) break; //old timer msg...
718 //if (! audioEnabled) {
722 showPicture(VMediaList::MV_NEXT,true,false);
727 case EVENT_DRAWINGERROR:
730 case EVENT_DRAWINGDONE:
733 case EVENT_DIRECTORYDONE:
734 //just disable audio without (poetntially) hiding us
735 //this gives the list a chance to decide whether audio
736 //should be on top afterwards without showing the list
738 setAudioMode(false,false);
739 parent->directoryDone();
740 if (! pictureShowing) setPictureMode(false);
741 if (! justPlaying) setAudioMode(false);
743 case AudioPlayer::STREAM_ERR:
744 case AudioPlayer::STREAM_END:
745 if (playall) play(playall,false,VMediaList::MV_NEXT);
751 case AudioPlayer::SHORT_UPDATE:
752 if (info && audioEnabled ) {
754 BoxStack::getInstance()->update(info,&clocksRegion);
755 BoxStack::getInstance()->update(info,&barRegion);
756 Timers::getInstance()->setTimerD(this, 4, 1);
759 case AudioPlayer::NEW_SONG:
760 if (audioEnabled) updateAudioInfo();
762 case AudioPlayer::CONNECTION_LOST:
763 if (audioEnabled) destroyInfo();
764 if (AudioPlayer *player=getPlayer(false)) {
768 Command::getInstance()->connectionLost();
775 VMediaView * VMediaView::createViewer(VMediaList * mparent) {
776 Log::getInstance()->log("VMediaView::createViewer", Log::DEBUG, "p=%p",
778 VMediaView *vmn=new VMediaView(mparent);
779 BoxStack::getInstance()->add(vmn);
781 BoxStack::getInstance()->update(vmn);
785 void VMediaView::startSlideshow() {
787 if (! pictureLoading) Timers::getInstance()->setTimerD(this,1,showtime);
790 void VMediaView::stopSlideshow(bool hard) {
791 if (hard) slideshow=false;
792 Timers::getInstance()->cancelTimer(this,1);
796 void VMediaView::showPicture(ULONG move,bool bslideshow,bool activateBanner) {
798 setPictureMode(true);
800 #ifdef DRAWING_THREAD
801 drawingThread->stop();
803 slideshow=bslideshow;
804 Media *newPicture=parent->getMedia(MEDIA_TYPE_PICTURE,move);
806 pictureShowing=false;
808 //within the event handler first directoryDone is called
809 //and afterwards everything is stopped if nothing new
810 sendCommandMsg(EVENT_DIRECTORYDONE);
817 if (currentPicture) {
818 delete currentPicture;
821 currentPicture=newPicture;
822 loadPicture(currentPicture,activateBanner);
826 //the real picture drawing method
827 //will start drawing on a new surface and will switch it wehn done
829 int VMediaView::loadPicture(Media *md,bool activateBanner) {
832 if (! md->getURI()) {
833 pictureError=tr("No media found");
834 Log::getInstance()->log("VMediaView::load",Log::ERR,"no URI in media");
837 Log::getInstance()->log("VMediaView::load", Log::DEBUG, "filename=%s,p=%p",
838 md->getURI()->getName(),this);
839 //do we have a pictureBanner?
840 havePictureBanner=pictureBanner!=NULL || activateBanner;
842 #ifdef DRAWING_THREAD
843 if (!audioEnabled && havePictureBanner ) showPictureBanner(true);
844 drawingThread->stop();
846 showPictureBanner(true);
850 int rtok=reader->initRead(md->getURI(),&size,ctl.scaleAmount); //scaleAmount is the same for both...
852 //now we can really draw
853 //get the surface for drawing
854 Surface * drawSurface=NULL;
855 WJpeg::JpegControl *drawCtl=NULL;
856 getDrawingParam(drawSurface,drawCtl);
858 drawCtl->rotation=rotate;
859 drawCtl->mode=cropmode;
860 drawCtl->compressedSize=size;
861 #ifdef DRAWING_THREAD
862 bool ok=drawingThread->start(drawCtl,drawSurface,reader,pictureBack);
864 Log::getInstance()->log("VMediaView::load", Log::ERR, "unable to start drawing thread");
865 pictureError=tr("JpegError");
866 pictureLoading=false;
867 destroyPictureBanner();
872 //here we could hand this over to the drawing thread
873 bool ok=WJpeg::drawJpeg(drawCtl,drawSurface,reader,pictureBack);
878 pictureLoading=false;
882 void VMediaView::drawingDone(bool hasError) {
883 pictureLoading=false;
884 destroyPictureBanner(); //disable loading indication (or real banner)
887 Log::getInstance()->log("VMediaView::drawingDone", Log::DEBUG, "success: sfc now=%p",surface);
889 //only show the pictureBanner if it was there before
890 if (havePictureBanner && ! audioEnabled) showPictureBanner();
891 Log::getInstance()->log("VMediaView::load", Log::DEBUG, "success" );
892 updatePictureInfo(); //will only do somethng if pictureEnabled
896 pictureError=tr("JpegError");
897 if (pictureEnabled) {
902 MediaPlayer::getInstance()->closeMediaChannel(1);
903 #ifndef DRAWING_THREAD
904 if (audioEnabled) showAudioBanner();
906 if (slideshow) startSlideshow();
907 BoxStack::getInstance()->update(this);
910 void VMediaView::showPictureBanner(bool loading) {
911 //we are in the main thread - so we can (and must) safely hard destroy/create the banner
912 Timers::getInstance()->cancelTimer(this,2);
913 if (! currentPicture) {
915 destroyPictureBanner(false);
918 if (pictureBanner) destroyPictureBanner(false);
919 if (! pictureEnabled || ! bannerEnabled) return;
920 pictureBanner= new VPictureBanner(loading, slideshow);
921 pictureBanner->fillColour(infoBack);
923 int len=strlen(currentPicture->getFileName())+Media::TIMEBUFLEN+20;
924 char *buf=new char[len];
925 char tbuf[Media::TIMEBUFLEN];
926 SNPRINTF(buf,len,"%c%02ds%c %s %s " ,
930 currentPicture->getTimeString(tbuf),
931 currentPicture->getFileName()
933 pictureBanner->setText(buf);
937 char *buf=new char[strlen(currentPicture->getDisplayName())+50];
938 SNPRINTF(buf,50,"%s %s",tr("Loading"), currentPicture->getDisplayName());
939 pictureBanner->setText(buf);
942 pictureBanner->draw();
943 if (! loading ) Timers::getInstance()->setTimerD(this,2,8);
944 BoxStack::getInstance()->add(pictureBanner);
945 BoxStack::getInstance()->update(pictureBanner);
948 void VMediaView::destroyPictureBanner(bool fromTimer) {
950 if (fromTimer) sendViewMsg(pictureBanner);
951 else BoxStack::getInstance()->remove(pictureBanner);
953 if (! fromTimer) Timers::getInstance()->cancelTimer(this,2);
956 void VMediaView::updatePictureBanner(bool loading) {
957 if (pictureBanner ) {
958 showPictureBanner(loading);
961 void VMediaView::timercall(int clientref) {
962 Log::getInstance()->log("VMediaView::timercall", Log::DEBUG, "id=%d",clientref);
967 sendCommandMsg(AudioPlayer::SHORT_UPDATE);
972 Log::getInstance()->log("VMediaView::timercall", Log::DEBUG, "infoEnd");
975 //we only did show the audio error info if audio is enabled
976 bool stillError=false;
977 if (AudioPlayer * player=getPlayer(false)) {
978 stillError=player->getState()==AudioPlayer::S_ERROR;
981 sendCommandMsg(AudioPlayer::STREAM_END);
987 if (! slideshow) return;
988 Log::getInstance()->log("VMediaView::timercall", Log::DEBUG, "slideshow");
989 sendCommandMsg(EVENT_SLIDESHOW);
992 Log::getInstance()->log("VMediaView::timercall", Log::DEBUG, "pictureBannerEnd");
993 destroyPictureBanner(true);
1000 void VMediaView::showPictureInfo(){
1001 if (! pictureEnabled || audioEnabled) return;
1002 if (info) destroyInfo();
1003 if (! currentPicture) return;
1006 info->setTitleText(currentPicture->getFileName());
1007 info->setDropThrough();
1008 info->setSize(500, 300);
1009 info->createBuffer();
1010 info->setBorderOn(1);
1011 info->setTitleBarOn(1);
1013 if (Video::getInstance()->getFormat() == Video::PAL)
1014 info->setPosition(100, 180);
1016 info->setPosition(100, 150);
1018 char tbuf[Media::TIMEBUFLEN];
1019 //modes should come from mediaoptions...
1020 const char *mode=NULL;
1021 switch (currentControl->mode) {
1022 case WJpeg::CROPPERCENT:
1032 const char *dirname=parent->getDirname(MEDIA_TYPE_PICTURE);
1034 const char *prfx="";
1035 if (dirname) ldir=strlen(dirname);
1037 dirname=dirname+ldir-35;
1040 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",
1041 tr("Directory"), prfx,dirname,
1042 tr("Format(px)"),currentControl->picw,currentControl->pich,
1043 tr("Filesize"),currentControl->compressedSize/1000,
1044 tr("Time"),currentPicture->getTimeString(tbuf),
1045 tr("Rotation"),90*currentControl->finalRotation,
1046 tr("Scale"),currentControl->scale,
1047 tr("Picture Mode"),mode );
1048 info->setMainText(buf);
1050 BoxStack::getInstance()->add(info);
1051 BoxStack::getInstance()->update(info);
1052 Timers::getInstance()->setTimerD(this,3,8);
1054 void VMediaView::updatePictureInfo(){
1059 void VMediaView::destroyInfo(bool fromTimer){
1062 Log::getInstance()->log("VMediaView",Log::DEBUG,"remove info %p",info);
1063 BoxStack::getInstance()->remove(info);
1066 Log::getInstance()->log("VMediaView",Log::DEBUG,"trigger remove info %p",info);
1071 if (! fromTimer) Timers::getInstance()->cancelTimer(this,3);
1072 if (! fromTimer) Timers::getInstance()->cancelTimer(this,4);
1075 void VMediaView::sendViewMsg(Boxx *v) {
1076 Message* m = new Message();
1077 m->message = Message::CLOSE_ME;
1078 m->to = BoxStack::getInstance();
1080 m->parameter=(ULONG)v;
1081 Command::getInstance()->postMessageFromOuterSpace(m);
1083 void VMediaView::sendCommandMsg(int command) {
1084 Message* m = new Message();
1085 //we misuse PLAYER_EVENT here
1086 m->message = Message::PLAYER_EVENT;
1089 m->parameter= command;
1090 Command::getInstance()->postMessageFromOuterSpace(m);
1093 void VMediaView::enableBanner(bool enable) {
1094 bannerEnabled=false;
1095 updatePictureBanner();
1098 void VMediaView::getDrawingParam(Surface *&sfc,WJpeg::JpegControl *&c){
1099 if (secondSurface()) {
1100 //we currently display on sfc2
1109 void VMediaView::switchSurface(){
1110 if (secondSurface()) {
1111 //now we switch to sfc1
1113 currentControl=&ctl;
1117 currentControl=&ctl2;
1121 AudioPlayer * VMediaView::getPlayer(bool createIfNeeded)
1123 AudioPlayer* rt=AudioPlayer::getInstance(this,false);
1124 if (! createIfNeeded && rt == NULL) return NULL;
1126 rt=AudioPlayer::getInstance(this);
1132 bool VMediaView::isAudioPlaying() {
1133 Log::getInstance()->log("VMediaView::isPlaying", Log::DEBUG, "rt=%s", justPlaying?"true":"false");
1142 int VMediaView::play(bool all,bool activate,ULONG move,bool showInfo) {
1144 if (getPlayer(false)) getPlayer(false)->stop();
1146 if (currentAudio) delete currentAudio;
1149 currentAudio=parent->getMedia(MEDIA_TYPE_AUDIO,move);
1151 if ( ! currentAudio || ! currentAudio->getURI()) {
1152 Log::getInstance()->log("VMediaView::load", Log::ERR, "no URI in media");
1153 audioError=tr("no audio file");
1154 if (audioEnabled) sendCommandMsg(EVENT_DIRECTORYDONE);
1158 Log::getInstance()->log("VMediaView::load", Log::DEBUG, "filename=%s,p=%p",
1159 currentAudio->getURI()->getName(),this);
1160 int wseq=getPlayer()->play(currentAudio->getURI());
1161 if (getPlayer()->waitForSequence(5,wseq)<0) {
1162 audioError=tr("unable to open audio file");
1169 Log::getInstance()->log("VMediaView", Log::DEBUG, "player started for %s",currentAudio->getURI()->getName());
1171 if (activate && ! audioEnabled){
1172 if (showInfo) retriggerAudioInfo=true;
1176 //avoid duplicate creation of banner and info
1180 if (activate && (! currentPicture || ! pictureShowing)) draw();
1181 BoxStack::getInstance()->update(this);
1185 void VMediaView::showAudioInfo() {
1186 if (! audioEnabled) return;
1187 Timers::getInstance()->cancelTimer(this,4);
1188 Timers::getInstance()->cancelTimer(this,3);
1189 if (info) destroyInfo();
1190 if (! retriggerAudioInfo) return;
1192 bool playerError=getPlayer()->getState()==AudioPlayer::S_ERROR || audioError;
1193 if (! playerError) Timers::getInstance()->setTimerD(this,3,AUDIOBANNER_TIME);
1194 else Timers::getInstance()->setTimerD(this,3,AUDIOERROR_TIME);
1195 if (! playerError) Timers::getInstance()->setTimerD(this,4, 1);
1196 BoxStack::getInstance()->update(info);
1199 void VMediaView::updateAudioInfo() {
1205 void VMediaView::drawAudioInfo(){
1206 Log::getInstance()->log("VMediaView",Log::DEBUG, "draw banner for %p",info);
1207 const char *title=NULL;
1208 char *playerTitle=NULL;
1209 bool playerError=false;
1213 const char *pl=tr("Playlist");
1214 const char *first=NULL;
1215 char *playerInfo=NULL;
1217 if (getPlayer()->getState() == AudioPlayer::S_PLAY) audioError=NULL;
1219 if (! currentAudio && ! audioError) audioError=tr("no audio file");
1221 title=tr("MediaError");
1224 playerTitle=getPlayer()->getTitle();
1225 if (playerTitle) title=playerTitle;
1226 else title=currentAudio->getDisplayName();
1227 num=parent->getNumEntries(MEDIA_TYPE_AUDIO,currentAudio->index);
1228 playerError=getPlayer()->getState() == AudioPlayer::S_ERROR;
1229 //1more line for long dirs
1230 numlines=playall?5:4;
1234 first=tr("Unable to play audio file");
1235 len=strlen(first)+3;
1237 else if (audioError) {
1240 len=strlen(first)+3;
1243 playerInfo=getPlayer()->getID3Info();drawText(tr("Loading"),5,3,Colour::LIGHTTEXT);
1244 len=strlen(currentAudio->getDisplayName())+strlen(pl)+30+strlen(parent->getDirname(MEDIA_TYPE_AUDIO))+Media::TIMEBUFLEN+110;
1246 for (UINT i=0;i<strlen(playerInfo);i++)
1247 if (playerInfo[i] == '\n') numlines++;
1248 len+=strlen(playerInfo);
1257 UINT height=numlines*30+60;
1258 UINT vheight=Video::getInstance()->getScreenHeight();
1259 UINT vwidth=Video::getInstance()->getScreenWidth();
1260 if (height > vheight-2*AUDIOBANNER_BOTTOM_MARGIN)
1261 height=vheight-2*AUDIOBANNER_BOTTOM_MARGIN;
1262 info->setSize(vwidth -2*AUDIOBANNER_X_MARGIN, height);
1263 info->createBuffer();
1264 if (Video::getInstance()->getFormat() == Video::PAL)
1266 info->setPosition(AUDIOBANNER_X_MARGIN, vheight-height-AUDIOBANNER_BOTTOM_MARGIN);
1270 info->setPosition(AUDIOBANNER_X_MARGIN, vheight-height-AUDIOBANNER_BOTTOM_MARGIN);
1273 info->setTitleBarOn(0);
1274 info->setDropThrough();
1276 //set the regions for the closcks and bars on banner
1277 barRegion.x = info->getWidth()-AUDIOBARLEN-20;
1278 barRegion.y = info->getHeight() - 30; // FIXME, need to be - 1? and below?
1279 barRegion.w = info->getWidth()-AUDIOBARLEN+10;
1282 clocksRegion.x = 130;
1283 clocksRegion.y = barRegion.y + 3;
1284 clocksRegion.w = 190;
1285 clocksRegion.h = surface->getFontHeight();
1286 Log::getInstance()->log("VMediaView",Log::DEBUG,"created AudioInfo %p",info);
1287 BoxStack::getInstance()->add(info);
1288 char *buf=new char [len];
1289 if (playerError || audioError) {
1290 SNPRINTF(buf,len,"%s",first);
1293 char tbuf[Media::TIMEBUFLEN];
1295 SNPRINTF(buf,len,"%s\n"
1301 tr("FileName"),currentAudio->getDisplayName(),
1302 pl,num,parent->getNumEntries(MEDIA_TYPE_AUDIO),
1303 tr("Directory"),parent->getDirname(MEDIA_TYPE_AUDIO),
1304 tr("Time"),currentAudio->getTimeString(tbuf));
1307 SNPRINTF(buf,len,"%s\n"
1312 tr("FileName"),currentAudio->getDisplayName(),
1313 tr("Directory"),parent->getDirname(MEDIA_TYPE_AUDIO),
1314 tr("Time"),currentAudio->getTimeString(tbuf));
1317 Log::getInstance()->log("VMediaView",Log::DEBUG,"info: (%d)%s",strlen(buf),buf);
1318 //now the real drawing functions
1321 info->rectangle(0, 0, info->getWidth(), 30, Colour::TITLEBARBACKGROUND);
1322 info->drawText(title, 5, 5, Colour::LIGHTTEXT);
1323 info->drawPara(buf,5,32,Colour::LIGHTTEXT);
1329 int ybottom=info->getHeight();
1330 info->rectangle(0, ybottom - barRegion.h, info->getWidth(), barRegion.h, Colour::TITLEBARBACKGROUND);
1331 bool drawSymbol=true;
1332 switch(getPlayer()->getState()) {
1333 case AudioPlayer::S_PAUSE:
1334 w.nextSymbol = WSymbol::PAUSE;
1336 case AudioPlayer::S_PLAY:
1337 w.nextSymbol = WSymbol::PLAY;
1339 case AudioPlayer::S_DONE:
1341 w.nextSymbol = WSymbol::PLAY;
1345 case AudioPlayer::S_BACK:
1346 w.nextSymbol = WSymbol::FBWD ;
1348 case AudioPlayer::S_FF:
1349 w.nextSymbol = WSymbol::FFWD ;
1356 w.setPosition(x, ybottom-24);
1361 info->rectangle(x, ybottom - 23, 18, 16, Colour::LIGHTTEXT);
1366 if (playerInfo) delete playerInfo;
1367 if (playerTitle) delete playerTitle;
1370 void VMediaView::drawAudioClocks() {
1371 if (! info || ! audioEnabled) return;
1372 Log::getInstance()->log("VMediaView::drawAudioClocks", Log::DEBUG, "");
1373 //draw clocks and bar
1374 info->rectangle(clocksRegion, Colour::TITLEBARBACKGROUND);
1376 time_t currentSec = (time_t)(getPlayer()->getCurrentTimes());
1377 time_t lengthSec=(time_t)(getPlayer()->getSonglen());
1380 /* gmtime_r(¤tSec,&cpos);
1381 gmtime_r(&lengthSec,&slen);*/
1382 cpos.tm_hour=currentSec/3600;
1383 cpos.tm_min=(currentSec-cpos.tm_hour*3600)/60;
1384 cpos.tm_sec=(currentSec-cpos.tm_hour*3600-cpos.tm_min*60);
1385 slen.tm_hour=lengthSec/3600;;
1386 slen.tm_min=(lengthSec-slen.tm_hour*3600)/60;
1387 slen.tm_sec=(lengthSec-slen.tm_hour*3600-slen.tm_min*60);
1390 if (currentSec >= lengthSec)
1392 SNPRINTF(buffer,99, "-:--:-- / -:--:-- %03dk",getPlayer()->getCurrentBitrate()/1000);
1396 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,
1397 getPlayer()->getCurrentBitrate()/1000);
1398 //Log::getInstance()->log("VMediaView", Log::DEBUG, buffer);
1401 info->drawText(buffer, clocksRegion.x, clocksRegion.y, Colour::LIGHTTEXT);
1403 // Draw progress bar
1404 int progBarXbase = 0;
1407 info->rectangle(barRegion.x + progBarXbase, barRegion.y + 3, barlen+10, 24, Colour::LIGHTTEXT);
1408 info->rectangle(barRegion.x + progBarXbase + 2, barRegion.y + 5, barlen+6, 20, barBlue);
1410 if (currentSec > lengthSec) currentSec=lengthSec;
1411 if (lengthSec == 0) return;
1413 // Draw yellow portion
1414 int progressWidth = (barlen+2) * currentSec / lengthSec;
1415 info->rectangle(barRegion.x + progBarXbase + 4, barRegion.y + 7, progressWidth, 16, Colour::SELECTHIGHLIGHT);
1419 void VMediaView::showAudioBanner() {
1420 destroyAudioBanner();
1421 if (! audioEnabled) return;
1422 audioBanner=new VInfo();
1423 Log::getInstance()->log("VMediaView",Log::DEBUG,"creating AudioBanner %p", audioBanner);
1424 Video *v=Video::getInstance();
1425 audioBanner->setSize(v->getScreenWidth()-100, 36);
1426 audioBanner->createBuffer();
1427 audioBanner->setPosition(50, v->getScreenHeight()-50);
1428 audioBanner->fillColour(audioBannerBack);
1429 audioBanner->setTitleBarOn(0);
1430 audioBanner->setDropThrough();
1431 if ( ! currentAudio || ! currentAudio->getDisplayName() || audioError) {
1432 audioBanner->drawText(tr("AudioPlayer - not playing"),5,3,Colour::LIGHTTEXT);
1435 char * buf=new char[strlen(currentAudio->getDisplayName())+50];
1436 SNPRINTF(buf,50,"%s %s",tr("AudioPlayer"),currentAudio->getDisplayName());
1437 audioBanner->drawText(buf,5,3,Colour::LIGHTTEXT);
1440 BoxStack::getInstance()->add(audioBanner);
1441 BoxStack::getInstance()->update(audioBanner);
1444 void VMediaView::destroyAudioBanner() {
1446 Log::getInstance()->log("VMediaView",Log::DEBUG,"deleting AudioBanner %p",audioBanner);
1447 BoxStack::getInstance()->remove(audioBanner);