From 02561be65c89a0ea31b7f75c9f4d48e18c410f29 Mon Sep 17 00:00:00 2001
From: Pierre-Antoine Rouby <pierre-antoine.rouby@inrae.fr>
Date: Fri, 21 Apr 2023 09:31:36 +0200
Subject: [PATCH] geometry: Fix copy/paste command (+ with undo command).

---
 src/Model/Geometry/Reach.py           | 18 ----------------
 src/View/Geometry/GeometryWindow.py   | 30 +++++++++++++++++++++++++--
 src/View/Geometry/ReachUndoCommand.py | 21 +++++++++++++++++++
 src/View/Geometry/qtableview_reach.py | 19 +++++++++++++++++
 4 files changed, 68 insertions(+), 20 deletions(-)

diff --git a/src/Model/Geometry/Reach.py b/src/Model/Geometry/Reach.py
index 0839f928..2040f81a 100644
--- a/src/Model/Geometry/Reach.py
+++ b/src/Model/Geometry/Reach.py
@@ -287,24 +287,6 @@ class Reach:
             )
         )
 
-    # Copy/Paste
-
-    def copy(self, index_list: List[int]):
-        self.__list_copied_profiles.clear()
-        index_list = list(set(index_list))  # delete duplicate index
-
-        for index in index_list:
-            try:
-                self.__list_copied_profiles.append(deepcopy(self.get_profile_i(index)))
-            except IndexError:
-                raise IndexError(f"Invalid profile index: {index}")
-
-    def paste(self):
-        if self.__list_copied_profiles:
-            for profile in self.__list_copied_profiles:
-                self._profiles.append(profile)
-
-
     # Import/Export
 
     def import_geometry(self, file_path_name: str):
diff --git a/src/View/Geometry/GeometryWindow.py b/src/View/Geometry/GeometryWindow.py
index 9d63ec42..4fbd5e1e 100644
--- a/src/View/Geometry/GeometryWindow.py
+++ b/src/View/Geometry/GeometryWindow.py
@@ -6,6 +6,7 @@ import sys
 import csv
 import time
 
+from copy import deepcopy
 from tools import timer
 
 from PyQt5 import QtWidgets
@@ -49,8 +50,10 @@ class GeometryWindow(QMainWindow, WindowToolKit):
 
         self._tablemodel = None
 
+        self._clipboard = None
+
         self.setup_window()
-        self.setup_undo()
+        self.setup_sc()
         self.setup_model()
         self.setup_plots()
         self.setup_connections()
@@ -59,11 +62,13 @@ class GeometryWindow(QMainWindow, WindowToolKit):
     def setup_window(self):
         self.setWindowTitle(f"{self.ui.mainwindow_title} - {self._reach.name}")
 
-    def setup_undo(self):
+    def setup_sc(self):
         self._undo_stack = QUndoStack()
 
         self.undo_sc = QShortcut(QKeySequence.Undo, self)
         self.redo_sc = QShortcut(QKeySequence.Redo, self)
+        self.copy_sc = QShortcut(QKeySequence.Copy, self)
+        self.paste_sc = QShortcut(QKeySequence.Paste, self)
 
     def setup_model(self):
         self._tablemodel = qtableview_reach.PandasModelEditable(
@@ -98,6 +103,8 @@ class GeometryWindow(QMainWindow, WindowToolKit):
 
         self.undo_sc.activated.connect(self.undo)
         self.redo_sc.activated.connect(self.redo)
+        self.copy_sc.activated.connect(self.copy)
+        self.paste_sc.activated.connect(self.paste)
 
         # Profile selection when line change in table
         self.tableView.selectionModel()\
@@ -408,6 +415,25 @@ class GeometryWindow(QMainWindow, WindowToolKit):
         self._tablemodel.move_row_up(row)
         self.select_current_profile()
 
+    def copy(self):
+        rows = self.tableView\
+                   .selectionModel()\
+                   .selectedRows()
+
+        self._clipboard = []
+
+        for row in rows:
+            self._clipboard.append(
+                deepcopy(
+                    self._reach.profile(row.row())
+                )
+            )
+
+    def paste(self):
+        row = self.index_selected_row()
+        self._tablemodel.paste(row, self._clipboard)
+        self.select_current_profile()
+
     def undo(self):
         self._tablemodel.undo()
         self.select_current_profile()
diff --git a/src/View/Geometry/ReachUndoCommand.py b/src/View/Geometry/ReachUndoCommand.py
index 7aacde6f..e3e2191f 100644
--- a/src/View/Geometry/ReachUndoCommand.py
+++ b/src/View/Geometry/ReachUndoCommand.py
@@ -104,3 +104,24 @@ class MoveCommand(QUndoCommand):
             self._reach.move_up_profile(self._i)
         else:
             self._reach.move_down_profile(self._i)
+
+
+class PasteCommand(QUndoCommand):
+    def __init__(self, reach, row, profiles):
+        QUndoCommand.__init__(self)
+
+        self._reach = reach
+        self._row = row
+        self._profiles = profiles
+
+    def undo(self):
+        for ind in range(len(self._profiles)):
+            self._reach.delete(self._row)
+
+    def redo(self):
+        self._profiles.reverse()
+
+        for pro in self._profiles:
+            self._reach.insert_profile(self._row, pro)
+
+        self._profiles.reverse()
diff --git a/src/View/Geometry/qtableview_reach.py b/src/View/Geometry/qtableview_reach.py
index b965d21b..07f4c0fd 100644
--- a/src/View/Geometry/qtableview_reach.py
+++ b/src/View/Geometry/qtableview_reach.py
@@ -211,6 +211,25 @@ class PandasModelEditable(QAbstractTableModel):
         self.endMoveRows()
         self.layoutChanged.emit()
 
+    def paste(self, row, profiles):
+        if row > self._reach.number_profiles:
+            return
+
+        if len(profiles) == 0:
+            return
+
+        self.layoutAboutToBeChanged.emit()
+
+        self._undo_stack.push(
+            PasteCommand(
+                self._reach, row, profiles
+            )
+        )
+
+        self.layoutAboutToBeChanged.emit()
+        self.layoutChanged.emit()
+
+
     def undo(self):
         self._undo_stack.undo()
         self.layoutChanged.emit()
-- 
GitLab