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