Browse Source

bug correction and improvements

Nicole Portas 1 tháng trước cách đây
mục cha
commit
185c8cdee3
1 tập tin đã thay đổi với 63 bổ sung30 xóa
  1. 63 30
      src/main.cpp

+ 63 - 30
src/main.cpp

@@ -1,4 +1,5 @@
 #include <gtkmm.h>
+#include <giomm.h> 
 #include <iostream>
 #include <filesystem>
 #include <vector>
@@ -11,6 +12,7 @@
 #include <stack>
 #include <iomanip>
 #include <sstream>
+#include <set>
 
 namespace fs = std::filesystem;
 
@@ -87,7 +89,6 @@ public:
     RenamerWindow() : m_Dispatcher() {
         set_title("Simple Image Renamer");
         set_default_size(1280, 850);
-        
         set_wmclass("simpleimagerenamer", "simpleimagerenamer");
 
         m_Dispatcher.connect(sigc::mem_fun(*this, &RenamerWindow::on_worker_notification));
@@ -121,7 +122,7 @@ public:
         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_label("Undo");
         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);
@@ -188,6 +189,7 @@ public:
         
         m_IconView.set_selection_mode(Gtk::SELECTION_NONE);
         m_IconView.set_reorderable(false);
+        m_IconView.signal_item_activated().connect(sigc::mem_fun(*this, &RenamerWindow::on_item_activated));
 
         // --- Context Menu Setup (Right Click) ---
         m_MenuItemSelectAll.set_label("Select All");
@@ -200,7 +202,7 @@ public:
 
         m_MenuPopup.append(m_MenuSeparator);
 
-        m_MenuItemDelete.set_label("Delete Selected Pictures");
+        m_MenuItemDelete.set_label("Move to Trash");
         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);
@@ -218,10 +220,14 @@ public:
         m_VBox.pack_start(m_ProgressBar, Gtk::PACK_SHRINK);
         m_VBox.pack_end(m_Statusbar, Gtk::PACK_SHRINK);
 
+        // --- Drag and Drop ---
         std::vector<Gtk::TargetEntry> 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));
 
+        // --- Keyboard Shortcuts (The "Hidden" Cockpit Controls) ---
+        this->signal_key_press_event().connect(sigc::mem_fun(*this, &RenamerWindow::on_window_key_press), false);
+
         show_all_children();
         m_ProgressBar.hide();
     }
@@ -245,7 +251,6 @@ protected:
     Gtk::CellRendererText m_cell_text;
     Gtk::CellRendererToggle m_cell_toggle;
 
-    // --- Menu Members ---
     Gtk::Menu m_MenuPopup;
     Gtk::MenuItem m_MenuItemSelectAll;
     Gtk::MenuItem m_MenuItemSelectNone;
@@ -270,6 +275,30 @@ protected:
     std::deque<LoadedItem> m_ResultQueue;
     std::stack<UndoStep> m_UndoStack;
 
+    // --- Key Press Handler ---
+    bool on_window_key_press(GdkEventKey* event) {
+        if ((event->state & GDK_CONTROL_MASK)) {
+            if (event->keyval == GDK_KEY_o || event->keyval == GDK_KEY_O) { on_open_folder_clicked(); return true; }
+            if (event->keyval == GDK_KEY_z || event->keyval == GDK_KEY_Z) { on_undo_clicked(); return true; }
+        }
+        if (event->keyval == GDK_KEY_Delete) { on_delete_selected(); return true; }
+        if (event->keyval == GDK_KEY_Return || event->keyval == GDK_KEY_KP_Enter) { on_rename_execute(); return true; }
+        return false;
+    }
+
+    // --- Quick View (Double Click) ---
+    void on_item_activated(const Gtk::TreeModel::Path& path) {
+        auto it = m_RefListStore->get_iter(path);
+        if (it) {
+            std::string file_path = (*it)[m_Columns.m_col_path];
+            try {
+                Gio::AppInfo::launch_default_for_uri("file://" + file_path);
+            } catch (const Glib::Error& ex) {
+                std::cerr << "Failed to open image: " << ex.what() << std::endl;
+            }
+        }
+    }
+
     std::string format_size(uintmax_t bytes) {
         double sz = (double)bytes;
         const char* units[] = {"B", "KB", "MB", "GB"};
@@ -309,15 +338,17 @@ protected:
         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?");
+        Gtk::MessageDialog dialog(*this, "Move to Trash?", false, Gtk::MESSAGE_QUESTION, Gtk::BUTTONS_YES_NO);
+        dialog.set_secondary_text("Move " + std::to_string(count) + " selected pictures to the system trash?");
         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);
+                    auto file = Gio::File::create_for_path((std::string)(*it)[m_Columns.m_col_path]);
+                    if (file->trash()) {
+                        it = m_RefListStore->erase(it);
+                    } else { ++it; }
                 } catch (...) { ++it; }
             } else ++it;
         }
@@ -337,8 +368,14 @@ protected:
         size_t hash_pos = pattern.find('#');
         int pad = (hash_pos != std::string::npos) ? (pattern.find_last_of('#') - hash_pos + 1) : 0;
         int count = 0;
+
+        std::set<std::string> existing_names;
+        for (auto row : m_RefListStore->children()) existing_names.insert((std::string)row[m_Columns.m_col_filename]);
+
         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;
+            bool conflict = false;
+
             if (row[m_Columns.m_col_checked]) {
                 if (mode == 0 && !pattern.empty()) {
                     std::string num = std::to_string(start_num + count);
@@ -352,10 +389,15 @@ protected:
                     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 (new_name != original && existing_names.count(new_name)) conflict = true;
             }
+
             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>";
+            if (new_name != original) {
+                mu << "<span size='small' alpha='60%'>" << Glib::Markup::escape_text(original) << "</span>\n";
+                if (conflict) mu << "<span foreground='red' weight='bold'>CONFLICT: " << Glib::Markup::escape_text(new_name) << "</span>";
+                else mu << "<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();
         }
@@ -381,29 +423,23 @@ protected:
                 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 (next != orig) {
+                if (fs::exists(fs::path((std::string)row[m_Columns.m_col_path]).parent_path() / next)) {
+                    std::cerr << "Rename skipped: Conflict with " << next << std::endl;
+                    continue;
+                }
+                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<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& s : stage) try { fs::rename(s.first, s.second); } catch (...) {}
+        for (auto& m : step.moves) try { fs::rename(m.original_path, m.new_path); } 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<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& s : stage) try { fs::rename(s.first, s.second); } catch(...) {}
+        for (const auto& m : last.moves) try { if (fs::exists(m.new_path)) fs::rename(m.new_path, m.original_path); } catch(...) {}
         start_loading_folder(m_current_path);
     }
 
@@ -455,7 +491,6 @@ protected:
                 }
                 { 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++; }
         }
         m_is_loading = false; m_total_files = 0; m_Dispatcher.emit();
@@ -482,10 +517,9 @@ protected:
         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);
+            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();
@@ -516,7 +550,6 @@ protected:
         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() {
@@ -536,7 +569,7 @@ protected:
 };
 
 int main(int argc, char *argv[]) {
-    auto app = Gtk::Application::create(argc, argv, "org.gtkmm.renamer_v03_1");
+    auto app = Gtk::Application::create(argc, argv, "org.gtkmm.renamer");
     RenamerWindow window;
     return app->run(window);
 }