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