]> git.mxchange.org Git - flightgear.git/blob - src/GUI/new_gui.cxx
Fix a segmentation fault when switching to a new style and selecting a new dialog...
[flightgear.git] / src / GUI / new_gui.cxx
1 // new_gui.cxx: implementation of XML-configurable GUI support.
2
3 #include "new_gui.hxx"
4
5 #include <plib/pu.h>
6 #include <plib/ul.h>
7
8 #include <simgear/compiler.h>
9 #include <simgear/structure/exception.hxx>
10
11 #include <Main/fg_props.hxx>
12
13 #include "menubar.hxx"
14 #include "dialog.hxx"
15
16 extern puFont FONT_HELVETICA_14;
17 extern puFont FONT_SANS_12B;
18
19
20
21 \f
22 ////////////////////////////////////////////////////////////////////////
23 // Implementation of NewGUI.
24 ////////////////////////////////////////////////////////////////////////
25
26
27
28 NewGUI::NewGUI ()
29     : _font(FONT_HELVETICA_14),
30       _menubar(new FGMenuBar),
31       _active_dialog(0)
32 {
33 }
34
35 NewGUI::~NewGUI ()
36 {
37     clear();
38 }
39
40 void
41 NewGUI::init ()
42 {
43     setStyle();
44     char path1[1024];
45     char path2[1024];
46     ulMakePath(path1, globals->get_fg_root().c_str(), "gui");
47     ulMakePath(path2, path1, "dialogs");
48     readDir(path2);
49     _menubar->init();
50 }
51
52 void
53 NewGUI::reinit ()
54 {
55     map<string,FGDialog *>::iterator iter;
56     vector<string> dlg;
57     // close all open dialogs and remember them ...
58     for (iter = _active_dialogs.begin(); iter != _active_dialogs.end(); iter++) {
59         dlg.push_back(iter->first);
60         closeDialog(iter->first);
61     }
62
63     unbind();
64     clear();
65     setStyle();
66     _menubar = new FGMenuBar;
67     init();
68     bind();
69
70     // open remembered dialogs again (no nasal generated ones, unfortunately)
71 //    for (unsigned int i = 0; i < dlg.size(); i++)
72 //        showDialog(dlg[i]);
73 }
74
75 void
76 NewGUI::bind ()
77 {
78     fgTie("/sim/menubar/visibility", this,
79           &NewGUI::getMenuBarVisible, &NewGUI::setMenuBarVisible);
80 }
81
82 void
83 NewGUI::unbind ()
84 {
85     fgUntie("/sim/menubar/visibility");
86 }
87
88 void
89 NewGUI::update (double delta_time_sec)
90 {
91     map<string,FGDialog *>::iterator iter = _active_dialogs.begin();
92     for(/**/; iter != _active_dialogs.end(); iter++)
93         iter->second->update();
94 }
95
96 bool
97 NewGUI::showDialog (const string &name)
98 {
99     if (_dialog_props.find(name) == _dialog_props.end()) {
100         SG_LOG(SG_GENERAL, SG_ALERT, "Dialog " << name << " not defined");
101         return false;
102     } else {
103         if(!_active_dialogs[name])
104             _active_dialogs[name] = new FGDialog(_dialog_props[name]);
105         return true;
106     }
107 }
108
109 bool
110 NewGUI::closeActiveDialog ()
111 {
112     if (_active_dialog == 0)
113         return false;
114
115     // Kill any entries in _active_dialogs...  Is there an STL
116     // algorithm to do (delete map entries by value, not key)?  I hate
117     // the STL :) -Andy
118     map<string,FGDialog *>::iterator iter = _active_dialogs.begin();
119     for(/**/; iter != _active_dialogs.end(); iter++) {
120         if(iter->second == _active_dialog) {
121             _active_dialogs.erase(iter);
122             // iter is no longer valid
123             break;
124         }
125     }
126
127     delete _active_dialog;
128     _active_dialog = 0;
129     return true;
130 }
131
132 bool
133 NewGUI::closeDialog (const string& name)
134 {
135     if(_active_dialogs.find(name) != _active_dialogs.end()) {
136         if(_active_dialog == _active_dialogs[name])
137             _active_dialog = 0;
138         delete _active_dialogs[name];
139         _active_dialogs.erase(name);
140         return true;
141     }
142     return false; // dialog wasn't open...
143 }
144
145 void
146 NewGUI::setActiveDialog (FGDialog * dialog)
147 {
148     _active_dialog = dialog;
149 }
150
151 FGDialog *
152 NewGUI::getActiveDialog ()
153 {
154     return _active_dialog;
155 }
156
157 FGMenuBar *
158 NewGUI::getMenuBar ()
159 {
160     return _menubar;
161 }
162
163 bool
164 NewGUI::getMenuBarVisible () const
165 {
166     return _menubar->isVisible();
167 }
168
169 void
170 NewGUI::setMenuBarVisible (bool visible)
171 {
172     if (visible)
173         _menubar->show();
174     else
175         _menubar->hide();
176 }
177
178 void
179 NewGUI::clear ()
180 {
181     delete _menubar;
182     _menubar = 0;
183     _dialog_props.clear();
184     _colors.clear();
185 }
186
187 static bool
188 test_extension (const char * path, const char * ext)
189 {
190     int pathlen = strlen(path);
191     int extlen = strlen(ext);
192
193     for (int i = 1; i <= pathlen && i <= extlen; i++) {
194         if (path[pathlen-i] != ext[extlen-i])
195             return false;
196     }
197     return true;
198 }
199
200 void
201 NewGUI::newDialog (SGPropertyNode* props)
202 {
203     const char* cname = props->getStringValue("name");
204     if(!cname) {
205         SG_LOG(SG_GENERAL, SG_ALERT, "New dialog has no <name> property");
206         return;
207     }
208     string name = cname;
209     if(!_active_dialogs[name])
210         _dialog_props[name] = props;
211 }
212
213 void
214 NewGUI::readDir (const char * path)
215 {
216     ulDir * dir = ulOpenDir(path);
217
218     if (dir == 0) {
219         SG_LOG(SG_GENERAL, SG_ALERT, "Failed to read GUI files from "
220                << path);
221         return;
222     }
223
224     for (ulDirEnt * dirEnt = ulReadDir(dir);
225          dirEnt != 0;
226          dirEnt = ulReadDir(dir)) {
227
228         char subpath[1024];
229
230         ulMakePath(subpath, path, dirEnt->d_name);
231
232         if (!dirEnt->d_isdir && test_extension(subpath, ".xml")) {
233             SGPropertyNode * props = new SGPropertyNode;
234             try {
235                 readProperties(subpath, props);
236             } catch (const sg_exception &) {
237                 SG_LOG(SG_INPUT, SG_ALERT, "Error parsing dialog "
238                        << subpath);
239                 delete props;
240                 continue;
241             }
242             SGPropertyNode *nameprop = props->getNode("name");
243             if (!nameprop) {
244                 SG_LOG(SG_INPUT, SG_WARN, "dialog " << subpath
245                    << " has no name; skipping.");
246                 delete props;
247                 continue;
248             }
249             string name = nameprop->getStringValue();
250             if (_dialog_props[name])
251                 delete (SGPropertyNode *)_dialog_props[name];
252
253             _dialog_props[name] = props;
254         }
255     }
256     ulCloseDir(dir);
257 }
258
259
260 \f
261 ////////////////////////////////////////////////////////////////////////
262 // Style handling.
263 ////////////////////////////////////////////////////////////////////////
264
265 void
266 NewGUI::setStyle (void)
267 {
268     _colors.clear();
269
270     // set up the traditional colors as default
271     _colors["background"] = new FGColor(0.8f, 0.8f, 0.9f, 0.85f);
272     _colors["foreground"] = new FGColor(0.0f, 0.0f, 0.0f, 1.0f);
273     _colors["highlight"]  = new FGColor(0.7f, 0.7f, 0.7f, 1.0f);
274     _colors["label"]      = new FGColor(0.0f, 0.0f, 0.0f, 1.0f);
275     _colors["legend"]     = new FGColor(0.0f, 0.0f, 0.0f, 1.0f);
276     _colors["misc"]       = new FGColor(0.0f, 0.0f, 0.0f, 1.0f);
277
278     //puSetDefaultStyle();
279
280     int which = fgGetInt("/sim/current-gui", 0);
281     SGPropertyNode *sim = globals->get_props()->getNode("sim");
282     SGPropertyNode *n = sim->getChild("gui", which);
283     if (!n)
284         n = sim->getChild("gui", 0, true);
285
286     setupFont(n->getNode("font", true));
287     n = n->getNode("colors", true);
288
289     for (int i = 0; i < n->nChildren(); i++) {
290         SGPropertyNode *child = n->getChild(i);
291         _colors[child->getName()] = new FGColor(child);
292     }
293
294     FGColor *c = _colors["background"];
295     puSetDefaultColourScheme(c->red(), c->green(), c->blue(), c->alpha());
296 }
297
298
299
300
301 static const struct {
302     char *name;
303     puFont *font;
304 } guifonts[] = {
305     { "default",      &FONT_HELVETICA_14 },
306     { "FIXED_8x13",   &PUFONT_8_BY_13 },
307     { "FIXED_9x15",   &PUFONT_9_BY_15 },
308     { "TIMES_10",     &PUFONT_TIMES_ROMAN_10 },
309     { "TIMES_24",     &PUFONT_TIMES_ROMAN_24 },
310     { "HELVETICA_10", &PUFONT_HELVETICA_10 },
311     { "HELVETICA_12", &PUFONT_HELVETICA_12 },
312     { "HELVETICA_14", &FONT_HELVETICA_14 },
313     { "HELVETICA_18", &PUFONT_HELVETICA_18 },
314     { "SANS_12B",     &FONT_SANS_12B },
315     { 0, 0 }
316 };
317
318 void
319 NewGUI::setupFont (SGPropertyNode *node)
320 {
321     string fontname = node->getStringValue("name", "Helvetica.txf");
322     float size = node->getFloatValue("size", 15.0);
323     float slant = node->getFloatValue("slant", 0.0);
324
325     int i;
326     for (i = 0; guifonts[i].name; i++)
327         if (fontname == guifonts[i].name)
328             break;
329     if (guifonts[i].name)
330         _font = *guifonts[i].font;
331     else {
332         SGPath fontpath;
333         char* envp = ::getenv("FG_FONTS");
334         if (envp != NULL) {
335             fontpath.set(envp);
336         } else {
337             fontpath.set(globals->get_fg_root());
338             fontpath.append("Fonts");
339         }
340
341         SGPath path(fontpath);
342         path.append(fontname);
343
344         if (_tex_font.load((char *)path.c_str())) {
345             _font.initialize((fntFont *)&_tex_font, size, slant);
346         } else {
347             _font = *guifonts[0].font;
348             fontname = "default";
349         }
350     }
351     puSetDefaultFonts(_font, _font);
352     node->setStringValue("name", fontname.c_str());
353 }
354
355
356
357 \f
358 ////////////////////////////////////////////////////////////////////////
359 // FGColor class.
360 ////////////////////////////////////////////////////////////////////////
361
362 bool
363 FGColor::merge(const SGPropertyNode *node)
364 {
365     if (!node)
366         return false;
367
368     bool dirty = false;
369     const SGPropertyNode * n;
370     if ((n = node->getNode("red")))
371         _red = n->getFloatValue(), dirty = true;
372     if ((n = node->getNode("green")))
373         _green = n->getFloatValue(), dirty = true;
374     if ((n = node->getNode("blue")))
375         _blue = n->getFloatValue(), dirty = true;
376     if ((n = node->getNode("alpha")))
377         _alpha = n->getFloatValue(), dirty = true;
378     return dirty;
379 }
380
381 bool
382 FGColor::merge(const FGColor *color)
383 {
384     bool dirty = false;
385     if (color && color->_red >= 0.0)
386         _red = color->_red, dirty = true;
387     if (color && color->_green >= 0.0)
388         _green = color->_green, dirty = true;
389     if (color && color->_blue >= 0.0)
390         _blue = color->_blue, dirty = true;
391     if (color && color->_alpha >= 0.0)
392         _alpha = color->_alpha, dirty = true;
393     return dirty;
394 }
395
396
397 //
398 FGFontCache::FGFontCache()
399 {
400     char *envp = ::getenv("FG_FONTS");
401     if (envp != NULL) {
402         _path.set(envp);
403     } else {
404         _path.set(globals->get_fg_root());
405         _path.append("Fonts");
406     }
407
408     for (int i=0; guifonts[i].name; i++)
409         _fonts[guifonts[i].name] = guifonts[i].font;
410 }
411
412 FGFontCache::~FGFontCache()
413 {
414    _fonts.clear();
415 }
416
417 puFont *
418 FGFontCache::get(const char *name, float size, float slant)
419 {
420     puFont *font;
421     _itt_t it;
422
423     if ((it = _fonts.find(name)) == _fonts.end())
424     {
425         SGPath path(_path);
426         path.append(name);
427
428         fntTexFont tex_font;
429         if (tex_font.load((char *)path.c_str()))
430         {
431             font = new puFont;
432             font->initialize((fntFont *)&tex_font, size, slant);
433             _fonts[name] = font;
434         }
435         else
436         {
437             font = _fonts["default"];
438             // puSetDefaultFonts(font, font);
439         }
440     }
441     else
442     {
443         font = it->second;
444     }
445
446     return font;
447 }
448
449 puFont *
450 FGFontCache::get(SGPropertyNode *node)
451 {
452     const char *name = node->getStringValue("name", "Helvetica.txf");
453     float size = node->getFloatValue("size", 15.0);
454     float slant = node->getFloatValue("slant", 0.0);
455
456     return get(name, size, slant);
457 }
458
459 // end of new_gui.cxx