]> git.mxchange.org Git - flightgear.git/blob - src/GUI/new_gui.cxx
Oliver Schroeder:
[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 SGPropertyNode_ptr
146 NewGUI::getDialog (const string &name)
147 {
148     if(_dialog_props.find(name) != _dialog_props.end())
149         return _dialog_props[name];
150
151     SG_LOG(SG_GENERAL, SG_ALERT, "dialog '" << name << "' missing");
152     return 0;
153 }
154
155 void
156 NewGUI::setActiveDialog (FGDialog * dialog)
157 {
158     _active_dialog = dialog;
159 }
160
161 FGDialog *
162 NewGUI::getActiveDialog ()
163 {
164     return _active_dialog;
165 }
166
167 FGMenuBar *
168 NewGUI::getMenuBar ()
169 {
170     return _menubar;
171 }
172
173 bool
174 NewGUI::getMenuBarVisible () const
175 {
176     return _menubar->isVisible();
177 }
178
179 void
180 NewGUI::setMenuBarVisible (bool visible)
181 {
182     if (visible)
183         _menubar->show();
184     else
185         _menubar->hide();
186 }
187
188 void
189 NewGUI::clear ()
190 {
191     delete _menubar;
192     _menubar = 0;
193     _dialog_props.clear();
194     _itt_t it;
195     for (it = _colors.begin(); it != _colors.end(); ++it)
196       delete it->second;
197     _colors.clear();
198 }
199
200 static bool
201 test_extension (const char * path, const char * ext)
202 {
203     int pathlen = strlen(path);
204     int extlen = strlen(ext);
205
206     for (int i = 1; i <= pathlen && i <= extlen; i++) {
207         if (path[pathlen-i] != ext[extlen-i])
208             return false;
209     }
210     return true;
211 }
212
213 void
214 NewGUI::newDialog (SGPropertyNode* props)
215 {
216     const char* cname = props->getStringValue("name");
217     if(!cname) {
218         SG_LOG(SG_GENERAL, SG_ALERT, "New dialog has no <name> property");
219         return;
220     }
221     string name = cname;
222     if(!_active_dialogs[name])
223         _dialog_props[name] = props;
224 }
225
226 void
227 NewGUI::readDir (const char * path)
228 {
229     ulDir * dir = ulOpenDir(path);
230
231     if (dir == 0) {
232         SG_LOG(SG_GENERAL, SG_ALERT, "Failed to read GUI files from "
233                << path);
234         return;
235     }
236
237     for (ulDirEnt * dirEnt = ulReadDir(dir);
238          dirEnt != 0;
239          dirEnt = ulReadDir(dir)) {
240
241         char subpath[1024];
242
243         ulMakePath(subpath, path, dirEnt->d_name);
244
245         if (!dirEnt->d_isdir && test_extension(subpath, ".xml")) {
246             SGPropertyNode * props = new SGPropertyNode;
247             try {
248                 readProperties(subpath, props);
249             } catch (const sg_exception &) {
250                 SG_LOG(SG_INPUT, SG_ALERT, "Error parsing dialog "
251                        << subpath);
252                 delete props;
253                 continue;
254             }
255             SGPropertyNode *nameprop = props->getNode("name");
256             if (!nameprop) {
257                 SG_LOG(SG_INPUT, SG_WARN, "dialog " << subpath
258                    << " has no name; skipping.");
259                 delete props;
260                 continue;
261             }
262             string name = nameprop->getStringValue();
263             if (_dialog_props[name])
264                 delete (SGPropertyNode *)_dialog_props[name];
265
266             _dialog_props[name] = props;
267         }
268     }
269     ulCloseDir(dir);
270 }
271
272
273 \f
274 ////////////////////////////////////////////////////////////////////////
275 // Style handling.
276 ////////////////////////////////////////////////////////////////////////
277
278 void
279 NewGUI::setStyle (void)
280 {
281     _itt_t it;
282     for (it = _colors.begin(); it != _colors.end(); ++it)
283       delete it->second;
284     _colors.clear();
285
286     // set up the traditional colors as default
287     _colors["background"] = new FGColor(0.8f, 0.8f, 0.9f, 0.85f);
288     _colors["foreground"] = new FGColor(0.0f, 0.0f, 0.0f, 1.0f);
289     _colors["highlight"]  = new FGColor(0.7f, 0.7f, 0.7f, 1.0f);
290     _colors["label"]      = new FGColor(0.0f, 0.0f, 0.0f, 1.0f);
291     _colors["legend"]     = new FGColor(0.0f, 0.0f, 0.0f, 1.0f);
292     _colors["misc"]       = new FGColor(0.0f, 0.0f, 0.0f, 1.0f);
293
294     //puSetDefaultStyle();
295
296     int which = fgGetInt("/sim/current-gui", 0);
297     SGPropertyNode *sim = globals->get_props()->getNode("sim");
298     SGPropertyNode *n = sim->getChild("gui", which);
299     if (!n)
300         n = sim->getChild("gui", 0, true);
301
302     setupFont(n->getNode("font", true));
303     n = n->getNode("colors", true);
304
305     for (int i = 0; i < n->nChildren(); i++) {
306         SGPropertyNode *child = n->getChild(i);
307         _colors[child->getName()] = new FGColor(child);
308     }
309
310     FGColor *c = _colors["background"];
311     puSetDefaultColourScheme(c->red(), c->green(), c->blue(), c->alpha());
312 }
313
314
315
316
317 static const struct {
318     char *name;
319     puFont *font;
320 } guifonts[] = {
321     { "default",      &FONT_HELVETICA_14 },
322     { "FIXED_8x13",   &PUFONT_8_BY_13 },
323     { "FIXED_9x15",   &PUFONT_9_BY_15 },
324     { "TIMES_10",     &PUFONT_TIMES_ROMAN_10 },
325     { "TIMES_24",     &PUFONT_TIMES_ROMAN_24 },
326     { "HELVETICA_10", &PUFONT_HELVETICA_10 },
327     { "HELVETICA_12", &PUFONT_HELVETICA_12 },
328     { "HELVETICA_14", &FONT_HELVETICA_14 },
329     { "HELVETICA_18", &PUFONT_HELVETICA_18 },
330     { "SANS_12B",     &FONT_SANS_12B },
331     { 0, 0 }
332 };
333
334 void
335 NewGUI::setupFont (SGPropertyNode *node)
336 {
337     string fontname = node->getStringValue("name", "Helvetica.txf");
338     float size = node->getFloatValue("size", 15.0);
339     float slant = node->getFloatValue("slant", 0.0);
340
341     int i;
342     for (i = 0; guifonts[i].name; i++)
343         if (fontname == guifonts[i].name)
344             break;
345     if (guifonts[i].name)
346         _font = *guifonts[i].font;
347     else {
348         SGPath fontpath;
349         char* envp = ::getenv("FG_FONTS");
350         if (envp != NULL) {
351             fontpath.set(envp);
352         } else {
353             fontpath.set(globals->get_fg_root());
354             fontpath.append("Fonts");
355         }
356
357         SGPath path(fontpath);
358         path.append(fontname);
359
360         if (_tex_font.load((char *)path.c_str())) {
361             _font.initialize((fntFont *)&_tex_font, size, slant);
362         } else {
363             _font = *guifonts[0].font;
364             fontname = "default";
365         }
366     }
367     puSetDefaultFonts(_font, _font);
368     node->setStringValue("name", fontname.c_str());
369 }
370
371
372
373 \f
374 ////////////////////////////////////////////////////////////////////////
375 // FGColor class.
376 ////////////////////////////////////////////////////////////////////////
377
378 bool
379 FGColor::merge(const SGPropertyNode *node)
380 {
381     if (!node)
382         return false;
383
384     bool dirty = false;
385     const SGPropertyNode * n;
386     if ((n = node->getNode("red")))
387         _red = n->getFloatValue(), dirty = true;
388     if ((n = node->getNode("green")))
389         _green = n->getFloatValue(), dirty = true;
390     if ((n = node->getNode("blue")))
391         _blue = n->getFloatValue(), dirty = true;
392     if ((n = node->getNode("alpha")))
393         _alpha = n->getFloatValue(), dirty = true;
394     return dirty;
395 }
396
397 bool
398 FGColor::merge(const FGColor *color)
399 {
400     bool dirty = false;
401     if (color && color->_red >= 0.0)
402         _red = color->_red, dirty = true;
403     if (color && color->_green >= 0.0)
404         _green = color->_green, dirty = true;
405     if (color && color->_blue >= 0.0)
406         _blue = color->_blue, dirty = true;
407     if (color && color->_alpha >= 0.0)
408         _alpha = color->_alpha, dirty = true;
409     return dirty;
410 }
411
412
413 //
414 FGFontCache::FGFontCache()
415 {
416     char *envp = ::getenv("FG_FONTS");
417     if (envp != NULL) {
418         _path.set(envp);
419     } else {
420         _path.set(globals->get_fg_root());
421         _path.append("Fonts");
422     }
423
424     for (int i=0; guifonts[i].name; i++)
425         _fonts[guifonts[i].name] = guifonts[i].font;
426 }
427
428 FGFontCache::~FGFontCache()
429 {
430    _fonts.clear();
431 }
432
433 puFont *
434 FGFontCache::get(const char *name, float size, float slant)
435 {
436     puFont *font;
437     _itt_t it;
438
439     if ((it = _fonts.find(name)) == _fonts.end())
440     {
441         SGPath path(_path);
442         path.append(name);
443
444         fntTexFont tex_font;
445         if (tex_font.load((char *)path.c_str()))
446         {
447             font = new puFont;
448             font->initialize((fntFont *)&tex_font, size, slant);
449             _fonts[name] = font;
450         }
451         else
452         {
453             font = _fonts["default"];
454             // puSetDefaultFonts(font, font);
455         }
456     }
457     else
458     {
459         font = it->second;
460     }
461
462     return font;
463 }
464
465 puFont *
466 FGFontCache::get(SGPropertyNode *node)
467 {
468     const char *name = node->getStringValue("name", "Helvetica.txf");
469     float size = node->getFloatValue("size", 15.0);
470     float slant = node->getFloatValue("slant", 0.0);
471
472     return get(name, size, slant);
473 }
474
475 // end of new_gui.cxx