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