]> git.mxchange.org Git - flightgear.git/blob - src/GUI/menubar.cxx
- remove unused puObject args in gui fgcommands
[flightgear.git] / src / GUI / menubar.cxx
1 #ifdef HAVE_CONFIG_H
2 #  include <config.h>
3 #endif
4
5 #include <string.h>
6 #include <iostream>
7 #include <plib/pu.h>
8 #include <simgear/debug/logstream.hxx>
9
10 #include <Input/input.hxx>
11 #include <Main/globals.hxx>
12
13 #include "new_gui.hxx"
14 #include "menubar.hxx"
15
16
17 \f
18 ////////////////////////////////////////////////////////////////////////
19 // FIXME!!
20 //
21 // Deprecated wrappers for old menu commands.
22 //
23 // DO NOT ADD TO THESE.  THEY WILL BE DELETED SOON!
24 //
25 // These are defined in gui_funcs.cxx.  They should be replaced with
26 // user-configured dialogs and new commands where necessary.
27 ////////////////////////////////////////////////////////////////////////
28
29 extern void reInit (void);
30 static bool
31 do_reinit_dialog (const SGPropertyNode * arg)
32 {
33     reInit();
34     return true;
35 }
36
37 #if defined(TR_HIRES_SNAP)
38 extern void dumpHiResSnapShot ();
39 static bool
40 do_hires_snapshot_dialog (const SGPropertyNode * arg)
41 {
42     dumpHiResSnapShot();
43     return true;
44 }
45 #endif // TR_HIRES_SNAP
46
47 #if defined( WIN32 ) && !defined( __CYGWIN__) && !defined(__MINGW32__)
48 extern void printScreen ();
49 static bool
50 do_print_dialog (const SGPropertyNode * arg)
51 {
52     printScreen();
53     return true;
54 }
55 #endif
56
57 extern void helpCb ();
58 static bool
59 do_help_dialog (const SGPropertyNode * arg)
60 {
61     helpCb();
62     return true;
63 }
64
65 static struct {
66     const char * name;
67     SGCommandMgr::command_t command;
68 } deprecated_dialogs [] = {
69     { "old-reinit-dialog", do_reinit_dialog },
70 #if defined(TR_HIRES_SNAP)
71     { "old-hires-snapshot-dialog", do_hires_snapshot_dialog },
72 #endif
73 #if defined( WIN32 ) && !defined( __CYGWIN__) && !defined(__MINGW32__)
74     { "old-print-dialog", do_print_dialog },
75 #endif
76     { "old-help-dialog", do_help_dialog },
77     { 0, 0 }
78 };
79
80 static void
81 add_deprecated_dialogs ()
82 {
83   SG_LOG(SG_GENERAL, SG_INFO, "Initializing old dialog commands:");
84   for (int i = 0; deprecated_dialogs[i].name != 0; i++) {
85     SG_LOG(SG_GENERAL, SG_INFO, "  " << deprecated_dialogs[i].name);
86     globals->get_commands()->addCommand(deprecated_dialogs[i].name,
87                                         deprecated_dialogs[i].command);
88   }
89 }
90
91
92 \f
93 ////////////////////////////////////////////////////////////////////////
94 // Static functions.
95 ////////////////////////////////////////////////////////////////////////
96
97
98 static void
99 menu_callback (puObject * object)
100 {
101     NewGUI * gui = (NewGUI *)globals->get_subsystem("gui");
102     gui->getMenuBar()->fireItem(object);
103 }
104
105
106 \f
107 ////////////////////////////////////////////////////////////////////////
108 // Implementation of FGMenuBar.
109 ////////////////////////////////////////////////////////////////////////
110
111
112 FGMenuBar::FGMenuBar ()
113     : _visible(false),
114       _menuBar(0)
115 {
116 }
117
118 FGMenuBar::~FGMenuBar ()
119 {
120     destroy_menubar();
121 }
122
123 void
124 FGMenuBar::init ()
125 {
126     delete _menuBar;            // FIXME: check if PUI owns the pointer
127     make_menubar();
128                                 // FIXME: temporary commands to get at
129                                 // old, hard-coded dialogs.
130     add_deprecated_dialogs();
131 }
132
133 void
134 FGMenuBar::show ()
135 {
136     if (_menuBar != 0)
137         _menuBar->reveal();
138     _visible = true;
139 }
140
141 void
142 FGMenuBar::hide ()
143 {
144     if (_menuBar != 0)
145         _menuBar->hide();
146     _visible = false;
147 }
148
149 bool
150 FGMenuBar::isVisible () const
151 {
152     return _visible;
153 }
154
155 void
156 FGMenuBar::fireItem (puObject * item)
157 {
158     const char * name = item->getLegend();
159     vector<SGBinding *> &bindings = _bindings[name];
160     int nBindings = bindings.size();
161
162     for (int i = 0; i < nBindings; i++)
163         bindings[i]->fire();
164 }
165
166 void
167 FGMenuBar::make_menu (SGPropertyNode * node)
168 {
169     const char * name = strdup(node->getStringValue("label"));
170     vector<SGPropertyNode_ptr> item_nodes = node->getChildren("item");
171
172     int array_size = item_nodes.size();
173
174     char ** items = make_char_array(array_size);
175     puCallback * callbacks = make_callback_array(array_size);
176
177     for (unsigned int i = 0, j = item_nodes.size() - 1;
178          i < item_nodes.size();
179          i++, j--) {
180
181                                 // Set up the PUI entries for this item
182         items[j] = strdup((char *)item_nodes[i]->getStringValue("label"));
183         callbacks[j] = menu_callback;
184
185                                 // Load all the bindings for this item
186         vector<SGPropertyNode_ptr> bindings = item_nodes[i]->getChildren("binding");
187         SGPropertyNode * dest = fgGetNode("/sim/bindings/menu", true);
188
189         for (unsigned int k = 0; k < bindings.size(); k++) {
190             unsigned int m = 0;
191             SGPropertyNode_ptr binding;
192             while (dest->getChild("binding", m))
193                 m++;
194
195             binding = dest->getChild("binding", m, true);
196             copyProperties(bindings[k], binding);
197             _bindings[items[j]].push_back(new SGBinding(binding, globals->get_props()));
198         }
199     }
200
201     _menuBar->add_submenu(name, items, callbacks);
202 }
203
204 void
205 FGMenuBar::make_menubar ()
206 {
207     SGPropertyNode *targetpath;
208    
209     targetpath = fgGetNode("/sim/menubar/default",true);
210     // fgLoadProps("gui/menubar.xml", targetpath);
211     
212     /* NOTE: there is no check to see whether there's any usable data at all
213      *
214      * This would also have the advantage of being able to create some kind of
215      * 'fallback' menu - just in case that either menubar.xml is empty OR that
216      * its XML data is not valid, that way we would avoid displaying an
217      * unusable menubar without any functionality - if we decided to add another
218      * char * element to the commands structure in
219      *  $FG_SRC/src/Main/fgcommands.cxx 
220      * we could additionally save each function's (short) description and use
221      * this as label for the fallback PUI menubar item labels - as a workaround
222      * one might simply use the internal fgcommands and put them into the 
223      * fallback menu, so that the user is at least able to re-init the menu
224      * loading - just in case there was some malformed XML in it
225      * (it happend to me ...)
226      */
227     
228     make_menubar(targetpath);
229 }
230
231 /* WARNING: We aren't yet doing any validation of what's found - but since
232  * this isn't done with menubar.xml either, it should not really matter
233  * right now. Although one should later on consider to validate the
234  * contents, whether they are representing a 'legal' menubar structure.
235  */
236 void
237 FGMenuBar::make_menubar(SGPropertyNode * props) 
238 {    
239     // Just in case.
240     destroy_menubar();
241     _menuBar = new puMenuBar;
242
243     vector<SGPropertyNode_ptr> menu_nodes = props->getChildren("menu");
244     for (unsigned int i = 0; i < menu_nodes.size(); i++)
245         make_menu(menu_nodes[i]);
246
247     _menuBar->close();
248     make_object_map(props);
249
250     if (_visible)
251         _menuBar->reveal();
252     else
253         _menuBar->hide();
254 }
255
256 void
257 FGMenuBar::destroy_menubar ()
258 {
259     if ( _menuBar == 0 )
260         return;
261
262     hide();
263     puDeleteObject(_menuBar);
264
265     unsigned int i;
266
267                                 // Delete all the character arrays
268                                 // we were forced to keep around for
269                                 // plib.
270     SG_LOG(SG_GENERAL, SG_INFO, "Deleting char arrays");
271     for (i = 0; i < _char_arrays.size(); i++) {
272         for (int j = 0; _char_arrays[i][j] != 0; j++)
273             free(_char_arrays[i][j]); // added with strdup
274         delete[] _char_arrays[i];
275     }
276
277                                 // Delete all the callback arrays
278                                 // we were forced to keep around for
279                                 // plib.
280     SG_LOG(SG_GENERAL, SG_INFO, "Deleting callback arrays");
281     for (i = 0; i < _callback_arrays.size(); i++)
282         delete[] _callback_arrays[i];
283
284                                 // Delete all those bindings
285     SG_LOG(SG_GENERAL, SG_INFO, "Deleting bindings");
286     map<string,vector<SGBinding *> >::iterator it;
287     for (it = _bindings.begin(); it != _bindings.end(); it++) {
288         SG_LOG(SG_GENERAL, SG_INFO, "Deleting bindings for " << it->first);
289         for ( i = 0; i < it->second.size(); i++ )
290             delete it->second[i];
291     }
292
293     SG_LOG(SG_GENERAL, SG_INFO, "Done.");
294 }
295
296 void
297 FGMenuBar::make_object_map(SGPropertyNode * node)
298 {
299     unsigned int menu_index = 0;
300     vector<SGPropertyNode_ptr> menus = node->getChildren("menu");
301     for (puObject *obj = ((puGroup *)_menuBar)->getFirstChild();
302             obj; obj = obj->getNextObject()) {
303
304         // skip puPopupMenus. They are also children of _menuBar,
305         // but we access them via getUserData()  (see below)
306         if (!(obj->getType() & PUCLASS_ONESHOT))
307             continue;
308
309         if (menu_index >= menus.size()) {
310             SG_LOG(SG_GENERAL, SG_WARN, "'menu' object without node: "
311                     << node->getPath() << "/menu[" << menu_index << ']');
312             return;
313         }
314
315         SGPropertyNode *menu = menus.at(menu_index);
316         _objects[menu->getPath()] = obj;
317         add_enabled_listener(menu);
318
319         puGroup *popup = (puGroup *)obj->getUserData();
320         if (!popup)
321             continue;
322
323         // the entries are for some reason reversed (last first), and we
324         // don't know yet how many there will be; so we collect first
325         vector<puObject *> e;
326         for (puObject *me = popup->getFirstChild(); me; me = me->getNextObject())
327             e.push_back(me);
328
329         vector<SGPropertyNode_ptr> items = menu->getChildren("item");
330         for (unsigned int i = 0; i < e.size(); i++) {
331             if (i >= items.size()) {
332                 SG_LOG(SG_GENERAL, SG_WARN, "'item' object without node: "
333                         << menu->getPath() << "/item[" << i << ']');
334                 break;
335             }
336             SGPropertyNode *item = items.at(e.size() - i - 1);
337             _objects[item->getPath()] = e[i];
338             add_enabled_listener(item);
339         }
340         menu_index++;
341     }
342 }
343
344 struct EnabledListener : SGPropertyChangeListener {
345     void valueChanged(SGPropertyNode *node) {
346         NewGUI * gui = (NewGUI *)globals->get_subsystem("gui");
347         if (!gui)
348             return;
349         FGMenuBar *menubar = gui->getMenuBar();
350         if (menubar)
351             menubar->enable_item(node->getParent(), node->getBoolValue());
352     }
353 };
354
355 void
356 FGMenuBar::add_enabled_listener(SGPropertyNode * node)
357 {
358     if (!node->hasValue("enabled"))
359         node->setBoolValue("enabled", true);
360
361     enable_item(node, node->getBoolValue("enabled"));
362     node->getNode("enabled")->addChangeListener(new EnabledListener());
363 }
364
365 bool
366 FGMenuBar::enable_item(const SGPropertyNode * node, bool state)
367 {
368     const char *path = node->getPath();
369     if (_objects.find(path) == _objects.end()) {
370         SG_LOG(SG_GENERAL, SG_ALERT, "Trying to enable/disable "
371             "non-existent menu item for node `" << path << '\'');
372         return false;
373     }
374     puObject *object = _objects[path];
375     if (state)
376         object->activate();
377     else
378         object->greyOut();
379
380     return true;
381 }
382
383 char **
384 FGMenuBar::make_char_array (int size)
385 {
386     char ** list = new char*[size+1];
387     for (int i = 0; i <= size; i++)
388         list[i] = 0;
389     _char_arrays.push_back(list);
390     return list;
391 }
392
393 puCallback *
394 FGMenuBar::make_callback_array (int size)
395 {
396     puCallback * list = new puCallback[size+1];
397     for (int i = 0; i <= size; i++)
398         list[i] = 0;
399     _callback_arrays.push_back(list);
400     return list;
401 }
402
403 // end of menubar.cxx