From 0b3159ec0c6984fdcc69bca8b5d1dbee56a51f4f Mon Sep 17 00:00:00 2001 From: andy Date: Mon, 8 Dec 2003 02:05:10 +0000 Subject: [PATCH] Add a new command, "dialog-new", allowing external code (e.g. Nasal scripts) to create dialogs at runtime. Augment "dialog-close" to take a name argument, allowing code other than PUI callbacks to close dialogs. The changes to the GUI directory to enable this are actually minor, basically amounting to using SGPropertyNode_ptr reference counting (the GUI subsystem no longer "controls" the dialog property trees, so it can't delete them). --- src/GUI/new_gui.cxx | 53 ++++++++++++++++++++++++++--------- src/GUI/new_gui.hxx | 29 +++++++++++++++++-- src/Main/fg_commands.cxx | 25 +++++++++++++++++ src/Scripting/nasal-props.cxx | 1 + 4 files changed, 91 insertions(+), 17 deletions(-) diff --git a/src/GUI/new_gui.cxx b/src/GUI/new_gui.cxx index f6e087ca9..8a3a12f3c 100644 --- a/src/GUI/new_gui.cxx +++ b/src/GUI/new_gui.cxx @@ -79,7 +79,8 @@ NewGUI::showDialog (const string &name) SG_LOG(SG_GENERAL, SG_ALERT, "Dialog " << name << " not defined"); return false; } else { - new FGDialog(_dialog_props[name]); // it will be deleted by a callback + if(!_active_dialogs[name]) + _active_dialogs[name] = new FGDialog(_dialog_props[name]); return true; } } @@ -87,13 +88,33 @@ NewGUI::showDialog (const string &name) bool NewGUI::closeActiveDialog () { - if (_active_dialog == 0) { + if (_active_dialog == 0) return false; - } else { - delete _active_dialog; - _active_dialog = 0; + + // Kill any entries in _active_dialogs... Is there an STL + // algorithm to do (delete map entries by value, not key)? I hate + // the STL :) -Andy + map::iterator iter = _active_dialogs.begin(); + for(/**/; iter != _active_dialogs.end(); iter++) + if(iter->second == _active_dialog) + _active_dialogs.erase(iter); + + delete _active_dialog; + _active_dialog = 0; + return true; +} + +bool +NewGUI::closeDialog (const string& name) +{ + if(_active_dialogs.find(name) != _active_dialogs.end()) { + if(_active_dialog == _active_dialogs[name]) + _active_dialog = 0; + delete _active_dialogs[name]; + _active_dialogs.erase(name); return true; } + return false; // dialog wasn't open... } void @@ -134,10 +155,6 @@ NewGUI::clear () { delete _menubar; _menubar = 0; - - map::iterator it; - for (it = _dialog_props.begin(); it != _dialog_props.end(); it++) - delete it->second; _dialog_props.clear(); } @@ -154,6 +171,18 @@ test_extension (const char * path, const char * ext) return true; } +void +NewGUI::newDialog (SGPropertyNode* props) +{ + const char* cname = props->getStringValue("name"); + if(!cname) { + SG_LOG(SG_GENERAL, SG_ALERT, "New dialog has no property"); + return; + } + string name = props->getStringValue("name"); + _dialog_props[name] = props; +} + void NewGUI::readDir (const char * path) { @@ -189,11 +218,7 @@ NewGUI::readDir (const char * path) delete props; continue; } - string name = props->getStringValue("name"); - SG_LOG(SG_INPUT, SG_BULK, "Saving dialog " << name); - if (_dialog_props[name] != 0) - delete _dialog_props[name]; - _dialog_props[name] = props; + newDialog(props); } } ulCloseDir(dir); diff --git a/src/GUI/new_gui.hxx b/src/GUI/new_gui.hxx index c0cda22b3..5aec7c538 100644 --- a/src/GUI/new_gui.hxx +++ b/src/GUI/new_gui.hxx @@ -80,6 +80,18 @@ public: */ virtual void update (double delta_time_sec); + /** + * Creates a new dialog box, using the same property format as the + * gui/dialogs configuration files. Does not display the + * resulting dialog. If a pre-existing dialog of the same name + * exists, it will be deleted. The node argument will be stored + * in the GUI subsystem using SGPropertNode_ptr reference counting. + * It should not be deleted by user code. + * + * @param node A property node containing the dialog definition + */ + virtual void newDialog (SGPropertyNode* node); + /** * Display a dialog box. * @@ -96,12 +108,22 @@ public: /** - * Close the currently-active dialog, if any. + * Close the currenty active dialog. This function is intended to + * be called from code (pui callbacks, for instance) that registers + * its dialog object as active via setActiveDialog(). Other + * user-level code should use the closeDialog(name) API. * - * @return true if a dialog was active, false otherwise. + * @return true if a dialog was active, false otherwise */ virtual bool closeActiveDialog (); + /** + * Close a named dialog, if it is open. + * + * @param name The name of the dialog box. + * @return true if the dialog was active, false otherwise. + */ + virtual bool closeDialog (const string &name); /** * Return a pointer to the current menubar. @@ -151,7 +173,8 @@ private: FGMenuBar * _menubar; FGDialog * _active_dialog; - map _dialog_props; + map _active_dialogs; + map _dialog_props; }; diff --git a/src/Main/fg_commands.cxx b/src/Main/fg_commands.cxx index 9663ad892..69edb7d0c 100644 --- a/src/Main/fg_commands.cxx +++ b/src/Main/fg_commands.cxx @@ -857,6 +857,28 @@ do_data_logging_commit (const SGPropertyNode * arg) return true; } +/** + * Built-in command: Add a dialog to the GUI system. Does *not* + * display the dialog. The property node should have the same format + * as a dialog XML configuration. It must include: + * + * name: the name of the GUI dialog for future reference. + */ +static bool +do_dialog_new (const SGPropertyNode * arg) +{ + NewGUI * gui = (NewGUI *)globals->get_subsystem("gui"); + + // Note the casting away of const: this is *real*. Doing a + // "dialog-apply" command later on will mutate this property node. + // I'm not convinced that this isn't the Right Thing though; it + // allows client to create a node, pass it to dialog-new, and get + // the values back from the dialog by reading the same node. + // Perhaps command arguments are not as "const" as they would + // seem? + gui->newDialog((SGPropertyNode*)arg); + return true; +} /** * Built-in command: Show an XML-configured dialog. @@ -879,6 +901,8 @@ static bool do_dialog_close (const SGPropertyNode * arg) { NewGUI * gui = (NewGUI *)globals->get_subsystem("gui"); + if(arg->hasValue("dialog-name")) + return gui->closeDialog(arg->getStringValue("dialog-name")); return gui->closeActiveDialog(); } @@ -1066,6 +1090,7 @@ static struct { { "property-cycle", do_property_cycle }, { "property-randomize", do_property_randomize }, { "data-logging-commit", do_data_logging_commit }, + { "dialog-new", do_dialog_new }, { "dialog-show", do_dialog_show }, { "dialog-close", do_dialog_close }, { "dialog-show", do_dialog_show }, diff --git a/src/Scripting/nasal-props.cxx b/src/Scripting/nasal-props.cxx index de0efca33..78401e1f9 100644 --- a/src/Scripting/nasal-props.cxx +++ b/src/Scripting/nasal-props.cxx @@ -25,6 +25,7 @@ naGhostType PropNodeGhostType = { propNodeGhostDestroy }; static naRef propNodeGhostCreate(naContext c, SGPropertyNode* n) { + if(!n) return naNil(); SGPropertyNode_ptr* ghost = new SGPropertyNode_ptr(n); return naNewGhost(c, &PropNodeGhostType, ghost); } -- 2.39.5