Kaynağa Gözat

Bumping to 0.2

Nicole Portas 1 ay önce
ebeveyn
işleme
2d23c99dd3
5 değiştirilmiş dosya ile 207 ekleme ve 235 silme
  1. 0 57
      Makefile
  2. 23 16
      README.md
  3. BIN
      simple_image_renamer.deb
  4. 184 162
      src/main.cpp
  5. BIN
      visual-renamer

+ 0 - 57
Makefile

@@ -1,57 +0,0 @@
-# Load configuration from ./configure
-include config.mk
-
-TARGET = visual-renamer
-SRC = src/main.cpp
-OBJ = $(SRC:.cpp=.o)
-
-# Default target
-all: check_config $(TARGET)
-
-# Check if config.mk exists
-check_config:
-	@if [ ! -f config.mk ]; then \
-		echo "Error: config.mk not found. Please run ./configure first."; \
-		exit 1; \
-	fi
-
-# Link the executable
-$(TARGET): $(OBJ)
-	@echo "Linking $(TARGET)..."
-	@$(CXX) $(OBJ) -o $(TARGET) $(LIBS)
-	@echo "Build complete."
-
-# Compile source files
-.cpp.o:
-	@echo "Compiling $<..."
-	@$(CXX) $(CXXFLAGS) -c $< -o $@
-
-# Install to system
-install: all
-	@echo "Installing to $(PREFIX)/bin..."
-	@mkdir -p $(PREFIX)/bin
-	@cp $(TARGET) $(PREFIX)/bin/
-	@chmod 755 $(PREFIX)/bin/$(TARGET)
-	@echo "creating desktop entry..."
-	@mkdir -p $(HOME)/.local/share/applications
-	@echo "[Desktop Entry]" > $(HOME)/.local/share/applications/visual-renamer.desktop
-	@echo "Type=Application" >> $(HOME)/.local/share/applications/visual-renamer.desktop
-	@echo "Name=Visual Renamer" >> $(HOME)/.local/share/applications/visual-renamer.desktop
-	@echo "Exec=$(PREFIX)/bin/$(TARGET)" >> $(HOME)/.local/share/applications/visual-renamer.desktop
-	@echo "Icon=system-file-manager" >> $(HOME)/.local/share/applications/visual-renamer.desktop
-	@echo "Categories=Utility;" >> $(HOME)/.local/share/applications/visual-renamer.desktop
-	@echo "Installation successful!"
-
-# Uninstall
-uninstall:
-	@echo "Removing $(PREFIX)/bin/$(TARGET)..."
-	@rm -f $(PREFIX)/bin/$(TARGET)
-	@rm -f $(HOME)/.local/share/applications/visual-renamer.desktop
-	@echo "Uninstall complete."
-
-# Clean build files
-clean:
-	@echo "Cleaning up..."
-	@rm -f $(OBJ) $(TARGET) config.mk
-
-.PHONY: all check_config install uninstall clean

+ 23 - 16
README.md

@@ -1,26 +1,33 @@
-# Simple Image Renamer 0.1
+# Simple Image Renamer 0.2
 
 
-**A safe, visual, and fast batch image renamer for Linux.**
+A lightweight, visual batch renaming tool built with **C++** and **gtkmm-3.0**. Designed for users who need to organize photo collections quickly with real-time feedback and a responsive interface.
 
 
-Simple Image Renamer is a lightweight C++ application designed to make organizing photos painless. Unlike command-line scripts, it provides a full visual interface with thumbnails, live previews of new filenames, and a robust undo system.
-
-![App Icon](icon.png)
+![Application Screenshot](screenshot.jpg)
 
 
 ## Key Features
 ## Key Features
 
 
-* **Live Preview:** See exactly what your files will look like (in blue text) *before* you rename them.
-* **Undo Support:** Made a mistake? Hit the Undo button to instantly revert changes.
-* **Pattern Renaming:** Easily serialize files (e.g., `vacation_001.jpg`, `vacation_002.jpg`) using `###` placeholders.
-* **Search & Replace:** Switch modes to fix typos or change specific words across multiple files.
-* **Selective Renaming:** Use checkboxes to rename only specific files in a folder.
-* **Smart Sorting:** Sort by Natural Name (1, 2, 10) or Date Modified (keep your vacation photos in order!).
-* **Visual Thumbnails:** Adjustable thumbnail sizes (Tiny, Normal, Huge).
+* **Responsive Grid View**: Thumbnails automatically reflow based on window size.
+* **Dual Renaming Modes**:
+    * **Sequential Numbering**: Use patterns (e.g., `vacation_###`) with adjustable padding and start indices.
+    * **Find & Replace**: Quickly swap specific text strings across multiple filenames.
+* **Intelligent Ordering**:
+    * Natural Sort (e.g., `2.jpg` comes before `10.jpg`).
+    * Chronological sorting (Oldest/Newest).
+    * **Manual Mode**: Full drag-and-drop support to define a custom rename sequence.
+* **Rich Metadata Display**: Each thumbnail shows the file format, original resolution, file size, and timestamp in `HH:mm dd-MMM-yyyy` format.
+* **Interactive Frames**: Thumbnails are housed in high-contrast frames that adapt to your system's light or dark theme.
+* **Safety Features**: Instant **Undo** for the last rename operation and a "Reload/Stop" toggle for folder processing.
+
+---
+
+## Requirements
 
 
-## Prerequisites
+To build on **Debian** or similar distributions, you need:
 
 
-This application is built with **C++17** and **gtkmm-3.0**.
+* `g++` (C++17 support)
+* `libgtkmm-3.0-dev`
+* `pkg-config`
 
 
-### Debian / Ubuntu / Mint
 ```bash
 ```bash
 sudo apt update
 sudo apt update
-sudo apt install build-essential libgtkmm-3.0-dev
+sudo apt install build-essential libgtkmm-3.0-dev pkg-config

BIN
simple_image_renamer.deb


+ 184 - 162
src/main.cpp

@@ -47,34 +47,24 @@ public:
         property_xpad() = 10;
         property_xpad() = 10;
         property_ypad() = 10;
         property_ypad() = 10;
     }
     }
-
 protected:
 protected:
     void render_vfunc(const Cairo::RefPtr<Cairo::Context>& cr,
     void render_vfunc(const Cairo::RefPtr<Cairo::Context>& cr,
                       Gtk::Widget& widget,
                       Gtk::Widget& widget,
                       const Gdk::Rectangle& background_area,
                       const Gdk::Rectangle& background_area,
                       const Gdk::Rectangle& cell_area,
                       const Gdk::Rectangle& cell_area,
                       Gtk::CellRendererState flags) override {
                       Gtk::CellRendererState flags) override {
-        
-        // 1. Draw the Frame Border
         cr->save();
         cr->save();
-        
-        // Get background color to determine "opposite" color
         auto style = widget.get_style_context();
         auto style = widget.get_style_context();
         Gdk::RGBA bg = style->get_background_color();
         Gdk::RGBA bg = style->get_background_color();
-        
-        // If background is light, use dark grey. If dark, use light grey.
         double brightness = (bg.get_red() + bg.get_green() + bg.get_blue()) / 3.0;
         double brightness = (bg.get_red() + bg.get_green() + bg.get_blue()) / 3.0;
-        if (brightness > 0.5) cr->set_source_rgb(0.5, 0.5, 0.5); // Medium Grey
-        else cr->set_source_rgb(0.8, 0.8, 0.8); // Light Grey for dark themes
+        if (brightness > 0.5) cr->set_source_rgb(0.5, 0.5, 0.5); 
+        else cr->set_source_rgb(0.8, 0.8, 0.8);
         
         
         cr->set_line_width(2.0);
         cr->set_line_width(2.0);
-        // Draw rectangle with a slight margin inside the cell area
         cr->rectangle(cell_area.get_x() + 2, cell_area.get_y() + 2, 
         cr->rectangle(cell_area.get_x() + 2, cell_area.get_y() + 2, 
                       cell_area.get_width() - 4, cell_area.get_height() - 4);
                       cell_area.get_width() - 4, cell_area.get_height() - 4);
         cr->stroke();
         cr->stroke();
         cr->restore();
         cr->restore();
-
-        // 2. Call base class to draw the actual Pixbuf inside our frame
         Gtk::CellRendererPixbuf::render_vfunc(cr, widget, background_area, cell_area, flags);
         Gtk::CellRendererPixbuf::render_vfunc(cr, widget, background_area, cell_area, flags);
     }
     }
 };
 };
@@ -88,20 +78,25 @@ struct LoadedItem {
     Glib::RefPtr<Gdk::Pixbuf> pixbuf;
     Glib::RefPtr<Gdk::Pixbuf> pixbuf;
     int orig_w = 0, orig_h = 0;
     int orig_w = 0, orig_h = 0;
     uintmax_t filesize = 0;
     uintmax_t filesize = 0;
+    bool is_update = false;
+    Gtk::TreeModel::Path model_path; 
 };
 };
 
 
 class RenamerWindow : public Gtk::Window {
 class RenamerWindow : public Gtk::Window {
 public:
 public:
     RenamerWindow() : m_Dispatcher() {
     RenamerWindow() : m_Dispatcher() {
-        set_title("Simple Image Renamer 0.1");
+        set_title("Simple Image Renamer 0.2");
         set_default_size(1280, 850);
         set_default_size(1280, 850);
         
         
+        // --- ADDED WMCLASS ---
+        set_wmclass("simpleimagerenamer", "simpleimagerenamer");
+
         m_Dispatcher.connect(sigc::mem_fun(*this, &RenamerWindow::on_worker_notification));
         m_Dispatcher.connect(sigc::mem_fun(*this, &RenamerWindow::on_worker_notification));
 
 
         m_VBox.set_orientation(Gtk::ORIENTATION_VERTICAL);
         m_VBox.set_orientation(Gtk::ORIENTATION_VERTICAL);
         add(m_VBox);
         add(m_VBox);
 
 
-        // Toolbar
+        // --- Toolbar Top ---
         m_ToolbarTop.set_margin_top(5); m_ToolbarTop.set_margin_bottom(5);
         m_ToolbarTop.set_margin_top(5); m_ToolbarTop.set_margin_bottom(5);
         m_ToolbarTop.set_margin_left(10); m_ToolbarTop.set_margin_right(10);
         m_ToolbarTop.set_margin_left(10); m_ToolbarTop.set_margin_right(10);
         m_VBox.pack_start(m_ToolbarTop, Gtk::PACK_SHRINK);
         m_VBox.pack_start(m_ToolbarTop, Gtk::PACK_SHRINK);
@@ -110,59 +105,68 @@ public:
         m_BtnOpen.signal_clicked().connect(sigc::mem_fun(*this, &RenamerWindow::on_open_folder_clicked));
         m_BtnOpen.signal_clicked().connect(sigc::mem_fun(*this, &RenamerWindow::on_open_folder_clicked));
         m_ToolbarTop.pack_start(m_BtnOpen, Gtk::PACK_SHRINK, 5);
         m_ToolbarTop.pack_start(m_BtnOpen, Gtk::PACK_SHRINK, 5);
 
 
-        m_BtnStop.set_label("Stop");
-        m_BtnStop.set_sensitive(false);
-        m_BtnStop.signal_clicked().connect([this](){ m_stop_flag = true; m_BtnStop.set_sensitive(false); });
-        m_ToolbarTop.pack_start(m_BtnStop, Gtk::PACK_SHRINK, 5);
+        m_BtnStopReload.set_label("Reload Folder");
+        m_BtnStopReload.set_sensitive(false);
+        m_BtnStopReload.signal_clicked().connect(sigc::mem_fun(*this, &RenamerWindow::on_stop_reload_clicked));
+        m_ToolbarTop.pack_start(m_BtnStopReload, Gtk::PACK_SHRINK, 5);
 
 
-        m_ComboSort.append("Name"); m_ComboSort.append("Oldest"); m_ComboSort.append("Newest"); m_ComboSort.append("Manual"); 
+        m_ToolbarTop.pack_start(*Gtk::manage(new Gtk::Label("  Sort By: ")), Gtk::PACK_SHRINK);
+        m_ComboSort.append("Name (Natural)"); m_ComboSort.append("Oldest First"); m_ComboSort.append("Newest First"); m_ComboSort.append("Manual (Drag & Drop)"); 
         m_ComboSort.set_active(0);
         m_ComboSort.set_active(0);
         m_ComboSort.signal_changed().connect(sigc::mem_fun(*this, &RenamerWindow::on_sort_changed));
         m_ComboSort.signal_changed().connect(sigc::mem_fun(*this, &RenamerWindow::on_sort_changed));
         m_ToolbarTop.pack_start(m_ComboSort, Gtk::PACK_SHRINK, 5);
         m_ToolbarTop.pack_start(m_ComboSort, Gtk::PACK_SHRINK, 5);
 
 
-        m_ComboSize.append("Tiny"); m_ComboSize.append("Normal"); m_ComboSize.append("Huge");
+        m_ToolbarTop.pack_start(*Gtk::manage(new Gtk::Label("  Zoom: ")), Gtk::PACK_SHRINK);
+        m_ComboSize.append("Small"); m_ComboSize.append("Medium"); m_ComboSize.append("Large");
         m_ComboSize.set_active(1);
         m_ComboSize.set_active(1);
         m_ComboSize.signal_changed().connect(sigc::mem_fun(*this, &RenamerWindow::on_size_changed));
         m_ComboSize.signal_changed().connect(sigc::mem_fun(*this, &RenamerWindow::on_size_changed));
         m_ToolbarTop.pack_start(m_ComboSize, Gtk::PACK_SHRINK, 5);
         m_ToolbarTop.pack_start(m_ComboSize, Gtk::PACK_SHRINK, 5);
 
 
-        m_BtnUndo.set_label("Undo");
+        m_BtnUndo.set_label("Undo Last Rename");
         m_BtnUndo.set_sensitive(false);
         m_BtnUndo.set_sensitive(false);
         m_BtnUndo.signal_clicked().connect(sigc::mem_fun(*this, &RenamerWindow::on_undo_clicked));
         m_BtnUndo.signal_clicked().connect(sigc::mem_fun(*this, &RenamerWindow::on_undo_clicked));
         m_ToolbarTop.pack_end(m_BtnUndo, Gtk::PACK_SHRINK, 5);
         m_ToolbarTop.pack_end(m_BtnUndo, Gtk::PACK_SHRINK, 5);
 
 
-        // Controls Frame
+        // --- Controls Frame ---
         m_FrameControls.set_shadow_type(Gtk::SHADOW_ETCHED_IN);
         m_FrameControls.set_shadow_type(Gtk::SHADOW_ETCHED_IN);
         m_VBox.pack_start(m_FrameControls, Gtk::PACK_SHRINK);
         m_VBox.pack_start(m_FrameControls, Gtk::PACK_SHRINK);
         m_ToolbarControls.set_margin_top(10); m_ToolbarControls.set_margin_bottom(10);
         m_ToolbarControls.set_margin_top(10); m_ToolbarControls.set_margin_bottom(10);
         m_FrameControls.add(m_ToolbarControls);
         m_FrameControls.add(m_ToolbarControls);
 
 
-        m_ComboMode.append("Numbering Pattern"); m_ComboMode.append("Find & Replace");
+        m_ToolbarControls.pack_start(*Gtk::manage(new Gtk::Label(" Mode: ")), Gtk::PACK_SHRINK);
+        m_ComboMode.append("Sequential Numbering"); m_ComboMode.append("Find & Replace");
         m_ComboMode.set_active(0);
         m_ComboMode.set_active(0);
         m_ComboMode.signal_changed().connect(sigc::mem_fun(*this, &RenamerWindow::on_mode_changed));
         m_ComboMode.signal_changed().connect(sigc::mem_fun(*this, &RenamerWindow::on_mode_changed));
         m_ToolbarControls.pack_start(m_ComboMode, Gtk::PACK_SHRINK, 5);
         m_ToolbarControls.pack_start(m_ComboMode, Gtk::PACK_SHRINK, 5);
 
 
+        // Sequential Numbering Widgets
+        m_BoxPattern.pack_start(*Gtk::manage(new Gtk::Label(" Pattern: ")), Gtk::PACK_SHRINK);
         m_EntryPattern.set_text("image_###");
         m_EntryPattern.set_text("image_###");
         m_EntryPattern.signal_changed().connect(sigc::mem_fun(*this, &RenamerWindow::update_preview));
         m_EntryPattern.signal_changed().connect(sigc::mem_fun(*this, &RenamerWindow::update_preview));
         m_BoxPattern.pack_start(m_EntryPattern, Gtk::PACK_EXPAND_WIDGET);
         m_BoxPattern.pack_start(m_EntryPattern, Gtk::PACK_EXPAND_WIDGET);
+        m_BoxPattern.pack_start(*Gtk::manage(new Gtk::Label(" Start at: ")), Gtk::PACK_SHRINK);
         m_AdjStartNum = Gtk::Adjustment::create(1, 0, 100000, 1, 10, 0);
         m_AdjStartNum = Gtk::Adjustment::create(1, 0, 100000, 1, 10, 0);
         m_SpinStartNum.set_adjustment(m_AdjStartNum);
         m_SpinStartNum.set_adjustment(m_AdjStartNum);
         m_SpinStartNum.signal_value_changed().connect(sigc::mem_fun(*this, &RenamerWindow::update_preview));
         m_SpinStartNum.signal_value_changed().connect(sigc::mem_fun(*this, &RenamerWindow::update_preview));
         m_BoxPattern.pack_start(m_SpinStartNum, Gtk::PACK_SHRINK);
         m_BoxPattern.pack_start(m_SpinStartNum, Gtk::PACK_SHRINK);
         m_StackModes.add(m_BoxPattern, "Pattern");
         m_StackModes.add(m_BoxPattern, "Pattern");
 
 
+        // Find & Replace Widgets
+        m_BoxReplace.pack_start(*Gtk::manage(new Gtk::Label(" Find Text: ")), Gtk::PACK_SHRINK);
         m_EntryFind.signal_changed().connect(sigc::mem_fun(*this, &RenamerWindow::update_preview));
         m_EntryFind.signal_changed().connect(sigc::mem_fun(*this, &RenamerWindow::update_preview));
         m_BoxReplace.pack_start(m_EntryFind, Gtk::PACK_EXPAND_WIDGET);
         m_BoxReplace.pack_start(m_EntryFind, Gtk::PACK_EXPAND_WIDGET);
+        m_BoxReplace.pack_start(*Gtk::manage(new Gtk::Label(" Replace With: ")), Gtk::PACK_SHRINK);
         m_EntryReplace.signal_changed().connect(sigc::mem_fun(*this, &RenamerWindow::update_preview));
         m_EntryReplace.signal_changed().connect(sigc::mem_fun(*this, &RenamerWindow::update_preview));
         m_BoxReplace.pack_start(m_EntryReplace, Gtk::PACK_EXPAND_WIDGET);
         m_BoxReplace.pack_start(m_EntryReplace, Gtk::PACK_EXPAND_WIDGET);
         m_StackModes.add(m_BoxReplace, "Replace");
         m_StackModes.add(m_BoxReplace, "Replace");
         m_ToolbarControls.pack_start(m_StackModes, Gtk::PACK_EXPAND_WIDGET, 5);
         m_ToolbarControls.pack_start(m_StackModes, Gtk::PACK_EXPAND_WIDGET, 5);
 
 
-        m_BtnRename.set_label("Rename Selected");
+        m_BtnRename.set_label("Apply New Names");
         m_BtnRename.get_style_context()->add_class("suggested-action");
         m_BtnRename.get_style_context()->add_class("suggested-action");
         m_BtnRename.signal_clicked().connect(sigc::mem_fun(*this, &RenamerWindow::on_rename_execute));
         m_BtnRename.signal_clicked().connect(sigc::mem_fun(*this, &RenamerWindow::on_rename_execute));
         m_ToolbarControls.pack_end(m_BtnRename, Gtk::PACK_SHRINK, 5);
         m_ToolbarControls.pack_end(m_BtnRename, Gtk::PACK_SHRINK, 5);
 
 
-        // IconView setup
+        // --- IconView setup ---
         m_ScrolledWindow.set_policy(Gtk::POLICY_AUTOMATIC, Gtk::POLICY_ALWAYS);
         m_ScrolledWindow.set_policy(Gtk::POLICY_AUTOMATIC, Gtk::POLICY_ALWAYS);
         m_VBox.pack_start(m_ScrolledWindow);
         m_VBox.pack_start(m_ScrolledWindow);
 
 
@@ -170,39 +174,32 @@ public:
         m_RefListStore->signal_row_inserted().connect([this](const Gtk::TreeModel::Path&, const Gtk::TreeModel::iterator&){ update_preview(); });
         m_RefListStore->signal_row_inserted().connect([this](const Gtk::TreeModel::Path&, const Gtk::TreeModel::iterator&){ update_preview(); });
 
 
         m_IconView.set_model(m_RefListStore);
         m_IconView.set_model(m_RefListStore);
-        
-        // Pack our custom framed renderer
         m_IconView.pack_start(m_cell_frame, false);
         m_IconView.pack_start(m_cell_frame, false);
         m_IconView.add_attribute(m_cell_frame, "pixbuf", m_Columns.m_col_pixbuf);
         m_IconView.add_attribute(m_cell_frame, "pixbuf", m_Columns.m_col_pixbuf);
-
         m_IconView.pack_start(m_cell_toggle, false);
         m_IconView.pack_start(m_cell_toggle, false);
         m_IconView.add_attribute(m_cell_toggle, "active", m_Columns.m_col_checked);
         m_IconView.add_attribute(m_cell_toggle, "active", m_Columns.m_col_checked);
         m_cell_toggle.property_activatable() = true;
         m_cell_toggle.property_activatable() = true;
         m_cell_toggle.signal_toggled().connect(sigc::mem_fun(*this, &RenamerWindow::on_cell_toggled));
         m_cell_toggle.signal_toggled().connect(sigc::mem_fun(*this, &RenamerWindow::on_cell_toggled));
-
         m_IconView.pack_start(m_cell_text, false);
         m_IconView.pack_start(m_cell_text, false);
         m_IconView.add_attribute(m_cell_text, "markup", m_Columns.m_col_markup);
         m_IconView.add_attribute(m_cell_text, "markup", m_Columns.m_col_markup);
         
         
         m_IconView.set_item_width(220);
         m_IconView.set_item_width(220);
-        m_IconView.set_selection_mode(Gtk::SELECTION_MULTIPLE);
-        m_IconView.set_reorderable(true); 
         m_IconView.set_columns(-1);
         m_IconView.set_columns(-1);
-        m_IconView.set_spacing(20); // Spacing between thumbnails
+        m_IconView.set_spacing(20);
+        
+        m_IconView.set_selection_mode(Gtk::SELECTION_NONE);
+        m_IconView.set_reorderable(false);
 
 
         m_ScrolledWindow.add(m_IconView);
         m_ScrolledWindow.add(m_IconView);
-
-        m_ProgressBar.set_no_show_all(true);
-        m_ProgressBar.hide();
         m_VBox.pack_start(m_ProgressBar, Gtk::PACK_SHRINK);
         m_VBox.pack_start(m_ProgressBar, Gtk::PACK_SHRINK);
         m_VBox.pack_end(m_Statusbar, Gtk::PACK_SHRINK);
         m_VBox.pack_end(m_Statusbar, Gtk::PACK_SHRINK);
 
 
-        // D&D
         std::vector<Gtk::TargetEntry> listTargets = { Gtk::TargetEntry("text/uri-list") };
         std::vector<Gtk::TargetEntry> listTargets = { Gtk::TargetEntry("text/uri-list") };
         this->drag_dest_set(listTargets, Gtk::DEST_DEFAULT_ALL, Gdk::ACTION_COPY);
         this->drag_dest_set(listTargets, Gtk::DEST_DEFAULT_ALL, Gdk::ACTION_COPY);
         this->signal_drag_data_received().connect(sigc::mem_fun(*this, &RenamerWindow::on_drag_data_received));
         this->signal_drag_data_received().connect(sigc::mem_fun(*this, &RenamerWindow::on_drag_data_received));
 
 
         show_all_children();
         show_all_children();
-        m_StackModes.set_visible_child("Pattern");
+        m_ProgressBar.hide();
     }
     }
 
 
     ~RenamerWindow() { m_stop_flag = true; if (m_WorkerThread.joinable()) m_WorkerThread.join(); }
     ~RenamerWindow() { m_stop_flag = true; if (m_WorkerThread.joinable()) m_WorkerThread.join(); }
@@ -210,7 +207,7 @@ public:
 protected:
 protected:
     Gtk::Box m_VBox, m_ToolbarTop, m_ToolbarControls, m_BoxPattern, m_BoxReplace;
     Gtk::Box m_VBox, m_ToolbarTop, m_ToolbarControls, m_BoxPattern, m_BoxReplace;
     Gtk::Frame m_FrameControls;
     Gtk::Frame m_FrameControls;
-    Gtk::Button m_BtnOpen, m_BtnRename, m_BtnUndo, m_BtnStop;
+    Gtk::Button m_BtnOpen, m_BtnRename, m_BtnUndo, m_BtnStopReload;
     Gtk::Entry m_EntryPattern, m_EntryFind, m_EntryReplace;
     Gtk::Entry m_EntryPattern, m_EntryFind, m_EntryReplace;
     Gtk::ComboBoxText m_ComboSize, m_ComboSort, m_ComboMode;
     Gtk::ComboBoxText m_ComboSize, m_ComboSort, m_ComboMode;
     Gtk::SpinButton m_SpinStartNum;
     Gtk::SpinButton m_SpinStartNum;
@@ -220,25 +217,22 @@ protected:
     Gtk::IconView m_IconView;
     Gtk::IconView m_IconView;
     Gtk::ProgressBar m_ProgressBar;
     Gtk::ProgressBar m_ProgressBar;
     Gtk::Statusbar m_Statusbar;
     Gtk::Statusbar m_Statusbar;
-    
-    CellRendererFrame m_cell_frame; // Custom Framed Renderer
+    CellRendererFrame m_cell_frame;
     Gtk::CellRendererText m_cell_text;
     Gtk::CellRendererText m_cell_text;
     Gtk::CellRendererToggle m_cell_toggle;
     Gtk::CellRendererToggle m_cell_toggle;
 
 
-    class ModelColumns : public Gtk::TreeModel::ColumnRecord {
-    public:
+    struct ModelColumns : public Gtk::TreeModel::ColumnRecord {
         ModelColumns() { add(m_col_path); add(m_col_filename); add(m_col_pixbuf); add(m_col_checked); add(m_col_markup); add(m_col_time); add(m_col_info_str); }
         ModelColumns() { add(m_col_path); add(m_col_filename); add(m_col_pixbuf); add(m_col_checked); add(m_col_markup); add(m_col_time); add(m_col_info_str); }
         Gtk::TreeModelColumn<std::string> m_col_path, m_col_filename, m_col_markup, m_col_info_str;
         Gtk::TreeModelColumn<std::string> m_col_path, m_col_filename, m_col_markup, m_col_info_str;
         Gtk::TreeModelColumn<Glib::RefPtr<Gdk::Pixbuf>> m_col_pixbuf;
         Gtk::TreeModelColumn<Glib::RefPtr<Gdk::Pixbuf>> m_col_pixbuf;
         Gtk::TreeModelColumn<bool> m_col_checked;
         Gtk::TreeModelColumn<bool> m_col_checked;
         Gtk::TreeModelColumn<long long> m_col_time;
         Gtk::TreeModelColumn<long long> m_col_time;
-    };
-    ModelColumns m_Columns;
+    } m_Columns;
     Glib::RefPtr<Gtk::ListStore> m_RefListStore;
     Glib::RefPtr<Gtk::ListStore> m_RefListStore;
 
 
     std::string m_current_path;
     std::string m_current_path;
     std::thread m_WorkerThread;
     std::thread m_WorkerThread;
-    std::atomic<bool> m_stop_flag{false};
+    std::atomic<bool> m_stop_flag{false}, m_is_loading{false};
     std::atomic<int> m_total_files{0}, m_processed_files{0};
     std::atomic<int> m_total_files{0}, m_processed_files{0};
     Glib::Dispatcher m_Dispatcher; 
     Glib::Dispatcher m_Dispatcher; 
     std::mutex m_QueueMutex;
     std::mutex m_QueueMutex;
@@ -246,7 +240,7 @@ protected:
     std::stack<UndoStep> m_UndoStack;
     std::stack<UndoStep> m_UndoStack;
 
 
     std::string format_size(uintmax_t bytes) {
     std::string format_size(uintmax_t bytes) {
-        double sz = static_cast<double>(bytes);
+        double sz = (double)bytes;
         const char* units[] = {"B", "KB", "MB", "GB"};
         const char* units[] = {"B", "KB", "MB", "GB"};
         int i = 0; while (sz >= 1024 && i < 3) { sz /= 1024; i++; }
         int i = 0; while (sz >= 1024 && i < 3) { sz /= 1024; i++; }
         std::stringstream ss; ss << std::fixed << std::setprecision(1) << sz << " " << units[i];
         std::stringstream ss; ss << std::fixed << std::setprecision(1) << sz << " " << units[i];
@@ -254,201 +248,229 @@ protected:
     }
     }
 
 
     void on_mode_changed() {
     void on_mode_changed() {
-        if (m_ComboMode.get_active_row_number() == 0) m_StackModes.set_visible_child("Pattern");
-        else m_StackModes.set_visible_child("Replace");
+        m_StackModes.set_visible_child(m_ComboMode.get_active_row_number() == 0 ? "Pattern" : "Replace");
         update_preview();
         update_preview();
     }
     }
 
 
-    void on_cell_toggled(const Glib::ustring& path_string) {
-        auto iter = m_RefListStore->get_iter(Gtk::TreePath(path_string));
-        if(iter) { (*iter)[m_Columns.m_col_checked] = !(*iter)[m_Columns.m_col_checked]; update_preview(); }
+    void on_cell_toggled(const Glib::ustring& p) {
+        auto it = m_RefListStore->get_iter(Gtk::TreePath(p));
+        if(it) { (*it)[m_Columns.m_col_checked] = !(*it)[m_Columns.m_col_checked]; update_preview(); }
+    }
+
+    void on_stop_reload_clicked() {
+        if (m_is_loading) m_stop_flag = true;
+        else if (!m_current_path.empty()) start_loading_folder(m_current_path);
     }
     }
 
 
     void update_preview() {
     void update_preview() {
+        if (!m_RefListStore) return;
         int mode = m_ComboMode.get_active_row_number();
         int mode = m_ComboMode.get_active_row_number();
         std::string pattern = m_EntryPattern.get_text();
         std::string pattern = m_EntryPattern.get_text();
         int start_num = m_SpinStartNum.get_value_as_int();
         int start_num = m_SpinStartNum.get_value_as_int();
         size_t hash_pos = pattern.find('#');
         size_t hash_pos = pattern.find('#');
-        int pad_width = (hash_pos != std::string::npos) ? (pattern.find_last_of('#') - hash_pos + 1) : 0;
+        int pad = (hash_pos != std::string::npos) ? (pattern.find_last_of('#') - hash_pos + 1) : 0;
         int count = 0;
         int count = 0;
         for (auto row : m_RefListStore->children()) {
         for (auto row : m_RefListStore->children()) {
-            std::string original = row[m_Columns.m_col_filename], meta_info = row[m_Columns.m_col_info_str], new_name = original;
+            std::string original = row[m_Columns.m_col_filename], meta = row[m_Columns.m_col_info_str], new_name = original;
             if (row[m_Columns.m_col_checked]) {
             if (row[m_Columns.m_col_checked]) {
                 if (mode == 0 && !pattern.empty()) {
                 if (mode == 0 && !pattern.empty()) {
-                    std::string num_str = std::to_string(start_num + count);
-                    if (pad_width > 0) {
-                        while (num_str.length() < (size_t)pad_width) num_str = "0" + num_str;
-                        std::string temp = pattern; temp.replace(hash_pos, pad_width, num_str);
-                        new_name = temp + fs::path(original).extension().string();
-                    } else { new_name = pattern + "_" + num_str + fs::path(original).extension().string(); }
+                    std::string num = std::to_string(start_num + count);
+                    while (num.length() < (size_t)pad) num = "0" + num;
+                    if (hash_pos != std::string::npos) {
+                        std::string t = pattern; t.replace(hash_pos, pad, num);
+                        new_name = t + fs::path(original).extension().string();
+                    } else new_name = pattern + "_" + num + fs::path(original).extension().string();
                     count++;
                     count++;
                 } else if (mode == 1 && !m_EntryFind.get_text().empty()) {
                 } else if (mode == 1 && !m_EntryFind.get_text().empty()) {
                     size_t pos = original.find(m_EntryFind.get_text());
                     size_t pos = original.find(m_EntryFind.get_text());
                     if (pos != std::string::npos) { new_name = original; new_name.replace(pos, m_EntryFind.get_text().length(), m_EntryReplace.get_text()); }
                     if (pos != std::string::npos) { new_name = original; new_name.replace(pos, m_EntryFind.get_text().length(), m_EntryReplace.get_text()); }
                 }
                 }
             }
             }
-            std::stringstream markup;
-            markup << "<span font='9'>";
-            if (new_name != original) markup << "<span size='small' alpha='60%'>" << Glib::Markup::escape_text(original) << "</span>\n<span foreground='#3584e4' weight='bold'>" << Glib::Markup::escape_text(new_name) << "</span>";
-            else markup << "<span weight='bold'>" << Glib::Markup::escape_text(original) << "</span>";
-            markup << "\n<span size='x-small' alpha='70%'>" << Glib::Markup::escape_text(meta_info) << "</span></span>";
-            row[m_Columns.m_col_markup] = markup.str();
+            std::stringstream mu; mu << "<span font='9'>";
+            if (new_name != original) mu << "<span size='small' alpha='60%'>" << Glib::Markup::escape_text(original) << "</span>\n<span foreground='#3584e4' weight='bold'>" << Glib::Markup::escape_text(new_name) << "</span>";
+            else mu << "<span weight='bold'>" << Glib::Markup::escape_text(original) << "</span>";
+            mu << "\n<span size='x-small' alpha='70%'>" << Glib::Markup::escape_text(meta) << "</span></span>";
+            row[m_Columns.m_col_markup] = mu.str();
         }
         }
     }
     }
 
 
     void on_rename_execute() {
     void on_rename_execute() {
-        UndoStep undo_step;
-        int mode = m_ComboMode.get_active_row_number(), start_num = m_SpinStartNum.get_value_as_int();
-        std::string pattern = m_EntryPattern.get_text();
-        size_t hash_pos = pattern.find('#');
-        int pad_width = (hash_pos != std::string::npos && mode == 0) ? (pattern.find_last_of('#') - hash_pos + 1) : 0;
-        int iter_count = 0;
+        UndoStep step;
+        int mode = m_ComboMode.get_active_row_number(), start = m_SpinStartNum.get_value_as_int();
+        std::string pat = m_EntryPattern.get_text();
+        size_t hpos = pat.find('#');
+        int pad = (hpos != std::string::npos && mode == 0) ? (pat.find_last_of('#') - hpos + 1) : 0;
+        int count = 0;
         for (auto row : m_RefListStore->children()) {
         for (auto row : m_RefListStore->children()) {
             if (!row[m_Columns.m_col_checked]) continue;
             if (!row[m_Columns.m_col_checked]) continue;
-            std::string original = row[m_Columns.m_col_filename], new_filename = original;
+            std::string orig = row[m_Columns.m_col_filename], next = orig;
             if (mode == 0) {
             if (mode == 0) {
-                std::string num_str = std::to_string(start_num + iter_count);
-                if (pad_width > 0) {
-                    while (num_str.length() < (size_t)pad_width) num_str = "0" + num_str;
-                    std::string temp = pattern; temp.replace(hash_pos, pad_width, num_str);
-                    new_filename = temp + fs::path(original).extension().string();
-                } else { new_filename = pattern + "_" + num_str + fs::path(original).extension().string(); }
-                iter_count++;
+                std::string num = std::to_string(start + count);
+                while (num.length() < (size_t)pad) num = "0" + num;
+                if (hpos != std::string::npos) { std::string t = pat; t.replace(hpos, pad, num); next = t + fs::path(orig).extension().string(); }
+                else next = pat + "_" + num + fs::path(orig).extension().string();
+                count++;
             } else if (mode == 1 && !m_EntryFind.get_text().empty()) {
             } else if (mode == 1 && !m_EntryFind.get_text().empty()) {
-                size_t pos = original.find(m_EntryFind.get_text());
-                if (pos != std::string::npos) { new_filename = original; new_filename.replace(pos, m_EntryFind.get_text().length(), m_EntryReplace.get_text()); }
+                size_t p = orig.find(m_EntryFind.get_text());
+                if (p != std::string::npos) { next = orig; next.replace(p, m_EntryFind.get_text().length(), m_EntryReplace.get_text()); }
             }
             }
-            if (new_filename != original) undo_step.moves.push_back({(std::string)row[m_Columns.m_col_path], (fs::path((std::string)row[m_Columns.m_col_path]).parent_path() / new_filename).string()});
+            if (next != orig) step.moves.push_back({(std::string)row[m_Columns.m_col_path], (fs::path((std::string)row[m_Columns.m_col_path]).parent_path() / next).string()});
         }
         }
-        if (undo_step.moves.empty()) return;
-        int t_idx = 0; std::vector<std::pair<std::string, std::string>> final_stage;
-        for (auto& move : undo_step.moves) {
-            fs::path temp = fs::path(move.original_path).parent_path() / ("__tmp_" + std::to_string(t_idx++) + fs::path(move.original_path).extension().string());
-            try { fs::rename(move.original_path, temp); final_stage.push_back({temp.string(), move.new_path}); } catch (...) {}
+        if (step.moves.empty()) return;
+        int ti = 0; std::vector<std::pair<std::string, std::string>> stage;
+        for (auto& m : step.moves) {
+            fs::path t = fs::path(m.original_path).parent_path() / ("__tmp_" + std::to_string(ti++) + fs::path(m.original_path).extension().string());
+            try { fs::rename(m.original_path, t); stage.push_back({t.string(), m.new_path}); } catch (...) {}
         }
         }
-        for (auto& item : final_stage) { try { fs::rename(item.first, item.second); } catch (...) {} }
-        m_UndoStack.push(undo_step); m_BtnUndo.set_sensitive(true); start_loading_folder(m_current_path);
+        for (auto& s : stage) try { fs::rename(s.first, s.second); } catch (...) {}
+        m_UndoStack.push(step); m_BtnUndo.set_sensitive(true); start_loading_folder(m_current_path);
     }
     }
 
 
     void on_undo_clicked() {
     void on_undo_clicked() {
         if (m_UndoStack.empty()) return;
         if (m_UndoStack.empty()) return;
         UndoStep last = m_UndoStack.top(); m_UndoStack.pop(); m_BtnUndo.set_sensitive(!m_UndoStack.empty());
         UndoStep last = m_UndoStack.top(); m_UndoStack.pop(); m_BtnUndo.set_sensitive(!m_UndoStack.empty());
-        int t_idx = 0; std::vector<std::pair<std::string, std::string>> final_stage;
-        for (const auto& move : last.moves) {
-            if (fs::exists(move.new_path)) {
-                fs::path temp = fs::path(move.new_path).parent_path() / ("__u_tmp_" + std::to_string(t_idx++) + fs::path(move.new_path).extension().string());
-                try { fs::rename(move.new_path, temp); final_stage.push_back({temp.string(), move.original_path}); } catch (...) {}
+        int ti = 0; std::vector<std::pair<std::string, std::string>> stage;
+        for (const auto& m : last.moves) {
+            if (fs::exists(m.new_path)) {
+                fs::path t = fs::path(m.new_path).parent_path() / ("__u_" + std::to_string(ti++) + fs::path(m.new_path).extension().string());
+                try { fs::rename(m.new_path, t); stage.push_back({t.string(), m.original_path}); } catch (...) {}
             }
             }
         }
         }
-        for (auto& item : final_stage) { try { fs::rename(item.first, item.second); } catch(...) {} }
+        for (auto& s : stage) try { fs::rename(s.first, s.second); } catch(...) {}
         start_loading_folder(m_current_path);
         start_loading_folder(m_current_path);
     }
     }
 
 
     void on_sort_changed() {
     void on_sort_changed() {
-        if (m_RefListStore->children().empty()) return;
+        if (!m_RefListStore || m_RefListStore->children().empty()) return;
         int mode = m_ComboSort.get_active_row_number(); 
         int mode = m_ComboSort.get_active_row_number(); 
-        if (mode == 3) { m_RefListStore->set_sort_column(GTK_TREE_SORTABLE_UNSORTED_SORT_COLUMN_ID, Gtk::SORT_ASCENDING); return; }
-        if (mode == 0) {
-            m_RefListStore->set_sort_func(m_Columns.m_col_filename, [](const Gtk::TreeModel::iterator& a, const Gtk::TreeModel::iterator& b){
-                return NaturalSort()((*a)[ModelColumns().m_col_filename], (*b)[ModelColumns().m_col_filename]) ? -1 : 1;
-            });
+        if (mode == 3) {
+            m_IconView.set_selection_mode(Gtk::SELECTION_MULTIPLE);
+            m_IconView.set_reorderable(true);
+            m_RefListStore->set_sort_column(GTK_TREE_SORTABLE_UNSORTED_SORT_COLUMN_ID, Gtk::SORT_ASCENDING); 
         } else {
         } else {
-            m_RefListStore->set_sort_func(m_Columns.m_col_time, [mode](const Gtk::TreeModel::iterator& a, const Gtk::TreeModel::iterator& b){
-                long long ta = (*a)[ModelColumns().m_col_time], tb = (*b)[ModelColumns().m_col_time];
-                return (mode == 1) ? (ta < tb ? -1 : 1) : (ta > tb ? -1 : 1);
-            });
+            m_IconView.set_selection_mode(Gtk::SELECTION_NONE);
+            m_IconView.set_reorderable(false);
+            if (mode == 0) {
+                m_RefListStore->set_sort_func(m_Columns.m_col_filename, [this](const Gtk::TreeModel::iterator& a, const Gtk::TreeModel::iterator& b){
+                    return NaturalSort()((*a)[m_Columns.m_col_filename], (*b)[m_Columns.m_col_filename]) ? -1 : 1;
+                });
+                m_RefListStore->set_sort_column(m_Columns.m_col_filename, Gtk::SORT_ASCENDING);
+            } else {
+                m_RefListStore->set_sort_func(m_Columns.m_col_time, [mode, this](const Gtk::TreeModel::iterator& a, const Gtk::TreeModel::iterator& b){
+                    long long ta = (*a)[m_Columns.m_col_time], tb = (*b)[m_Columns.m_col_time];
+                    if (ta == tb) return 0;
+                    return (mode == 1) ? (ta < tb ? -1 : 1) : (ta > tb ? -1 : 1);
+                });
+                m_RefListStore->set_sort_column(m_Columns.m_col_time, Gtk::SORT_ASCENDING);
+            }
         }
         }
-        m_RefListStore->set_sort_column(m_Columns.m_col_filename, Gtk::SORT_ASCENDING); update_preview(); 
+        Glib::signal_idle().connect_once([this](){ update_preview(); });
     }
     }
 
 
-    void worker_thread_folder(std::string dir_path, int size) {
-        std::vector<fs::path> files;
-        try {
-            for (const auto& entry : fs::directory_iterator(dir_path)) {
-                if (m_stop_flag) return;
-                std::string ext = entry.path().extension().string(); std::transform(ext.begin(), ext.end(), ext.begin(), ::tolower);
-                if (ext == ".jpg" || ext == ".png" || ext == ".jpeg" || ext == ".gif" || ext == ".bmp") files.push_back(entry.path());
-            }
-        } catch (...) { return; }
-        std::sort(files.begin(), files.end(), [](const fs::path& a, const fs::path& b){ return NaturalSort()(a.filename().string(), b.filename().string()); });
-        m_total_files = files.size(); m_processed_files = 0; process_files(files, size);
-    }
-    
-    void process_files(const std::vector<fs::path>& files, int size) {
-        for (const auto& path : files) {
+    void worker_thread_logic(std::vector<fs::path> paths, int sz, bool is_zoom_only) {
+        m_is_loading = true; m_total_files = paths.size(); m_processed_files = 0;
+        for (size_t idx = 0; idx < paths.size(); ++idx) {
             if (m_stop_flag) break;
             if (m_stop_flag) break;
-            LoadedItem item; item.path = path.string(); item.filename = path.filename().string();
-            item.ext = path.extension().string(); if (!item.ext.empty() && item.ext[0] == '.') item.ext = item.ext.substr(1);
-            std::transform(item.ext.begin(), item.ext.end(), item.ext.begin(), ::toupper);
-            try { item.last_write_time = fs::last_write_time(path); item.filesize = fs::file_size(path); } catch(...) {}
+            const auto& p = paths[idx];
+            LoadedItem item; item.path = p.string(); item.is_update = is_zoom_only;
+            if (is_zoom_only) item.model_path = Gtk::TreePath(std::to_string(idx));
             try {
             try {
-                auto pixbuf = Gdk::Pixbuf::create_from_file(path.string());
-                item.orig_w = pixbuf->get_width(); item.orig_h = pixbuf->get_height();
-                float ratio = (float)item.orig_w / item.orig_h;
-                int dw = (ratio > 1) ? size : size * ratio, dh = (ratio > 1) ? size / ratio : size;
-                item.pixbuf = pixbuf->scale_simple(dw, dh, Gdk::INTERP_BILINEAR);
-                { std::lock_guard<std::mutex> lock(m_QueueMutex); m_ResultQueue.push_back(item); }
-                m_processed_files++; m_Dispatcher.emit(); std::this_thread::sleep_for(std::chrono::microseconds(100)); 
+                auto pb = Gdk::Pixbuf::create_from_file(p.string());
+                float r = (float)pb->get_width() / pb->get_height();
+                int dw = (r > 1) ? sz : sz * r, dh = (r > 1) ? sz / r : sz;
+                item.pixbuf = pb->scale_simple(dw, dh, Gdk::INTERP_BILINEAR);
+                if (!is_zoom_only) {
+                    item.filename = p.filename().string();
+                    item.ext = p.extension().string(); if (!item.ext.empty() && item.ext[0] == '.') item.ext = item.ext.substr(1);
+                    std::transform(item.ext.begin(), item.ext.end(), item.ext.begin(), ::toupper);
+                    item.orig_w = pb->get_width(); item.orig_h = pb->get_height();
+                    item.last_write_time = fs::last_write_time(p); item.filesize = fs::file_size(p);
+                }
+                { std::lock_guard<std::mutex> l(m_QueueMutex); m_ResultQueue.push_back(item); }
+                m_processed_files++; m_Dispatcher.emit();
+                std::this_thread::sleep_for(std::chrono::microseconds(50));
             } catch (...) { m_processed_files++; }
             } catch (...) { m_processed_files++; }
         }
         }
-        m_total_files = 0; m_Dispatcher.emit();
+        m_is_loading = false; m_total_files = 0; m_Dispatcher.emit();
     }
     }
 
 
     void on_worker_notification() {
     void on_worker_notification() {
-        std::lock_guard<std::mutex> lock(m_QueueMutex);
+        std::lock_guard<std::mutex> l(m_QueueMutex);
         while (!m_ResultQueue.empty()) {
         while (!m_ResultQueue.empty()) {
-            LoadedItem item = m_ResultQueue.front(); m_ResultQueue.pop_front();
-            auto row = *(m_RefListStore->append());
-            row[m_Columns.m_col_path] = item.path; row[m_Columns.m_col_filename] = item.filename;
-            row[m_Columns.m_col_pixbuf] = item.pixbuf; row[m_Columns.m_col_checked] = true;
-            auto sctp = std::chrono::time_point_cast<std::chrono::system_clock::duration>(item.last_write_time - fs::file_time_type::clock::now() + std::chrono::system_clock::now());
-            std::time_t tt = std::chrono::system_clock::to_time_t(sctp);
-            std::stringstream ss; ss << std::put_time(std::localtime(&tt), "%H:%M %d-%b-%Y");
-            row[m_Columns.m_col_info_str] = item.ext + " | " + std::to_string(item.orig_w) + "x" + std::to_string(item.orig_h) + " | " + format_size(item.filesize) + " | " + ss.str();
-            row[m_Columns.m_col_time] = sctp.time_since_epoch().count();
+            LoadedItem i = m_ResultQueue.front(); m_ResultQueue.pop_front();
+            if (i.is_update) {
+                auto it = m_RefListStore->get_iter(i.model_path);
+                if (it) (*it)[m_Columns.m_col_pixbuf] = i.pixbuf;
+            } else {
+                auto r = *(m_RefListStore->append());
+                r[m_Columns.m_col_path] = i.path; r[m_Columns.m_col_filename] = i.filename;
+                r[m_Columns.m_col_pixbuf] = i.pixbuf; r[m_Columns.m_col_checked] = true;
+                auto sctp = std::chrono::time_point_cast<std::chrono::system_clock::duration>(i.last_write_time - fs::file_time_type::clock::now() + std::chrono::system_clock::now());
+                std::time_t tt = std::chrono::system_clock::to_time_t(sctp);
+                std::stringstream ss; ss << std::put_time(std::localtime(&tt), "%H:%M %d-%b-%Y");
+                r[m_Columns.m_col_info_str] = i.ext + " | " + std::to_string(i.orig_w) + "x" + std::to_string(i.orig_h) + " | " + format_size(i.filesize) + " | " + ss.str();
+                r[m_Columns.m_col_time] = sctp.time_since_epoch().count();
+            }
+        }
+        if (m_total_files > 0) {
+            m_ProgressBar.set_fraction(std::min(1.0, (double)m_processed_files / m_total_files));
+            m_ProgressBar.show();
+            m_BtnStopReload.set_label("Stop Loading"); m_BtnStopReload.set_sensitive(true);
+        } else {
+            m_ProgressBar.hide();
+            m_BtnStopReload.set_label("Reload Folder");
+            m_BtnStopReload.set_sensitive(!m_current_path.empty());
         }
         }
-        if (m_total_files > 0) { m_ProgressBar.set_fraction(std::min(1.0, (double)m_processed_files / m_total_files)); m_ProgressBar.show(); m_BtnStop.set_sensitive(true); }
-        else { m_ProgressBar.hide(); m_BtnStop.set_sensitive(false); }
-        if (!m_RefListStore->children().empty()) update_preview();
+        if (m_total_files == 0) update_preview();
     }
     }
 
 
     void on_size_changed() {
     void on_size_changed() {
-        std::vector<fs::path> current; for(auto row : m_RefListStore->children()) current.push_back(fs::path((std::string)row[m_Columns.m_col_path]));
-        if (current.empty()) return;
+        if (m_RefListStore->children().empty()) return;
         m_stop_flag = true; if (m_WorkerThread.joinable()) m_WorkerThread.join();
         m_stop_flag = true; if (m_WorkerThread.joinable()) m_WorkerThread.join();
-        m_RefListStore->clear();
         int sz = (m_ComboSize.get_active_row_number() == 0) ? 100 : (m_ComboSize.get_active_row_number() == 2 ? 450 : 220);
         int sz = (m_ComboSize.get_active_row_number() == 0) ? 100 : (m_ComboSize.get_active_row_number() == 2 ? 450 : 220);
-        m_IconView.set_item_width(sz); m_total_files = current.size(); m_processed_files = 0; m_stop_flag = false;
-        m_WorkerThread = std::thread(&RenamerWindow::process_files, this, current, sz);
+        m_IconView.set_item_width(sz);
+        std::vector<fs::path> paths;
+        for(auto r : m_RefListStore->children()) paths.push_back(fs::path((std::string)r[m_Columns.m_col_path]));
+        m_stop_flag = false;
+        m_WorkerThread = std::thread(&RenamerWindow::worker_thread_logic, this, paths, sz, true);
     }
     }
 
 
-    void start_loading_folder(std::string path) {
-        m_current_path = path; m_stop_flag = true; if (m_WorkerThread.joinable()) m_WorkerThread.join();
+    void start_loading_folder(std::string p) {
+        m_current_path = p; m_stop_flag = true; if (m_WorkerThread.joinable()) m_WorkerThread.join();
         m_RefListStore->clear();
         m_RefListStore->clear();
+        std::vector<fs::path> f;
+        try {
+            for (const auto& e : fs::directory_iterator(p)) {
+                std::string x = e.path().extension().string(); std::transform(x.begin(), x.end(), x.begin(), ::tolower);
+                if (x == ".jpg" || x == ".png" || x == ".jpeg" || x == ".gif" || x == ".bmp") f.push_back(e.path());
+            }
+        } catch (...) {}
+        std::sort(f.begin(), f.end(), [](const fs::path& a, const fs::path& b){ return NaturalSort()(a.filename().string(), b.filename().string()); });
         int sz = (m_ComboSize.get_active_row_number() == 0) ? 100 : (m_ComboSize.get_active_row_number() == 2 ? 450 : 220);
         int sz = (m_ComboSize.get_active_row_number() == 0) ? 100 : (m_ComboSize.get_active_row_number() == 2 ? 450 : 220);
         m_IconView.set_item_width(sz); m_stop_flag = false;
         m_IconView.set_item_width(sz); m_stop_flag = false;
-        m_WorkerThread = std::thread(&RenamerWindow::worker_thread_folder, this, path, sz);
+        m_WorkerThread = std::thread(&RenamerWindow::worker_thread_logic, this, f, sz, false);
+        m_BtnStopReload.set_sensitive(true);
     }
     }
 
 
     void on_open_folder_clicked() {
     void on_open_folder_clicked() {
-        Gtk::FileChooserDialog d(*this, "Select Folder", Gtk::FILE_CHOOSER_ACTION_SELECT_FOLDER);
+        Gtk::FileChooserDialog d(*this, "Select Image Folder", Gtk::FILE_CHOOSER_ACTION_SELECT_FOLDER);
         d.add_button("Cancel", Gtk::RESPONSE_CANCEL); d.add_button("Select", Gtk::RESPONSE_OK);
         d.add_button("Cancel", Gtk::RESPONSE_CANCEL); d.add_button("Select", Gtk::RESPONSE_OK);
         if (d.run() == Gtk::RESPONSE_OK) start_loading_folder(d.get_filename());
         if (d.run() == Gtk::RESPONSE_OK) start_loading_folder(d.get_filename());
     }
     }
 
 
-    void on_drag_data_received(const Glib::RefPtr<Gdk::DragContext>& context, int, int, const Gtk::SelectionData& sd, guint, guint t) {
-        auto uris = sd.get_uris();
+    void on_drag_data_received(const Glib::RefPtr<Gdk::DragContext>& c, int, int, const Gtk::SelectionData& s, guint, guint t) {
+        auto uris = s.get_uris();
         if (!uris.empty()) {
         if (!uris.empty()) {
-            std::string fn = Glib::filename_from_uri(uris[0]);
-            start_loading_folder(fs::is_directory(fn) ? fn : fs::path(fn).parent_path().string());
-            context->drag_finish(true, false, t);
-        } else context->drag_finish(false, false, t);
+            std::string f = Glib::filename_from_uri(uris[0]);
+            start_loading_folder(fs::is_directory(f) ? f : fs::path(f).parent_path().string());
+            c->drag_finish(true, false, t);
+        } else c->drag_finish(false, false, t);
     }
     }
 };
 };
 
 
 int main(int argc, char *argv[]) {
 int main(int argc, char *argv[]) {
     Glib::thread_init(); 
     Glib::thread_init(); 
-    auto app = Gtk::Application::create(argc, argv, "org.gtkmm.renamer_v01");
+    auto app = Gtk::Application::create(argc, argv, "org.gtkmm.renamer_v02");
     RenamerWindow window;
     RenamerWindow window;
     return app->run(window);
     return app->run(window);
 }
 }

BIN
visual-renamer