]> git.mxchange.org Git - flightgear.git/blob - src/GUI/new_gui.cxx
Bug-fix: Cocoa menus work in aircraft with custom dialogs.
[flightgear.git] / src / GUI / new_gui.cxx
1 // new_gui.cxx: implementation of XML-configurable GUI support.
2
3 #ifdef HAVE_CONFIG_H
4 #  include <config.h>
5 #endif
6
7 #include "new_gui.hxx"
8
9 #include <algorithm>
10 #include <iostream>
11 #include <cstring>
12 #include <sys/types.h>
13
14 #include <plib/pu.h>
15
16 #include <simgear/compiler.h>
17 #include <simgear/structure/exception.hxx>
18 #include <simgear/props/props_io.hxx>
19 #include <simgear/misc/sg_dir.hxx>
20
21 #include <boost/algorithm/string/case_conv.hpp>
22 #include <boost/foreach.hpp>
23
24 #include <Main/fg_props.hxx>
25
26 #if defined(SG_UNIX) && !defined(SG_MAC) 
27 #include "GL/glx.h"
28 #endif
29
30 #include "FGPUIMenuBar.hxx"
31
32 #if defined(SG_MAC)
33 #include "FGCocoaMenuBar.hxx"
34 #endif
35
36 #include "FGPUIDialog.hxx"
37 #include "FGFontCache.hxx"
38 #include "FGColor.hxx"
39
40 // ignore the word Navaid here, it's a DataCache
41 #include <Navaids/NavDataCache.hxx>
42
43 using std::map;
44 using std::string;
45
46 ////////////////////////////////////////////////////////////////////////
47 // Implementation of NewGUI.
48 ////////////////////////////////////////////////////////////////////////
49
50
51
52 NewGUI::NewGUI () :
53   _active_dialog(0)
54 {
55 }
56
57 NewGUI::~NewGUI ()
58 {
59     for (_itt_t it = _colors.begin(); it != _colors.end(); ++it)
60         delete it->second;
61 }
62
63 void
64 NewGUI::init ()
65 {
66     createMenuBarImplementation();
67     fgTie("/sim/menubar/visibility", this,
68           &NewGUI::getMenuBarVisible, &NewGUI::setMenuBarVisible);
69     
70     setStyle();
71     SGPath p(globals->get_fg_root(), "gui/dialogs");
72     readDir(p);
73     
74     SGPath aircraftDialogDir(string(fgGetString("/sim/aircraft-dir")), "gui/dialogs");
75     if (aircraftDialogDir.exists()) {
76         readDir(aircraftDialogDir);
77     }
78     
79     // Fix for http://code.google.com/p/flightgear-bugs/issues/detail?id=947
80     fgGetNode("sim/menubar")->setAttribute(SGPropertyNode::PRESERVE, true);
81     _menubar->init();
82 }
83
84 void
85 NewGUI::shutdown()
86 {
87     fgUntie("/sim/menubar/visibility");
88     _menubar.reset();
89     _dialog_props.clear();
90 }
91
92 void
93 NewGUI::reinit ()
94 {
95     reset(true);
96     fgSetBool("/sim/signals/reinit-gui", true);
97 }
98
99 void
100 NewGUI::redraw ()
101 {
102     reset(false);
103 }
104
105 void
106 NewGUI::createMenuBarImplementation()
107 {
108 #if defined(SG_MAC)
109     if (fgGetBool("/sim/menubar/native", true)) {
110         _menubar.reset(new FGCocoaMenuBar);
111     }
112 #endif
113     if (!_menubar.get()) {
114         _menubar.reset(new FGPUIMenuBar);
115     }
116 }
117
118 void
119 NewGUI::reset (bool reload)
120 {
121     map<string,FGDialog *>::iterator iter;
122     string_list openDialogs;
123     // close all open dialogs and remember them ...
124     for (iter = _active_dialogs.begin(); iter != _active_dialogs.end(); ++iter)
125         openDialogs.push_back(iter->first);
126
127     BOOST_FOREACH(string d, openDialogs)
128         closeDialog(d);
129
130     setStyle();
131
132     unbind();
133
134     if (reload) {
135         _dialog_props.clear();
136         _dialog_names.clear();
137         init();
138     } else {
139         createMenuBarImplementation();
140         _menubar->init();
141     }
142
143     bind();
144
145     // open dialogs again
146     BOOST_FOREACH(string d, openDialogs)
147         showDialog(d);
148 }
149
150 void
151 NewGUI::bind ()
152 {
153 }
154
155 void
156 NewGUI::unbind ()
157 {
158 }
159
160 void
161 NewGUI::update (double delta_time_sec)
162 {
163     SG_UNUSED(delta_time_sec);
164     map<string,FGDialog *>::iterator iter = _active_dialogs.begin();
165     for(/**/; iter != _active_dialogs.end(); iter++)
166         iter->second->update();
167 }
168
169 bool
170 NewGUI::showDialog (const string &name)
171 {
172     // first, check if it's already shown
173     if (_active_dialogs.find(name) != _active_dialogs.end())
174       return true;
175   
176     // check we know about the dialog by name
177     if (_dialog_names.find(name) == _dialog_names.end()) {
178         SG_LOG(SG_GENERAL, SG_ALERT, "Dialog " << name << " not defined");
179         return false;
180     }
181     
182     _active_dialogs[name] = new FGPUIDialog(getDialogProperties(name));
183     return true;
184 }
185
186 bool
187 NewGUI::closeActiveDialog ()
188 {
189     if (_active_dialog == 0)
190         return false;
191
192     // Kill any entries in _active_dialogs...  Is there an STL
193     // algorithm to do (delete map entries by value, not key)?  I hate
194     // the STL :) -Andy
195     map<string,FGDialog *>::iterator iter = _active_dialogs.begin();
196     for(/**/; iter != _active_dialogs.end(); iter++) {
197         if(iter->second == _active_dialog) {
198             _active_dialogs.erase(iter);
199             // iter is no longer valid
200             break;
201         }
202     }
203
204     delete _active_dialog;
205     _active_dialog = 0;
206     return true;
207 }
208
209 bool
210 NewGUI::closeDialog (const string& name)
211 {
212     if(_active_dialogs.find(name) != _active_dialogs.end()) {
213         if(_active_dialog == _active_dialogs[name])
214             _active_dialog = 0;
215         delete _active_dialogs[name];
216         _active_dialogs.erase(name);
217         return true;
218     }
219     return false; // dialog wasn't open...
220 }
221
222 SGPropertyNode_ptr
223 NewGUI::getDialogProperties (const string &name)
224 {
225     if (_dialog_names.find(name) == _dialog_names.end()) {
226       SG_LOG(SG_GENERAL, SG_ALERT, "Dialog " << name << " not defined");
227       return NULL;
228     }
229   
230     NameDialogDict::iterator it = _dialog_props.find(name);
231     if (it == _dialog_props.end()) {
232       // load the XML
233       SGPath path = _dialog_names[name];
234       SGPropertyNode_ptr props = new SGPropertyNode;
235       try {
236         readProperties(path.str(), props);
237       } catch (const sg_exception &) {
238         SG_LOG(SG_INPUT, SG_ALERT, "Error parsing dialog " << path);
239         return NULL;
240       }
241       
242       it = _dialog_props.insert(it, std::make_pair(name, props));
243     }
244
245     return it->second;
246 }
247
248 FGDialog *
249 NewGUI::getDialog (const string &name)
250 {
251     if(_active_dialogs.find(name) != _active_dialogs.end())
252         return _active_dialogs[name];
253
254     SG_LOG(SG_GENERAL, SG_DEBUG, "dialog '" << name << "' missing");
255     return 0;
256 }
257
258 void
259 NewGUI::setActiveDialog (FGDialog * dialog)
260 {
261     _active_dialog = dialog;
262 }
263
264 FGDialog *
265 NewGUI::getActiveDialog ()
266 {
267     return _active_dialog;
268 }
269
270 FGMenuBar *
271 NewGUI::getMenuBar ()
272 {
273     return _menubar.get();
274 }
275
276 bool
277 NewGUI::getMenuBarVisible () const
278 {
279     return _menubar->isVisible();
280 }
281
282 void
283 NewGUI::setMenuBarVisible (bool visible)
284 {
285     if (visible)
286         _menubar->show();
287     else
288         _menubar->hide();
289 }
290
291 void
292 NewGUI::newDialog (SGPropertyNode* props)
293 {
294     const char* cname = props->getStringValue("name");
295     if(!cname) {
296         SG_LOG(SG_GENERAL, SG_ALERT, "New dialog has no <name> property");
297         return;
298     }
299     string name = cname;
300   
301     if(_active_dialogs.find(name) == _active_dialogs.end()) {
302         _dialog_props[name] = props;
303     // add a dummy path entry, so we believe the dialog exists
304         _dialog_names[name] = SGPath();
305     }
306 }
307
308 void
309 NewGUI::readDir (const SGPath& path)
310 {
311     simgear::Dir dir(path);
312     if( !dir.exists() )
313     {
314       SG_LOG(SG_INPUT, SG_INFO, "directory does not exist: " << path.str());
315       return;
316     }
317
318     flightgear::NavDataCache* cache = flightgear::NavDataCache::instance();
319     flightgear::NavDataCache::Transaction txn(cache);
320     simgear::PathList xmls = dir.children(simgear::Dir::TYPE_FILE, ".xml");
321     
322     BOOST_FOREACH(SGPath xmlPath, xmls) {
323       if (!cache->isCachedFileModified(xmlPath)) {
324         // cached, easy
325         string name = cache->readStringProperty(xmlPath.str());
326         _dialog_names[name] = xmlPath;
327         continue;
328       }
329       
330     // we need to parse the actual XML
331       SGPropertyNode_ptr props = new SGPropertyNode;
332       try {
333         readProperties(xmlPath.str(), props);
334       } catch (const sg_exception &) {
335         SG_LOG(SG_INPUT, SG_ALERT, "Error parsing dialog " << xmlPath);
336         continue;
337       }
338       
339       SGPropertyNode *nameprop = props->getNode("name");
340       if (!nameprop) {
341         SG_LOG(SG_INPUT, SG_WARN, "dialog " << xmlPath << " has no name; skipping.");
342         continue;
343       }
344       
345       string name = nameprop->getStringValue();
346       _dialog_names[name] = xmlPath;
347     // update cached values
348         if (!cache->isReadOnly()) {
349             cache->stampCacheFile(xmlPath);
350             cache->writeStringProperty(xmlPath.str(), name);
351         }
352     } // of directory children iteration
353   
354     txn.commit();
355 }\f
356 ////////////////////////////////////////////////////////////////////////
357 // Style handling.
358 ////////////////////////////////////////////////////////////////////////
359
360 void
361 NewGUI::setStyle (void)
362 {
363     _itt_t it;
364     for (it = _colors.begin(); it != _colors.end(); ++it)
365       delete it->second;
366     _colors.clear();
367
368     // set up the traditional colors as default
369     _colors["background"] = new FGColor(0.8f, 0.8f, 0.9f, 0.85f);
370     _colors["foreground"] = new FGColor(0.0f, 0.0f, 0.0f, 1.0f);
371     _colors["highlight"]  = new FGColor(0.7f, 0.7f, 0.7f, 1.0f);
372     _colors["label"]      = new FGColor(0.0f, 0.0f, 0.0f, 1.0f);
373     _colors["legend"]     = new FGColor(0.0f, 0.0f, 0.0f, 1.0f);
374     _colors["misc"]       = new FGColor(0.0f, 0.0f, 0.0f, 1.0f);
375     _colors["inputfield"] = new FGColor(0.8f, 0.7f, 0.7f, 1.0f);
376
377     //puSetDefaultStyle();
378
379     int which = fgGetInt("/sim/gui/current-style", 0);
380     SGPropertyNode *sim = globals->get_props()->getNode("sim/gui", true);
381     SGPropertyNode *n = sim->getChild("style", which);
382     if (!n)
383         n = sim->getChild("style", 0, true);
384
385     setupFont(n->getNode("fonts/gui", true));
386     n = n->getNode("colors", true);
387
388     for (int i = 0; i < n->nChildren(); i++) {
389         SGPropertyNode *child = n->getChild(i);
390         _colors[child->getName()] = new FGColor(child);
391     }
392
393     FGColor *c = _colors["background"];
394     puSetDefaultColourScheme(c->red(), c->green(), c->blue(), c->alpha());
395 }
396
397
398 void
399 NewGUI::setupFont (SGPropertyNode *node)
400 {
401     _font = globals->get_fontcache()->get(node);
402     puSetDefaultFonts(*_font, *_font);
403     return;
404 }
405
406 // end of new_gui.cxx