diff --git a/rtgui/filmsimulation.cc b/rtgui/filmsimulation.cc index e1cbd4966..feed0ba35 100644 --- a/rtgui/filmsimulation.cc +++ b/rtgui/filmsimulation.cc @@ -148,7 +148,7 @@ int ClutComboBox::fillFromDir (const Glib::ustring& path) m_model = Gtk::TreeStore::create (m_columns); set_model (m_model); - const auto result = parseDir (path, nullptr); + const auto result = parseDir (path); if (result > 0) { pack_start (m_columns.label, false); @@ -157,68 +157,113 @@ int ClutComboBox::fillFromDir (const Glib::ustring& path) return result; } -Gtk::TreeIter appendToModel( Glib::RefPtr model, Gtk::TreeModel::Row *parent ) +int ClutComboBox::parseDir (const Glib::ustring& path) { - Gtk::TreeIter result; - - if ( parent ) { - result = model->append( parent->children() ); - - } else { - result = model->append(); + if (path.empty () || !Glib::file_test (path, Glib::FILE_TEST_IS_DIR)) { + return 0; } - return result; -} + // Build menu of limited directory structure using breadth-first search + using Dirs = std::vector>; + Dirs dirs; -int ClutComboBox::parseDir( Glib::ustring path, Gtk::TreeModel::Row *parentRow ) -{ - int result = 0; + { + Dirs currDirs; + Dirs nextDirs; - if ( path.empty() || !safe_file_test( path, Glib::FILE_TEST_EXISTS ) || !safe_file_test ( path, Glib::FILE_TEST_IS_DIR ) ) { - return result; - } + constexpr auto maxDirCount = 128, maxDirDepth = 4; + auto dirCount = 0, dirDepth = 0; - Glib::Dir* dir = new Glib::Dir( path ); + currDirs.emplace_back (path, Gtk::TreeModel::Row ()); - Strings names; + while (!currDirs.empty ()) { - for( Glib::DirIterator it = dir->begin(); it != dir->end(); ++it ) { - Glib::ustring current = *it; + for (auto& dir : currDirs) { - if ( current != "." && current != ".." ) { - names.push_back( current ); - } - } + const auto& path = dir.first; + const auto& row = dir.second; - std::sort( names.begin(), names.end() ); + try { + for (const auto& entry : Glib::Dir (path)) { - for ( Strings::iterator it = names.begin(); it != names.end(); ++it ) { - Glib::ustring current = *it; - Glib::ustring fullname = Glib::build_filename( path, current ); + const auto entryPath = Glib::build_filename (path, entry); - if ( safe_file_test( fullname, Glib::FILE_TEST_IS_DIR ) ) { + if (!Glib::file_test (entryPath, Glib::FILE_TEST_IS_DIR)) { + continue; + } - Gtk::TreeModel::Row newFolderMenu = *appendToModel( m_model, parentRow ); - newFolderMenu[ m_columns.label ] = current; - result += parseDir( fullname, &newFolderMenu ); - } else { - Glib::ustring name, extension, profileName; - splitClutFilename( current, name, extension, profileName ); + auto newRow = row ? *m_model->append (row.children ()) : *m_model->append (); + newRow[m_columns.label] = entry; - if ( extension == "tif" || - extension == "TIF" || - extension == "png" || - extension == "PNG" ) { - Gtk::TreeModel::Row newClut = *appendToModel( m_model, parentRow ); - newClut[ m_columns.label ] = name; - newClut[ m_columns.clutFilename ] = fullname; - ++result; + nextDirs.emplace_back (entryPath, newRow); + } + } catch (Glib::Exception&) {} + + dirs.push_back (std::move (dir)); + if (++dirCount > maxDirCount) { + m_model->clear (); + return 0; + } + } + + currDirs.clear (); + currDirs.swap (nextDirs); + if (++dirDepth > maxDirDepth) { + m_model->clear (); + return 0; } } } - return result; + // Fill menu structure with CLUT files + Strings entries; + + constexpr auto maxFileCount = 4096; + auto fileCount = 0; + + for (const auto& dir : dirs) { + + const auto& path = dir.first; + const auto& row = dir.second; + + entries.clear (); + + try { + for (const auto& entry : Glib::Dir (path)) { + entries.push_back (entry); + } + } catch (Glib::Exception&) {} + + std::sort (entries.begin (), entries.end ()); + + for (const auto& entry : entries) { + + const auto entryPath = Glib::build_filename (path, entry); + + if (!Glib::file_test (entryPath, Glib::FILE_TEST_IS_REGULAR)) { + continue; + } + + Glib::ustring name, extension, profileName; + splitClutFilename (entry, name, extension, profileName); + + extension = extension.casefold (); + if (extension.compare ("tif") == 0 && extension.compare ("png") == 0) { + continue; + } + + auto newRow = row ? *m_model->append (row.children ()) : *m_model->append (); + newRow[m_columns.label] = name; + newRow[m_columns.clutFilename] = entryPath; + + if (++fileCount > maxFileCount) { + m_model->clear (); + return 0; + } + } + } + + return fileCount; } Glib::ustring ClutComboBox::getSelectedClut() diff --git a/rtgui/filmsimulation.h b/rtgui/filmsimulation.h index a463bdf60..440bf0c9e 100644 --- a/rtgui/filmsimulation.h +++ b/rtgui/filmsimulation.h @@ -25,7 +25,7 @@ private: ClutColumns(); }; - int parseDir( Glib::ustring path, Gtk::TreeModel::Row *parentRow ); + int parseDir (const Glib::ustring& path); Gtk::TreeIter findRowByClutFilename( Gtk::TreeModel::Children childs, Glib::ustring filename ); Glib::RefPtr m_model;