Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
85 changes: 49 additions & 36 deletions radio/src/gui/colorlcd/libui/file_browser.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -22,29 +22,14 @@
#include "file_browser.h"
#include "lib_file.h"
#include "fonts.h"
#include "sdcard.h"

#include <list>
#include <string>

#define CELL_CTRL_DIR LV_TABLE_CELL_CTRL_CUSTOM_1
#define CELL_CTRL_FILE LV_TABLE_CELL_CTRL_CUSTOM_2

static const char* getFullPath(const char* filename)
{
static char full_path[FF_MAX_LFN + 1];
f_getcwd((TCHAR*)full_path, FF_MAX_LFN);
strcat(full_path, "/");
strcat(full_path, filename);
return full_path;
}

static const char* getCurrentPath()
{
static char path[FF_MAX_LFN + 1];
f_getcwd((TCHAR*)path, FF_MAX_LFN);
return path;
}

static int strnatcasecmp(char const *s1, char const *s2)
{
int i1, i2;
Expand Down Expand Up @@ -104,7 +89,7 @@ static bool natural_compare_nocase(const std::string & first, const std::string
return strnatcasecmp(first.c_str(), second.c_str()) < 0;
}

static int scan_files(std::list<std::string>& files,
int FileBrowser::scan_files(std::list<std::string>& files,
std::list<std::string>& directories)
{
FILINFO fno;
Expand All @@ -116,14 +101,18 @@ static int scan_files(std::list<std::string>& files,
// read all entries
bool firstTime = true;
for (;;) {
res = sdReadDir(&dir, &fno, firstTime);
if (firstTime && currentPath != ROOT_PATH) {
strcpy(fno.fname, "..");
fno.fattrib = AM_DIR;
Comment thread
coderabbitai[bot] marked this conversation as resolved.
} else {
res = f_readdir(&dir, &fno);

if (res != FR_OK || fno.fname[0] == 0)
break; // Break on error or end of dir
// if (strlen((const char*)fno.fname) > SD_SCREEN_FILE_LENGTH)
// continue;
if (fno.fattrib & (AM_HID|AM_SYS)) continue; /* Ignore hidden and system files */
if (fno.fname[0] == '.' && fno.fname[1] != '.') continue; // Ignore hidden files under UNIX, but not ..
if (res != FR_OK || fno.fname[0] == 0)
break; // Break on error or end of dir
if (fno.fattrib & (AM_HID|AM_SYS)) continue; /* Ignore hidden and system files */
if (fno.fname[0] == '.') continue; // Ignore hidden files under UNIX
}
firstTime = false;

if (fno.fattrib & AM_DIR) {
directories.push_back((char*)fno.fname);
Expand All @@ -138,10 +127,25 @@ static int scan_files(std::list<std::string>& files,
return 0;
}

void FileBrowser::setFullPath(const char* name)
{
if (currentPath.back() == '/')
fullPathBuf = currentPath + name;
else
fullPathBuf = currentPath + "/" + name;
}

FileBrowser::FileBrowser(Window* parent, const rect_t& rect, const char* dir) :
TableField(parent, rect)
TableField(parent, rect), currentPath((dir && dir[0]) ? dir : "/")
{
Comment thread
coderabbitai[bot] marked this conversation as resolved.
f_chdir(dir);
// Normalize once: drop any trailing slash (except root) so the join/parent
// logic never has to deal with it later.
while (currentPath.size() > 1 && currentPath.back() == '/') currentPath.pop_back();

if (etxChdir(currentPath.c_str()) != FR_OK) {
currentPath = ROOT_PATH; // keep currentPath in sync with the FatFs CWD
etxChdir(currentPath.c_str());
}

setAutoEdit();

Expand Down Expand Up @@ -244,18 +248,25 @@ void FileBrowser::onSelected(const char* name, bool is_dir)
return;
}

const char* path = getCurrentPath();
const char* fullpath = getFullPath(name);
if (fileSelected) fileSelected(path, name, fullpath, is_dir);
setFullPath(name);
if (fileSelected) fileSelected(currentPath.c_str(), name, fullPathBuf.c_str(), is_dir);
selected = name;
}

void FileBrowser::onPress(const char* name, bool is_dir)
{
const char* path = getCurrentPath();
const char* fullpath = getFullPath(name);
if (is_dir) {
f_chdir(fullpath);
std::string nextPath = currentPath;
if (strcmp(name, "..") == 0) {
// Go up: trim last segment (f_chdir("..") / f_getcwd are no-ops on exFAT).
auto pos = nextPath.find_last_of('/');
nextPath = (pos == 0 || pos == std::string::npos) ? "/" : nextPath.substr(0, pos);
} else {
setFullPath(name);
nextPath = fullPathBuf;
}
// Only commit the tracked path if the CWD actually changed.
if (etxChdir(nextPath.c_str()) == FR_OK) currentPath = nextPath;
if (fileSelected) fileSelected(nullptr, nullptr, nullptr, is_dir);
selected = nullptr;
refresh();
Expand All @@ -268,20 +279,22 @@ void FileBrowser::onPress(const char* name, bool is_dir)
}

if (fileAction){
fileAction(path, name, fullpath, is_dir);
setFullPath(name);
fileAction(currentPath.c_str(), name, fullPathBuf.c_str(), is_dir);
}
}

void FileBrowser::onPressLong(const char* name, bool is_dir)
{
const char* path = getCurrentPath();
const char* fullpath = getFullPath(name);
// the synthetic parent-directory row is navigation-only
if (strcmp(name, "..") == 0) return;

if (!selected || (selected != name)) {
onSelected(name, is_dir);
}

if (fileAction){
fileAction(path, name, fullpath, is_dir);
setFullPath(name);
fileAction(currentPath.c_str(), name, fullPathBuf.c_str(), is_dir);
}
}
11 changes: 11 additions & 0 deletions radio/src/gui/colorlcd/libui/file_browser.h
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@

#pragma once

#include <string>

#include "table.h"

class FileBrowser : public TableField
Expand Down Expand Up @@ -52,6 +54,15 @@ class FileBrowser : public TableField
void onPressLong(const char* name, bool is_dir);

private:
// Current dir, tracked explicitly because f_getcwd() is broken on exFAT.
std::string currentPath;
// Outlives the file-action call: callbacks capture the pointer for later use.
std::string fullPathBuf;
void setFullPath(const char* name);

int scan_files(std::list<std::string>& files,
std::list<std::string>& directories);

const char* selected = nullptr;
FileAction fileAction;
FileAction fileSelected;
Expand Down
7 changes: 3 additions & 4 deletions radio/src/gui/colorlcd/libui/filechoice.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -127,15 +127,14 @@ void FileChoice::loadFiles()

FRESULT res = f_opendir(&dir, folder.c_str()); // Open the directory
if (res == FR_OK) {
bool firstTime = true;
for (;;) {
res = sdReadDir(&dir, &fno, firstTime);
res = f_readdir(&dir, &fno);
if (res != FR_OK || fno.fname[0] == 0)
break; // break on error or end of dir
if (fno.fattrib & (AM_HID | AM_SYS | AM_DIR))
continue; // Ignore subfolders, hidden files and system files
if (fno.fname[0] == '.' && fno.fname[1] != '.')
continue; // Ignore hidden files under UNIX, but not ..
if (fno.fname[0] == '.')
continue; // Ignore hidden files under UNIX

fnExt = getFileExtension(fno.fname, 0, 0, &fnLen, &extLen);

Expand Down
9 changes: 7 additions & 2 deletions radio/src/gui/colorlcd/radio/radio_sdmanager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -467,14 +467,19 @@ void RadioSdManagerPage::fileAction(const char* path, const char* name,
}
menu->addLine(STR_COPY_FILE, [=]() {
clipboard.type = CLIPBOARD_TYPE_SD_FILE;
f_getcwd(clipboard.data.sd.directory, CLIPBOARD_PATH_LEN);
// f_getcwd() is a no-op on exFAT; use the tracked path.
strncpy(clipboard.data.sd.directory, path, CLIPBOARD_PATH_LEN - 1);
clipboard.data.sd.directory[CLIPBOARD_PATH_LEN - 1] = '\0';
strncpy(clipboard.data.sd.filename, name, CLIPBOARD_PATH_LEN - 1);
clipboard.data.sd.filename[CLIPBOARD_PATH_LEN - 1] = '\0';
});
if (clipboard.type == CLIPBOARD_TYPE_SD_FILE) {
menu->addLine(STR_PASTE, [=]() {
static char lfn[FF_MAX_LFN + 1]; // TODO optimize that!
char destFileName[2 * CLIPBOARD_PATH_LEN + 1];
f_getcwd((TCHAR*)lfn, FF_MAX_LFN);
// f_getcwd() is a no-op on exFAT; use the tracked path.
strncpy(lfn, path, FF_MAX_LFN);
lfn[FF_MAX_LFN] = '\0';
// prevent copying to the same directory with the same name
char* destNamePtr = clipboard.data.sd.filename;
if (!strcmp(clipboard.data.sd.directory, lfn)) {
Expand Down
2 changes: 1 addition & 1 deletion radio/src/gui/colorlcd/radio/radio_tools.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ static void run_lua_tool(const std::string& path)
char toolPath[FF_MAX_LFN + 1];
strncpy(toolPath, path.c_str(), sizeof(toolPath) - 1);
*((char*)getBasename(toolPath) - 1) = '\0';
f_chdir(toolPath);
etxChdir(toolPath);

luaExecStandalone(path.c_str());
}
Expand Down
13 changes: 8 additions & 5 deletions radio/src/gui/colorlcd/themes/theme_manager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -319,16 +319,19 @@ void ThemePersistance::scanForThemes()
if (res == FR_OK) {
TRACE("scanForThemes: open successful");
// read all entries
bool firstTime = true;
for (;;) {
res = sdReadDir(&dir, &fno, firstTime);
res = f_readdir(&dir, &fno);

if (res != FR_OK || fno.fname[0] == 0)
break; // Break on error or end of dir

if (strlen((const char*)fno.fname) > SD_SCREEN_FILE_LENGTH) continue;
if (fno.fattrib & AM_DIR)
scanThemeFolder(fno.fname);
if (fno.fattrib & (AM_HID | AM_SYS)) continue; // skip hidden/system
if (fno.fname[0] == '.') continue; // skip dot entries

if (fno.fattrib & AM_DIR) {
if (strlen((const char*)fno.fname) <= SD_SCREEN_FILE_LENGTH)
scanThemeFolder(fno.fname);
Comment thread
coderabbitai[bot] marked this conversation as resolved.
}
}

f_closedir(&dir);
Expand Down
53 changes: 41 additions & 12 deletions radio/src/gui/common/stdlcd/radio_sdmanager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -61,11 +61,31 @@ inline bool isFilenameLower(bool isfile, const char * fn, const char * line)
return (!isfile && IS_FILE(line)) || (isfile==IS_FILE(line) && strcasecmp(fn, line) < 0);
}

// Current dir, tracked explicitly: on exFAT f_getcwd() and f_chdir("..") are no-ops.
static char sdManagerPath[FF_MAX_LFN + 1] = "/";

static void sdManagerChdir(const char* name)
{
if (!strcmp(name, "..")) {
// go up: drop the last path segment
char* sep = strrchr(sdManagerPath, '/');
if (sep == sdManagerPath) sep[1] = '\0'; // parent is the root
else if (sep) *sep = '\0';
} else {
// descend into 'name' (bounded, no duplicate slash at the root)
size_t len = strlen(sdManagerPath);
if (strcmp(sdManagerPath, ROOT_PATH) != 0 && len + 1 < sizeof(sdManagerPath))
sdManagerPath[len++] = '/';
strncpy(sdManagerPath + len, name, sizeof(sdManagerPath) - len - 1);
sdManagerPath[sizeof(sdManagerPath) - 1] = '\0';
}
etxChdir(sdManagerPath); // absolute path: resolves on FAT and exFAT alike
}
Comment thread
coderabbitai[bot] marked this conversation as resolved.

void getSelectionFullPath(char * lfn)
{
f_getcwd(lfn, FF_MAX_LFN);
strcat(lfn, "/");
strcat(lfn, reusableBuffer.sdManager.lines[menuVerticalPosition - HEADER_LINE - menuVerticalOffset]);
snprintf(lfn, FF_MAX_LFN + 1, "%s/%s", sdManagerPath,
reusableBuffer.sdManager.lines[menuVerticalPosition - HEADER_LINE - menuVerticalOffset]);
}

#if defined(PXX2)
Expand Down Expand Up @@ -121,12 +141,14 @@ void onSdManagerMenu(const char * result)
}
else if (result == STR_COPY_FILE) {
clipboard.type = CLIPBOARD_TYPE_SD_FILE;
f_getcwd(clipboard.data.sd.directory, CLIPBOARD_PATH_LEN);
strncpy(clipboard.data.sd.directory, sdManagerPath, CLIPBOARD_PATH_LEN - 1);
clipboard.data.sd.directory[CLIPBOARD_PATH_LEN - 1] = '\0';
strncpy(clipboard.data.sd.filename, line, CLIPBOARD_PATH_LEN-1);
clipboard.data.sd.filename[CLIPBOARD_PATH_LEN - 1] = '\0';
}
else if (result == STR_PASTE) {
char destFileName[2 * CLIPBOARD_PATH_LEN + 1];
f_getcwd(lfn, FF_MAX_LFN);
strcpy(lfn, sdManagerPath);
// if destination is dir, copy into that dir
if (IS_DIRECTORY(line)) {
strcat(lfn, "/");
Expand Down Expand Up @@ -278,7 +300,8 @@ void menuRadioSdManager(event_t _event)
#endif

if (_event == EVT_ENTRY) {
f_chdir(ROOT_PATH);
etxChdir(ROOT_PATH);
strcpy(sdManagerPath, ROOT_PATH);
#if LCD_DEPTH > 1
lastPos = -1;
#endif
Expand Down Expand Up @@ -325,7 +348,7 @@ void menuRadioSdManager(event_t _event)
else {
int index = menuVerticalPosition - HEADER_LINE - menuVerticalOffset;
if (IS_DIRECTORY(reusableBuffer.sdManager.lines[index])) {
f_chdir(reusableBuffer.sdManager.lines[index]);
sdManagerChdir(reusableBuffer.sdManager.lines[index]);
menuVerticalOffset = 0;
menuVerticalPosition = HEADER_LINE;
REFRESH_FILES();
Expand Down Expand Up @@ -483,11 +506,17 @@ void menuRadioSdManager(event_t _event)
if (res == FR_OK) {
bool firstTime = true;
for (;;) {
res = sdReadDir(&dir, &fno, firstTime);
if (res != FR_OK || fno.fname[0] == 0) break; /* Break on error or end of dir */
if (strlen(fno.fname) > SD_SCREEN_FILE_LENGTH) continue;
if (fno.fattrib & (AM_HID|AM_SYS)) continue; /* Ignore hidden and system files */
if (fno.fname[0] == '.' && fno.fname[1] != '.') continue; /* Ignore UNIX hidden files, but not .. */
if (firstTime && (strcmp(sdManagerPath, ROOT_PATH) != 0)) {
strcpy(fno.fname, "..");
fno.fattrib = AM_DIR;
} else {
res = f_readdir(&dir, &fno);
if (res != FR_OK || fno.fname[0] == 0) break; /* Break on error or end of dir */
if (strlen(fno.fname) > SD_SCREEN_FILE_LENGTH) continue;
if (fno.fattrib & (AM_HID|AM_SYS)) continue; /* Ignore hidden and system files */
if (fno.fname[0] == '.') continue; /* Ignore UNIX hidden files */
}
firstTime = false;

reusableBuffer.sdManager.count++;

Expand Down
2 changes: 1 addition & 1 deletion radio/src/gui/common/stdlcd/radio_tools.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ static void displayRadioTool(uint8_t index)
pushMenu(reusableBuffer.radioTools.script[index - menuVerticalOffset].tool);
}
else if (reusableBuffer.radioTools.script[index - menuVerticalOffset].filename[0]) {
f_chdir(SCRIPTS_TOOLS_PATH);
etxChdir(SCRIPTS_TOOLS_PATH);
char path[FF_MAX_LFN + 1] = SCRIPTS_TOOLS_PATH "/";
strcat(path, reusableBuffer.radioTools.script[index - menuVerticalOffset].filename);
luaExec(path);
Expand Down
Loading