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