Save/load pinned/recent files/folders correctly

This commit is contained in:
David Capello 2018-12-22 02:08:04 -03:00
parent 72313e1c48
commit 7a57007fa3
10 changed files with 207 additions and 138 deletions

View File

@ -545,6 +545,8 @@ go_up_button_tooltip = Up to parent folder
new_folder_button_tooltip = New folder new_folder_button_tooltip = New folder
file_name = File name: file_name = File name:
file_type = File type: file_type = File type:
pinned_folders = Pinned Folders
recent_folders = Recent Folders
[filters] [filters]
selected_cels = Selected selected_cels = Selected

2
laf

@ -1 +1 @@
Subproject commit 3355825d61b8d0d92ffdea970fd9e1434bd4ecca Subproject commit 75f29b27c661e66e1549dc6e6a172f284cd9326b

View File

@ -474,17 +474,20 @@ bool AppMenus::rebuildRecentList()
submenu = new Menu(); submenu = new Menu();
list_menuitem->setSubmenu(submenu); list_menuitem->setSubmenu(submenu);
auto it = App::instance()->recentFiles()->files_begin(); auto recent = App::instance()->recentFiles();
auto end = App::instance()->recentFiles()->files_end(); base::paths files;
if (it != end) { files.insert(files.end(),
recent->pinnedFiles().begin(),
recent->pinnedFiles().end());
files.insert(files.end(),
recent->recentFiles().begin(),
recent->recentFiles().end());
if (!files.empty()) {
Params params; Params params;
for (const auto& fn : files) {
for (; it != end; ++it) { params.set("filename", fn.c_str());
const char* filename = it->c_str();
params.set("filename", filename);
menuitem = new AppMenuItem( menuitem = new AppMenuItem(
base::get_file_name(filename).c_str(), base::get_file_name(fn).c_str(),
cmd_open_file, cmd_open_file,
params); params);
submenu->addChild(menuitem); submenu->addChild(menuitem);

View File

@ -274,4 +274,11 @@ void del_config_value(const char* section, const char* name)
g_configs.back()->deleteValue(section, name); g_configs.back()->deleteValue(section, name);
} }
base::paths enum_config_keys(const char* section)
{
base::paths keys;
g_configs.back()->getAllKeys(section, keys);
return keys;
}
} // namespace app } // namespace app

View File

@ -10,6 +10,7 @@
#pragma once #pragma once
#include "app/color.h" #include "app/color.h"
#include "base/paths.h"
#include "gfx/point.h" #include "gfx/point.h"
#include "gfx/rect.h" #include "gfx/rect.h"
#include "gfx/size.h" #include "gfx/size.h"
@ -58,6 +59,8 @@ namespace app {
void del_config_value(const char* section, const char* name); void del_config_value(const char* section, const char* name);
base::paths enum_config_keys(const char* section);
// Generic get/set_config_value functions // Generic get/set_config_value functions
inline const char* get_config_value(const char* section, const char* name, const char* value) { inline const char* get_config_value(const char* section, const char* name, const char* value) {

View File

@ -12,8 +12,8 @@
#include "app/recent_files.h" #include "app/recent_files.h"
#include "app/ini_file.h" #include "app/ini_file.h"
#include "base/convert_to.h"
#include "base/fs.h" #include "base/fs.h"
#include "fmt/format.h"
#include <cstdio> #include <cstdio>
#include <cstring> #include <cstring>
@ -21,8 +21,10 @@
namespace { namespace {
const char* kRecentFilesSection = "RecentFiles"; const char* kSectionName[] = { "PinnedFiles",
const char* kRecentFoldersSection = "RecentPaths"; "RecentFiles",
"PinnedPaths",
"RecentPaths" };
struct compare_path { struct compare_path {
std::string a; std::string a;
@ -32,81 +34,40 @@ struct compare_path {
} }
}; };
std::string format_filename_var(const int i) {
return fmt::format("Filename{0:02d}", i);
}
std::string format_folder_var(const int i) {
return fmt::format("Path{0:02d}", i);
}
} }
namespace app { namespace app {
RecentFiles::RecentFiles(const int limit) RecentFiles::RecentFiles(const int limit)
: m_files(limit) : m_limit(limit)
, m_paths(limit)
{ {
for (int c=m_files.limit()-1; c>=0; c--) { load();
const char* filename = get_config_string(kRecentFilesSection,
format_filename_var(c).c_str(),
nullptr);
if (filename && *filename && base::is_file(filename)) {
std::string fn = normalizePath(filename);
m_files.addItem(fn, compare_path(fn));
}
}
for (int c=m_paths.limit()-1; c>=0; c--) {
const char* path = get_config_string(kRecentFoldersSection,
format_folder_var(c).c_str(),
nullptr);
if (path && *path) {
std::string p = normalizePath(path);
m_paths.addItem(p, compare_path(p));
}
}
} }
RecentFiles::~RecentFiles() RecentFiles::~RecentFiles()
{ {
// Save recent files save();
int c = 0;
for (auto const& filename : m_files) {
set_config_string(kRecentFilesSection,
format_filename_var(c).c_str(),
filename.c_str());
++c;
}
for (; c<m_files.limit(); ++c) {
del_config_value(kRecentFilesSection,
format_filename_var(c).c_str());
}
// Save recent folders
c = 0;
for (auto const& path : m_paths) {
set_config_string(kRecentFoldersSection,
format_folder_var(c).c_str(),
path.c_str());
++c;
}
for (; c<m_files.limit(); ++c) {
del_config_value(kRecentFoldersSection,
format_folder_var(c).c_str());
}
} }
void RecentFiles::addRecentFile(const std::string& filename) void RecentFiles::addRecentFile(const std::string& filename)
{ {
std::string fn = normalizePath(filename); std::string fn = normalizePath(filename);
m_files.addItem(fn, compare_path(fn));
// If the filename is already pinned, we don't add it in the
// collection of recent files collection.
auto it = std::find(m_paths[kPinnedFiles].begin(),
m_paths[kPinnedFiles].end(), fn);
if (it != m_paths[kPinnedFiles].end())
return;
addItem(m_paths[kRecentFiles], fn);
// Add recent folder
std::string path = base::get_file_path(fn); std::string path = base::get_file_path(fn);
m_paths.addItem(path, compare_path(path)); it = std::find(m_paths[kPinnedFolders].begin(),
m_paths[kPinnedFolders].end(), path);
if (it == m_paths[kPinnedFolders].end()) {
addItem(m_paths[kRecentFolders], path);
}
Changed(); Changed();
} }
@ -114,7 +75,7 @@ void RecentFiles::addRecentFile(const std::string& filename)
void RecentFiles::removeRecentFile(const std::string& filename) void RecentFiles::removeRecentFile(const std::string& filename)
{ {
std::string fn = normalizePath(filename); std::string fn = normalizePath(filename);
m_files.removeItem(fn, compare_path(fn)); removeItem(m_paths[kRecentFiles], fn);
std::string dir = base::get_file_path(fn); std::string dir = base::get_file_path(fn);
if (!base::is_directory(dir)) if (!base::is_directory(dir))
@ -126,43 +87,48 @@ void RecentFiles::removeRecentFile(const std::string& filename)
void RecentFiles::removeRecentFolder(const std::string& dir) void RecentFiles::removeRecentFolder(const std::string& dir)
{ {
std::string fn = normalizePath(dir); std::string fn = normalizePath(dir);
m_paths.removeItem(fn, compare_path(fn)); removeItem(m_paths[kRecentFolders], fn);
Changed(); Changed();
} }
void RecentFiles::setLimit(const int n) void RecentFiles::setLimit(const int newLimit)
{ {
m_files.setLimit(n); ASSERT(newLimit >= 0);
m_paths.setLimit(n);
for (auto& list : m_paths) {
if (newLimit < list.size()) {
auto it = list.begin();
std::advance(it, newLimit);
list.erase(it, list.end());
}
}
m_limit = newLimit;
Changed(); Changed();
} }
void RecentFiles::clear() void RecentFiles::clear()
{ {
m_files.clear(); // Clear only recent items (not pinned items)
m_paths.clear(); m_paths[kRecentFiles].clear();
m_paths[kRecentFolders].clear();
Changed(); Changed();
} }
void RecentFiles::setFiles(base::paths paths) void RecentFiles::setFiles(const base::paths& pinnedFiles,
const base::paths& recentFiles)
{ {
m_files.clear(); m_paths[kPinnedFiles] = pinnedFiles;
for (auto it=paths.rbegin(), end=paths.rend(); it!=end; ++it) { m_paths[kRecentFiles] = recentFiles;
const auto& p = *it;
m_files.addItem(p, compare_path(p));
}
} }
void RecentFiles::setFolders(base::paths paths) void RecentFiles::setFolders(const base::paths& pinnedFolders,
const base::paths& recentFolders)
{ {
m_paths.clear(); m_paths[kPinnedFolders] = pinnedFolders;
for (auto it=paths.rbegin(), end=paths.rend(); it!=end; ++it) { m_paths[kRecentFolders] = recentFolders;
const auto& p = *it;
m_paths.addItem(p, compare_path(p));
}
} }
std::string RecentFiles::normalizePath(const std::string& filename) std::string RecentFiles::normalizePath(const std::string& filename)
@ -170,4 +136,60 @@ std::string RecentFiles::normalizePath(const std::string& filename)
return base::normalize_path(filename); return base::normalize_path(filename);
} }
void RecentFiles::addItem(base::paths& list, const std::string& fn)
{
auto it = std::find_if(list.begin(), list.end(), compare_path(fn));
// If the item already exist in the list...
if (it != list.end()) {
// Move it to the first position
list.erase(it);
list.insert(list.begin(), fn);
return;
}
if (m_limit > 0)
list.insert(list.begin(), fn);
while (list.size() > m_limit)
list.erase(--list.end());
}
void RecentFiles::removeItem(base::paths& list, const std::string& fn)
{
auto it = std::find_if(list.begin(), list.end(), compare_path(fn));
if (it != list.end())
list.erase(it);
}
void RecentFiles::load()
{
for (int i=0; i<kCollections; ++i) {
for (const auto& key : enum_config_keys(kSectionName[i])) {
const char* fn = get_config_string(kSectionName[i], key.c_str(), nullptr);
if (fn && *fn &&
((i < 2 && base::is_file(fn)) ||
(i >= 2 && base::is_directory(fn)))) {
std::string normalFn = normalizePath(fn);
m_paths[i].push_back(normalFn);
}
}
}
}
void RecentFiles::save()
{
for (int i=0; i<kCollections; ++i) {
for (const auto& key : enum_config_keys(kSectionName[i]))
del_config_value(kSectionName[i],
key.c_str());
for (int j=0; j<m_paths[i].size(); ++j) {
set_config_string(kSectionName[i],
base::convert_to<std::string>(j).c_str(),
m_paths[i][j].c_str());
}
}
}
} // namespace app } // namespace app

View File

@ -10,7 +10,6 @@
#pragma once #pragma once
#include "base/paths.h" #include "base/paths.h"
#include "base/recent_items.h"
#include "obs/signal.h" #include "obs/signal.h"
#include <string> #include <string>
@ -18,18 +17,16 @@
namespace app { namespace app {
class RecentFiles { class RecentFiles {
enum { kPinnedFiles,
kRecentFiles,
kPinnedFolders,
kRecentFolders,
kCollections };
public: public:
typedef base::RecentItems<std::string> List; const base::paths& pinnedFiles() { return m_paths[kPinnedFiles]; }
typedef List::iterator iterator; const base::paths& recentFiles() { return m_paths[kRecentFiles]; }
typedef List::const_iterator const_iterator; const base::paths& pinnedFolders() { return m_paths[kPinnedFolders]; }
const base::paths& recentFolders() { return m_paths[kRecentFolders]; }
// Iterate through recent files.
const_iterator files_begin() { return m_files.begin(); }
const_iterator files_end() { return m_files.end(); }
// Iterate through recent paths.
const_iterator paths_begin() { return m_paths.begin(); }
const_iterator paths_end() { return m_paths.end(); }
RecentFiles(const int limit); RecentFiles(const int limit);
~RecentFiles(); ~RecentFiles();
@ -37,19 +34,25 @@ namespace app {
void addRecentFile(const std::string& filename); void addRecentFile(const std::string& filename);
void removeRecentFile(const std::string& filename); void removeRecentFile(const std::string& filename);
void removeRecentFolder(const std::string& dir); void removeRecentFolder(const std::string& dir);
void setLimit(const int n); void setLimit(const int newLimit);
void clear(); void clear();
void setFiles(base::paths paths); void setFiles(const base::paths& pinnedFiles,
void setFolders(base::paths paths); const base::paths& recentFiles);
void setFolders(const base::paths& pinnedFolders,
const base::paths& recentFolders);
obs::signal<void()> Changed; obs::signal<void()> Changed;
private: private:
std::string normalizePath(const std::string& filename); std::string normalizePath(const std::string& filename);
void addItem(base::paths& list, const std::string& filename);
void removeItem(base::paths& list, const std::string& filename);
void load();
void save();
List m_files; base::paths m_paths[kCollections];
List m_paths; int m_limit;
}; };
} // namespace app } // namespace app

View File

@ -20,6 +20,7 @@
#include "app/recent_files.h" #include "app/recent_files.h"
#include "app/ui/file_list.h" #include "app/ui/file_list.h"
#include "app/ui/file_list_view.h" #include "app/ui/file_list_view.h"
#include "app/ui/separator_in_view.h"
#include "app/ui/skin/skin_theme.h" #include "app/ui/skin/skin_theme.h"
#include "app/widget_loader.h" #include "app/widget_loader.h"
#include "base/bind.h" #include "base/bind.h"
@ -36,6 +37,7 @@
#include <cctype> #include <cctype>
#include <cerrno> #include <cerrno>
#include <iterator> #include <iterator>
#include <list>
#include <set> #include <set>
#include <string> #include <string>
#include <vector> #include <vector>
@ -675,14 +677,20 @@ void FileSelector::updateLocation()
} }
// Add paths from recent files list // Add paths from recent files list
{ auto recent = App::instance()->recentFiles();
location()->addItem(""); if (!recent->pinnedFolders().empty()) {
location()->addItem("-------- Recent Paths --------"); auto sep = new SeparatorInView(Strings::file_selector_pinned_folders(), HORIZONTAL);
sep->setMinSize(gfx::Size(0, sep->sizeHint().h*2));
auto it = App::instance()->recentFiles()->paths_begin(); location()->addItem(sep);
auto end = App::instance()->recentFiles()->paths_end(); for (const auto& fn : recent->pinnedFolders())
for (; it != end; ++it) location()->addItem(new CustomFolderNameItem(fn.c_str()));
location()->addItem(new CustomFolderNameItem(it->c_str())); }
if (!recent->recentFolders().empty()) {
auto sep = new SeparatorInView(Strings::file_selector_recent_folders(), HORIZONTAL);
sep->setMinSize(gfx::Size(0, sep->sizeHint().h*2));
location()->addItem(sep);
for (const auto& fn : recent->recentFolders())
location()->addItem(new CustomFolderNameItem(fn.c_str()));
} }
// Select the location // Select the location

View File

@ -43,11 +43,13 @@ using namespace skin;
class RecentFileItem : public DraggableWidget<LinkLabel> { class RecentFileItem : public DraggableWidget<LinkLabel> {
public: public:
RecentFileItem(const std::string& file) RecentFileItem(const std::string& file,
const bool pinned)
: DraggableWidget<LinkLabel>("") : DraggableWidget<LinkLabel>("")
, m_fullpath(file) , m_fullpath(file)
, m_name(base::get_file_name(file)) , m_name(base::get_file_name(file))
, m_path(base::get_file_path(file)) { , m_path(base::get_file_path(file))
, m_pinned(pinned) {
initTheme(); initTheme();
} }
@ -121,6 +123,7 @@ protected:
parent->moveChildTo(this, moveTo); parent->moveChildTo(this, moveTo);
parent->layout(); parent->layout();
} }
saveConfig();
return true; return true;
} }
break; break;
@ -188,7 +191,7 @@ protected:
} }
} }
static_cast<RecentListBox*>(parent())->updateRecentListFromUIItems(); saveConfig();
} }
private: private:
@ -204,10 +207,14 @@ private:
pinSize.w, pinSize.h); pinSize.w, pinSize.h);
} }
void saveConfig() {
static_cast<RecentListBox*>(parent())->updateRecentListFromUIItems();
}
std::string m_fullpath; std::string m_fullpath;
std::string m_name; std::string m_name;
std::string m_path; std::string m_path;
bool m_pinned = false; bool m_pinned;
}; };
////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////
@ -243,10 +250,17 @@ void RecentListBox::rebuildList()
void RecentListBox::updateRecentListFromUIItems() void RecentListBox::updateRecentListFromUIItems()
{ {
base::paths paths; base::paths pinnedPaths;
for (auto item : children()) base::paths recentPaths;
paths.push_back(static_cast<RecentFileItem*>(item)->fullpath()); for (auto item : children()) {
onUpdateRecentListFromUIItems(paths); auto fi = static_cast<RecentFileItem*>(item);
if (fi->pinned())
pinnedPaths.push_back(fi->fullpath());
else
recentPaths.push_back(fi->fullpath());
}
onUpdateRecentListFromUIItems(pinnedPaths,
recentPaths);
} }
void RecentListBox::onScrollRegion(ui::ScrollRegionEvent& ev) void RecentListBox::onScrollRegion(ui::ScrollRegionEvent& ev)
@ -266,10 +280,10 @@ RecentFilesListBox::RecentFilesListBox()
void RecentFilesListBox::onRebuildList() void RecentFilesListBox::onRebuildList()
{ {
auto recent = App::instance()->recentFiles(); auto recent = App::instance()->recentFiles();
auto it = recent->files_begin(); for (const auto& fn : recent->pinnedFiles())
auto end = recent->files_end(); addChild(new RecentFileItem(fn, true));
for (; it != end; ++it) for (const auto& fn : recent->recentFiles())
addChild(new RecentFileItem(it->c_str())); addChild(new RecentFileItem(fn, false));
} }
void RecentFilesListBox::onClick(const std::string& path) void RecentFilesListBox::onClick(const std::string& path)
@ -286,9 +300,11 @@ void RecentFilesListBox::onClick(const std::string& path)
UIContext::instance()->executeCommand(command, params); UIContext::instance()->executeCommand(command, params);
} }
void RecentFilesListBox::onUpdateRecentListFromUIItems(const base::paths& paths) void RecentFilesListBox::onUpdateRecentListFromUIItems(const base::paths& pinnedPaths,
const base::paths& recentPaths)
{ {
App::instance()->recentFiles()->setFiles(paths); App::instance()->recentFiles()->setFiles(pinnedPaths,
recentPaths);
} }
////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////
@ -302,10 +318,10 @@ RecentFoldersListBox::RecentFoldersListBox()
void RecentFoldersListBox::onRebuildList() void RecentFoldersListBox::onRebuildList()
{ {
auto recent = App::instance()->recentFiles(); auto recent = App::instance()->recentFiles();
auto it = recent->paths_begin(); for (const auto& fn : recent->pinnedFolders())
auto end = recent->paths_end(); addChild(new RecentFileItem(fn, true));
for (; it != end; ++it) for (const auto& fn : recent->recentFolders())
addChild(new RecentFileItem(*it)); addChild(new RecentFileItem(fn, false));
} }
void RecentFoldersListBox::onClick(const std::string& path) void RecentFoldersListBox::onClick(const std::string& path)
@ -322,9 +338,11 @@ void RecentFoldersListBox::onClick(const std::string& path)
UIContext::instance()->executeCommand(command, params); UIContext::instance()->executeCommand(command, params);
} }
void RecentFoldersListBox::onUpdateRecentListFromUIItems(const base::paths& paths) void RecentFoldersListBox::onUpdateRecentListFromUIItems(const base::paths& pinnedPaths,
const base::paths& recentPaths)
{ {
App::instance()->recentFiles()->setFolders(paths); App::instance()->recentFiles()->setFolders(pinnedPaths,
recentPaths);
} }
} // namespace app } // namespace app

View File

@ -32,7 +32,8 @@ namespace app {
virtual void onRebuildList() = 0; virtual void onRebuildList() = 0;
virtual void onClick(const std::string& path) = 0; virtual void onClick(const std::string& path) = 0;
virtual void onUpdateRecentListFromUIItems(const base::paths& paths) = 0; virtual void onUpdateRecentListFromUIItems(const base::paths& pinnedPaths,
const base::paths& recentPaths) = 0;
private: private:
void rebuildList(); void rebuildList();
@ -48,7 +49,8 @@ namespace app {
private: private:
void onRebuildList() override; void onRebuildList() override;
void onClick(const std::string& path) override; void onClick(const std::string& path) override;
void onUpdateRecentListFromUIItems(const base::paths& paths) override; void onUpdateRecentListFromUIItems(const base::paths& pinnedPaths,
const base::paths& recentPaths) override;
}; };
class RecentFoldersListBox : public RecentListBox { class RecentFoldersListBox : public RecentListBox {
@ -58,7 +60,8 @@ namespace app {
private: private:
void onRebuildList() override; void onRebuildList() override;
void onClick(const std::string& path) override; void onClick(const std::string& path) override;
void onUpdateRecentListFromUIItems(const base::paths& paths) override; void onUpdateRecentListFromUIItems(const base::paths& pinnedPaths,
const base::paths& recentPaths) override;
}; };
} // namespace app } // namespace app