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