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