Fix 40+ bugs from audit passes 9-12
- PNG chunk parsing overflow protection with checked arithmetic - Font directory traversal bounded with global result limit - find_unique_path TOCTOU race fixed with create_new + marker byte - Watch mode "processed" dir exclusion narrowed to prevent false skips - Metadata copy now checks format support before little_exif calls - Clipboard temp files cleaned up on app exit - Atomic writes for file manager integration scripts - BMP format support added to encoder and convert step - Regex DoS protection with DFA size limit - Watermark NaN/negative scale guard - Selective EXIF stripping for privacy/custom metadata modes - CLI watch mode: file stability checks, per-file history saves - High contrast toggle preserves and restores original theme - Image list deduplication uses O(1) HashSet lookups - Saturation/trim/padding overflow guards in adjustments
This commit is contained in:
@@ -199,12 +199,21 @@ fn is_image_file(path: &std::path::Path) -> bool {
|
||||
}
|
||||
|
||||
fn add_images_from_dir(dir: &std::path::Path, files: &mut Vec<PathBuf>) {
|
||||
let existing: std::collections::HashSet<PathBuf> = files.iter().cloned().collect();
|
||||
add_images_from_dir_inner(dir, files, &existing);
|
||||
}
|
||||
|
||||
fn add_images_from_dir_inner(
|
||||
dir: &std::path::Path,
|
||||
files: &mut Vec<PathBuf>,
|
||||
existing: &std::collections::HashSet<PathBuf>,
|
||||
) {
|
||||
if let Ok(entries) = std::fs::read_dir(dir) {
|
||||
for entry in entries.flatten() {
|
||||
let path = entry.path();
|
||||
if path.is_dir() {
|
||||
add_images_from_dir(&path, files);
|
||||
} else if is_image_file(&path) && !files.contains(&path) {
|
||||
add_images_from_dir_inner(&path, files, existing);
|
||||
} else if is_image_file(&path) && !existing.contains(&path) && !files.contains(&path) {
|
||||
files.push(path);
|
||||
}
|
||||
}
|
||||
@@ -212,10 +221,11 @@ fn add_images_from_dir(dir: &std::path::Path, files: &mut Vec<PathBuf>) {
|
||||
}
|
||||
|
||||
fn add_images_flat(dir: &std::path::Path, files: &mut Vec<PathBuf>) {
|
||||
let existing: std::collections::HashSet<PathBuf> = files.iter().cloned().collect();
|
||||
if let Ok(entries) = std::fs::read_dir(dir) {
|
||||
for entry in entries.flatten() {
|
||||
let path = entry.path();
|
||||
if path.is_file() && is_image_file(&path) && !files.contains(&path) {
|
||||
if path.is_file() && is_image_file(&path) && !existing.contains(&path) {
|
||||
files.push(path);
|
||||
}
|
||||
}
|
||||
@@ -548,20 +558,32 @@ fn build_loaded_state(state: &AppState) -> gtk::Box {
|
||||
let select_all_button = gtk::Button::builder()
|
||||
.icon_name("edit-select-all-symbolic")
|
||||
.tooltip_text("Select all images (Ctrl+A)")
|
||||
.sensitive(false)
|
||||
.build();
|
||||
select_all_button.add_css_class("flat");
|
||||
select_all_button.update_property(&[
|
||||
gtk::accessible::Property::Label("Select all images for processing"),
|
||||
]);
|
||||
|
||||
let deselect_all_button = gtk::Button::builder()
|
||||
.icon_name("edit-clear-symbolic")
|
||||
.tooltip_text("Deselect all images (Ctrl+Shift+A)")
|
||||
.sensitive(false)
|
||||
.build();
|
||||
deselect_all_button.add_css_class("flat");
|
||||
deselect_all_button.update_property(&[
|
||||
gtk::accessible::Property::Label("Deselect all images from processing"),
|
||||
]);
|
||||
|
||||
let clear_button = gtk::Button::builder()
|
||||
.icon_name("edit-clear-all-symbolic")
|
||||
.tooltip_text("Remove all images")
|
||||
.sensitive(false)
|
||||
.build();
|
||||
clear_button.add_css_class("flat");
|
||||
clear_button.update_property(&[
|
||||
gtk::accessible::Property::Label("Remove all images from list"),
|
||||
]);
|
||||
|
||||
// Build the grid view model
|
||||
let store = gtk::gio::ListStore::new::<ImageItem>();
|
||||
@@ -835,6 +857,19 @@ fn build_loaded_state(state: &AppState) -> gtk::Box {
|
||||
toolbar.append(&add_button);
|
||||
toolbar.append(&clear_button);
|
||||
|
||||
// Enable/disable toolbar buttons based on whether the store has items
|
||||
{
|
||||
let sa = select_all_button.clone();
|
||||
let da = deselect_all_button.clone();
|
||||
let cl = clear_button.clone();
|
||||
store.connect_items_changed(move |store, _, _, _| {
|
||||
let has_items = store.n_items() > 0;
|
||||
sa.set_sensitive(has_items);
|
||||
da.set_sensitive(has_items);
|
||||
cl.set_sensitive(has_items);
|
||||
});
|
||||
}
|
||||
|
||||
toolbar.update_property(&[
|
||||
gtk::accessible::Property::Label("Image toolbar with count, selection, and add controls"),
|
||||
]);
|
||||
|
||||
Reference in New Issue
Block a user