openMSX
OSDGUI.cc
Go to the documentation of this file.
1 #include "OSDGUI.hh"
2 #include "OSDWidget.hh"
3 #include "OSDTopWidget.hh"
4 #include "OSDRectangle.hh"
5 #include "OSDText.hh"
6 #include "Display.hh"
7 #include "Command.hh"
8 #include "CommandException.hh"
9 #include "TclObject.hh"
10 #include "StringOp.hh"
11 #include "memory.hh"
12 #include <algorithm>
13 #include <cassert>
14 
15 using std::string;
16 using std::shared_ptr;
17 using std::vector;
18 
19 namespace openmsx {
20 
21 class OSDCommand : public Command
22 {
23 public:
24  OSDCommand(OSDGUI& gui, CommandController& commandController);
25  virtual void execute(const vector<TclObject>& tokens, TclObject& result);
26  virtual string help(const vector<string>& tokens) const;
27  virtual void tabCompletion(vector<string>& tokens) const;
28 
29 private:
30  void create (const vector<TclObject>& tokens, TclObject& result);
31  void destroy (const vector<TclObject>& tokens, TclObject& result);
32  void info (const vector<TclObject>& tokens, TclObject& result);
33  void exists (const vector<TclObject>& tokens, TclObject& result);
34  void configure(const vector<TclObject>& tokens, TclObject& result);
35  shared_ptr<OSDWidget> create(string_ref type, const string& name) const;
36  void configure(OSDWidget& widget, const vector<TclObject>& tokens,
37  unsigned skip);
38 
39  OSDWidget& getWidget(string_ref name) const;
40 
41  OSDGUI& gui;
42 };
43 
44 
45 // class OSDGUI
46 
47 OSDGUI::OSDGUI(CommandController& commandController, Display& display_)
48  : display(display_)
49  , osdCommand(make_unique<OSDCommand>(*this, commandController))
50  , topWidget(make_unique<OSDTopWidget>())
51 {
52 }
53 
55 {
56 }
57 
59 {
60  return display;
61 }
62 
64 {
65  return *topWidget;
66 }
67 
68 void OSDGUI::refresh() const
69 {
70  getDisplay().repaintDelayed(40000); // 25 fps
71 }
72 
73 
74 // class OSDCommand
75 
77  : Command(commandController, "osd")
78  , gui(gui_)
79 {
80 }
81 
82 void OSDCommand::execute(const vector<TclObject>& tokens, TclObject& result)
83 {
84  if (tokens.size() < 2) {
85  throw SyntaxError();
86  }
87  string_ref subCommand = tokens[1].getString();
88  if (subCommand == "create") {
89  create(tokens, result);
90  gui.refresh();
91  } else if (subCommand == "destroy") {
92  destroy(tokens, result);
93  gui.refresh();
94  } else if (subCommand == "info") {
95  info(tokens, result);
96  } else if (subCommand == "exists") {
97  exists(tokens, result);
98  } else if (subCommand == "configure") {
99  configure(tokens, result);
100  gui.refresh();
101  } else {
102  throw CommandException(
103  "Invalid subcommand '" + subCommand + "', expected "
104  "'create', 'destroy', 'info', 'exists' or 'configure'.");
105  }
106 }
107 
108 void OSDCommand::create(const vector<TclObject>& tokens, TclObject& result)
109 {
110  if (tokens.size() < 4) {
111  throw SyntaxError();
112  }
113  string_ref type = tokens[2].getString();
114  string_ref fullname = tokens[3].getString();
115  string_ref parentname, name;
116  StringOp::splitOnLast(fullname, '.', parentname, name);
117  if (name.empty()) std::swap(parentname, name);
118 
119  OSDWidget* parent = gui.getTopWidget().findSubWidget(parentname);
120  if (!parent) {
121  throw CommandException(
122  "Parent widget doesn't exist yet:" + parentname);
123  }
124  if (parent->findSubWidget(name)) {
125  throw CommandException(
126  "There already exists a widget with this name: " +
127  fullname);
128  }
129 
130  shared_ptr<OSDWidget> widget = create(type, name.str());
131  configure(*widget, tokens, 4);
132  parent->addWidget(widget);
133 
134  result.setString(fullname);
135 }
136 
137 shared_ptr<OSDWidget> OSDCommand::create(
138  string_ref type, const string& name) const
139 {
140  if (type == "rectangle") {
141  return std::make_shared<OSDRectangle>(gui, name);
142  } else if (type == "text") {
143  return std::make_shared<OSDText>(gui, name);
144  } else {
145  throw CommandException(
146  "Invalid widget type '" + type + "', expected "
147  "'rectangle' or 'text'.");
148  }
149 }
150 
151 void OSDCommand::destroy(const vector<TclObject>& tokens, TclObject& result)
152 {
153  if (tokens.size() != 3) {
154  throw SyntaxError();
155  }
156  OSDWidget* widget = gui.getTopWidget().findSubWidget(tokens[2].getString());
157  if (!widget) {
158  // widget not found, not an error
159  result.setBoolean(false);
160  return;
161  }
162  OSDWidget* parent = widget->getParent();
163  if (!parent) {
164  throw CommandException("Can't destroy the top widget.");
165  }
166  parent->deleteWidget(*widget);
167  result.setBoolean(true);
168 }
169 
170 void OSDCommand::info(const vector<TclObject>& tokens, TclObject& result)
171 {
172  switch (tokens.size()) {
173  case 2: {
174  // list widget names
175  vector<string> names;
176  gui.getTopWidget().listWidgetNames("", names);
177  result.addListElements(names);
178  break;
179  }
180  case 3: {
181  // list properties for given widget
182  const OSDWidget& widget = getWidget(tokens[2].getString());
183  result.addListElements(widget.getProperties());
184  break;
185  }
186  case 4: {
187  // get current value for given widget/property
188  const OSDWidget& widget = getWidget(tokens[2].getString());
189  widget.getProperty(tokens[3].getString(), result);
190  break;
191  }
192  default:
193  throw SyntaxError();
194  }
195 }
196 
197 void OSDCommand::exists(const vector<TclObject>& tokens, TclObject& result)
198 {
199  if (tokens.size() != 3) {
200  throw SyntaxError();
201  }
202  OSDWidget* widget = gui.getTopWidget().findSubWidget(tokens[2].getString());
203  result.setBoolean(widget != nullptr);
204 }
205 
206 void OSDCommand::configure(const vector<TclObject>& tokens, TclObject& /*result*/)
207 {
208  if (tokens.size() < 3) {
209  throw SyntaxError();
210  }
211  configure(getWidget(tokens[2].getString()), tokens, 3);
212 }
213 
214 void OSDCommand::configure(OSDWidget& widget, const vector<TclObject>& tokens,
215  unsigned skip)
216 {
217  assert(tokens.size() >= skip);
218  if ((tokens.size() - skip) & 1) {
219  // odd number of extra arguments
220  throw CommandException(
221  "Missing value for '" + tokens.back().getString() + "'.");
222  }
223 
224  for (size_t i = skip; i < tokens.size(); i += 2) {
225  const auto& name = tokens[i + 0].getString();
226  widget.setProperty(name, tokens[i + 1]);
227  }
228 }
229 
230 string OSDCommand::help(const vector<string>& tokens) const
231 {
232  if (tokens.size() >= 2) {
233  if (tokens[1] == "create") {
234  return
235  "osd create <type> <widget-path> [<property-name> <property-value>]...\n"
236  "\n"
237  "Creates a new OSD widget of given type. Path is a "
238  "hierarchical name for the widget (separated by '.'). "
239  "The parent widget for this new widget must already "
240  "exist.\n"
241  "Optionally you can set initial values for one or "
242  "more properties.\n"
243  "This command returns the path of the newly created "
244  "widget. This is path is again needed to configure "
245  "or to remove the widget. It may be useful to assign "
246  "this path to a variable.";
247  } else if (tokens[1] == "destroy") {
248  return
249  "osd destroy <widget-path>\n"
250  "\n"
251  "Remove the specified OSD widget. Returns '1' on "
252  "success and '0' when widget couldn't be destroyed "
253  "because there was no widget with that name";
254  } else if (tokens[1] == "info") {
255  return
256  "osd info [<widget-path> [<property-name>]]\n"
257  "\n"
258  "Query various information about the OSD status. "
259  "You can call this command with 0, 1 or 2 arguments.\n"
260  "Without any arguments, this command returns a list "
261  "of all existing widget IDs.\n"
262  "When a path is given as argument, this command "
263  "returns a list of available properties for that widget.\n"
264  "When both path and property name arguments are "
265  "given, this command returns the current value of "
266  "that property.";
267  } else if (tokens[1] == "exists") {
268  return
269  "osd exists <widget-path>\n"
270  "\n"
271  "Test whether there exists a widget with given name. "
272  "This subcommand is meant to be used in scripts.";
273  } else if (tokens[1] == "configure") {
274  return
275  "osd configure <widget-path> [<property-name> <property-value>]...\n"
276  "\n"
277  "Modify one or more properties on the given widget.";
278  } else {
279  return "No such subcommand, see 'help osd'.";
280  }
281  } else {
282  return
283  "Low level OSD GUI commands\n"
284  " osd create <type> <widget-path> [<property-name> <property-value>]...\n"
285  " osd destroy <widget-path>\n"
286  " osd info [<widget-path> [<property-name>]]\n"
287  " osd exists <widget-path>\n"
288  " osd configure <widget-path> [<property-name> <property-value>]...\n"
289  "Use 'help osd <subcommand>' to see more info on a specific subcommand";
290  }
291 }
292 
293 void OSDCommand::tabCompletion(vector<string>& tokens) const
294 {
295  if (tokens.size() == 2) {
296  static const char* const cmds[] = {
297  "create", "destroy", "info", "exists", "configure"
298  };
299  completeString(tokens, cmds);
300  } else if ((tokens.size() == 3) && (tokens[1] == "create")) {
301  static const char* const types[] = { "rectangle", "text" };
302  completeString(tokens, types);
303  } else if ((tokens.size() == 3) ||
304  ((tokens.size() == 4) && (tokens[1] == "create"))) {
305  vector<string> names;
306  gui.getTopWidget().listWidgetNames("", names);
307  completeString(tokens, names);
308  } else {
309  try {
310  vector<string_ref> properties;
311  if (tokens[1] == "create") {
312  shared_ptr<OSDWidget> widget = create(tokens[2], "");
313  properties = widget->getProperties();
314  } else if ((tokens[1] == "configure") ||
315  (tokens[1] == "info")) {
316  const OSDWidget& widget = getWidget(tokens[2]);
317  properties = widget.getProperties();
318  }
319  completeString(tokens, properties);
320  } catch (MSXException&) {
321  // ignore
322  }
323  }
324 }
325 
326 OSDWidget& OSDCommand::getWidget(string_ref name) const
327 {
328  OSDWidget* widget = gui.getTopWidget().findSubWidget(name);
329  if (!widget) {
330  throw CommandException("No widget with name " + name);
331  }
332  return *widget;
333 }
334 
335 } // namespace openmsx
OSDWidget * getParent()
Definition: OSDWidget.cc:141
Represents the output window/screen of openMSX.
Definition: Display.hh:33
std::string str() const
Definition: string_ref.cc:10
void splitOnLast(string_ref str, string_ref chars, string_ref &first, string_ref &last)
Definition: StringOp.cc:323
OSDWidget * findSubWidget(string_ref name)
Definition: OSDWidget.cc:156
OSDWidget & getTopWidget() const
Definition: OSDGUI.cc:63
This class implements a subset of the proposal for std::string_ref (proposed for the next c++ standar...
Definition: string_ref.hh:18
void refresh() const
Definition: OSDGUI.cc:68
OSDGUI(CommandController &commandController, Display &display)
Definition: OSDGUI.cc:47
Display & getDisplay() const
Definition: OSDGUI.cc:58
void repaintDelayed(uint64_t delta)
Definition: Display.cc:406
virtual string help(const vector< string > &tokens) const
Print help for this command.
Definition: OSDGUI.cc:230
static void completeString(std::vector< std::string > &tokens, const RANGE &possibleValues, bool caseSensitive=true)
Definition: Completer.hh:88
void setString(string_ref value)
Definition: TclObject.cc:99
virtual void tabCompletion(vector< string > &tokens) const
Attempt tab completion for this command.
Definition: OSDGUI.cc:293
virtual void execute(const vector< TclObject > &tokens, TclObject &result)
Execute this command.
Definition: OSDGUI.cc:82
virtual std::vector< string_ref > getProperties() const
Definition: OSDWidget.cc:254
OSDCommand(OSDGUI &gui, CommandController &commandController)
Definition: OSDGUI.cc:76
std::unique_ptr< T > make_unique()
Definition: memory.hh:27
bool empty() const
Definition: string_ref.hh:56