diff --git a/src/View/About/Window.py b/src/View/About/Window.py
index 7c269aaf17155517136c4bf6f83a39c557b36301..218869b5fc7d87595dcadc8b018660f6edfcb8e3 100644
--- a/src/View/About/Window.py
+++ b/src/View/About/Window.py
@@ -19,14 +19,17 @@
 import os
 import logging
 
-from View.ASubWindow import ASubWindow
+from View.Tools.PamhyrWindow import PamhyrDialog
 
 from PyQt5.QtCore import QCoreApplication
 
 _translate = QCoreApplication.translate
 logger = logging.getLogger()
 
-class AboutWindow(ASubWindow):
+class AboutWindow(PamhyrDialog):
+    _pamhyr_ui = "about"
+    _pamhyr_name = "About"
+
     def _path_file(self, filename):
         return os.path.abspath(
             os.path.join(
@@ -35,10 +38,14 @@ class AboutWindow(ASubWindow):
             )
         )
 
-
-    def __init__(self, title="About", parent=None):
-        super(AboutWindow, self).__init__(name=title, ui="about", parent=parent)
-        self.ui.setWindowTitle(title)
+    def __init__(self, study = None, config = None, parent=None):
+        super(AboutWindow, self).__init__(
+            title = _translate("About", "About"),
+            study = study,
+            config = config,
+            options = [],
+            parent = parent
+        )
 
         # Version
         with open(self._path_file("VERSION"), "r") as f:
diff --git a/src/View/Configure/Solver/Window.py b/src/View/Configure/Solver/Window.py
index c295481f1e46ecd870137db6c3d8a29a4466d38c..369ff40ad28272803d4f280344a951ff47990b2c 100644
--- a/src/View/Configure/Solver/Window.py
+++ b/src/View/Configure/Solver/Window.py
@@ -16,7 +16,7 @@
 
 # -*- coding: utf-8 -*-
 
-from View.ASubWindow import ASubWindow
+from View.Tools.PamhyrWindow import PamhyrDialog
 from Solver.Solvers import solver_type_list
 from Solver.GenericSolver import GenericSolver
 
@@ -24,12 +24,22 @@ from PyQt5.QtWidgets import (
     QPushButton,
 )
 
-class ConfigureSolverWindow(ASubWindow):
-    def __init__(self, data=None, title="Configuration : Add Solver", parent=None):
+class ConfigureSolverWindow(PamhyrDialog):
+    _pamhyr_ui = "ConfigureAddSolverDialog"
+    _pamhyr_name = "Add/Edit Solver"
+
+    def __init__(self, data=None, config=None, parent=None):
+        if data is not None:
+            name = f"Edit Solver - {data.name}"
+        else:
+            name = "Add a new Solver"
+
         super(ConfigureSolverWindow, self).__init__(
-            name=title, ui="ConfigureAddSolverDialog", parent=parent
+            title = name,
+            config = config,
+            options = [],
+            parent=parent
         )
-        self.ui.setWindowTitle(title)
 
         # Combo box item
         for solver in solver_type_list:
@@ -37,10 +47,10 @@ class ConfigureSolverWindow(ASubWindow):
 
         # Data to return
         self.data = data
-        if not self.data is None:
+        if self.data is not None:
             self.copy_data()
 
-        self.connect()
+        self.setup_connection()
 
     def copy_data(self):
         self.set_combobox_text("comboBox_solver", self.data.type)
@@ -53,7 +63,7 @@ class ConfigureSolverWindow(ASubWindow):
         self.set_line_edit_text("lineEdit_output", self.data._path_output)
         self.set_line_edit_text("lineEdit_output_cmd", self.data._cmd_output)
 
-    def connect(self):
+    def setup_connection(self):
         # File button
         buttons = {
             "pushButton_input": (lambda: self.file_dialog(
diff --git a/src/View/Configure/Window.py b/src/View/Configure/Window.py
index 23a371ac98d702e1a435098b47e05a6d046bcf8a..83db81f6bfe0cad93deca88ff2698b9a7a1516db 100644
--- a/src/View/Configure/Window.py
+++ b/src/View/Configure/Window.py
@@ -21,8 +21,7 @@ import logging
 from copy import deepcopy
 
 from config import Config
-from View.ASubWindow import ASubWindow
-from View.ListedSubWindow import ListedSubWindow
+from View.Tools.PamhyrWindow import PamhyrDialog
 
 from View.Stricklers.Table import TableModel
 from View.Stricklers.translate import *
@@ -95,50 +94,42 @@ class SolverTableModel(QAbstractTableModel):
         self.layoutChanged.emit()
 
 
-class ConfigureWindow(ASubWindow, ListedSubWindow):
-    def __init__(self, conf=None, title="Configure", parent=None):
+class ConfigureWindow(PamhyrDialog):
+    _pamhyr_ui = "ConfigureDialog"
+    _pamhyr_name = "Configure"
+
+    def __init__(self, config=None, parent=None):
+        if config is None:
+            config = Config()
+
         super(ConfigureWindow, self).__init__(
-            name=title, ui="ConfigureDialog", parent=parent
+            title = self._pamhyr_name,
+            config = config,
+            options = [],
+            parent=parent
         )
-        self.ui.setWindowTitle(title)
 
-        if conf is None:
-            self.conf = Config()
-        else:
-            self.conf = conf
+        self.setup_custom_sc()
+        self.setup_solver()
+        self.setup_stricklers()
+        self.setup_data()
+        self.setup_connection()
 
-        self.setup_sc()
-
-        # Solver
+    def setup_solver(self):
         table = self.find(QTableView, "tableView_solver")
         self.solver_table_model = SolverTableModel(
             headers = ["name", "type", "description"],
-            rows = conf.solvers
+            rows = self._config.solvers
         )
         table.setModel(self.solver_table_model)
         table.setSelectionBehavior(QAbstractItemView.SelectRows)
         table.horizontalHeader().setSectionResizeMode(QHeaderView.Stretch)
         table.setAlternatingRowColors(True)
+        table.resizeColumnsToContents()
 
-        # Meshing_Tool
-        self.set_line_edit_text("lineEdit_meshing_tool", self.conf.meshing_tool)
-
-        # Const
-        self.set_line_edit_text("lineEdit_segment", str(self.conf.segment))
-        self.set_line_edit_text("lineEdit_max_listing", str(self.conf.max_listing))
-
-        # Backup
-        self.set_check_box("checkBox_backup", self.conf.backup_enable)
-        self.set_line_edit_text("lineEdit_backup_path", self.conf.backup_path)
-        self.set_time_edit("timeEdit_backup_frequence", self.conf.backup_frequence)
-        self.set_spin_box("spinBox_backup_max", self.conf.backup_max)
-
-        self.find(QTableView, "tableView_solver").resizeColumnsToContents()
-        self.connect()
-
-        # Stricklers
+    def setup_stricklers(self):
         table = self.find(QTableView, f"tableView_stricklers")
-        self._stricklers = deepcopy(self.conf.stricklers)
+        self._stricklers = deepcopy(self._config.stricklers)
         self._stricklers_table = TableModel(
             data = self._stricklers,
             undo = self._undo_stack,
@@ -148,26 +139,35 @@ class ConfigureWindow(ASubWindow, ListedSubWindow):
         table.horizontalHeader().setSectionResizeMode(QHeaderView.Stretch)
         table.setAlternatingRowColors(True)
 
+    def setup_data(self):
+        # Meshing_Tool
+        self.set_line_edit_text("lineEdit_meshing_tool", self._config.meshing_tool)
+
+        # Const
+        self.set_line_edit_text("lineEdit_segment", str(self._config.segment))
+        self.set_line_edit_text("lineEdit_max_listing", str(self._config.max_listing))
+
+        # Backup
+        self.set_check_box("checkBox_backup", self._config.backup_enable)
+        self.set_line_edit_text("lineEdit_backup_path", self._config.backup_path)
+        self.set_time_edit("timeEdit_backup_frequence", self._config.backup_frequence)
+        self.set_spin_box("spinBox_backup_max", self._config.backup_max)
+
         # Editor
-        self.set_line_edit_text("lineEdit_editor_cmd", str(self.conf.editor))
+        self.set_line_edit_text("lineEdit_editor_cmd", str(self._config.editor))
 
         # Language
         languages = Config.languages()
         for lang in languages:
             self.combobox_add_item("comboBox_language", lang)
-            if self.conf.lang == languages[lang]:
+            if self._config.lang == languages[lang]:
                 self.set_combobox_text("comboBox_language", lang)
 
-    def setup_sc(self):
-        self._undo_stack = QUndoStack()
+    def setup_custom_sc(self):
+        self._debug_sc = QShortcut(QKeySequence("Ctrl+G"), self)
+        self._debug_sc.activated.connect(self.set_debug)
 
-        self.debug_sc = QShortcut(QKeySequence("Ctrl+G"), self)
-        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 connect(self):
+    def setup_connection(self):
         buttons = {
             # Solvers
             "pushButton_solver_add": self.add_solver,
@@ -195,33 +195,31 @@ class ConfigureWindow(ASubWindow, ListedSubWindow):
         for button in buttons:
             self.find(QPushButton, button).clicked.connect(buttons[button])
 
-        self.debug_sc.activated.connect(self.set_debug)
-
     def accept(self):
         # Solvers
-        self.conf.solvers = self.solver_table_model.rows.copy()
+        self._config.solvers = self.solver_table_model.rows.copy()
 
         # Meshing_Tool
-        self.conf.meshing_tool = self.get_line_edit_text("lineEdit_meshing_tool")
+        self._config.meshing_tool = self.get_line_edit_text("lineEdit_meshing_tool")
 
         # Const
-        self.conf.segment = self.get_line_edit_text("lineEdit_segment")
-        self.conf.max_listing = self.get_line_edit_text("lineEdit_max_listing")
+        self._config.segment = self.get_line_edit_text("lineEdit_segment")
+        self._config.max_listing = self.get_line_edit_text("lineEdit_max_listing")
 
         # Backup
-        self.conf.backup_enable = self.get_check_box("checkBox_backup")
-        self.conf.backup_path = self.get_line_edit_text("lineEdit_backup_path")
-        self.conf.backup_frequence = self.get_time_edit("timeEdit_backup_frequence")
-        self.conf.backup_max = self.get_spin_box("spinBox_backup_max")
+        self._config.backup_enable = self.get_check_box("checkBox_backup")
+        self._config.backup_path = self.get_line_edit_text("lineEdit_backup_path")
+        self._config.backup_frequence = self.get_time_edit("timeEdit_backup_frequence")
+        self._config.backup_max = self.get_spin_box("spinBox_backup_max")
 
         # Stricklers
-        self.conf.stricklers = deepcopy(self._stricklers)
+        self._config.stricklers = deepcopy(self._stricklers)
 
         # Editor
-        self.conf.editor = self.get_line_edit_text("lineEdit_editor_cmd")
+        self._config.editor = self.get_line_edit_text("lineEdit_editor_cmd")
 
         # Language
-        self.conf.lang = Config.languages()[self.get_combobox_text("comboBox_language")]
+        self._config.lang = Config.languages()[self.get_combobox_text("comboBox_language")]
 
         self.end()
 
@@ -230,13 +228,13 @@ class ConfigureWindow(ASubWindow, ListedSubWindow):
         self.end()
 
     def end(self):
-        self.conf.save()
+        self._config.save()
         self.close()
 
     # Debug
     def set_debug(self):
-        self.conf.debug = not self.conf.debug
-        logger.info(f"Debug mode set : {self.conf.debug}")
+        self._config.debug = not self._config.debug
+        logger.info(f"Debug mode set : {self._config.debug}")
         self.parent.setup_debug_mode()
 
     # Solvers
@@ -246,6 +244,7 @@ class ConfigureWindow(ASubWindow, ListedSubWindow):
         for index in indexes:
             self.edit_solver = ConfigureSolverWindow(
                 data=self.solver_table_model.rows[index.row()],
+                config=self._config,
                 parent=self
             )
             if self.edit_solver.exec_():
diff --git a/src/View/DummyWindow.py b/src/View/DummyWindow.py
index 05117bca15659184948745eac437321b3a282d8b..768d6fbc7ce67427df18ed3671c13009f6d3132a 100644
--- a/src/View/DummyWindow.py
+++ b/src/View/DummyWindow.py
@@ -16,9 +16,14 @@
 
 # -*- coding: utf-8 -*-
 
-from View.ASubWindow import ASubWindow
+from View.Tools.PamhyrWindow import PamhyrWindow
 
-class DummyWindow(ASubWindow):
-    def __init__(self, title="Dummy", parent=None):
-        super(DummyWindow, self).__init__(name=title, ui="dummy", parent=parent)
-        self.ui.setWindowTitle(title)
+class DummyWindow(PamhyrWindow):
+    __ui = "Dummy"
+    __name = "Pamhyr Dummy Window"
+
+    def __init__(self, parent=None):
+        super(DummyWindow, self).__init__(
+            title = self.__name,
+            parent = parent,
+        )
diff --git a/src/View/MainWindow.py b/src/View/MainWindow.py
index 47e148508b1d80c3877b5bebfd9bb8995da15e91..cf253dd5d4408f954988c7604cffbce7f58b8328 100644
--- a/src/View/MainWindow.py
+++ b/src/View/MainWindow.py
@@ -36,27 +36,27 @@ from PyQt5.QtWidgets import (
 )
 from PyQt5.uic import loadUi
 
-from View.ASubWindow import WindowToolKit
-from View.ListedSubWindow import ListedSubWindow
+from View.Tools.ASubWindow import WindowToolKit
+from View.Tools.ListedSubWindow import ListedSubWindow
 from View.DummyWindow import DummyWindow
 
 from View.Configure.Window import ConfigureWindow
 from View.Study.Window import NewStudyWindow
 from View.About.Window import AboutWindow
 from View.Network.Window import NetworkWindow
-from View.Geometry.Window import GeometryWindow
-from View.BoundaryCondition.Window import BoundaryConditionWindow
-from View.LateralContribution.Window import LateralContributionWindow
-from View.InitialConditions.Window import InitialConditionsWindow
-from View.Stricklers.Window import StricklersWindow
-from View.Frictions.Window import FrictionsWindow
-from View.SedimentLayers.Window import SedimentLayersWindow
-from View.SedimentLayers.Reach.Window import ReachSedimentLayersWindow
-from View.SolverParameters.Window import SolverParametersWindow
-from View.RunSolver.Window import SelectSolverWindow, SolverLogWindow
-from View.CheckList.Window import CheckListWindow
-from View.Results.Window import ResultsWindow
-from View.Debug.Window import ReplWindow
+# from View.Geometry.Window import GeometryWindow
+# from View.BoundaryCondition.Window import BoundaryConditionWindow
+# from View.LateralContribution.Window import LateralContributionWindow
+# from View.InitialConditions.Window import InitialConditionsWindow
+# from View.Stricklers.Window import StricklersWindow
+# from View.Frictions.Window import FrictionsWindow
+# from View.SedimentLayers.Window import SedimentLayersWindow
+# from View.SedimentLayers.Reach.Window import ReachSedimentLayersWindow
+# from View.SolverParameters.Window import SolverParametersWindow
+# from View.RunSolver.Window import SelectSolverWindow, SolverLogWindow
+# from View.CheckList.Window import CheckListWindow
+# from View.Results.Window import ResultsWindow
+# from View.Debug.Window import ReplWindow
 
 from Model.Study import Study
 
@@ -108,7 +108,7 @@ class ApplicationWindow(QMainWindow, ListedSubWindow, WindowToolKit):
         self.conf = conf
 
         # Model
-        self.model = None
+        self._study = None
 
         # Results
         self._last_results = None
@@ -133,8 +133,8 @@ class ApplicationWindow(QMainWindow, ListedSubWindow, WindowToolKit):
     def set_title(self):
         title = "(dbg) " if self.conf.debug else ""
 
-        if self.model is not None:
-            title += f"Pamhyr2 - {self.model.name}"
+        if self._study is not None:
+            title += f"Pamhyr2 - {self._study.name}"
             self.setWindowTitle(title)
         else:
             title += "Pamhyr2"
@@ -216,7 +216,7 @@ class ApplicationWindow(QMainWindow, ListedSubWindow, WindowToolKit):
         super(ApplicationWindow, self).changeEvent(event)
 
     def close(self):
-        if self.model is not None and not self.model.is_saved:
+        if self._study is not None and not self._study.is_saved:
             self._close_question = True
             if self.dialog_close():
                 # PAMHYR is close correctly (no crash)
@@ -233,7 +233,7 @@ class ApplicationWindow(QMainWindow, ListedSubWindow, WindowToolKit):
 
     def closeEvent(self, event):
         if not self._close_question:
-            if self.model is not None and not self.model.is_saved:
+            if self._study is not None and not self._study.is_saved:
                 if self.dialog_close(cancel = False):
                     # PAMHYR is close correctly (no crash)
                     self.conf.set_close_correctly()
@@ -291,16 +291,16 @@ class ApplicationWindow(QMainWindow, ListedSubWindow, WindowToolKit):
     #########
 
     def get_model(self):
-        return self.model
+        return self._study
 
     def set_model(self, model):
-        self.model = model
+        self._study = model
         self.update_enable_action()
-        self.conf.set_last_study(self.model.filename)
+        self.conf.set_last_study(self._study.filename)
         self.set_title()
 
     def close_model(self):
-        self.model = None
+        self._study = None
         self.update_enable_action()
         self.conf.set_close_correctly()
         self.set_title()
@@ -314,7 +314,7 @@ class ApplicationWindow(QMainWindow, ListedSubWindow, WindowToolKit):
         Returns:
             Nothing
         """
-        no_model = self.model is None
+        no_model = self._study is None
 
         for action in no_model_action:
             self.enable_actions(action, no_model)
@@ -342,7 +342,7 @@ class ApplicationWindow(QMainWindow, ListedSubWindow, WindowToolKit):
             Nothing
         """
         self.set_model(Study.open(filename))
-        logger.info(f"Open Study - {self.model.name}")
+        logger.info(f"Open Study - {self._study.name}")
         self.set_title()
 
     def save_study(self):
@@ -354,22 +354,22 @@ class ApplicationWindow(QMainWindow, ListedSubWindow, WindowToolKit):
         Returns:
             Nothing
         """
-        if self.model.filename is None or self.model.filename == "":
+        if self._study.filename is None or self._study.filename == "":
             file_name, _ = QFileDialog.getSaveFileName(
                 self, "Save File",
                 "", "Pamhyr(*.pamhyr)"
             )
 
             if file_name.rsplit(".", 1)[-1] == "pamhyr":
-                self.model.filename = file_name
+                self._study.filename = file_name
             else:
-                self.model.filename = file_name + ".pamhyr"
+                self._study.filename = file_name + ".pamhyr"
 
-        if self.model.is_saved:
+        if self._study.is_saved:
             return
 
         logger.info("Save...")
-        self.model.save()
+        self._study.save()
 
     def save_as_study(self):
         """Save current study as new file
@@ -386,11 +386,11 @@ class ApplicationWindow(QMainWindow, ListedSubWindow, WindowToolKit):
         )
 
         if file_name[-4:] == ".pamhyr":
-            self.model.filename = file_name
+            self._study.filename = file_name
         else:
-            self.model.filename = file_name + ".pamhyr"
+            self._study.filename = file_name + ".pamhyr"
 
-        self.model.save()
+        self._study.save()
 
     ##################
     # MSG AND DIALOG #
@@ -456,7 +456,7 @@ class ApplicationWindow(QMainWindow, ListedSubWindow, WindowToolKit):
         Returns:
             Nothing
         """
-        self.config = ConfigureWindow(conf=self.conf, parent=self)
+        self.config = ConfigureWindow(config=self.conf, parent=self)
         self.config.show()
 
     def open_about(self):
@@ -476,7 +476,7 @@ class ApplicationWindow(QMainWindow, ListedSubWindow, WindowToolKit):
         Returns:
             Nothing
         """
-        if self.model is None:
+        if self._study is None:
             dialog = QFileDialog(self)
             dialog.setFileMode(QFileDialog.FileMode.ExistingFile)
             dialog.setDefaultSuffix(".pamhyr")
@@ -494,7 +494,7 @@ class ApplicationWindow(QMainWindow, ListedSubWindow, WindowToolKit):
         Returns:
             Nothing
         """
-        if self.model is None:
+        if self._study is None:
             self.new_study = NewStudyWindow(parent=self)
             self.new_study.show()
 
@@ -504,8 +504,8 @@ class ApplicationWindow(QMainWindow, ListedSubWindow, WindowToolKit):
         Returns:
             Nothing
         """
-        if not self.model is None:
-            self.new_study = NewStudyWindow(study=self.model, parent=self)
+        if not self._study is None:
+            self.new_study = NewStudyWindow(study=self._study, parent=self)
             self.new_study.show()
 
     def open_network(self):
@@ -514,9 +514,9 @@ class ApplicationWindow(QMainWindow, ListedSubWindow, WindowToolKit):
         Returns:
             Nothing
         """
-        if self.model is not None:
+        if self._study is not None:
             if not self.sub_win_exists("River network"):
-                self.network = NetworkWindow(model=self.model, parent=self)
+                self.network = NetworkWindow(study=self._study, parent=self)
                 self.network.show()
             else:
                 self.network.activateWindow()
@@ -527,14 +527,14 @@ class ApplicationWindow(QMainWindow, ListedSubWindow, WindowToolKit):
         Returns:
             Nothing
         """
-        if (self.model is not None and self.model.river.has_current_reach()):
+        if (self._study is not None and self._study.river.has_current_reach()):
             geometry = self.sub_win_filter_first(
                 "Geometry",
-                contain = [self.model.river.current_reach().name]
+                contain = [self._study.river.current_reach().name]
             )
 
             if geometry is None:
-                geometry = GeometryWindow(model=self.model, parent=self)
+                geometry = GeometryWindow(model=self._study, parent=self)
                 geometry.show()
             else:
                 geometry.activateWindow()
@@ -548,7 +548,7 @@ class ApplicationWindow(QMainWindow, ListedSubWindow, WindowToolKit):
         )
 
         if bound is None:
-            bound = BoundaryConditionWindow(study = self.model, parent = self)
+            bound = BoundaryConditionWindow(study = self._study, parent = self)
             bound.show()
         else:
             bound.activateWindow()
@@ -560,7 +560,7 @@ class ApplicationWindow(QMainWindow, ListedSubWindow, WindowToolKit):
         )
 
         if lateral is None:
-            lateral = LateralContributionWindow(study = self.model, parent = self)
+            lateral = LateralContributionWindow(study = self._study, parent = self)
             lateral.show()
         else:
             lateral.activateWindow()
@@ -573,7 +573,7 @@ class ApplicationWindow(QMainWindow, ListedSubWindow, WindowToolKit):
 
         if strick is None:
             strick = StricklersWindow(
-                study = self.model,
+                study = self._study,
                 config = self.conf,
                 parent = self
             )
@@ -582,17 +582,17 @@ class ApplicationWindow(QMainWindow, ListedSubWindow, WindowToolKit):
             strick.activateWindow()
 
     def open_frictions(self):
-        if (self.model is not None and
-            self.model.river.has_current_reach()):
+        if (self._study is not None and
+            self._study.river.has_current_reach()):
 
             frictions = self.sub_win_filter_first(
                 "Frictions",
-                contain = [self.model.river.current_reach().name]
+                contain = [self._study.river.current_reach().name]
             )
 
             if frictions is None:
                 frictions = FrictionsWindow(
-                    study = self.model,
+                    study = self._study,
                     parent = self
                 )
                 frictions.show()
@@ -602,15 +602,15 @@ class ApplicationWindow(QMainWindow, ListedSubWindow, WindowToolKit):
             self.msg_select_reach()
 
     def open_initial_conditions(self):
-        if self.model.river.has_current_reach():
+        if self._study.river.has_current_reach():
             initial = self.sub_win_filter_first(
                 "Initial condition",
-                contain = [self.model.river.current_reach().name]
+                contain = [self._study.river.current_reach().name]
             )
 
             if initial is None:
                 initial = InitialConditionsWindow(
-                    study = self.model,
+                    study = self._study,
                     parent = self
                 )
                 initial.show()
@@ -627,7 +627,7 @@ class ApplicationWindow(QMainWindow, ListedSubWindow, WindowToolKit):
 
         if params is None:
             params = SolverParametersWindow(
-                study = self.model,
+                study = self._study,
                 parent = self
             )
             params.show()
@@ -636,31 +636,31 @@ class ApplicationWindow(QMainWindow, ListedSubWindow, WindowToolKit):
 
     def open_sediment_layers(self):
         sl = SedimentLayersWindow(
-            study = self.model,
+            study = self._study,
             parent = self
         )
         sl.show()
 
     def open_reach_sediment_layers(self):
         sl = ReachSedimentLayersWindow(
-            study = self.model,
+            study = self._study,
             parent = self
         )
         sl.show()
 
     def run_solver(self):
-        if self.model is None:
+        if self._study is None:
             return
 
         run = SelectSolverWindow(
-            study = self.model,
+            study = self._study,
             config = self.conf,
             parent = self
         )
         if run.exec():
             solver = run.solver
             check = CheckListWindow(
-                study = self.model,
+                study = self._study,
                 config = self.conf,
                 solver = solver,
                 parent = self
@@ -669,7 +669,7 @@ class ApplicationWindow(QMainWindow, ListedSubWindow, WindowToolKit):
 
     def solver_log(self, solver):
         sol = SolverLogWindow(
-            study = self.model,
+            study = self._study,
             config = self.conf,
             solver = solver,
             parent = self
@@ -693,7 +693,7 @@ class ApplicationWindow(QMainWindow, ListedSubWindow, WindowToolKit):
 
         if res is None:
             res = ResultsWindow(
-                study = self.model,
+                study = self._study,
                 solver = solver,
                 results = results,
                 parent = self
@@ -708,18 +708,18 @@ class ApplicationWindow(QMainWindow, ListedSubWindow, WindowToolKit):
 
     def open_debug(self):
         repl = ReplWindow(
-            study = self.model,
+            study = self._study,
             config = self.conf,
             parent = self
         )
         repl.show()
 
     def open_sqlite(self):
-        if self.model is None:
+        if self._study is None:
             logger.debug("No study open for sql debuging...")
             return
 
-        file = self.model.filename
+        file = self._study.filename
         _ = subprocess.Popen(
             f"sqlitebrowser {file}",
             shell=True
diff --git a/src/View/Network/Table.py b/src/View/Network/Table.py
index 733a910d5e8194c62cf0928aa7a5fdc4b6fc363f..da74d8135d65275889c18e5e6d914835240e8d2e 100644
--- a/src/View/Network/Table.py
+++ b/src/View/Network/Table.py
@@ -22,7 +22,6 @@ import traceback
 from Model.Network.Node import Node
 from Model.Network.Edge import Edge
 from Model.Network.Graph import Graph
-from View.ASubWindow import ASubWindow
 from View.Network.GraphWidget import GraphWidget
 from View.Network.UndoCommand import *
 from View.Tools.PamhyrTable import PamhyrTableModel
diff --git a/src/View/Network/Window.py b/src/View/Network/Window.py
index 95e575ea8fd94d6c1b547ef55ef662e264535a30..a54c0527b4a1a57a976f78e02fb1360020977831 100644
--- a/src/View/Network/Window.py
+++ b/src/View/Network/Window.py
@@ -16,6 +16,8 @@
 
 # -*- coding: utf-8 -*-
 
+import logging
+
 from PyQt5.QtCore import QCoreApplication
 
 from PyQt5.QtGui import (
@@ -36,7 +38,7 @@ from PyQt5.QtWidgets import (
 
 from Model.River import RiverNode, RiverReach, River
 
-from View.ASubWindow import ASubMainWindow
+from View.Tools.PamhyrWindow import PamhyrWindow
 from View.Network.GraphWidget import GraphWidget
 from View.Network.UndoCommand import *
 from View.Network.translate import (
@@ -47,36 +49,30 @@ from View.Network.Table import (
     ComboBoxDelegate, NodeTableModel, EdgeTableModel,
 )
 
-_translate = QCoreApplication.translate
+logger = logging.getLogger()
 
+_translate = QCoreApplication.translate
 
-class NetworkWindow(ASubMainWindow):
-    def __init__(self, model=None, title="River network", parent=None):
-        self._title = title
-        self._model = model
-        self._graph = self._model.river
 
-        self.setup_title()
+class NetworkWindow(PamhyrWindow):
+    _pamhyr_ui = "Network"
+    _pamhyr_name = "River network"
 
+    def __init__(self, study=None, config=None, parent=None):
         super(NetworkWindow, self).__init__(
-            name=title, ui="Network", parent=parent
+            title = self._pamhyr_name + " - " + study.name,
+            study = study,
+            config = config,
+            options = ['undo'],
+            parent=parent,
         )
-        self.ui.setWindowTitle(self._title)
 
-        self.setup_sc()
+        self._graph = study.river
+
         self.setup_graph()
         self.setup_table()
         self.setup_connections()
 
-    def setup_title(self):
-        self._title = self._title + " - " + self._model.name
-
-    def setup_sc(self):
-        self._undo_stack = QUndoStack()
-
-        self.undo_sc = QShortcut(QKeySequence.Undo, self)
-        self.redo_sc = QShortcut(QKeySequence.Redo, self)
-
     def setup_table(self):
         retranslate()
 
@@ -143,9 +139,6 @@ class NetworkWindow(ASubMainWindow):
             self.clicked_del
         )
 
-        self.undo_sc.activated.connect(self.undo)
-        self.redo_sc.activated.connect(self.redo)
-
     def clicked_add(self):
         if self.get_action_checkable("action_toolBar_add"):
             self.set_action_checkable("action_toolBar_del", False)
@@ -166,13 +159,15 @@ class NetworkWindow(ASubMainWindow):
         if key == Qt.Key_Escape:
             self._graph_widget.reset_selection
 
-    def undo(self):
+    # Redefine undo/redo method
+
+    def _undo(self):
         self._undo_stack.undo()
         self._reachs_model.update()
         self._nodes_model.update()
         self._graph_widget.display_update()
 
-    def redo(self):
+    def _redo(self):
         self._undo_stack.redo()
         self._reachs_model.update()
         self._nodes_model.update()
diff --git a/src/View/Study/Window.py b/src/View/Study/Window.py
index 7218f9d36571cc315142ea9a3bb6ad7aa207a867..871d0545afdaacf7c3704ca30854bbf38ec06e52 100644
--- a/src/View/Study/Window.py
+++ b/src/View/Study/Window.py
@@ -17,7 +17,7 @@
 # -*- coding: utf-8 -*-
 
 from Model.Study import Study
-from View.ASubWindow import ASubWindow
+from View.Tools.PamhyrWindow import PamhyrDialog
 
 from PyQt5.QtCore import QCoreApplication
 
@@ -27,27 +27,37 @@ from PyQt5.QtWidgets import (
 
 _translate = QCoreApplication.translate
 
-class NewStudyWindow(ASubWindow):
-    def __init__(self, study=None, title="New Study", parent=None):
-        super(NewStudyWindow, self).__init__(name=title, ui="NewStudy", parent=parent)
-        self.ui.setWindowTitle(title)
+class NewStudyWindow(PamhyrDialog):
+    _pamhyr_ui = "NewStudy"
+    _pamhyr_name = "Edit/New Study"
 
-        self.parent = parent
-        self.study = study
+    def __init__(self, study=None, config = None, parent=None):
+        if study is not None:
+            name = f"Edit study - {study.name}"
+        else:
+            name = "New study"
+
+        super(NewStudyWindow, self).__init__(
+            title = name,
+            study = study,
+            config = config,
+            options = [],
+            parent = parent
+        )
 
-        if not self.study is None:
-            self.set_line_edit_text("lineEdit_name", self.study.name)
-            self.set_text_edit_text("textEdit_description", self.study.description)
-            self.set_datetime_edit("dateTimeEdit_date", self.study.date)
+        if not self._study is None:
+            self.set_line_edit_text("lineEdit_name", self._study.name)
+            self.set_text_edit_text("textEdit_description", self._study.description)
+            self.set_datetime_edit("dateTimeEdit_date", self._study.date)
 
             self.find(QLabel, "label_creation_date_data").setText(
-                self.study.creation_date.isoformat(sep=" ")
+                self._study.creation_date.isoformat(sep=" ")
             )
             self.find(QLabel, "label_last_modification_data").setText(
-                self.study.last_save_date.isoformat(sep=" ")
+                self._study.last_save_date.isoformat(sep=" ")
             )
 
-            if self.study.time_system == "date":
+            if self._study.time_system == "date":
                 self.set_radio_button("radioButton_date", True)
                 self.find(QLabel, "label_date").setEnabled(True)
                 self.find(QDateTimeEdit, "dateTimeEdit_date").setEnabled(True)
@@ -76,18 +86,18 @@ class NewStudyWindow(ASubWindow):
         name = self.get_line_edit_text("lineEdit_name")
         description = self.get_text_edit_text("textEdit_description")
 
-        if self.study is None:
+        if self._study is None:
             study = Study.new(name, description)
             if self.get_radio_button("radioButton_date"):
                 date = self.get_datetime_edit("dateTimeEdit_date")
                 study.use_date(date)
             self.parent.set_model(study)
         else:
-            self.study.name = name
-            self.study.description = description
+            self._study.name = name
+            self._study.description = description
             if self.get_radio_button("radioButton_date"):
                 date = self.get_datetime_edit("dateTimeEdit_date")
-                self.study.use_date(date)
+                self._study.use_date(date)
             else:
-                self.study.use_time()
+                self._study.use_time()
         self.done(True)
diff --git a/src/View/ASubWindow.py b/src/View/Tools/ASubWindow.py
similarity index 97%
rename from src/View/ASubWindow.py
rename to src/View/Tools/ASubWindow.py
index 4e1c13279313adb77683afe7a3e9a4a9c36e6bba..9ac63c2d91be9bb1e44879f0a5b1b2b5865c7b40 100644
--- a/src/View/ASubWindow.py
+++ b/src/View/Tools/ASubWindow.py
@@ -463,11 +463,11 @@ class ASubWindowFeatures(object):
 # Top level interface
 
 class ASubMainWindow(QMainWindow, ASubWindowFeatures, WindowToolKit):
-    def __init__(self, name="", ui="dummy", parent=None):
+    def __init__(self, name="", ui="dummy", parent=None, **kwargs):
         super(ASubMainWindow, self).__init__(parent=parent)
         if ui is not None:
             self.ui = loadUi(
-                os.path.join(os.path.dirname(__file__), "ui", f"{ui}.ui"),
+                os.path.join(os.path.dirname(__file__), "..", "ui", f"{ui}.ui"),
                 self
             )
 
@@ -496,10 +496,10 @@ class ASubMainWindow(QMainWindow, ASubWindowFeatures, WindowToolKit):
         return self.ui.findChild(qtype, name)
 
 class ASubWindow(QDialog, ASubWindowFeatures, WindowToolKit):
-    def __init__(self, name="", ui="dummy", parent=None):
+    def __init__(self, name="", ui="dummy", parent=None, **kwargs):
         super(ASubWindow, self).__init__(parent=parent)
         self.ui = loadUi(
-            os.path.join(os.path.dirname(__file__), "ui", f"{ui}.ui"),
+            os.path.join(os.path.dirname(__file__), "..", "ui", f"{ui}.ui"),
             self
         )
         self.name = name
@@ -530,7 +530,7 @@ class AWidget(QWidget, ASubWindowFeatures):
     def __init__(self, ui="", parent=None):
         super(AWidget, self).__init__(parent=parent)
         self.ui = loadUi(
-            os.path.join(os.path.dirname(__file__), "ui", "Widgets", f"{ui}.ui"),
+            os.path.join(os.path.dirname(__file__), "..", "ui", "Widgets", f"{ui}.ui"),
             self
         )
         self.parent = parent
diff --git a/src/View/ListedSubWindow.py b/src/View/Tools/ListedSubWindow.py
similarity index 98%
rename from src/View/ListedSubWindow.py
rename to src/View/Tools/ListedSubWindow.py
index b4dfe416d1dc62899f54dc88eddc36c5e78058aa..f671cc6041515869660220f9d331c7e2281ac674 100644
--- a/src/View/ListedSubWindow.py
+++ b/src/View/Tools/ListedSubWindow.py
@@ -22,7 +22,7 @@ from functools import reduce
 logger = logging.getLogger()
 
 class ListedSubWindow(object):
-    def __init__(self):
+    def __init__(self, **kwargs):
         super(ListedSubWindow, self).__init__()
         self.sub_win_cnt = 0
         self.sub_win_list = []
diff --git a/src/View/Tools/PamhyrWindow.py b/src/View/Tools/PamhyrWindow.py
new file mode 100644
index 0000000000000000000000000000000000000000..d6ae675247696462aa7b2e21554169c7c9e56a8c
--- /dev/null
+++ b/src/View/Tools/PamhyrWindow.py
@@ -0,0 +1,189 @@
+# PamhyrWindow.py -- Pamhyr
+# Copyright (C) 2023  INRAE
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <https://www.gnu.org/licenses/>.
+
+# -*- coding: utf-8 -*-
+
+import logging
+
+from PyQt5.QtGui import (
+    QKeySequence,
+)
+
+from PyQt5.QtWidgets import (
+    QUndoStack, QShortcut,
+)
+
+from View.Tools.ASubWindow import ASubMainWindow, ASubWindow
+from View.Tools.ListedSubWindow import ListedSubWindow
+
+logger = logging.getLogger()
+
+class PamhyrWindowTools(object):
+    def __init__(self, options = ["undo", "copy"], parent = None, **kwargs):
+        super(PamhyrWindowTools, self).__init__()
+
+        self._undo_stack = None
+
+        if "undo" in options:
+            self._init_undo()
+
+        if "copy" in options:
+            self._init_copy()
+
+    # Undo/Redo
+
+    def _init_undo(self):
+        self._undo_stack = QUndoStack()
+
+        self._undo_sc = QShortcut(QKeySequence.Undo, self)
+        self._redo_sc = QShortcut(QKeySequence.Redo, self)
+
+        self._undo_sc.activated.connect(self._undo)
+        self._redo_sc.activated.connect(self._redo)
+
+    def _undo(self):
+        if self._undo_stack is not None:
+            self._undo_stack.undo()
+            self._update()
+
+    def _redo(self):
+        if self._undo_stack is not None:
+            self._undo_stack.redo()
+            self._update()
+
+    # Copy/Paste
+
+    def _init_copy(self):
+        self._copy_sc = QShortcut(QKeySequence.Copy, self)
+        self._paste_sc = QShortcut(QKeySequence.Paste, self)
+
+        self._copy_sc.activated.connect(self._copy)
+        self._paste_sc.activated.connect(self._paste)
+
+    def _copy(self):
+        if self._copy_stack is not None:
+            self._copy_stack.copy()
+            self._update()
+
+    def _paste(self):
+        if self._copy_stack is not None:
+            self._copy_stack.redo()
+            self._update()
+
+    # Display
+
+    def _set_title(self):
+        """Apply `self._title` at current window title displayed
+
+        Returns:
+            Nothing
+        """
+        self.ui.setWindowTitle(self._title)
+
+    def _update(self):
+        """Update window display component
+
+        Returns:
+            Nothing
+        """
+        self._set_title()
+
+    # Hash methods
+
+    @classmethod
+    def _hash(cls, data):
+        """Compute window hash
+
+        Args:
+            data: window data parameters
+
+        Returns:
+            The hash
+        """
+        hash_str = cls._pamhyr_name
+        hash_str += cls._pamhyr_ui
+
+        for el in data:
+            hash_str += repr(el)
+
+        h = hash(hash_str)
+        logger.debug(f"Compute hash = {h} for window {cls._pamhyr_name}")
+
+        return h
+
+    def hash(self):
+        """Compute window hash
+
+        Returns:
+            The hash
+        """
+        data = [
+            self._study,
+            self._config,
+        ]
+
+        return self._hash()
+
+
+class PamhyrWindow(ASubMainWindow, ListedSubWindow, PamhyrWindowTools):
+    _pamhyr_ui = "dummy"
+    _pamhyr_name = "PamhyrWindow"
+
+    def __init__(self,
+                 title = "Pamhyr2",
+                 study = None, config = None,
+                 options = ["undo", "copy"],
+                 parent = None):
+        self._title = title
+        self._study = study
+        self._config = config
+        self._parent = parent
+
+        logger.info(self._pamhyr_name)
+
+        super(PamhyrWindow, self).__init__(
+            name = self._pamhyr_name,
+            ui = self._pamhyr_ui,
+            parent = parent,
+        )
+
+        self._set_title()
+
+
+class PamhyrDialog(ASubWindow, ListedSubWindow, PamhyrWindowTools):
+    _pamhyr_ui = "dummy"
+    _pamhyr_name = "PamhyrWindow"
+
+    def __init__(self,
+                 title = "Pamhyr2",
+                 study = None, config = None,
+                 options = ["undo", "copy"],
+                 parent = None):
+        self._title = title
+        self._study = study
+        self._config = config
+        self._parent = parent
+
+        logger.info(self._pamhyr_name)
+        logger.info(self._pamhyr_ui)
+
+        super(PamhyrDialog, self).__init__(
+            name = self._pamhyr_name,
+            ui = self._pamhyr_ui,
+            parent = parent,
+        )
+
+        self._set_title()