From ed11f34e0addfb9cdff7c2202208c8ffda11aebb Mon Sep 17 00:00:00 2001
From: Christophe Geuzaine <cgeuzaine@ulg.ac.be>
Date: Sun, 20 Mar 2016 09:06:59 +0000
Subject: [PATCH] Onelab "changed" flag is now multi-level (not just
 true/false). This allows finer granularity for complex clients. For example,
 Gmsh now responds to 4 levels of "changed"

- changed == 0: nothing to do
- changed == 1: only save the mesh (useful if we only chanaged some physical groups)
- changed == 2: only remesh and save the mesh (useful if we only changed some meshing paramaters)
- changed >= 3: reload geometry, remesh and save the mesh
---
 Common/GmshMessage.cpp            |  18 +++--
 Common/GmshMessage.h              |   6 +-
 Common/OpenFile.cpp               |   5 +-
 Common/Options.cpp                |  50 ++++++-------
 Common/gmshLocalNetworkClient.cpp |   7 +-
 Common/onelab.h                   | 112 +++++++++++++++++++-----------
 Common/onelabUtils.cpp            |  38 +++++-----
 Fltk/graphicWindow.cpp            |   1 +
 Geo/GModel.cpp                    |   4 +-
 Geo/GeoStringInterface.cpp        |  19 +++--
 10 files changed, 157 insertions(+), 103 deletions(-)

diff --git a/Common/GmshMessage.cpp b/Common/GmshMessage.cpp
index d700ef9e09..22e0d5b4ca 100644
--- a/Common/GmshMessage.cpp
+++ b/Common/GmshMessage.cpp
@@ -742,7 +742,7 @@ bool Msg::UseOnelab()
 }
 
 void Msg::SetOnelabNumber(std::string name, double val, bool visible,
-                          bool persistent, bool readOnly, bool neverChanged)
+                          bool persistent, bool readOnly, int changedValue)
 {
 #if defined(HAVE_ONELAB)
   if(_onelabClient){
@@ -756,14 +756,14 @@ void Msg::SetOnelabNumber(std::string name, double val, bool visible,
     numbers[0].setVisible(visible);
     if(persistent) numbers[0].setAttribute("Persistent", "1");
     numbers[0].setReadOnly(readOnly);
-    numbers[0].setNeverChanged(neverChanged);
+    numbers[0].setChangedValue(changedValue);
     _onelabClient->set(numbers[0]);
   }
 #endif
 }
 
 void Msg::SetOnelabString(std::string name, std::string val, bool visible,
-                          bool persistent, bool readOnly, bool neverChanged,
+                          bool persistent, bool readOnly, int changedValue,
                           const std::string &kind)
 {
 #if defined(HAVE_ONELAB)
@@ -778,7 +778,7 @@ void Msg::SetOnelabString(std::string name, std::string val, bool visible,
     strings[0].setVisible(visible);
     if(persistent) strings[0].setAttribute("Persistent", "1");
     strings[0].setReadOnly(readOnly);
-    strings[0].setNeverChanged(neverChanged);
+    strings[0].setChangedValue(changedValue);
     if(kind.size()) strings[0].setKind(kind);
     _onelabClient->set(strings[0]);
   }
@@ -897,7 +897,7 @@ void Msg::SetOnelabAction(const std::string &action)
   if(_onelabClient){
     onelab::string o(_onelabClient->getName() + "/Action", action);
     o.setVisible(false);
-    o.setNeverChanged(true);
+    o.setChangedValue(0);
     _onelabClient->set(o);
   }
 #endif
@@ -970,6 +970,7 @@ static void _setStandardOptions(onelab::parameter *p,
   if(fopt.count("Visible")) p->setVisible(fopt["Visible"][0] ? true : false);
   if(fopt.count("ReadOnly")) p->setReadOnly(fopt["ReadOnly"][0] ? true : false);
   if(fopt.count("NeverChanged")) p->setNeverChanged(fopt["NeverChanged"][0] ? true : false);
+  if(fopt.count("ChangedValue")) p->setChangedValue(fopt["ChangedValue"][0]);
   if(fopt.count("ReadOnlyRange"))
     p->setAttribute("ReadOnlyRange", fopt["ReadOnlyRange"][0] ? "1" : "0");
   if(fopt.count("AutoCheck"))
@@ -1177,12 +1178,14 @@ void Msg::ImportPhysicalGroupsInOnelab()
       size += groups[dim].size();
     onelab::number n("Gmsh/Number of physical groups", size);
     n.setReadOnly(true);
+    n.setChangedValue(1);
     n.setVisible(false);
     n.setAttribute("Closed", "1");
     _onelabClient->set(n);
 
     onelab::number nd("Gmsh/Model dimension", GModel::current()->getDim());
     nd.setReadOnly(true);
+    nd.setChangedValue(1);
     nd.setVisible(false);
     nd.setAttribute("Closed", "1");
     _onelabClient->set(nd);
@@ -1203,14 +1206,17 @@ void Msg::ImportPhysicalGroupsInOnelab()
         std::string str = tmp;
         onelab::number n1(str + "Dimension", dim);
         n1.setReadOnly(true);
+        n1.setChangedValue(1);
         n1.setVisible(false);
         _onelabClient->set(n1);
         onelab::number n2(str + "Number", num);
         n2.setReadOnly(true);
+        n2.setChangedValue(1);
         n2.setVisible(false);
         _onelabClient->set(n2);
         onelab::string s(str + "Name", name);
         s.setReadOnly(true);
+        s.setChangedValue(1);
         s.setVisible(false);
         _onelabClient->set(s);
         index++;
@@ -1235,7 +1241,7 @@ void Msg::RunOnelabClient(const std::string &name, const std::string &command)
 #endif
 }
 
-void Msg::SetOnelabChanged(bool value, const std::string &client)
+void Msg::SetOnelabChanged(int value, const std::string &client)
 {
 #if defined(HAVE_ONELAB)
   onelab::server::instance()->setChanged(value, client);
diff --git a/Common/GmshMessage.h b/Common/GmshMessage.h
index a7582c2764..3829b14c29 100644
--- a/Common/GmshMessage.h
+++ b/Common/GmshMessage.h
@@ -118,10 +118,10 @@ class Msg {
   static bool UseOnelab();
   static void SetOnelabNumber(std::string name, double val, bool visible=true,
                               bool persistent=false, bool readOnly=false,
-                              bool neverChanged=false);
+                              int changedValue=3);
   static void SetOnelabString(std::string name, std::string val, bool visible=true,
                               bool persistent=false, bool readOnly=false,
-                              bool neverChanged=false, const std::string &kind="");
+                              int changedValue=3, const std::string &kind="");
   static double GetOnelabNumber(std::string name, double defaultValue=0.,
                                 bool errorIfMissing=false);
   static std::string GetOnelabString(std::string name, const std::string &defaultValue="",
@@ -138,7 +138,7 @@ class Msg {
                                       std::map<std::string, std::vector<std::string> > &copt);
   static void UndefineOnelabParameter(const std::string &name);
   static void RunOnelabClient(const std::string &name, const std::string &exe="");
-  static void SetOnelabChanged(bool value=true, const std::string &client="Gmsh");
+  static void SetOnelabChanged(int value, const std::string &client="Gmsh");
   static void ImportPhysicalGroupsInOnelab();
 };
 
diff --git a/Common/OpenFile.cpp b/Common/OpenFile.cpp
index c5c06c45e0..831c60639f 100644
--- a/Common/OpenFile.cpp
+++ b/Common/OpenFile.cpp
@@ -344,11 +344,12 @@ int MergeFile(const std::string &fileName, bool warnIfMissing, bool setBoundingB
   if(solver.size()){
     int num = defineSolver(solver);
     Msg::SetOnelabString(solver + "/Model name", fileName, true, true,
-                         false, false, "file");
+                         false, 3, "file");
     if(GModel::current()->getName() == "" ||
        Msg::GetOnelabString("Gmsh/Model name").empty()){
       GModel::current()->setFileName(split[0] + split[1] + ".geo");
       GModel::current()->setName(split[1] + ".geo");
+      Msg::SetOnelabChanged(3);
     }
     CTX::instance()->launchSolverAtStartup = num;
     CTX::instance()->geom.draw = 1;
@@ -579,7 +580,7 @@ int MergePostProcessingFile(const std::string &fileName, int showViews,
     GModel::setCurrent(m);
   }
   // FIXME: disabled onelab physical group import for now, as the number of
-  // groups in mesh-bases post-pro files can be different from the # in the
+  // groups in mesh-based post-pro files can be different from the # in the
   // model, which will trigger setChanged(Gmsh), leading undesirable remeshing
   int ret = MergeFile(fileName, warnIfMissing, old->bounds().empty() ? true : false, false);
   GModel::setCurrent(old);
diff --git a/Common/Options.cpp b/Common/Options.cpp
index 1681ea9a63..5d61e8d0fc 100644
--- a/Common/Options.cpp
+++ b/Common/Options.cpp
@@ -4868,7 +4868,7 @@ double opt_mesh_optimize(OPT_ARGS_NUM)
 {
   if(action & GMSH_SET){
     if(!(action & GMSH_SET_DEFAULT) && (int)val != CTX::instance()->mesh.optimize)
-      Msg::SetOnelabChanged();
+      Msg::SetOnelabChanged(2);
     CTX::instance()->mesh.optimize = (int)val;
   }
 #if defined(HAVE_FLTK)
@@ -4883,7 +4883,7 @@ double opt_mesh_optimize_netgen(OPT_ARGS_NUM)
 {
   if(action & GMSH_SET){
     if(!(action & GMSH_SET_DEFAULT) && (int)val != CTX::instance()->mesh.optimizeNetgen)
-      Msg::SetOnelabChanged();
+      Msg::SetOnelabChanged(2);
     CTX::instance()->mesh.optimizeNetgen = (int)val;
   }
 #if defined(HAVE_FLTK)
@@ -4905,7 +4905,7 @@ double opt_mesh_refine_steps(OPT_ARGS_NUM)
 {
   if(action & GMSH_SET){
     if(!(action & GMSH_SET_DEFAULT) && (int)val != CTX::instance()->mesh.refineSteps)
-      Msg::SetOnelabChanged();
+      Msg::SetOnelabChanged(2);
     CTX::instance()->mesh.refineSteps = (int)val;
   }
   return CTX::instance()->mesh.refineSteps;
@@ -4973,7 +4973,7 @@ double opt_mesh_scaling_factor(OPT_ARGS_NUM)
 {
   if(action & GMSH_SET){
     if(!(action & GMSH_SET_DEFAULT) && val != CTX::instance()->mesh.scalingFactor)
-      Msg::SetOnelabChanged();
+      Msg::SetOnelabChanged(2);
     CTX::instance()->mesh.scalingFactor = val;
   }
   return CTX::instance()->mesh.scalingFactor;
@@ -4984,7 +4984,7 @@ double opt_mesh_lc_factor(OPT_ARGS_NUM)
   if(action & GMSH_SET){
     if(val > 0){
       if(!(action & GMSH_SET_DEFAULT) && val != CTX::instance()->mesh.lcFactor)
-        Msg::SetOnelabChanged();
+        Msg::SetOnelabChanged(2);
       CTX::instance()->mesh.lcFactor = val;
     }
   }
@@ -5000,7 +5000,7 @@ double opt_mesh_lc_min(OPT_ARGS_NUM)
 {
   if(action & GMSH_SET){
     if(!(action & GMSH_SET_DEFAULT) && val != CTX::instance()->mesh.lcMin)
-      Msg::SetOnelabChanged();
+      Msg::SetOnelabChanged(2);
     CTX::instance()->mesh.lcMin = val;
   }
 #if defined(HAVE_FLTK)
@@ -5015,7 +5015,7 @@ double opt_mesh_lc_max(OPT_ARGS_NUM)
 {
   if(action & GMSH_SET){
     if(!(action & GMSH_SET_DEFAULT) && val != CTX::instance()->mesh.lcMax)
-      Msg::SetOnelabChanged();
+      Msg::SetOnelabChanged(2);
     CTX::instance()->mesh.lcMax = val;
   }
 #if defined(HAVE_FLTK)
@@ -5030,7 +5030,7 @@ double opt_mesh_tolerance_edge_length(OPT_ARGS_NUM)
 {
   if(action & GMSH_SET){
     if(!(action & GMSH_SET_DEFAULT) && val != CTX::instance()->mesh.toleranceEdgeLength)
-      Msg::SetOnelabChanged();
+      Msg::SetOnelabChanged(2);
     CTX::instance()->mesh.toleranceEdgeLength = val;
   }
   return CTX::instance()->mesh.toleranceEdgeLength;
@@ -5040,7 +5040,7 @@ double opt_mesh_tolerance_initial_delaunay(OPT_ARGS_NUM)
 {
   if(action & GMSH_SET){
     if(!(action & GMSH_SET_DEFAULT) && val != CTX::instance()->mesh.toleranceInitialDelaunay)
-      Msg::SetOnelabChanged();
+      Msg::SetOnelabChanged(2);
     CTX::instance()->mesh.toleranceInitialDelaunay = val;
   }
   return CTX::instance()->mesh.toleranceInitialDelaunay;
@@ -5050,7 +5050,7 @@ double opt_mesh_lc_from_curvature(OPT_ARGS_NUM)
 {
   if(action & GMSH_SET){
     if(!(action & GMSH_SET_DEFAULT) && (int)val != CTX::instance()->mesh.lcFromCurvature)
-      Msg::SetOnelabChanged();
+      Msg::SetOnelabChanged(2);
     CTX::instance()->mesh.lcFromCurvature = (int)val;
   }
 #if defined(HAVE_FLTK)
@@ -5065,7 +5065,7 @@ double opt_mesh_lc_from_points(OPT_ARGS_NUM)
 {
   if(action & GMSH_SET){
     if(!(action & GMSH_SET_DEFAULT) && (int)val != CTX::instance()->mesh.lcFromPoints)
-      Msg::SetOnelabChanged();
+      Msg::SetOnelabChanged(2);
     CTX::instance()->mesh.lcFromPoints = (int)val;
   }
 #if defined(HAVE_FLTK)
@@ -5080,7 +5080,7 @@ double opt_mesh_lc_extend_from_boundary(OPT_ARGS_NUM)
 {
   if(action & GMSH_SET){
     if(!(action & GMSH_SET_DEFAULT) && (int)val != CTX::instance()->mesh.lcExtendFromBoundary)
-      Msg::SetOnelabChanged();
+      Msg::SetOnelabChanged(2);
     CTX::instance()->mesh.lcExtendFromBoundary = (int)val;
   }
 #if defined(HAVE_FLTK)
@@ -5095,7 +5095,7 @@ double opt_mesh_lc_integration_precision(OPT_ARGS_NUM)
 {
   if(action & GMSH_SET){
     if(!(action & GMSH_SET_DEFAULT) && val != CTX::instance()->mesh.lcIntegrationPrecision)
-      Msg::SetOnelabChanged();
+      Msg::SetOnelabChanged(2);
     CTX::instance()->mesh.lcIntegrationPrecision = val;
   }
   return CTX::instance()->mesh.lcIntegrationPrecision;
@@ -5105,7 +5105,7 @@ double opt_mesh_rand_factor(OPT_ARGS_NUM)
 {
   if(action & GMSH_SET){
     if(!(action & GMSH_SET_DEFAULT) && val != CTX::instance()->mesh.randFactor)
-      Msg::SetOnelabChanged();
+      Msg::SetOnelabChanged(2);
     CTX::instance()->mesh.randFactor = val;
   }
   return CTX::instance()->mesh.randFactor;
@@ -5751,7 +5751,7 @@ double opt_mesh_algo2d(OPT_ARGS_NUM)
 {
   if(action & GMSH_SET){
     if(!(action & GMSH_SET_DEFAULT) && (int)val != CTX::instance()->mesh.algo2d)
-      Msg::SetOnelabChanged();
+      Msg::SetOnelabChanged(2);
     CTX::instance()->mesh.algo2d = (int)val;
   }
 #if defined(HAVE_FLTK)
@@ -5786,7 +5786,7 @@ double opt_mesh_algo_recombine(OPT_ARGS_NUM)
 {
   if(action & GMSH_SET){
     if(!(action & GMSH_SET_DEFAULT) && (int)val != CTX::instance()->mesh.algoRecombine)
-      Msg::SetOnelabChanged();
+      Msg::SetOnelabChanged(2);
     CTX::instance()->mesh.algoRecombine = (int)val;
     if(CTX::instance()->mesh.algoRecombine < 0 &&
        CTX::instance()->mesh.algoRecombine > 1)
@@ -5805,7 +5805,7 @@ double opt_mesh_recombine_all(OPT_ARGS_NUM)
 {
   if(action & GMSH_SET){
     if(!(action & GMSH_SET_DEFAULT) && (int)val != CTX::instance()->mesh.recombineAll)
-      Msg::SetOnelabChanged();
+      Msg::SetOnelabChanged(2);
     CTX::instance()->mesh.recombineAll = (int)val;
   }
 #if defined(HAVE_FLTK)
@@ -5820,7 +5820,7 @@ double opt_mesh_recombine3d_all(OPT_ARGS_NUM)
 {
   if(action & GMSH_SET){
     if(!(action & GMSH_SET_DEFAULT) && (int)val != CTX::instance()->mesh.recombine3DAll)
-      Msg::SetOnelabChanged();
+      Msg::SetOnelabChanged(2);
     CTX::instance()->mesh.recombine3DAll = (int)val;
   }
 #if defined(HAVE_FLTK)
@@ -5930,7 +5930,7 @@ double opt_mesh_algo_subdivide(OPT_ARGS_NUM)
 {
   if(action & GMSH_SET){
     if(!(action & GMSH_SET_DEFAULT) && (int)val != CTX::instance()->mesh.algoSubdivide)
-      Msg::SetOnelabChanged();
+      Msg::SetOnelabChanged(2);
     CTX::instance()->mesh.algoSubdivide = (int)val;
     if(CTX::instance()->mesh.algoSubdivide < 0 &&
        CTX::instance()->mesh.algoSubdivide > 2)
@@ -5949,7 +5949,7 @@ double opt_mesh_algo3d(OPT_ARGS_NUM)
 {
   if(action & GMSH_SET){
     if(!(action & GMSH_SET_DEFAULT) && (int)val != CTX::instance()->mesh.algo3d)
-      Msg::SetOnelabChanged();
+      Msg::SetOnelabChanged(2);
     CTX::instance()->mesh.algo3d = (int)val;
   }
 #if defined(HAVE_FLTK)
@@ -5984,7 +5984,7 @@ double opt_mesh_mesh_only_visible(OPT_ARGS_NUM)
 {
   if(action & GMSH_SET){
     if(!(action & GMSH_SET_DEFAULT) && (int)val != CTX::instance()->mesh.meshOnlyVisible)
-      Msg::SetOnelabChanged();
+      Msg::SetOnelabChanged(2);
     CTX::instance()->mesh.meshOnlyVisible = (int)val;
   }
   return CTX::instance()->mesh.meshOnlyVisible;
@@ -5994,7 +5994,7 @@ double opt_mesh_min_circ_points(OPT_ARGS_NUM)
 {
   if(action & GMSH_SET){
     if(!(action & GMSH_SET_DEFAULT) && (int)val != CTX::instance()->mesh.minCircPoints)
-      Msg::SetOnelabChanged();
+      Msg::SetOnelabChanged(2);
     CTX::instance()->mesh.minCircPoints = (int)val;
   }
   return CTX::instance()->mesh.minCircPoints;
@@ -6018,7 +6018,7 @@ double opt_mesh_order(OPT_ARGS_NUM)
 {
   if(action & GMSH_SET){
     if(!(action & GMSH_SET_DEFAULT) && (int)val != CTX::instance()->mesh.order)
-      Msg::SetOnelabChanged();
+      Msg::SetOnelabChanged(2);
     CTX::instance()->mesh.order = (int)val;
   }
 #if defined(HAVE_FLTK)
@@ -6093,7 +6093,7 @@ double opt_mesh_second_order_linear(OPT_ARGS_NUM)
 {
   if(action & GMSH_SET){
     if(!(action & GMSH_SET_DEFAULT) && (int)val != CTX::instance()->mesh.secondOrderLinear)
-      Msg::SetOnelabChanged();
+      Msg::SetOnelabChanged(2);
     CTX::instance()->mesh.secondOrderLinear = (int)val;
   }
   return CTX::instance()->mesh.secondOrderLinear;
@@ -6103,7 +6103,7 @@ double opt_mesh_second_order_incomplete(OPT_ARGS_NUM)
 {
   if(action & GMSH_SET){
     if(!(action & GMSH_SET_DEFAULT) && (int)val != CTX::instance()->mesh.secondOrderIncomplete)
-      Msg::SetOnelabChanged();
+      Msg::SetOnelabChanged(2);
     CTX::instance()->mesh.secondOrderIncomplete = (int)val;
   }
 #if defined(HAVE_FLTK)
diff --git a/Common/gmshLocalNetworkClient.cpp b/Common/gmshLocalNetworkClient.cpp
index e2d5136e4f..21a3ad0758 100644
--- a/Common/gmshLocalNetworkClient.cpp
+++ b/Common/gmshLocalNetworkClient.cpp
@@ -399,7 +399,7 @@ bool gmshLocalNetworkClient::receiveMessage(gmshLocalNetworkClient *master)
       }
       else if(command == "set"){
 	std::string changed = onelab::parameter::getNextToken(message, first);
-	onelab::server::instance()->setChanged(changed=="true"?true:false,name);
+	onelab::server::instance()->setChanged(changed == "true" ? 31 : 0, name);
       }
     }
     break;
@@ -767,6 +767,9 @@ void resetDb(bool runGmshClient)
                persistentStrings[i].getName().c_str());
     onelab::server::instance()->set(persistentStrings[i]);
   }
+
+  // mark all parameters as changed
+  onelab::server::instance()->setChanged(3);
 }
 
 void solver_batch_cb(void *data)
@@ -842,7 +845,7 @@ void solver_batch_cb(void *data)
       o.setValue("compute");
       onelab::server::instance()->set(o);
       c->run();
-      onelab::server::instance()->setChanged(false, c->getName());
+      onelab::server::instance()->setChanged(0, c->getName());
     }
   } while(incrementLoops());
 
diff --git a/Common/onelab.h b/Common/onelab.h
index 025e605c85..49b4e724c0 100644
--- a/Common/onelab.h
+++ b/Common/onelab.h
@@ -54,12 +54,16 @@ namespace onelab{
     // a help string
     std::string _help;
     // map of clients that use this parameter, associated with a "changed" flag
-    // (set to false if the client has already been run with the current value of
-    // the parameter)
-    std::map<std::string, bool> _clients;
-    // flag indicating that the "changed" flags of this parameter will always be
-    // reset to false when the parameter is updated
-    bool _neverChanged;
+    // (set to 0 if the client has already been run with the current value of
+    // the parameter; set to defaultChangedValue() when the value of a parameter
+    // has been changed; values between 1 and defaultChangedValue() can be used
+    // to "modulate" the degree of change, e.g. to change the action to be
+    // performed depending on the "severity" of the change)
+    std::map<std::string, int> _clients;
+    // flag indicating what the "changed" value should be set to when a
+    // parameter is updated to a different value (if set to 0, the parameter
+    // will appear as never being changed)
+    int _changedValue;
     // should the parameter be visible in the interface?
     bool _visible;
     // sould the paramete be "read-only" (not settable by the user)
@@ -70,25 +74,32 @@ namespace onelab{
   public:
     parameter(const std::string &name="", const std::string &label="",
               const std::string &help="")
-      : _name(name), _label(label), _help(help), _neverChanged(false),
-        _visible(true), _readOnly(false) {}
+      : _name(name), _label(label), _help(help), _visible(true),
+        _readOnly(false)
+    {
+      _changedValue = defaultChangedValue();
+    }
     virtual ~parameter(){}
     void setName(const std::string &name){ _name = name; }
     void setLabel(const std::string &label){ _label = label; }
     void setHelp(const std::string &help){ _help = help; }
-    void setChanged(bool changed, const std::string &client="")
+    void setChanged(int changed, const std::string &client="")
     {
       if(client.size()){
-        std::map<std::string, bool>::iterator it = _clients.find(client);
+        std::map<std::string, int>::iterator it = _clients.find(client);
         if(it != _clients.end()) it->second = changed;
       }
       else{
-        for(std::map<std::string, bool>::iterator it = _clients.begin();
+        for(std::map<std::string, int>::iterator it = _clients.begin();
             it != _clients.end(); it++)
           it->second = changed;
       }
     }
-    void setNeverChanged(bool never){ _neverChanged = never; }
+    void setChangedValue(int value){ _changedValue = value; }
+    void setNeverChanged(bool never)
+    {
+      _changedValue = never ? 0 : defaultChangedValue();
+    }
     void setVisible(bool visible){ _visible = visible; }
     void setReadOnly(bool readOnly){ _readOnly = readOnly; }
     void setAttribute(const std::string &key, const std::string &value)
@@ -99,13 +110,13 @@ namespace onelab{
     {
       _attributes = attributes;
     }
-    void setClients(const std::map<std::string, bool> &clients){ _clients = clients; }
-    void addClient(const std::string &client, bool changed)
+    void setClients(const std::map<std::string, int> &clients){ _clients = clients; }
+    void addClient(const std::string &client, int changed)
     {
       if(_clients.find(client) == _clients.end())
         _clients[client] = changed;
     }
-    void addClients(const std::map<std::string, bool> &clients)
+    void addClients(const std::map<std::string, int> &clients)
     {
       _clients.insert(clients.begin(), clients.end());
     }
@@ -144,22 +155,24 @@ namespace onelab{
         s = s.substr(1);
       return s;
     }
-    bool getChanged(const std::string &client="") const
+    int getChanged(const std::string &client="") const
     {
       if(client.size()){
-        std::map<std::string, bool>::const_iterator it = _clients.find(client);
+        std::map<std::string, int>::const_iterator it = _clients.find(client);
         if(it != _clients.end()) return it->second;
-        else return false;
+        else return 0;
       }
       else{
-        for(std::map<std::string, bool>::const_iterator it = _clients.begin();
+        int changed = 0;
+        for(std::map<std::string, int>::const_iterator it = _clients.begin();
             it != _clients.end(); it++){
-          if(it->second) return true;
+          changed = std::max(changed, it->second);
         }
-        return false;
+        return changed;
       }
     }
-    bool getNeverChanged() const { return _neverChanged; }
+    int getChangedValue() const { return _changedValue; }
+    bool getNeverChanged() const { return _changedValue ? false : true; }
     bool getVisible() const { return _visible; }
     bool getReadOnly() const { return _readOnly; }
     std::string getAttribute(const std::string &key) const
@@ -172,10 +185,11 @@ namespace onelab{
     {
       return _attributes;
     }
-    const std::map<std::string, bool> &getClients() const { return _clients; }
+    const std::map<std::string, int> &getClients() const { return _clients; }
     static char charSep() { return '\0'; }
     static double maxNumber() { return 1e200; }
     static std::string version() { return "1.1"; }
+    static int defaultChangedValue() { return 31; }
     static std::string getNextToken(const std::string &msg,
                                     std::string::size_type &first,
                                     char separator=charSep())
@@ -220,7 +234,7 @@ namespace onelab{
               << sanitize(getName()) << charSep()
               << sanitize(getLabel()) << charSep()
               << sanitize(getHelp()) << charSep()
-              << (getNeverChanged() ? 1 : 0) << charSep()
+              << getChangedValue() << charSep()
               << (getVisible() ? 1 : 0) << charSep()
               << (getReadOnly() ? 1 : 0) << charSep()
               << _attributes.size() << charSep();
@@ -229,7 +243,7 @@ namespace onelab{
         sstream << sanitize(it->first) << charSep()
                 << sanitize(it->second) << charSep();
       sstream << getClients().size() << charSep();
-      for(std::map<std::string, bool>::const_iterator it = getClients().begin();
+      for(std::map<std::string, int>::const_iterator it = getClients().begin();
           it != getClients().end(); it++)
         sstream << sanitize(it->first) << charSep()
                 << (it->second ? 1 : 0) << charSep();
@@ -243,7 +257,7 @@ namespace onelab{
       setName(getNextToken(msg, pos));
       setLabel(getNextToken(msg, pos));
       setHelp(getNextToken(msg, pos));
-      setNeverChanged(atoi(getNextToken(msg, pos).c_str()));
+      setChangedValue(atoi(getNextToken(msg, pos).c_str()));
       setVisible(atoi(getNextToken(msg, pos).c_str()));
       setReadOnly(atoi(getNextToken(msg, pos).c_str()));
       int numAttributes = atoi(getNextToken(msg, pos).c_str());
@@ -255,7 +269,7 @@ namespace onelab{
       for(int i = 0; i < numClients; i++){
         std::string client(getNextToken(msg, pos));
         int changed = atoi(getNextToken(msg, pos).c_str());
-        addClient(client, changed ? true : false);
+        addClient(client, changed);
       }
       return pos;
     }
@@ -307,7 +321,7 @@ namespace onelab{
         << ", \"name\":\"" << sanitize(getName()) << "\""
         << ", \"label\":\"" << sanitize(getLabel()) << "\""
         << ", \"help\":\"" << sanitize(getHelp()) << "\""
-        << ", \"neverChanged\":" << (getNeverChanged() ? "true" : "false")
+        << ", \"changedValue\":" << getChangedValue() << "\""
         << ", \"visible\":" << (getVisible() ? "true" : "false")
         << ", \"readOnly\":" << (getReadOnly() ? "true" : "false");
       sstream << ", \"attributes\":{ ";
@@ -319,7 +333,7 @@ namespace onelab{
       }
       sstream << " }";
       sstream << ", \"clients\":{ ";
-      for(std::map<std::string, bool>::const_iterator it = getClients().begin();
+      for(std::map<std::string, int>::const_iterator it = getClients().begin();
           it != getClients().end(); it++){
         if(it != getClients().begin()) sstream << ", ";
         sstream << "\"" << sanitize(it->first) << "\":"
@@ -401,7 +415,7 @@ namespace onelab{
       setAttributes(p.getAttributes());
       if(p.getValue() != getValue()){
         setValue(p.getValue());
-        setChanged(true);
+        setChanged(getChangedValue());
       }
       setMin(p.getMin());
       setMax(p.getMax());
@@ -409,7 +423,7 @@ namespace onelab{
       setIndex(p.getIndex());
       setChoices(p.getChoices());
       setValueLabels(p.getValueLabels());
-      if(getNeverChanged()) setChanged(false);
+      if(getNeverChanged()) setChanged(0);
     }
     std::string toChar() const
     {
@@ -508,14 +522,14 @@ namespace onelab{
       setAttributes(p.getAttributes());
       if(p.getValue() != getValue()){
         setValue(p.getValue());
-        setChanged(true);
+        setChanged(getChangedValue());
       }
       if(p.getKind() != getKind()){
         setKind(p.getKind());
-        setChanged(true);
+        setChanged(getChangedValue());
       }
       setChoices(p.getChoices());
-      if(getNeverChanged()) setChanged(false);
+      if(getNeverChanged()) setChanged(0);
     }
     std::string toChar() const
     {
@@ -709,21 +723,20 @@ namespace onelab{
     }
     // check if some parameters have changed (optionnally only check the
     // parameters that depend on a given client)
-    bool getChanged(const std::string &client="") const
+    int getChanged(const std::string &client="") const
     {
       std::set<parameter*, parameterLessThan> ps;
       _getAllParameters(ps);
+      int changed = 0;
       for(std::set<parameter*, parameterLessThan>::iterator it = ps.begin();
           it != ps.end(); it++){
-        if((*it)->getChanged(client)){
-          return true;
-        }
+        changed = std::max(changed, (*it)->getChanged(client));
       }
-      return false;
+      return changed;
     }
     // set the changed flag for all the parameters that depend on the given
     // client (or for all parameters if no client name is provided)
-    void setChanged(bool changed, const std::string &client="")
+    void setChanged(int changed, const std::string &client="")
     {
       std::set<parameter*, parameterLessThan> ps;
       _getAllParameters(ps);
@@ -731,6 +744,17 @@ namespace onelab{
           it != ps.end(); it++)
         (*it)->setChanged(changed, client);
     }
+    void thresholdChanged(int threshold, const std::string &client="")
+    {
+      std::set<parameter*, parameterLessThan> ps;
+      _getAllParameters(ps);
+      for(std::set<parameter*, parameterLessThan>::iterator it = ps.begin();
+          it != ps.end(); it++){
+        int changed = (*it)->getChanged(client);
+        if(changed > threshold)
+          (*it)->setChanged(threshold, client);
+      }
+    }
     // serialize the parameter space (optionally only serialize those parameters
     // that depend on the given client)
     std::vector<std::string> toChar(const std::string &client="") const
@@ -911,14 +935,18 @@ namespace onelab{
       c->setId(_clients.size());
     }
     void unregisterClient(client *c){ _clients.erase(c); }
-    void setChanged(bool changed, const std::string &client="")
+    void setChanged(int changed, const std::string &client="")
     {
       _parameterSpace.setChanged(changed, client);
     }
-    bool getChanged(const std::string &client="")
+    int getChanged(const std::string &client="")
     {
       return _parameterSpace.getChanged(client);
     }
+    void thresholdChanged(int value, const std::string &client="")
+    {
+      _parameterSpace.thresholdChanged(value, client);
+    }
     unsigned int getNumParameters(){ return _parameterSpace.getNumParameters(); }
     std::vector<std::string> toChar(const std::string &client="")
     {
diff --git a/Common/onelabUtils.cpp b/Common/onelabUtils.cpp
index 6b808fd7e9..ac89154ea5 100644
--- a/Common/onelabUtils.cpp
+++ b/Common/onelabUtils.cpp
@@ -337,9 +337,13 @@ namespace onelabUtils {
     onelab::client *c = *it;
     std::string mshFileName = onelabUtils::getMshFileName(c);
 
-    Msg::SetOnelabAction(action);
+    int changed = onelab::server::instance()->getChanged("Gmsh");
+    // if = 0: do nothing
+    //    = 1: only save mesh (e.g. if physiscals changed)
+    //    = 2: mesh and save mesh (e.g. if char length changed)
+    //    > 2: reload geometry, mesh and save mesh (other things have changed)
 
-    static std::string modelName = GModel::current()->getName();
+    Msg::SetOnelabAction(action);
 
     if(action == "initialize"){
       // nothing to do
@@ -349,30 +353,29 @@ namespace onelabUtils {
       // nothing more to do: "check" will be called right afterwards
     }
     else if(action == "check"){
-      if(onelab::server::instance()->getChanged("Gmsh") ||
-         modelName != GModel::current()->getName()){
-        // reload geometry if Gmsh parameters have been modified or
-        // if the model name has changed
-        modelName = GModel::current()->getName();
+      if(changed > 2){
         redraw = true;
         OpenProject(GModel::current()->getFileName());
+        onelab::server::instance()->thresholdChanged(2, "Gmsh");
       }
     }
     else if(action == "compute"){
-      if(onelab::server::instance()->getChanged("Gmsh") ||
-         modelName != GModel::current()->getName()){
-        // reload the geometry, mesh it and save the mesh if Gmsh parameters
-        // have been modified or if the model name has changed
-        modelName = GModel::current()->getName();
+      if(changed){
         redraw = true;
-        OpenProject(GModel::current()->getFileName());
+        if(changed > 2){
+          OpenProject(GModel::current()->getFileName());
+        }
         if(getFirstComputationFlag() && !StatFile(mshFileName) && meshAuto != 2){
           Msg::Info("Skipping mesh generation: assuming '%s' is up-to-date "
                     "(use Solver.AutoMesh=2 to force mesh generation)",
                     mshFileName.c_str());
         }
         else if(!GModel::current()->empty() && meshAuto){
-          GModel::current()->mesh(3);
+          if(changed > 1 || StatFile(mshFileName) ||
+             (!StatFile(mshFileName) &&
+              GModel::current()->getMeshStatus() < GModel::current()->getDim())){
+            GModel::current()->mesh(3);
+          }
           CreateOutputFile(mshFileName, CTX::instance()->mesh.fileFormat);
         }
       }
@@ -380,12 +383,15 @@ namespace onelabUtils {
         // mesh+save if the mesh file does not exist
         if(meshAuto){
           redraw = true;
-          GModel::current()->mesh(3);
+          if(changed > 1 ||
+             GModel::current()->getMeshStatus() < GModel::current()->getDim()){
+            GModel::current()->mesh(3);
+          }
           CreateOutputFile(mshFileName, CTX::instance()->mesh.fileFormat);
         }
       }
       setFirstComputationFlag(false);
-      onelab::server::instance()->setChanged(false, "Gmsh");
+      onelab::server::instance()->setChanged(0, "Gmsh");
     }
 
     Msg::SetOnelabAction("");
diff --git a/Fltk/graphicWindow.cpp b/Fltk/graphicWindow.cpp
index 4f3d8f32db..c55f76908b 100644
--- a/Fltk/graphicWindow.cpp
+++ b/Fltk/graphicWindow.cpp
@@ -505,6 +505,7 @@ static void file_rename_cb(Fl_Widget *w, void *data)
     rename(GModel::current()->getFileName().c_str(), name.c_str());
     GModel::current()->setFileName(name);
     GModel::current()->setName(SplitFileName(name)[1]);
+    Msg::SetOnelabChanged(3);
     if(onelabUtils::haveSolverToRun())
       onelab_cb(0, (void*)"check");
     drawContext::global()->draw();
diff --git a/Geo/GModel.cpp b/Geo/GModel.cpp
index 8e8a5eafe6..4157e80cfd 100644
--- a/Geo/GModel.cpp
+++ b/Geo/GModel.cpp
@@ -123,10 +123,10 @@ void GModel::setFileName(std::string fileName)
 {
   _fileName = fileName;
   _fileNames.insert(fileName);
-  Msg::SetOnelabString("Gmsh/Model name", fileName, false, false, true, true);
+  Msg::SetOnelabString("Gmsh/Model name", fileName, false, false, true, 0);
   Msg::SetOnelabString
     ("Gmsh/Model absolute path", SplitFileName(GetAbsolutePath(fileName))[0],
-     false, false, true, true);
+     false, false, true, 0);
   Msg::SetWindowTitle(fileName);
 }
 
diff --git a/Geo/GeoStringInterface.cpp b/Geo/GeoStringInterface.cpp
index 3da2542988..885079b694 100644
--- a/Geo/GeoStringInterface.cpp
+++ b/Geo/GeoStringInterface.cpp
@@ -139,11 +139,20 @@ void add_infile(const std::string &text, const std::string &fileName, bool force
   Msg::Error("GEO file creation not available without Gmsh parser");
 #endif
 
-  // mark all Gmsh data as changed in onelab (will force e.g. a reload and a
-  // remesh)
-#if defined(HAVE_ONELAB)
-  onelab::server::instance()->setChanged(true, "Gmsh");
-#endif
+  // mark Gmsh data as changed in onelab
+  if(text.find("Physical") != std::string::npos){
+    // re-import the physical groups in onelab, and only ask to re-save the mesh
+    Msg::ImportPhysicalGroupsInOnelab();
+    Msg::SetOnelabChanged(1);
+  }
+  else if(text.find("Characteristic") != std::string::npos){
+    // only ask to remesh and re-save
+    Msg::SetOnelabChanged(2);
+  }
+  else{
+    // ask to reload the geometry, remesh and re-save
+    Msg::SetOnelabChanged(3);
+  }
 }
 
 void coherence(const std::string &fileName)
-- 
GitLab