Allow immediate saving at point of save
[lsnes.git] / src / core / settings.cpp
blob186fcd4304162005c480bdcaf0f0ac42062419db
1 #include "core/command.hpp"
2 #include "core/dispatch.hpp"
3 #include "core/globalwrap.hpp"
4 #include "core/misc.hpp"
5 #include "core/settings.hpp"
6 #include "library/string.hpp"
8 #include <map>
9 #include <sstream>
10 #include <iostream>
12 namespace
14 globalwrap<std::map<std::string, setting*>> settings;
16 function_ptr_command<const std::string&> set_setting("set-setting", "set a setting",
17 "Syntax: set-setting <setting> [<value>]\nSet setting to a new value. Omit <value> to set to ''\n",
18 [](const std::string& t) throw(std::bad_alloc, std::runtime_error) {
19 auto r = regex("([^ \t]+)([ \t]+(|[^ \t].*))?", t, "Setting name required.");
20 setting::set(r[1], r[3]);
21 messages << "Setting '" << r[1] << "' set to '" << r[3] << "'" << std::endl;
22 });
24 function_ptr_command<const std::string&> unset_setting("unset-setting", "unset a setting",
25 "Syntax: unset-setting <setting>\nTry to unset a setting. Note that not all settings can be unset\n",
26 [](const std::string& t) throw(std::bad_alloc, std::runtime_error) {
27 auto r = regex("([^ \t]+)[ \t]*", t, "Expected setting name and nothing else");
28 setting::blank(r[1]);
29 messages << "Setting '" << r[1] << "' unset" << std::endl;
30 });
32 function_ptr_command<const std::string&> get_command("get-setting", "get value of a setting",
33 "Syntax: get-setting <setting>\nShow value of setting\n",
34 [](const std::string& t) throw(std::bad_alloc, std::runtime_error) {
35 auto r = regex("([^ \t]+)[ \t]*", t, "Expected setting name and nothing else");
36 if(setting::is_set(r[1]))
37 messages << "Setting '" << r[1] << "' has value '"
38 << setting::get(r[1]) << "'" << std::endl;
39 else
40 messages << "Setting '" << r[1] << "' is unset" << std::endl;
41 });
43 function_ptr_command<> show_settings("show-settings", "Show values of all settings",
44 "Syntax: show-settings\nShow value of all settings\n",
45 []() throw(std::bad_alloc, std::runtime_error) {
46 for(auto i : setting::get_settings_set()) {
47 if(!setting::is_set(i))
48 messages << i << ": (unset)" << std::endl;
49 else
50 messages << i << ": " << setting::get(i) << std::endl;
52 });
54 //Return the mutex.
55 mutex& stlock()
57 static mutex& m = mutex::aquire();
58 return m;
61 class stlock_hold
63 public:
64 stlock_hold() { stlock().lock(); }
65 ~stlock_hold() { stlock().unlock(); }
66 private:
67 stlock_hold(const stlock_hold& k);
68 stlock_hold& operator=(const stlock_hold& k);
72 std::map<std::string, std::string> setting::invalid_values;
73 bool setting::storage_mode = false;
75 setting::setting(const std::string& name) throw(std::bad_alloc)
77 stlock_hold lck;
78 mut = &mutex::aquire();
79 settings()[settingname = name] = this;
82 setting::~setting() throw()
84 stlock_hold lck;
85 delete mut;
86 settings().erase(settingname);
89 setting* setting::get_by_name(const std::string& name)
91 stlock_hold lck;
92 if(!settings().count(name))
93 throw std::runtime_error("No such setting '" + name + "'");
94 return settings()[name];
97 void setting::set(const std::string& _setting, const std::string& value) throw(std::bad_alloc, std::runtime_error)
99 setting* tochange;
100 try {
101 tochange = get_by_name(_setting);
102 } catch(...) {
103 if(storage_mode)
104 invalid_values[_setting] = value;
105 throw;
107 try {
109 lock_holder lck(tochange);
110 tochange->set(value);
111 invalid_values.erase(_setting);
113 information_dispatch::do_setting_change(_setting, value);
114 } catch(std::bad_alloc& e) {
115 throw;
116 } catch(std::exception& e) {
117 if(storage_mode)
118 invalid_values[_setting] = value;
119 throw std::runtime_error("Can't set setting '" + _setting + "': " + e.what());
123 bool setting::blank(bool really) throw(std::bad_alloc, std::runtime_error)
125 return false;
128 bool setting::blankable(const std::string& _setting) throw(std::bad_alloc, std::runtime_error)
130 setting* tochange = get_by_name(_setting);
131 try {
133 lock_holder lck(tochange);
134 return tochange->blank(false);
136 } catch(...) {
137 return false;
141 void setting::blank(const std::string& _setting) throw(std::bad_alloc, std::runtime_error)
143 setting* tochange = get_by_name(_setting);
144 try {
146 lock_holder lck(tochange);
147 if(!tochange->blank(true))
148 throw std::runtime_error("This setting can't be cleared");
149 invalid_values.erase(_setting);
151 information_dispatch::do_setting_clear(_setting);
152 } catch(std::bad_alloc& e) {
153 throw;
154 } catch(std::exception& e) {
155 throw std::runtime_error("Can't clear setting '" + _setting + "': " + e.what());
159 std::string setting::get(const std::string& _setting) throw(std::bad_alloc, std::runtime_error)
161 setting* tochange = get_by_name(_setting);
163 lock_holder lck(tochange);
164 return tochange->get();
168 bool setting::is_set(const std::string& _setting) throw(std::bad_alloc, std::runtime_error)
170 setting* tochange = get_by_name(_setting);
172 lock_holder lck(tochange);
173 return tochange->is_set();
177 std::set<std::string> setting::get_settings_set() throw(std::bad_alloc)
179 std::set<std::string> r;
180 for(auto i : settings())
181 r.insert(i.first);
182 return r;
185 void setting::set_storage_mode(bool enable) throw()
187 storage_mode = enable;
190 std::map<std::string, std::string> setting::get_invalid_values() throw(std::bad_alloc)
192 return invalid_values;
196 numeric_setting::numeric_setting(const std::string& sname, int32_t minv, int32_t maxv, int32_t dflt)
197 throw(std::bad_alloc)
198 : setting(sname)
200 minimum = minv;
201 maximum = maxv;
202 value = dflt;
205 bool numeric_setting::is_set() throw()
207 return true;
210 void numeric_setting::set(const std::string& _value) throw(std::bad_alloc, std::runtime_error)
212 int32_t v = parse_value<int32_t>(_value);
213 if(v < minimum || v > maximum) {
214 std::ostringstream x;
215 x << "Value out of range (" << minimum << " - " << maximum << ")";
216 throw std::runtime_error(x.str());
218 value = v;
221 std::string numeric_setting::get() throw(std::bad_alloc)
223 std::ostringstream x;
224 x << value;
225 return x.str();
228 numeric_setting::operator int32_t() throw()
230 lock_holder lck(this);
231 return value;
234 boolean_setting::boolean_setting(const std::string& sname, bool dflt) throw(std::bad_alloc)
235 : setting(sname)
237 value = dflt;
240 bool boolean_setting::is_set() throw()
242 return true;
245 void boolean_setting::set(const std::string& v) throw(std::bad_alloc, std::runtime_error)
247 switch(string_to_bool(v)) {
248 case 1:
249 value = true;
250 break;
251 case 0:
252 value = false;
253 break;
254 default:
255 throw std::runtime_error("Invalid value for boolean setting");
259 std::string boolean_setting::get() throw(std::bad_alloc)
261 if(value)
262 return "true";
263 else
264 return "false";
267 boolean_setting::operator bool() throw()
269 lock_holder lck(this);
270 return value;
273 path_setting::path_setting(const std::string& sname) throw(std::bad_alloc)
274 : setting(sname)
276 path = ".";
277 _default = true;
280 bool path_setting::blank(bool really) throw(std::bad_alloc, std::runtime_error)
282 if(!really)
283 return true;
284 path = ".";
285 _default = true;
286 return true;
289 bool path_setting::is_set() throw()
291 return !_default;
294 void path_setting::set(const std::string& value) throw(std::bad_alloc, std::runtime_error)
296 if(value == "") {
297 path = ".";
298 _default = true;
299 } else {
300 path = value;
301 _default = false;
305 std::string path_setting::get() throw(std::bad_alloc)
307 return path;
310 path_setting::operator std::string()
312 lock_holder lck(this);
313 return path;