mirror of
https://github.com/portapack-mayhem/mayhem-firmware.git
synced 2025-08-21 12:27:35 +00:00
Fileman copy/paste support (#970)
* Add copy/paste UI instead of file save
This commit is contained in:
@@ -192,8 +192,8 @@ const fileman_entry& FileManBaseView::get_selected_entry() const {
|
||||
FileManBaseView::FileManBaseView(
|
||||
NavigationView& nav,
|
||||
std::string filter
|
||||
) : nav_ (nav),
|
||||
extension_filter { filter }
|
||||
) : nav_{ nav },
|
||||
extension_filter{ filter }
|
||||
{
|
||||
add_children({
|
||||
&labels,
|
||||
@@ -206,7 +206,7 @@ FileManBaseView::FileManBaseView(
|
||||
};
|
||||
|
||||
if (!sdcIsCardInserted(&SDCD1)) {
|
||||
empty_root = true;
|
||||
empty_ = EmptyReason::NoSDC;
|
||||
text_current.set("NO SD CARD!");
|
||||
return;
|
||||
}
|
||||
@@ -214,7 +214,7 @@ FileManBaseView::FileManBaseView(
|
||||
load_directory_contents(current_path);
|
||||
|
||||
if (!entry_list.size()) {
|
||||
empty_root = true;
|
||||
empty_ = EmptyReason::NoFiles;
|
||||
text_current.set("EMPTY SD CARD!");
|
||||
} else {
|
||||
menu_view.on_left = [this]() {
|
||||
@@ -224,7 +224,7 @@ FileManBaseView::FileManBaseView(
|
||||
}
|
||||
|
||||
void FileManBaseView::focus() {
|
||||
if (empty_root) {
|
||||
if (empty_ != EmptyReason::NotEmpty) {
|
||||
button_exit.focus();
|
||||
} else {
|
||||
menu_view.focus();
|
||||
@@ -310,34 +310,8 @@ const FileManBaseView::file_assoc_t& FileManBaseView::get_assoc(
|
||||
return file_types[index];
|
||||
}
|
||||
|
||||
/*void FileSaveView::on_save_name() {
|
||||
text_prompt(nav_, &filename_buffer, 8, [this](std::string * buffer) {
|
||||
nav_.pop();
|
||||
});
|
||||
}
|
||||
FileSaveView::FileSaveView(
|
||||
NavigationView& nav
|
||||
) : FileManBaseView(nav)
|
||||
{
|
||||
name_buffer.clear();
|
||||
|
||||
add_children({
|
||||
&text_save,
|
||||
&button_save_name,
|
||||
&live_timestamp
|
||||
});
|
||||
|
||||
button_save_name.on_select = [this, &nav](Button&) {
|
||||
on_save_name();
|
||||
};
|
||||
}*/
|
||||
|
||||
/* FileLoadView **************************************************************/
|
||||
|
||||
void FileLoadView::refresh_widgets(const bool) {
|
||||
set_dirty();
|
||||
}
|
||||
|
||||
FileLoadView::FileLoadView(
|
||||
NavigationView& nav,
|
||||
std::string filter
|
||||
@@ -367,6 +341,68 @@ FileLoadView::FileLoadView(
|
||||
};
|
||||
}
|
||||
|
||||
void FileLoadView::refresh_widgets(const bool) {
|
||||
set_dirty();
|
||||
}
|
||||
|
||||
/* FileSaveView **************************************************************/
|
||||
/*
|
||||
FileSaveView::FileSaveView(
|
||||
NavigationView& nav,
|
||||
const fs::path& path,
|
||||
const fs::path& file
|
||||
) : nav_{ nav },
|
||||
path_{ path },
|
||||
file_{ file }
|
||||
{
|
||||
add_children({
|
||||
&labels,
|
||||
&text_path,
|
||||
&button_edit_path,
|
||||
&text_name,
|
||||
&button_edit_name,
|
||||
&button_save,
|
||||
&button_cancel,
|
||||
});
|
||||
|
||||
button_edit_path.on_select = [this](Button&) {
|
||||
buffer_ = path_.string();
|
||||
text_prompt(nav_, buffer_, max_filename_length,
|
||||
[this](std::string&) {
|
||||
path_ = buffer_;
|
||||
refresh_widgets();
|
||||
});
|
||||
};
|
||||
|
||||
button_edit_name.on_select = [this](Button&) {
|
||||
buffer_ = file_.string();
|
||||
text_prompt(nav_, buffer_, max_filename_length,
|
||||
[this](std::string&) {
|
||||
file_ = buffer_;
|
||||
refresh_widgets();
|
||||
});
|
||||
};
|
||||
|
||||
button_save.on_select = [this](Button&) {
|
||||
if (on_save)
|
||||
on_save(path_ / file_);
|
||||
else
|
||||
nav_.pop();
|
||||
};
|
||||
|
||||
button_cancel.on_select = [this](Button&) {
|
||||
nav_.pop();
|
||||
};
|
||||
|
||||
refresh_widgets();
|
||||
}
|
||||
|
||||
void FileSaveView::refresh_widgets() {
|
||||
text_path.set(truncate(path_, 30));
|
||||
text_name.set(truncate(file_, 30));
|
||||
set_dirty();
|
||||
}
|
||||
*/
|
||||
/* FileManagerView ***********************************************************/
|
||||
|
||||
void FileManagerView::on_rename() {
|
||||
@@ -429,6 +465,30 @@ void FileManagerView::on_new_dir() {
|
||||
});
|
||||
}
|
||||
|
||||
void FileManagerView::on_paste() {
|
||||
auto stem = copy_path.stem();
|
||||
auto ext = copy_path.extension();
|
||||
auto serial = 1;
|
||||
fs::path new_path = copy_path.filename();
|
||||
|
||||
// Create a unique name.
|
||||
while (fs::file_exists(current_path / new_path)) {
|
||||
new_path = stem;
|
||||
new_path += fs::path{ u"_" };
|
||||
new_path += to_string_dec_int(serial++);
|
||||
new_path += ext;
|
||||
}
|
||||
|
||||
// TODO: handle partner file. Need to fix nav stack first.
|
||||
auto result = copy_file(copy_path, current_path / new_path);
|
||||
if (result.code() != FR_OK)
|
||||
nav_.display_modal("Paste Failed", result.what());
|
||||
|
||||
copy_path = fs::path{ };
|
||||
menu_view.focus();
|
||||
reload_current();
|
||||
}
|
||||
|
||||
bool FileManagerView::selected_is_valid() const {
|
||||
return !entry_list.empty() &&
|
||||
get_selected_entry().path != parent_dir_path;
|
||||
@@ -438,62 +498,79 @@ void FileManagerView::refresh_widgets(const bool v) {
|
||||
button_rename.hidden(v);
|
||||
button_delete.hidden(v);
|
||||
button_new_dir.hidden(v);
|
||||
button_copy.hidden(v);
|
||||
button_paste.hidden(v);
|
||||
set_dirty();
|
||||
}
|
||||
|
||||
FileManagerView::~FileManagerView() {
|
||||
}
|
||||
|
||||
FileManagerView::FileManagerView(
|
||||
NavigationView& nav
|
||||
) : FileManBaseView(nav, "")
|
||||
{
|
||||
if (!empty_root) {
|
||||
on_refresh_widgets = [this](bool v) {
|
||||
refresh_widgets(v);
|
||||
};
|
||||
|
||||
add_children({
|
||||
&menu_view,
|
||||
&labels,
|
||||
&text_date,
|
||||
&button_rename,
|
||||
&button_delete,
|
||||
&button_new_dir,
|
||||
});
|
||||
|
||||
menu_view.on_highlight = [this]() {
|
||||
// TODO: enable/disable buttons.
|
||||
if (selected_is_valid())
|
||||
text_date.set(to_string_FAT_timestamp(file_created_date(get_selected_full_path())));
|
||||
else
|
||||
text_date.set("");
|
||||
};
|
||||
|
||||
refresh_list();
|
||||
// Don't bother with the UI in the case of no SDC.
|
||||
if (empty_ == EmptyReason::NoSDC)
|
||||
return;
|
||||
|
||||
on_refresh_widgets = [this](bool v) {
|
||||
refresh_widgets(v);
|
||||
};
|
||||
|
||||
on_select_entry = [this](KeyEvent key) {
|
||||
if (key == KeyEvent::Select && get_selected_entry().is_directory) {
|
||||
push_dir(get_selected_entry().path);
|
||||
} else {
|
||||
button_rename.focus();
|
||||
}
|
||||
};
|
||||
|
||||
button_rename.on_select = [this](Button&) {
|
||||
if (selected_is_valid())
|
||||
on_rename();
|
||||
};
|
||||
add_children({
|
||||
&menu_view,
|
||||
&labels,
|
||||
&text_date,
|
||||
&button_rename,
|
||||
&button_delete,
|
||||
&button_new_dir,
|
||||
&button_copy,
|
||||
&button_paste
|
||||
});
|
||||
|
||||
menu_view.on_highlight = [this]() {
|
||||
// TODO: enable/disable buttons.
|
||||
if (selected_is_valid())
|
||||
text_date.set(to_string_FAT_timestamp(file_created_date(get_selected_full_path())));
|
||||
else
|
||||
text_date.set("");
|
||||
};
|
||||
|
||||
refresh_list();
|
||||
|
||||
button_delete.on_select = [this](Button&) {
|
||||
if (selected_is_valid())
|
||||
on_delete();
|
||||
};
|
||||
on_select_entry = [this](KeyEvent key) {
|
||||
if (key == KeyEvent::Select && get_selected_entry().is_directory) {
|
||||
push_dir(get_selected_entry().path);
|
||||
} else {
|
||||
button_rename.focus();
|
||||
}
|
||||
};
|
||||
|
||||
button_rename.on_select = [this](Button&) {
|
||||
if (selected_is_valid())
|
||||
on_rename();
|
||||
};
|
||||
|
||||
button_new_dir.on_select = [this](Button&) {
|
||||
on_new_dir();
|
||||
};
|
||||
}
|
||||
button_delete.on_select = [this](Button&) {
|
||||
if (selected_is_valid())
|
||||
on_delete();
|
||||
};
|
||||
|
||||
button_new_dir.on_select = [this](Button&) {
|
||||
on_new_dir();
|
||||
};
|
||||
|
||||
button_copy.on_select = [this](Button&) {
|
||||
if (selected_is_valid() && !get_selected_entry().is_directory)
|
||||
copy_path = get_selected_full_path();
|
||||
else
|
||||
nav_.display_modal("Copy", "Can't copy that.");
|
||||
};
|
||||
|
||||
button_paste.on_select = [this](Button&) {
|
||||
if (!copy_path.empty())
|
||||
on_paste();
|
||||
else
|
||||
nav_.display_modal("Paste", "Copy a file first.");
|
||||
};
|
||||
}
|
||||
|
||||
}
|
||||
|
@@ -36,6 +36,12 @@ struct fileman_entry {
|
||||
bool is_directory { };
|
||||
};
|
||||
|
||||
enum class EmptyReason : uint8_t {
|
||||
NotEmpty,
|
||||
NoFiles,
|
||||
NoSDC
|
||||
};
|
||||
|
||||
class FileManBaseView : public View {
|
||||
public:
|
||||
FileManBaseView(
|
||||
@@ -43,11 +49,13 @@ public:
|
||||
std::string filter
|
||||
);
|
||||
|
||||
virtual ~FileManBaseView() { }
|
||||
|
||||
void focus() override;
|
||||
std::string title() const override { return "Fileman"; };
|
||||
|
||||
protected:
|
||||
static constexpr size_t max_filename_length = 50;
|
||||
static constexpr size_t max_filename_length = 64;
|
||||
|
||||
struct file_assoc_t {
|
||||
std::filesystem::path extension;
|
||||
@@ -65,7 +73,6 @@ protected:
|
||||
{ u"", &bitmap_icon_file, ui::Color::light_grey() } // NB: Must be last.
|
||||
};
|
||||
|
||||
|
||||
std::filesystem::path get_selected_full_path() const;
|
||||
const fileman_entry& get_selected_entry() const;
|
||||
|
||||
@@ -78,7 +85,7 @@ protected:
|
||||
|
||||
NavigationView& nav_;
|
||||
|
||||
bool empty_root { false };
|
||||
EmptyReason empty_ { EmptyReason::NotEmpty };
|
||||
std::function<void(KeyEvent)> on_select_entry { nullptr };
|
||||
std::function<void(bool)> on_refresh_widgets { nullptr };
|
||||
|
||||
@@ -104,57 +111,96 @@ protected:
|
||||
};
|
||||
|
||||
Button button_exit {
|
||||
{ 16 * 8, 34 * 8, 14 * 8, 32 },
|
||||
{ 21 * 8, 34 * 8, 9 * 8, 32 },
|
||||
"Exit"
|
||||
};
|
||||
};
|
||||
|
||||
/*class FileSaveView : public FileManBaseView {
|
||||
public:
|
||||
FileSaveView(NavigationView& nav);
|
||||
~FileSaveView();
|
||||
|
||||
private:
|
||||
std::string name_buffer { };
|
||||
|
||||
void on_save_name();
|
||||
|
||||
Text text_save {
|
||||
{ 4 * 8, 15 * 8, 8 * 8, 16 },
|
||||
"Save as:",
|
||||
};
|
||||
Button button_save_name {
|
||||
{ 4 * 8, 18 * 8, 12 * 8, 32 },
|
||||
"Name (set)"
|
||||
};
|
||||
LiveDateTime live_timestamp {
|
||||
{ 17 * 8, 24 * 8, 11 * 8, 16 }
|
||||
};
|
||||
};*/
|
||||
|
||||
class FileLoadView : public FileManBaseView {
|
||||
public:
|
||||
std::function<void(std::filesystem::path)> on_changed { };
|
||||
|
||||
FileLoadView(NavigationView& nav, std::string filter);
|
||||
virtual ~FileLoadView() { }
|
||||
|
||||
private:
|
||||
void refresh_widgets(const bool v);
|
||||
};
|
||||
|
||||
/*
|
||||
// It would be nice to be able to launch FileLoadView
|
||||
// but it will OOM if launched from within FileManager.
|
||||
class FileSaveView : public View {
|
||||
public:
|
||||
FileSaveView(
|
||||
NavigationView& nav,
|
||||
const std::filesystem::path& path,
|
||||
const std::filesystem::path& file);
|
||||
|
||||
std::function<void(std::filesystem::path)> on_save { };
|
||||
|
||||
private:
|
||||
static constexpr size_t max_filename_length = 64;
|
||||
|
||||
void refresh_widgets();
|
||||
|
||||
NavigationView& nav_;
|
||||
std::filesystem::path path_;
|
||||
std::filesystem::path file_;
|
||||
std::string buffer_ { };
|
||||
|
||||
Labels labels {
|
||||
{ { 0 * 8, 1 * 16 }, "Path:", Color::light_grey() },
|
||||
{ { 0 * 8, 6 * 16 }, "Filename:", Color::light_grey() },
|
||||
};
|
||||
|
||||
Text text_path {
|
||||
{ 0 * 8, 2 * 16, 30 * 8, 16 },
|
||||
"",
|
||||
};
|
||||
|
||||
Button button_edit_path {
|
||||
{ 18 * 8, 3 * 16, 11 * 8, 32 },
|
||||
"Edit Path"
|
||||
};
|
||||
|
||||
Text text_name {
|
||||
{ 0 * 8, 7 * 16, 30 * 8, 16 },
|
||||
"",
|
||||
};
|
||||
|
||||
Button button_edit_name {
|
||||
{ 18 * 8, 8 * 16, 11 * 8, 32 },
|
||||
"Edit Name"
|
||||
};
|
||||
|
||||
Button button_save {
|
||||
{ 10 * 8, 16 * 16, 9 * 8, 32 },
|
||||
"Save"
|
||||
};
|
||||
|
||||
Button button_cancel {
|
||||
{ 20 * 8, 16 * 16, 9 * 8, 32 },
|
||||
"Cancel"
|
||||
};
|
||||
};
|
||||
*/
|
||||
|
||||
class FileManagerView : public FileManBaseView {
|
||||
public:
|
||||
FileManagerView(NavigationView& nav);
|
||||
~FileManagerView();
|
||||
virtual ~FileManagerView() { }
|
||||
|
||||
private:
|
||||
// Passed by ref to other views needing lifetime extension.
|
||||
std::string name_buffer { };
|
||||
std::filesystem::path copy_path { };
|
||||
|
||||
void refresh_widgets(const bool v);
|
||||
void on_rename();
|
||||
void on_delete();
|
||||
void on_new_dir();
|
||||
void on_paste();
|
||||
|
||||
// True if the selected entry is a real file item.
|
||||
bool selected_is_valid() const;
|
||||
@@ -169,19 +215,29 @@ private:
|
||||
};
|
||||
|
||||
Button button_rename {
|
||||
{ 0 * 8, 29 * 8, 14 * 8, 32 },
|
||||
{ 0 * 8, 29 * 8, 9 * 8, 32 },
|
||||
"Rename"
|
||||
};
|
||||
|
||||
Button button_delete {
|
||||
{ 16 * 8, 29 * 8, 14 * 8, 32 },
|
||||
{ 21 * 4, 29 * 8, 9 * 8, 32 },
|
||||
"Delete"
|
||||
};
|
||||
|
||||
Button button_new_dir {
|
||||
{ 0 * 8, 34 * 8, 14 * 8, 32 },
|
||||
{ 21 * 8, 29 * 8, 9 * 8, 32 },
|
||||
"New Dir"
|
||||
};
|
||||
|
||||
Button button_copy {
|
||||
{ 0 * 8, 34 * 8, 9 * 8, 32 },
|
||||
"Copy"
|
||||
};
|
||||
|
||||
Button button_paste {
|
||||
{ 21 * 4, 34 * 8, 9 * 8, 32 },
|
||||
"Paste"
|
||||
};
|
||||
};
|
||||
|
||||
} /* namespace ui */
|
||||
|
Reference in New Issue
Block a user