allows the user to restore the default game options, refs #3737

This was SVN commit r17739.
This commit is contained in:
mimo 2016-02-07 15:10:44 +00:00
parent 43f358563c
commit 971d70fa99
7 changed files with 167 additions and 65 deletions

View File

@ -5,7 +5,6 @@ function init(data)
{
if (data && data.callback)
g_HasCallback = true;
let revert = data && data.revert;
g_Controls = {};
var options = Engine.ReadJSONFile("gui/options/options.json");
@ -21,9 +20,10 @@ function init(data)
let label = Engine.GetGUIObjectByName(category + "Label[" + i + "]");
let config = option.parameters.config;
g_Controls[config] = {
"control": setupControl(option, i, category, revert),
"control": setupControl(option, i, category),
"type": option.type,
"dependencies": option.dependencies || undefined
"dependencies": option.dependencies || undefined,
"parameters": option.parameters
};
label.caption = translate(option.label);
label.tooltip = option.tooltip ? translate(option.tooltip) : "";
@ -47,7 +47,7 @@ function init(data)
continue;
if (!opt.dependencies || opt.dependencies.indexOf(config) === -1)
continue;
label.caption = " " + label.caption;
label.caption = " " + label.caption;
break;
}
// Show element.
@ -55,9 +55,7 @@ function init(data)
}
}
updateDependencies();
if (!revert)
updateStatus();
updateOptionPanel();
}
/**
@ -66,7 +64,7 @@ function init(data)
* @param option Structure containing the data to setup an option.
* @param prefix Prefix to use when accessing control, for example "generalSetting" when the tickbox name is generalSettingTickbox[i].
*/
function setupControl(option, i, category, revert)
function setupControl(option, i, category)
{
var control;
var onUpdate;
@ -92,26 +90,21 @@ function setupControl(option, i, category, revert)
{
case "config":
keyConfig = option.parameters.config;
if (checked === undefined || revert)
checked = Engine.ConfigDB_GetValue("user", keyConfig) === "true";
else if ((Engine.ConfigDB_GetValue("user", keyConfig) === "true") !== checked)
{
Engine.ConfigDB_CreateValue("user", keyConfig, String(checked));
updateStatus(true);
}
checked = Engine.ConfigDB_GetValue("user", keyConfig) === "true";
break;
case "renderer":
keyRenderer = option.parameters.renderer;
if (!Engine["Renderer_Get" + keyRenderer + "Enabled"])
{
warn(" invalid renderer key " + keyRenderer);
warn("Invalid renderer key " + keyRenderer);
keyRenderer = undefined;
break;
}
if (checked === undefined)
checked = Engine["Renderer_Get" + keyRenderer + "Enabled"]();
else if (Engine["Renderer_Get" + keyRenderer + "Enabled"]() !== checked)
if (Engine["Renderer_Get" + keyRenderer + "Enabled"]() !== checked)
{
warn("Incompatible renderer option value for " + keyRenderer);
Engine["Renderer_Set" + keyRenderer + "Enabled"](checked);
}
break;
default:
warn("Unknown option source type '" + param + "'");
@ -130,9 +123,11 @@ function setupControl(option, i, category, revert)
if (keyRenderer)
Engine["Renderer_Set" + keyRenderer + "Enabled"](val);
if (keyConfig)
{
Engine.ConfigDB_CreateValue("user", keyConfig, String(val));
updateDependencies();
updateStatus(true);
Engine.ConfigDB_SetChanges("user", true);
}
updateOptionPanel();
};
}(keyRenderer, keyConfig, inverted);
@ -188,10 +183,10 @@ function setupControl(option, i, category, revert)
if (Engine.ConfigDB_GetValue("user", key) === this.caption)
return;
Engine.ConfigDB_CreateValue("user", key, this.caption);
Engine.ConfigDB_SetChanges("user", true);
if (functionBody)
Engine[functionBody](+this.caption);
updateDependencies();
updateStatus(true);
updateOptionPanel();
};
}(key, functionBody, minval, maxval);
@ -201,6 +196,7 @@ function setupControl(option, i, category, revert)
break;
case "dropdown":
control = Engine.GetGUIObjectByName(category + "Dropdown[" + i + "]");
control.onSelectionChange = function(){}; // just the time to setup the value
var caption;
var key;
var functionBody;
@ -237,8 +233,8 @@ function setupControl(option, i, category, revert)
if (key === "materialmgr.quality")
val = val == 0 ? 2 : val == 1 ? 5 : 8;
Engine.ConfigDB_CreateValue("user", key, val);
updateDependencies();
updateStatus(true);
Engine.ConfigDB_SetChanges("user", true);
updateOptionPanel();
};
}(key);
@ -254,15 +250,23 @@ function setupControl(option, i, category, revert)
return control;
}
function updateStatus(val)
function updateOptionPanel()
{
if (typeof val == "boolean")
Engine.ConfigDB_CreateValue("user", "nosave.haschanges", String(val));
else
val = Engine.ConfigDB_GetValue("user", "nosave.haschanges") === "true";
// Update dependencies
for (let item in g_Controls)
{
let control = g_Controls[item];
if (control.type !== "boolean" && control.type !== "invertedboolean" || !control.dependencies)
continue;
Engine.GetGUIObjectByName("loadOptions").enabled = val;
Engine.GetGUIObjectByName("saveOptions").enabled = val;
for (let dependency of control.dependencies)
g_Controls[dependency].control.enabled = control.control.checked;
}
// And main buttons
let hasChanges = Engine.ConfigDB_HasChanges("user");
Engine.GetGUIObjectByName("revertChanges").enabled = hasChanges;
Engine.GetGUIObjectByName("saveChanges").enabled = hasChanges;
}
/**
@ -275,31 +279,50 @@ function registerChanges()
g_Controls[item].control.onPress();
}
function updateDependencies()
function setDefaults()
{
let btCaptions = [translate("No"), translate("Yes")];
let btCode = [null, function(){ reallySetDefaults(); }];
messageBox(500, 200, translate("Resetting the options will erase your saved settings. Do you want to continue ?"),
translate("Warning"), 0, btCaptions, btCode);
}
function reallySetDefaults()
{
for (let item in g_Controls)
{
let control = g_Controls[item];
if (control.type !== "boolean" && control.type !== "invertedboolean" || !control.dependencies)
continue;
Engine.ConfigDB_RemoveValue("user", item);
for (let dependency of control.dependencies)
g_Controls[dependency].control.enabled = control.control.checked;
}
Engine.ConfigDB_WriteFile("user", "config/user.cfg");
revertChanges();
}
function revertChanges()
{
Engine.ConfigDB_Reload("user");
updateStatus(false);
init({ "revert": true });
// needs to update renderer values (which are all of boolean type)
for (let item in g_Controls)
{
let control = g_Controls[item];
if (!control.parameters.renderer)
continue;
if (control.type !== "boolean" && control.type !== "invertedboolean")
{
warn("Invalid type option defined in renderer '" + control.type + "': will not be reverted");
continue;
}
let checked = Engine.ConfigDB_GetValue("user", item) === "true";
Engine["Renderer_Set" + control.parameters.renderer + "Enabled"](checked);
}
Engine.ConfigDB_SetChanges("user", false);
init();
}
function saveChanges()
{
registerChanges();
Engine.ConfigDB_WriteFile("user", "config/user.cfg");
updateStatus(false);
Engine.ConfigDB_SetChanges("user", false);
updateOptionPanel();
}
/**
@ -308,7 +331,7 @@ function saveChanges()
function closePage()
{
registerChanges();
if (Engine.ConfigDB_GetValue("user", "nosave.haschanges") === "true")
if (Engine.ConfigDB_HasChanges("user"))
{
let btCaptions = [translate("No"), translate("Yes")];
let btCode = [null, function(){ closePageWithoutConfirmation(); }];

View File

@ -68,13 +68,13 @@
"type": "boolean",
"label": "Prefer GLSL",
"tooltip": "Use OpenGL 2.0 shaders (recommended)",
"parameters": { "renderer": "PreferGLSL", "config": "preferglsl" }
"parameters": { "config": "preferglsl", "renderer": "PreferGLSL" }
},
{
"type": "boolean",
"label": "Post Processing",
"tooltip": "Use screen-space postprocessing filters (HDR, Bloom, DOF, etc)",
"parameters": { "renderer": "Postproc", "config": "postproc" }
"parameters": { "config": "postproc", "renderer": "Postproc" }
},
{
"type": "dropdown",
@ -86,75 +86,75 @@
"type": "boolean",
"label": "Shadows",
"tooltip": "Enable shadows",
"parameters": { "renderer": "Shadows", "config": "shadows"},
"parameters": { "config": "shadows", "renderer": "Shadows" },
"dependencies": [ "shadowpcf" ]
},
{
"type": "boolean",
"label": "Shadow Filtering",
"tooltip": "Smooth shadows",
"parameters": { "renderer": "ShadowPCF", "config": "shadowpcf" }
"parameters": { "config": "shadowpcf", "renderer": "ShadowPCF" }
},
{
"type": "boolean",
"label": "Unit Silhouettes",
"tooltip": "Show outlines of units behind buildings",
"parameters": { "renderer": "Silhouettes", "config": "silhouettes" }
"parameters": { "config": "silhouettes", "renderer": "Silhouettes" }
},
{
"type": "boolean",
"label": "Particles",
"tooltip": "Enable particles",
"parameters": { "renderer": "Particles", "config": "particles" }
"parameters": { "config": "particles", "renderer": "Particles" }
},
{
"type": "invertedboolean",
"label": "Activate water effects",
"tooltip": "When OFF, use the lowest settings possible to render water. This makes other settings irrelevant.",
"parameters": { "renderer": "WaterUgly", "config": "waterugly" },
"parameters": { "config": "waterugly", "renderer": "WaterUgly" },
"dependencies": [ "waterfancyeffects", "waterrealdepth", "waterreflection", "waterrefraction", "watershadows" ]
},
{
"type": "boolean",
"label": "HQ Water Effects",
"tooltip": "Use higher-quality effects for water, rendering coastal waves, shore foam, and ships trails.",
"parameters": { "renderer": "WaterFancyEffects", "config": "waterfancyeffects" }
"parameters": { "config": "waterfancyeffects", "renderer": "WaterFancyEffects" }
},
{
"type": "boolean",
"label": "Real Water Depth",
"tooltip": "Use actual water depth in rendering calculations",
"parameters": { "renderer": "WaterRealDepth", "config": "waterrealdepth" }
"parameters": { "config": "waterrealdepth", "renderer": "WaterRealDepth" }
},
{
"type": "boolean",
"label": "Water Reflections",
"tooltip": "Allow water to reflect a mirror image",
"parameters": { "renderer": "WaterReflection", "config": "waterreflection" }
"parameters": { "config": "waterreflection", "renderer": "WaterReflection" }
},
{
"type": "boolean",
"label": "Water Refraction",
"tooltip": "Use a real water refraction map and not transparency",
"parameters": { "renderer": "WaterRefraction", "config": "waterrefraction" }
"parameters": { "config": "waterrefraction", "renderer": "WaterRefraction" }
},
{
"type": "boolean",
"label": "Shadows on Water",
"tooltip": "Cast shadows on water",
"parameters": { "renderer": "WaterShadows", "config": "watershadows" }
"parameters": { "config": "watershadows", "renderer": "WaterShadows" }
},
{
"type": "boolean",
"label": "Smooth LOS",
"tooltip": "Lift darkness and fog-of-war smoothly",
"parameters": { "renderer": "SmoothLOS", "config": "smoothlos" }
"parameters": { "config": "smoothlos", "renderer": "SmoothLOS" }
},
{
"type": "boolean",
"label": "Show Sky",
"tooltip": "Render Sky",
"parameters": { "renderer": "ShowSky", "config": "showsky" }
"parameters": { "config": "showsky", "renderer": "ShowSky" }
},
{
"type": "boolean",

View File

@ -69,17 +69,22 @@
</object>
</repeat>
</object>
<object name="loadOptions" type="button" style="ModernButtonRed" size="50%-170 100%-44 50%-70 100%-16" hotkey="cancel">
<object type="button" style="ModernButtonRed" size="50%-236 100%-44 50%-136 100%-16">
<translatableAttribute id="caption">Reset</translatableAttribute>
<translatableAttribute id="tooltip">Resets user settings to their game default</translatableAttribute>
<action on="Press">setDefaults();</action>
</object>
<object name="revertChanges" type="button" style="ModernButtonRed" size="50%-104 100%-44 50%-4 100%-16">
<translatableAttribute id="caption">Revert</translatableAttribute>
<translatableAttribute id="tooltip">Revert to previous saved settings</translatableAttribute>
<translatableAttribute id="tooltip">Reverts to previous saved settings</translatableAttribute>
<action on="Press">revertChanges();</action>
</object>
<object name="saveOptions" type="button" style="ModernButtonRed" size="50%-62 100%-44 50%+38 100%-16">
<object name="saveChanges" type="button" style="ModernButtonRed" size="50%+4 100%-44 50%+104 100%-16">
<translatableAttribute id="caption">Save</translatableAttribute>
<translatableAttribute id="tooltip">Save changes</translatableAttribute>
<translatableAttribute id="tooltip">Saves changes</translatableAttribute>
<action on="Press">saveChanges();</action>
</object>
<object type="button" style="ModernButtonRed" size="50%+70 100%-44 50%+170 100%-16">
<object type="button" style="ModernButtonRed" size="50%+136 100%-44 50%+236 100%-16">
<translatableAttribute id="caption">Close</translatableAttribute>
<translatableAttribute id="tooltip">Unsaved changes affect this session only</translatableAttribute>
<action on="Press">closePage();</action>

View File

@ -29,6 +29,7 @@
typedef std::map<CStr, CConfigValueSet> TConfigMap;
TConfigMap CConfigDB::m_Map[CFG_LAST];
VfsPath CConfigDB::m_ConfigFile[CFG_LAST];
bool CConfigDB::m_HasChanges[CFG_LAST];
static pthread_mutex_t cfgdb_mutex = PTHREAD_MUTEX_INITIALIZER;
@ -114,6 +115,22 @@ GETVAL(double)
GETVAL(std::string)
#undef GETVAL
bool CConfigDB::HasChanges(EConfigNamespace ns) const
{
CHECK_NS(false);
CScopeLock s(&cfgdb_mutex);
return m_HasChanges[ns];
}
void CConfigDB::SetChanges(EConfigNamespace ns, bool& value)
{
CHECK_NS(;);
CScopeLock s(&cfgdb_mutex);
m_HasChanges[ns] = value;
}
void CConfigDB::GetValues(EConfigNamespace ns, const CStr& name, CConfigValueSet& values) const
{
CHECK_NS(;);
@ -195,6 +212,17 @@ void CConfigDB::SetValueBool(EConfigNamespace ns, const CStr& name, const bool v
SetValueString(ns, name, valueString);
}
void CConfigDB::RemoveValue(EConfigNamespace ns, const CStr& name)
{
CHECK_NS(;);
CScopeLock s(&cfgdb_mutex);
TConfigMap::iterator it = m_Map[ns].find(name);
if (it == m_Map[ns].end())
return;
m_Map[ns].erase(it);
}
void CConfigDB::SetConfigFile(EConfigNamespace ns, const VfsPath& path)
{
CHECK_NS(;);
@ -384,8 +412,6 @@ bool CConfigDB::WriteFile(EConfigNamespace ns, const VfsPath& path) const
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)

View File

@ -52,6 +52,7 @@ class CConfigDB: public Singleton<CConfigDB>
{
static std::map<CStr, CConfigValueSet> m_Map[];
static VfsPath m_ConfigFile[];
static bool m_HasChanges[];
public:
CConfigDB();
@ -71,6 +72,13 @@ public:
///@copydoc CConfigDB::GetValue
void GetValue(EConfigNamespace ns, const CStr& name, std::string& value);
/**
* Returns true if changed with respect to last write on file
*/
bool HasChanges(EConfigNamespace ns) const;
void SetChanges(EConfigNamespace ns, bool& value);
/**
* Attempt to retrieve a vector of values corresponding to the given setting;
* will search CFG_COMMAND first, and then all namespaces from the specified
@ -98,6 +106,11 @@ public:
void SetValueString(EConfigNamespace ns, const CStr& name, const CStr& value);
void SetValueBool(EConfigNamespace ns, const CStr& name, const bool value);
/**
* Remove a config value in the specified namespace.
*/
void RemoveValue(EConfigNamespace ns, const CStr& name);
/**
* Set the path to the config file used to populate the specified namespace

View File

@ -42,6 +42,25 @@ bool JSI_ConfigDB::GetConfigNamespace(const std::wstring& cfgNsString, EConfigNa
return true;
}
bool JSI_ConfigDB::HasChanges(ScriptInterface::CxPrivate* UNUSED(pCxPrivate), const std::wstring& cfgNsString)
{
EConfigNamespace cfgNs;
if (!GetConfigNamespace(cfgNsString, cfgNs))
return false;
return g_ConfigDB.HasChanges(cfgNs);
}
bool JSI_ConfigDB::SetChanges(ScriptInterface::CxPrivate* UNUSED(pCxPrivate), const std::wstring& cfgNsString, bool value)
{
EConfigNamespace cfgNs;
if (!GetConfigNamespace(cfgNsString, cfgNs))
return false;
g_ConfigDB.SetChanges(cfgNs, value);
return true;
}
std::string JSI_ConfigDB::GetValue(ScriptInterface::CxPrivate* UNUSED(pCxPrivate), const std::wstring& cfgNsString, const std::string& name)
{
EConfigNamespace cfgNs;
@ -63,6 +82,16 @@ bool JSI_ConfigDB::CreateValue(ScriptInterface::CxPrivate* UNUSED(pCxPrivate), c
return true;
}
bool JSI_ConfigDB::RemoveValue(ScriptInterface::CxPrivate* UNUSED(pCxPrivate), const std::wstring& cfgNsString, const std::string& name)
{
EConfigNamespace cfgNs;
if (!GetConfigNamespace(cfgNsString, cfgNs))
return false;
g_ConfigDB.RemoveValue(cfgNs, name);
return true;
}
bool JSI_ConfigDB::WriteFile(ScriptInterface::CxPrivate* UNUSED(pCxPrivate), const std::wstring& cfgNsString, const Path& path)
{
EConfigNamespace cfgNs;
@ -105,8 +134,11 @@ bool JSI_ConfigDB::SetFile(ScriptInterface::CxPrivate* UNUSED(pCxPrivate), const
void JSI_ConfigDB::RegisterScriptFunctions(ScriptInterface& scriptInterface)
{
scriptInterface.RegisterFunction<bool, std::wstring, &JSI_ConfigDB::HasChanges>("ConfigDB_HasChanges");
scriptInterface.RegisterFunction<bool, std::wstring, bool, &JSI_ConfigDB::SetChanges>("ConfigDB_SetChanges");
scriptInterface.RegisterFunction<std::string, std::wstring, std::string, &JSI_ConfigDB::GetValue>("ConfigDB_GetValue");
scriptInterface.RegisterFunction<bool, std::wstring, std::string, std::string, &JSI_ConfigDB::CreateValue>("ConfigDB_CreateValue");
scriptInterface.RegisterFunction<bool, std::wstring, std::string, &JSI_ConfigDB::RemoveValue>("ConfigDB_RemoveValue");
scriptInterface.RegisterFunction<bool, std::wstring, Path, &JSI_ConfigDB::WriteFile>("ConfigDB_WriteFile");
scriptInterface.RegisterFunction<bool, std::wstring, std::string, std::string, Path, &JSI_ConfigDB::WriteValueToFile>("ConfigDB_WriteValueToFile");
scriptInterface.RegisterFunction<bool, std::wstring, Path, &JSI_ConfigDB::SetFile>("ConfigDB_SetFile");

View File

@ -24,8 +24,11 @@
namespace JSI_ConfigDB
{
bool GetConfigNamespace(const std::wstring& cfgNsString, EConfigNamespace& cfgNs);
bool HasChanges(ScriptInterface::CxPrivate* pCxPrivate, const std::wstring& cfgNsString);
bool SetChanges(ScriptInterface::CxPrivate* pCxPrivate, const std::wstring& cfgNsString, bool value);
std::string GetValue(ScriptInterface::CxPrivate* pCxPrivate, const std::wstring& cfgNsString, const std::string& name);
bool CreateValue(ScriptInterface::CxPrivate* pCxPrivate, const std::wstring& cfgNsString, const std::string& name, const std::string& value);
bool RemoveValue(ScriptInterface::CxPrivate* pCxPrivate, const std::wstring& cfgNsString, const std::string& name);
bool WriteFile(ScriptInterface::CxPrivate* pCxPrivate, const std::wstring& cfgNsString, const Path& path);
bool WriteValueToFile(ScriptInterface::CxPrivate* pCxPrivate, const std::wstring& cfgNsString, const std::string& name, const std::string& value, const Path& path);
bool Reload(ScriptInterface::CxPrivate* pCxPrivate, const std::wstring& cfgNsString);