Index: binaries/data/mods/public/gui/options/options.js
===================================================================
--- binaries/data/mods/public/gui/options/options.js	(révision 17638)
+++ binaries/data/mods/public/gui/options/options.js	(copie de travail)
@@ -87,6 +87,9 @@
 			body.hidden = false;
 		}
 	}
+
+	if (!reload)
+		updateChanges();
 }
 
 /**
@@ -98,7 +101,6 @@
 function setupControl(option, i, prefix, reload)
 {
 	var control;
-	var onPress = function(){ g_hasChanges = true; };
 
 	switch (option[3])
 	{
@@ -110,56 +112,51 @@
 		text.size = size;
 		control = Engine.GetGUIObjectByName(prefix + "Tickbox[" + i + "]");
 		var checked;
-		// Different option action load and save differently, so this switch is needed.
+		var keyRenderer;
+		var keyConfig;
+		var functionBody;
+
 		for (let action of Object.keys(option[2]))
 		{
 			switch (action)
 			{
 			case "config":
-				// Load initial value if not yet loaded.
-				if (!checked || typeof checked != "boolean")
-					checked = Engine.ConfigDB_GetValue("user", option[2][action]) === "true";
-				// Hacky macro to create the callback.
-				var callback = function(key)
-				{
-					return function()
-						Engine.ConfigDB_CreateValue("user", key, String(this.checked));
-				}(option[2][action]);
-				// Merge the new callback with any existing callbacks.
-				onPress = mergeFunctions(callback, onPress);
+				keyConfig = option[2].config;
+				if (checked === undefined || reload)
+					checked = Engine.ConfigDB_GetValue("user", keyConfig) === "true";
+				else if ((Engine.ConfigDB_GetValue("user", keyConfig) === "true") !== checked)
+					Engine.ConfigDB_CreateValue("user", keyConfig, String(checked));
 				break;
 			case "renderer":
-				// If reloading, config values have priority, otherwise load initial value if not yet loaded
-				if (reload && option[2].config)
-				{
-					checked = Engine.ConfigDB_GetValue("user", option[2].config) === "true";
-					let rendererChecked = eval("Engine.Renderer_Get" + option[2].renderer + "Enabled()");
-					if (rendererChecked != checked)
-						eval("Engine.Renderer_Set" + option[2].renderer + "Enabled(" + checked + ")");
-				}
-				else if (!checked || typeof checked != "boolean")
-					checked = eval("Engine.Renderer_Get" + option[2][action] + "Enabled()");
-				// Hacky macro to create the callback (updating also the config value if any).
-				var callback = function(key, keyConfig)
-				{
-					return function()
-					{
-						eval("Engine.Renderer_Set" + key + "Enabled(" + this.checked + ")");
-						if (keyConfig)
-							Engine.ConfigDB_CreateValue("user", keyConfig, String(this.checked));
-					};
-				}(option[2][action], option[2].config);
-				// Merge the new callback with any existing callbacks.
-				onPress = mergeFunctions(callback, onPress);
+				keyRenderer = option[2].renderer;
+				if (checked === undefined)
+					checked = eval("Engine.Renderer_Get" + keyRenderer + "Enabled()");
+				else if (eval("Engine.Renderer_Get" + keyRenderer + "Enabled()") !== checked)
+					eval("Engine.Renderer_Set" + keyRenderer + "Enabled(" + checked + ")");
 				break;
 			case "function":
 				// This allows for doing low-level actions, like hiding/showing UI elements.
-				onPress = mergeFunctions(eval("function(){" + option[2][action] + "}"), onPress);
+				functionBody = option[2].function;
 				break;
 			default:
 				warn("Unknown option source type '" + action + "'");
 			}
 		}
+
+		var onPress = function(keyRenderer, keyConfig, functionBody)
+		{
+			return function()
+			{
+				if (keyRenderer)
+					eval("Engine.Renderer_Set" + keyRenderer + "Enabled(" + this.checked + ")");
+				if (keyConfig)
+					Engine.ConfigDB_CreateValue("user", keyConfig, String(this.checked));
+				if (functionBody)
+					eval(functionBody);
+				updateChanges(true);
+			};
+		}(keyRenderer, keyConfig, functionBody);
+
 		// Load final data to the control element.
 		control.checked = checked;
 		control.onPress = onPress;
@@ -169,41 +166,45 @@
 	case "string":
 		control = Engine.GetGUIObjectByName(prefix + "Input[" + i + "]");
 		var caption;
+		var key;
+		var functionBody;
+
 		for (let action of Object.keys(option[2]))
 		{
 			switch (action)
 			{
 			case "config":
-				onPress = function(){};
-				caption = Engine.ConfigDB_GetValue("user", option[2][action]);
-				// Hacky macro to create the callback.
-				var callback = function(key)
-				{
-					return function()
-					{
-						if (Engine.ConfigDB_GetValue("user", key) == this.caption)
-							return;
-						Engine.ConfigDB_CreateValue("user", key, String(this.caption));
-						g_hasChanges = true;
-					};
-				}(option[2][action]);
-				// Merge the new callback with any existing callbacks.
-				onPress = mergeFunctions(callback, onPress);
+				key = option[2].config;
+				caption = Engine.ConfigDB_GetValue("user", key);
 				break;
 			case "function":
 				// This allows for doing low-level actions, like hiding/showing UI elements.
-				onPress = mergeFunctions(function(){eval(option[2][action]);}, onPress);
+				functionBody = option[2].function;
 				break;
 			default:
 				warn("Unknown option source type '" + action + "'");
 			}
 		}
+
+		// as we use MouseLeave events, we must ensure that something has been modified
+		var onMouseLeave = function(key, functionBody)
+		{
+			return function()
+			{
+				if (Engine.ConfigDB_GetValue("user", key) === this.caption)
+					return;
+				Engine.ConfigDB_CreateValue("user", key, String(this.caption));
+				if (functionBody)
+					eval(functionBody);
+				updateChanges(true);
+			};
+		}(key, functionBody);
+
 		control.caption = caption;
-		control.onPress = onPress;
-		control.onMouseLeave = onPress;
+		control.onMouseLeave = onMouseLeave;
 		break;
 	default:
-		warn("Unknown option type '" + options[3] + "', assuming string. Valid types are 'number', 'string', or 'bool'.");
+		warn("Unknown option type '" + option[3] + "', assuming string. Valid types are 'number', 'string', or 'bool'.");
 		control = Engine.GetGUIObjectByName(prefix + "Input[" + i + "]");
 		break;
 	}
@@ -212,31 +213,28 @@
 	return control;
 }
 
-/**
- * Merge two functions which don't expect arguments.
- *
- * @return Merged function.
- */
-function mergeFunctions(function1, function2)
+function updateChanges(val)
 {
-	return function()
-	{
-		function1.apply(this);
-		function2.apply(this);
-	};
+	if (typeof val == "boolean")
+		Engine.ConfigDB_CreateValue("user", "nosave.haschanges", String(val));
+	else
+		val = Engine.ConfigDB_GetValue("user", "nosave.haschanges") === "true";
+
+	Engine.GetGUIObjectByName("loadOptions").enabled = val;
+	Engine.GetGUIObjectByName("saveOptions").enabled = val;
 }
 
 function reloadDefaults()
 {
 	Engine.ConfigDB_Reload("user");
+	updateChanges(false);
 	init({ "reload": true });
-	g_hasChanges = false;
 }
 
 function saveDefaults()
 {
-	g_hasChanges = false;
 	Engine.ConfigDB_WriteFile("user", "config/user.cfg");
+	updateChanges(false);
 }
 
 /**
@@ -244,7 +242,7 @@
  **/
 function closePage()
 {
-	if (g_hasChanges)
+	if (Engine.ConfigDB_GetValue("user", "nosave.haschanges") === "true")
 	{
 		let btCaptions = [translate("No"), translate("Yes")];
 		let btCode = [null, function(){ closePageWithoutConfirmation(); }];
Index: binaries/data/mods/public/gui/options/options.xml
===================================================================
--- binaries/data/mods/public/gui/options/options.xml	(révision 17638)
+++ binaries/data/mods/public/gui/options/options.xml	(copie de travail)
@@ -65,14 +65,14 @@
 				</object>
 			</repeat>
 		</object>
-		<object type="button" style="ModernButtonRed" size="50%-170 100%-44 50%-70 100%-16" hotkey="cancel">
+		<object name="loadOptions" type="button" style="ModernButtonRed" size="50%-170 100%-44 50%-70 100%-16" hotkey="cancel">
 			<translatableAttribute id="caption">Reload</translatableAttribute>
-			<translatableAttribute id="tooltip">Reload default values from file</translatableAttribute>
+			<translatableAttribute id="tooltip">Reload previous saved settings</translatableAttribute>
 			<action on="Press">reloadDefaults();</action>
 		</object>
-		<object type="button" style="ModernButtonRed" size="50%-62 100%-44 50%+38 100%-16">
+		<object name="saveOptions" type="button" style="ModernButtonRed" size="50%-62 100%-44 50%+38 100%-16">
 			<translatableAttribute id="caption">Save</translatableAttribute>
-			<translatableAttribute id="tooltip">Save changes to file</translatableAttribute>
+			<translatableAttribute id="tooltip">Save changes</translatableAttribute>
 			<action on="Press">saveDefaults();</action>
 		</object>
 		<object type="button" style="ModernButtonRed" size="50%+70 100%-44 50%+170 100%-16">
Index: source/ps/ConfigDB.cpp
===================================================================
--- source/ps/ConfigDB.cpp	(révision 17638)
+++ source/ps/ConfigDB.cpp	(copie de travail)
@@ -378,6 +378,8 @@
 	char* pos = (char*)buf.get();
 	for (const std::pair<CStr, CConfigValueSet>& p : m_Map[ns])
 	{
+		if (boost::algorithm::starts_with(p.first, "nosave."))
+			continue;
 		size_t i;
 		pos += sprintf(pos, "%s = ", p.first.c_str());
 		for (i = 0; i < p.second.size() - 1; ++i)
