/* Neutrino-GUI - DBoxII-Project Copyright (C) 2001 Steffen Hehn 'McClean' Copyright (C) 2007-2009, 2011-2012 Stefan Seyfried 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 #endif /* include before to enable 64 bit file offsets */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #if LIBCURL_VERSION_NUM < 0x071507 #include #endif #include #include #ifndef __USE_FILE_OFFSET64 //#error not using 64 bit file offsets #endif #define SMSKEY_TIMEOUT 2000 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); const char *key = ""; if (CRCInput::isNumeric(msg)) { int n = CRCInput::getNumericValue(msg); const char *Chars[10] = { "0", "1", "abc2", "def3", "ghi4", "jkl5", "mno6", "pqrs7", "tuv8", "wxyz9" }; if (timeoutNotReached) { key = Chars[n]; while (*key && *key != m_oldKey) key++; if (*key) key++; } if (!*key) key = Chars[n]; } 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; } #if 0 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; } #endif 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; } // Sorts alphabetically with Directories first bool sortByNameDirsFirst(const CFile& a, const CFile& b) { 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; m_SMSKeyInput.setTimeout(SMSKEY_TIMEOUT); //fontInit(); } void CFileBrowser::fontInit() { fnt_title = g_Font[SNeutrinoSettings::FONT_TYPE_MENU_TITLE]; fnt_item = g_Font[SNeutrinoSettings::FONT_TYPE_FILEBROWSER_ITEM]; fnt_foot = g_Font[SNeutrinoSettings::FONT_TYPE_MENU_FOOT]; width = frameBuffer->getWindowWidth(); height = frameBuffer->getWindowHeight(); x = getScreenStartX(width); header_height = fnt_title->getHeight(); item_height = fnt_item->getHeight(); if (item_height == 0) item_height = 1; /* avoid div by zero on invalid font */ footer_height = paintFoot(false); smskey_width = fnt_foot->getRenderWidth("M") + OFFSET_INNER_MID; liststart = 0; listmaxshow = std::max(1,(int)(height - header_height - footer_height - OFFSET_SHADOW)/item_height); //recalc height height = header_height + listmaxshow*item_height + footer_height + OFFSET_SHADOW; y = getScreenStartY(height); } 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('/'); if (pos == std::string::npos) newpath = Path; else newpath = Path.substr(0, pos + 1); } 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 && !file->isDir() && 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, F_OK) == 0) continue; } } } } if (Dir_Mode && !file->isDir()) 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(); paintFoot(); } bool CFileBrowser::readDir(const std::string & dirname, CFileList* flist) { #ifdef ENABLE_INTERNETRADIO if (m_Mode == ModeSC) return readDir_sc(dirname, flist); else #endif return readDir_std(dirname, flist); } #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: ... = 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", (int)answer.size()); xmlDocPtr answer_parser = parseXml(answer.c_str()); if (answer_parser != NULL) { const char *ptr = NULL; 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 = xmlChildrenNode(element); 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 { const 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 ; const char *eptr = xmlGetAttribute(element, "name"); if(eptr) file.Name = eptr; std::string fname_temp = file.Name; fname_temp = str_replace(" ", "%20", fname_temp); fname_temp = str_replace("&", "%26", fname_temp); file.Name = fname_temp; 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 ; const char *aptr = xmlGetAttribute(element, "name"); if(aptr) file.Name = aptr; const char *idptr = xmlGetAttribute(element, "id"); std::string id; if(idptr) id = idptr; file.Url = sc_tune_in_base + tunein_base + (std::string)"?id=" + 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 = xmlNextNode(element); } } 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()); struct stat64 statbuf; dirent64 **namelist; int n; std::string to_scan_dir = dirname; n = scandir64(to_scan_dir.c_str(), &namelist, 0, alphasort64); if (n < 0) { std::string scn_err = "Filebrowser scandir: " + to_scan_dir + " "; to_scan_dir = "/media/"; dprintf(DEBUG_NORMAL, "\033[33m[CFileBrowser]\[%s - %d], %s failed, %s, try fallback to [%s]\033[0m\n", __func__, __LINE__, scn_err.c_str(), strerror(errno), to_scan_dir.c_str()); n = scandir64(to_scan_dir.c_str(), &namelist, 0, alphasort64); name = to_scan_dir; } if (n < 0){ //fallback failed perror(("Filebrowser scandir: "+to_scan_dir).c_str()); return false; } for (int i = 0; i < n; i++) { CFile file; if(strcmp(namelist[i]->d_name,".") != 0) { file.Name = to_scan_dir + 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(stat64((file.Name).c_str(),&statbuf) != 0) fprintf(stderr, "stat '%s' error: %m\n", file.Name.c_str()); 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::checkBD(CFile &file) { if (file.isDir()) { std::string bdmv = file.Name + "/BDMV/index.bdmv"; if (access(bdmv.c_str(), F_OK) == 0) return true; } return false; } bool CFileBrowser::exec(const char * const dirname) { neutrino_msg_t msg; neutrino_msg_data_t data; bool res = false; menu_ret = menu_return::RETURN_REPAINT; playlistmode = false; #ifdef ENABLE_INTERNETRADIO if (m_Mode == ModeSC) { m_baseurl = base; } #endif name = dirname; std::replace(name.begin(), name.end(), '\\', '/'); fontInit(); //paintHead(); int selection = -1; if (name == Path) selection = selected; ChangeDir(name, selection); unsigned int oldselected = selected; int timeout = g_settings.timing[SNeutrinoSettings::TIMING_FILEBROWSER]; uint64_t timeoutEnd = CRCInput::calcTimeoutEnd(timeout); 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(timeout); if(!CRCInput::isNumeric(msg)) { m_SMSKeyInput.resetOldKey(); paintSMSKey(); } if (msg == CRCInput::RC_blue) { if(Filter != NULL) { use_filter = !use_filter; ChangeDir(Path); } } else if (msg == CRCInput::RC_home) { loop = false; } else if (msg == CRCInput::RC_left) { #ifdef ENABLE_INTERNETRADIO if (m_Mode == ModeSC) { for(unsigned int i = 0; i < filelist.size();i++) { if (filelist[i].isDir() && filelist[i].getFileName() == "..") { ChangeDir(filelist[i].Url); break; } } } else #endif if (!selections.empty()) { ChangeDir("..",selections.back()); selections.pop_back(); } else { ChangeDir(".."); } } else if (msg == NeutrinoMessages::STANDBY_ON || msg == NeutrinoMessages::LEAVE_ALL || msg == NeutrinoMessages::SHUTDOWN || msg == NeutrinoMessages::SLEEPTIMER) { menu_ret = menu_return::RETURN_EXIT_ALL; loop = false; g_RCInput->postMsg(msg, data); } else if (msg == CRCInput::RC_timeout) { selected = oldselected; loop = false; } else if (msg > CRCInput::RC_MaxRC) { if (CNeutrinoApp::getInstance()->handleMsg( msg, data ) & messages_return::cancel_all) { menu_ret = menu_return::RETURN_EXIT_ALL; loop = false; } } if ((filelist.empty())) continue; if (msg == CRCInput::RC_yellow || msg == CRCInput::RC_play || msg == CRCInput::RC_playpause) { if ((Multi_Select) && (selected < filelist.size())) { if(filelist[selected].getFileName() != "..") { if(!filelist[selected].isDir() || Dirs_Selectable) { filelist[selected].Marked = !filelist[selected].Marked; msg_repeatok = CRCInput::RC_down; // jump to next item } } } } if (msg_repeatok == CRCInput::RC_up) { unsigned int prevselected=selected; unsigned int prevliststart = liststart; if (selected) selected --; else selected = filelist.size() - 1; liststart = (selected/listmaxshow)*listmaxshow; if(prevliststart != liststart) { paint(); } else { paintItem(prevselected - prevliststart); paintItem(selected - liststart); } } else if (msg_repeatok == CRCInput::RC_down) { unsigned int prevselected=selected; unsigned int prevliststart = liststart; selected = (selected + 1) % filelist.size(); liststart = (selected/listmaxshow)*listmaxshow; if(prevliststart != liststart) { paint(); } else { paintItem(prevselected - prevliststart); paintItem(selected - liststart); } } else if (msg == CRCInput::RC_right) { if (filelist[selected].isDir()) { #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 == (neutrino_msg_t) g_settings.key_pagedown) { unsigned int last = filelist.size() - 1; if (selected != last && selected + listmaxshow >= filelist.size()) { selected = last; } else { selected = (selected == last) ? 0 : selected + listmaxshow; liststart = (selected / listmaxshow) * listmaxshow; } paint(); } else if (msg == (neutrino_msg_t) g_settings.key_pageup) { if (selected && selected < listmaxshow) { selected = 0; } else { selected = selected ? selected - listmaxshow : filelist.size() - 1; liststart = (selected/listmaxshow)*listmaxshow; } paint(); } else if (msg == CRCInput::RC_spkr) { 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 (ShowMsg(LOCALE_FILEBROWSER_DELETE, _msg.str(), CMsgBox::mbrYes, CMsgBox::mbNoYes)==CMsgBox::mbrYes) { std::string n = filelist[selected].Name; recursiveDelete(n.c_str()); if(n.length() > 3 && ".ts" == n.substr(n.length() - 3)) recursiveDelete((n.substr(0, n.length() - 2) + "xml").c_str()); ChangeDir(Path); } } } else if (msg == CRCInput::RC_ok) { bool has_selected = false; if (Multi_Select) { for(unsigned int i = 0; i < filelist.size();i++) { if(filelist[i].Marked) { has_selected = true; break; } } } if (has_selected) { res = true; break; } if (filelist[selected].getFileName() == "..") { #ifdef ENABLE_INTERNETRADIO if (m_Mode == ModeSC) ChangeDir(filelist[selected].Url); else #endif { if (!selections.empty() ) { 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(".."); } } } else { bool return_dir = Hide_records && checkBD(filelist[selected]); if(!return_dir && filelist[selected].isDir() && !Dir_Mode) { #ifdef ENABLE_INTERNETRADIO if (m_Mode == ModeSC) ChangeDir(filelist[selected].Url); else #endif { selections.push_back(selected); ChangeDir(filelist[selected].Name); } } else { filelist[selected].Marked = true; loop = false; res = true; } } } else if (msg == CRCInput::RC_help || msg == CRCInput::RC_red) { 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)) { SMSInput(msg_repeatok); } } hide(); selected_filelist.clear(); if(res && Multi_Select) { CProgressWindow * progress = NULL; for(unsigned int i = 0; i < filelist.size();i++) if(filelist[i].Marked) { bool return_dir = Hide_records && checkBD(filelist[i]); if(!return_dir && filelist[i].isDir()) { if (!progress) { progress = new CProgressWindow(); progress->setTitle(LOCALE_FILEBROWSER_SCAN); progress->exec(NULL,""); } #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]); } if (progress) { progress->hide(); delete progress; } } return res; } bool CFileBrowser::playlist_manager(CFileList &playlist, unsigned int playing, bool is_audio_playing) { neutrino_msg_t msg; neutrino_msg_data_t data; bool res = false; menu_ret = menu_return::RETURN_REPAINT; filelist = playlist; playlistmode = true; fontInit(); paintHead(); selected = playing; unsigned int oldselected = selected; paint(); paintFoot(); int timeout = g_settings.timing[SNeutrinoSettings::TIMING_FILEBROWSER]; uint64_t timeoutEnd = CRCInput::calcTimeoutEnd(timeout); bool loop=true; while (loop) { frameBuffer->blit(); g_RCInput->getMsgAbsoluteTimeout( &msg, &data, &timeoutEnd ); neutrino_msg_t msg_repeatok = msg & ~CRCInput::RC_Repeat; if (msg <= CRCInput::RC_MaxRC) timeoutEnd = CRCInput::calcTimeoutEnd(timeout); if(!CRCInput::isNumeric(msg)) { m_SMSKeyInput.resetOldKey(); paintSMSKey(); } if (msg == CRCInput::RC_home) { loop = false; } else if (msg == NeutrinoMessages::STANDBY_ON || msg == NeutrinoMessages::LEAVE_ALL || msg == NeutrinoMessages::SHUTDOWN || msg == NeutrinoMessages::SLEEPTIMER) { menu_ret = menu_return::RETURN_EXIT_ALL; loop = false; g_RCInput->postMsg(msg, data); } else if (msg == CRCInput::RC_timeout) { selected = oldselected; loop = false; } else if (msg > CRCInput::RC_MaxRC) { if (CNeutrinoApp::getInstance()->handleMsg( msg, data ) & messages_return::cancel_all) { menu_ret = menu_return::RETURN_EXIT_ALL; loop = false; } } if ((filelist.empty())) continue; if (msg_repeatok == CRCInput::RC_up) { unsigned int prevselected=selected; unsigned int prevliststart = liststart; if (selected) selected --; else selected = filelist.size() - 1; liststart = (selected/listmaxshow)*listmaxshow; if(prevliststart != liststart) { paint(); } else { paintItem(prevselected - prevliststart); paintItem(selected - liststart); } } else if (msg_repeatok == CRCInput::RC_down) { unsigned int prevselected=selected; unsigned int prevliststart = liststart; selected = (selected + 1) % filelist.size(); liststart = (selected/listmaxshow)*listmaxshow; if(prevliststart != liststart) { paint(); } else { paintItem(prevselected - prevliststart); paintItem(selected - liststart); } } else if (msg == (neutrino_msg_t) g_settings.key_pagedown) { unsigned int last = filelist.size() - 1; if (selected != last && selected + listmaxshow >= filelist.size()) { selected = last; } else { selected = (selected == last) ? 0 : selected + listmaxshow; liststart = (selected / listmaxshow) * listmaxshow; } paint(); } else if (msg == (neutrino_msg_t) g_settings.key_pageup) { if (selected && selected < listmaxshow) { selected = 0; } else { selected = selected ? selected - listmaxshow : filelist.size() - 1; liststart = (selected/listmaxshow)*listmaxshow; } paint(); } else if (msg == CRCInput::RC_red) { if (filelist.size() > 1) { filelist.erase(filelist.begin()+selected); if (selected) selected --; } paint(); } else if (msg == CRCInput::RC_green) { CFileBrowser *addfiles = new CFileBrowser; addfiles->Hide_records = true; addfiles->Multi_Select = true; addfiles->Dirs_Selectable = true; addfiles->exec(is_audio_playing ? g_settings.network_nfs_audioplayerdir.c_str() : g_settings.network_nfs_moviedir.c_str()); CFileList tmplist = addfiles->getSelectedFiles(); filelist.insert( filelist.end(), tmplist.begin(), tmplist.end() ); tmplist.clear(); delete addfiles; paintHead(); paint(); paintFoot(); } else if (msg == CRCInput::RC_yellow ) { 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 (msg == CRCInput::RC_blue ) { std::random_shuffle ( filelist.begin(), filelist.end() ); selected = 0; paint(); paintFoot(); } else if (msg == CRCInput::RC_ok) { filelist[selected].Marked = true; loop = false; res = true; } else if (CRCInput::isNumeric(msg_repeatok)) { SMSInput(msg_repeatok); } } hide(); frameBuffer->blit(); playlist = filelist; 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_MaxRC && 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 && (!tmplist[i].isDir()) && use_filter) { if(!Filter->matchFilter(tmplist[i].Name)) continue; } if(!tmplist[i].isDir()) re_filelist->push_back(tmplist[i]); else addRecursiveDir(re_filelist,tmplist[i].Name, false, progress); } } } } void CFileBrowser::hide() { frameBuffer->paintBackgroundBoxRel(x, y, width + OFFSET_SHADOW, height + OFFSET_SHADOW); } void CFileBrowser::paintItem(unsigned int pos) { int col1_width, col2_width, col3_width; int ypos = y + header_height + pos*item_height; CFile * actual_file = NULL; std::string fileicon; unsigned int currpos = liststart + pos; if (currpos >= filelist.size()) { // just paint an empty line frameBuffer->paintBoxRel(x, ypos, width - SCROLLBAR_WIDTH, item_height, COL_MENUCONTENT_PLUS_0); return; } actual_file = &filelist[currpos]; bool i_selected = currpos == selected; bool i_marked = actual_file->Marked; bool i_switch = false; //(currpos < filelist.size()) && (pos & 1); int i_radius = RADIUS_NONE; fb_pixel_t color; fb_pixel_t bgcolor; getItemColors(color, bgcolor, i_selected, i_marked, i_switch); if (i_selected || i_marked) i_radius = RADIUS_LARGE; if (i_radius) frameBuffer->paintBoxRel(x, ypos, width - SCROLLBAR_WIDTH, item_height, COL_MENUCONTENT_PLUS_0); frameBuffer->paintBoxRel(x, ypos, width - SCROLLBAR_WIDTH, item_height, bgcolor, i_radius); if (g_settings.filebrowser_showrights == 0 && S_ISREG(actual_file->Mode)) col2_width = 0; else col2_width = fnt_item->getRenderWidth("rwxrwxrwx"); col3_width = fnt_item->getRenderWidth("222.222G"); col1_width = width - SCROLLBAR_WIDTH - OFFSET_INNER_MID - col3_width - OFFSET_INNER_MID - col2_width - OFFSET_INNER_MID; if ( !actual_file->Name.empty() ) { std::string fname_temp = actual_file->Name; fname_temp = str_replace("%20", " ", fname_temp); fname_temp = str_replace("%26", "&", fname_temp); actual_file->Name = fname_temp; if (currpos == 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: case CFile::FILE_FLAC: case CFile::FILE_AAC: fileicon = g_IconFont ? FA_MUSIC : NEUTRINO_ICON_MP3; break; case CFile::FILE_PLAYLIST: fileicon = g_IconFont ? FA_LIST : NEUTRINO_ICON_MP3; break; case CFile::FILE_DIR: if (actual_file->getFileName() == "..") fileicon = g_IconFont ? FA_FOLDER_OPEN : NEUTRINO_ICON_FOLDER; else fileicon = g_IconFont ? FA_FOLDER : NEUTRINO_ICON_FOLDER; break; case CFile::FILE_PICTURE: fileicon = g_IconFont ? FA_IMAGE : NEUTRINO_ICON_PICTURE; break; case CFile::FILE_AVI: case CFile::FILE_ASF: case CFile::FILE_MKV: case CFile::FILE_VOB: case CFile::FILE_MPG: case CFile::FILE_TS: fileicon = g_IconFont ? FA_FILM : NEUTRINO_ICON_MOVIE; break; case CFile::FILE_TEXT: default: fileicon = g_IconFont ? FA_FILE : NEUTRINO_ICON_FILE; } int icon_w = 0; int icon_h = 0; if (g_IconFont) { int iconfont_size = g_IconFont->getSize(); g_IconFont->setSize(item_height / 4 * 3); icon_w = g_IconFont->getWidestWidth(); int icon_w_real = g_IconFont->getRenderWidth(fileicon); int spacer_x = (icon_w - icon_w_real) / 2; g_IconFont->RenderString(x + OFFSET_INNER_MID + spacer_x, ypos + g_IconFont->getHeight() /*item_height - spacer_y*/, icon_w_real, fileicon, color); g_IconFont->setSize(iconfont_size); } else { frameBuffer->getIconSize(NEUTRINO_ICON_FILE, &icon_w, &icon_h); frameBuffer->paintIcon(fileicon, x + OFFSET_INNER_MID, ypos, item_height); } int col1_offset = OFFSET_INNER_MID + icon_w + OFFSET_INNER_MID; fnt_item->RenderString(x + col1_offset, ypos + item_height, col1_width - col1_offset, FILESYSTEM_ENCODING_TO_UTF8_STRING(actual_file->getFileName()), color); 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; fnt_item->RenderString(x + width - SCROLLBAR_WIDTH - OFFSET_INNER_MID - col3_width - OFFSET_INNER_MID - col2_width , ypos + item_height, col2_width, modestring, color); } #define GIGABYTE 1073741824LL #define MEGABYTE 1048576LL #define KILOBYTE 1024LL char sizestring[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(sizestring, sizeof(sizestring), "%d.%03d%s", a, b, unit); } else snprintf(sizestring,sizeof(sizestring),"%d", (int)actual_file->Size); /* right align file size */ int sz_w = fnt_item->getRenderWidth(sizestring); fnt_item->RenderString(x + width - SCROLLBAR_WIDTH - OFFSET_INNER_MID - sz_w, ypos + item_height, sz_w, sizestring, color); } if(actual_file->isDir()) { char timestring[18]; time_t rawtime; rawtime = actual_file->Time; strftime(timestring, 18, "%d-%m-%Y %H:%M", gmtime(&rawtime)); /* right align directory time */ int time_w = fnt_item->getRenderWidth(timestring); fnt_item->RenderString(x + width - SCROLLBAR_WIDTH - OFFSET_INNER_MID - time_w, ypos + item_height, time_w, timestring, color); } } } void CFileBrowser::paintHead() { char *l_name; int i = 0; int l; #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(playlistmode ? LOCALE_FILEBROWSER_PM : 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 (fnt_title->getRenderWidth(l_name) > width - 2*OFFSET_INNER_MID) 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 ((fnt_title->getRenderWidth(&l_name[i]) > width - 2*OFFSET_INNER_MID) && (i < l)) i++; CComponentsHeader header(x, y, width, header_height, &l_name[i]); header.enableShadow(CC_SHADOW_RIGHT | CC_SHADOW_CORNER_TOP_RIGHT | CC_SHADOW_CORNER_BOTTOM_RIGHT, -1, true); header.paint(CC_SAVE_SCREEN_NO); free(l_name); } bool chooserDir(char *setting_dir, bool test_dir, const char *action_str, size_t str_leng, bool allow_tmp) { std::string tmp_setting_dir = setting_dir; if(chooserDir(tmp_setting_dir, test_dir, action_str,allow_tmp)) { 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, bool allow_tmp) { 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,allow_tmp)) { printf("%s %s dir %s\n",wrong_str ,action_str, newdir); return false; } else { setting_dir = b.getSelectedFile()->Name; return true; } } return false; } int CFileBrowser::paintFoot(bool show) { int cnt, res; std::string sort_text = g_Locale->getText(LOCALE_MOVIEBROWSER_FOOT_SORT); sort_text += " "; sort_text += g_Locale->getText(sortByNames[g_settings.filebrowser_sortmethod]); int sort_text_len = g_Font[SNeutrinoSettings::FONT_TYPE_MENU_FOOT]->getRenderWidth(g_Locale->getText(LOCALE_MOVIEBROWSER_FOOT_SORT)); int len = 0; for (int i = 0; i < FILEBROWSER_NUMBER_OF_SORT_VARIANTS; i++) len = std::max(len, g_Font[SNeutrinoSettings::FONT_TYPE_MENU_FOOT]->getRenderWidth(g_Locale->getText(sortByNames[i]))); sort_text_len += len; neutrino_locale_t locale_filebrowser_filter = LOCALE_FILEBROWSER_FILTER_INACTIVE; if (Filter != NULL && use_filter) locale_filebrowser_filter = LOCALE_FILEBROWSER_FILTER_ACTIVE; button_label_ext buttons_playlistmode[] = { { NEUTRINO_ICON_BUTTON_RED, LOCALE_FILEBROWSER_DELETE, NULL, 0, false }, { NEUTRINO_ICON_BUTTON_GREEN, LOCALE_FILEBROWSER_ADD, NULL, 0, false }, { NEUTRINO_ICON_BUTTON_YELLOW, NONEXISTANT_LOCALE, sort_text.c_str(), sort_text_len, false }, { NEUTRINO_ICON_BUTTON_BLUE, LOCALE_AUDIOPLAYER_SHUFFLE, NULL, 0, false }, { NEUTRINO_ICON_BUTTON_OKAY, LOCALE_FILEBROWSER_SELECT, NULL, 0, false }, { NEUTRINO_ICON_BUTTON_PLAY, LOCALE_FILEBROWSER_MARK, NULL, 0, false }, }; button_label_ext buttons_filelistmode[] = { { NEUTRINO_ICON_BUTTON_RED, NONEXISTANT_LOCALE, sort_text.c_str(), sort_text_len, false }, { NEUTRINO_ICON_BUTTON_OKAY, LOCALE_FILEBROWSER_SELECT, NULL, 0, false }, { NEUTRINO_ICON_BUTTON_MUTE_SMALL, LOCALE_FILEBROWSER_DELETE, NULL, 0, false }, { NEUTRINO_ICON_BUTTON_PLAY, LOCALE_FILEBROWSER_MARK, NULL, 0, false }, { NEUTRINO_ICON_BUTTON_BLUE, locale_filebrowser_filter, NULL, 0, false }, }; if (playlistmode) { cnt = sizeof(buttons_playlistmode)/sizeof(button_label_ext); if (!show) return paintButtons(buttons_playlistmode, cnt, 0, 0, 0, 0, 0, false, NULL, NULL); } else { cnt = sizeof(buttons_filelistmode)/sizeof(button_label_ext); if (!show) return paintButtons(buttons_filelistmode, cnt, 0, 0, 0, 0, 0, false, NULL, NULL); } int footer_y = y + height - OFFSET_SHADOW - footer_height; int footer_width = width - smskey_width; // shadow frameBuffer->paintBoxRel(x + OFFSET_SHADOW, footer_y + OFFSET_SHADOW, width, footer_height, COL_SHADOW_PLUS_0, RADIUS_MID, CORNER_BOTTOM); if (filelist.empty()) { // show an empty footer frameBuffer->paintBoxRel(x, footer_y, width, footer_height, COL_MENUFOOT_PLUS_0, RADIUS_LARGE, CORNER_BOTTOM); return footer_height; } /* We can't use CComponentsFooter because CComponentsFooter can't handle button_label_ext */ if (playlistmode) res = paintButtons(buttons_playlistmode, cnt, x, footer_y, width, footer_height, footer_width); else res = paintButtons(buttons_filelistmode, cnt, x, footer_y, width, footer_height, footer_width); paintSMSKey(); return res; } void CFileBrowser::paintSMSKey() { int smskey_height = fnt_foot->getHeight(); int smskey_y = y + height - OFFSET_SHADOW - footer_height; //background frameBuffer->paintBoxRel(x + width - smskey_width, smskey_y, smskey_width, footer_height, COL_MENUFOOT_PLUS_0, RADIUS_LARGE, CORNER_BOTTOM_RIGHT); if(m_SMSKeyInput.getOldKey()!=0) { char cKey[2] = {(char)m_SMSKeyInput.getOldKey(), 0}; cKey[0] = toupper(cKey[0]); int len = fnt_foot->getRenderWidth(cKey); fnt_foot->RenderString(x + width - smskey_width, smskey_y + footer_height/2 + smskey_height/2, len, cKey, COL_MENUHEAD_TEXT); } } void CFileBrowser::paint() { liststart = (selected/listmaxshow)*listmaxshow; CVFD::getInstance()->setMode(CVFD::MODE_MENU_UTF8, g_Locale->getText(LOCALE_FILEBROWSER_HEAD)); for(unsigned int count=0;countd_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); }