openMSX
UserSettings.cc
Go to the documentation of this file.
1#include "UserSettings.hh"
2
3#include "BooleanSetting.hh"
4#include "EnumSetting.hh"
5#include "FloatSetting.hh"
6#include "IntegerSetting.hh"
7#include "SettingsManager.hh"
8#include "StringSetting.hh"
9
11#include "CommandException.hh"
12#include "TclObject.hh"
13
14#include "checked_cast.hh"
15#include "ranges.hh"
16
17#include <cassert>
18#include <memory>
19
20namespace openmsx {
21
22// class UserSettings
23
25 : userSettingCommand(commandController_)
26{
27}
28
30{
31 assert(!findSetting(info.setting->getFullName()));
32 settings.push_back(std::move(info));
33}
34
36{
37 move_pop_back(settings, rfind_unguarded(settings, &setting,
38 [](auto& info) { return info.setting.get(); }));
39}
40
41Setting* UserSettings::findSetting(std::string_view name) const
42{
43 auto it = ranges::find(settings, name, [](auto& info) {
44 return info.setting->getFullName(); });
45 return (it != end(settings)) ? it->setting.get() : nullptr;
46}
47
48
49// class UserSettings::Cmd
50
51UserSettings::Cmd::Cmd(CommandController& commandController_)
52 : Command(commandController_, "user_setting")
53{
54}
55
56void UserSettings::Cmd::execute(std::span<const TclObject> tokens, TclObject& result)
57{
58 checkNumArgs(tokens, AtLeast{2}, "subcommand ?arg ...?");
59 executeSubCommand(tokens[1].getString(),
60 "create", [&]{ create(tokens, result); },
61 "destroy", [&]{ destroy(tokens, result); },
62 "info", [&]{ info(tokens, result); });
63}
64
65void UserSettings::Cmd::create(std::span<const TclObject> tokens, TclObject& result)
66{
67 checkNumArgs(tokens, AtLeast{5}, Prefix{2}, "type name ?arg ...?");
68 const auto& type = tokens[2].getString();
69 const auto& settingName = tokens[3].getString();
70
71 auto& controller = checked_cast<GlobalCommandController&>(getCommandController());
72 if (controller.getSettingsManager().findSetting(settingName)) {
73 throw CommandException(
74 "There already exists a setting with this name: ", settingName);
75 }
76
77 auto getInfo = [&] {
78 if (type == "string") {
79 return createString(tokens);
80 } else if (type == "boolean") {
81 return createBoolean(tokens);
82 } else if (type == "integer") {
83 return createInteger(tokens);
84 } else if (type == "float") {
85 return createFloat(tokens);
86 } else if (type == "enum") {
87 return createEnum(tokens);
88 } else {
89 throw CommandException(
90 "Invalid setting type '", type, "', expected "
91 "'string', 'boolean', 'integer', 'float' or 'enum'.");
92 }
93 };
94 auto& userSettings = OUTER(UserSettings, userSettingCommand);
95 userSettings.addSetting(getInfo());
96
97 result = tokens[3]; // name
98}
99
100UserSettings::Info UserSettings::Cmd::createString(std::span<const TclObject> tokens) const
101{
102 checkNumArgs(tokens, 6, Prefix{3}, "name description initial-value");
103 const auto& sName = tokens[3].getString();
104 const auto& desc = tokens[4].getString();
105 const auto& initVal = tokens[5].getString();
106
107 auto [storage, view] = make_string_storage(desc);
108 return {std::make_unique<StringSetting>(getCommandController(), sName,
109 view, initVal),
110 std::move(storage)};
111}
112
113UserSettings::Info UserSettings::Cmd::createBoolean(std::span<const TclObject> tokens) const
114{
115 checkNumArgs(tokens, 6, Prefix{3}, "name description initial-value");
116 const auto& sName = tokens[3].getString();
117 const auto& desc = tokens[4].getString();
118 const auto& initVal = tokens[5].getBoolean(getInterpreter());
119
120 auto [storage, view] = make_string_storage(desc);
121 return {std::make_unique<BooleanSetting>(getCommandController(), sName,
122 view, initVal),
123 std::move(storage)};
124}
125
126UserSettings::Info UserSettings::Cmd::createInteger(std::span<const TclObject> tokens) const
127{
128 checkNumArgs(tokens, 8, Prefix{3}, "name description initial-value min-value max-value");
129 auto& interp = getInterpreter();
130 const auto& sName = tokens[3].getString();
131 const auto& desc = tokens[4].getString();
132 const auto& initVal = tokens[5].getInt(interp);
133 const auto& minVal = tokens[6].getInt(interp);
134 const auto& maxVal = tokens[7].getInt(interp);
135
136 auto [storage, view] = make_string_storage(desc);
137 return {std::make_unique<IntegerSetting>(getCommandController(), sName,
138 view, initVal, minVal, maxVal),
139 std::move(storage)};
140}
141
142UserSettings::Info UserSettings::Cmd::createFloat(std::span<const TclObject> tokens) const
143{
144 checkNumArgs(tokens, 8, Prefix{3}, "name description initial-value min-value max-value");
145 auto& interp = getInterpreter();
146 const auto& sName = tokens[3].getString();
147 const auto& desc = tokens[4].getString();
148 const auto& initVal = tokens[5].getDouble(interp);
149 const auto& minVal = tokens[6].getDouble(interp);
150 const auto& maxVal = tokens[7].getDouble(interp);
151
152 auto [storage, view] = make_string_storage(desc);
153 return {std::make_unique<FloatSetting>(getCommandController(), sName,
154 view, initVal, minVal, maxVal),
155 std::move(storage)};
156}
157
158UserSettings::Info UserSettings::Cmd::createEnum(std::span<const TclObject> tokens) const
159{
160 checkNumArgs(tokens, 7, Prefix{3}, "name description initial-value allowed-values-list");
161 const auto& sName = tokens[3].getString();
162 const auto& desc = tokens[4].getString();
163 const auto& initStr = tokens[5].getString();
164 const auto& list = tokens[6];
165
166 int initVal = -1;
167 int i = 0;
168 auto map = to_vector(view::transform(list, [&](const auto& s) {
169 if (s == initStr) initVal = i;
170 return EnumSettingBase::MapEntry{std::string(s), i++};
171 }));
172 if (initVal == -1) {
173 throw CommandException(
174 "Initial value '", initStr, "' "
175 "must be one of the allowed values '",
176 list.getString(), '\'');
177 }
178
179 auto [storage, view] = make_string_storage(desc);
180 return {std::make_unique<EnumSetting<int>>(
181 getCommandController(), sName, view, initVal, std::move(map)),
182 std::move(storage)};
183}
184
185void UserSettings::Cmd::destroy(std::span<const TclObject> tokens, TclObject& /*result*/)
186{
187 checkNumArgs(tokens, 3, "name");
188 const auto& settingName = tokens[2].getString();
189
190 auto& userSettings = OUTER(UserSettings, userSettingCommand);
191 auto* setting = userSettings.findSetting(settingName);
192 if (!setting) {
193 throw CommandException(
194 "There is no user setting with this name: ", settingName);
195 }
196 userSettings.deleteSetting(*setting);
197}
198
199void UserSettings::Cmd::info(std::span<const TclObject> /*tokens*/, TclObject& result) const
200{
201 result.addListElements(getSettingNames());
202}
203
204std::string UserSettings::Cmd::help(std::span<const TclObject> tokens) const
205{
206 if (tokens.size() < 2) {
207 return
208 "Manage user-defined settings.\n"
209 "\n"
210 "User defined settings are mainly used in Tcl scripts "
211 "to create variables (=settings) that are persistent over "
212 "different openMSX sessions.\n"
213 "\n"
214 " user_setting create <type> <name> <description> <init-value> [<min-value> <max-value>]\n"
215 " user_setting destroy <name>\n"
216 " user_setting info\n"
217 "\n"
218 "Use 'help user_setting <subcommand>' to see more info "
219 "on a specific subcommand.";
220 }
221 assert(tokens.size() >= 2);
222 if (tokens[1] == "create") {
223 return
224 "user_setting create <type> <name> <description> <init-value> [<min-value> <max-value> | <value-list>]\n"
225 "\n"
226 "Create a user defined setting. The extra arguments have the following meaning:\n"
227 " <type> The type for the setting, must be 'string', 'boolean', 'integer' or 'float'.\n"
228 " <name> The name for the setting.\n"
229 " <description> A (short) description for this setting.\n"
230 " This text can be queried via 'help set <setting>'.\n"
231 " <init-value> The initial value for the setting.\n"
232 " This value is only used the very first time the setting is created, otherwise the value is taken from previous openMSX sessions.\n"
233 " <min-value> This parameter is only required for 'integer' and 'float' setting types.\n"
234 " Together with max-value this parameter defines the range of valid values.\n"
235 " <max-value> See min-value.\n"
236 " <value-list> Enum settings have no min and max but instead have a list of possible values";
237
238 } else if (tokens[1] == "destroy") {
239 return
240 "user_setting destroy <name>\n"
241 "\n"
242 "Remove a previously defined user setting. This only "
243 "removes the setting from the current openMSX session, "
244 "the value of this setting is still preserved for "
245 "future sessions.";
246
247 } else if (tokens[1] == "info") {
248 return
249 "user_setting info\n"
250 "\n"
251 "Returns a list of all user defined settings that are "
252 "active in this openMSX session.";
253
254 } else {
255 return "No such subcommand, see 'help user_setting'.";
256 }
257}
258
259void UserSettings::Cmd::tabCompletion(std::vector<std::string>& tokens) const
260{
261 using namespace std::literals;
262 if (tokens.size() == 2) {
263 static constexpr std::array cmds = {
264 "create"sv, "destroy"sv, "info"sv,
265 };
266 completeString(tokens, cmds);
267 } else if ((tokens.size() == 3) && (tokens[1] == "create")) {
268 static constexpr std::array types = {
269 "string"sv, "boolean"sv, "integer"sv, "float"sv, "enum"sv
270 };
271 completeString(tokens, types);
272 } else if ((tokens.size() == 3) && (tokens[1] == "destroy")) {
273 completeString(tokens, getSettingNames());
274 }
275}
276
277} // namespace openmsx
BaseSetting * setting
void deleteSetting(Setting &setting)
void addSetting(Info &&info)
Setting * findSetting(std::string_view name) const
UserSettings(CommandController &commandController)
std::unique_ptr< IDEDevice > create(const DeviceConfig &config)
This file implemented 3 utility functions:
Definition Autofire.cc:11
auto find(InputRange &&range, const T &value)
Definition ranges.hh:160
Definition view.hh:15
constexpr auto transform(Range &&range, UnaryOp op)
Definition view.hh:520
#define OUTER(type, member)
Definition outer.hh:42
auto make_string_storage(std::string_view sv)
Take a string_view, make a copy of it, and return a pair of.
auto to_vector(Range &&range) -> std::vector< detail::ToVectorType< T, decltype(std::begin(range))> >
Definition stl.hh:275
void move_pop_back(VECTOR &v, typename VECTOR::iterator it)
Erase the pointed to element from the given vector.
Definition stl.hh:134
auto rfind_unguarded(RANGE &range, const VAL &val, Proj proj={})
Similar to the find(_if)_unguarded functions above, but searches from the back to front.
Definition stl.hh:109
constexpr auto end(const zstring_view &x)