/* This file is part of vdr-filebrowser.
vdr-filebrowser is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
vdr-filebrowser is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Foobar. If not, see .
*/
#include
#include
#include
#include
#include
#include
#include
#include
#include "menu-filebrowser.h"
#include "commands.h"
#ifdef FILEBROWSER_PLUGIN_BUILD
#include "threads.h"
#include "command-other.h"
#endif
#include "tools.h"
#include "menu-accesscode.h"
#include "menu-userinput.h"
void cOsdItemFileEntry::UpdateText()
{
char name[strlen ( Name ) + 5];
struct stat64 buf;
if ( stat64 ( Filename, &buf ) == 0 && S_ISDIR ( buf.st_mode ) )
{
if ( Marked )
{
sprintf ( name, "*[%s]*", Name );
}
else
{
sprintf ( name, "[%s]", Name );
}
}
else
{
if ( Marked )
{
sprintf ( name, "*%s*", Name );
}
else
{
sprintf ( name, "%s", Name );
}
}
SetText ( name );
}
bool cOsdItemFileEntry::IsDir()
{
struct stat64 buf;
return stat64 ( Filename, &buf ) == 0 && S_ISDIR ( buf.st_mode );
}
cOsdItemFileEntry::cOsdItemFileEntry ( dirent64* DirectoryEntry, cString Directory, cFilebrowserStatebag* Statebag ) : cOsdItem ( "" )
{
Filename= ( char* ) malloc ( strlen ( Directory ) + strlen ( DirectoryEntry->d_name ) + 2 );
bool NeedsSlash=strlen ( *Directory ) > 0 && ( *Directory ) [strlen ( *Directory )-1]!='/';
sprintf ( Filename, "%s%s%s", *Directory, NeedsSlash ? "/" : "", DirectoryEntry->d_name );
Name=strdup ( DirectoryEntry->d_name );
IsParentDir= ( strcmp ( DirectoryEntry->d_name, ".." ) == 0 );
Marked=Statebag->GetSelectedFiles()->Contains ( Filename );
UpdateText();
}
cOsdItemFileEntry::~cOsdItemFileEntry()
{
if ( Filename ) free ( Filename );
if ( Name ) free ( Name );
}
eOSState cOsdItemFileEntry::ProcessKey ( eKeys Key )
{
eOSState state=cOsdItem::ProcessKey ( Key );
return state;
}
void cOsdItemFileEntry::Mark ( bool Marked )
{
this->Marked=Marked;
UpdateText();
}
/*
cOsdMenuFilebrowser
*/
cOsdMenuFilebrowser::cOsdMenuFilebrowser ( char* Directory, cFilebrowserStatebag* Statebag ) : cOsdMenu ( tr ( "Filebrowser" ), 10 )
{
this->Statebag=Statebag;
DIR* BaseDir;
if ( Directory && ( BaseDir=opendir ( Directory ) ) )
{
BaseDirectory=strdup ( Directory );
closedir ( BaseDir );
}
else
{
BaseDirectory=strdup ( "/" );
}
Task=taskBrowse;
DestinationContainerCommand=NULL;
LoadDir ( * ( Statebag->CurrentDirectory ) ? Statebag->CurrentDirectory : cString ( BaseDirectory ) );
UpdateHelp();
}
cOsdMenuFilebrowser::~cOsdMenuFilebrowser()
{
if ( DestinationContainerCommand ) delete DestinationContainerCommand;
if ( BaseDirectory ) delete BaseDirectory;
}
int cOsdMenuFilebrowser::DirectorySort ( const dirent64** File1, const dirent64** File2 )
{
struct dirent64* ent1=* ( struct dirent64** ) File1;
struct dirent64* ent2=* ( struct dirent64** ) File2;
if ( strcmp ( ent1->d_name, ".." ) == 0 )
{
return -1;
}
if ( strcmp ( ent2->d_name, ".." ) == 0 )
{
return 1;
}
struct stat64 buf1, buf2;
if ( stat64 ( ent1->d_name, &buf1 ) ==0 && stat64 ( ent2->d_name, &buf2 ) ==0 )
{
if ( ( ( buf1.st_mode & S_IFMT ) != ( buf2.st_mode & S_IFMT ) ) && ( S_ISDIR ( buf1.st_mode ) || S_ISDIR ( buf2.st_mode ) ) )
{
return S_ISDIR ( buf1.st_mode ) ? -1 : 1;
}
else
{
return strcoll ( ent1->d_name, ent2->d_name );
}
}
D ( fprintf ( stderr, "stat failed for %s or %s\n", ent1->d_name, ent2->d_name ) );
/*
if(ent1->d_type!=ent2->d_type && (S_ISDIR(DTTOIF(ent1->d_type)) || S_ISDIR(DTTOIF(ent2->d_type))))
{
return S_ISDIR(DTTOIF(ent1->d_type)) ? -1 : 1;
}
*/
return strcoll ( ent1->d_name, ent2->d_name );
}
void cOsdMenuFilebrowser::LoadDir ( cString Directory )
{
D ( fprintf ( stderr, "Current dir is %s, loading %s\n", * ( Statebag->CurrentDirectory ), *Directory ) );
char* CurrentFile=NULL;
if ( !* ( Statebag->CurrentDirectory ) || strcmp ( Directory, ".." ) != 0 )
{
Statebag->CurrentDirectory=Directory;
if ( Current() >= 0 )
{
Statebag->CurrentFiles->Add ( new cStringContainer ( strdup ( ( ( cOsdItemFileEntry* ) Get ( Current() ) )->GetFilename() ) ) );
}
}
else
{
if ( * ( Statebag->CurrentDirectory ) )
{
char* slash = (char*) strrchr ( * ( Statebag->CurrentDirectory ), '/' );
if ( slash )
{
* ( slash + ( slash - * ( Statebag->CurrentDirectory ) > 0 ? 0 : 1 ) ) ='\0';
CurrentFile=Statebag->CurrentFiles->Last() ? strdup ( Statebag->CurrentFiles->Last()->GetObject() ) : NULL;
if ( Statebag->CurrentFiles->Last() )
{
Statebag->CurrentFiles->Del ( Statebag->CurrentFiles->Last() );
}
}
}
}
D ( fprintf ( stderr, "Current file is %s\n", CurrentFile ? CurrentFile : NULL ) );
Refresh ( CurrentFile );
if ( CurrentFile ) free ( CurrentFile );
}
bool cOsdMenuFilebrowser::MatchesFilter ( dirent64* Entry )
{
if ( !Statebag->ShowHiddenFiles && Entry->d_name[0]=='.' && strcmp ( Entry->d_name, ".." ) !=0 )
{
return false;
}
return Entry->d_type==DT_DIR || !*Statebag->Filter || fnmatch ( *Statebag->Filter, Entry->d_name, FNM_FILE_NAME | FNM_EXTMATCH ) != FNM_NOMATCH;
}
void cOsdMenuFilebrowser::Refresh ( const char* CurrentFile )
{
if ( Task==taskBrowse )
{
char* Title= ( char* ) malloc ( strlen ( tr ( "Filebrowser" ) ) + strlen ( Statebag->CurrentDirectory ) + 3 );
char* Title_tmp=NULL;
sprintf ( Title, "%s: %s", tr ( "Filebrowser" ), ( ( Title_tmp=(char *) strrchr ( Statebag->CurrentDirectory, '/' ) ) && ! ( Statebag->ShowFullPath ) ) ? Title_tmp + 1 : * ( Statebag->CurrentDirectory ) );
SetTitle ( Title );
free ( Title );
}
else if ( Task==taskSelectDestination )
{
SetTitle ( tr ( "Select Destination" ) );
}
Clear();
D ( fprintf ( stderr, "Refreshing, current dir is %s and i got %s\n", * ( Statebag->CurrentDirectory ), CurrentFile ) );
char* cwd=getcwd ( NULL, 0 );
dirent64** entries;
int count=scandir64 ( Statebag->CurrentDirectory, &entries, NULL, &cOsdMenuFilebrowser::DirectorySort );
if ( (chdir ( Statebag->CurrentDirectory ) == 0) && (count > 0) )
{
for ( int i=0; id_name, "." ) ==0 || ( strcmp ( entries[i]->d_name, ".." ) ==0 && strcmp ( Statebag->BaseDir, Statebag->CurrentDirectory ) ==0 ) )
{
continue;
}
if ( MatchesFilter ( entries[i] ) )
{
cOsdItemFileEntry* FileEntry=new cOsdItemFileEntry ( entries[i], * ( Statebag->CurrentDirectory ), Statebag );
Add ( FileEntry );
if ( CurrentFile && strcmp ( CurrentFile, FileEntry->GetFilename() ) == 0 )
{
SetCurrent ( FileEntry );
}
}
}
free ( entries );
chdir ( cwd );
}
else
{
Skins.Message ( mtError, tr ( "Failed to load directory" ) );
}
if ( cwd ) free ( cwd );
if ( Count() == 0 )
{
/* Update Commands */
UpdateCommands ( NULL, false, false );
UpdateHelp();
}
Display();
}
eOSState cOsdMenuFilebrowser::ProcessKey ( eKeys Key )
{
eOSState state=cOsdMenu::ProcessKey ( Key );
cFilebrowserCommand* ExecCommand=NULL;
if ( state == ( eOSState ) osFilebrowserCloseRefresh )
{
/* A submenu of this menu was closed - refresh files */
if ( HasSubMenu() )
{
CloseSubMenu();
}
char* CurrentFile=CurrentFilename() ? strdup ( CurrentFilename() ) : NULL;
Task=taskBrowse;
Refresh ( CurrentFile );
if ( CurrentFile ) free ( CurrentFile );
}
else if ( state == osUnknown || state == osBack )
{
state=osContinue;
switch ( Key )
{
case kRed:
if ( LeftVisibleCommand )
{
if ( LeftVisibleCommand->Prev() ) // this is the "<<"-key
{
if ( LeftVisibleCommand->Prev()->Prev()->Prev()->Prev() )
{
LeftVisibleCommand=LeftVisibleCommand->Prev()->Prev();
}
else
{
LeftVisibleCommand=LeftVisibleCommand->Prev()->Prev()->Prev();
}
UpdateHelp();
}
else
{
ExecCommand=LeftVisibleCommand->GetObject();
}
}
break;
case kGreen:
if ( LeftVisibleCommand )
{
cFilebrowserCommandContainer* Container=LeftVisibleCommand->Prev() ? LeftVisibleCommand : LeftVisibleCommand->Next();
ExecCommand=Container ? Container->GetObject() : NULL;
}
break;
case kYellow:
if ( LeftVisibleCommand && LeftVisibleCommand->Next() )
{
cFilebrowserCommandContainer* Container=LeftVisibleCommand->Prev() ? LeftVisibleCommand->Next() : LeftVisibleCommand->Next()->Next();
ExecCommand=Container ? Container->GetObject() : NULL;
}
break;
case kBlue:
if ( LeftVisibleCommand && LeftVisibleCommand->Next() && LeftVisibleCommand->Next()->Next() )
{
cFilebrowserCommandContainer* Right;
Right=LeftVisibleCommand->Prev() ? LeftVisibleCommand->Next()->Next() : LeftVisibleCommand->Next()->Next()->Next();
if ( Right )
{
if ( Right->Next() ) //this is the ">>"-Key
{
LeftVisibleCommand=Right;
UpdateHelp();
}
else
{
ExecCommand=Right->GetObject();
}
}
}
break;
case kOk:
cOsdItemFileEntry* CurrentEntry;
CurrentEntry= ( cOsdItemFileEntry* ) this->Get ( this->Current() );
if ( CurrentEntry && CurrentEntry->IsDir() )
{
if ( !CurrentEntry->IsParentDirectory() )
{
LoadDir ( cString ( CurrentEntry->GetFilename() ) );
}
else
{
LoadDir ( cString ( ".." ) );
}
}
#ifdef FILEBROWSER_PLUGIN_BUILD
else
{
for ( cFilebrowserCommandContainer* i=ActualCommands->First(); i; i=i->Next() )
{
if ( dynamic_cast ( i->GetObject() ) )
{
ExecCommand=i->GetObject();
break;
}
}
}
#endif
break;
case kBack:
if ( Statebag->CurrentFiles->Count() > 0 )
{
LoadDir ( cString ( ".." ) );
}
else
{
state=osBack;
}
break;
case kNone:
#ifdef FILEBROWSER_PLUGIN_BUILD
if ( Statebag->UpdateRequested )
{
Statebag->UpdateRequested=false;
Refresh ( *cString ( CurrentFilename() ) );
}
#endif
break;
default:
state=osUnknown;
break;
}
}
else if ( state== ( eOSState ) osFilebrowserCloseExec )
{
if ( ActualCommands->Count() >0 )
{
ExecCommand=ActualCommands->Get ( 0 )->GetObject();
D ( fprintf ( stderr, "Preparing for execution: %s\n", ExecCommand->GetName() ) );
}
CloseSubMenu();
}
else if ( state== ( eOSState ) osFilebrowserAccessCodeOk )
{
if ( ActualCommands->Count() >0 )
{
ExecCommand=ActualCommands->Get ( 0 )->GetObject();
D ( fprintf ( stderr, "Preparing for execution: %s\n", ExecCommand->GetName() ) );
}
CloseSubMenu();
}
if ( ExecCommand )
{
char* CurrentFile=CurrentFilename();
if ( ExecCommand->UsesSelectedFiles() && Statebag->GetSelectedFiles()->Count() ==0 )
{
Statebag->GetSelectedFiles()->Add ( new cStringContainer ( strdup ( CurrentFile ) ) );
}
if ( ExecCommand->UsesDestination() && Task!=taskSelectDestination && Task!=taskRequireAccessCode && Task!=taskGetUserInput )
{
Task=taskSelectDestination;
SetTitle ( tr ( "Select Destination" ) );
DestinationContainerCommand=new cFilebrowserDestinationContainerCommand ( ExecCommand, CurrentFile, Statebag );
UpdateHelp();
Display();
state=ExecCommand->GetState();
}
else if ( ( ExecCommand->UsesUserInput() || ExecCommand->UsesUserInputPassword() ) && Task!=taskGetUserInput && Task!=taskRequireAccessCode )
{
Task=taskGetUserInput;
ActualCommands->Clear();
ActualCommands->Add ( new cFilebrowserCommandContainer ( ExecCommand ) );
AddSubMenu ( new cOsdMenuUserInput ( ExecCommand->UsesUserInput() ? &Statebag->UserInput : NULL, ExecCommand->UsesUserInputPassword() ? &Statebag->UserInputPassword : NULL ) );
}
else if ( ExecCommand->GetAccessCode() && Task!=taskRequireAccessCode)
{
Task=taskRequireAccessCode;
ActualCommands->Clear();
ActualCommands->Add ( new cFilebrowserCommandContainer ( ExecCommand ) );
AddSubMenu ( new cOsdMenuAccessCode ( ExecCommand->GetAccessCode() ) );
}
else
{
bool Execute=true;
D ( fprintf ( stderr, "confirmation needed: %s\n", ExecCommand->NeedsConfirmation() ? "yes" : "no" ) );
if ( ExecCommand->NeedsConfirmation() )
{
char* Text= ( char* ) malloc ( strlen ( ExecCommand->GetName() ) + 2 );
sprintf ( Text, "%s?", ExecCommand->GetName() );
Execute=Interface->Confirm ( Text );
free ( Text );
}
if ( Execute )
{
#ifdef FILEBROWSER_PLUGIN_BUILD
int ThreadCount=Statebag->GetThreads()->Count();
#endif
ExecCommand->Execute ( this, NULL, CurrentFile );
#ifdef FILEBROWSER_PLUGIN_BUILD
for ( ; ThreadCount < Statebag->GetThreads()->Count() && !ExecCommand->IsSynchronous(); ThreadCount++ )
{
cCommandThread* Thread=Statebag->GetThreads()->Get ( ThreadCount )->GetObject();
if ( ExecCommand->ShowMenu() )
{
AddSubMenu ( Thread->GetMenu() );
}
Thread->Start();
}
#endif
Task=ExecCommand->Task();
if ( ExecCommand->UsesSelectedFiles() )
{
Statebag->GetSelectedFiles()->Clear();
for ( cOsdItem* i=First(); i!=NULL; i=cOsdMenu::Next ( i ) )
{
( ( cOsdItemFileEntry* ) i )->Mark ( false );
}
}
if ( ! ( ExecCommand->ShowMenu() ) || ExecCommand->IsSynchronous() )
{
char* CurrentFile=strdup ( CurrentFilename() );
Refresh ( CurrentFile );
if ( CurrentFile ) free ( CurrentFile );
}
state=ExecCommand->GetState();
}
}
}
return state;
}
void cOsdMenuFilebrowser::OsdCurrentItem ( const char *Text )
{
if ( HasSubMenu() ) /* Do nothing if this is not top most menu */
{
return;
}
for ( cOsdItem* i=First(); i!=NULL; i=cOsdMenu::Next ( i ) )
{
if ( !Text || strcmp ( i->Text(), Text ) == 0 )
{
cOsdItemNewFileEntry* NewFileEntry=dynamic_cast ( i );
UpdateCommands ( ( ( cOsdItemFileEntry* ) i )->GetFilename(), NewFileEntry, NewFileEntry ? NewFileEntry->InEditMode() : false );
break;
}
}
}
void cOsdMenuFilebrowser::UpdateCommands ( const char* Filename, bool IsVirtualNewFile, bool NewFileInEditMode )
{
cFilebrowserCommands* Commands;
if ( Task==taskSelectDestination )
{
Commands=new cFilebrowserCommands();
Commands->Add ( new cFilebrowserCommandContainer ( DestinationContainerCommand ) );
Commands->Add ( new cFilebrowserCommandContainer ( new cFilebrowserDestinationAbortCommand ( Statebag ) ) );
if ( DestinationContainerCommand->UsesNewDestination() && !IsVirtualNewFile )
{
Commands->Add ( new cFilebrowserCommandContainer ( new cFilebrowserDestinationNewCommand ( Statebag ) ) );
}
}
else
{
Commands=Statebag->GetCommands();
}
ActualCommands=new cFilebrowserCommands();
for ( cFilebrowserCommandContainer* i=Commands->First(); i; i=Commands->Next ( i ) )
{
if ( i->GetObject()->Matches ( Filename ) )
{
ActualCommands->Add ( new cFilebrowserCommandContainer ( i->GetObject(), false ) );
}
}
LeftVisibleCommand=ActualCommands->First();
if ( ! ( Task==taskSelectDestination && NewFileInEditMode ) )
{
UpdateHelp();
}
}
void cOsdMenuFilebrowser::UpdateHelp()
{
const char* Red=NULL;
const char* Green=NULL;
const char* Yellow=NULL;
const char* Blue=NULL;
int HelpPos=0;
for ( cFilebrowserCommandContainer* i = ActualCommands ? LeftVisibleCommand : NULL; i; i=ActualCommands->Next ( i ) )
{
switch ( HelpPos )
{
case 0:
if ( i->Prev() )
{
Red="<<";
i=ActualCommands->Prev ( i );
}
else
{
Red=i->GetObject()->GetName();
}
break;
case 1:
Green=i->GetObject()->GetName();
break;
case 2:
Yellow=i->GetObject()->GetName();
break;
case 3:
if ( i->Next() )
{
Blue=">>";
}
else
{
Blue=i->GetObject()->GetName();
};
break;
}
HelpPos++;
if ( HelpPos > 3 )
{
break;
}
}
SetHelp ( Red, Green, Yellow, Blue );
}
char* cOsdMenuFilebrowser::CurrentFilename()
{
if ( Current() < 0 )
{
return NULL;
}
if ( dynamic_cast ( Get ( Current() ) ) )
{
return ( ( cOsdItemFileEntry* ) Get ( Current() ) )->GetFilename();
}
else if ( dynamic_cast ( Get ( Current() ) ) )
{
return ( ( cOsdItemNewFileEntry* ) Get ( Current() ) )->GetFilename();
}
return NULL;
}
/*
cOsdItemNewFileEntry
*/
cOsdItemNewFileEntry::cOsdItemNewFileEntry ( char* Value, cString Directory ) : cMenuEditStrItem ( tr ( "New" ), Name= ( char* ) malloc ( NAME_MAX ), NAME_MAX, FileNameChars )
{
Filename=NULL;
strcpy ( Name, Value );
this->Directory=strdup ( *Directory );
ProcessKey ( kLeft );
}
cOsdItemNewFileEntry::~cOsdItemNewFileEntry()
{
if ( Directory ) free ( Directory );
if ( Name ) free ( Name );
if ( Filename ) free ( Filename );
}
bool cOsdItemNewFileEntry::InEditMode()
{
return cMenuEditStrItem::InEditMode();
}
char* cOsdItemNewFileEntry::GetFilename()
{
if ( Filename ) free ( Filename );
Filename= ( char* ) malloc ( strlen ( Directory ) + strlen ( Name ) + 2 );
sprintf ( Filename, "%s/%s", Directory, Name );
return Filename;
}
eOSState cOsdItemNewFileEntry::ProcessKey ( eKeys Key )
{
if ( InEditMode() || Key==kLeft || Key==kRight || Key==kNone )
{
return cMenuEditStrItem::ProcessKey ( Key );
}
else
{
if ( Key==kRed || Key==kYellow || Key==kBlue || Key==kGreen )
{
return osUnknown;
}
else
{
return osContinue;
}
}
}