Files
neutrino/src/gui/filebrowser.cpp
Stefan Seyfried 2f65aea744 neutrino: make button bar use available space intelligently
modify the paintButtons() function, so that it spreads the buttons
over the available width with constant space between them.
Port over all users to the new method and clean up quite some custom
button drawing code on the way.

Attention: This code is GPL v3+ only for now!
2012-04-17 19:33:49 +02:00

1595 lines
41 KiB
C++

/*
Neutrino-GUI - DBoxII-Project
Copyright (C) 2001 Steffen Hehn 'McClean'
Homepage: http://dbox.cyberphoria.org/
Kommentar:
Diese GUI wurde von Grund auf neu programmiert und sollte nun vom
Aufbau und auch den Ausbaumoeglichkeiten gut aussehen. Neutrino basiert
auf der Client-Server Idee, diese GUI ist also von der direkten DBox-
Steuerung getrennt. Diese wird dann von Daemons uebernommen.
License: GPL
This program 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 2 of the License, or
(at your option) any later version.
This program 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 this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
/* include <config.h> before <gui/filebrowser.h> to enable 64 bit file offsets */
#include <gui/filebrowser.h>
#include <gui/widget/buttons.h>
#include <gui/widget/icons.h>
#include <gui/widget/messagebox.h>
#include <driver/encoding.h>
#include <algorithm>
#include <iostream>
#include <cctype>
#include <global.h>
#include <neutrino.h>
#include <stdio.h>
#include <stdlib.h>
#include <dirent.h>
#include <sstream>
#include <sys/stat.h>
#include <curl/curl.h>
#include <curl/easy.h>
#ifndef NEW_LIBCURL
#include <curl/types.h>
#endif
#include <driver/encoding.h>
#include <xmltree/xmlinterface.h>
#ifdef __USE_FILE_OFFSET64
typedef struct dirent64 dirent_struct;
#define my_alphasort alphasort64
#define my_scandir scandir64
typedef struct stat64 stat_struct;
#define my_stat stat64
#define my_lstat lstat64
#else
typedef struct dirent dirent_struct;
#define my_alphasort alphasort
#define my_scandir scandir
typedef struct stat stat_struct;
#define my_stat stat
#define my_lstat lstat
#error not using 64 bit file offsets
#endif
#define SMSKEY_TIMEOUT 2000
//------------------------------------------------------------------------
size_t CurlWriteToString(void *ptr, size_t size, size_t nmemb, void *data)
{
std::string* pStr = (std::string*) data;
pStr->append((char*) ptr, nmemb); // do only add the correct size, do not go until end of data (chunked transfer)
return size*nmemb;
}
//------------------------------------------------------------------------
//------------------------------------------------------------------------
SMSKeyInput::SMSKeyInput()
{
resetOldKey();
m_timeout = SMSKEY_TIMEOUT;
}
//------------------------------------------------------------------------
unsigned char SMSKeyInput::handleMsg(const neutrino_msg_t msg)
{
timeval keyTime;
gettimeofday(&keyTime,NULL);
bool timeoutNotReached = (keyTime.tv_sec*1000+keyTime.tv_usec/1000
<= m_oldKeyTime.tv_sec*1000+m_oldKeyTime.tv_usec/1000 + m_timeout);
// printf("act: %ld , old: %ld (diff: %ld ) , timeout: %ld => timout= %d\n",
// keyTime.tv_sec*1000+keyTime.tv_usec/1000,
// m_oldKeyTime.tv_sec*1000+m_oldKeyTime.tv_usec/1000,
// keyTime.tv_sec*1000+keyTime.tv_usec/1000 -
// m_oldKeyTime.tv_sec*1000+m_oldKeyTime.tv_usec/1000,
// m_timeout,!timeoutNotReached);
unsigned char key = 0;
if(msg == CRCInput::RC_1)
{
key = '1';
}
if(msg == CRCInput::RC_2)
{
if(m_oldKey == 'a' && timeoutNotReached)
key = 'b';
else if(m_oldKey == 'b' && timeoutNotReached)
key = 'c';
else if(m_oldKey == 'c' && timeoutNotReached)
key = '2';
else
key = 'a';
}
else if(msg == CRCInput::RC_3)
{
if(m_oldKey == 'd' && timeoutNotReached)
key = 'e';
else if(m_oldKey == 'e' && timeoutNotReached)
key = 'f';
else if(m_oldKey == 'f' && timeoutNotReached)
key = '3';
else
key = 'd';
}
else if(msg == CRCInput::RC_4)
{
if(m_oldKey == 'g' && timeoutNotReached)
key = 'h';
else if(m_oldKey == 'h' && timeoutNotReached)
key = 'i';
else if(m_oldKey == 'i' && timeoutNotReached)
key = '4';
else
key = 'g';
}
else if(msg == CRCInput::RC_5)
{
if(m_oldKey == 'j' && timeoutNotReached)
key = 'k';
else if(m_oldKey == 'k' && timeoutNotReached)
key = 'l';
else if(m_oldKey == 'l' && timeoutNotReached)
key = '5';
else
key = 'j';
}
else if(msg == CRCInput::RC_6)
{
if(m_oldKey == 'm' && timeoutNotReached)
key = 'n';
else if(m_oldKey == 'n' && timeoutNotReached)
key = 'o';
else if(m_oldKey == 'o' && timeoutNotReached)
key = '6';
else
key = 'm';
}
else if(msg == CRCInput::RC_7)
{
if(m_oldKey == 'p' && timeoutNotReached)
key = 'q';
else if(m_oldKey == 'q' && timeoutNotReached)
key = 'r';
else if(m_oldKey == 'r' && timeoutNotReached)
key = 's';
else if(m_oldKey == 's' && timeoutNotReached)
key = 's';
else
key = 'p';
}
else if(msg == CRCInput::RC_8)
{
if(m_oldKey == 't' && timeoutNotReached)
key = 'u';
else if(m_oldKey == 'u' && timeoutNotReached)
key = 'v';
else if(m_oldKey == 'v' && timeoutNotReached)
key = '8';
else
key = 't';
}
else if(msg == CRCInput::RC_9)
{
if(m_oldKey == 'w' && timeoutNotReached)
key = 'x';
else if(m_oldKey == 'x' &&timeoutNotReached)
key = 'y';
else if(m_oldKey == 'y' &&timeoutNotReached)
key = 'z';
else if(m_oldKey == 'z' && timeoutNotReached)
key = '9';
else
key = 'w';
}
else if(msg == CRCInput::RC_0)
{
key = '0';
}
m_oldKeyTime=keyTime;
m_oldKey=key;
return key;
}
//------------------------------------------------------------------------
void SMSKeyInput::resetOldKey()
{
m_oldKeyTime.tv_sec = 0;
m_oldKeyTime.tv_usec = 0;
m_oldKey = 0;
}
unsigned char SMSKeyInput::getOldKey() const
{
return m_oldKey;
}
const timeval* SMSKeyInput::getOldKeyTime() const
{
return &m_oldKeyTime;
}
time_t SMSKeyInput::getOldKeyTimeSec() const
{
return m_oldKeyTime.tv_sec;
}
int SMSKeyInput::getTimeout() const
{
return m_timeout;
}
void SMSKeyInput::setTimeout(int timeout)
{
m_timeout = timeout;
}
//------------------------------------------------------------------------
//------------------------------------------------------------------------
bool comparetolower(const char a, const char b)
{
return tolower(a) < tolower(b);
};
// sort operators
bool sortByName (const CFile& a, const CFile& b)
{
if (std::lexicographical_compare(a.Name.begin(), a.Name.end(), b.Name.begin(), b.Name.end(), comparetolower))
return true;
if (std::lexicographical_compare(b.Name.begin(), b.Name.end(), a.Name.begin(), a.Name.end(), comparetolower))
return false;
return a.Mode < b.Mode;
/*
int result = __gnu_cxx::lexicographical_compare_3way(a.Name.begin(), a.Name.end(), b.Name.begin(), b.Name.end(), comparetolower);
if (result == 0)
return a.Mode < b.Mode;
else
return result < 0;
*/
}
bool sortByNameDirsFirst(const CFile& a, const CFile& b)
// Sorts alphabetically with Directories first
{
int typea, typeb;
typea = a.getType();
typeb = b.getType();
if (typea == CFile::FILE_DIR)
if (typeb == CFile::FILE_DIR)
//both directories
return sortByName(a, b);
else
//only a is directory
return true;
else if (typeb == CFile::FILE_DIR)
//only b is directory
return false;
else
//no directory
return sortByName(a, b);
}
bool sortByType (const CFile& a, const CFile& b)
{
if(a.Mode == b.Mode)
return sortByName(a, b);
else
return a.Mode < b.Mode;
}
bool sortByDate (const CFile& a, const CFile& b)
{
if(a.getFileName()=="..")
return true;
if(b.getFileName()=="..")
return false;
return a.Time < b.Time ;
}
bool sortBySize (const CFile& a, const CFile& b)
{
if(a.getFileName()=="..")
return true;
if(b.getFileName()=="..")
return false;
return a.Size < b.Size;
}
bool (* const sortBy[FILEBROWSER_NUMBER_OF_SORT_VARIANTS])(const CFile& a, const CFile& b) =
{
&sortByName,
&sortByNameDirsFirst,
&sortByType,
&sortByDate,
&sortBySize
};
const neutrino_locale_t sortByNames[FILEBROWSER_NUMBER_OF_SORT_VARIANTS] =
{
LOCALE_FILEBROWSER_SORT_NAME,
LOCALE_FILEBROWSER_SORT_NAMEDIRSFIRST,
LOCALE_FILEBROWSER_SORT_TYPE,
LOCALE_FILEBROWSER_SORT_DATE,
LOCALE_FILEBROWSER_SORT_SIZE
};
//------------------------------------------------------------------------
CFileBrowser::CFileBrowser()
{
commonInit();
base = "";
m_Mode = ModeFile;
}
CFileBrowser::CFileBrowser(const char * const _base, const tFileBrowserMode mode)
{
commonInit();
base = _base;
m_Mode = mode;
}
void CFileBrowser::commonInit()
{
frameBuffer = CFrameBuffer::getInstance();
//shoutcast
sc_init_dir = "/legacy/genrelist?k=" + g_settings.shoutcast_dev_id;
Filter = NULL;
use_filter = true;
Multi_Select = false;
Dirs_Selectable = false;
Dir_Mode = false;
Hide_records = false;
selected = 0;
selections.clear();
x = g_settings.screen_StartX + 20;
y = g_settings.screen_StartY + 20;
width = (g_settings.screen_EndX - g_settings.screen_StartX - 40);
height = (g_settings.screen_EndY - g_settings.screen_StartY - 40);
theight = g_Font[SNeutrinoSettings::FONT_TYPE_EVENTLIST_TITLE]->getHeight();
fheight = g_Font[SNeutrinoSettings::FONT_TYPE_FILEBROWSER_ITEM]->getHeight();
if (fheight == 0)
fheight = 1; /* avoid div by zero on invalid font */
foheight = g_Font[SNeutrinoSettings::FONT_TYPE_INFOBAR_SMALL]->getHeight()+6; //initial height value for buttonbar; TODO get value from buttonbar
liststart = 0;
listmaxshow = std::max(1,(int)(height - theight - 2 * foheight)/fheight);
//recalc height
height = theight + listmaxshow * fheight + 2 * foheight;
m_SMSKeyInput.setTimeout(SMSKEY_TIMEOUT);
}
//------------------------------------------------------------------------
CFileBrowser::~CFileBrowser()
{
}
//------------------------------------------------------------------------
CFile *CFileBrowser::getSelectedFile()
{
if ((!(filelist.empty())) && (!(filelist[selected].Name.empty())))
return &filelist[selected];
else
return NULL;
}
//------------------------------------------------------------------------
void CFileBrowser::ChangeDir(const std::string & filename, int selection)
{
std::string newpath;
if((m_Mode != ModeSC) && (filename == ".."))
{
std::string::size_type pos = Path.substr(0,Path.length()-1).rfind('/');
#ifdef ENABLE_MOVIEPLAYER_VLC
bool is_vlc = (strncmp(Path.c_str(), VLC_URI, strlen(VLC_URI)) == 0);
#endif
if (pos == std::string::npos)
{
newpath = Path;
}
else
{
#ifdef ENABLE_MOVIEPLAYER_VLC
if (is_vlc && (pos < strlen(VLC_URI) - 1))
newpath = VLC_URI;
else
#endif
newpath = Path.substr(0, pos + 1);
}
#ifdef ENABLE_MOVIEPLAYER_VLC
if (strncmp(is_vlc ? &(newpath.c_str()[strlen(VLC_URI)]) : newpath.c_str(), base.c_str(), base.length()) != 0)
return;
#endif
}
else
{
newpath=filename;
}
if(m_Mode != ModeSC && (newpath.rfind('/') != newpath.length()-1 || newpath.length() == 0))
{
newpath += '/';
}
filelist.clear();
Path = newpath;
name = newpath;
CFileList allfiles;
readDir(newpath, &allfiles);
// filter
CFileList::iterator file = allfiles.begin();
for(; file != allfiles.end() ; file++)
{
if(Filter != NULL && (!S_ISDIR(file->Mode)) && use_filter)
{
if(!Filter->matchFilter(file->Name))
{
continue;
}
if(Hide_records) {
int ext_pos = file->Name.rfind('.');
if( ext_pos > 0) {
std::string extension = file->Name.substr(ext_pos + 1, name.length() - ext_pos);
if(strcasecmp(extension.c_str(), "ts") == 0) {
std::string fname = file->Name.substr(0, ext_pos) + ".xml";
if(access(fname.c_str(), F_OK) == 0)
continue;
}
}
}
}
if(Dir_Mode && (!S_ISDIR(file->Mode)))
{
continue;
}
filelist.push_back(*file);
}
// sort result
sort(filelist.begin(), filelist.end(), sortBy[g_settings.filebrowser_sortmethod]);
selected = 0;
if ((selection != -1) && (selection < (int)filelist.size()))
selected = selection;
paintHead();
paint();
}
//------------------------------------------------------------------------
bool CFileBrowser::readDir(const std::string & dirname, CFileList* flist)
{
bool ret;
#ifdef ENABLE_INTERNETRADIO
if (m_Mode == ModeSC) {
ret = readDir_sc(dirname, flist);
}
else
#endif
#ifdef ENABLE_MOVIEPLAYER_VLC
if (strncmp(dirname.c_str(), VLC_URI, strlen(VLC_URI)) == 0)
{
ret = readDir_vlc(dirname, flist);
}
else
#endif
{
ret = readDir_std(dirname, flist);
}
return ret;
}
#ifdef ENABLE_MOVIEPLAYER_VLC
bool CFileBrowser::readDir_vlc(const std::string & dirname, CFileList* flist)
{
// printf("readDir_vlc %s\n",dirname.c_str());
std::string answer="";
char *dir_escaped = curl_escape(dirname.substr(strlen(VLC_URI)).c_str(), 0);
std::string url = m_baseurl;
url += dir_escaped;
curl_free(dir_escaped);
std::cout << "[FileBrowser] vlc URL: " << url << std::endl;
CURL *curl_handle;
CURLcode httpres;
/* init the curl session */
curl_handle = curl_easy_init();
/* timeout. 15 seconds should be enough */
curl_easy_setopt(curl_handle, CURLOPT_CONNECTTIMEOUT, 15);
/* specify URL to get */
curl_easy_setopt(curl_handle, CURLOPT_URL, url.c_str());
/* send all data to this function */
curl_easy_setopt(curl_handle, CURLOPT_WRITEFUNCTION, CurlWriteToString);
/* we pass our 'chunk' struct to the callback function */
curl_easy_setopt(curl_handle, CURLOPT_FILE, (void *)&answer);
/* Generate error if http error >= 400 occurs */
curl_easy_setopt(curl_handle, CURLOPT_FAILONERROR, 1);
/* error handling */
char error[CURL_ERROR_SIZE];
curl_easy_setopt(curl_handle, CURLOPT_ERRORBUFFER, error);
/* get it! */
httpres = curl_easy_perform(curl_handle);
/* cleanup curl stuff */
curl_easy_cleanup(curl_handle);
// std::cout << "Answer:" << std::endl << "----------------" << std::endl << answer << std::endl;
if (!answer.empty() && httpres == 0)
{
xmlDocPtr answer_parser = parseXml(answer.c_str());
if (answer_parser != NULL) {
xmlNodePtr element = xmlDocGetRootElement(answer_parser);
element = element->xmlChildrenNode;
char *ptr;
if (element == NULL) {
printf("[FileBrowser] vlc: Drive is not readable. Possibly no disc inserted\n");
CFile file;
file.Mode = S_IFDIR + 0777 ;
file.Name = dirname + "..";
file.Size = 0;
file.Time = 0;
flist->push_back(file);
} else {
while (element) {
CFile file;
ptr = xmlGetAttribute(element, "type");
if (strcmp(ptr, "directory")==0)
file.Mode = S_IFDIR + 0777 ;
else
file.Mode = S_IFREG + 0777 ;
file.Name = dirname + xmlGetAttribute(element, "name");
ptr = xmlGetAttribute(element, "size");
if (ptr)
file.Size = atoi(ptr);
else
file.Size = 0;
file.Time = 0;
element = element->xmlNextNode;
flist->push_back(file);
}
}
xmlFreeDoc(answer_parser);
return true;
}
}
/* since all CURL error messages use only US-ASCII characters, when can safely print them as if they were UTF-8 encoded */
if (httpres == 22) {
strcat(error, "\nProbably wrong vlc version\nPlease use vlc 0.8.5 or higher");
}
DisplayErrorMessage(error); // UTF-8
CFile file;
file.Name = dirname + "..";
file.Mode = S_IFDIR + 0777;
file.Size = 0;
file.Time = 0;
flist->push_back(file);
return false;
}
#endif /* ENABLE_MOVIEPLAYER_VLC */
#ifdef ENABLE_INTERNETRADIO
bool CFileBrowser::readDir_sc(const std::string & dirname, CFileList* flist)
{
#define GET_SHOUTCAST_TIMEOUT 60
/* how the shoutcast xml interfaces looks/works:
1st step: get http://www.shoutcast.com/sbin/newxml.phtml
example answer:
<genrelist>
...
<genre name="Trance"/>
<genre name=...
...
2nd step: get http://www.shoutcast.com/sbin/newxml.phtml?genre=Trance
example answer:
<stationlist>
<tunein base="/sbin/tunein-station.pls"/>
<station name="TechnoBase.FM - 24h Techno, Dance, Trance, House and More - 128k MP3" mt="audio/mpeg" id="524" br="128" genre="Techno Trance Dance House" ct="We aRe oNe" lc="4466"/>
<station name=...
...
3rd step: get/decode playlist http://www.shoutcast.com/sbin/tunein-station.pls?id=524
and add to neutrino playlist
4th step: play from neutrio playlist
*/
//shoutcast
const std::string sc_get_top500 = "/legacy/Top500?k=" + g_settings.shoutcast_dev_id;
const std::string sc_get_genre = "/legacy/stationsearch?k=" + g_settings.shoutcast_dev_id + "&search=";
const std::string sc_tune_in_base = "http://yp.shoutcast.com";
// printf("readDir_sc %s\n",dirname.c_str());
std::string answer="";
// char *dir_escaped = curl_escape(dirname.c_str(), 0);
std::string url = m_baseurl;
// url += dir_escaped;
// curl_free(dir_escaped);
url += dirname;
std::cout << "[FileBrowser] SC URL: " << url << std::endl;
CURL *curl_handle;
CURLcode httpres;
/* init the curl session */
curl_handle = curl_easy_init();
/* specify URL to get */
curl_easy_setopt(curl_handle, CURLOPT_URL, url.c_str());
/* send all data to this function */
curl_easy_setopt(curl_handle, CURLOPT_WRITEFUNCTION, CurlWriteToString);
/* we pass our 'chunk' struct to the callback function */
curl_easy_setopt(curl_handle, CURLOPT_FILE, (void *)&answer);
/* Generate error if http error >= 400 occurs */
curl_easy_setopt(curl_handle, CURLOPT_FAILONERROR, 1);
/* set timeout to 30 seconds */
curl_easy_setopt(curl_handle, CURLOPT_TIMEOUT, GET_SHOUTCAST_TIMEOUT);
/* error handling */
char error[CURL_ERROR_SIZE];
curl_easy_setopt(curl_handle, CURLOPT_ERRORBUFFER, error);
/* get it! */
httpres = curl_easy_perform(curl_handle);
/* cleanup curl stuff */
curl_easy_cleanup(curl_handle);
//std::cout << "Answer:" << std::endl << "----------------" << std::endl << answer << std::endl;
if (!answer.empty() && httpres == 0)
{
printf("CFileBrowser::readDir_sc: read done, size %d\n", answer.size());
xmlDocPtr answer_parser = parseXml(answer.c_str());
if (answer_parser != NULL) {
char *ptr;
unsigned char xml_decode = 0;
xmlNodePtr element = xmlDocGetRootElement(answer_parser);
if (strcmp(xmlGetName(element), "genrelist") == 0)
xml_decode = 1;
else if (strcmp(xmlGetName(element), "stationlist") == 0)
xml_decode = 2;
element = element->xmlChildrenNode;
if (element == NULL) {
printf("[FileBrowser] SC: Directory cannot be read.\n");
CFile file;
file.Mode = S_IFDIR + 0777 ;
file.Name = dirname + "..";
file.Size = 0;
file.Time = 0;
flist->push_back(file);
} else {
char * tunein_base = NULL;
if (xml_decode == 1) {
CFile file;
file.Mode = S_IFDIR + 0777 ;
file.Name = " Top500"; // use space to have it at the beginning of the list
file.Url = sc_get_top500;
file.Size = 0;
file.Time = 0;
flist->push_back(file);
} else if (xml_decode == 2) {
CFile file2;
file2.Mode = S_IFDIR + 0777 ;
file2.Name = "..";
file2.Url = sc_init_dir;
file2.Size = 0;
file2.Time = 0;
flist->push_back(file2);
}
while (element) {
CFile file;
if (xml_decode == 1) {
file.Mode = S_IFDIR + 0777 ;
file.Name = xmlGetAttribute(element, "name");
file.Url = sc_get_genre + file.Name;
file.Size = 0;
file.Time = 0;
flist->push_back(file);
}
else if (xml_decode == 2) {
ptr = xmlGetName(element);
if (ptr != NULL) {
if (strcmp(ptr, "tunein")==0) {
ptr = xmlGetAttribute(element, "base");
if (ptr)
tunein_base = ptr;
} else if (strcmp(ptr, "station")==0) {
ptr = xmlGetAttribute(element, "mt");
if (ptr && (strcmp(ptr, "audio/mpeg")==0)) {
file.Mode = S_IFREG + 0777 ;
file.Name = xmlGetAttribute(element, "name");
file.Url = sc_tune_in_base + tunein_base + (std::string)"?id=" + xmlGetAttribute(element, "id") + (std::string)"&k=" + g_settings.shoutcast_dev_id;
//printf("adding %s (%s)\n", file.Name.c_str(), file.Url.c_str());
ptr = xmlGetAttribute(element, "br");
if (ptr) {
file.Size = atoi(ptr);
file.Time = atoi(ptr);
} else {
file.Size = 0;
file.Time = 0;
}
flist->push_back(file);
}
}
}
}
element = element->xmlNextNode;
}
}
xmlFreeDoc(answer_parser);
return true;
}
}
/* since all CURL error messages use only US-ASCII characters, when can safely print them as if they were UTF-8 encoded */
if (httpres == 22) {
strcat(error, "\nProbably wrong link.");
}
printf("CFileBrowser::readDir_sc: httpres %d error, %s\n", httpres, error);
DisplayErrorMessage(error); // UTF-8
CFile file;
file.Name = dirname + "..";
file.Mode = S_IFDIR + 0777;
file.Size = 0;
file.Time = 0;
flist->push_back(file);
return false;
}
#endif
bool CFileBrowser::readDir_std(const std::string & dirname, CFileList* flist)
{
// printf("readDir_std %s\n",dirname.c_str());
stat_struct statbuf;
dirent_struct **namelist;
int n;
n = my_scandir(dirname.c_str(), &namelist, 0, my_alphasort);
if (n < 0)
{
perror(("Filebrowser scandir: "+dirname).c_str());
return false;
}
for(int i = 0; i < n;i++)
{
CFile file;
if(strcmp(namelist[i]->d_name,".") != 0)
{
file.Name = dirname + namelist[i]->d_name;
// printf("file.Name: '%s', getFileName: '%s' getPath: '%s'\n",file.Name.c_str(),file.getFileName().c_str(),file.getPath().c_str());
if(my_stat((file.Name).c_str(),&statbuf) != 0)
perror("stat error");
else
{
file.Mode = statbuf.st_mode;
file.Size = statbuf.st_size;
file.Time = statbuf.st_mtime;
flist->push_back(file);
}
}
free(namelist[i]);
}
free(namelist);
return true;
}
//------------------------------------------------------------------------
bool CFileBrowser::exec(const char * const dirname)
{
neutrino_msg_t msg;
neutrino_msg_data_t data;
bool res = false;
#ifdef ENABLE_INTERNETRADIO
if (m_Mode == ModeSC) {
m_baseurl = base;
}
#endif
name = dirname;
std::replace(name.begin(), name.end(), '\\', '/');
paintHead();
ChangeDir(name);
paint();
paintFoot();
int oldselected = selected;
uint64_t timeoutEnd = CRCInput::calcTimeoutEnd(g_settings.timing[SNeutrinoSettings::TIMING_FILEBROWSER]);
bool loop=true;
while (loop)
{
g_RCInput->getMsgAbsoluteTimeout( &msg, &data, &timeoutEnd );
neutrino_msg_t msg_repeatok = msg & ~CRCInput::RC_Repeat;
if ( msg <= CRCInput::RC_MaxRC )
timeoutEnd = CRCInput::calcTimeoutEnd(g_settings.timing[SNeutrinoSettings::TIMING_FILEBROWSER]);
if(!CRCInput::isNumeric(msg))
{
m_SMSKeyInput.resetOldKey();
}
if (msg == CRCInput::RC_yellow)
{
if ((Multi_Select) && (selected < filelist.size()))
{
if(filelist[selected].getFileName() != "..")
{
if( (S_ISDIR(filelist[selected].Mode) && Dirs_Selectable) || !S_ISDIR(filelist[selected].Mode) )
{
filelist[selected].Marked = !filelist[selected].Marked;
paintItem(selected - liststart);
}
}
msg_repeatok = CRCInput::RC_down; // jump to next item
}
}
if ((msg == CRCInput::RC_red) || msg == CRCInput::RC_page_down)
{
selected += listmaxshow;
if (selected >= filelist.size()) {
if (((filelist.size() / listmaxshow) + 1) * listmaxshow == filelist.size() + listmaxshow) // last page has full entries
selected = 0;
else
selected = selected < (((filelist.size() / listmaxshow) + 1) * listmaxshow) ? (filelist.size() - 1) : 0;
}
liststart = (selected / listmaxshow) * listmaxshow;
paint();
}
else if ((msg == CRCInput::RC_green) || (msg == CRCInput::RC_page_up) )
{
if ((int(selected)-int(listmaxshow))<0)
selected=filelist.size()-1;
else
selected -= listmaxshow;
liststart = (selected/listmaxshow)*listmaxshow;
paint();
}
else if (msg_repeatok == CRCInput::RC_up)
{
int prevselected=selected;
if(selected==0)
{
selected = filelist.size()-1;
}
else
selected--;
paintItem(prevselected - liststart);
unsigned int oldliststart = liststart;
liststart = (selected/listmaxshow)*listmaxshow;
if(oldliststart!=liststart)
{
paint();
}
else
{
paintItem(selected - liststart);
}
}
else if (msg_repeatok == CRCInput::RC_down)
{
if (!(filelist.empty()))
{
int prevselected=selected;
selected = (selected + 1) % filelist.size();
paintItem(prevselected - liststart);
unsigned int oldliststart = liststart;
liststart = (selected/listmaxshow)*listmaxshow;
if(oldliststart!=liststart)
paint();
else
paintItem(selected - liststart);
}
}
else if ( ( msg == CRCInput::RC_timeout ) )
{
selected = oldselected;
loop=false;
}
else if ( msg == CRCInput::RC_right )
{
if (!(filelist.empty()))
{
if (S_ISDIR(filelist[selected].Mode))
{
#ifdef ENABLE_INTERNETRADIO
if (m_Mode == ModeSC) {
ChangeDir(filelist[selected].Url);
} else
#endif
{
if (filelist[selected].getFileName() != "..") {
selections.push_back(selected);
ChangeDir(filelist[selected].Name);
}
}
}
}
}
else if ( msg == CRCInput::RC_left )
{
#ifdef ENABLE_INTERNETRADIO
if (m_Mode == ModeSC)
{
for(unsigned int i = 0; i < filelist.size();i++) {
if (S_ISDIR(filelist[i].Mode) && filelist[i].getFileName() == "..") {
ChangeDir(filelist[i].Url);
break;
}
}
}
else
#endif
if (selections.size() > 0)
{
ChangeDir("..",selections.back());
selections.pop_back();
} else
{
ChangeDir("..");
}
}
else if ( msg == CRCInput::RC_blue )
{
if(Filter != NULL)
{
use_filter = !use_filter;
paintFoot();
ChangeDir(Path);
}
}
else if ( msg == CRCInput::RC_home )
{
loop = false;
}
else if ( msg == CRCInput::RC_spkr && strncmp(Path.c_str(), VLC_URI, strlen(VLC_URI)) != 0) //Not in vlc mode
{
if(".." !=(filelist[selected].getFileName().substr(0,2))) // do not delete that
{
std::stringstream _msg;
_msg << g_Locale->getText(LOCALE_FILEBROWSER_DODELETE1) << " ";
if (filelist[selected].getFileName().length() > 25)
{
_msg << filelist[selected].getFileName().substr(0, 25) << "...";
}
else
_msg << filelist[selected].getFileName();
_msg << " " << g_Locale->getText(LOCALE_FILEBROWSER_DODELETE2);
if (ShowMsgUTF(LOCALE_FILEBROWSER_DELETE, _msg.str(), CMessageBox::mbrNo, CMessageBox::mbYes|CMessageBox::mbNo)==CMessageBox::mbrYes)
{
recursiveDelete(filelist[selected].Name.c_str());
if(".ts" ==(filelist[selected].getFileName().substr(filelist[selected].getFileName().length()-3,filelist[selected].getFileName().length())))//if bla.ts
{
recursiveDelete((filelist[selected].Name.substr(0,filelist[selected].Name.length()-7)+".xml").c_str());//remove bla.xml von bla.ts
}
ChangeDir(Path);
}
}
}
else if (msg == CRCInput::RC_ok)
{
if (!(filelist.empty()))
{
if (filelist[selected].getFileName() == "..")
{
#ifdef ENABLE_INTERNETRADIO
if (m_Mode == ModeSC)
ChangeDir(filelist[selected].Url);
else
#endif
{
if (selections.size() > 0)
{
ChangeDir("..",selections.back());
selections.pop_back();
} else
{
std::string::size_type pos = Path.substr(0,Path.length()-1).rfind('/');
if (pos != std::string::npos) {
ChangeDir("..");
}
#if 0 // quick fix for #253, i dont think we ever need to return "/" as selected -- focus
else {
loop = false;
res = true;
filelist[selected].Name = "/";
}
#endif
}
}
}
else
{
std::string filename = filelist[selected].Name;
if ( filename.length() > 1 )
{
if((!Multi_Select) && S_ISDIR(filelist[selected].Mode) && !Dir_Mode)
{
#ifdef ENABLE_INTERNETRADIO
if (m_Mode == ModeSC)
ChangeDir(filelist[selected].Url);
else
#endif
ChangeDir(filelist[selected].Name);
}
else
{
filelist[selected].Marked = true;
loop = false;
res = true;
}
}
}
}
}
else if (msg==CRCInput::RC_help)
{
if (++g_settings.filebrowser_sortmethod >= FILEBROWSER_NUMBER_OF_SORT_VARIANTS)
g_settings.filebrowser_sortmethod = 0;
sort(filelist.begin(), filelist.end(), sortBy[g_settings.filebrowser_sortmethod]);
paint();
paintFoot();
}
else if (CRCInput::isNumeric(msg_repeatok))
{
if (!(filelist.empty()))
SMSInput(msg_repeatok);
}
else if (msg == CRCInput::RC_sat || msg == CRCInput::RC_favorites) {
//FIXME do nothing ?
}
else
{
if ( CNeutrinoApp::getInstance()->handleMsg( msg, data ) & messages_return::cancel_all )
{
loop = false;
}
}
}
hide();
selected_filelist.clear();
if(res && Multi_Select)
{
CProgressWindow * progress = new CProgressWindow();
progress->setTitle(LOCALE_FILEBROWSER_SCAN);
progress->exec(NULL,"");
for(unsigned int i = 0; i < filelist.size();i++)
if(filelist[i].Marked)
{
if(S_ISDIR(filelist[i].Mode)) {
#ifdef ENABLE_INTERNETRADIO
if (m_Mode == ModeSC)
addRecursiveDir(&selected_filelist,filelist[i].Url, true, progress);
else
#endif
addRecursiveDir(&selected_filelist,filelist[i].Name, true, progress);
} else
selected_filelist.push_back(filelist[i]);
}
progress->hide();
delete progress;
}
return res;
}
//------------------------------------------------------------------------
void CFileBrowser::addRecursiveDir(CFileList * re_filelist, std::string rpath, bool bRootCall, CProgressWindow * progress)
{
neutrino_msg_t msg;
neutrino_msg_data_t data;
int n;
//printf("addRecursiveDir %s\n",rpath.c_str());
if (bRootCall) bCancel=false;
g_RCInput->getMsg_us(&msg, &data, 1);
if (msg==CRCInput::RC_home)
{
// home key cancel scan
bCancel=true;
}
else if (msg!=CRCInput::RC_timeout)
{
// other event, save to low priority queue
g_RCInput->postMsg( msg, data, false );
}
if(bCancel)
return;
if ((m_Mode != ModeSC) && ((rpath.empty()) || ((*rpath.rbegin()) != '/')))
{
rpath += '/';
}
CFileList tmplist;
if(!readDir(rpath, &tmplist))
{
perror(("Recursive scandir: "+rpath).c_str());
}
else
{
n = tmplist.size();
if(progress)
{
progress->showStatusMessageUTF(FILESYSTEM_ENCODING_TO_UTF8_STRING(rpath));
}
for(int i = 0; i < n;i++)
{
if(progress)
{
progress->showGlobalStatus(100/n*i);
}
std::string basename = tmplist[i].Name.substr(tmplist[i].Name.rfind('/')+1);
if( basename != ".." )
{
if(Filter != NULL && (!S_ISDIR(tmplist[i].Mode)) && use_filter)
{
if(!Filter->matchFilter(tmplist[i].Name))
{
continue;
}
}
if(!S_ISDIR(tmplist[i].Mode))
re_filelist->push_back(tmplist[i]);
else
addRecursiveDir(re_filelist,tmplist[i].Name, false, progress);
}
}
}
}
//------------------------------------------------------------------------
void CFileBrowser::hide()
{
frameBuffer->paintBackgroundBoxRel(x,y, width,height);
}
//------------------------------------------------------------------------
void CFileBrowser::paintItem(unsigned int pos)
{
int colwidth1, colwidth2, colwidth3, colwidth1_dir, colwidth2_dir;
int c_rad_small;
uint8_t color;
fb_pixel_t bgcolor;
int ypos = y+ theight+0 + pos*fheight;
CFile * actual_file = NULL;
std::string fileicon;
colwidth2_dir = 180;
colwidth1_dir = width - 35 - colwidth2_dir - 10;
if (liststart + pos == selected)
{
color = COL_MENUCONTENTSELECTED;
bgcolor = COL_MENUCONTENTSELECTED_PLUS_0;
c_rad_small = RADIUS_SMALL;
// paintFoot();
}
else
{
color = COL_MENUCONTENT;//DARK;
bgcolor = COL_MENUCONTENT_PLUS_0;//DARK;
c_rad_small = 0;
}
frameBuffer->paintBoxRel(x,ypos, width- 15, fheight, COL_MENUCONTENT_PLUS_0);
frameBuffer->paintBoxRel(x,ypos, width- 15, fheight, bgcolor, c_rad_small);
if( (liststart + pos) < filelist.size() )
{
actual_file = &filelist[liststart+pos];
if (actual_file->Marked)
{
color = COL_MENUCONTENTINACTIVE; //+= 2; FIXME
bgcolor = (liststart + pos == selected) ? COL_MENUCONTENTSELECTED_PLUS_2 : COL_MENUCONTENT_PLUS_2;
}
if (g_settings.filebrowser_showrights == 0 && S_ISREG(actual_file->Mode))
{
colwidth2 = 0;
colwidth3 = 90;
}
else
{
colwidth2 = 90;
colwidth3 = 90;
}
colwidth1 = width - 35 - colwidth2 - colwidth3 - 10;
if ( actual_file->Name.length() > 0 )
{
if (liststart+pos==selected)
CVFD::getInstance()->showMenuText(0, FILESYSTEM_ENCODING_TO_UTF8_STRING(actual_file->getFileName()).c_str(), -1, true); // UTF-8
switch(actual_file->getType())
{
case CFile::FILE_CDR:
case CFile::FILE_MP3:
case CFile::FILE_OGG:
case CFile::FILE_WAV:
#ifdef ENABLE_FLAC
case CFile::FILE_FLAC:
#endif
fileicon = NEUTRINO_ICON_MP3;
// color = COL_MENUCONTENT;
break;
case CFile::FILE_DIR:
fileicon = NEUTRINO_ICON_FOLDER;
break;
case CFile::FILE_PICTURE:
case CFile::FILE_TEXT:
default:
fileicon = NEUTRINO_ICON_FILE;
}
frameBuffer->paintIcon(fileicon, x+5 , ypos + (fheight-16) / 2 );
g_Font[SNeutrinoSettings::FONT_TYPE_FILEBROWSER_ITEM]->RenderString(x + 35, ypos + fheight, colwidth1 - 10 , FILESYSTEM_ENCODING_TO_UTF8_STRING(actual_file->getFileName()), color, 0, true); // UTF-8
if( S_ISREG(actual_file->Mode) )
{
if (g_settings.filebrowser_showrights != 0)
{
const char * attribute = "xwr";
char modestring[9 + 1];
for (int m = 8; m >= 0; m--)
{
modestring[8 - m] = (actual_file->Mode & (1 << m)) ? attribute[m % 3] : '-';
}
modestring[9] = 0;
g_Font[SNeutrinoSettings::FONT_TYPE_FILEBROWSER_ITEM]->RenderString(x + 35 + colwidth1 , ypos+ fheight, colwidth2 - 10, modestring, color, 0, true); // UTF-8
}
#define GIGABYTE 1073741824LL
#define MEGABYTE 1048576LL
#define KILOBYTE 1024LL
char tmpstr[256];
const char *unit = "";
int64_t factor = 0;
if (actual_file->Size >= GIGABYTE)
{
factor = GIGABYTE;
unit = "G";
}
else if (actual_file->Size >= MEGABYTE)
{
factor = MEGABYTE;
unit = "M";
}
else if (actual_file->Size >= KILOBYTE)
{
factor = KILOBYTE;
unit = "k";
}
if (factor)
{
int a = actual_file->Size / factor;
int b = (actual_file->Size - a * factor) * 1000 / factor;
snprintf(tmpstr, sizeof(tmpstr), "%d.%03d%s", a, b, unit);
}
else
snprintf(tmpstr,sizeof(tmpstr),"%d", (int)actual_file->Size);
g_Font[SNeutrinoSettings::FONT_TYPE_FILEBROWSER_ITEM]->RenderString(x + 35 + colwidth1 + colwidth2, ypos+ fheight, colwidth3 - 10, tmpstr, color);
}
if( S_ISDIR(actual_file->Mode) )
{
char timestring[18];
time_t rawtime;
rawtime = actual_file->Time;
strftime(timestring, 18, "%d-%m-%Y %H:%M", gmtime(&rawtime));
g_Font[SNeutrinoSettings::FONT_TYPE_FILEBROWSER_ITEM]->RenderString(x + 35 + colwidth1_dir, ypos+ fheight, colwidth2_dir - 10, timestring, color);
}
}
}
else
frameBuffer->paintBoxRel(x,ypos, width- 15, fheight, COL_MENUCONTENT_PLUS_0/*DARK*/);
}
//------------------------------------------------------------------------
void CFileBrowser::paintHead()
{
char *l_name;
int i = 0;
int l;
frameBuffer->paintBoxRel(x,y, width,theight+0, COL_MENUHEAD_PLUS_0, RADIUS_MID, CORNER_TOP);
#ifdef ENABLE_INTERNETRADIO
if(m_Mode == ModeSC)
l = asprintf(&l_name, "%s %s", g_Locale->getText(LOCALE_AUDIOPLAYER_ADD_SC), FILESYSTEM_ENCODING_TO_UTF8_STRING(name).c_str());
else
#endif
l = asprintf(&l_name, "%s %s", g_Locale->getText(LOCALE_FILEBROWSER_HEAD), FILESYSTEM_ENCODING_TO_UTF8_STRING(name).c_str());
if (l < 1) /* at least 1 for the " " space */
{
perror("CFileBrowser::paintHead asprintf");
return;
}
/* too long? Leave out the "Filebrowser" or "Shoutcast" prefix
* the allocated space is sufficient since it is surely shorter than before */
if (g_Font[SNeutrinoSettings::FONT_TYPE_EVENTLIST_TITLE]->getRenderWidth(l_name) > width - 11)
l = sprintf(l_name, "%s", FILESYSTEM_ENCODING_TO_UTF8_STRING(name).c_str());
if (l_name[l - 1] == '/')
l_name[--l] = '\0';
/* still too long? the last part is probably more interesting than the first part... */
while ((g_Font[SNeutrinoSettings::FONT_TYPE_EVENTLIST_TITLE]->getRenderWidth(&l_name[i]) > width - 11)
&& (i < l))
i++;
g_Font[SNeutrinoSettings::FONT_TYPE_EVENTLIST_TITLE]->RenderString(x+10,y+theight+1, width-11, &l_name[i], COL_MENUHEAD, 0, true);
free(l_name);
}
bool chooserDir(char *setting_dir, bool test_dir, const char *action_str, size_t str_leng)
{
std::string tmp_setting_dir = setting_dir;
if(chooserDir(tmp_setting_dir, test_dir, action_str)){
strncpy(setting_dir,tmp_setting_dir.c_str(), str_leng);
return true;
}
return false;
}
bool chooserDir(std::string &setting_dir, bool test_dir, const char *action_str)
{
const char *wrong_str = "Wrong/unsupported";
CFileBrowser b;
b.Dir_Mode=true;
if (b.exec(setting_dir.c_str())) {
const char * newdir = b.getSelectedFile()->Name.c_str();
if(test_dir && check_dir(newdir)){
printf("%s %s dir %s\n",wrong_str ,action_str, newdir);
return false;
}else {
setting_dir = b.getSelectedFile()->Name;
return true;
}
}
return false;
}
//------------------------------------------------------------------------
const struct button_label FileBrowserFilterButton[2] =
{
{ NEUTRINO_ICON_BUTTON_BLUE , LOCALE_FILEBROWSER_FILTER_INACTIVE },
{ NEUTRINO_ICON_BUTTON_BLUE , LOCALE_FILEBROWSER_FILTER_ACTIVE },
};
void CFileBrowser::paintFoot()
{
struct button_label FileBrowserButtons[4] =
{
{ NEUTRINO_ICON_BUTTON_RED , LOCALE_FILEBROWSER_NEXTPAGE },
{ NEUTRINO_ICON_BUTTON_GREEN , LOCALE_FILEBROWSER_PREVPAGE },
{ NEUTRINO_ICON_BUTTON_YELLOW, LOCALE_FILEBROWSER_MARK },
};
const struct button_label FileBrowserButtons2[3] =
{
{ NEUTRINO_ICON_BUTTON_OKAY , LOCALE_FILEBROWSER_SELECT },
{ NEUTRINO_ICON_BUTTON_HELP_SMALL , sortByNames[g_settings.filebrowser_sortmethod] },
{ NEUTRINO_ICON_BUTTON_MUTE_SMALL, LOCALE_FILEBROWSER_DELETE },
};
// int iw = 0, ih = 0;
// frameBuffer->getIconSize(NEUTRINO_ICON_BUTTON_RED, &iw, &ih);
//Background
int by0 = y + height - (2 * foheight );
frameBuffer->paintBoxRel(x, by0, width, (2 * foheight ), COL_INFOBAR_SHADOW_PLUS_1, RADIUS_MID, CORNER_BOTTOM);
//Second Line (bottom, top)
int by2 = by0 + foheight;
if (!(filelist.empty()))
{
int idx = 1;
int num_buttons = Multi_Select ? 3 : 2;
if (Filter != NULL)
{
FileBrowserButtons[num_buttons].button = FileBrowserFilterButton[!use_filter].button;
FileBrowserButtons[num_buttons].locale = FileBrowserFilterButton[!use_filter].locale;
num_buttons++;
}
//red, green, yellow button
::paintButtons(x, by0, 0, num_buttons, FileBrowserButtons, width, foheight);
/* TODO: the changing existence of the OK button makes the sort button
* shift its place :-( */
num_buttons = 1;
//OK-Button
if ((filelist[selected].getType() != CFile::FILE_UNKNOWN) || S_ISDIR(filelist[selected].Mode))
{
idx = 0;
num_buttons++;
}
if (strncmp(Path.c_str(), VLC_URI, strlen(VLC_URI)) != 0) // No delete in vlc mode
num_buttons++;
/* width-26 to leave room for the SMSinput indicator */
::paintButtons(x, by2, 0, num_buttons, &(FileBrowserButtons2[idx]), width - 26, foheight);
if(m_SMSKeyInput.getOldKey()!=0)
{
char cKey[2]={m_SMSKeyInput.getOldKey(),0};
cKey[0] = toupper(cKey[0]);
int len = g_Font[SNeutrinoSettings::FONT_TYPE_INFOBAR_SMALL]->getRenderWidth(cKey);
g_Font[SNeutrinoSettings::FONT_TYPE_INFOBAR_SMALL]->RenderString(x + width - 10 - len, by2 + foheight, len, cKey, COL_MENUHEAD, 0, true);
}
}
}
//------------------------------------------------------------------------
void CFileBrowser::paint()
{
liststart = (selected/listmaxshow)*listmaxshow;
// if (filelist[0].Name.length() != 0)
// frameBuffer->paintIcon(NEUTRINO_ICON_BUTTON_HELP, x+ width- 30, y+ 5 );
CVFD::getInstance()->setMode(CVFD::MODE_MENU_UTF8, g_Locale->getText(LOCALE_FILEBROWSER_HEAD));
for(unsigned int count=0;count<listmaxshow;count++)
paintItem(count);
//scrollbar
int ypos = y+ theight;
int sb = fheight* listmaxshow;
frameBuffer->paintBoxRel(x+ width- 15,ypos, 15, sb, COL_MENUCONTENT_PLUS_1);
int sbc= ((filelist.size()- 1)/ listmaxshow)+ 1;
int sbs= (selected/listmaxshow);
frameBuffer->paintBoxRel(x+ width- 13, ypos+ 2+ sbs*(sb-4)/sbc, 11, (sb-4)/sbc, COL_MENUCONTENT_PLUS_3, RADIUS_SMALL);
}
//------------------------------------------------------------------------
void CFileBrowser::SMSInput(const neutrino_msg_t msg)
{
unsigned char key = m_SMSKeyInput.handleMsg(msg);
unsigned int i;
for(i=(selected+1) % filelist.size(); i != selected ; i= (i+1) % filelist.size())
{
if(tolower(filelist[i].getFileName()[0]) == key)
{
break;
}
}
int prevselected=selected;
selected=i;
paintItem(prevselected - liststart);
unsigned int oldliststart = liststart;
liststart = (selected/listmaxshow)*listmaxshow;
if(oldliststart!=liststart)
{
paint();
}
else
{
paintItem(selected - liststart);
}
}
//------------------------------------------------------------------------
void CFileBrowser::recursiveDelete(const char* file)
{
stat_struct statbuf;
dirent_struct **namelist;
int n;
printf("Delete %s\n", file);
if(my_lstat(file,&statbuf) == 0)
{
if(S_ISDIR(statbuf.st_mode))
{
n = my_scandir(file, &namelist, 0, my_alphasort);
while(n--)
{
if(strcmp(namelist[n]->d_name, ".")!=0 && strcmp(namelist[n]->d_name, "..")!=0)
{
std::string fullname = (std::string)file + "/" + namelist[n]->d_name;
recursiveDelete(fullname.c_str());
}
free(namelist[n]);
}
free(namelist);
rmdir(file);
}
else
{
unlink(file);
}
}
else
perror(file);
}