From d328806916e723b77244ffac5e2473beac0d2f63 Mon Sep 17 00:00:00 2001
From: Pierre-Antoine Rouby <pierre-antoine.rouby@inrae.fr>
Date: Mon, 15 May 2023 11:37:53 +0200
Subject: [PATCH] pamhyr: Add saved status in model.

---
 .../BoundaryCondition/BoundaryCondition.py    | 16 +++++++++-
 .../BoundaryConditionList.py                  | 11 ++++++-
 .../BoundaryConditionTypes.py                 | 20 ++++++------
 src/Model/Geometry/Point.py                   |  5 ++-
 src/Model/Geometry/PointXY.py                 |  6 ++--
 src/Model/Geometry/PointXYZ.py                |  7 ++--
 src/Model/Geometry/Profile.py                 | 18 ++++++++++-
 src/Model/Geometry/ProfileXYZ.py              | 13 +++++---
 src/Model/Geometry/Reach.py                   | 14 +++++++-
 .../LateralContribution.py                    | 15 ++++++++-
 .../LateralContributionList.py                | 11 ++++++-
 .../LateralContributionTypes.py               | 16 +++++-----
 src/Model/Network/Edge.py                     |  7 +++-
 src/Model/Network/Graph.py                    | 17 ++++++++--
 src/Model/Network/Node.py                     |  8 ++++-
 src/Model/River.py                            | 20 +++++++-----
 src/Model/Saved.py                            | 17 ++++++++++
 src/Model/Serializable.py                     |  2 +-
 src/Model/Study.py                            | 32 +++++++++++++++++--
 src/View/MainWindow.py                        |  8 +++++
 src/View/Network/NetworkWindow.py             |  6 +---
 21 files changed, 215 insertions(+), 54 deletions(-)
 create mode 100644 src/Model/Saved.py

diff --git a/src/Model/BoundaryCondition/BoundaryCondition.py b/src/Model/BoundaryCondition/BoundaryCondition.py
index a7b97ed2..d9ea8f21 100644
--- a/src/Model/BoundaryCondition/BoundaryCondition.py
+++ b/src/Model/BoundaryCondition/BoundaryCondition.py
@@ -5,9 +5,12 @@ from tools import trace, timer, old_pamhyr_date_to_timestamp
 from Model.Except import NotImplementedMethodeError
 
 class BoundaryCondition(object):
-    def __init__(self, name:str = ""):
+    def __init__(self, name:str = "",
+                 status = None):
         super(BoundaryCondition, self).__init__()
 
+        self._status = status
+
         self._name = name
         self._type = ""
         self._node = None
@@ -36,6 +39,7 @@ class BoundaryCondition(object):
     @name.setter
     def name(self, name):
         self._name = name
+        self._status.modified()
 
     @property
     def bctype(self):
@@ -48,6 +52,7 @@ class BoundaryCondition(object):
     @node.setter
     def node(self, node):
         self._node = node
+        self._status.modified()
 
     def has_node(self):
         return self._node is not None
@@ -97,10 +102,12 @@ class BoundaryCondition(object):
     def add(self, index:int):
         value = (self._default_0, self._default_1)
         self._data.insert(index, value)
+        self._status.modified()
         return value
 
     def insert(self, index:int, value):
         self._data.insert(index, value)
+        self._status.modified()
 
     def delete_i(self, indexes):
         self._data = list(
@@ -112,6 +119,7 @@ class BoundaryCondition(object):
                 )
             )
         )
+        self._status.modified()
 
     def delete(self, els):
         self._data = list(
@@ -120,12 +128,14 @@ class BoundaryCondition(object):
                 self.data
             )
         )
+        self._status.modified()
 
     def sort(self, _reverse=False, key=None):
         if key is None:
             self._data.sort(reverse=_reverse)
         else:
             self._data.sort(reverse=_reverse, key=key)
+        self._status.modified()
 
     def get_i(self, index):
         return self.data[index]
@@ -141,6 +151,7 @@ class BoundaryCondition(object):
         v = list(self._data[index])
         v[column] = self._types[column](value)
         self._data[index] = tuple(v)
+        self._status.modified()
 
     def set_i_0(self, index:int, value):
         self._set_i_c_v(index, 0, value)
@@ -160,6 +171,7 @@ class BoundaryCondition(object):
                     for ind, v in self.data:
                         new._set_i_c_v(ind, j, v[i])
 
+        self._status.modified()
         return new
 
     def move_up(self, index):
@@ -167,9 +179,11 @@ class BoundaryCondition(object):
             next = index - 1
             d = self._data
             d[index], d[next] = d[next], d[index]
+            self._status.modified()
 
     def move_down(self, index):
         if index >= 0:
             prev = index + 1
             d = self._data
             d[index], d[prev] = d[prev], d[index]
+            self._status.modified()
diff --git a/src/Model/BoundaryCondition/BoundaryConditionList.py b/src/Model/BoundaryCondition/BoundaryConditionList.py
index 4b14d924..f1e4150e 100644
--- a/src/Model/BoundaryCondition/BoundaryConditionList.py
+++ b/src/Model/BoundaryCondition/BoundaryConditionList.py
@@ -12,9 +12,11 @@ from Model.BoundaryCondition.BoundaryConditionTypes import (
 )
 
 class BoundaryConditionList(object):
-    def __init__(self):
+    def __init__(self, status = None):
         super(BoundaryConditionList, self).__init__()
 
+        self._status = status
+
         self._tabs = {
             "liquid" : [],
             "solid" : [],
@@ -32,18 +34,22 @@ class BoundaryConditionList(object):
 
     def set(self, lst, row, new):
         self._tabs[lst][row] = new
+        self._status.modified()
 
     def new(self, lst, index):
         n = NotDefined()
         self._tabs[lst].insert(index, n)
+        self._status.modified()
         return n
 
     def insert(self, lst, index, new):
         self._tabs[lst].insert(index, new)
+        self._status.modified()
 
     def delete(self, lst, bcs):
         for bc in bcs:
             self._tabs[lst].remove(bc)
+        self._status.modified()
 
     def delete_i(self, lst, indexes):
         bcs = list(
@@ -59,6 +65,7 @@ class BoundaryConditionList(object):
 
     def sort(self, lst, reverse=False, key=None):
         self._tabs[lst].sort(reverse=reverse, key=key)
+        self._status.modified()
 
     def move_up(self, lst, index):
         if index < len(self._tabs[lst]):
@@ -66,6 +73,7 @@ class BoundaryConditionList(object):
 
             l = self._tabs[lst]
             l[index], l[next] = l[next], l[index]
+            self._status.modified()
 
     def move_down(self, lst, index):
         if index >= 0:
@@ -73,6 +81,7 @@ class BoundaryConditionList(object):
 
             l = self._tabs[lst]
             l[index], l[prev] = l[prev], l[index]
+            self._status.modified()
 
     def __copy__(self):
         new = BoundaryConditionList()
diff --git a/src/Model/BoundaryCondition/BoundaryConditionTypes.py b/src/Model/BoundaryCondition/BoundaryConditionTypes.py
index 4bc1d538..537d8141 100644
--- a/src/Model/BoundaryCondition/BoundaryConditionTypes.py
+++ b/src/Model/BoundaryCondition/BoundaryConditionTypes.py
@@ -6,8 +6,8 @@ from Model.BoundaryCondition.BoundaryCondition import BoundaryCondition
 
 
 class NotDefined(BoundaryCondition):
-    def __init__(self, name:str = ""):
-        super(NotDefined, self).__init__(name=name)
+    def __init__(self, name:str = "", status = None):
+        super(NotDefined, self).__init__(name=name, status=status)
 
         self._type = "ND"
         self._header = ["x", "y"]
@@ -17,8 +17,8 @@ class NotDefined(BoundaryCondition):
         return 0.0
 
 class PonctualContribution(BoundaryCondition):
-    def __init__(self, name:str = ""):
-        super(PonctualContribution, self).__init__(name=name)
+    def __init__(self, name:str = "", status = None):
+        super(PonctualContribution, self).__init__(name=name, status=status)
 
         self._type = "PC"
         self._header = ["time", "debit"]
@@ -29,8 +29,8 @@ class PonctualContribution(BoundaryCondition):
         return ["liquid"]
 
 class TimeOverZ(BoundaryCondition):
-    def __init__(self, name:str = ""):
-        super(TimeOverZ, self).__init__(name=name)
+    def __init__(self, name:str = "", status = None):
+        super(TimeOverZ, self).__init__(name=name, status=status)
 
         self._type = "TZ"
         self._header = ["time", "z"]
@@ -41,8 +41,8 @@ class TimeOverZ(BoundaryCondition):
         return ["liquid"]
 
 class TimeOverDebit(BoundaryCondition):
-    def __init__(self, name:str = ""):
-        super(TimeOverDebit, self).__init__(name=name)
+    def __init__(self, name:str = "", status = None):
+        super(TimeOverDebit, self).__init__(name=name, status=status)
 
         self._type = "TD"
         self._header = ["time", "debit"]
@@ -53,8 +53,8 @@ class TimeOverDebit(BoundaryCondition):
         return ["liquid"]
 
 class ZOverDebit(BoundaryCondition):
-    def __init__(self, name:str = ""):
-        super(ZOverDebit, self).__init__(name=name)
+    def __init__(self, name:str = "", status = None):
+        super(ZOverDebit, self).__init__(name=name, status=status)
 
         self._type = "ZD"
         self._header = ["z", "debit"]
diff --git a/src/Model/Geometry/Point.py b/src/Model/Geometry/Point.py
index 69900441..2108d962 100644
--- a/src/Model/Geometry/Point.py
+++ b/src/Model/Geometry/Point.py
@@ -3,9 +3,11 @@
 from Model.Except import NotImplementedMethodeError
 
 class Point(object):
-    def __init__(self, name:str = ""):
+    def __init__(self, name:str = "", status = None):
         super(Point, self).__init__()
 
+        self._status = status
+
         self._name = name
 
     @property
@@ -15,6 +17,7 @@ class Point(object):
     @name.setter
     def name(self, name):
         self._name = name
+        self._status.modified()
 
     def point_is_named(self):
         """
diff --git a/src/Model/Geometry/PointXY.py b/src/Model/Geometry/PointXY.py
index f118522a..9b84ef7c 100644
--- a/src/Model/Geometry/PointXY.py
+++ b/src/Model/Geometry/PointXY.py
@@ -6,8 +6,8 @@ from Model.Geometry.Point import Point
 
 class PointAC(Point):
     def __init__(self, a:float = 0.0, c:float = 0.0,
-                 name: str = ""):
-        super(PointXY, self).__init__(name = name)
+                 name: str = "", status = None):
+        super(PointXY, self).__init__(name = name, status = status)
 
         self._a = float(a)
         self._c = float(c)
@@ -22,6 +22,7 @@ class PointAC(Point):
     @a.setter
     def a(self, value):
         self._a = float(value)
+        self._status.modified()
 
     @property
     def c(self):
@@ -30,6 +31,7 @@ class PointAC(Point):
     @c.setter
     def c(self, value):
         self._c = float(value)
+        self._status.modified()
 
     def dist(self, p2):
         return PointAC.distance(self, p2)
diff --git a/src/Model/Geometry/PointXYZ.py b/src/Model/Geometry/PointXYZ.py
index 78d5a09f..5f99f67b 100644
--- a/src/Model/Geometry/PointXYZ.py
+++ b/src/Model/Geometry/PointXYZ.py
@@ -7,8 +7,8 @@ from Model.Geometry.Point import Point
 
 class PointXYZ(Point):
     def __init__(self, x:float = 0.0, y:float = 0.0, z:float = 0.0,
-                 name:str = ""):
-        super(PointXYZ, self).__init__(name=name)
+                 name:str = "", status = None):
+        super(PointXYZ, self).__init__(name=name, status=status)
 
         self._x = float(x)
         self._y = float(y)
@@ -47,6 +47,7 @@ class PointXYZ(Point):
     @x.setter
     def x(self, value):
         self._x = float(value)
+        self._status.modified()
 
     @property
     def y(self):
@@ -55,6 +56,7 @@ class PointXYZ(Point):
     @y.setter
     def y(self, value):
         self._y = float(value)
+        self._status.modified()
 
     @property
     def z(self):
@@ -63,6 +65,7 @@ class PointXYZ(Point):
     @z.setter
     def z(self, value):
         self._z = float(value)
+        self._status.modified()
 
     def is_nan(self):
         """
diff --git a/src/Model/Geometry/Profile.py b/src/Model/Geometry/Profile.py
index ae0eef4b..f2678c68 100644
--- a/src/Model/Geometry/Profile.py
+++ b/src/Model/Geometry/Profile.py
@@ -9,9 +9,12 @@ class Profile(object):
     def __init__(self, num: int = 0,
                  kp:float = 0.0, name:str = "",
                  code1: int = 0, code2: int = 0,
-                 _type:str = "", reach = None):
+                 _type:str = "", reach = None,
+                 status = None):
         super(Profile, self).__init__()
 
+        self._status = status
+
         self._num = int(num)
         self._code1 = int(code1)
         self._code2 = int(code2)
@@ -46,6 +49,7 @@ class Profile(object):
     @num.setter
     def num(self, value: int):
         self._num = int(value)
+        self._status.modified()
 
     @property
     def code1(self):
@@ -58,6 +62,7 @@ class Profile(object):
     @code1.setter
     def code1(self, value: int):
         self._code1 = int(value)
+        self._status.modified()
 
     @property
     def code2(self):
@@ -70,6 +75,7 @@ class Profile(object):
     @code2.setter
     def code2(self, value: int):
         self._code2 = int(value)
+        self._status.modified()
 
     @property
     def nb_points(self):
@@ -86,6 +92,7 @@ class Profile(object):
     @kp.setter
     def kp(self, value: float):
         self._kp = float(value)
+        self._status.modified()
 
     @property
     def name(self):
@@ -98,6 +105,7 @@ class Profile(object):
     @name.setter
     def name(self, value: str):
         self._name = value.strip()
+        self._status.modified()
 
     @property
     def profile_type(self):
@@ -110,6 +118,7 @@ class Profile(object):
     @profile_type.setter
     def profile_type(self, value: str):
         self._profile_type = value
+        self._status.modified()
 
     def point(self, i:int):
         if i < len(self._points):
@@ -138,6 +147,7 @@ class Profile(object):
             Nothing.
         """
         self._points.insert(index, point)
+        self._status.modified()
 
 
     def delete(self, indexes: int):
@@ -165,6 +175,7 @@ class Profile(object):
                 self.points
             )
         )
+        self._status.modified()
 
     def delete_points(self, points):
         """Delete some elements in profile list
@@ -181,6 +192,7 @@ class Profile(object):
                 self.points
             )
         )
+        self._status.modified()
 
     # Move
 
@@ -190,6 +202,7 @@ class Profile(object):
 
             p = self._points
             p[index], p[next] = p[next], p[index]
+            self._status.modified()
 
     def move_down_point(self, index: int):
         if index >= 0:
@@ -197,6 +210,7 @@ class Profile(object):
 
             p = self._points
             p[index], p[prev] = p[prev], p[index]
+            self._status.modified()
 
     # Sort
 
@@ -213,6 +227,7 @@ class Profile(object):
             key=predicate,
             reverse=is_reversed
         )
+        self._status.modified()
 
     @timer
     def sort_with_indexes(self, indexes: list):
@@ -228,6 +243,7 @@ class Profile(object):
                 )
             )
         )
+        self._status.modified()
 
     # Abstract method, must be implemented for in non abstract class
     def get_station(self):
diff --git a/src/Model/Geometry/ProfileXYZ.py b/src/Model/Geometry/ProfileXYZ.py
index d3200075..de4c843d 100644
--- a/src/Model/Geometry/ProfileXYZ.py
+++ b/src/Model/Geometry/ProfileXYZ.py
@@ -17,7 +17,8 @@ class ProfileXYZ(Profile):
                  reach = None,
                  num = 0,
                  nb_point: int = 0,
-                 code1: int = 0, code2: int = 0):
+                 code1: int = 0, code2: int = 0,
+                 status = None):
         """ProfileXYZ constructor
 
         Args:
@@ -37,6 +38,7 @@ class ProfileXYZ(Profile):
             code1 = code1, code2 = code2,
             _type = "XYZ",
             reach = reach,
+            status = status,
         )
 
     @classmethod
@@ -101,8 +103,9 @@ class ProfileXYZ(Profile):
             Nothing.
         """
         for point in list_points:
-            pt = PointXYZ(*point)
+            pt = PointXYZ(*point, status=self._status)
             self._points.append(pt)
+        self._status.modified()
 
     def get_point_i(self, index: int) -> PointXYZ:
         """Get point at index.
@@ -124,8 +127,9 @@ class ProfileXYZ(Profile):
         Returns:
             Nothing.
         """
-        point_xyz = PointXYZ(0., 0., 0.)
+        point_xyz = PointXYZ(0., 0., 0., status=self._status)
         self._points.append(point_xyz)
+        self._status.modified()
 
     def insert(self, index: int):
         """Insert a new point at index.
@@ -136,8 +140,9 @@ class ProfileXYZ(Profile):
         Returns:
             The new point.
         """
-        point = PointXYZ(0., 0., 0.)
+        point = PointXYZ(0., 0., 0., status=self._status)
         self._points.insert(index, point)
+        self._status.modified()
         return point
 
     def filter_isnan(self, lst):
diff --git a/src/Model/Geometry/Reach.py b/src/Model/Geometry/Reach.py
index 16ee09f8..94ca70d6 100644
--- a/src/Model/Geometry/Reach.py
+++ b/src/Model/Geometry/Reach.py
@@ -16,7 +16,8 @@ from Model.Geometry.ProfileXYZ import ProfileXYZ
 from Model.Except import FileFormatError, exception_message_box
 
 class Reach:
-    def __init__(self, parent=None):
+    def __init__(self, status=None, parent=None):
+        self._status = status
         self._parent = parent
         self._profiles: List[Profile] = []
 
@@ -86,6 +87,8 @@ class Reach:
         self._profiles.insert(index, profile)
         self._update_profile_numbers()
 
+        self._status.modified()
+
         return profile
 
     def insert_profile(self, index: int, profile: Profile):
@@ -99,6 +102,7 @@ class Reach:
         """
         self._profiles.insert(index, profile)
         self._update_profile_numbers()
+        self._status.modified()
 
 
     def delete(self, indexes):
@@ -127,6 +131,7 @@ class Reach:
             )
         )
         self._update_profile_numbers()
+        self._status.modified()
 
     def delete_profiles(self, profiles):
         """Delete some elements in profile list
@@ -144,6 +149,7 @@ class Reach:
             )
         )
         self._update_profile_numbers()
+        self._status.modified()
 
 
     def move_up_profile(self, index: int):
@@ -152,6 +158,7 @@ class Reach:
 
             p = self._profiles
             p[index], p[next] = p[next], p[index]
+            self._status.modified()
 
     def move_down_profile(self, index: int):
         if index >= 0:
@@ -159,6 +166,7 @@ class Reach:
 
             p = self._profiles
             p[index], p[prev] = p[prev], p[index]
+            self._status.modified()
 
     def get_x(self):
         return [profile.x() for profile in self.profiles]
@@ -260,6 +268,7 @@ class Reach:
         self._compute_guidelines_cache(guide_set, named_points,
                                        complete, incomplete)
 
+        self._status.modified()
         return (complete, incomplete)
 
     def _map_guidelines_points(self, func, full=False):
@@ -306,6 +315,7 @@ class Reach:
             key=lambda profile: profile.kp,
             reverse=is_reversed
         )
+        self._status.modified()
 
     @timer
     def sort_with_indexes(self, indexes: list):
@@ -321,6 +331,7 @@ class Reach:
                 )
             )
         )
+        self._status.modified()
 
     # Import/Export
 
@@ -352,6 +363,7 @@ class Reach:
                     self._profiles.append(prof)
                     self._update_profile_numbers()
 
+                self._status.modified()
         except FileNotFoundError as e:
             print(e)
             exception_message_box(e)
diff --git a/src/Model/LateralContribution/LateralContribution.py b/src/Model/LateralContribution/LateralContribution.py
index 086bb628..47f0650b 100644
--- a/src/Model/LateralContribution/LateralContribution.py
+++ b/src/Model/LateralContribution/LateralContribution.py
@@ -5,9 +5,11 @@ from tools import trace, timer, old_pamhyr_date_to_timestamp
 from Model.Except import NotImplementedMethodeError
 
 class LateralContribution(object):
-    def __init__(self, name:str = ""):
+    def __init__(self, name:str = "", status = None):
         super(LateralContribution, self).__init__()
 
+        self._status = status
+
         self._name = name
         self._type = ""
         self._edge = None
@@ -36,6 +38,7 @@ class LateralContribution(object):
     @name.setter
     def name(self, name):
         self._name = name
+        self._status.modified()
 
     @property
     def lctype(self):
@@ -48,6 +51,7 @@ class LateralContribution(object):
     @edge.setter
     def edge(self, edge):
         self._edge = edge
+        self._status.modified()
 
     def has_edge(self):
         return self._edge is not None
@@ -97,10 +101,12 @@ class LateralContribution(object):
     def add(self, index:int):
         value = (self._default_0, self._default_1)
         self._data.insert(index, value)
+        self._status.modified()
         return value
 
     def insert(self, index:int, value):
         self._data.insert(index, value)
+        self._status.modified()
 
     def delete_i(self, indexes):
         self._data = list(
@@ -112,6 +118,7 @@ class LateralContribution(object):
                 )
             )
         )
+        self._status.modified()
 
     def delete(self, els):
         self._data = list(
@@ -120,12 +127,14 @@ class LateralContribution(object):
                 self.data
             )
         )
+        self._status.modified()
 
     def sort(self, _reverse=False, key=None):
         if key is None:
             self._data.sort(reverse=_reverse)
         else:
             self._data.sort(reverse=_reverse, key=key)
+        self._status.modified()
 
     def get_i(self, index):
         return self.data[index]
@@ -141,6 +150,7 @@ class LateralContribution(object):
         v = list(self._data[index])
         v[column] = self._types[column](value)
         self._data[index] = tuple(v)
+        self._status.modified()
 
     def set_i_0(self, index:int, value):
         self._set_i_c_v(index, 0, value)
@@ -160,6 +170,7 @@ class LateralContribution(object):
                     for ind, v in self.data:
                         new._set_i_c_v(ind, j, v[i])
 
+        self._status.modified()
         return new
 
     def move_up(self, index):
@@ -167,9 +178,11 @@ class LateralContribution(object):
             next = index - 1
             d = self._data
             d[index], d[next] = d[next], d[index]
+            self._status.modified()
 
     def move_down(self, index):
         if index >= 0:
             prev = index + 1
             d = self._data
             d[index], d[prev] = d[prev], d[index]
+            self._status.modified()
diff --git a/src/Model/LateralContribution/LateralContributionList.py b/src/Model/LateralContribution/LateralContributionList.py
index c8957d9c..f0947493 100644
--- a/src/Model/LateralContribution/LateralContributionList.py
+++ b/src/Model/LateralContribution/LateralContributionList.py
@@ -10,9 +10,11 @@ from Model.LateralContribution.LateralContributionTypes import (
 )
 
 class LateralContributionList(object):
-    def __init__(self):
+    def __init__(self, status = None):
         super(LateralContributionList, self).__init__()
 
+        self._status = status
+
         self._tabs = {
             "liquid" : [],
             "solid" : [],
@@ -30,18 +32,22 @@ class LateralContributionList(object):
 
     def set(self, lst, row, new):
         self._tabs[lst][row] = new
+        self._status.modified()
 
     def new(self, lst, index):
         n = NotDefined()
         self._tabs[lst].insert(index, n)
+        self._status.modified()
         return n
 
     def insert(self, lst, index, new):
         self._tabs[lst].insert(index, new)
+        self._status.modified()
 
     def delete(self, lst, bcs):
         for bc in bcs:
             self._tabs[lst].remove(bc)
+        self._status.modified()
 
     def delete_i(self, lst, indexes):
         bcs = list(
@@ -57,6 +63,7 @@ class LateralContributionList(object):
 
     def sort(self, lst, reverse=False, key=None):
         self._tabs[lst].sort(reverse=reverse, key=key)
+        self._status.modified()
 
     def move_up(self, lst, index):
         if index < len(self._tabs[lst]):
@@ -64,6 +71,7 @@ class LateralContributionList(object):
 
             l = self._tabs[lst]
             l[index], l[next] = l[next], l[index]
+            self._status.modified()
 
     def move_down(self, lst, index):
         if index >= 0:
@@ -71,6 +79,7 @@ class LateralContributionList(object):
 
             l = self._tabs[lst]
             l[index], l[prev] = l[prev], l[index]
+            self._status.modified()
 
     def __copy__(self):
         new = LateralContributionList()
diff --git a/src/Model/LateralContribution/LateralContributionTypes.py b/src/Model/LateralContribution/LateralContributionTypes.py
index 6ff07c3f..d45cd1e5 100644
--- a/src/Model/LateralContribution/LateralContributionTypes.py
+++ b/src/Model/LateralContribution/LateralContributionTypes.py
@@ -6,8 +6,8 @@ from Model.LateralContribution.LateralContribution import LateralContribution
 
 
 class NotDefined(LateralContribution):
-    def __init__(self, name:str = ""):
-        super(NotDefined, self).__init__(name=name)
+    def __init__(self, name:str = "", status=None):
+        super(NotDefined, self).__init__(name=name, status=status)
 
         self._type = "ND"
         self._header = ["x", "y"]
@@ -17,8 +17,8 @@ class NotDefined(LateralContribution):
         return 0.0
 
 class LateralContrib(LateralContribution):
-    def __init__(self, name:str = ""):
-        super(LateralContrib, self).__init__(name=name)
+    def __init__(self, name:str = "", status=None):
+        super(LateralContrib, self).__init__(name=name, status=status)
 
         self._type = "LC"
         self._header = ["time", "debit"]
@@ -29,8 +29,8 @@ class LateralContrib(LateralContribution):
         return ["liquid"]
 
 class Rain(LateralContribution):
-    def __init__(self, name:str = ""):
-        super(Rain, self).__init__(name=name)
+    def __init__(self, name:str = "", status=None):
+        super(Rain, self).__init__(name=name, status=status)
 
         self._type = "RA"
         self._header = ["time", "debit"]
@@ -41,8 +41,8 @@ class Rain(LateralContribution):
         return ["liquid"]
 
 class Evaporation(LateralContribution):
-    def __init__(self, name:str = ""):
-        super(Evaporation, self).__init__(name=name)
+    def __init__(self, name:str = "", status=None):
+        super(Evaporation, self).__init__(name=name, status=status)
 
         self._type = "EV"
         self._header = ["time", "debit"]
diff --git a/src/Model/Network/Edge.py b/src/Model/Network/Edge.py
index 5dcb12c9..08ae23e7 100644
--- a/src/Model/Network/Edge.py
+++ b/src/Model/Network/Edge.py
@@ -5,9 +5,12 @@ from Model.Network.Node import Node
 class Edge(object):
     def __init__(self, id:str, name:str,
                  node1:Node = None,
-                 node2:Node = None):
+                 node2:Node = None,
+                 status = None):
         super(Edge, self).__init__()
 
+        self._status = status
+
         self.id = id
         self._name = name if name != "" else f"{node1.name} -> {node2.name}"
 
@@ -44,6 +47,8 @@ class Edge(object):
         elif name == "enable":
             self.enable = value
 
+        self._status.modified()
+
     @property
     def name(self):
         return self._name
diff --git a/src/Model/Network/Graph.py b/src/Model/Network/Graph.py
index 59553e0c..fce8c4c3 100644
--- a/src/Model/Network/Graph.py
+++ b/src/Model/Network/Graph.py
@@ -6,9 +6,11 @@ from Model.Network.Node import Node
 from Model.Network.Edge import Edge
 
 class Graph(object):
-    def __init__(self):
+    def __init__(self, status = None):
         super(Graph, self).__init__()
 
+        self._status = status
+
         self._node_ctor = Node
         self._edge_ctor = Edge
 
@@ -82,13 +84,16 @@ class Graph(object):
     def _add_node(self, node):
         self._nodes.append(node)
         self._nodes_ids += 1
+
+        self._status.modified()
         return node
 
     def add_node(self, x:float = 0.0, y:float = 0.0):
         node = self._node_ctor(
             self._nodes_ids,
             f"Node {self._nodes_ids}",
-            x = x, y = y
+            x = x, y = y,
+            status = self._status
         )
         return self._add_node(node)
 
@@ -101,10 +106,14 @@ class Graph(object):
 
         self._edges.append(edge)
         self._edges_ids += 1
+
+        self._status.modified()
         return edge
 
     def add_edge(self, n1:Node, n2:Node):
-        edge = self._edge_ctor(self._edges_ids, "", n1, n2)
+        edge = self._edge_ctor(self._edges_ids,
+                               "", n1, n2,
+                               status = self._status)
         return self._add_edge(edge)
 
     def remove_node(self, node_name:str):
@@ -114,6 +123,7 @@ class Graph(object):
                 self._nodes
             )
         )
+        self._status.modified()
 
     def remove_edge(self, edge_name:str):
         self._edges = list(
@@ -122,6 +132,7 @@ class Graph(object):
                 self._edges
             )
         )
+        self._status.modified()
 
     def is_upstream_node(self, node):
         return reduce(
diff --git a/src/Model/Network/Node.py b/src/Model/Network/Node.py
index 8adf7ccb..5da21909 100644
--- a/src/Model/Network/Node.py
+++ b/src/Model/Network/Node.py
@@ -4,9 +4,12 @@ from Model.Network.Point import Point
 
 class Node(object):
     def __init__(self, id:str, name:str,
-                 x:float = 0.0, y:float = 0.0):
+                 x:float = 0.0, y:float = 0.0,
+                 status = None):
         super(Node, self).__init__()
 
+        self._status = status
+
         self.id = id
         self._name = name
         self.pos = Point(x, y)
@@ -29,6 +32,8 @@ class Node(object):
         elif name == "id":
             self.id = value
 
+        self._status.modified()
+
     @property
     def name(self):
         return self._name
@@ -36,3 +41,4 @@ class Node(object):
     def setPos(self, x, y):
         self.pos.x = x
         self.pos.y = y
+        self._status.modified()
diff --git a/src/Model/River.py b/src/Model/River.py
index 0f13e6f7..fc4e33d8 100644
--- a/src/Model/River.py
+++ b/src/Model/River.py
@@ -13,10 +13,12 @@ from Model.LateralContribution.LateralContributionList import LateralContributio
 
 class RiverNode(Node):
     def __init__(self, id:str, name:str,
-                 x:float, y:float):
+                 x:float, y:float,
+                 status = None):
         super(RiverNode, self).__init__(
             id, name,
-            x, y
+            x, y,
+            status = status
         )
 
         self._locker = None
@@ -33,10 +35,12 @@ class RiverNode(Node):
 class RiverReach(Edge):
     def __init__(self, id:str, name:str,
                  node1:RiverNode = None,
-                 node2:RiverNode = None):
+                 node2:RiverNode = None,
+                 status = None):
         super(RiverReach, self).__init__(
             id, name,
-            node1, node2
+            node1, node2,
+            status = status
         )
 
         self._reach = Reach(self)
@@ -47,16 +51,16 @@ class RiverReach(Edge):
 
 
 class River(Graph):
-    def __init__(self):
-        super(River, self).__init__()
+    def __init__(self, status=None):
+        super(River, self).__init__(status=status)
 
         # Replace Node and Edge ctor by custom ctor
         self._node_ctor = RiverNode
         self._edge_ctor = RiverReach
 
         self._current_reach = None
-        self._boundary_condition = BoundaryConditionList()
-        self._lateral_contribution = LateralContributionList()
+        self._boundary_condition = BoundaryConditionList(status=self._status)
+        self._lateral_contribution = LateralContributionList(status=self._status)
 
     @property
     def boundary_condition(self):
diff --git a/src/Model/Saved.py b/src/Model/Saved.py
new file mode 100644
index 00000000..637ee1d4
--- /dev/null
+++ b/src/Model/Saved.py
@@ -0,0 +1,17 @@
+# -*- coding: utf-8 -*-
+
+class SavedStatus(object):
+    def __init__(self):
+        super(SavedStatus, self).__init__()
+        self._saved = True
+
+    def is_saved(self):
+        return self._saved
+
+    def save(self):
+        print(" * save")
+        self._saved = True
+
+    def modified(self):
+        print(" * modified ...")
+        self._saved = False
diff --git a/src/Model/Serializable.py b/src/Model/Serializable.py
index e3b65cd7..17b12345 100644
--- a/src/Model/Serializable.py
+++ b/src/Model/Serializable.py
@@ -12,6 +12,6 @@ class Serializable():
             me = pickle.load(in_file)
         return me
 
-    def save(self):
+    def _save(self):
         with open(self.filename, 'wb') as out_file:
             pickle.dump(self, out_file)
diff --git a/src/Model/Study.py b/src/Model/Study.py
index f1a50131..7d04b9a1 100644
--- a/src/Model/Study.py
+++ b/src/Model/Study.py
@@ -2,16 +2,23 @@
 
 import os
 from datetime import datetime
+
+from Model.Saved import SavedStatus
 from Model.Serializable import Serializable
 
+from Model.River import River
+
+
 class Study(Serializable):
     def __init__(self):
         # Serialization information
         super(Study, self).__init__("")
         self.filename = ""
 
+        self.status = SavedStatus()
+
         # Study general information
-        self.name = ""
+        self._name = ""
         self.description = ""
         # Time system
         self._time_system = "time"
@@ -22,7 +29,25 @@ class Study(Serializable):
         self.last_save_date = datetime.now()
 
         # Study data
-        self.river = None
+        self.river = River(status=self.status)
+
+    @property
+    def is_saved(self):
+        return self.status.is_saved()
+
+    def save(self):
+        self.last_save_date = datetime.now()
+        self.status.save()
+        self._save()
+
+    @property
+    def name(self):
+        return self._name
+
+    @name.setter
+    def name(self, name):
+        self._name = str(name)
+        self.status.modified()
 
     @property
     def time_system(self):
@@ -30,10 +55,12 @@ class Study(Serializable):
 
     def use_time(self):
         self._time_system = "time"
+        self.status.modified()
 
     def use_date(self, date:datetime):
         self._time_system = "date"
         self._date = date
+        self.status.modified()
 
     @property
     def date(self):
@@ -42,6 +69,7 @@ class Study(Serializable):
     @date.setter
     def date(self, timestamp):
         self._date = timestamp
+        self.status.modified()
 
     @classmethod
     def new(cls):
diff --git a/src/View/MainWindow.py b/src/View/MainWindow.py
index 1be5b60d..94b412b6 100644
--- a/src/View/MainWindow.py
+++ b/src/View/MainWindow.py
@@ -75,6 +75,12 @@ class ApplicationWindow(QMainWindow, ListedSubWindow, WindowToolKit):
         self.trans = QTranslator(self)
         #self.ui.retranslateUi()
 
+    def set_title(self):
+        if self.model is not None:
+            self.setWindowTitle(f"PAMHYR - {self.model.name}")
+        else:
+            self.setWindowTitle("PAMHYR")
+
     def enable_actions(self, action:str, enable:bool):
         """Enable of disable an action componant
 
@@ -155,10 +161,12 @@ class ApplicationWindow(QMainWindow, ListedSubWindow, WindowToolKit):
     def set_model(self, model):
         self.model = model
         self.update_enable_action()
+        self.set_title()
 
     def close_model(self):
         self.model = None
         self.update_enable_action()
+        self.set_title()
 
     def update_enable_action(self):
         """Update status of action componante
diff --git a/src/View/Network/NetworkWindow.py b/src/View/Network/NetworkWindow.py
index b3b22cdf..4a05e547 100644
--- a/src/View/Network/NetworkWindow.py
+++ b/src/View/Network/NetworkWindow.py
@@ -25,11 +25,7 @@ class NetworkWindow(ASubMainWindow):
         self.ui.setWindowTitle(title)
 
         self.model = model
-        if self.model.river is None:
-            self.graph = River()
-            self.model.river = self.graph
-        else:
-            self.graph = self.model.river
+        self.graph = self.model.river
 
         # Graph Widget
 
-- 
GitLab