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