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