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
63 changes: 34 additions & 29 deletions radio/src/gui/colorlcd/libui/file_browser.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -29,22 +29,6 @@
#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 @@ -138,10 +122,25 @@ static int scan_files(std::list<std::string>& files,
return 0;
}

void FileBrowser::setFullPath(const char* name)
{
if (currentPath == "/")
fullPathBuf = std::string("/") + 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 (f_chdir(currentPath.c_str()) != FR_OK) {
currentPath = "/"; // keep currentPath in sync with the FatFs CWD
f_chdir(currentPath.c_str());
}

setAutoEdit();

Expand Down Expand Up @@ -244,18 +243,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 (f_chdir(nextPath.c_str()) == FR_OK) currentPath = nextPath;
if (fileSelected) fileSelected(nullptr, nullptr, nullptr, is_dir);
selected = nullptr;
refresh();
Expand All @@ -268,20 +274,19 @@ 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);

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);
}
}
8 changes: 8 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,12 @@ 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);

const char* selected = nullptr;
FileAction fileAction;
FileAction fileSelected;
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
35 changes: 29 additions & 6 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';
}
f_chdir(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 @@ -279,6 +301,7 @@ void menuRadioSdManager(event_t _event)

if (_event == EVT_ENTRY) {
f_chdir(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
15 changes: 4 additions & 11 deletions radio/src/lib_file.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -83,16 +83,6 @@ bool isExtensionMatching(const char * extension, const char * pattern, char * ma
return false;
}

// returns true if current working dir is at the root level
bool isCwdAtRoot()
{
char path[10];
if (f_getcwd(path, sizeof(path)-1) == FR_OK) {
return (strcasecmp("/", path) == 0);
}
return false;
}

/*
Wrapper around the f_readdir() function which
also returns ".." entry for sub-dirs. (FatFS 0.12 does
Expand All @@ -101,7 +91,10 @@ bool isCwdAtRoot()
FRESULT sdReadDir(DIR * dir, FILINFO * fno, bool & firstTime)
{
FRESULT res;
if (firstTime && !isCwdAtRoot()) {
// Fake ".." only when browsing the non-root CWD itself (sclust == cdir, 0 == root).
// Filesystem-CWD based so it works on exFAT, where f_getcwd() always returns root;
// the sclust check keeps callers that open an arbitrary path (no chdir) unaffected.
if (firstTime && dir->obj.fs->cdir != 0 && dir->obj.sclust == dir->obj.fs->cdir) {
// fake parent directory entry
strcpy(fno->fname, "..");
fno->fattrib = AM_DIR;
Expand Down