From 676ab8649bde38432907e0a29e5530fb7abd23d3 Mon Sep 17 00:00:00 2001
From: Lawrence Lee <45837045+Lawrence37@users.noreply.github.com>
Date: Sat, 10 Jun 2023 18:28:17 -0700
Subject: [PATCH 1/6] Add recursive sub-folder image browsing
Add toggle button in the top toolbar of the file browser to activate or
deactivate recursive browsing. Toggle state is saved in options.
Limit recursion depth and explored directory count.
---
rtdata/images/svg/folder-subfolder.svg | 98 +++++++++++++++
rtdata/languages/default | 1 +
rtgui/filecatalog.cc | 159 ++++++++++++++++++-------
rtgui/filecatalog.h | 14 ++-
rtgui/options.cc | 18 +++
rtgui/options.h | 3 +
6 files changed, 246 insertions(+), 47 deletions(-)
create mode 100644 rtdata/images/svg/folder-subfolder.svg
diff --git a/rtdata/images/svg/folder-subfolder.svg b/rtdata/images/svg/folder-subfolder.svg
new file mode 100644
index 000000000..f22d0da0f
--- /dev/null
+++ b/rtdata/images/svg/folder-subfolder.svg
@@ -0,0 +1,98 @@
+
+
+
+
diff --git a/rtdata/languages/default b/rtdata/languages/default
index 050836d21..c23410661 100644
--- a/rtdata/languages/default
+++ b/rtdata/languages/default
@@ -200,6 +200,7 @@ FILEBROWSER_SHOWRANK4HINT;Show images ranked as 4-star.\nShortcut: 4
FILEBROWSER_SHOWRANK5HINT;Show images ranked as 5-star.\nShortcut: 5
FILEBROWSER_SHOWRECENTLYSAVEDHINT;Show saved images.\nShortcut: Alt-7
FILEBROWSER_SHOWRECENTLYSAVEDNOTHINT;Show unsaved images.\nShortcut: Alt-6
+FILEBROWSER_SHOWRECURSIVE;Show images in sub-folders recursively.
FILEBROWSER_SHOWTRASHHINT;Show contents of trash.\nShortcut: Ctrl-t
FILEBROWSER_SHOWUNCOLORHINT;Show images without a color label.\nShortcut: Alt-0
FILEBROWSER_SHOWUNRANKHINT;Show unranked images.\nShortcut: 0
diff --git a/rtgui/filecatalog.cc b/rtgui/filecatalog.cc
index d6c440570..5c20aa5fe 100644
--- a/rtgui/filecatalog.cc
+++ b/rtgui/filecatalog.cc
@@ -19,6 +19,8 @@
*/
#include "filecatalog.h"
+#include
+#include
#include
#include
@@ -347,9 +349,17 @@ FileCatalog::FileCatalog (CoarsePanel* cp, ToolBar* tb, FilePanel* filepanel) :
bCateg[19] = bOriginal->signal_toggled().connect (sigc::bind(sigc::mem_fun(*this, &FileCatalog::categoryButtonToggled), bOriginal, true));
bOriginal->signal_button_press_event().connect (sigc::mem_fun(*this, &FileCatalog::capture_event), false);
+ bRecursive = Gtk::manage(new Gtk::ToggleButton());
+ bRecursive->set_image(*Gtk::manage(new RTImage("folder-subfolder.png")));
+ bRecursive->set_tooltip_text(M("FILEBROWSER_SHOWRECURSIVE"));
+ bRecursive->set_relief(Gtk::RELIEF_NONE);
+ bRecursive->set_active(options.browseRecursive);
+ bRecursive->signal_toggled().connect(sigc::mem_fun(*this, &FileCatalog::showRecursiveToggled));
+
buttonBar->pack_start (*bTrash, Gtk::PACK_SHRINK);
buttonBar->pack_start (*bNotTrash, Gtk::PACK_SHRINK);
buttonBar->pack_start (*bOriginal, Gtk::PACK_SHRINK);
+ buttonBar->pack_start(*bRecursive, Gtk::PACK_SHRINK);
buttonBar->pack_start (*Gtk::manage(new Gtk::Separator(Gtk::ORIENTATION_VERTICAL)), Gtk::PACK_SHRINK);
fileBrowser->trash_changed().connect( sigc::mem_fun(*this, &FileCatalog::trashChanged) );
@@ -544,9 +554,7 @@ void FileCatalog::closeDir ()
exportPanel->set_sensitive (false);
}
- if (dirMonitor) {
- dirMonitor->cancel ();
- }
+ dirMonitors.clear();
// ignore old requests
++selectedDirectoryId;
@@ -570,60 +578,73 @@ void FileCatalog::closeDir ()
redrawAll ();
}
-std::vector FileCatalog::getFileList()
+std::vector FileCatalog::getFileList(std::vector> *dirs_explored)
{
std::vector names;
const std::set& extensions = options.parsedExtensionsSet;
- try {
+ static void (*getFilesRecursively)(const Glib::ustring &, int, int &, std::vector &, std::vector> *) = [](const Glib::ustring &dir_path, int max_depth, int &dir_quota, std::vector &file_names, std::vector> * directories_explored) {
+ try {
- const auto dir = Gio::File::create_for_path(selectedDirectory);
+ const auto dir = Gio::File::create_for_path(dir_path);
- auto enumerator = dir->enumerate_children("standard::name,standard::type,standard::is-hidden");
+ auto enumerator = dir->enumerate_children("standard::name,standard::type,standard::is-hidden");
- while (true) {
- try {
- const auto file = enumerator->next_file();
- if (!file) {
- break;
- }
+ if (directories_explored) {
+ directories_explored->push_back(dir);
+ }
- if (file->get_file_type() == Gio::FILE_TYPE_DIRECTORY) {
- continue;
- }
+ while (true) {
+ try {
+ const auto file = enumerator->next_file();
+ if (!file) {
+ break;
+ }
- if (!options.fbShowHidden && file->is_hidden()) {
- continue;
- }
+ if (!options.fbShowHidden && file->is_hidden()) {
+ continue;
+ }
- const Glib::ustring fname = file->get_name();
- const auto lastdot = fname.find_last_of('.');
+ if (file->get_file_type() == Gio::FILE_TYPE_DIRECTORY) {
+ if (max_depth > 0 && dir_quota > 0) {
+ const Glib::ustring child_dir_path = Glib::build_filename(dir_path, file->get_name());
+ getFilesRecursively(child_dir_path, max_depth - 1, --dir_quota, file_names, directories_explored);
+ }
+ continue;
+ }
- if (lastdot >= fname.length() - 1) {
- continue;
- }
+ const Glib::ustring fname = file->get_name();
+ const auto lastdot = fname.find_last_of('.');
- if (extensions.find(fname.substr(lastdot + 1).lowercase()) == extensions.end()) {
- continue;
- }
+ if (lastdot >= fname.length() - 1) {
+ continue;
+ }
- names.push_back(Glib::build_filename(selectedDirectory, fname));
- } catch (Glib::Exception& exception) {
- if (rtengine::settings->verbose) {
- std::cerr << exception.what() << std::endl;
+ if (extensions.find(fname.substr(lastdot + 1).lowercase()) == extensions.end()) {
+ continue;
+ }
+
+ file_names.emplace_back(Glib::build_filename(dir_path, fname));
+ } catch (Glib::Exception& exception) {
+ if (rtengine::settings->verbose) {
+ std::cerr << exception.what() << std::endl;
+ }
}
}
+
+ } catch (Glib::Exception& exception) {
+
+ if (rtengine::settings->verbose) {
+ std::cerr << "Failed to list directory \"" << dir_path << "\": " << exception.what() << std::endl;
+ }
+
}
+ };
- } catch (Glib::Exception& exception) {
-
- if (rtengine::settings->verbose) {
- std::cerr << "Failed to list directory \"" << selectedDirectory << "\": " << exception.what() << std::endl;
- }
-
- }
+ int dirs_left = options.browseRecursive ? options.browseRecursiveMaxDirs : 0;
+ getFilesRecursively(selectedDirectory, options.browseRecursiveDepth, dirs_left, names, dirs_explored);
return names;
}
@@ -649,9 +670,10 @@ void FileCatalog::dirSelected (const Glib::ustring& dirname, const Glib::ustring
selectedDirectory = dir->get_parse_name();
+ std::vector> allDirs;
BrowsePath->set_text(selectedDirectory);
buttonBrowsePath->set_image(*iRefreshWhite);
- fileNameList = getFileList();
+ fileNameList = getFileList(&allDirs);
for (unsigned int i = 0; i < fileNameList.size(); i++) {
if (openfile.empty() || fileNameList[i] != openfile) { // if we opened a file at the beginning don't add it again
@@ -667,13 +689,45 @@ void FileCatalog::dirSelected (const Glib::ustring& dirname, const Glib::ustring
filepanel->loadingThumbs(M("PROGRESSBAR_LOADINGTHUMBS"), 0);
}
- dirMonitor = dir->monitor_directory ();
- dirMonitor->signal_changed().connect (sigc::bind(sigc::mem_fun(*this, &FileCatalog::on_dir_changed), false));
+ refreshDirectoryMonitors(allDirs);
} catch (Glib::Exception& ex) {
std::cout << ex.what();
}
}
+void FileCatalog::refreshDirectoryMonitors(const std::vector> &dirs_to_monitor)
+{
+ std::vector updated_dir_names;
+ std::transform(
+ dirs_to_monitor.cbegin(), dirs_to_monitor.cend(),
+ std::back_inserter(updated_dir_names),
+ [](const Glib::RefPtr &updated_dir) { return updated_dir->get_path(); });
+
+ // Remove monitors on directories that are no longer shown.
+ dirMonitors.erase(
+ std::remove_if(dirMonitors.begin(), dirMonitors.end(),
+ [&updated_dir_names](const FileMonitorInfo &fileMonitorInfo) {
+ return std::find(updated_dir_names.cbegin(), updated_dir_names.cend(), fileMonitorInfo.filePath) == updated_dir_names.cend();
+ }),
+ dirMonitors.end());
+
+ // Add monitors that do not exist yet.
+ std::vector monitored_dir_names;
+ std::transform(
+ dirMonitors.cbegin(), dirMonitors.cend(),
+ std::back_inserter(monitored_dir_names),
+ [](const FileMonitorInfo &dir_monitor) { return dir_monitor.filePath; });
+ for (const auto &dir_to_monitor : dirs_to_monitor) {
+ const auto dir_path = dir_to_monitor->get_path();
+ if (std::find(monitored_dir_names.cbegin(), monitored_dir_names.cend(), dir_path) != monitored_dir_names.cend()) {
+ continue; // A monitor exists already.
+ }
+ auto dir_monitor = dir_to_monitor->monitor_directory();
+ dir_monitor->signal_changed().connect(sigc::bind(sigc::mem_fun(*this, &FileCatalog::on_dir_changed), false));
+ dirMonitors.emplace_back(dir_monitor, dir_path);
+ }
+}
+
void FileCatalog::enableTabMode(bool enable)
{
inTabMode = enable;
@@ -1581,6 +1635,12 @@ void FileCatalog::categoryButtonToggled (Gtk::ToggleButton* b, bool isMouseClick
}
}
+void FileCatalog::showRecursiveToggled()
+{
+ options.browseRecursive = bRecursive->get_active();
+ reparseDirectory();
+}
+
BrowserFilter FileCatalog::getFilter ()
{
@@ -1717,18 +1777,25 @@ void FileCatalog::reparseDirectory ()
// check if a thumbnailed file has been deleted
const std::vector& t = fileBrowser->getEntries();
std::vector fileNamesToDel;
+ std::vector fileNamesToRemove;
for (const auto& entry : t) {
if (!Glib::file_test(entry->filename, Glib::FILE_TEST_EXISTS)) {
fileNamesToDel.push_back(entry->filename);
+ fileNamesToRemove.push_back(entry->filename);
+ }
+ else if (!options.browseRecursive && Glib::path_get_dirname(entry->filename) != selectedDirectory) {
+ fileNamesToRemove.push_back(entry->filename);
}
}
- for (const auto& toDelete : fileNamesToDel) {
- delete fileBrowser->delEntry(toDelete);
- cacheMgr->deleteEntry(toDelete);
+ for (const auto& toRemove : fileNamesToRemove) {
+ delete fileBrowser->delEntry(toRemove);
--previewsLoaded;
}
+ for (const auto& toDelete : fileNamesToDel) {
+ cacheMgr->deleteEntry(toDelete);
+ }
if (!fileNamesToDel.empty()) {
_refreshProgressBar();
@@ -1741,7 +1808,8 @@ void FileCatalog::reparseDirectory ()
oldNames.insert(oldName.collate_key());
}
- fileNameList = getFileList();
+ std::vector> allDirs;
+ fileNameList = getFileList(&allDirs);
for (const auto& newName : fileNameList) {
if (oldNames.find(newName.collate_key()) == oldNames.end()) {
addFile(newName);
@@ -1749,6 +1817,7 @@ void FileCatalog::reparseDirectory ()
}
}
+ refreshDirectoryMonitors(allDirs);
}
void FileCatalog::on_dir_changed (const Glib::RefPtr& file, const Glib::RefPtr& other_file, Gio::FileMonitorEvent event_type, bool internal)
diff --git a/rtgui/filecatalog.h b/rtgui/filecatalog.h
index 23d56af73..f41c42475 100644
--- a/rtgui/filecatalog.h
+++ b/rtgui/filecatalog.h
@@ -54,6 +54,13 @@ public:
typedef sigc::slot DirSelectionSlot;
private:
+ struct FileMonitorInfo {
+ FileMonitorInfo(const Glib::RefPtr &file_monitor, const Glib::ustring &file_path) :
+ fileMonitor(file_monitor), filePath(file_path) {}
+ Glib::RefPtr fileMonitor;
+ Glib::ustring filePath;
+ };
+
FilePanel* filepanel;
Gtk::Box* hBox;
Glib::ustring selectedDirectory;
@@ -95,6 +102,7 @@ private:
Gtk::ToggleButton* bTrash;
Gtk::ToggleButton* bNotTrash;
Gtk::ToggleButton* bOriginal;
+ Gtk::ToggleButton* bRecursive;
Gtk::ToggleButton* categoryButtons[20];
Gtk::ToggleButton* exifInfo;
sigc::connection bCateg[20];
@@ -143,14 +151,15 @@ private:
std::set editedFiles;
guint modifierKey; // any modifiers held when rank button was pressed
- Glib::RefPtr dirMonitor;
+ std::vector dirMonitors;
IdleRegister idle_register;
void addAndOpenFile (const Glib::ustring& fname);
void addFile (const Glib::ustring& fName);
- std::vector getFileList ();
+ std::vector getFileList(std::vector> *dirs_explored = nullptr);
BrowserFilter getFilter ();
+ void refreshDirectoryMonitors(const std::vector> &dirs_to_monitor);
void trashChanged ();
public:
@@ -240,6 +249,7 @@ public:
void setExportPanel (ExportPanel* expanel);
void exifInfoButtonToggled();
void categoryButtonToggled (Gtk::ToggleButton* b, bool isMouseClick);
+ void showRecursiveToggled();
bool capture_event(GdkEventButton* event);
void filterChanged ();
void runFilterDialog ();
diff --git a/rtgui/options.cc b/rtgui/options.cc
index e89fcd647..ed6f03eb7 100644
--- a/rtgui/options.cc
+++ b/rtgui/options.cc
@@ -435,6 +435,9 @@ void Options::setDefaults()
parseExtensionsEnabled.clear();
parsedExtensions.clear();
parsedExtensionsSet.clear();
+ browseRecursive = false;
+ browseRecursiveDepth = 10;
+ browseRecursiveMaxDirs = 100;
renameUseTemplates = false;
renameTemplates.clear();
thumbnailZoomRatios.clear();
@@ -1342,6 +1345,18 @@ void Options::readFromFile(Glib::ustring fname)
if (keyFile.has_key("File Browser", "SortDescending")) {
sortDescending = keyFile.get_boolean("File Browser", "SortDescending");
}
+
+ if (keyFile.has_key("File Browser", "BrowseRecursive")) {
+ browseRecursive = keyFile.get_boolean("File Browser", "BrowseRecursive");
+ }
+
+ if (keyFile.has_key("File Browser", "BrowseRecursiveDepth")) {
+ browseRecursiveDepth = keyFile.get_integer("File Browser", "BrowseRecursiveDepth");
+ }
+
+ if (keyFile.has_key("File Browser", "BrowseRecursiveMaxDirs")) {
+ browseRecursiveMaxDirs = keyFile.get_integer("File Browser", "BrowseRecursiveMaxDirs");
+ }
}
if (keyFile.has_group("Clipping Indication")) {
@@ -2410,6 +2425,9 @@ void Options::saveToFile(Glib::ustring fname)
}
keyFile.set_integer("File Browser", "SortMethod", sortMethod);
keyFile.set_boolean("File Browser", "SortDescending", sortDescending);
+ keyFile.set_boolean("File Browser", "BrowseRecursive", browseRecursive);
+ keyFile.set_integer("File Browser", "BrowseRecursiveDepth", browseRecursiveDepth);
+ keyFile.set_integer("File Browser", "BrowseRecursiveMaxDirs", browseRecursiveMaxDirs);
keyFile.set_integer("Clipping Indication", "HighlightThreshold", highlightThreshold);
keyFile.set_integer("Clipping Indication", "ShadowThreshold", shadowThreshold);
keyFile.set_boolean("Clipping Indication", "BlinkClipped", blinkClipped);
diff --git a/rtgui/options.h b/rtgui/options.h
index 78059357e..6380cdb56 100644
--- a/rtgui/options.h
+++ b/rtgui/options.h
@@ -315,6 +315,9 @@ public:
std::vector parseExtensionsEnabled; // List of bool to retain extension or not
std::vector parsedExtensions; // List containing all retained extensions (lowercase)
std::set parsedExtensionsSet; // Set containing all retained extensions (lowercase)
+ bool browseRecursive;
+ int browseRecursiveDepth;
+ int browseRecursiveMaxDirs;
std::vector tpOpen;
bool autoSaveTpOpen;
//std::vector crvOpen;
From 660c9507cceb35c914af243ad476ce6cbf91f89f Mon Sep 17 00:00:00 2001
From: Lawrence Lee <45837045+Lawrence37@users.noreply.github.com>
Date: Sun, 11 Jun 2023 11:47:46 -0700
Subject: [PATCH 2/6] Add recursive browsing options to preferences
Allow maximum recursion depth and maximum sub-directories to be
configured in preferences.
---
rtdata/languages/default | 2 ++
rtgui/preferences.cc | 22 ++++++++++++++++++++++
rtgui/preferences.h | 2 ++
3 files changed, 26 insertions(+)
diff --git a/rtdata/languages/default b/rtdata/languages/default
index c23410661..0b581d05d 100644
--- a/rtdata/languages/default
+++ b/rtdata/languages/default
@@ -1824,6 +1824,8 @@ PREFERENCES_BEHADDALLHINT;Set all parameters to the Add mode.\nAdjustment
PREFERENCES_BEHAVIOR;Behavior
PREFERENCES_BEHSETALL;All to 'Set'
PREFERENCES_BEHSETALLHINT;Set all parameters to the Set mode.\nAdjustments of parameters in the batch tool panel will be absolute, the actual values will be displayed.
+PREFERENCES_BROWSERECURSIVEDEPTH;Browse sub-folders depth
+PREFERENCES_BROWSERECURSIVEMAXDIRS;Maximum sub-folders
PREFERENCES_CACHECLEAR;Clear
PREFERENCES_CACHECLEAR_ALL;Clear all cached files:
PREFERENCES_CACHECLEAR_ALLBUTPROFILES;Clear all cached files except for cached processing profiles:
diff --git a/rtgui/preferences.cc b/rtgui/preferences.cc
index c9737704e..025fbb3ae 100644
--- a/rtgui/preferences.cc
+++ b/rtgui/preferences.cc
@@ -1389,6 +1389,24 @@ Gtk::Widget* Preferences::getFileBrowserPanel()
maxRecentFolders->set_range(1, 25);
vbro->pack_start(*hbrecent, Gtk::PACK_SHRINK, 4);
+ // Recursive browsing options.
+ Gtk::Box *hbBrowseRecursive = Gtk::manage(new Gtk::Box());
+ Gtk::Label *labBrowseRecursiveDepth = Gtk::manage(new Gtk::Label(M("PREFERENCES_BROWSERECURSIVEDEPTH") + ":"));
+ browseRecursiveDepth = Gtk::manage(new Gtk::SpinButton());
+ browseRecursiveDepth->set_digits(0);
+ browseRecursiveDepth->set_increments(1, 5);
+ browseRecursiveDepth->set_range(1, 999);
+ Gtk::Label *labBrowseRecursiveMaxDirs = Gtk::manage(new Gtk::Label(M("PREFERENCES_BROWSERECURSIVEMAXDIRS") + ":"));
+ browseRecursiveMaxDirs = Gtk::manage(new Gtk::SpinButton());
+ browseRecursiveMaxDirs->set_digits(0);
+ browseRecursiveMaxDirs->set_increments(1, 5);
+ browseRecursiveMaxDirs->set_range(1, 999);
+ hbBrowseRecursive->pack_start(*labBrowseRecursiveDepth, Gtk::PACK_SHRINK, 4);
+ hbBrowseRecursive->pack_start(*browseRecursiveDepth, Gtk::PACK_SHRINK, 4);
+ hbBrowseRecursive->pack_start(*labBrowseRecursiveMaxDirs, Gtk::PACK_SHRINK, 4);
+ hbBrowseRecursive->pack_start(*browseRecursiveMaxDirs, Gtk::PACK_SHRINK, 4);
+ vbro->pack_start(*hbBrowseRecursive, Gtk::PACK_SHRINK, 0);
+
fro->add(*vbro);
@@ -1864,6 +1882,8 @@ void Preferences::storePreferences()
moptions.filmStripOverlayedFileNames = filmStripOverlayedFileNames->get_active();
moptions.sameThumbSize = sameThumbSize->get_active();
moptions.internalThumbIfUntouched = ckbInternalThumbIfUntouched->get_active();
+ moptions.browseRecursiveDepth = static_cast(browseRecursiveDepth->get_value());
+ moptions.browseRecursiveMaxDirs = static_cast(browseRecursiveMaxDirs->get_value());
auto save_where = saveParamsPreference->get_active_row_number();
moptions.saveParamsFile = save_where == 0 || save_where == 2;
@@ -2089,6 +2109,8 @@ void Preferences::fillPreferences()
filmStripOverlayedFileNames->set_active(moptions.filmStripOverlayedFileNames);
sameThumbSize->set_active(moptions.sameThumbSize);
ckbInternalThumbIfUntouched->set_active(moptions.internalThumbIfUntouched);
+ browseRecursiveDepth->set_value(moptions.browseRecursiveDepth);
+ browseRecursiveMaxDirs->set_value(moptions.browseRecursiveMaxDirs);
saveParamsPreference->set_active(moptions.saveParamsFile ? (moptions.saveParamsCache ? 2 : 0) : 1);
diff --git a/rtgui/preferences.h b/rtgui/preferences.h
index 6e31761a4..829506894 100644
--- a/rtgui/preferences.h
+++ b/rtgui/preferences.h
@@ -194,6 +194,8 @@ class Preferences final :
Gtk::CheckButton* overlayedFileNames;
Gtk::CheckButton* filmStripOverlayedFileNames;
Gtk::CheckButton* sameThumbSize;
+ Gtk::SpinButton* browseRecursiveDepth;
+ Gtk::SpinButton* browseRecursiveMaxDirs;
Gtk::SpinButton* threadsSpinBtn;
Gtk::SpinButton* clutCacheSizeSB;
From eef54f0f2d7dcc51dd6bd4a2b8787a836b858a31 Mon Sep 17 00:00:00 2001
From: Lawrence Lee <45837045+Lawrence37@users.noreply.github.com>
Date: Sun, 11 Jun 2023 17:22:40 -0700
Subject: [PATCH 3/6] Add option to disable symlinks in recursive browse
---
rtdata/languages/default | 1 +
rtgui/filecatalog.cc | 11 ++++++++++-
rtgui/options.cc | 6 ++++++
rtgui/options.h | 1 +
rtgui/preferences.cc | 4 ++++
rtgui/preferences.h | 1 +
6 files changed, 23 insertions(+), 1 deletion(-)
diff --git a/rtdata/languages/default b/rtdata/languages/default
index 0b581d05d..8362c1c15 100644
--- a/rtdata/languages/default
+++ b/rtdata/languages/default
@@ -1825,6 +1825,7 @@ PREFERENCES_BEHAVIOR;Behavior
PREFERENCES_BEHSETALL;All to 'Set'
PREFERENCES_BEHSETALLHINT;Set all parameters to the Set mode.\nAdjustments of parameters in the batch tool panel will be absolute, the actual values will be displayed.
PREFERENCES_BROWSERECURSIVEDEPTH;Browse sub-folders depth
+PREFERENCES_BROWSERECURSIVEFOLLOWLINKS;Follow symbolic links when browsing sub-folders
PREFERENCES_BROWSERECURSIVEMAXDIRS;Maximum sub-folders
PREFERENCES_CACHECLEAR;Clear
PREFERENCES_CACHECLEAR_ALL;Clear all cached files:
diff --git a/rtgui/filecatalog.cc b/rtgui/filecatalog.cc
index 5c20aa5fe..b01300d95 100644
--- a/rtgui/filecatalog.cc
+++ b/rtgui/filecatalog.cc
@@ -590,7 +590,16 @@ std::vector FileCatalog::getFileList(std::vectorenumerate_children("standard::name,standard::type,standard::is-hidden");
+ static const auto enumerate_attrs =
+ std::string(G_FILE_ATTRIBUTE_STANDARD_NAME) + "," +
+ G_FILE_ATTRIBUTE_STANDARD_TYPE + "," +
+ G_FILE_ATTRIBUTE_STANDARD_IS_HIDDEN + "," +
+ G_FILE_ATTRIBUTE_STANDARD_SYMLINK_TARGET;
+ auto enumerator = dir->enumerate_children(
+ enumerate_attrs,
+ options.browseRecursiveFollowLinks
+ ? Gio::FileQueryInfoFlags::FILE_QUERY_INFO_NONE
+ : Gio::FileQueryInfoFlags::FILE_QUERY_INFO_NOFOLLOW_SYMLINKS);
if (directories_explored) {
directories_explored->push_back(dir);
diff --git a/rtgui/options.cc b/rtgui/options.cc
index ed6f03eb7..f8143996a 100644
--- a/rtgui/options.cc
+++ b/rtgui/options.cc
@@ -438,6 +438,7 @@ void Options::setDefaults()
browseRecursive = false;
browseRecursiveDepth = 10;
browseRecursiveMaxDirs = 100;
+ browseRecursiveFollowLinks = true;
renameUseTemplates = false;
renameTemplates.clear();
thumbnailZoomRatios.clear();
@@ -1357,6 +1358,10 @@ void Options::readFromFile(Glib::ustring fname)
if (keyFile.has_key("File Browser", "BrowseRecursiveMaxDirs")) {
browseRecursiveMaxDirs = keyFile.get_integer("File Browser", "BrowseRecursiveMaxDirs");
}
+
+ if (keyFile.has_key("File Browser", "BrowseRecursiveFollowLinks")) {
+ browseRecursiveFollowLinks = keyFile.get_integer("File Browser", "BrowseRecursiveFollowLinks");
+ }
}
if (keyFile.has_group("Clipping Indication")) {
@@ -2428,6 +2433,7 @@ void Options::saveToFile(Glib::ustring fname)
keyFile.set_boolean("File Browser", "BrowseRecursive", browseRecursive);
keyFile.set_integer("File Browser", "BrowseRecursiveDepth", browseRecursiveDepth);
keyFile.set_integer("File Browser", "BrowseRecursiveMaxDirs", browseRecursiveMaxDirs);
+ keyFile.set_boolean("File Browser", "BrowseRecursiveFollowLinks", browseRecursiveFollowLinks);
keyFile.set_integer("Clipping Indication", "HighlightThreshold", highlightThreshold);
keyFile.set_integer("Clipping Indication", "ShadowThreshold", shadowThreshold);
keyFile.set_boolean("Clipping Indication", "BlinkClipped", blinkClipped);
diff --git a/rtgui/options.h b/rtgui/options.h
index 6380cdb56..2e2839723 100644
--- a/rtgui/options.h
+++ b/rtgui/options.h
@@ -318,6 +318,7 @@ public:
bool browseRecursive;
int browseRecursiveDepth;
int browseRecursiveMaxDirs;
+ bool browseRecursiveFollowLinks;
std::vector tpOpen;
bool autoSaveTpOpen;
//std::vector crvOpen;
diff --git a/rtgui/preferences.cc b/rtgui/preferences.cc
index 025fbb3ae..1838ee299 100644
--- a/rtgui/preferences.cc
+++ b/rtgui/preferences.cc
@@ -1405,7 +1405,9 @@ Gtk::Widget* Preferences::getFileBrowserPanel()
hbBrowseRecursive->pack_start(*browseRecursiveDepth, Gtk::PACK_SHRINK, 4);
hbBrowseRecursive->pack_start(*labBrowseRecursiveMaxDirs, Gtk::PACK_SHRINK, 4);
hbBrowseRecursive->pack_start(*browseRecursiveMaxDirs, Gtk::PACK_SHRINK, 4);
+ browseRecursiveFollowLinks = Gtk::manage(new Gtk::CheckButton(M("PREFERENCES_BROWSERECURSIVEFOLLOWLINKS")));
vbro->pack_start(*hbBrowseRecursive, Gtk::PACK_SHRINK, 0);
+ vbro->pack_start(*browseRecursiveFollowLinks, Gtk::PACK_SHRINK, 0);
fro->add(*vbro);
@@ -1884,6 +1886,7 @@ void Preferences::storePreferences()
moptions.internalThumbIfUntouched = ckbInternalThumbIfUntouched->get_active();
moptions.browseRecursiveDepth = static_cast(browseRecursiveDepth->get_value());
moptions.browseRecursiveMaxDirs = static_cast(browseRecursiveMaxDirs->get_value());
+ moptions.browseRecursiveFollowLinks = browseRecursiveFollowLinks->get_active();
auto save_where = saveParamsPreference->get_active_row_number();
moptions.saveParamsFile = save_where == 0 || save_where == 2;
@@ -2111,6 +2114,7 @@ void Preferences::fillPreferences()
ckbInternalThumbIfUntouched->set_active(moptions.internalThumbIfUntouched);
browseRecursiveDepth->set_value(moptions.browseRecursiveDepth);
browseRecursiveMaxDirs->set_value(moptions.browseRecursiveMaxDirs);
+ browseRecursiveFollowLinks->set_active(moptions.browseRecursiveFollowLinks);
saveParamsPreference->set_active(moptions.saveParamsFile ? (moptions.saveParamsCache ? 2 : 0) : 1);
diff --git a/rtgui/preferences.h b/rtgui/preferences.h
index 829506894..c15ea71e8 100644
--- a/rtgui/preferences.h
+++ b/rtgui/preferences.h
@@ -196,6 +196,7 @@ class Preferences final :
Gtk::CheckButton* sameThumbSize;
Gtk::SpinButton* browseRecursiveDepth;
Gtk::SpinButton* browseRecursiveMaxDirs;
+ Gtk::CheckButton* browseRecursiveFollowLinks;
Gtk::SpinButton* threadsSpinBtn;
Gtk::SpinButton* clutCacheSizeSB;
From 051bbaf322543257bda3bc56ecba3e3a873b0300 Mon Sep 17 00:00:00 2001
From: Lawrence Lee <45837045+Lawrence37@users.noreply.github.com>
Date: Sun, 11 Jun 2023 18:28:16 -0700
Subject: [PATCH 4/6] Listen to changed dirs when browsing recursively
Remove images in removed directories and rescan when new directories are
added.
---
rtgui/filecatalog.cc | 8 +++++---
1 file changed, 5 insertions(+), 3 deletions(-)
diff --git a/rtgui/filecatalog.cc b/rtgui/filecatalog.cc
index b01300d95..058236733 100644
--- a/rtgui/filecatalog.cc
+++ b/rtgui/filecatalog.cc
@@ -1783,7 +1783,7 @@ void FileCatalog::reparseDirectory ()
return;
}
- // check if a thumbnailed file has been deleted
+ // check if a thumbnailed file has been deleted or is not in a directory of interest
const std::vector& t = fileBrowser->getEntries();
std::vector fileNamesToDel;
std::vector fileNamesToRemove;
@@ -1832,8 +1832,10 @@ void FileCatalog::reparseDirectory ()
void FileCatalog::on_dir_changed (const Glib::RefPtr& file, const Glib::RefPtr& other_file, Gio::FileMonitorEvent event_type, bool internal)
{
- if (options.has_retained_extention(file->get_parse_name())
- && (event_type == Gio::FILE_MONITOR_EVENT_CREATED || event_type == Gio::FILE_MONITOR_EVENT_DELETED || event_type == Gio::FILE_MONITOR_EVENT_CHANGED)) {
+ if ((options.has_retained_extention(file->get_parse_name())
+ && (event_type == Gio::FILE_MONITOR_EVENT_CREATED || event_type == Gio::FILE_MONITOR_EVENT_DELETED || event_type == Gio::FILE_MONITOR_EVENT_CHANGED))
+ || (event_type == Gio::FILE_MONITOR_EVENT_CREATED && Glib::file_test(file->get_path(), Glib::FileTest::FILE_TEST_IS_DIR))
+ || (event_type == Gio::FILE_MONITOR_EVENT_DELETED && std::find_if(dirMonitors.cbegin(), dirMonitors.cend(), [&file](const FileMonitorInfo &monitor) { return monitor.filePath == file->get_path(); }) != dirMonitors.cend())) {
if (!internal) {
GThreadLock lock;
reparseDirectory ();
From b6f3ea421e3e5c826f40443cc088342073d86eca Mon Sep 17 00:00:00 2001
From: Lawrence Lee <45837045+Lawrence37@users.noreply.github.com>
Date: Tue, 17 Oct 2023 21:20:18 -0700
Subject: [PATCH 5/6] Remove recursive symlink browsing for Windows
Windows doesn't have symbolic links, so the preference should not be
shown.
---
rtgui/preferences.cc | 12 +++++++++---
rtgui/preferences.h | 2 +-
2 files changed, 10 insertions(+), 4 deletions(-)
diff --git a/rtgui/preferences.cc b/rtgui/preferences.cc
index 1838ee299..07f6e5fd6 100644
--- a/rtgui/preferences.cc
+++ b/rtgui/preferences.cc
@@ -1405,9 +1405,11 @@ Gtk::Widget* Preferences::getFileBrowserPanel()
hbBrowseRecursive->pack_start(*browseRecursiveDepth, Gtk::PACK_SHRINK, 4);
hbBrowseRecursive->pack_start(*labBrowseRecursiveMaxDirs, Gtk::PACK_SHRINK, 4);
hbBrowseRecursive->pack_start(*browseRecursiveMaxDirs, Gtk::PACK_SHRINK, 4);
- browseRecursiveFollowLinks = Gtk::manage(new Gtk::CheckButton(M("PREFERENCES_BROWSERECURSIVEFOLLOWLINKS")));
vbro->pack_start(*hbBrowseRecursive, Gtk::PACK_SHRINK, 0);
+#ifndef _WIN32
+ browseRecursiveFollowLinks = Gtk::manage(new Gtk::CheckButton(M("PREFERENCES_BROWSERECURSIVEFOLLOWLINKS")));
vbro->pack_start(*browseRecursiveFollowLinks, Gtk::PACK_SHRINK, 0);
+#endif
fro->add(*vbro);
@@ -1886,7 +1888,9 @@ void Preferences::storePreferences()
moptions.internalThumbIfUntouched = ckbInternalThumbIfUntouched->get_active();
moptions.browseRecursiveDepth = static_cast(browseRecursiveDepth->get_value());
moptions.browseRecursiveMaxDirs = static_cast(browseRecursiveMaxDirs->get_value());
- moptions.browseRecursiveFollowLinks = browseRecursiveFollowLinks->get_active();
+ if (browseRecursiveFollowLinks) {
+ moptions.browseRecursiveFollowLinks = browseRecursiveFollowLinks->get_active();
+ }
auto save_where = saveParamsPreference->get_active_row_number();
moptions.saveParamsFile = save_where == 0 || save_where == 2;
@@ -2114,7 +2118,9 @@ void Preferences::fillPreferences()
ckbInternalThumbIfUntouched->set_active(moptions.internalThumbIfUntouched);
browseRecursiveDepth->set_value(moptions.browseRecursiveDepth);
browseRecursiveMaxDirs->set_value(moptions.browseRecursiveMaxDirs);
- browseRecursiveFollowLinks->set_active(moptions.browseRecursiveFollowLinks);
+ if (browseRecursiveFollowLinks) {
+ browseRecursiveFollowLinks->set_active(moptions.browseRecursiveFollowLinks);
+ }
saveParamsPreference->set_active(moptions.saveParamsFile ? (moptions.saveParamsCache ? 2 : 0) : 1);
diff --git a/rtgui/preferences.h b/rtgui/preferences.h
index c15ea71e8..8ee67bf69 100644
--- a/rtgui/preferences.h
+++ b/rtgui/preferences.h
@@ -196,7 +196,7 @@ class Preferences final :
Gtk::CheckButton* sameThumbSize;
Gtk::SpinButton* browseRecursiveDepth;
Gtk::SpinButton* browseRecursiveMaxDirs;
- Gtk::CheckButton* browseRecursiveFollowLinks;
+ Gtk::CheckButton* browseRecursiveFollowLinks{nullptr};
Gtk::SpinButton* threadsSpinBtn;
Gtk::SpinButton* clutCacheSizeSB;
From 82dce5fd47960324eca6a1367dd37c7d1b753b54 Mon Sep 17 00:00:00 2001
From: Lawrence Lee <45837045+Lawrence37@users.noreply.github.com>
Date: Sat, 9 Mar 2024 12:47:52 -0800
Subject: [PATCH 6/6] Adapt to real hi-DPI update
Change recursive folder browsing button icon color and size.
---
.../icons/rawtherapee/scalable/apps/folder-subfolder.svg | 8 ++++----
rtgui/filecatalog.cc | 2 +-
2 files changed, 5 insertions(+), 5 deletions(-)
diff --git a/rtdata/icons/rawtherapee/scalable/apps/folder-subfolder.svg b/rtdata/icons/rawtherapee/scalable/apps/folder-subfolder.svg
index f22d0da0f..eb858b543 100644
--- a/rtdata/icons/rawtherapee/scalable/apps/folder-subfolder.svg
+++ b/rtdata/icons/rawtherapee/scalable/apps/folder-subfolder.svg
@@ -71,13 +71,13 @@
inkscape:groupmode="layer"
inkscape:label="Layer 1">
diff --git a/rtgui/filecatalog.cc b/rtgui/filecatalog.cc
index 2bd16edf5..69b9e6dc9 100644
--- a/rtgui/filecatalog.cc
+++ b/rtgui/filecatalog.cc
@@ -350,7 +350,7 @@ FileCatalog::FileCatalog (CoarsePanel* cp, ToolBar* tb, FilePanel* filepanel) :
bOriginal->signal_button_press_event().connect (sigc::mem_fun(*this, &FileCatalog::capture_event), false);
bRecursive = Gtk::manage(new Gtk::ToggleButton());
- bRecursive->set_image(*Gtk::manage(new RTImage("folder-subfolder.png")));
+ bRecursive->set_image(*Gtk::manage(new RTImage("folder-subfolder", Gtk::ICON_SIZE_LARGE_TOOLBAR)));
bRecursive->set_tooltip_text(M("FILEBROWSER_SHOWRECURSIVE"));
bRecursive->set_relief(Gtk::RELIEF_NONE);
bRecursive->set_active(options.browseRecursive);