2001-06-01 17:52:17 +00:00
|
|
|
|
// fg_commands.cxx - internal FGFS commands.
|
|
|
|
|
|
2006-02-18 13:58:09 +00:00
|
|
|
|
#ifdef HAVE_CONFIG_H
|
|
|
|
|
# include "config.h"
|
|
|
|
|
#endif
|
|
|
|
|
|
2002-03-20 22:15:22 +00:00
|
|
|
|
#include <string.h> // strcmp()
|
|
|
|
|
|
2001-06-05 22:12:17 +00:00
|
|
|
|
#include <simgear/compiler.h>
|
|
|
|
|
|
2008-07-25 18:38:29 +00:00
|
|
|
|
#include <string>
|
|
|
|
|
#include <fstream>
|
2001-06-01 17:52:17 +00:00
|
|
|
|
|
2003-05-06 23:46:24 +00:00
|
|
|
|
#include <simgear/sg_inlines.h>
|
2001-06-01 17:52:17 +00:00
|
|
|
|
#include <simgear/debug/logstream.hxx>
|
2003-04-04 22:38:35 +00:00
|
|
|
|
#include <simgear/math/sg_random.h>
|
2007-05-13 19:03:15 +00:00
|
|
|
|
#include <simgear/scene/material/mat.hxx>
|
2009-08-08 10:20:32 +00:00
|
|
|
|
#include <simgear/scene/material/matlib.hxx>
|
2003-09-24 17:20:55 +00:00
|
|
|
|
#include <simgear/structure/exception.hxx>
|
|
|
|
|
#include <simgear/structure/commands.hxx>
|
2003-05-06 23:46:24 +00:00
|
|
|
|
#include <simgear/props/props.hxx>
|
2011-07-30 09:47:28 +00:00
|
|
|
|
#include <simgear/props/props_io.hxx>
|
2008-07-31 12:04:32 +00:00
|
|
|
|
#include <simgear/structure/event_mgr.hxx>
|
2009-10-04 13:52:53 +00:00
|
|
|
|
#include <simgear/sound/soundmgr_openal.hxx>
|
2010-07-01 21:43:28 +00:00
|
|
|
|
#include <simgear/timing/sg_time.hxx>
|
2012-02-02 21:17:34 +00:00
|
|
|
|
#include <simgear/misc/interpolator.hxx>
|
2012-04-18 09:25:27 +00:00
|
|
|
|
#include <simgear/io/HTTPRequest.hxx>
|
2001-06-01 17:52:17 +00:00
|
|
|
|
|
2001-06-05 22:12:17 +00:00
|
|
|
|
#include <Cockpit/panel.hxx>
|
|
|
|
|
#include <Cockpit/panel_io.hxx>
|
2002-11-30 20:10:16 +00:00
|
|
|
|
#include <FDM/flight.hxx>
|
|
|
|
|
#include <GUI/gui.h>
|
|
|
|
|
#include <GUI/new_gui.hxx>
|
2003-01-18 15:57:51 +00:00
|
|
|
|
#include <GUI/dialog.hxx>
|
2005-11-01 13:41:49 +00:00
|
|
|
|
#include <Aircraft/replay.hxx>
|
2007-05-03 19:15:20 +00:00
|
|
|
|
#include <Scenery/scenery.hxx>
|
2003-11-25 21:08:36 +00:00
|
|
|
|
#include <Scripting/NasalSys.hxx>
|
2009-10-07 20:56:24 +00:00
|
|
|
|
#include <Sound/sample_queue.hxx>
|
2011-05-24 23:03:51 +00:00
|
|
|
|
#include <Airports/xmlloader.hxx>
|
2011-06-01 22:55:04 +00:00
|
|
|
|
#include <ATC/CommStation.hxx>
|
|
|
|
|
#include <Navaids/navrecord.hxx>
|
|
|
|
|
#include <Navaids/navlist.hxx>
|
2012-04-18 09:25:27 +00:00
|
|
|
|
#include <Network/HTTPClient.hxx>
|
2001-06-05 22:12:17 +00:00
|
|
|
|
|
2002-11-30 20:10:16 +00:00
|
|
|
|
#include "fg_init.hxx"
|
2003-11-24 01:49:04 +00:00
|
|
|
|
#include "fg_io.hxx"
|
2009-08-05 14:29:32 +00:00
|
|
|
|
#include "fg_os.hxx"
|
2001-06-05 22:12:17 +00:00
|
|
|
|
#include "fg_commands.hxx"
|
2003-08-06 17:13:42 +00:00
|
|
|
|
#include "fg_props.hxx"
|
2003-11-24 01:49:04 +00:00
|
|
|
|
#include "globals.hxx"
|
2004-02-15 18:41:14 +00:00
|
|
|
|
#include "logger.hxx"
|
2003-11-24 01:49:04 +00:00
|
|
|
|
#include "util.hxx"
|
|
|
|
|
#include "viewmgr.hxx"
|
2005-12-22 13:51:11 +00:00
|
|
|
|
#include "main.hxx"
|
2009-09-19 18:41:55 +00:00
|
|
|
|
#include <Main/viewer.hxx>
|
2011-01-19 18:36:04 +00:00
|
|
|
|
#include <Environment/presets.hxx>
|
2001-06-05 22:12:17 +00:00
|
|
|
|
|
2012-02-02 21:17:34 +00:00
|
|
|
|
#include <boost/scoped_array.hpp>
|
|
|
|
|
|
2008-07-27 16:25:13 +00:00
|
|
|
|
using std::string;
|
|
|
|
|
using std::ifstream;
|
|
|
|
|
using std::ofstream;
|
2001-06-01 17:52:17 +00:00
|
|
|
|
|
|
|
|
|
|
2001-07-09 16:17:20 +00:00
|
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////
|
2002-10-25 22:19:51 +00:00
|
|
|
|
// Static helper functions.
|
2001-07-09 16:17:20 +00:00
|
|
|
|
////////////////////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
|
|
|
2002-10-25 22:19:51 +00:00
|
|
|
|
static inline SGPropertyNode *
|
|
|
|
|
get_prop (const SGPropertyNode * arg)
|
2001-07-09 16:17:20 +00:00
|
|
|
|
{
|
2002-10-25 22:19:51 +00:00
|
|
|
|
return fgGetNode(arg->getStringValue("property[0]", "/null"), true);
|
2001-07-09 16:17:20 +00:00
|
|
|
|
}
|
|
|
|
|
|
2002-10-25 22:19:51 +00:00
|
|
|
|
static inline SGPropertyNode *
|
|
|
|
|
get_prop2 (const SGPropertyNode * arg)
|
|
|
|
|
{
|
|
|
|
|
return fgGetNode(arg->getStringValue("property[1]", "/null"), true);
|
|
|
|
|
}
|
2001-07-09 16:17:20 +00:00
|
|
|
|
|
|
|
|
|
|
2002-12-30 19:50:17 +00:00
|
|
|
|
/**
|
|
|
|
|
* Get a double value and split it as required.
|
|
|
|
|
*/
|
|
|
|
|
static void
|
|
|
|
|
split_value (double full_value, const char * mask,
|
|
|
|
|
double * unmodifiable, double * modifiable)
|
|
|
|
|
{
|
|
|
|
|
if (!strcmp("integer", mask)) {
|
|
|
|
|
*modifiable = (full_value < 0 ? ceil(full_value) : floor (full_value));
|
|
|
|
|
*unmodifiable = full_value - *modifiable;
|
|
|
|
|
} else if (!strcmp("decimal", mask)) {
|
|
|
|
|
*unmodifiable = (full_value < 0 ? ceil(full_value) : floor(full_value));
|
|
|
|
|
*modifiable = full_value - *unmodifiable;
|
|
|
|
|
} else {
|
|
|
|
|
if (strcmp("all", mask))
|
|
|
|
|
SG_LOG(SG_GENERAL, SG_ALERT, "Bad value " << mask << " for mask;"
|
|
|
|
|
<< " assuming 'all'");
|
|
|
|
|
*unmodifiable = 0;
|
|
|
|
|
*modifiable = full_value;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Clamp or wrap a value as specified.
|
|
|
|
|
*/
|
|
|
|
|
static void
|
|
|
|
|
limit_value (double * value, const SGPropertyNode * arg)
|
|
|
|
|
{
|
|
|
|
|
const SGPropertyNode * min_node = arg->getChild("min");
|
|
|
|
|
const SGPropertyNode * max_node = arg->getChild("max");
|
|
|
|
|
|
|
|
|
|
bool wrap = arg->getBoolValue("wrap");
|
|
|
|
|
|
|
|
|
|
if (min_node == 0 || max_node == 0)
|
|
|
|
|
wrap = false;
|
|
|
|
|
|
2003-01-04 18:47:25 +00:00
|
|
|
|
if (wrap) { // wrap such that min <= x < max
|
|
|
|
|
double min_val = min_node->getDoubleValue();
|
|
|
|
|
double max_val = max_node->getDoubleValue();
|
|
|
|
|
double resolution = arg->getDoubleValue("resolution");
|
|
|
|
|
if (resolution > 0.0) {
|
|
|
|
|
// snap to (min + N*resolution), taking special care to handle imprecision
|
|
|
|
|
int n = (int)floor((*value - min_val) / resolution + 0.5);
|
|
|
|
|
int steps = (int)floor((max_val - min_val) / resolution + 0.5);
|
|
|
|
|
SG_NORMALIZE_RANGE(n, 0, steps);
|
|
|
|
|
*value = min_val + resolution * n;
|
|
|
|
|
} else {
|
|
|
|
|
// plain circular wrapping
|
|
|
|
|
SG_NORMALIZE_RANGE(*value, min_val, max_val);
|
|
|
|
|
}
|
|
|
|
|
} else { // clamp such that min <= x <= max
|
2002-12-30 19:50:17 +00:00
|
|
|
|
if ((min_node != 0) && (*value < min_node->getDoubleValue()))
|
|
|
|
|
*value = min_node->getDoubleValue();
|
|
|
|
|
else if ((max_node != 0) && (*value > max_node->getDoubleValue()))
|
|
|
|
|
*value = max_node->getDoubleValue();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2003-01-18 21:13:11 +00:00
|
|
|
|
static bool
|
|
|
|
|
compare_values (SGPropertyNode * value1, SGPropertyNode * value2)
|
|
|
|
|
{
|
|
|
|
|
switch (value1->getType()) {
|
2009-07-15 23:15:17 +00:00
|
|
|
|
case simgear::props::BOOL:
|
2003-01-18 21:13:11 +00:00
|
|
|
|
return (value1->getBoolValue() == value2->getBoolValue());
|
2009-07-15 23:15:17 +00:00
|
|
|
|
case simgear::props::INT:
|
2003-01-18 21:13:11 +00:00
|
|
|
|
return (value1->getIntValue() == value2->getIntValue());
|
2009-07-15 23:15:17 +00:00
|
|
|
|
case simgear::props::LONG:
|
2003-01-18 21:13:11 +00:00
|
|
|
|
return (value1->getLongValue() == value2->getLongValue());
|
2009-07-15 23:15:17 +00:00
|
|
|
|
case simgear::props::FLOAT:
|
2003-01-18 21:13:11 +00:00
|
|
|
|
return (value1->getFloatValue() == value2->getFloatValue());
|
2009-07-15 23:15:17 +00:00
|
|
|
|
case simgear::props::DOUBLE:
|
2003-01-18 21:13:11 +00:00
|
|
|
|
return (value1->getDoubleValue() == value2->getDoubleValue());
|
|
|
|
|
default:
|
|
|
|
|
return !strcmp(value1->getStringValue(), value2->getStringValue());
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2002-12-30 19:50:17 +00:00
|
|
|
|
|
2001-07-09 16:17:20 +00:00
|
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////
|
|
|
|
|
// Command implementations.
|
|
|
|
|
////////////////////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
|
|
|
2001-06-04 21:38:44 +00:00
|
|
|
|
/**
|
|
|
|
|
* Built-in command: do nothing.
|
|
|
|
|
*/
|
|
|
|
|
static bool
|
2002-10-26 01:19:23 +00:00
|
|
|
|
do_null (const SGPropertyNode * arg)
|
2001-06-04 21:38:44 +00:00
|
|
|
|
{
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
2003-11-25 21:08:36 +00:00
|
|
|
|
/**
|
|
|
|
|
* Built-in command: run a Nasal script.
|
|
|
|
|
*/
|
|
|
|
|
static bool
|
|
|
|
|
do_nasal (const SGPropertyNode * arg)
|
|
|
|
|
{
|
|
|
|
|
return ((FGNasalSys*)globals->get_subsystem("nasal"))->handleCommand(arg);
|
|
|
|
|
}
|
2001-06-04 21:38:44 +00:00
|
|
|
|
|
2001-06-01 17:52:17 +00:00
|
|
|
|
/**
|
|
|
|
|
* Built-in command: exit FlightGear.
|
|
|
|
|
*
|
2003-01-24 03:08:23 +00:00
|
|
|
|
* status: the exit status to return to the operating system (defaults to 0)
|
2001-06-01 17:52:17 +00:00
|
|
|
|
*/
|
|
|
|
|
static bool
|
2002-10-26 01:19:23 +00:00
|
|
|
|
do_exit (const SGPropertyNode * arg)
|
2001-06-01 17:52:17 +00:00
|
|
|
|
{
|
2005-12-17 15:34:37 +00:00
|
|
|
|
SG_LOG(SG_INPUT, SG_INFO, "Program exit requested.");
|
2006-01-29 20:33:33 +00:00
|
|
|
|
fgSetBool("/sim/signals/exit", true);
|
2011-12-29 23:39:08 +00:00
|
|
|
|
globals->saveUserSettings();
|
2010-09-29 20:04:11 +00:00
|
|
|
|
fgOSExit(arg->getIntValue("status", 0));
|
2005-12-17 15:34:37 +00:00
|
|
|
|
return true;
|
2001-06-01 17:52:17 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2008-07-09 12:22:33 +00:00
|
|
|
|
/**
|
|
|
|
|
* Reset FlightGear (Shift-Escape or Menu->File->Reset)
|
|
|
|
|
*/
|
|
|
|
|
static bool
|
|
|
|
|
do_reset (const SGPropertyNode * arg)
|
|
|
|
|
{
|
2011-03-22 20:02:57 +00:00
|
|
|
|
fgReInitSubsystems();
|
2008-07-09 12:22:33 +00:00
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2003-01-21 02:07:29 +00:00
|
|
|
|
/**
|
|
|
|
|
* Built-in command: reinitialize one or more subsystems.
|
|
|
|
|
*
|
|
|
|
|
* subsystem[*]: the name(s) of the subsystem(s) to reinitialize; if
|
|
|
|
|
* none is specified, reinitialize all of them.
|
|
|
|
|
*/
|
|
|
|
|
static bool
|
|
|
|
|
do_reinit (const SGPropertyNode * arg)
|
|
|
|
|
{
|
|
|
|
|
bool result = true;
|
|
|
|
|
|
|
|
|
|
vector<SGPropertyNode_ptr> subsystems = arg->getChildren("subsystem");
|
2003-06-24 20:04:29 +00:00
|
|
|
|
if (subsystems.size() == 0) {
|
2003-01-21 02:07:29 +00:00
|
|
|
|
globals->get_subsystem_mgr()->reinit();
|
2003-06-24 20:04:29 +00:00
|
|
|
|
} else {
|
|
|
|
|
for ( unsigned int i = 0; i < subsystems.size(); i++ ) {
|
|
|
|
|
const char * name = subsystems[i]->getStringValue();
|
2003-09-24 17:20:55 +00:00
|
|
|
|
SGSubsystem * subsystem = globals->get_subsystem(name);
|
2003-06-24 20:04:29 +00:00
|
|
|
|
if (subsystem == 0) {
|
|
|
|
|
result = false;
|
|
|
|
|
SG_LOG( SG_GENERAL, SG_ALERT,
|
2011-02-13 17:31:26 +00:00
|
|
|
|
"Subsystem " << name << " not found" );
|
2003-06-24 20:04:29 +00:00
|
|
|
|
} else {
|
|
|
|
|
subsystem->reinit();
|
|
|
|
|
}
|
2003-01-21 02:07:29 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
2003-06-24 20:04:29 +00:00
|
|
|
|
|
2003-09-24 17:20:55 +00:00
|
|
|
|
globals->get_event_mgr()->reinit();
|
|
|
|
|
|
2003-01-21 02:07:29 +00:00
|
|
|
|
return result;
|
|
|
|
|
}
|
|
|
|
|
|
2004-04-01 15:27:53 +00:00
|
|
|
|
#if 0
|
|
|
|
|
//
|
|
|
|
|
// these routines look useful ??? but are never used in the code ???
|
|
|
|
|
//
|
|
|
|
|
|
2003-01-21 02:07:29 +00:00
|
|
|
|
/**
|
|
|
|
|
* Built-in command: suspend one or more subsystems.
|
|
|
|
|
*
|
|
|
|
|
* subsystem[*] - the name(s) of the subsystem(s) to suspend.
|
|
|
|
|
*/
|
|
|
|
|
static bool
|
|
|
|
|
do_suspend (const SGPropertyNode * arg)
|
|
|
|
|
{
|
|
|
|
|
bool result = true;
|
|
|
|
|
|
|
|
|
|
vector<SGPropertyNode_ptr> subsystems = arg->getChildren("subsystem");
|
2003-05-06 23:46:24 +00:00
|
|
|
|
for ( unsigned int i = 0; i < subsystems.size(); i++ ) {
|
2003-01-21 02:07:29 +00:00
|
|
|
|
const char * name = subsystems[i]->getStringValue();
|
2003-09-24 17:20:55 +00:00
|
|
|
|
SGSubsystem * subsystem = globals->get_subsystem(name);
|
2003-01-21 02:07:29 +00:00
|
|
|
|
if (subsystem == 0) {
|
|
|
|
|
result = false;
|
2011-02-13 17:31:26 +00:00
|
|
|
|
SG_LOG(SG_GENERAL, SG_ALERT, "Subsystem " << name << " not found");
|
2003-01-21 02:07:29 +00:00
|
|
|
|
} else {
|
|
|
|
|
subsystem->suspend();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return result;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Built-in command: suspend one or more subsystems.
|
|
|
|
|
*
|
|
|
|
|
* subsystem[*] - the name(s) of the subsystem(s) to suspend.
|
|
|
|
|
*/
|
|
|
|
|
static bool
|
|
|
|
|
do_resume (const SGPropertyNode * arg)
|
|
|
|
|
{
|
|
|
|
|
bool result = true;
|
|
|
|
|
|
|
|
|
|
vector<SGPropertyNode_ptr> subsystems = arg->getChildren("subsystem");
|
2003-05-06 23:46:24 +00:00
|
|
|
|
for ( unsigned int i = 0; i < subsystems.size(); i++ ) {
|
2003-01-21 02:07:29 +00:00
|
|
|
|
const char * name = subsystems[i]->getStringValue();
|
2003-09-24 17:20:55 +00:00
|
|
|
|
SGSubsystem * subsystem = globals->get_subsystem(name);
|
2003-01-21 02:07:29 +00:00
|
|
|
|
if (subsystem == 0) {
|
|
|
|
|
result = false;
|
2011-02-13 17:31:26 +00:00
|
|
|
|
SG_LOG(SG_GENERAL, SG_ALERT, "Subsystem " << name << " not found");
|
2003-01-21 02:07:29 +00:00
|
|
|
|
} else {
|
|
|
|
|
subsystem->resume();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return result;
|
|
|
|
|
}
|
|
|
|
|
|
2004-04-01 15:27:53 +00:00
|
|
|
|
#endif
|
|
|
|
|
|
2011-10-01 20:58:40 +00:00
|
|
|
|
/**
|
|
|
|
|
* Built-in command: replay the FDR buffer
|
|
|
|
|
*/
|
|
|
|
|
static bool
|
|
|
|
|
do_replay (const SGPropertyNode * arg)
|
|
|
|
|
{
|
|
|
|
|
FGReplay *r = (FGReplay *)(globals->get_subsystem( "replay" ));
|
|
|
|
|
return r->start();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Built-in command: pause/unpause the sim
|
|
|
|
|
*/
|
2011-05-21 11:27:09 +00:00
|
|
|
|
static bool
|
|
|
|
|
do_pause (const SGPropertyNode * arg)
|
|
|
|
|
{
|
2011-05-21 12:51:10 +00:00
|
|
|
|
bool paused = fgGetBool("/sim/freeze/master",true) || fgGetBool("/sim/freeze/clock",true);
|
2011-10-01 20:58:40 +00:00
|
|
|
|
if (paused && (fgGetInt("/sim/freeze/replay-state",0)>0))
|
|
|
|
|
{
|
|
|
|
|
do_replay(NULL);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
fgSetBool("/sim/freeze/master",!paused);
|
|
|
|
|
fgSetBool("/sim/freeze/clock",!paused);
|
|
|
|
|
}
|
2011-05-21 12:07:03 +00:00
|
|
|
|
return true;
|
2011-05-21 11:27:09 +00:00
|
|
|
|
}
|
2003-01-21 02:07:29 +00:00
|
|
|
|
|
2001-06-05 22:12:17 +00:00
|
|
|
|
/**
|
|
|
|
|
* Built-in command: load flight.
|
|
|
|
|
*
|
|
|
|
|
* file (optional): the name of the file to load (relative to current
|
2003-01-21 02:07:29 +00:00
|
|
|
|
* directory). Defaults to "fgfs.sav"
|
2001-06-05 22:12:17 +00:00
|
|
|
|
*/
|
|
|
|
|
static bool
|
2002-10-26 01:19:23 +00:00
|
|
|
|
do_load (const SGPropertyNode * arg)
|
2001-06-05 22:12:17 +00:00
|
|
|
|
{
|
2008-07-11 20:55:43 +00:00
|
|
|
|
string file = arg->getStringValue("file", "fgfs.sav");
|
|
|
|
|
if (file.size() < 4 || file.substr(file.size() - 4) != ".sav")
|
|
|
|
|
file += ".sav";
|
2008-07-11 16:36:54 +00:00
|
|
|
|
|
|
|
|
|
if (!fgValidatePath(file.c_str(), false)) {
|
|
|
|
|
SG_LOG(SG_IO, SG_ALERT, "load: reading '" << file << "' denied "
|
|
|
|
|
"(unauthorized access)");
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ifstream input(file.c_str());
|
|
|
|
|
if (input.good() && fgLoadFlight(input)) {
|
|
|
|
|
input.close();
|
|
|
|
|
SG_LOG(SG_INPUT, SG_INFO, "Restored flight from " << file);
|
|
|
|
|
return true;
|
|
|
|
|
} else {
|
|
|
|
|
SG_LOG(SG_INPUT, SG_WARN, "Cannot load flight from " << file);
|
|
|
|
|
return false;
|
|
|
|
|
}
|
2001-06-05 22:12:17 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Built-in command: save flight.
|
|
|
|
|
*
|
|
|
|
|
* file (optional): the name of the file to save (relative to the
|
|
|
|
|
* current directory). Defaults to "fgfs.sav".
|
|
|
|
|
*/
|
|
|
|
|
static bool
|
2002-10-26 01:19:23 +00:00
|
|
|
|
do_save (const SGPropertyNode * arg)
|
2001-06-05 22:12:17 +00:00
|
|
|
|
{
|
2008-07-11 20:55:43 +00:00
|
|
|
|
string file = arg->getStringValue("file", "fgfs.sav");
|
|
|
|
|
if (file.size() < 4 || file.substr(file.size() - 4) != ".sav")
|
|
|
|
|
file += ".sav";
|
2008-07-11 16:36:54 +00:00
|
|
|
|
|
|
|
|
|
if (!fgValidatePath(file.c_str(), false)) {
|
2009-02-01 23:44:36 +00:00
|
|
|
|
SG_LOG(SG_IO, SG_ALERT, "save: writing '" << file << "' denied "
|
2008-07-11 16:36:54 +00:00
|
|
|
|
"(unauthorized access)");
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool write_all = arg->getBoolValue("write-all", false);
|
|
|
|
|
SG_LOG(SG_INPUT, SG_INFO, "Saving flight");
|
|
|
|
|
ofstream output(file.c_str());
|
|
|
|
|
if (output.good() && fgSaveFlight(output, write_all)) {
|
|
|
|
|
output.close();
|
|
|
|
|
SG_LOG(SG_INPUT, SG_INFO, "Saved flight to " << file);
|
|
|
|
|
return true;
|
|
|
|
|
} else {
|
|
|
|
|
SG_LOG(SG_INPUT, SG_ALERT, "Cannot save flight to " << file);
|
|
|
|
|
return false;
|
|
|
|
|
}
|
2001-06-05 22:12:17 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Built-in command: (re)load the panel.
|
|
|
|
|
*
|
|
|
|
|
* path (optional): the file name to load the panel from
|
|
|
|
|
* (relative to FG_ROOT). Defaults to the value of /sim/panel/path,
|
|
|
|
|
* and if that's unspecified, to "Panels/Default/default.xml".
|
|
|
|
|
*/
|
|
|
|
|
static bool
|
2002-10-26 01:19:23 +00:00
|
|
|
|
do_panel_load (const SGPropertyNode * arg)
|
2001-06-05 22:12:17 +00:00
|
|
|
|
{
|
|
|
|
|
string panel_path =
|
2010-10-28 12:54:45 +00:00
|
|
|
|
arg->getStringValue("path", fgGetString("/sim/panel/path"));
|
|
|
|
|
if (panel_path.empty()) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2001-06-05 22:12:17 +00:00
|
|
|
|
FGPanel * new_panel = fgReadPanel(panel_path);
|
|
|
|
|
if (new_panel == 0) {
|
|
|
|
|
SG_LOG(SG_INPUT, SG_ALERT,
|
|
|
|
|
"Error reading new panel from " << panel_path);
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
SG_LOG(SG_INPUT, SG_INFO, "Loaded new panel from " << panel_path);
|
2003-03-30 12:46:08 +00:00
|
|
|
|
globals->get_current_panel()->unbind();
|
|
|
|
|
globals->set_current_panel( new_panel );
|
|
|
|
|
globals->get_current_panel()->bind();
|
2001-06-05 22:12:17 +00:00
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2002-03-23 23:16:13 +00:00
|
|
|
|
/**
|
|
|
|
|
* Built-in command: pass a mouse click to the panel.
|
|
|
|
|
*
|
|
|
|
|
* button: the mouse button number, zero-based.
|
|
|
|
|
* is-down: true if the button is down, false if it is up.
|
|
|
|
|
* x-pos: the x position of the mouse click.
|
|
|
|
|
* y-pos: the y position of the mouse click.
|
|
|
|
|
*/
|
|
|
|
|
static bool
|
2002-10-26 01:19:23 +00:00
|
|
|
|
do_panel_mouse_click (const SGPropertyNode * arg)
|
2002-03-23 23:16:13 +00:00
|
|
|
|
{
|
2003-03-30 12:46:08 +00:00
|
|
|
|
if (globals->get_current_panel() != 0)
|
|
|
|
|
return globals->get_current_panel()
|
2002-03-23 23:16:13 +00:00
|
|
|
|
->doMouseAction(arg->getIntValue("button"),
|
|
|
|
|
arg->getBoolValue("is-down") ? PU_DOWN : PU_UP,
|
|
|
|
|
arg->getIntValue("x-pos"),
|
|
|
|
|
arg->getIntValue("y-pos"));
|
|
|
|
|
else
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2001-06-05 22:12:17 +00:00
|
|
|
|
/**
|
|
|
|
|
* Built-in command: (re)load preferences.
|
|
|
|
|
*
|
|
|
|
|
* path (optional): the file name to load the panel from (relative
|
|
|
|
|
* to FG_ROOT). Defaults to "preferences.xml".
|
|
|
|
|
*/
|
|
|
|
|
static bool
|
2002-10-26 01:19:23 +00:00
|
|
|
|
do_preferences_load (const SGPropertyNode * arg)
|
2001-06-05 22:12:17 +00:00
|
|
|
|
{
|
2001-07-19 04:51:05 +00:00
|
|
|
|
try {
|
2002-11-06 18:57:31 +00:00
|
|
|
|
fgLoadProps(arg->getStringValue("path", "preferences.xml"),
|
|
|
|
|
globals->get_props());
|
2001-07-24 23:50:08 +00:00
|
|
|
|
} catch (const sg_exception &e) {
|
|
|
|
|
guiErrorMessage("Error reading global preferences: ", e);
|
2001-06-05 22:12:17 +00:00
|
|
|
|
return false;
|
|
|
|
|
}
|
2001-07-19 04:51:05 +00:00
|
|
|
|
SG_LOG(SG_INPUT, SG_INFO, "Successfully read global preferences.");
|
|
|
|
|
return true;
|
2001-06-05 22:12:17 +00:00
|
|
|
|
}
|
|
|
|
|
|
2003-01-04 18:47:25 +00:00
|
|
|
|
static void
|
2002-08-25 20:56:16 +00:00
|
|
|
|
do_view_next( bool )
|
|
|
|
|
{
|
|
|
|
|
globals->get_current_view()->setHeadingOffset_deg(0.0);
|
|
|
|
|
globals->get_viewmgr()->next_view();
|
|
|
|
|
}
|
|
|
|
|
|
2003-01-04 18:47:25 +00:00
|
|
|
|
static void
|
2002-08-25 20:56:16 +00:00
|
|
|
|
do_view_prev( bool )
|
|
|
|
|
{
|
|
|
|
|
globals->get_current_view()->setHeadingOffset_deg(0.0);
|
|
|
|
|
globals->get_viewmgr()->prev_view();
|
|
|
|
|
}
|
|
|
|
|
|
2001-06-01 17:52:17 +00:00
|
|
|
|
/**
|
|
|
|
|
* Built-in command: cycle view.
|
|
|
|
|
*/
|
|
|
|
|
static bool
|
2002-10-26 01:19:23 +00:00
|
|
|
|
do_view_cycle (const SGPropertyNode * arg)
|
2001-06-01 17:52:17 +00:00
|
|
|
|
{
|
Major viewer-code overhaul from Jim Wilson:
Description:
This update includes the new viewer interface as proposed by David M. and
a first pass at cleaning up the viewer/view manager code by Jim W.
Note that I have dropped Main/viewer_lookat.?xx and Main/viewer_rph.?xx and
modified the Makefile.am accordingly.
Detail of work:
Overall:
The code reads a little easier. There are still some unnecessary bits in
there and I'd like to supplement the comments in the viewer.hxx with a tiny
bit on each interface group and what the groupings mean (similar but briefer
than what you emailed me the other day). I tried not to mess up the style,
but there is an occasional inconsistency. In general I wouldn't call it done
(especially since there's no tower yet! :)), but I'd like to get this out
there so others can comment, and test.
In Viewer:
The interface as you suggested has been implemented. Basically everything
seems to work as it did visually. There is no difference that I can see in
performance, although some things might be a tiny bit faster.
I've merged the lookat and rph (pilot view) code into the recalc for the
viewer. There is still some redundancy between the two, but a lot has been
removed. In some cases I've taken some code that we'd likely want to inline
anyway and left it in there in duplicate. You'll see that the code for both
looks a little cleaner. I need to take a closer look at the rotations in
particular. I've cleaned up a little there, but I suspect more can be done
to streamline this.
The external declaration to the Quat_mat in mouse.cxx has been removed. IMHO
the quat doesn't serve any intrinsic purpose in mouse.cxx, but I'm not about
to rip it out. It would seem that there more conventional ways to get
spherical data that are just as fast. In any case all the viewer was pulling
from the quat matrix was the pitch value so I modified mouse.cxx to output to
our pitchOffset input and that works fine.
I've changed the native values to degrees from radians where appropriate.
This required a conversion from degrees to radians in a couple modules that
access the interface. Perhaps we should add interface calls that do the
conversion, e.g. a getHeadingOffset_rad() to go along with the
getHeadingOffset_deg().
On the view_offset (now headingOffset) thing there are two entry points
because of the ability to instantly switch views or to scroll to a new view
angle (by hitting the numeric keys for example). This leaves an anomaly in
the interface which should be resolved by adding "goal" settings to the
interface, e.g. a setGoalHeadingOffset_deg(), setGoalPitchOffset_deg(), etc.
Other than these two issues, the next step here will be to look at some
further optimizations, and to write support code for a tower view. That
should be fairly simple at this point. I was considering creating a
"simulated tower view" or "pedestrian view" that defaulted to a position off
to the right of whereever the plane is at the moment you switch to the tower
view. This could be a fall back when we don't have an actual tower location
at hand (as would be the case with rural airports).
ViewManager:
Basically all I did here was neaten things up by ripping out excess crap and
made it compatible as is with the new interface.
The result is that viewmanager is now ready to be developed. The two
preexisting views are still hardcoded into the view manager. The next step
would be to design configuration xml (eg /sim/view[x]/config/blahblah) that
could be used to set up as many views as we want. If we want to take the easy
way out, we might want to insist that view[0] be a pilot-view and have
viewmanager check for that.
2002-03-20 17:43:28 +00:00
|
|
|
|
globals->get_current_view()->setHeadingOffset_deg(0.0);
|
2002-03-14 00:29:20 +00:00
|
|
|
|
globals->get_viewmgr()->next_view();
|
2001-06-01 17:52:17 +00:00
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Built-in command: capture screen.
|
|
|
|
|
*/
|
|
|
|
|
static bool
|
2002-10-26 01:19:23 +00:00
|
|
|
|
do_screen_capture (const SGPropertyNode * arg)
|
2001-06-01 17:52:17 +00:00
|
|
|
|
{
|
2008-04-26 22:34:46 +00:00
|
|
|
|
return fgDumpSnapShot();
|
2001-06-01 17:52:17 +00:00
|
|
|
|
}
|
|
|
|
|
|
2009-08-08 10:20:32 +00:00
|
|
|
|
static bool
|
|
|
|
|
do_reload_shaders (const SGPropertyNode*)
|
|
|
|
|
{
|
|
|
|
|
simgear::reload_shaders();
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
2007-05-26 11:39:13 +00:00
|
|
|
|
static bool
|
|
|
|
|
do_dump_scene_graph (const SGPropertyNode*)
|
|
|
|
|
{
|
|
|
|
|
fgDumpSceneGraph();
|
|
|
|
|
return true;
|
|
|
|
|
}
|
2001-06-01 17:52:17 +00:00
|
|
|
|
|
2007-11-21 20:51:49 +00:00
|
|
|
|
static bool
|
|
|
|
|
do_dump_terrain_branch (const SGPropertyNode*)
|
|
|
|
|
{
|
|
|
|
|
fgDumpTerrainBranch();
|
|
|
|
|
|
|
|
|
|
double lon_deg = fgGetDouble("/position/longitude-deg");
|
|
|
|
|
double lat_deg = fgGetDouble("/position/latitude-deg");
|
|
|
|
|
SGGeod geodPos = SGGeod::fromDegFt(lon_deg, lat_deg, 0.0);
|
|
|
|
|
SGVec3d zero = SGVec3d::fromGeod(geodPos);
|
|
|
|
|
|
|
|
|
|
SG_LOG(SG_INPUT, SG_INFO, "Model parameters:");
|
|
|
|
|
SG_LOG(SG_INPUT, SG_INFO, "Center: " << zero.x() << ", " << zero.y() << ", " << zero.z() );
|
|
|
|
|
SG_LOG(SG_INPUT, SG_INFO, "Rotation: " << lat_deg << ", " << lon_deg );
|
|
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
2010-11-14 14:12:58 +00:00
|
|
|
|
static bool
|
|
|
|
|
do_print_visible_scene_info(const SGPropertyNode*)
|
|
|
|
|
{
|
|
|
|
|
fgPrintVisibleSceneInfoCommand();
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
2004-09-27 02:39:54 +00:00
|
|
|
|
/**
|
|
|
|
|
* Built-in command: hires capture screen.
|
|
|
|
|
*/
|
|
|
|
|
static bool
|
|
|
|
|
do_hires_screen_capture (const SGPropertyNode * arg)
|
|
|
|
|
{
|
|
|
|
|
fgHiResDump();
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2001-06-26 21:59:49 +00:00
|
|
|
|
/**
|
|
|
|
|
* Reload the tile cache.
|
|
|
|
|
*/
|
|
|
|
|
static bool
|
2002-10-26 01:19:23 +00:00
|
|
|
|
do_tile_cache_reload (const SGPropertyNode * arg)
|
2001-06-26 21:59:49 +00:00
|
|
|
|
{
|
2002-01-20 03:52:36 +00:00
|
|
|
|
static const SGPropertyNode *master_freeze
|
|
|
|
|
= fgGetNode("/sim/freeze/master");
|
|
|
|
|
bool freeze = master_freeze->getBoolValue();
|
|
|
|
|
SG_LOG(SG_INPUT, SG_INFO, "ReIniting TileCache");
|
|
|
|
|
if ( !freeze ) {
|
|
|
|
|
fgSetBool("/sim/freeze/master", true);
|
|
|
|
|
}
|
2010-10-01 18:09:19 +00:00
|
|
|
|
|
|
|
|
|
globals->get_subsystem("tile-manager")->reinit();
|
|
|
|
|
|
2002-01-20 03:52:36 +00:00
|
|
|
|
if ( !freeze ) {
|
|
|
|
|
fgSetBool("/sim/freeze/master", false);
|
|
|
|
|
}
|
|
|
|
|
return true;
|
2001-06-26 21:59:49 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2011-01-19 18:36:04 +00:00
|
|
|
|
#if 0
|
|
|
|
|
These do_set_(some-environment-parameters) are deprecated and no longer
|
|
|
|
|
useful/functional - Torsten Dreyer, January 2011
|
2004-01-31 19:47:45 +00:00
|
|
|
|
/**
|
|
|
|
|
* Set the sea level outside air temperature and assigning that to all
|
|
|
|
|
* boundary and aloft environment layers.
|
|
|
|
|
*/
|
|
|
|
|
static bool
|
2010-03-24 14:46:28 +00:00
|
|
|
|
do_set_sea_level_degc ( double temp_sea_level_degc)
|
2004-01-31 19:47:45 +00:00
|
|
|
|
{
|
|
|
|
|
SGPropertyNode *node, *child;
|
|
|
|
|
|
|
|
|
|
// boundary layers
|
|
|
|
|
node = fgGetNode( "/environment/config/boundary" );
|
|
|
|
|
if ( node != NULL ) {
|
|
|
|
|
int i = 0;
|
|
|
|
|
while ( ( child = node->getNode( "entry", i ) ) != NULL ) {
|
|
|
|
|
child->setDoubleValue( "temperature-sea-level-degc",
|
|
|
|
|
temp_sea_level_degc );
|
|
|
|
|
++i;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// aloft layers
|
|
|
|
|
node = fgGetNode( "/environment/config/aloft" );
|
|
|
|
|
if ( node != NULL ) {
|
|
|
|
|
int i = 0;
|
|
|
|
|
while ( ( child = node->getNode( "entry", i ) ) != NULL ) {
|
|
|
|
|
child->setDoubleValue( "temperature-sea-level-degc",
|
|
|
|
|
temp_sea_level_degc );
|
|
|
|
|
++i;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
2010-03-24 14:46:28 +00:00
|
|
|
|
static bool
|
|
|
|
|
do_set_sea_level_degc (const SGPropertyNode * arg)
|
|
|
|
|
{
|
|
|
|
|
return do_set_sea_level_degc( arg->getDoubleValue("temp-degc", 15.0) );
|
|
|
|
|
}
|
|
|
|
|
|
2004-01-31 19:47:45 +00:00
|
|
|
|
|
2003-11-10 21:59:35 +00:00
|
|
|
|
/**
|
|
|
|
|
* Set the outside air temperature at the "current" altitude by first
|
|
|
|
|
* calculating the corresponding sea level temp, and assigning that to
|
|
|
|
|
* all boundary and aloft environment layers.
|
|
|
|
|
*/
|
|
|
|
|
static bool
|
|
|
|
|
do_set_oat_degc (const SGPropertyNode * arg)
|
|
|
|
|
{
|
2010-03-24 14:46:28 +00:00
|
|
|
|
double oat_degc = arg->getDoubleValue("temp-degc", 15.0);
|
2004-02-22 14:21:37 +00:00
|
|
|
|
// check for an altitude specified in the arguments, otherwise use
|
|
|
|
|
// current aircraft altitude.
|
|
|
|
|
const SGPropertyNode *altitude_ft = arg->getChild("altitude-ft");
|
|
|
|
|
if ( altitude_ft == NULL ) {
|
|
|
|
|
altitude_ft = fgGetNode("/position/altitude-ft");
|
|
|
|
|
}
|
2003-11-10 21:59:35 +00:00
|
|
|
|
|
|
|
|
|
FGEnvironment dummy; // instantiate a dummy so we can leech a method
|
|
|
|
|
dummy.set_elevation_ft( altitude_ft->getDoubleValue() );
|
2010-03-24 14:46:28 +00:00
|
|
|
|
dummy.set_temperature_degc( oat_degc );
|
|
|
|
|
return do_set_sea_level_degc( dummy.get_temperature_sea_level_degc());
|
2003-11-10 21:59:35 +00:00
|
|
|
|
}
|
|
|
|
|
|
2004-02-22 14:21:37 +00:00
|
|
|
|
/**
|
|
|
|
|
* Set the sea level outside air dewpoint and assigning that to all
|
|
|
|
|
* boundary and aloft environment layers.
|
|
|
|
|
*/
|
|
|
|
|
static bool
|
2010-03-24 14:46:28 +00:00
|
|
|
|
do_set_dewpoint_sea_level_degc (double dewpoint_sea_level_degc)
|
2004-02-22 14:21:37 +00:00
|
|
|
|
{
|
|
|
|
|
|
|
|
|
|
SGPropertyNode *node, *child;
|
|
|
|
|
|
|
|
|
|
// boundary layers
|
|
|
|
|
node = fgGetNode( "/environment/config/boundary" );
|
|
|
|
|
if ( node != NULL ) {
|
|
|
|
|
int i = 0;
|
|
|
|
|
while ( ( child = node->getNode( "entry", i ) ) != NULL ) {
|
|
|
|
|
child->setDoubleValue( "dewpoint-sea-level-degc",
|
|
|
|
|
dewpoint_sea_level_degc );
|
|
|
|
|
++i;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// aloft layers
|
|
|
|
|
node = fgGetNode( "/environment/config/aloft" );
|
|
|
|
|
if ( node != NULL ) {
|
|
|
|
|
int i = 0;
|
|
|
|
|
while ( ( child = node->getNode( "entry", i ) ) != NULL ) {
|
|
|
|
|
child->setDoubleValue( "dewpoint-sea-level-degc",
|
|
|
|
|
dewpoint_sea_level_degc );
|
|
|
|
|
++i;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
2010-03-24 14:46:28 +00:00
|
|
|
|
static bool
|
|
|
|
|
do_set_dewpoint_sea_level_degc (const SGPropertyNode * arg)
|
|
|
|
|
{
|
|
|
|
|
return do_set_dewpoint_sea_level_degc(arg->getDoubleValue("dewpoint-degc", 5.0));
|
|
|
|
|
}
|
2004-02-22 14:21:37 +00:00
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Set the outside air dewpoint at the "current" altitude by first
|
|
|
|
|
* calculating the corresponding sea level dewpoint, and assigning
|
|
|
|
|
* that to all boundary and aloft environment layers.
|
|
|
|
|
*/
|
|
|
|
|
static bool
|
|
|
|
|
do_set_dewpoint_degc (const SGPropertyNode * arg)
|
|
|
|
|
{
|
2010-03-24 14:46:28 +00:00
|
|
|
|
double dewpoint_degc = arg->getDoubleValue("dewpoint-degc", 5.0);
|
2004-02-22 14:21:37 +00:00
|
|
|
|
|
|
|
|
|
// check for an altitude specified in the arguments, otherwise use
|
|
|
|
|
// current aircraft altitude.
|
|
|
|
|
const SGPropertyNode *altitude_ft = arg->getChild("altitude-ft");
|
|
|
|
|
if ( altitude_ft == NULL ) {
|
|
|
|
|
altitude_ft = fgGetNode("/position/altitude-ft");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
FGEnvironment dummy; // instantiate a dummy so we can leech a method
|
|
|
|
|
dummy.set_elevation_ft( altitude_ft->getDoubleValue() );
|
2010-03-24 14:46:28 +00:00
|
|
|
|
dummy.set_dewpoint_degc( dewpoint_degc );
|
|
|
|
|
return do_set_dewpoint_sea_level_degc(dummy.get_dewpoint_sea_level_degc());
|
2004-02-22 14:21:37 +00:00
|
|
|
|
}
|
2011-01-19 18:36:04 +00:00
|
|
|
|
#endif
|
2003-09-16 22:35:16 +00:00
|
|
|
|
|
2001-06-01 17:52:17 +00:00
|
|
|
|
/**
|
|
|
|
|
* Built-in command: toggle a bool property value.
|
|
|
|
|
*
|
|
|
|
|
* property: The name of the property to toggle.
|
|
|
|
|
*/
|
|
|
|
|
static bool
|
2002-10-26 01:19:23 +00:00
|
|
|
|
do_property_toggle (const SGPropertyNode * arg)
|
2001-06-01 17:52:17 +00:00
|
|
|
|
{
|
2002-10-25 22:19:51 +00:00
|
|
|
|
SGPropertyNode * prop = get_prop(arg);
|
2001-07-09 16:17:20 +00:00
|
|
|
|
return prop->setBoolValue(!prop->getBoolValue());
|
2001-06-01 17:52:17 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Built-in command: assign a value to a property.
|
|
|
|
|
*
|
|
|
|
|
* property: the name of the property to assign.
|
2002-12-31 03:05:38 +00:00
|
|
|
|
* value: the value to assign; or
|
|
|
|
|
* property[1]: the property to copy from.
|
2001-06-01 17:52:17 +00:00
|
|
|
|
*/
|
|
|
|
|
static bool
|
2002-10-26 01:19:23 +00:00
|
|
|
|
do_property_assign (const SGPropertyNode * arg)
|
2001-06-01 17:52:17 +00:00
|
|
|
|
{
|
2002-10-25 22:19:51 +00:00
|
|
|
|
SGPropertyNode * prop = get_prop(arg);
|
|
|
|
|
const SGPropertyNode * value = arg->getNode("value");
|
2001-06-01 17:52:17 +00:00
|
|
|
|
|
2002-12-31 03:05:38 +00:00
|
|
|
|
if (value != 0)
|
|
|
|
|
return prop->setUnspecifiedValue(value->getStringValue());
|
|
|
|
|
else
|
2011-07-10 19:25:16 +00:00
|
|
|
|
{
|
|
|
|
|
const SGPropertyNode * prop2 = get_prop2(arg);
|
|
|
|
|
if (prop2)
|
|
|
|
|
return prop->setUnspecifiedValue(prop2->getStringValue());
|
|
|
|
|
else
|
|
|
|
|
return false;
|
|
|
|
|
}
|
2001-06-01 17:52:17 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Built-in command: increment or decrement a property value.
|
|
|
|
|
*
|
2002-12-31 12:08:32 +00:00
|
|
|
|
* If the 'step' argument is present, it will be used; otherwise,
|
|
|
|
|
* the command uses 'offset' and 'factor', usually from the mouse.
|
|
|
|
|
*
|
2001-06-01 17:52:17 +00:00
|
|
|
|
* property: the name of the property to increment or decrement.
|
2001-07-27 22:00:35 +00:00
|
|
|
|
* step: the amount of the increment or decrement (default: 0).
|
2002-12-31 12:08:32 +00:00
|
|
|
|
* offset: offset from the current setting (used for the mouse; multiplied
|
|
|
|
|
* by factor)
|
|
|
|
|
* factor: scaling amount for the offset (defaults to 1).
|
2001-07-27 22:00:35 +00:00
|
|
|
|
* min: the minimum allowed value (default: no minimum).
|
|
|
|
|
* max: the maximum allowed value (default: no maximum).
|
2002-12-30 19:50:17 +00:00
|
|
|
|
* mask: 'integer' to apply only to the left of the decimal point,
|
|
|
|
|
* 'decimal' to apply only to the right of the decimal point,
|
|
|
|
|
* or 'all' to apply to the whole number (the default).
|
2001-07-27 22:00:35 +00:00
|
|
|
|
* wrap: true if the value should be wrapped when it passes min or max;
|
|
|
|
|
* both min and max must be present for this to work (default:
|
|
|
|
|
* false).
|
2001-06-01 17:52:17 +00:00
|
|
|
|
*/
|
|
|
|
|
static bool
|
2002-10-26 01:19:23 +00:00
|
|
|
|
do_property_adjust (const SGPropertyNode * arg)
|
2001-06-01 17:52:17 +00:00
|
|
|
|
{
|
2002-10-25 22:19:51 +00:00
|
|
|
|
SGPropertyNode * prop = get_prop(arg);
|
|
|
|
|
|
2002-12-31 12:08:32 +00:00
|
|
|
|
double amount = 0;
|
|
|
|
|
if (arg->hasValue("step"))
|
|
|
|
|
amount = arg->getDoubleValue("step");
|
|
|
|
|
else
|
|
|
|
|
amount = (arg->getDoubleValue("factor", 1)
|
|
|
|
|
* arg->getDoubleValue("offset"));
|
|
|
|
|
|
2002-12-30 19:50:17 +00:00
|
|
|
|
double unmodifiable, modifiable;
|
|
|
|
|
split_value(prop->getDoubleValue(), arg->getStringValue("mask", "all"),
|
|
|
|
|
&unmodifiable, &modifiable);
|
2002-12-31 12:08:32 +00:00
|
|
|
|
modifiable += amount;
|
2002-12-30 19:50:17 +00:00
|
|
|
|
limit_value(&modifiable, arg);
|
2002-10-25 22:19:51 +00:00
|
|
|
|
|
2002-12-30 19:50:17 +00:00
|
|
|
|
prop->setDoubleValue(unmodifiable + modifiable);
|
2003-01-04 18:47:25 +00:00
|
|
|
|
|
|
|
|
|
return true;
|
2001-06-01 17:52:17 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2001-06-26 21:59:49 +00:00
|
|
|
|
/**
|
2001-06-29 03:47:39 +00:00
|
|
|
|
* Built-in command: multiply a property value.
|
|
|
|
|
*
|
|
|
|
|
* property: the name of the property to multiply.
|
|
|
|
|
* factor: the amount by which to multiply.
|
2002-12-30 19:50:17 +00:00
|
|
|
|
* min: the minimum allowed value (default: no minimum).
|
|
|
|
|
* max: the maximum allowed value (default: no maximum).
|
|
|
|
|
* mask: 'integer' to apply only to the left of the decimal point,
|
|
|
|
|
* 'decimal' to apply only to the right of the decimal point,
|
|
|
|
|
* or 'all' to apply to the whole number (the default).
|
|
|
|
|
* wrap: true if the value should be wrapped when it passes min or max;
|
|
|
|
|
* both min and max must be present for this to work (default:
|
|
|
|
|
* false).
|
2001-06-29 03:47:39 +00:00
|
|
|
|
*/
|
|
|
|
|
static bool
|
2002-10-26 01:19:23 +00:00
|
|
|
|
do_property_multiply (const SGPropertyNode * arg)
|
2001-06-29 03:47:39 +00:00
|
|
|
|
{
|
2002-10-25 22:19:51 +00:00
|
|
|
|
SGPropertyNode * prop = get_prop(arg);
|
2002-12-30 19:50:17 +00:00
|
|
|
|
double factor = arg->getDoubleValue("factor", 1);
|
2002-10-25 22:19:51 +00:00
|
|
|
|
|
2002-12-30 19:50:17 +00:00
|
|
|
|
double unmodifiable, modifiable;
|
|
|
|
|
split_value(prop->getDoubleValue(), arg->getStringValue("mask", "all"),
|
|
|
|
|
&unmodifiable, &modifiable);
|
|
|
|
|
modifiable *= factor;
|
|
|
|
|
limit_value(&modifiable, arg);
|
2002-10-25 22:19:51 +00:00
|
|
|
|
|
2002-12-30 19:50:17 +00:00
|
|
|
|
prop->setDoubleValue(unmodifiable + modifiable);
|
2003-01-04 18:47:25 +00:00
|
|
|
|
|
|
|
|
|
return true;
|
2001-06-26 21:59:49 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2001-06-01 17:52:17 +00:00
|
|
|
|
/**
|
|
|
|
|
* Built-in command: swap two property values.
|
|
|
|
|
*
|
|
|
|
|
* property[0]: the name of the first property.
|
|
|
|
|
* property[1]: the name of the second property.
|
|
|
|
|
*/
|
|
|
|
|
static bool
|
2002-10-26 01:19:23 +00:00
|
|
|
|
do_property_swap (const SGPropertyNode * arg)
|
2001-06-01 17:52:17 +00:00
|
|
|
|
{
|
2002-10-25 22:19:51 +00:00
|
|
|
|
SGPropertyNode * prop1 = get_prop(arg);
|
|
|
|
|
SGPropertyNode * prop2 = get_prop2(arg);
|
2001-07-09 16:17:20 +00:00
|
|
|
|
|
|
|
|
|
// FIXME: inefficient
|
|
|
|
|
const string & tmp = prop1->getStringValue();
|
|
|
|
|
return (prop1->setUnspecifiedValue(prop2->getStringValue()) &&
|
2002-03-19 17:12:13 +00:00
|
|
|
|
prop2->setUnspecifiedValue(tmp.c_str()));
|
2001-06-01 17:52:17 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2001-06-04 21:38:44 +00:00
|
|
|
|
/**
|
2002-12-22 19:58:34 +00:00
|
|
|
|
* Built-in command: Set a property to an axis or other moving input.
|
2001-06-04 21:38:44 +00:00
|
|
|
|
*
|
|
|
|
|
* property: the name of the property to set.
|
|
|
|
|
* setting: the current input setting, usually between -1.0 and 1.0.
|
|
|
|
|
* offset: the offset to shift by, before applying the factor.
|
|
|
|
|
* factor: the factor to multiply by (use negative to reverse).
|
|
|
|
|
*/
|
|
|
|
|
static bool
|
2002-10-26 01:19:23 +00:00
|
|
|
|
do_property_scale (const SGPropertyNode * arg)
|
2001-06-04 21:38:44 +00:00
|
|
|
|
{
|
2002-10-25 22:19:51 +00:00
|
|
|
|
SGPropertyNode * prop = get_prop(arg);
|
|
|
|
|
double setting = arg->getDoubleValue("setting");
|
|
|
|
|
double offset = arg->getDoubleValue("offset", 0.0);
|
|
|
|
|
double factor = arg->getDoubleValue("factor", 1.0);
|
|
|
|
|
bool squared = arg->getBoolValue("squared", false);
|
2003-03-01 21:28:51 +00:00
|
|
|
|
int power = arg->getIntValue("power", (squared ? 2 : 1));
|
|
|
|
|
|
|
|
|
|
int sign = (setting < 0 ? -1 : 1);
|
|
|
|
|
|
|
|
|
|
switch (power) {
|
|
|
|
|
case 1:
|
|
|
|
|
break;
|
|
|
|
|
case 2:
|
|
|
|
|
setting = setting * setting * sign;
|
|
|
|
|
break;
|
|
|
|
|
case 3:
|
|
|
|
|
setting = setting * setting * setting;
|
|
|
|
|
break;
|
|
|
|
|
case 4:
|
|
|
|
|
setting = setting * setting * setting * setting * sign;
|
|
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
setting = pow(setting, power);
|
|
|
|
|
if ((power % 2) == 0)
|
|
|
|
|
setting *= sign;
|
|
|
|
|
break;
|
|
|
|
|
}
|
2002-02-19 23:54:47 +00:00
|
|
|
|
|
2002-10-25 22:19:51 +00:00
|
|
|
|
return prop->setDoubleValue((setting + offset) * factor);
|
2001-06-04 21:38:44 +00:00
|
|
|
|
}
|
|
|
|
|
|
2002-12-22 19:58:34 +00:00
|
|
|
|
|
2003-01-18 21:13:11 +00:00
|
|
|
|
/**
|
|
|
|
|
* Built-in command: cycle a property through a set of values.
|
|
|
|
|
*
|
|
|
|
|
* If the current value isn't in the list, the cycle will
|
|
|
|
|
* (re)start from the beginning.
|
|
|
|
|
*
|
|
|
|
|
* property: the name of the property to cycle.
|
|
|
|
|
* value[*]: the list of values to cycle through.
|
|
|
|
|
*/
|
|
|
|
|
static bool
|
|
|
|
|
do_property_cycle (const SGPropertyNode * arg)
|
|
|
|
|
{
|
|
|
|
|
SGPropertyNode * prop = get_prop(arg);
|
|
|
|
|
vector<SGPropertyNode_ptr> values = arg->getChildren("value");
|
|
|
|
|
int selection = -1;
|
|
|
|
|
int nSelections = values.size();
|
|
|
|
|
|
|
|
|
|
if (nSelections < 1) {
|
|
|
|
|
SG_LOG(SG_GENERAL, SG_ALERT, "No values for property-cycle");
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Try to find the current selection
|
|
|
|
|
for (int i = 0; i < nSelections; i++) {
|
|
|
|
|
if (compare_values(prop, values[i])) {
|
|
|
|
|
selection = i + 1;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Default or wrap to the first selection
|
|
|
|
|
if (selection < 0 || selection >= nSelections)
|
|
|
|
|
selection = 0;
|
|
|
|
|
|
|
|
|
|
prop->setUnspecifiedValue(values[selection]->getStringValue());
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2003-04-04 22:38:35 +00:00
|
|
|
|
/**
|
|
|
|
|
* Built-in command: randomize a numeric property value.
|
|
|
|
|
*
|
|
|
|
|
* property: the name of the property value to randomize.
|
|
|
|
|
* min: the minimum allowed value.
|
|
|
|
|
* max: the maximum allowed value.
|
|
|
|
|
*/
|
|
|
|
|
static bool
|
|
|
|
|
do_property_randomize (const SGPropertyNode * arg)
|
|
|
|
|
{
|
|
|
|
|
SGPropertyNode * prop = get_prop(arg);
|
|
|
|
|
double min = arg->getDoubleValue("min", DBL_MIN);
|
|
|
|
|
double max = arg->getDoubleValue("max", DBL_MAX);
|
|
|
|
|
prop->setDoubleValue(sg_random() * (max - min) + min);
|
2003-04-05 00:30:38 +00:00
|
|
|
|
return true;
|
2003-04-04 22:38:35 +00:00
|
|
|
|
}
|
|
|
|
|
|
2012-02-02 21:17:34 +00:00
|
|
|
|
/**
|
|
|
|
|
* Built-in command: interpolate a property value over time
|
|
|
|
|
*
|
|
|
|
|
* property: the name of the property value to interpolate.
|
|
|
|
|
* value[0..n] any number of constant values to interpolate
|
|
|
|
|
* time[0..n] time between each value, number of time elements must
|
|
|
|
|
* match those of value elements
|
|
|
|
|
* -or-
|
|
|
|
|
* property[1..n] any number of target values taken from named properties
|
|
|
|
|
* time[0..n] time between each value, number of time elements must
|
|
|
|
|
* match those of property elements minus one
|
|
|
|
|
*/
|
|
|
|
|
static bool
|
|
|
|
|
do_property_interpolate (const SGPropertyNode * arg)
|
|
|
|
|
{
|
|
|
|
|
SGPropertyNode * prop = get_prop(arg);
|
|
|
|
|
|
|
|
|
|
simgear::PropertyList valueNodes = arg->getChildren( "value" );
|
|
|
|
|
simgear::PropertyList timeNodes = arg->getChildren( "time" );
|
|
|
|
|
|
|
|
|
|
boost::scoped_array<double> value;
|
|
|
|
|
boost::scoped_array<double> time;
|
|
|
|
|
|
|
|
|
|
if( valueNodes.size() > 0 ) {
|
|
|
|
|
// must match
|
|
|
|
|
if( timeNodes.size() != valueNodes.size() )
|
|
|
|
|
return false;
|
|
|
|
|
|
|
|
|
|
value.reset( new double[valueNodes.size()] );
|
|
|
|
|
for( simgear::PropertyList::size_type n = 0; n < valueNodes.size(); n++ ) {
|
|
|
|
|
value[n] = valueNodes[n]->getDoubleValue();
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
valueNodes = arg->getChildren("property");
|
|
|
|
|
// must have one more property node
|
|
|
|
|
if( valueNodes.size() - 1 != timeNodes.size() )
|
|
|
|
|
return false;
|
|
|
|
|
|
|
|
|
|
value.reset( new double[valueNodes.size()-1] );
|
|
|
|
|
for( simgear::PropertyList::size_type n = 0; n < valueNodes.size()-1; n++ ) {
|
|
|
|
|
value[n] = fgGetNode(valueNodes[n+1]->getStringValue(), "/null")->getDoubleValue();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
time.reset( new double[timeNodes.size()] );
|
|
|
|
|
for( simgear::PropertyList::size_type n = 0; n < timeNodes.size(); n++ ) {
|
|
|
|
|
time[n] = timeNodes[n]->getDoubleValue();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
((SGInterpolator*)globals->get_subsystem_mgr()
|
|
|
|
|
->get_group(SGSubsystemMgr::INIT)->get_subsystem("interpolator"))
|
|
|
|
|
->interpolate(prop, timeNodes.size(), value.get(), time.get() );
|
|
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
}
|
2003-04-04 22:38:35 +00:00
|
|
|
|
|
2003-11-24 01:49:04 +00:00
|
|
|
|
/**
|
2003-11-25 21:08:36 +00:00
|
|
|
|
* Built-in command: reinit the data logging system based on the
|
|
|
|
|
* current contents of the /logger tree.
|
2003-11-24 01:49:04 +00:00
|
|
|
|
*/
|
|
|
|
|
static bool
|
|
|
|
|
do_data_logging_commit (const SGPropertyNode * arg)
|
|
|
|
|
{
|
|
|
|
|
FGLogger *log = (FGLogger *)globals->get_subsystem("logger");
|
|
|
|
|
log->reinit();
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
2003-12-08 02:05:10 +00:00
|
|
|
|
/**
|
|
|
|
|
* 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;
|
|
|
|
|
}
|
2003-11-24 01:49:04 +00:00
|
|
|
|
|
2002-12-22 19:58:34 +00:00
|
|
|
|
/**
|
|
|
|
|
* Built-in command: Show an XML-configured dialog.
|
|
|
|
|
*
|
|
|
|
|
* dialog-name: the name of the GUI dialog to display.
|
|
|
|
|
*/
|
2002-11-07 16:27:47 +00:00
|
|
|
|
static bool
|
2002-12-22 19:58:34 +00:00
|
|
|
|
do_dialog_show (const SGPropertyNode * arg)
|
2002-11-07 16:27:47 +00:00
|
|
|
|
{
|
2003-01-20 16:03:48 +00:00
|
|
|
|
NewGUI * gui = (NewGUI *)globals->get_subsystem("gui");
|
|
|
|
|
gui->showDialog(arg->getStringValue("dialog-name"));
|
2002-11-07 16:27:47 +00:00
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
2001-06-04 21:38:44 +00:00
|
|
|
|
|
2002-12-22 19:58:34 +00:00
|
|
|
|
/**
|
2003-01-04 18:47:25 +00:00
|
|
|
|
* Built-in Command: Hide the active XML-configured dialog.
|
2002-12-22 19:58:34 +00:00
|
|
|
|
*/
|
|
|
|
|
static bool
|
|
|
|
|
do_dialog_close (const SGPropertyNode * arg)
|
|
|
|
|
{
|
2003-01-20 16:03:48 +00:00
|
|
|
|
NewGUI * gui = (NewGUI *)globals->get_subsystem("gui");
|
2003-12-08 02:05:10 +00:00
|
|
|
|
if(arg->hasValue("dialog-name"))
|
|
|
|
|
return gui->closeDialog(arg->getStringValue("dialog-name"));
|
2003-03-23 09:59:46 +00:00
|
|
|
|
return gui->closeActiveDialog();
|
2002-12-22 19:58:34 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Update a value in the active XML-configured dialog.
|
|
|
|
|
*
|
|
|
|
|
* object-name: The name of the GUI object(s) (all GUI objects if omitted).
|
|
|
|
|
*/
|
|
|
|
|
static bool
|
|
|
|
|
do_dialog_update (const SGPropertyNode * arg)
|
|
|
|
|
{
|
2003-01-20 16:03:48 +00:00
|
|
|
|
NewGUI * gui = (NewGUI *)globals->get_subsystem("gui");
|
2006-04-28 10:27:54 +00:00
|
|
|
|
FGDialog * dialog;
|
|
|
|
|
if (arg->hasValue("dialog-name"))
|
|
|
|
|
dialog = gui->getDialog(arg->getStringValue("dialog-name"));
|
|
|
|
|
else
|
|
|
|
|
dialog = gui->getActiveDialog();
|
|
|
|
|
|
2003-01-20 16:03:48 +00:00
|
|
|
|
if (dialog != 0) {
|
2006-04-28 11:49:11 +00:00
|
|
|
|
dialog->updateValues(arg->getStringValue("object-name"));
|
2002-12-22 19:58:34 +00:00
|
|
|
|
return true;
|
|
|
|
|
} else {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2011-10-09 10:37:43 +00:00
|
|
|
|
static bool
|
|
|
|
|
do_open_browser (const SGPropertyNode * arg)
|
|
|
|
|
{
|
|
|
|
|
string path;
|
|
|
|
|
if (arg->hasValue("path"))
|
|
|
|
|
path = arg->getStringValue("path");
|
|
|
|
|
else
|
|
|
|
|
if (arg->hasValue("url"))
|
|
|
|
|
path = arg->getStringValue("url");
|
|
|
|
|
else
|
|
|
|
|
return false;
|
|
|
|
|
|
|
|
|
|
return openBrowser(path);
|
|
|
|
|
}
|
2002-12-22 19:58:34 +00:00
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Apply a value in the active XML-configured dialog.
|
|
|
|
|
*
|
|
|
|
|
* object-name: The name of the GUI object(s) (all GUI objects if omitted).
|
|
|
|
|
*/
|
|
|
|
|
static bool
|
|
|
|
|
do_dialog_apply (const SGPropertyNode * arg)
|
|
|
|
|
{
|
2003-01-20 16:03:48 +00:00
|
|
|
|
NewGUI * gui = (NewGUI *)globals->get_subsystem("gui");
|
2006-04-28 10:27:54 +00:00
|
|
|
|
FGDialog * dialog;
|
|
|
|
|
if (arg->hasValue("dialog-name"))
|
|
|
|
|
dialog = gui->getDialog(arg->getStringValue("dialog-name"));
|
|
|
|
|
else
|
|
|
|
|
dialog = gui->getActiveDialog();
|
|
|
|
|
|
2003-01-20 16:03:48 +00:00
|
|
|
|
if (dialog != 0) {
|
2006-04-28 11:49:11 +00:00
|
|
|
|
dialog->applyValues(arg->getStringValue("object-name"));
|
2002-12-22 19:58:34 +00:00
|
|
|
|
return true;
|
|
|
|
|
} else {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2005-11-09 17:16:59 +00:00
|
|
|
|
/**
|
|
|
|
|
* Redraw GUI (applying new widget colors). Doesn't reload the dialogs,
|
|
|
|
|
* unlike reinit().
|
|
|
|
|
*/
|
|
|
|
|
static bool
|
|
|
|
|
do_gui_redraw (const SGPropertyNode * arg)
|
|
|
|
|
{
|
|
|
|
|
NewGUI * gui = (NewGUI *)globals->get_subsystem("gui");
|
|
|
|
|
gui->redraw();
|
2005-11-11 07:17:26 +00:00
|
|
|
|
return true;
|
2005-11-09 17:16:59 +00:00
|
|
|
|
}
|
|
|
|
|
|
2008-05-07 21:49:35 +00:00
|
|
|
|
|
add "add-model" command, which adds model properties to the first
free /models/model[*] slot and lets the model manager load the
model. The target address is returned under "property". Simple
use in Nasal:
fgcommand("add-model", props.Node.new({
"path": "Models/Fauna/cow.ac",
"latitude-deg": 16.234,
"longitude-deg": 48.321,
"elevation-ft": 300,
});
An "elevation-m" property can alternatively be used and overrides
the "elevation-ft" property.
Each of the properties "latitude-deg", "longitude-deg", "elevation-ft",
"heading-deg", "pitch-deg", and "roll-deg" can alternatively be used
with "-prop" suffix. Their values must then be property path strings
pointing to a node that can be changed at runtime to move the model.
2008-11-19 15:59:54 +00:00
|
|
|
|
/**
|
|
|
|
|
* Adds model to the scenery. The path to the added branch (/models/model[*])
|
|
|
|
|
* is returned in property "property".
|
|
|
|
|
*/
|
|
|
|
|
static bool
|
|
|
|
|
do_add_model (const SGPropertyNode * arg)
|
|
|
|
|
{
|
|
|
|
|
SGPropertyNode * model = fgGetNode("models", true);
|
2011-07-10 19:25:16 +00:00
|
|
|
|
int i;
|
|
|
|
|
for (i = 0; model->hasChild("model",i); i++);
|
|
|
|
|
model = model->getChild("model", i, true);
|
add "add-model" command, which adds model properties to the first
free /models/model[*] slot and lets the model manager load the
model. The target address is returned under "property". Simple
use in Nasal:
fgcommand("add-model", props.Node.new({
"path": "Models/Fauna/cow.ac",
"latitude-deg": 16.234,
"longitude-deg": 48.321,
"elevation-ft": 300,
});
An "elevation-m" property can alternatively be used and overrides
the "elevation-ft" property.
Each of the properties "latitude-deg", "longitude-deg", "elevation-ft",
"heading-deg", "pitch-deg", and "roll-deg" can alternatively be used
with "-prop" suffix. Their values must then be property path strings
pointing to a node that can be changed at runtime to move the model.
2008-11-19 15:59:54 +00:00
|
|
|
|
copyProperties(arg, model);
|
|
|
|
|
if (model->hasValue("elevation-m"))
|
|
|
|
|
model->setDoubleValue("elevation-ft", model->getDoubleValue("elevation-m")
|
|
|
|
|
* SG_METER_TO_FEET);
|
|
|
|
|
model->getNode("load", true);
|
|
|
|
|
model->removeChildren("load");
|
|
|
|
|
const_cast<SGPropertyNode *>(arg)->setStringValue("property", model->getPath());
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2008-05-07 21:49:35 +00:00
|
|
|
|
/**
|
2008-05-08 10:07:51 +00:00
|
|
|
|
* Set mouse cursor coordinates and cursor shape.
|
2008-05-07 21:49:35 +00:00
|
|
|
|
*/
|
|
|
|
|
static bool
|
2008-05-08 10:07:51 +00:00
|
|
|
|
do_set_cursor (const SGPropertyNode * arg)
|
2008-05-07 21:49:35 +00:00
|
|
|
|
{
|
|
|
|
|
if (arg->hasValue("x") || arg->hasValue("y")) {
|
2008-05-08 10:07:51 +00:00
|
|
|
|
SGPropertyNode *mx = fgGetNode("/devices/status/mice/mouse/x", true);
|
|
|
|
|
SGPropertyNode *my = fgGetNode("/devices/status/mice/mouse/y", true);
|
|
|
|
|
int x = arg->getIntValue("x", mx->getIntValue());
|
|
|
|
|
int y = arg->getIntValue("y", my->getIntValue());
|
2008-05-07 21:49:35 +00:00
|
|
|
|
fgWarpMouse(x, y);
|
2008-05-08 10:07:51 +00:00
|
|
|
|
mx->setIntValue(x);
|
|
|
|
|
my->setIntValue(y);
|
2008-05-07 21:49:35 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
SGPropertyNode *cursor = const_cast<SGPropertyNode *>(arg)->getNode("cursor", true);
|
2009-07-15 23:15:17 +00:00
|
|
|
|
if (cursor->getType() != simgear::props::NONE)
|
2008-05-07 21:49:35 +00:00
|
|
|
|
fgSetMouseCursor(cursor->getIntValue());
|
|
|
|
|
|
|
|
|
|
cursor->setIntValue(fgGetMouseCursor());
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2006-01-09 02:23:02 +00:00
|
|
|
|
/**
|
|
|
|
|
* Built-in command: play an audio message (i.e. a wav file) This is
|
|
|
|
|
* fire and forget. Call this once per message and it will get dumped
|
|
|
|
|
* into a queue. Messages are played sequentially so they do not
|
|
|
|
|
* overlap.
|
|
|
|
|
*/
|
|
|
|
|
static bool
|
2007-05-15 14:39:56 +00:00
|
|
|
|
do_play_audio_sample (const SGPropertyNode * arg)
|
2006-01-09 02:23:02 +00:00
|
|
|
|
{
|
|
|
|
|
string path = arg->getStringValue("path");
|
|
|
|
|
string file = arg->getStringValue("file");
|
2009-10-07 20:56:24 +00:00
|
|
|
|
float volume = arg->getFloatValue("volume");
|
2006-02-15 22:03:15 +00:00
|
|
|
|
// cout << "playing " << path << " / " << file << endl;
|
2007-05-15 14:39:56 +00:00
|
|
|
|
try {
|
2009-10-07 20:56:24 +00:00
|
|
|
|
static FGSampleQueue *queue = 0;
|
|
|
|
|
if ( !queue ) {
|
2009-10-24 08:31:37 +00:00
|
|
|
|
SGSoundMgr *smgr = globals->get_soundmgr();
|
2009-12-02 09:34:06 +00:00
|
|
|
|
queue = new FGSampleQueue(smgr, "chatter");
|
2009-10-07 20:56:24 +00:00
|
|
|
|
queue->tie_to_listener();
|
2009-10-04 13:52:53 +00:00
|
|
|
|
}
|
2009-10-07 20:56:24 +00:00
|
|
|
|
|
2010-09-06 08:28:28 +00:00
|
|
|
|
SGSoundSample *msg = new SGSoundSample(file.c_str(), path);
|
2009-10-07 20:56:24 +00:00
|
|
|
|
msg->set_volume( volume );
|
|
|
|
|
queue->add( msg );
|
|
|
|
|
|
2007-05-15 14:39:56 +00:00
|
|
|
|
return true;
|
2006-01-09 02:23:02 +00:00
|
|
|
|
|
2009-06-14 11:00:01 +00:00
|
|
|
|
} catch (const sg_io_exception&) {
|
2007-05-15 14:39:56 +00:00
|
|
|
|
SG_LOG(SG_GENERAL, SG_ALERT, "play-audio-sample: "
|
|
|
|
|
"failed to load" << path << '/' << file);
|
|
|
|
|
return false;
|
|
|
|
|
}
|
2006-01-09 02:23:02 +00:00
|
|
|
|
}
|
|
|
|
|
|
2002-11-30 20:10:16 +00:00
|
|
|
|
/**
|
|
|
|
|
* Built-in command: commit presets (read from in /sim/presets/)
|
|
|
|
|
*/
|
|
|
|
|
static bool
|
|
|
|
|
do_presets_commit (const SGPropertyNode * arg)
|
|
|
|
|
{
|
2010-11-02 23:19:09 +00:00
|
|
|
|
if (fgGetBool("/sim/initialized", false)) {
|
|
|
|
|
fgReInitSubsystems();
|
|
|
|
|
} else {
|
|
|
|
|
// Nasal can trigger this during initial init, which confuses
|
|
|
|
|
// the logic in ReInitSubsystems, since initial state has not been
|
|
|
|
|
// saved at that time. Short-circuit everything here.
|
|
|
|
|
fgInitPosition();
|
|
|
|
|
}
|
|
|
|
|
|
2002-11-30 20:10:16 +00:00
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
2003-03-22 10:38:15 +00:00
|
|
|
|
/**
|
|
|
|
|
* Built-in command: set log level (0 ... 7)
|
|
|
|
|
*/
|
|
|
|
|
static bool
|
|
|
|
|
do_log_level (const SGPropertyNode * arg)
|
|
|
|
|
{
|
|
|
|
|
sglog().setLogLevels( SG_ALL, (sgDebugPriority)arg->getIntValue() );
|
|
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
2011-01-19 18:36:04 +00:00
|
|
|
|
/*
|
2003-08-06 17:13:42 +00:00
|
|
|
|
static bool
|
2003-08-09 13:31:44 +00:00
|
|
|
|
do_decrease_visibility (const SGPropertyNode * arg)
|
2003-08-06 17:13:42 +00:00
|
|
|
|
{
|
2011-01-19 18:36:04 +00:00
|
|
|
|
Environment::Presets::VisibilitySingleton::instance()->adjust( 0.9 );
|
2003-08-09 13:31:44 +00:00
|
|
|
|
return true;
|
2003-08-06 17:13:42 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static bool
|
2003-08-09 13:31:44 +00:00
|
|
|
|
do_increase_visibility (const SGPropertyNode * arg)
|
2003-08-06 17:13:42 +00:00
|
|
|
|
{
|
2011-01-19 18:36:04 +00:00
|
|
|
|
Environment::Presets::VisibilitySingleton::instance()->adjust( 1.1 );
|
2003-08-09 13:31:44 +00:00
|
|
|
|
return true;
|
2003-08-06 17:13:42 +00:00
|
|
|
|
}
|
2011-01-19 18:36:04 +00:00
|
|
|
|
*/
|
2004-10-24 09:13:17 +00:00
|
|
|
|
/**
|
|
|
|
|
* An fgcommand to allow loading of xml files via nasal,
|
|
|
|
|
* the xml file's structure will be made available within
|
2007-06-04 10:07:31 +00:00
|
|
|
|
* a property tree node defined under argument "targetnode",
|
|
|
|
|
* or in the given argument tree under "data" otherwise.
|
2004-10-24 09:13:17 +00:00
|
|
|
|
*
|
2007-06-12 16:13:21 +00:00
|
|
|
|
* @param filename a string to hold the complete path & filename of an XML file
|
2004-10-24 09:13:17 +00:00
|
|
|
|
* @param targetnode a string pointing to a location within the property tree
|
2007-06-12 16:13:21 +00:00
|
|
|
|
* where to store the parsed XML file. If <targetnode> is undefined, then the
|
|
|
|
|
* file contents are stored under a node <data> in the argument tree.
|
2004-10-24 09:13:17 +00:00
|
|
|
|
*/
|
|
|
|
|
|
2007-06-04 10:07:31 +00:00
|
|
|
|
static bool
|
|
|
|
|
do_load_xml_to_proptree(const SGPropertyNode * arg)
|
2004-10-24 09:13:17 +00:00
|
|
|
|
{
|
2007-06-12 16:13:21 +00:00
|
|
|
|
SGPath file(arg->getStringValue("filename"));
|
|
|
|
|
if (file.str().empty())
|
|
|
|
|
return false;
|
|
|
|
|
|
|
|
|
|
if (file.extension() != "xml")
|
|
|
|
|
file.concat(".xml");
|
2011-05-24 23:03:51 +00:00
|
|
|
|
|
|
|
|
|
std::string icao = arg->getStringValue("icao");
|
|
|
|
|
if (icao.empty()) {
|
|
|
|
|
if (file.isRelative()) {
|
2011-07-03 11:06:41 +00:00
|
|
|
|
SGPath absPath = globals->resolve_maybe_aircraft_path(file.str());
|
|
|
|
|
if (!absPath.isNull())
|
|
|
|
|
file = absPath;
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
SG_LOG(SG_IO, SG_ALERT, "loadxml: Cannot find XML property file '"
|
|
|
|
|
<< file.str() << "'.");
|
|
|
|
|
return false;
|
|
|
|
|
}
|
2011-05-24 23:03:51 +00:00
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
if (!XMLLoader::findAirportData(icao, file.str(), file)) {
|
|
|
|
|
SG_LOG(SG_IO, SG_INFO, "loadxml: failed to find airport data for "
|
|
|
|
|
<< file.str() << " at ICAO:" << icao);
|
|
|
|
|
return false;
|
|
|
|
|
}
|
2010-08-15 10:02:15 +00:00
|
|
|
|
}
|
2011-05-24 23:03:51 +00:00
|
|
|
|
|
2008-07-11 16:36:54 +00:00
|
|
|
|
if (!fgValidatePath(file.c_str(), false)) {
|
|
|
|
|
SG_LOG(SG_IO, SG_ALERT, "loadxml: reading '" << file.str() << "' denied "
|
|
|
|
|
"(unauthorized access)");
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2007-06-04 10:07:31 +00:00
|
|
|
|
SGPropertyNode *targetnode;
|
2007-06-12 16:13:21 +00:00
|
|
|
|
if (arg->hasValue("targetnode"))
|
|
|
|
|
targetnode = fgGetNode(arg->getStringValue("targetnode"), true);
|
2007-06-04 10:07:31 +00:00
|
|
|
|
else
|
2007-06-12 16:13:21 +00:00
|
|
|
|
targetnode = const_cast<SGPropertyNode *>(arg)->getNode("data", true);
|
2004-10-24 09:13:17 +00:00
|
|
|
|
|
|
|
|
|
try {
|
2007-06-12 16:13:21 +00:00
|
|
|
|
readProperties(file.c_str(), targetnode, true);
|
2004-10-24 09:13:17 +00:00
|
|
|
|
} catch (const sg_exception &e) {
|
2007-06-19 10:56:50 +00:00
|
|
|
|
SG_LOG(SG_IO, SG_WARN, "loadxml: " << e.getFormattedMessage());
|
2004-10-24 09:13:17 +00:00
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2008-07-11 16:36:54 +00:00
|
|
|
|
return true;
|
2004-10-24 09:13:17 +00:00
|
|
|
|
}
|
|
|
|
|
|
2012-04-18 09:25:27 +00:00
|
|
|
|
class RemoteXMLRequest : public simgear::HTTP::Request
|
|
|
|
|
{
|
|
|
|
|
public:
|
|
|
|
|
SGPropertyNode_ptr _complete;
|
|
|
|
|
SGPropertyNode_ptr _status;
|
|
|
|
|
SGPropertyNode_ptr _failed;
|
|
|
|
|
SGPropertyNode_ptr _target;
|
|
|
|
|
string propsData;
|
|
|
|
|
|
|
|
|
|
RemoteXMLRequest(const std::string& url, SGPropertyNode* targetNode) :
|
|
|
|
|
simgear::HTTP::Request(url),
|
|
|
|
|
_target(targetNode)
|
|
|
|
|
{
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void setCompletionProp(SGPropertyNode_ptr p)
|
|
|
|
|
{
|
|
|
|
|
_complete = p;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void setStatusProp(SGPropertyNode_ptr p)
|
|
|
|
|
{
|
|
|
|
|
_status = p;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void setFailedProp(SGPropertyNode_ptr p)
|
|
|
|
|
{
|
|
|
|
|
_failed = p;
|
|
|
|
|
}
|
|
|
|
|
protected:
|
|
|
|
|
virtual void gotBodyData(const char* s, int n)
|
|
|
|
|
{
|
|
|
|
|
propsData += string(s, n);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
virtual void responseComplete()
|
|
|
|
|
{
|
|
|
|
|
int response = responseCode();
|
|
|
|
|
bool failed = false;
|
|
|
|
|
if (response == 200) {
|
|
|
|
|
try {
|
|
|
|
|
const char* buffer = propsData.c_str();
|
|
|
|
|
readProperties(buffer, propsData.size(), _target, true);
|
|
|
|
|
} catch (const sg_exception &e) {
|
|
|
|
|
SG_LOG(SG_IO, SG_WARN, "parsing XML from remote, failed: " << e.getFormattedMessage());
|
|
|
|
|
failed = true;
|
|
|
|
|
response = 406; // 'not acceptable', anything better?
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
failed = true;
|
|
|
|
|
}
|
|
|
|
|
// now the response data is output, signal Nasal / listeners
|
|
|
|
|
if (_complete) _complete->setBoolValue(true);
|
|
|
|
|
if (_status) _status->setIntValue(response);
|
|
|
|
|
if (_failed) _failed->setBoolValue(failed);
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static bool
|
|
|
|
|
do_load_xml_from_url(const SGPropertyNode * arg)
|
|
|
|
|
{
|
|
|
|
|
std::string url(arg->getStringValue("url"));
|
|
|
|
|
if (url.empty())
|
|
|
|
|
return false;
|
|
|
|
|
|
|
|
|
|
SGPropertyNode *targetnode;
|
|
|
|
|
if (arg->hasValue("targetnode"))
|
|
|
|
|
targetnode = fgGetNode(arg->getStringValue("targetnode"), true);
|
|
|
|
|
else
|
|
|
|
|
targetnode = const_cast<SGPropertyNode *>(arg)->getNode("data", true);
|
|
|
|
|
|
|
|
|
|
RemoteXMLRequest* req = new RemoteXMLRequest(url, targetnode);
|
|
|
|
|
|
|
|
|
|
// connect up optional reporting properties
|
|
|
|
|
if (arg->hasValue("complete"))
|
|
|
|
|
req->setCompletionProp(fgGetNode(arg->getStringValue("complete"), true));
|
|
|
|
|
if (arg->hasValue("failure"))
|
|
|
|
|
req->setFailedProp(fgGetNode(arg->getStringValue("failure"), true));
|
|
|
|
|
if (arg->hasValue("status"))
|
|
|
|
|
req->setStatusProp(fgGetNode(arg->getStringValue("status"), true));
|
|
|
|
|
|
|
|
|
|
FGHTTPClient::instance()->makeRequest(req);
|
|
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
2004-10-24 09:13:17 +00:00
|
|
|
|
|
|
|
|
|
/**
|
2007-06-12 16:13:21 +00:00
|
|
|
|
* An fgcommand to allow saving of xml files via nasal,
|
2004-10-24 09:13:17 +00:00
|
|
|
|
* the file's structure will be determined based on what's
|
|
|
|
|
* encountered in the passed (source) property tree node
|
|
|
|
|
*
|
|
|
|
|
* @param filename a string to hold the complete path & filename of the (new)
|
|
|
|
|
* XML file
|
|
|
|
|
* @param sourcenode a string pointing to a location within the property tree
|
|
|
|
|
* where to find the nodes that should be written recursively into an XML file
|
2007-06-06 21:18:25 +00:00
|
|
|
|
* @param data if no sourcenode is given, then the file contents are taken from
|
|
|
|
|
* the argument tree's "data" node.
|
2004-10-24 09:13:17 +00:00
|
|
|
|
*/
|
|
|
|
|
|
2007-06-12 16:13:21 +00:00
|
|
|
|
static bool
|
|
|
|
|
do_save_xml_from_proptree(const SGPropertyNode * arg)
|
2004-10-24 09:13:17 +00:00
|
|
|
|
{
|
2007-06-12 16:13:21 +00:00
|
|
|
|
SGPath file(arg->getStringValue("filename"));
|
|
|
|
|
if (file.str().empty())
|
2007-06-06 21:18:25 +00:00
|
|
|
|
return false;
|
|
|
|
|
|
2007-06-12 16:13:21 +00:00
|
|
|
|
if (file.extension() != "xml")
|
|
|
|
|
file.concat(".xml");
|
|
|
|
|
|
2008-07-11 16:36:54 +00:00
|
|
|
|
if (!fgValidatePath(file.c_str(), true)) {
|
|
|
|
|
SG_LOG(SG_IO, SG_ALERT, "savexml: writing to '" << file.str() << "' denied "
|
|
|
|
|
"(unauthorized access)");
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2007-06-12 16:13:21 +00:00
|
|
|
|
SGPropertyNode *sourcenode;
|
|
|
|
|
if (arg->hasValue("sourcenode"))
|
|
|
|
|
sourcenode = fgGetNode(arg->getStringValue("sourcenode"), true);
|
|
|
|
|
else if (arg->getNode("data", false))
|
|
|
|
|
sourcenode = const_cast<SGPropertyNode *>(arg)->getNode("data");
|
|
|
|
|
else
|
2007-06-06 21:18:25 +00:00
|
|
|
|
return false;
|
2004-10-24 09:13:17 +00:00
|
|
|
|
|
|
|
|
|
try {
|
2007-06-12 16:13:21 +00:00
|
|
|
|
writeProperties (file.c_str(), sourcenode, true);
|
2004-10-24 09:13:17 +00:00
|
|
|
|
} catch (const sg_exception &e) {
|
2007-06-19 10:56:50 +00:00
|
|
|
|
SG_LOG(SG_IO, SG_WARN, "savexml: " << e.getFormattedMessage());
|
2004-10-24 09:13:17 +00:00
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
2006-03-04 20:21:32 +00:00
|
|
|
|
static bool
|
|
|
|
|
do_press_cockpit_button (const SGPropertyNode *arg)
|
|
|
|
|
{
|
|
|
|
|
const char *prefix = arg->getStringValue("prefix");
|
|
|
|
|
|
|
|
|
|
if (arg->getBoolValue("guarded") && fgGetDouble((string(prefix) + "-guard").c_str()) < 1)
|
|
|
|
|
return true;
|
|
|
|
|
|
|
|
|
|
string prop = string(prefix) + "-button";
|
|
|
|
|
double value;
|
|
|
|
|
|
|
|
|
|
if (arg->getBoolValue("latching"))
|
|
|
|
|
value = fgGetDouble(prop.c_str()) > 0 ? 0 : 1;
|
|
|
|
|
else
|
|
|
|
|
value = 1;
|
|
|
|
|
|
|
|
|
|
fgSetDouble(prop.c_str(), value);
|
|
|
|
|
fgSetBool(arg->getStringValue("discrete"), value > 0);
|
|
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static bool
|
|
|
|
|
do_release_cockpit_button (const SGPropertyNode *arg)
|
|
|
|
|
{
|
|
|
|
|
const char *prefix = arg->getStringValue("prefix");
|
|
|
|
|
|
|
|
|
|
if (arg->getBoolValue("guarded")) {
|
|
|
|
|
string prop = string(prefix) + "-guard";
|
|
|
|
|
if (fgGetDouble(prop.c_str()) < 1) {
|
|
|
|
|
fgSetDouble(prop.c_str(), 1);
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (! arg->getBoolValue("latching")) {
|
|
|
|
|
fgSetDouble((string(prefix) + "-button").c_str(), 0);
|
|
|
|
|
fgSetBool(arg->getStringValue("discrete"), false);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
2011-06-01 22:55:04 +00:00
|
|
|
|
static SGGeod commandSearchPos(const SGPropertyNode* arg)
|
|
|
|
|
{
|
|
|
|
|
if (arg->hasChild("longitude-deg") && arg->hasChild("latitude-deg")) {
|
|
|
|
|
return SGGeod::fromDeg(arg->getDoubleValue("longitude-deg"),
|
|
|
|
|
arg->getDoubleValue("latitude-deg"));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// use current viewer/aircraft position
|
|
|
|
|
return SGGeod::fromDeg(fgGetDouble("/position/longitude-deg"),
|
|
|
|
|
fgGetDouble("/position/latitude-deg"));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static bool
|
|
|
|
|
do_comm_search(const SGPropertyNode* arg)
|
|
|
|
|
{
|
|
|
|
|
SGGeod pos = commandSearchPos(arg);
|
|
|
|
|
int khz = static_cast<int>(arg->getDoubleValue("frequency-mhz") * 100.0 + 0.25);
|
|
|
|
|
|
|
|
|
|
flightgear::CommStation* sta = flightgear::CommStation::findByFreq(khz, pos, NULL);
|
|
|
|
|
if (!sta) {
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
SGPropertyNode* result = fgGetNode(arg->getStringValue("result"));
|
|
|
|
|
sta->createBinding(result);
|
|
|
|
|
return true;
|
|
|
|
|
}
|
2005-05-04 21:25:57 +00:00
|
|
|
|
|
2011-06-01 22:55:04 +00:00
|
|
|
|
static bool
|
|
|
|
|
do_nav_search(const SGPropertyNode* arg)
|
|
|
|
|
{
|
|
|
|
|
SGGeod pos = commandSearchPos(arg);
|
|
|
|
|
double mhz = arg->getDoubleValue("frequency-mhz");
|
|
|
|
|
|
|
|
|
|
FGNavList* navList = globals->get_navlist();
|
|
|
|
|
string type(arg->getStringValue("type", "vor"));
|
|
|
|
|
if (type == "dme") {
|
|
|
|
|
navList = globals->get_dmelist();
|
|
|
|
|
} else if (type == "tacan") {
|
|
|
|
|
navList = globals->get_tacanlist();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
FGNavRecord* nav = navList->findByFreq(mhz, pos);
|
|
|
|
|
if (!nav && (type == "vor")) {
|
|
|
|
|
// if we're searching VORs, look for localizers too
|
|
|
|
|
nav = globals->get_loclist()->findByFreq(mhz, pos);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!nav) {
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
SGPropertyNode* result = fgGetNode(arg->getStringValue("result"));
|
|
|
|
|
nav->createBinding(result);
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
2001-07-09 16:17:20 +00:00
|
|
|
|
////////////////////////////////////////////////////////////////////////
|
|
|
|
|
// Command setup.
|
|
|
|
|
////////////////////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
|
|
|
2001-06-01 17:52:17 +00:00
|
|
|
|
/**
|
|
|
|
|
* Table of built-in commands.
|
|
|
|
|
*
|
|
|
|
|
* New commands do not have to be added here; any module in the application
|
|
|
|
|
* can add a new command using globals->get_commands()->addCommand(...).
|
|
|
|
|
*/
|
|
|
|
|
static struct {
|
|
|
|
|
const char * name;
|
|
|
|
|
SGCommandMgr::command_t command;
|
|
|
|
|
} built_ins [] = {
|
2001-06-14 22:18:01 +00:00
|
|
|
|
{ "null", do_null },
|
2003-11-25 21:08:36 +00:00
|
|
|
|
{ "nasal", do_nasal },
|
2001-06-14 22:18:01 +00:00
|
|
|
|
{ "exit", do_exit },
|
2008-07-09 12:22:33 +00:00
|
|
|
|
{ "reset", do_reset },
|
2003-01-21 02:07:29 +00:00
|
|
|
|
{ "reinit", do_reinit },
|
|
|
|
|
{ "suspend", do_reinit },
|
|
|
|
|
{ "resume", do_reinit },
|
2011-05-21 11:27:09 +00:00
|
|
|
|
{ "pause", do_pause },
|
2001-06-14 22:18:01 +00:00
|
|
|
|
{ "load", do_load },
|
|
|
|
|
{ "save", do_save },
|
|
|
|
|
{ "panel-load", do_panel_load },
|
2002-03-23 23:16:13 +00:00
|
|
|
|
{ "panel-mouse-click", do_panel_mouse_click },
|
2001-06-14 22:18:01 +00:00
|
|
|
|
{ "preferences-load", do_preferences_load },
|
|
|
|
|
{ "view-cycle", do_view_cycle },
|
|
|
|
|
{ "screen-capture", do_screen_capture },
|
2004-09-27 02:39:54 +00:00
|
|
|
|
{ "hires-screen-capture", do_hires_screen_capture },
|
2001-06-26 21:59:49 +00:00
|
|
|
|
{ "tile-cache-reload", do_tile_cache_reload },
|
2011-01-19 18:36:04 +00:00
|
|
|
|
/*
|
2004-01-31 19:47:45 +00:00
|
|
|
|
{ "set-sea-level-air-temp-degc", do_set_sea_level_degc },
|
2003-11-10 21:59:35 +00:00
|
|
|
|
{ "set-outside-air-temp-degc", do_set_oat_degc },
|
2004-02-22 14:21:37 +00:00
|
|
|
|
{ "set-dewpoint-sea-level-air-temp-degc", do_set_dewpoint_sea_level_degc },
|
|
|
|
|
{ "set-dewpoint-temp-degc", do_set_dewpoint_degc },
|
2011-01-19 18:36:04 +00:00
|
|
|
|
*/
|
2001-06-14 22:18:01 +00:00
|
|
|
|
{ "property-toggle", do_property_toggle },
|
|
|
|
|
{ "property-assign", do_property_assign },
|
|
|
|
|
{ "property-adjust", do_property_adjust },
|
2001-06-26 21:59:49 +00:00
|
|
|
|
{ "property-multiply", do_property_multiply },
|
2001-06-14 22:18:01 +00:00
|
|
|
|
{ "property-swap", do_property_swap },
|
|
|
|
|
{ "property-scale", do_property_scale },
|
2003-01-18 21:13:11 +00:00
|
|
|
|
{ "property-cycle", do_property_cycle },
|
2003-04-04 22:38:35 +00:00
|
|
|
|
{ "property-randomize", do_property_randomize },
|
2012-02-02 21:17:34 +00:00
|
|
|
|
{ "property-interpolate", do_property_interpolate },
|
2003-11-24 01:49:04 +00:00
|
|
|
|
{ "data-logging-commit", do_data_logging_commit },
|
2003-12-08 02:05:10 +00:00
|
|
|
|
{ "dialog-new", do_dialog_new },
|
2002-12-22 19:58:34 +00:00
|
|
|
|
{ "dialog-show", do_dialog_show },
|
|
|
|
|
{ "dialog-close", do_dialog_close },
|
|
|
|
|
{ "dialog-update", do_dialog_update },
|
|
|
|
|
{ "dialog-apply", do_dialog_apply },
|
2011-10-09 10:37:43 +00:00
|
|
|
|
{ "open-browser", do_open_browser },
|
2005-11-09 17:16:59 +00:00
|
|
|
|
{ "gui-redraw", do_gui_redraw },
|
add "add-model" command, which adds model properties to the first
free /models/model[*] slot and lets the model manager load the
model. The target address is returned under "property". Simple
use in Nasal:
fgcommand("add-model", props.Node.new({
"path": "Models/Fauna/cow.ac",
"latitude-deg": 16.234,
"longitude-deg": 48.321,
"elevation-ft": 300,
});
An "elevation-m" property can alternatively be used and overrides
the "elevation-ft" property.
Each of the properties "latitude-deg", "longitude-deg", "elevation-ft",
"heading-deg", "pitch-deg", and "roll-deg" can alternatively be used
with "-prop" suffix. Their values must then be property path strings
pointing to a node that can be changed at runtime to move the model.
2008-11-19 15:59:54 +00:00
|
|
|
|
{ "add-model", do_add_model },
|
2008-05-08 10:07:51 +00:00
|
|
|
|
{ "set-cursor", do_set_cursor },
|
2007-05-15 14:39:56 +00:00
|
|
|
|
{ "play-audio-sample", do_play_audio_sample },
|
2003-01-05 00:10:36 +00:00
|
|
|
|
{ "presets-commit", do_presets_commit },
|
2003-03-22 10:38:15 +00:00
|
|
|
|
{ "log-level", do_log_level },
|
2003-07-17 18:24:17 +00:00
|
|
|
|
{ "replay", do_replay },
|
2011-01-19 18:36:04 +00:00
|
|
|
|
/*
|
2003-08-09 13:31:44 +00:00
|
|
|
|
{ "decrease-visibility", do_decrease_visibility },
|
|
|
|
|
{ "increase-visibility", do_increase_visibility },
|
2011-01-19 18:36:04 +00:00
|
|
|
|
*/
|
2004-10-24 09:13:17 +00:00
|
|
|
|
{ "loadxml", do_load_xml_to_proptree},
|
2007-06-04 10:07:31 +00:00
|
|
|
|
{ "savexml", do_save_xml_from_proptree },
|
2012-04-18 09:25:27 +00:00
|
|
|
|
{ "xmlhttprequest", do_load_xml_from_url },
|
2006-03-04 20:21:32 +00:00
|
|
|
|
{ "press-cockpit-button", do_press_cockpit_button },
|
|
|
|
|
{ "release-cockpit-button", do_release_cockpit_button },
|
2007-05-26 11:39:13 +00:00
|
|
|
|
{ "dump-scenegraph", do_dump_scene_graph },
|
2007-11-21 20:51:49 +00:00
|
|
|
|
{ "dump-terrainbranch", do_dump_terrain_branch },
|
2010-11-14 14:12:58 +00:00
|
|
|
|
{ "print-visible-scene", do_print_visible_scene_info },
|
2009-08-08 10:20:32 +00:00
|
|
|
|
{ "reload-shaders", do_reload_shaders },
|
2011-06-01 22:55:04 +00:00
|
|
|
|
|
|
|
|
|
{ "find-navaid", do_nav_search },
|
|
|
|
|
{ "find-comm", do_comm_search },
|
|
|
|
|
|
2001-06-14 22:18:01 +00:00
|
|
|
|
{ 0, 0 } // zero-terminated
|
2001-06-01 17:52:17 +00:00
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Initialize the default built-in commands.
|
|
|
|
|
*
|
|
|
|
|
* Other commands may be added by other parts of the application.
|
|
|
|
|
*/
|
|
|
|
|
void
|
|
|
|
|
fgInitCommands ()
|
|
|
|
|
{
|
2010-02-15 23:57:56 +00:00
|
|
|
|
SG_LOG(SG_GENERAL, SG_BULK, "Initializing basic built-in commands:");
|
2001-06-01 17:52:17 +00:00
|
|
|
|
for (int i = 0; built_ins[i].name != 0; i++) {
|
2010-02-15 23:57:56 +00:00
|
|
|
|
SG_LOG(SG_GENERAL, SG_BULK, " " << built_ins[i].name);
|
2001-06-01 17:52:17 +00:00
|
|
|
|
globals->get_commands()->addCommand(built_ins[i].name,
|
|
|
|
|
built_ins[i].command);
|
|
|
|
|
}
|
2002-08-25 20:56:16 +00:00
|
|
|
|
|
|
|
|
|
typedef bool (*dummy)();
|
|
|
|
|
fgTie( "/command/view/next", dummy(0), do_view_next );
|
|
|
|
|
fgTie( "/command/view/prev", dummy(0), do_view_prev );
|
2001-06-01 17:52:17 +00:00
|
|
|
|
}
|
|
|
|
|
|
2002-05-20 16:13:37 +00:00
|
|
|
|
// end of fg_commands.cxx
|