]> git.vomp.tv Git - vompclient-marten.git/blob - vmedialist.cc
Live radio prebuffering display - code upgrades for connection lost handling
[vompclient-marten.git] / vmedialist.cc
1 /*
2     Copyright 2004-2005 Chris Tallon, Andreas Vogel
3
4     This file is part of VOMP.
5
6     VOMP is free software; you can redistribute it and/or modify
7     it under the terms of the GNU General Public License as published by
8     the Free Software Foundation; either version 2 of the License, or
9     (at your option) any later version.
10
11     VOMP is distributed in the hope that it will be useful,
12     but WITHOUT ANY WARRANTY; without even the implied warranty of
13     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14     GNU General Public License for more details.
15
16     You should have received a copy of the GNU General Public License
17     along with VOMP; if not, write to the Free Software
18     Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
19 */
20
21 #include <vector>
22 #include <time.h>
23 #include <string.h>
24 #ifndef WIN32
25 #include "unistd.h"
26 #endif
27 #include "vmedialist.h"
28
29 #include "vpicture.h"
30 #include "vaudioplayer.h"
31 #include "remote.h"
32 #include "wsymbol.h"
33 #include "boxstack.h"
34 #include "vvideolive.h"
35 #include "colour.h"
36 #include "video.h"
37 #include "i18n.h"
38 #include "vdr.h"
39 #include "command.h"
40 #include "vinfo.h"
41
42 using namespace std;
43 class MediaDirectory {
44         private:
45                 char * dirname;
46                 char * displayname;
47                 char * fullpath;
48                 int selection;
49                 int sortorder;
50                 ULONG mtype;
51         public:
52                 void setDirname(const char * n) {
53                         if (dirname) delete dirname;
54                         dirname=NULL;
55                         if (!n) return;
56                         dirname=new char[strlen(n)+1];
57                         strcpy(dirname,n);
58                 }
59                 void setDisplayname(const char * n) {
60                         if (displayname) delete displayname;
61                         displayname=NULL;
62                         if (!n) return;
63                         displayname=new char[strlen(n)+1];
64                         strcpy(displayname,n);
65                 }
66                 void setFullpath(const char * n) {
67                         if (fullpath) delete fullpath;
68                         fullpath=NULL;
69                         if (!n) return;
70                         fullpath=new char[strlen(n)+1];
71                         strcpy(fullpath,n);
72                 }
73                 void setSelection(int s) {
74                         selection=s;
75                 }
76                 void setMediaType(ULONG n) {mtype=n;}
77                 void setSortorder(int s) {
78                         sortorder=s;
79                 }
80                 const char * getDirname() {
81                         return dirname;
82                 }
83                 const char * getDisplayname() {
84                         if (displayname) return displayname;
85                         if (fullpath) return fullpath;
86                         if (dirname) return dirname;
87                         return "/";
88                 }
89                 const char * getFullPath() {
90                         return fullpath;
91                 }
92                 int getSelection() {
93                         return selection;
94                 }
95                 ULONG getMediaType(){return mtype;}
96                 int getSortorder() {
97                         return sortorder;
98                 }
99         public:
100                 MediaDirectory(const char *d) : dirname(NULL),displayname(NULL),fullpath(NULL),selection(0),
101             sortorder(0),mtype(MEDIA_TYPE_ALL){
102                   setDirname(d);
103                   }
104                 MediaDirectory(MediaDirectory &c) {
105                         MediaDirectory(c.getDirname());
106                         setDisplayname(c.getDisplayname());
107                         setSelection(c.getSelection());
108                         setMediaType(c.getMediaType());
109                         setSortorder(c.getSortorder());
110                 }
111                 ~MediaDirectory() {
112                         if (dirname) delete dirname;
113                         if (displayname) delete displayname;
114                         if (fullpath) delete fullpath;
115                 }
116 };
117
118 typedef vector<MediaDirectory*> MDirList;
119 class DirList {
120         private:
121                 int current;
122     MDirList list;
123         public:
124                 DirList() {
125                         current=0;
126                         list.push_back(new MediaDirectory(NULL));
127                 }
128                 ~DirList() {
129                         MDirList::iterator it;
130                         for (it=list.begin();it<list.end();it++) {
131                                 delete *it;
132                         }
133                 }
134                 MediaDirectory *getCurrent() {
135                         return list[current];
136                 }
137                 const char * getPath() {
138                         return getCurrent()->getFullPath();
139                 }
140                 int dropTop() {
141                         if (current > 0) {
142                                 current--;
143                                 delete list.back();
144                                 list.pop_back();
145                         }
146                         return current;
147                 }
148                 int append(const char *dirname) {
149                         if (! dirname) return current;
150                         MediaDirectory* md=new MediaDirectory(dirname);
151                         const char *cp=getCurrent()->getFullPath();
152                         int len=strlen(dirname)+2;
153                         if (cp) len+=strlen(cp);
154                         char * fp=new char[len];
155                         *fp=0;
156                         if (cp) {
157                                 strcpy(fp,cp);
158                                 strcat(fp,"/");
159                         }
160                         else if (*dirname != '/' ) strcpy(fp,"/");
161                         strcat(fp,dirname);
162                         md->setFullpath(fp);
163                         delete fp;
164                         list.push_back(md);
165                         current++;
166                         return current;
167                 }
168
169                 int getLevel() {
170                         return current;
171                 }
172
173 };
174
175
176 class MediaSorterName
177 {
178   public:
179   bool operator() (const Media* a, const Media* b)
180   {
181     if (b->getMediaType() == MEDIA_TYPE_DIR &&
182          a->getMediaType() != MEDIA_TYPE_DIR)
183       return false;
184     if ( b->getMediaType() != MEDIA_TYPE_DIR &&
185         a->getMediaType() == MEDIA_TYPE_DIR)
186       return true;
187     int c = strcmp(b->getFileName(), a->getFileName());
188     if (c > 0) return true;
189     return false;
190   }
191 };
192
193 //a sorter with a randomly initialized order
194 class MediaSorterRandom
195 {
196   public:
197   MediaSorterRandom(int start) {
198     mystart=start;
199   }
200   bool operator() (const Media* a, const Media* b)
201   {
202     if (b->getMediaType() == MEDIA_TYPE_DIR &&
203          a->getMediaType() != MEDIA_TYPE_DIR)
204       return false;
205     if ( b->getMediaType() != MEDIA_TYPE_DIR &&
206         a->getMediaType() == MEDIA_TYPE_DIR)
207       return true;
208     unsigned char suma=sum(a->getFileName(),(unsigned char)mystart);
209     unsigned char sumb=sum(b->getFileName(),(unsigned char)mystart);
210     if (sumb > suma) return true;
211     return false;
212   }
213   private:
214   unsigned char sum(const char *name,unsigned char start) {
215     for (;*name!=0;name++) start=start ^ (unsigned char)*name;
216     return start;
217   }
218   int mystart;
219 };
220
221
222 struct MediaSorterTime
223 {
224   bool operator() (const Media* a, const Media* b)
225   {
226     if (b->getMediaType() == MEDIA_TYPE_DIR &&
227          a->getMediaType() != MEDIA_TYPE_DIR)
228       return false;
229     if ( b->getMediaType() != MEDIA_TYPE_DIR &&
230         a->getMediaType() == MEDIA_TYPE_DIR)
231       return true;
232     if (b->getTime()>a->getTime()) return true;
233     return false;
234   }
235 };
236
237
238 VMediaList::VMediaList()
239 {
240   boxstack = BoxStack::getInstance();
241
242         dirlist=new DirList();
243   Log::getInstance()->log("VMediaList::VMediaList", Log::DEBUG, "dirlist=%p,curren=%p",dirlist,dirlist->getCurrent());
244         dirlist->getCurrent()->setSortorder(SORT_NAME);
245   setSize(570, 420);
246   createBuffer();
247   if (Video::getInstance()->getFormat() == Video::PAL)
248     setPosition(80, 70);
249   else
250     setPosition(70, 35);
251
252   setTitleBarOn(1);
253   setTitleBarColour(Colour::TITLEBARBACKGROUND);
254
255   sl.setPosition(10, 30 + 5);
256   sl.setSize(area.w - 20, area.h - 30 - 15 - 30);
257   sl.addColumn(0);
258   sl.addColumn(60);
259   add(&sl);
260   
261   mediaList=NULL;
262   loading=true;
263   sortOrder=SORT_NONE;
264 }
265
266 VMediaList::~VMediaList()
267 {
268         delete dirlist;
269   clearMediaList();
270 }
271
272 void VMediaList::clearMediaList() {
273   if (mediaList)
274   {
275     for (UINT i = 0; i < mediaList->size(); i++)
276     {
277       delete (*mediaList)[i];
278     }
279
280     mediaList->clear();
281     delete mediaList;
282   }
283 }
284
285
286
287 int VMediaList::getNumEntries(int mediaType,int lowerThen) {
288   if (mediaType == MEDIA_TYPE_ALL && lowerThen < 0) return mediaList->size();
289   if (lowerThen < 0) lowerThen=mediaList->size();
290   int rt=0;
291   for (int i=0;i<(int)(mediaList->size()) && i <= lowerThen;i++) {
292     if ((*mediaList)[i]->getMediaType() & mediaType) rt++;
293   }
294   return rt;
295 }
296
297 void VMediaList::setList(MediaList* tlist)
298 {
299   if (mediaList != tlist) {
300     clearMediaList();
301   }
302   sortOrder=SORT_NONE;
303   mediaList = tlist;
304   sortList(dirlist->getCurrent()->getSortorder());
305   updateSelectList(dirlist->getCurrent()->getSelection());
306 }
307
308
309 void VMediaList::updateSelectList(){
310         updateSelectList(-1);
311 }
312 void VMediaList::updateSelectList(int currentNumber){
313   char str[5000];
314   char tempA[Media::TIMEBUFLEN];
315   Log::getInstance()->log("VMediaList::updateSelectList", Log::DEBUG, "media=%p",mediaList);
316
317   ULONG currentSelection=0;
318   if (sl.getNumOptions() >= 1 && currentNumber < 0) {
319     currentSelection=sl.getCurrentOptionData();
320         }
321   sl.clear();
322
323   Media* media;
324   int first = 1;
325   if (mediaList)
326   {
327     for (UINT i = 0; i < mediaList->size(); i++)
328     {
329       media = (*mediaList)[i];
330       if (media->getMediaType() == MEDIA_TYPE_DIR) {
331         sprintf(str, "%04u  %s  [%s]", i,media->getTimeString(tempA), media->getDisplayName());
332         //Log::getInstance()->log("VMediaList", Log::DEBUG, "add to select list %s",str);
333         media->index = sl.addOption(str, (ULONG)media, first);
334       }
335       else {
336         sprintf(str, "%04u  %s  %s", i,media->getTimeString(tempA), media->getDisplayName());
337         //Log::getInstance()->log("VMediaList", Log::DEBUG, "add to select list %s",str);
338         media->index = sl.addOption(str, (ULONG)media, first);
339       }
340       first = 0;
341     }
342   }
343         if (currentNumber >= 0) sl.hintSetCurrent(currentNumber);
344         else sl.hintSetCurrent(0);
345   if (currentSelection != 0) {
346     bool found=false;
347     //position to the previous selection
348     for (int i=0;i<sl.getNumOptions();i++) {
349       sl.hintSetCurrent(i);
350       if (sl.getCurrentOptionData() == currentSelection) {
351         found=true;
352         break;
353       }
354     }
355     if (! found) sl.hintSetCurrent(0);
356   }
357   loading=false;
358   if (sl.getNumOptions() > 0)
359     sl.draw();
360   doShowingBar();
361 }
362
363 void VMediaList::highlightMedia(Media* media)
364 {
365   sl.hintSetCurrent(media->index);
366   sl.draw();
367   doShowingBar();
368   boxstack->update(this);
369 }
370
371 void VMediaList::draw()
372 {
373   Log::getInstance()->log("VMediaList::draw", Log::DEBUG, "namestr=%s",dirlist->getCurrent()->getDisplayname());
374   char title[400];
375   SNPRINTF(title, 398, tr("Media - %s"), dirlist->getCurrent()->getDisplayname());
376   title[399]=0;
377   setTitleText(title);
378   
379   if (loading)
380   {
381     sl.setVisible(false);
382     TBBoxx::draw();
383     drawText(tr("Loading..."), 240, 180, Colour::LIGHTTEXT);
384   }
385   else 
386   {
387     //if (sl.getNumOptions() > 0) sl.draw();
388     sl.setVisible(true);
389     TBBoxx::draw();
390
391     // Put the status stuff at the bottom
392
393     WSymbol w;
394     TEMPADD(&w);
395
396     w.nextSymbol = WSymbol::UP;
397     w.setPosition(20, 385);
398     w.draw();
399
400     w.nextSymbol = WSymbol::DOWN;
401     w.setPosition(50, 385);
402     w.draw();
403
404     w.nextSymbol = WSymbol::SKIPBACK;
405     w.setPosition(85, 385);
406     w.draw();
407
408     w.nextSymbol = WSymbol::SKIPFORWARD;
409     w.setPosition(115, 385);
410     w.draw();
411
412     w.nextSymbol = WSymbol::PLAY;
413     w.setPosition(150, 385);
414     w.draw();
415
416     doShowingBar();
417   }
418 }
419
420 void VMediaList::doShowingBar()
421 {
422   int topOption = sl.getTopOption() + 1;
423   if (sl.getNumOptions() == 0) topOption = 0;
424
425   char showing[250];
426   const char* strmode=tr("Name");
427   switch (sortOrder) {
428     case SORT_TIME:
429       strmode=tr("Rand");
430       break;
431     case SORT_NAME:
432       strmode=tr("Time");
433       break;
434     default:
435       break;
436   }
437   SNPRINTF(showing, 250,tr("%i to %i of %i"), 
438       topOption, sl.getBottomOption(), sl.getNumOptions());
439
440 //  Box b;
441 //  b.setSurfaceOffset(220, 385);
442 //  b.setDimensions(160, 25);
443 //  b.fillColour(Colour::VIEWBACKGROUND);
444 //  b.drawText(showing, 0, 0, Colour::LIGHTTEXT);
445
446   rectangle(200, 384, 18, 16, Colour::VIDEOBLUE);
447   rectangle(220, 385, 220+160, 385+25, Colour::VIEWBACKGROUND);
448   drawText(strmode, 220, 385, Colour::LIGHTTEXT);
449   drawText(showing, 280, 385, Colour::LIGHTTEXT);
450   if (sl.getCurrentOptionData() != 0) Log::getInstance()->log("VMediaList",Log::DEBUG,"selected %s",((Media *)sl.getCurrentOptionData())->getDisplayName());
451 }
452
453 Media * VMediaList::getMedia(int ltype,ULONG move) {
454   int cur=sl.getCurrentOption();
455   Media *m;
456   bool more=true;
457   while (more) {
458     int last=sl.getCurrentOption();
459     switch (move) {
460       case MV_NEXT:
461         sl.down();
462         break;
463       case MV_PREV:
464         sl.up();
465         break;
466       default:
467         more=false;
468       break;
469     }
470     m=(Media*)sl.getCurrentOptionData();
471     if (m->getMediaType() & ltype) {
472                         sl.draw();
473                         return m;
474                 }
475     //stop if we are done
476     if (sl.getCurrentOption() == cur || sl.getCurrentOption() == last) break;
477   }
478   return NULL;
479 }
480
481 int VMediaList::handleCommand(int command)
482 {
483   switch(command)
484   {
485                 case Remote::ONE:
486                         {
487       sl.hintSetCurrent(0);
488       sl.draw();
489       doShowingBar();
490       boxstack->update(this);
491       return 2;
492                         }
493     case Remote::DF_UP:
494     case Remote::UP:
495     {
496       sl.up();
497       sl.draw();
498
499       doShowingBar();
500       boxstack->update(this);
501       return 2;
502     }
503     case Remote::DF_DOWN:
504     case Remote::DOWN:
505     {
506       sl.down();
507       sl.draw();
508
509       doShowingBar();
510       boxstack->update(this);
511       return 2;
512     }
513     case Remote::SKIPBACK:
514     {
515       sl.pageUp();
516       sl.draw();
517
518       doShowingBar();
519       boxstack->update(this);
520       return 2;
521     }
522     case Remote::SKIPFORWARD:
523     {
524       sl.pageDown();
525       sl.draw();
526
527       doShowingBar();
528       boxstack->update(this);
529       return 2;
530     }
531     case Remote::BLUE:
532     {
533       switch(sortOrder) {
534         case SORT_NAME:
535           sortList(SORT_TIME);
536           boxstack->update(this);
537           return 2;
538         case SORT_TIME:
539           sortList(SORT_RANDOM);
540           boxstack->update(this);
541           return 2;
542         default:
543           sortList(SORT_NAME);
544           boxstack->update(this);
545           return 2;
546       }
547     }
548     case Remote::OK:
549     case Remote::PLAY:
550     {
551       Media* media = NULL;
552       if (mediaList) media = (Media*)sl.getCurrentOptionData();
553       if (media == NULL) return 2;
554       Log::getInstance()->log("VMediaList", Log::DEBUG, "activated %lu", media->index);
555       switch(media->getMediaType())
556       {
557         case MEDIA_TYPE_DIR:
558         { 
559         //create child
560         Log::getInstance()->log("VMediaList", Log::DEBUG, "create child for %s",media->getFileName());
561         if (media->getFileName() == NULL ) return 2;
562                                 if (sl.getNumOptions() >=1) {
563                                         dirlist->getCurrent()->setSelection(sl.getCurrentOption());
564                                 }
565                                 dirlist->getCurrent()->setSortorder(sortOrder);
566                                 dirlist->append(media->getFileName());
567                                 //same sort order for next level
568                                 dirlist->getCurrent()->setSortorder(sortOrder);
569                                 load();
570         break;
571         }
572         case MEDIA_TYPE_AUDIO:
573         Log::getInstance()->log("VMediaList", Log::DEBUG, "play audio file %s",
574           media->getFileName());
575         VAudioplayer::createPlayer(this,command==Remote::PLAY);
576         break;
577         case MEDIA_TYPE_VIDEO:
578         Log::getInstance()->log("VMediaList", Log::DEBUG, "play video file %s",
579           media->getFileName());
580         //play video
581         break;
582         case MEDIA_TYPE_PICTURE:
583         Log::getInstance()->log("VMediaList", Log::DEBUG, "show picture file %s",
584          media->getFileName());
585         VPicture::createViewer(this,command==Remote::PLAY);
586               //play video
587         break;
588         default:
589         Log::getInstance()->log("VMediaList", Log::DEBUG, "unknown media type %d file %s",
590           media->getMediaType(),media->getFileName());
591
592         }
593       /*
594       VVideoLive* v = new VVideoLive(mediaList, media->type, this);
595
596       v->draw();
597       boxstack->add(v);
598       boxstack->updatev);
599
600       v->medianelChange(VVideoLive::NUMBER, media->number);
601       */
602
603       return 2;
604     }
605     case Remote::BACK:
606     {
607                         if (dirlist->getLevel() < 1) return 4;
608                         dirlist->dropTop();
609                         load();
610     }
611   }
612   // stop command getting to any more views
613   return 1;
614 }
615
616 void VMediaList::processMessage(Message* m)
617 {
618   if (m->message == Message::MOUSE_MOVE)
619   {
620     if (sl.mouseMove((m->parameter>>16)-getScreenX(),(m->parameter&0xFFFF)-getScreenY()))
621     {
622       sl.draw();
623       doShowingBar();
624       boxstack->update(this);
625     }
626   }
627   else if (m->message == Message::MOUSE_LBDOWN)
628   {
629     if (sl.mouseLBDOWN((m->parameter>>16)-getScreenX(),(m->parameter&0xFFFF)-getScreenY()))
630     {
631       boxstack->handleCommand(Remote::OK); //simulate OK press
632     }
633     else
634     { //check if press is outside this view! then simulate cancel
635       int x=(m->parameter>>16)-getScreenX();
636       int y=(m->parameter&0xFFFF)-getScreenY();
637       if (x<0 || y <0 || x>(int)getWidth() || y>(int)getHeight())
638       {
639         boxstack->handleCommand(Remote::BACK); //simulate cancel press
640       }
641     }
642   }
643 }
644
645 int VMediaList::createList() {
646    Log::getInstance()->log("VMediaList::createList", Log::DEBUG, "");
647    VMediaList *vmn=new VMediaList();
648    //show the "loading" indicator
649    BoxStack::getInstance()->add(vmn);
650    int rt=vmn->load();
651    if ( rt != 0) {
652      BoxStack::getInstance()->remove(vmn);
653      }
654    return rt;
655 }
656
657 void VMediaList::sortList(int newSort) {
658   if (sortOrder == newSort) return;
659    Log::getInstance()->log("VMediaList::sortList", Log::DEBUG, "p=%p,sort=%d, size=%d",this,newSort,mediaList->size());
660    if (mediaList->begin() != mediaList->end()) {
661    switch (newSort) {
662      case SORT_TIME:
663        ::sort(mediaList->begin(),mediaList->end(),MediaSorterTime());
664        break;
665      case SORT_NAME:
666        ::sort(mediaList->begin(),mediaList->end(),MediaSorterName());
667        break;
668      case SORT_RANDOM:
669        ::sort(mediaList->begin(),mediaList->end(),MediaSorterRandom(time(NULL)));
670        break;
671      }
672    }
673    sortOrder=newSort;
674    updateSelectList();
675 }
676
677
678 int VMediaList::load() {
679         
680         loading=true;
681         draw();
682   boxstack->update(this);
683   VDR* vdr=VDR::getInstance();
684    Log::getInstance()->log("VMediaList::load", Log::DEBUG, "load list for %s",dirlist->getPath());
685   if (vdr->isConnected()) {
686                  MediaDirectory *md=dirlist->getCurrent();
687      MediaList *mn=vdr->getMediaList(md->getFullPath(),md->getMediaType());
688      if (mn != NULL) {
689        setList(mn);
690                          draw();
691        boxstack->update(this);
692        return 0;
693        }
694   }
695   if (! vdr->isConnected()) {
696     Command::getInstance()->connectionLost();
697   }
698   else {
699     Log::getInstance()->log("VMediaList", Log::ERR, "unable to get MediaList for %s",dirlist->getPath());
700
701     VInfo* vi = new VInfo();
702     vi->setSize(400, 150);
703     vi->createBuffer();
704     vi->setExitable();
705     vi->setBorderOn(1);
706     vi->setTitleBarOn(0);
707
708     if (Video::getInstance()->getFormat() == Video::PAL)
709       vi->setPosition(170, 200);
710     else
711       vi->setPosition(160, 150);
712     vi->setOneLiner(tr("unable to get media list"));
713     vi->draw();
714
715     Message* m = new Message();
716     m->message = Message::ADD_VIEW;
717     m->to = boxstack;
718     m->parameter = (ULONG)vi;
719     Command::getInstance()->postMessageNoLock(m);
720   }
721   return 1;
722 }
723
724 const char * VMediaList::getDirname() const {
725         return dirlist->getCurrent()->getFullPath();
726 }
727