openMSX
SettingsManager.cc
Go to the documentation of this file.
1 #include "SettingsManager.hh"
3 #include "MSXException.hh"
4 #include "TclObject.hh"
5 #include "CommandException.hh"
6 #include "XMLElement.hh"
7 #include "outer.hh"
8 #include "StringOp.hh"
9 #include "view.hh"
10 #include "vla.hh"
11 #include <cassert>
12 #include <cstring>
13 
14 using std::string;
15 using std::string_view;
16 using std::vector;
17 
18 namespace openmsx {
19 
20 // SettingsManager implementation:
21 
23  : settingInfo (commandController.getOpenMSXInfoCommand())
24  , setCompleter (commandController)
25  , incrCompleter (commandController, *this, "incr")
26  , unsetCompleter(commandController, *this, "unset")
27 {
28 }
29 
31 {
32  assert(settings.empty());
33 }
34 
36 {
37  assert(!settings.contains(setting.getFullNameObj()));
38  settings.emplace_noDuplicateCheck(&setting);
39 }
40 
42 {
43  const auto& name = setting.getFullNameObj();
44  assert(settings.contains(name));
45  settings.erase(name);
46 }
47 
48 BaseSetting* SettingsManager::findSetting(string_view name) const
49 {
50  if (auto it = settings.find(name); it != end(settings)) {
51  return *it;
52  }
53  if (StringOp::startsWith(name, "::")) {
54  // try without leading ::
55  if (auto it = settings.find(name.substr(2)); it != end(settings)) {
56  return *it;
57  }
58  } else {
59  // try adding ::
60  if (auto it = settings.find(tmpStrCat("::", name)); it != end(settings)) {
61  return *it;
62  }
63  }
64  return nullptr;
65 }
66 
67 BaseSetting* SettingsManager::findSetting(string_view prefix, string_view baseName) const
68 {
69  auto size = prefix.size() + baseName.size();
70  VLA(char, fullname, size);
71  memcpy(&fullname[0], prefix .data(), prefix .size());
72  memcpy(&fullname[prefix.size()], baseName.data(), baseName.size());
73  return findSetting(string_view(fullname, size));
74 }
75 
76 // Helper functions for setting commands
77 
78 BaseSetting& SettingsManager::getByName(string_view cmd, string_view name) const
79 {
80  if (auto* setting = findSetting(name)) {
81  return *setting;
82  }
83  throw CommandException(cmd, ": ", name, ": no such setting");
84 }
85 
86 vector<string> SettingsManager::getTabSettingNames() const
87 {
88  vector<string> result;
89  result.reserve(settings.size() * 2);
90  for (auto* s : settings) {
91  string_view name = s->getFullName();
92  result.emplace_back(name);
93  if (StringOp::startsWith(name, "::")) {
94  result.emplace_back(name.substr(2));
95  } else {
96  result.push_back(strCat("::", name));
97  }
98  }
99  return result;
100 }
101 
103 {
104  // restore default values
105  for (auto* s : settings) {
106  if (s->needLoadSave()) {
107  s->setValue(s->getRestoreValue());
108  }
109  }
110 
111  // load new values
112  const auto* settingsElem = config.findChild("settings");
113  if (!settingsElem) return;
114  for (auto* s : settings) {
115  if (!s->needLoadSave()) continue;
116  if (const auto* elem = settingsElem->findChildWithAttribute(
117  "setting", "id", s->getFullName())) {
118  try {
119  s->setValue(TclObject(elem->getData()));
120  } catch (MSXException&) {
121  // ignore, keep default value
122  }
123  }
124  }
125 }
126 
127 
128 // class SettingInfo
129 
130 SettingsManager::SettingInfo::SettingInfo(InfoCommand& openMSXInfoCommand)
131  : InfoTopic(openMSXInfoCommand, "setting")
132 {
133 }
134 
135 void SettingsManager::SettingInfo::execute(
136  span<const TclObject> tokens, TclObject& result) const
137 {
138  auto& manager = OUTER(SettingsManager, settingInfo);
139  switch (tokens.size()) {
140  case 2:
141  result.addListElements(view::transform(
142  manager.settings,
143  [](auto* p) { return p->getFullNameObj(); }));
144  break;
145  case 3: {
146  const auto& settingName = tokens[2].getString();
147  auto* setting = manager.findSetting(settingName);
148  if (!setting) {
149  throw CommandException("No such setting: ", settingName);
150  }
151  try {
152  setting->info(result);
153  } catch (MSXException& e) {
154  throw CommandException(e.getMessage());
155 
156  }
157  break;
158  }
159  default:
160  throw CommandException("Too many parameters.");
161  }
162 }
163 
164 string SettingsManager::SettingInfo::help(const vector<string>& /*tokens*/) const
165 {
166  return "openmsx_info setting : "
167  "returns list of all settings\n"
168  "openmsx_info setting <name> : "
169  "returns info on a specific setting\n";
170 }
171 
172 void SettingsManager::SettingInfo::tabCompletion(vector<string>& tokens) const
173 {
174  if (tokens.size() == 3) {
175  // complete setting name
176  auto& manager = OUTER(SettingsManager, settingInfo);
177  completeString(tokens, manager.getTabSettingNames());
178  }
179 }
180 
181 
182 // class SetCompleter
183 
184 SettingsManager::SetCompleter::SetCompleter(
185  CommandController& commandController_)
186  : CommandCompleter(commandController_, "set")
187 {
188 }
189 
190 string SettingsManager::SetCompleter::help(const vector<string>& tokens) const
191 {
192  if (tokens.size() == 2) {
193  auto& manager = OUTER(SettingsManager, setCompleter);
194  return string(manager.getByName("set", tokens[1]).getDescription());
195  }
196  return "Set or query the value of a openMSX setting or Tcl variable\n"
197  " set <setting> shows current value\n"
198  " set <setting> <value> set a new value\n"
199  "Use 'help set <setting>' to get more info on a specific\n"
200  "openMSX setting.\n";
201 }
202 
203 void SettingsManager::SetCompleter::tabCompletion(vector<string>& tokens) const
204 {
205  auto& manager = OUTER(SettingsManager, setCompleter);
206  switch (tokens.size()) {
207  case 2: {
208  // complete setting name
209  completeString(tokens, manager.getTabSettingNames(), false); // case insensitive
210  break;
211  }
212  case 3:
213  // complete setting value
214  if (auto* setting = manager.findSetting(tokens[1])) {
215  setting->tabCompletion(tokens);
216  }
217  break;
218  }
219 }
220 
221 
222 // class SettingCompleter
223 
224 SettingsManager::SettingCompleter::SettingCompleter(
225  CommandController& commandController_, SettingsManager& manager_,
226  const string& name_)
227  : CommandCompleter(commandController_, name_)
228  , manager(manager_)
229 {
230 }
231 
232 string SettingsManager::SettingCompleter::help(const vector<string>& /*tokens*/) const
233 {
234  return {}; // TODO
235 }
236 
237 void SettingsManager::SettingCompleter::tabCompletion(vector<string>& tokens) const
238 {
239  if (tokens.size() == 2) {
240  // complete setting name
241  completeString(tokens, manager.getTabSettingNames());
242  }
243 }
244 
245 } // namespace openmsx
const TclObject & getFullNameObj() const
Get the name of this setting.
Definition: Setting.hh:35
BaseSetting * findSetting(std::string_view name) const
Find the setting with given name.
SettingsManager(const SettingsManager &)=delete
void unregisterSetting(BaseSetting &setting)
void loadSettings(const XMLElement &config)
void registerSetting(BaseSetting &setting)
const XMLElement * findChild(std::string_view childName) const
Definition: XMLElement.cc:67
Definition: span.hh:126
constexpr index_type size() const noexcept
Definition: span.hh:296
bool startsWith(string_view total, string_view part)
Definition: StringOp.cc:33
This file implemented 3 utility functions:
Definition: Autofire.cc:9
size_t size(std::string_view utf8)
constexpr auto transform(Range &&range, UnaryOp op)
Definition: view.hh:306
#define OUTER(type, member)
Definition: outer.hh:41
TemporaryString tmpStrCat(Ts &&... ts)
Definition: strCat.hh:659
std::string strCat(Ts &&...ts)
Definition: strCat.hh:591
#define VLA(TYPE, NAME, LENGTH)
Definition: vla.hh:10
constexpr auto end(const zstring_view &x)
Definition: zstring_view.hh:83