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