#include #include #include #include #include #include #include #include #include #include #include #include #include namespace fs = std::filesystem; // --- Natural Sort Helper --- struct NaturalSort { bool operator()(const std::string& a, const std::string& b) const { size_t i = 0, j = 0; while (i < a.length() && j < b.length()) { if (isdigit(a[i]) && isdigit(b[j])) { size_t start1 = i, start2 = j; while (i < a.length() && isdigit(a[i])) i++; while (j < b.length() && isdigit(b[j])) j++; try { long n1 = std::stol(a.substr(start1, i - start1)); long n2 = std::stol(b.substr(start2, j - start2)); if (n1 != n2) return n1 < n2; } catch (...) { if (a.substr(start1, i - start1) != b.substr(start2, j - start2)) return a.substr(start1, i - start1) < b.substr(start2, j - start2); } } else { if (a[i] != b[j]) return a[i] < b[j]; i++; j++; } } return a.length() < b.length(); } }; // --- Custom Renderer to draw a Frame --- class CellRendererFrame : public Gtk::CellRendererPixbuf { public: CellRendererFrame() { property_xpad() = 10; property_ypad() = 10; } protected: void render_vfunc(const Cairo::RefPtr& cr, Gtk::Widget& widget, const Gdk::Rectangle& background_area, const Gdk::Rectangle& cell_area, Gtk::CellRendererState flags) override { cr->save(); auto style = widget.get_style_context(); Gdk::RGBA bg = style->get_background_color(); 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); else cr->set_source_rgb(0.8, 0.8, 0.8); cr->set_line_width(2.0); cr->rectangle(cell_area.get_x() + 2, cell_area.get_y() + 2, cell_area.get_width() - 4, cell_area.get_height() - 4); cr->stroke(); cr->restore(); Gtk::CellRendererPixbuf::render_vfunc(cr, widget, background_area, cell_area, flags); } }; struct RenameAction { std::string original_path; std::string new_path; }; struct UndoStep { std::vector moves; }; struct LoadedItem { std::string path, filename, ext; fs::file_time_type last_write_time; Glib::RefPtr pixbuf; int orig_w = 0, orig_h = 0; uintmax_t filesize = 0; bool is_update = false; Gtk::TreeModel::Path model_path; }; class RenamerWindow : public Gtk::Window { public: RenamerWindow() : m_Dispatcher() { // --- VERSION 0.3.1 --- set_title("Simple Image Renamer 0.3.1"); set_default_size(1280, 850); set_wmclass("simpleimagerenamer", "simpleimagerenamer"); m_Dispatcher.connect(sigc::mem_fun(*this, &RenamerWindow::on_worker_notification)); m_VBox.set_orientation(Gtk::ORIENTATION_VERTICAL); add(m_VBox); // --- Toolbar Top --- 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_VBox.pack_start(m_ToolbarTop, Gtk::PACK_SHRINK); m_BtnOpen.set_label("Open Folder"); 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_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_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.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(*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.signal_changed().connect(sigc::mem_fun(*this, &RenamerWindow::on_size_changed)); m_ToolbarTop.pack_start(m_ComboSize, Gtk::PACK_SHRINK, 5); m_BtnUndo.set_label("Undo Last Rename"); m_BtnUndo.set_sensitive(false); m_BtnUndo.signal_clicked().connect(sigc::mem_fun(*this, &RenamerWindow::on_undo_clicked)); m_ToolbarTop.pack_end(m_BtnUndo, Gtk::PACK_SHRINK, 5); // --- Controls Frame --- m_FrameControls.set_shadow_type(Gtk::SHADOW_ETCHED_IN); m_VBox.pack_start(m_FrameControls, Gtk::PACK_SHRINK); m_ToolbarControls.set_margin_top(10); m_ToolbarControls.set_margin_bottom(10); m_FrameControls.add(m_ToolbarControls); 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.signal_changed().connect(sigc::mem_fun(*this, &RenamerWindow::on_mode_changed)); 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.signal_changed().connect(sigc::mem_fun(*this, &RenamerWindow::update_preview)); 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_SpinStartNum.set_adjustment(m_AdjStartNum); m_SpinStartNum.signal_value_changed().connect(sigc::mem_fun(*this, &RenamerWindow::update_preview)); m_BoxPattern.pack_start(m_SpinStartNum, Gtk::PACK_SHRINK); 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_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_BoxReplace.pack_start(m_EntryReplace, Gtk::PACK_EXPAND_WIDGET); m_StackModes.add(m_BoxReplace, "Replace"); m_ToolbarControls.pack_start(m_StackModes, Gtk::PACK_EXPAND_WIDGET, 5); m_BtnRename.set_label("Apply New Names"); m_BtnRename.get_style_context()->add_class("suggested-action"); m_BtnRename.signal_clicked().connect(sigc::mem_fun(*this, &RenamerWindow::on_rename_execute)); m_ToolbarControls.pack_end(m_BtnRename, Gtk::PACK_SHRINK, 5); // --- IconView setup --- m_ScrolledWindow.set_policy(Gtk::POLICY_AUTOMATIC, Gtk::POLICY_ALWAYS); m_VBox.pack_start(m_ScrolledWindow); m_RefListStore = Gtk::ListStore::create(m_Columns); 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.pack_start(m_cell_frame, false); m_IconView.add_attribute(m_cell_frame, "pixbuf", m_Columns.m_col_pixbuf); m_IconView.pack_start(m_cell_toggle, false); m_IconView.add_attribute(m_cell_toggle, "active", m_Columns.m_col_checked); m_cell_toggle.property_activatable() = true; m_cell_toggle.signal_toggled().connect(sigc::mem_fun(*this, &RenamerWindow::on_cell_toggled)); m_IconView.pack_start(m_cell_text, false); m_IconView.add_attribute(m_cell_text, "markup", m_Columns.m_col_markup); m_IconView.set_item_width(220); m_IconView.set_columns(-1); m_IconView.set_spacing(20); m_IconView.set_selection_mode(Gtk::SELECTION_NONE); m_IconView.set_reorderable(false); // --- Context Menu Setup (Right Click) --- m_MenuItemSelectAll.set_label("Select All"); m_MenuItemSelectAll.signal_activate().connect([this](){ on_selection_change(true); }); m_MenuPopup.append(m_MenuItemSelectAll); m_MenuItemSelectNone.set_label("Select None"); m_MenuItemSelectNone.signal_activate().connect([this](){ on_selection_change(false); }); m_MenuPopup.append(m_MenuItemSelectNone); m_MenuPopup.append(m_MenuSeparator); m_MenuItemDelete.set_label("Delete Selected Pictures"); auto delete_img = Gtk::manage(new Gtk::Image()); delete_img->set_from_icon_name("user-trash", Gtk::ICON_SIZE_MENU); m_MenuItemDelete.set_image(*delete_img); m_MenuItemDelete.set_always_show_image(true); m_MenuItemDelete.signal_activate().connect(sigc::mem_fun(*this, &RenamerWindow::on_delete_selected)); m_MenuPopup.append(m_MenuItemDelete); m_MenuPopup.show_all(); m_MenuPopup.attach_to_widget(m_IconView); m_IconView.add_events(Gdk::BUTTON_PRESS_MASK); m_IconView.signal_button_press_event().connect(sigc::mem_fun(*this, &RenamerWindow::on_iconview_button_press), false); m_ScrolledWindow.add(m_IconView); m_VBox.pack_start(m_ProgressBar, Gtk::PACK_SHRINK); m_VBox.pack_end(m_Statusbar, Gtk::PACK_SHRINK); std::vector listTargets = { Gtk::TargetEntry("text/uri-list") }; 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)); show_all_children(); m_ProgressBar.hide(); } ~RenamerWindow() { m_stop_flag = true; if (m_WorkerThread.joinable()) m_WorkerThread.join(); } protected: Gtk::Box m_VBox, m_ToolbarTop, m_ToolbarControls, m_BoxPattern, m_BoxReplace; Gtk::Frame m_FrameControls; Gtk::Button m_BtnOpen, m_BtnRename, m_BtnUndo, m_BtnStopReload; Gtk::Entry m_EntryPattern, m_EntryFind, m_EntryReplace; Gtk::ComboBoxText m_ComboSize, m_ComboSort, m_ComboMode; Gtk::SpinButton m_SpinStartNum; Glib::RefPtr m_AdjStartNum; Gtk::Stack m_StackModes; Gtk::ScrolledWindow m_ScrolledWindow; Gtk::IconView m_IconView; Gtk::ProgressBar m_ProgressBar; Gtk::Statusbar m_Statusbar; CellRendererFrame m_cell_frame; Gtk::CellRendererText m_cell_text; Gtk::CellRendererToggle m_cell_toggle; // --- Menu Members --- Gtk::Menu m_MenuPopup; Gtk::MenuItem m_MenuItemSelectAll; Gtk::MenuItem m_MenuItemSelectNone; Gtk::SeparatorMenuItem m_MenuSeparator; Gtk::ImageMenuItem m_MenuItemDelete; 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); } Gtk::TreeModelColumn m_col_path, m_col_filename, m_col_markup, m_col_info_str; Gtk::TreeModelColumn> m_col_pixbuf; Gtk::TreeModelColumn m_col_checked; Gtk::TreeModelColumn m_col_time; } m_Columns; Glib::RefPtr m_RefListStore; std::string m_current_path; std::thread m_WorkerThread; std::atomic m_stop_flag{false}, m_is_loading{false}; std::atomic m_total_files{0}, m_processed_files{0}; Glib::Dispatcher m_Dispatcher; std::mutex m_QueueMutex; std::deque m_ResultQueue; std::stack m_UndoStack; std::string format_size(uintmax_t bytes) { double sz = (double)bytes; const char* units[] = {"B", "KB", "MB", "GB"}; int i = 0; while (sz >= 1024 && i < 3) { sz /= 1024; i++; } std::stringstream ss; ss << std::fixed << std::setprecision(1) << sz << " " << units[i]; return ss.str(); } void on_mode_changed() { m_StackModes.set_visible_child(m_ComboMode.get_active_row_number() == 0 ? "Pattern" : "Replace"); 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_selection_change(bool select_all) { if (!m_RefListStore) return; for (auto row : m_RefListStore->children()) { row[m_Columns.m_col_checked] = select_all; } update_preview(); } bool on_iconview_button_press(GdkEventButton* event) { if (event->type == GDK_BUTTON_PRESS && event->button == 3) { m_MenuPopup.popup(event->button, event->time); return true; } return false; } void on_delete_selected() { int count = 0; for (auto row : m_RefListStore->children()) if (row[m_Columns.m_col_checked]) count++; if (count == 0) return; Gtk::MessageDialog dialog(*this, "Confirm Deletion", false, Gtk::MESSAGE_QUESTION, Gtk::BUTTONS_YES_NO); dialog.set_secondary_text("Are you sure you want to permanently delete " + std::to_string(count) + " selected pictures?"); if (dialog.run() != Gtk::RESPONSE_YES) return; for (auto it = m_RefListStore->children().begin(); it != m_RefListStore->children().end(); ) { if ((*it)[m_Columns.m_col_checked]) { try { fs::remove((std::string)(*it)[m_Columns.m_col_path]); it = m_RefListStore->erase(it); } catch (...) { ++it; } } else ++it; } 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() { if (!m_RefListStore) return; int mode = m_ComboMode.get_active_row_number(); std::string pattern = m_EntryPattern.get_text(); int start_num = m_SpinStartNum.get_value_as_int(); size_t hash_pos = pattern.find('#'); int pad = (hash_pos != std::string::npos) ? (pattern.find_last_of('#') - hash_pos + 1) : 0; int count = 0; for (auto row : m_RefListStore->children()) { 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 (mode == 0 && !pattern.empty()) { 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++; } else if (mode == 1 && !m_EntryFind.get_text().empty()) { 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()); } } } std::stringstream mu; mu << ""; if (new_name != original) mu << "" << Glib::Markup::escape_text(original) << "\n" << Glib::Markup::escape_text(new_name) << ""; else mu << "" << Glib::Markup::escape_text(original) << ""; mu << "\n" << Glib::Markup::escape_text(meta) << ""; row[m_Columns.m_col_markup] = mu.str(); } } void on_rename_execute() { 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()) { if (!row[m_Columns.m_col_checked]) continue; std::string orig = row[m_Columns.m_col_filename], next = orig; if (mode == 0) { 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()) { 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 (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 (step.moves.empty()) return; int ti = 0; std::vector> 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& 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() { if (m_UndoStack.empty()) return; UndoStep last = m_UndoStack.top(); m_UndoStack.pop(); m_BtnUndo.set_sensitive(!m_UndoStack.empty()); int ti = 0; std::vector> 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& s : stage) try { fs::rename(s.first, s.second); } catch(...) {} start_loading_folder(m_current_path); } void on_sort_changed() { if (!m_RefListStore || m_RefListStore->children().empty()) return; int mode = m_ComboSort.get_active_row_number(); 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 { 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); } } Glib::signal_idle().connect_once([this](){ update_preview(); }); } void worker_thread_logic(std::vector 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; 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 { 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 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++; } } m_is_loading = false; m_total_files = 0; m_Dispatcher.emit(); } void on_worker_notification() { std::lock_guard l(m_QueueMutex); while (!m_ResultQueue.empty()) { 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(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) update_preview(); } void on_size_changed() { if (m_RefListStore->children().empty()) return; m_stop_flag = true; if (m_WorkerThread.joinable()) m_WorkerThread.join(); 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); std::vector 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 p) { m_current_path = p; m_stop_flag = true; if (m_WorkerThread.joinable()) m_WorkerThread.join(); m_RefListStore->clear(); std::vector 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); m_IconView.set_item_width(sz); m_stop_flag = false; m_WorkerThread = std::thread(&RenamerWindow::worker_thread_logic, this, f, sz, false); m_BtnStopReload.set_sensitive(true); } void on_open_folder_clicked() { 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); if (d.run() == Gtk::RESPONSE_OK) start_loading_folder(d.get_filename()); } void on_drag_data_received(const Glib::RefPtr& c, int, int, const Gtk::SelectionData& s, guint, guint t) { auto uris = s.get_uris(); if (!uris.empty()) { 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[]) { auto app = Gtk::Application::create(argc, argv, "org.gtkmm.renamer_v03_1"); RenamerWindow window; return app->run(window); }