X-Git-Url: https://git.mxchange.org/?a=blobdiff_plain;f=src%2FCockpit%2Fpanel_io.cxx;h=0e395a31c08a2cf29a49739f81dd16eaf15ad733;hb=29275ce1ecf9c4ea302aacca8c5ae5d4d3319a17;hp=1175b0d7129f10937656babc6cba2408de4a1c21;hpb=a2cd367ab5061c62c1b6c2a0fd14b216956724af;p=flightgear.git diff --git a/src/Cockpit/panel_io.cxx b/src/Cockpit/panel_io.cxx index 1175b0d71..0e395a31c 100644 --- a/src/Cockpit/panel_io.cxx +++ b/src/Cockpit/panel_io.cxx @@ -27,68 +27,262 @@ #endif #include + +#include #include #include -#include -#include +#include STL_IOSTREAM +#include STL_FSTREAM +#include STL_STRING + +#include
+#include
#include "panel.hxx" +#include "steam.hxx" +#include "panel_io.hxx" + +#if !defined (FG_HAVE_NATIVE_SGI_COMPILERS) +FG_USING_STD(istream); +FG_USING_STD(ifstream); +#endif +FG_USING_STD(string); + + + +//////////////////////////////////////////////////////////////////////// +// Default panel, instrument, and layer for when things go wrong... +//////////////////////////////////////////////////////////////////////// + +static FGCroppedTexture defaultTexture("Textures/default.rgb"); + + +/** + * Default layer: the default texture. + */ +class DefaultLayer : public FGTexturedLayer +{ +public: + DefaultLayer () : FGTexturedLayer(defaultTexture) + { + } + +}; + +/** + * Default instrument: a single default layer. + */ +class DefaultInstrument : public FGLayeredInstrument +{ +public: + DefaultInstrument (int x, int y, int w, int h) + : FGLayeredInstrument(x, y, w, h) + { + addLayer(new DefaultLayer()); + } +}; + + +/** + * Default panel: the default texture. + */ +class DefaultPanel : public FGPanel +{ +public: + DefaultPanel (int x, int y, int w, int h) : FGPanel(x, y, w, h) + { + setBackground(defaultTexture.getTexture()); + } +}; + + + +//////////////////////////////////////////////////////////////////////// +// Built-in layer for the magnetic compass ribbon layer. +// +// TODO: move this out into a special directory for built-in +// layers of various sorts. +//////////////////////////////////////////////////////////////////////// + +class FGMagRibbon : public FGTexturedLayer +{ +public: + FGMagRibbon (int w, int h); + virtual ~FGMagRibbon () {} + + virtual void draw (); +}; + +FGMagRibbon::FGMagRibbon (int w, int h) + : FGTexturedLayer(w, h) +{ + FGCroppedTexture texture("Aircraft/c172/Instruments/Textures/compass-ribbon.rgb"); + setTexture(texture); +} + +void +FGMagRibbon::draw () +{ + double heading = FGSteam::get_MH_deg(); + double xoffset, yoffset; + + while (heading >= 360.0) { + heading -= 360.0; + } + while (heading < 0.0) { + heading += 360.0; + } + + if (heading >= 60.0 && heading <= 180.0) { + xoffset = heading / 240.0; + yoffset = 0.75; + } else if (heading >= 150.0 && heading <= 270.0) { + xoffset = (heading - 90.0) / 240.0; + yoffset = 0.50; + } else if (heading >= 240.0 && heading <= 360.0) { + xoffset = (heading - 180.0) / 240.0; + yoffset = 0.25; + } else { + if (heading < 270.0) + heading += 360.0; + xoffset = (heading - 270.0) / 240.0; + yoffset = 0.0; + } + + xoffset = 1.0 - xoffset; + // Adjust to put the number in the centre + xoffset -= 0.25; -using std::istream; -using std::string; + FGCroppedTexture &t = getTexture(); + t.setCrop(xoffset, yoffset, xoffset + 0.5, yoffset + 0.25); + FGTexturedLayer::draw(); +} //////////////////////////////////////////////////////////////////////// // Read and construct a panel. +// +// The panel is specified as a regular property list, and each of the +// instruments is its own, separate property list (and thus, a separate +// XML document). The functions in this section read in the files +// as property lists, then extract properties to set up the panel +// itself. +// +// A panel contains zero or more instruments. +// +// An instrument contains one or more layers and zero or more actions. +// +// A layer contains zero or more transformations. +// +// Some special types of layers also contain other objects, such as +// chunks of text or other layers. +// +// There are currently four types of layers: +// +// 1. Textured Layer (type="texture"), the default +// 2. Text Layer (type="text") +// 3. Switch Layer (type="switch") +// 4. Built-in Layer (type="built-in", must also specify class) +// +// The only built-in layer so far is the ribbon for the magnetic compass +// (class="compass-ribbon"). +// +// There are three types of actions: +// +// 1. Adjust (type="adjust"), the default +// 2. Swap (type="swap") +// 3. Toggle (type="toggle") +// +// There are three types of transformations: +// +// 1. X shift (type="x-shift"), the default +// 2. Y shift (type="y-shift") +// 3. Rotation (type="rotation") +// +// Each of these may be associated with a property, so that a needle +// will rotate with the airspeed, for example, or may have a fixed +// floating-point value. //////////////////////////////////////////////////////////////////////// /** - * Read a cropped texture. + * Read a cropped texture from the instrument's property list. + * + * The x1 and y1 properties give the starting position of the texture + * (between 0.0 and 1.0), and the the x2 and y2 properties give the + * ending position. For example, to use the bottom-left quarter of a + * texture, x1=0.0, y1=0.0, x2=0.5, y2=0.5. */ -static CroppedTexture -readTexture (SGPropertyNode node) +static FGCroppedTexture +readTexture (const SGPropertyNode * node) { - CroppedTexture texture(node.getStringValue("path"), - node.getFloatValue("x1"), - node.getFloatValue("y1"), - node.getFloatValue("x2", 1.0), - node.getFloatValue("y2", 1.0)); - FG_LOG(FG_INPUT, FG_INFO, "Read texture " << node.getName()); + FGCroppedTexture texture(node->getStringValue("path"), + node->getFloatValue("x1"), + node->getFloatValue("y1"), + node->getFloatValue("x2", 1.0), + node->getFloatValue("y2", 1.0)); + FG_LOG(FG_INPUT, FG_INFO, "Read texture " << node->getName()); return texture; } /** - * Read an action. + * Read an action from the instrument's property list. + * + * The action will be performed when the user clicks a mouse button + * within the specified region of the instrument. Actions always + * work by modifying the value of a property (see the SGValue class). + * + * The following action types are defined: + * + * "adjust" - modify the value of a floating-point property by + * the increment specified. This is the default. + * + * "swap" - swap the values of two-floating-point properties. + * + * "toggle" - toggle the value of a boolean property between true and + * false. + * + * For the adjust action, it is possible to specify an increment + * (use a negative number for a decrement), a minimum allowed value, + * a maximum allowed value, and a flag to indicate whether the value + * should freeze or wrap-around when it reachs the minimum or maximum. + * + * The action will be scaled automatically if the instrument is not + * being drawn at its regular size. */ static FGPanelAction * -readAction (SGPropertyNode node) +readAction (const SGPropertyNode * node, float hscale, float vscale) { FGPanelAction * action = 0; - cerr << "Reading action\n"; + string name = node->getStringValue("name"); + string type = node->getStringValue("type"); - string type = node.getStringValue("type"); + int button = node->getIntValue("button"); + int x = int(node->getIntValue("x") * hscale); + int y = int(node->getIntValue("y") * vscale); + int w = int(node->getIntValue("w") * hscale); + int h = int(node->getIntValue("h") * vscale); - int button = node.getIntValue("button"); - int x = node.getIntValue("x"); - int y = node.getIntValue("y"); - int w = node.getIntValue("w"); - int h = node.getIntValue("h"); + if (type == "") { + FG_LOG(FG_INPUT, FG_ALERT, + "No type supplied for action " << name << " assuming \"adjust\""); + type = "adjust"; + } // Adjust a property value if (type == "adjust") { - string propName = node.getStringValue("property"); - SGValue * value = current_properties.getValue(propName, true); - float increment = node.getFloatValue("increment", 1.0); - float min = node.getFloatValue("min", 0.0); - float max = node.getFloatValue("max", 0.0); - bool wrap = node.getBoolValue("wrap", false); + string propName = node->getStringValue("property"); + SGValue * value = fgGetValue(propName, true); + float increment = node->getFloatValue("increment", 1.0); + float min = node->getFloatValue("min", 0.0); + float max = node->getFloatValue("max", 0.0); + bool wrap = node->getBoolValue("wrap", false); if (min == max) - FG_LOG(FG_INPUT, FG_ALERT, "Action " << node.getName() + FG_LOG(FG_INPUT, FG_ALERT, "Action " << node->getName() << " has same min and max value"); action = new FGAdjustAction(button, x, y, w, h, value, increment, min, max, wrap); @@ -96,30 +290,23 @@ readAction (SGPropertyNode node) // Swap two property values else if (type == "swap") { - string propName1 = node.getStringValue("property1"); - string propName2 = node.getStringValue("property2"); - SGValue * value1 = current_properties.getValue(propName1, true); - SGValue * value2 = current_properties.getValue(propName2, true); + string propName1 = node->getStringValue("property1"); + string propName2 = node->getStringValue("property2"); + SGValue * value1 = fgGetValue(propName1, true); + SGValue * value2 = fgGetValue(propName2, true); action = new FGSwapAction(button, x, y, w, h, value1, value2); } // Toggle a boolean value else if (type == "toggle") { - string propName = node.getStringValue("property"); - SGValue * value = current_properties.getValue(propName, true); + string propName = node->getStringValue("property"); + SGValue * value = fgGetValue(propName, true); action = new FGToggleAction(button, x, y, w, h, value); } - // No type supplied - else if (type == "") { - FG_LOG(FG_INPUT, FG_ALERT, "No type specified for action " - << node.getName()); - return 0; - } - // Unrecognized type else { - FG_LOG(FG_INPUT, FG_ALERT, "Unrecognized action type " << node.getName()); + FG_LOG(FG_INPUT, FG_ALERT, "Unrecognized action type " << type); return 0; } @@ -128,40 +315,82 @@ readAction (SGPropertyNode node) /** - * Read a single transformation. + * Read a transformation from the instrument's property list. + * + * The panel module uses the transformations to slide or spin needles, + * knobs, and other indicators, and to place layers in the correct + * positions. Every layer starts centered exactly on the x,y co-ordinate, + * and many layers need to be moved or rotated simply to display the + * instrument correctly. + * + * There are three types of transformations: + * + * "x-shift" - move the layer horizontally. + * + * "y-shift" - move the layer vertically. + * + * "rotation" - rotate the layer. + * + * Each transformation may have a fixed offset, and may also have + * a floating-point property value to add to the offset. The + * floating-point property may be clamped to a minimum and/or + * maximum range and scaled (after clamping). + * + * Note that because of the way OpenGL works, transformations will + * appear to be applied backwards. */ static FGPanelTransformation * -readTransformation (SGPropertyNode node) +readTransformation (const SGPropertyNode * node, float hscale, float vscale) { FGPanelTransformation * t = new FGPanelTransformation; - string name = node.getName(); - string type = node.getStringValue("type"); - string propName = node.getStringValue("property", ""); + string name = node->getName(); + string type = node->getStringValue("type"); + string propName = node->getStringValue("property", ""); SGValue * value = 0; - if (propName != "") { - value = current_properties.getValue(propName, true); + if (type == "") { + FG_LOG(FG_INPUT, FG_ALERT, + "No type supplied for transformation " << name + << " assuming \"rotation\""); + type = "rotation"; + } + + if (propName != (string)"") { + value = fgGetValue(propName, true); } t->value = value; - t->min = node.getFloatValue("min", 0.0); - t->max = node.getFloatValue("max", 1.0); - t->factor = node.getFloatValue("factor", 1.0); - t->offset = node.getFloatValue("offset", 0.0); + t->min = node->getFloatValue("min", -9999999); + t->max = node->getFloatValue("max", 99999999); + t->factor = node->getFloatValue("scale", 1.0); + t->offset = node->getFloatValue("offset", 0.0); + + // Move the layer horizontally. if (type == "x-shift") { t->type = FGPanelTransformation::XSHIFT; - } else if (type == "y-shift") { + t->min *= hscale; + t->max *= hscale; + t->offset *= hscale; + } + + // Move the layer vertically. + else if (type == "y-shift") { t->type = FGPanelTransformation::YSHIFT; - } else if (type == "rotation") { + t->min *= vscale; + t->max *= vscale; + t->offset *= vscale; + } + + // Rotate the layer. The rotation + // is in degrees, and does not need + // to scale with the instrument size. + else if (type == "rotation") { t->type = FGPanelTransformation::ROTATION; - } else if (type == "") { - FG_LOG(FG_INPUT, FG_ALERT, - "'type' must be specified for transformation"); - delete t; - return 0; - } else { - FG_LOG(FG_INPUT, FG_ALERT, "Unrecognized transformation: " << type); + } + + else { + FG_LOG(FG_INPUT, FG_ALERT, "Unrecognized transformation type " << type); delete t; return 0; } @@ -172,117 +401,349 @@ readTransformation (SGPropertyNode node) /** - * Read a single layer of an instrument. + * Read a chunk of text from the instrument's property list. + * + * A text layer consists of one or more chunks of text. All chunks + * share the same font size and color (and eventually, font), but + * each can come from a different source. There are three types of + * text chunks: + * + * "literal" - a literal text string (the default) + * + * "text-value" - the current value of a string property + * + * "number-value" - the current value of a floating-point property. + * + * All three may also include a printf-style format string. + */ +FGTextLayer::Chunk * +readTextChunk (const SGPropertyNode * node) +{ + FGTextLayer::Chunk * chunk; + string name = node->getStringValue("name"); + string type = node->getStringValue("type"); + string format = node->getStringValue("format"); + + // Default to literal text. + if (type == "") { + FG_LOG(FG_INPUT, FG_INFO, "No type provided for text chunk " << name + << " assuming \"literal\""); + type = "literal"; + } + + // A literal text string. + if (type == "literal") { + string text = node->getStringValue("text"); + chunk = new FGTextLayer::Chunk(text, format); + } + + // The value of a string property. + else if (type == "text-value") { + SGValue * value = + fgGetValue(node->getStringValue("property"), true); + chunk = new FGTextLayer::Chunk(FGTextLayer::TEXT_VALUE, value, format); + } + + // The value of a float property. + else if (type == "number-value") { + string propName = node->getStringValue("property"); + float scale = node->getFloatValue("scale", 1.0); + SGValue * value = fgGetValue(propName, true); + chunk = new FGTextLayer::Chunk(FGTextLayer::DOUBLE_VALUE, value, + format, scale); + } + + // Unknown type. + else { + FG_LOG(FG_INPUT, FG_ALERT, "Unrecognized type " << type + << " for text chunk " << name); + return 0; + } + + return chunk; +} + + +/** + * Read a single layer from an instrument's property list. + * + * Each instrument consists of one or more layers stacked on top + * of each other; the lower layers show through only where the upper + * layers contain an alpha component. Each layer can be moved + * horizontally and vertically and rotated using transformations. + * + * This module currently recognizes four kinds of layers: + * + * "texture" - a layer containing a texture (the default) + * + * "text" - a layer containing text + * + * "switch" - a layer that switches between two other layers + * based on the current value of a boolean property. + * + * "built-in" - a hard-coded layer supported by C++ code in FlightGear. + * + * Currently, the only built-in layer class is "compass-ribbon". */ static FGInstrumentLayer * -readLayer (SGPropertyNode node) +readLayer (const SGPropertyNode * node, float hscale, float vscale) { - FGInstrumentLayer * l; - string name = node.getName(); + FGInstrumentLayer * layer = NULL; + string name = node->getStringValue("name"); + string type = node->getStringValue("type"); + int w = node->getIntValue("w", -1); + int h = node->getIntValue("h", -1); + if (w != -1) + w = int(w * hscale); + if (h != -1) + h = int(h * vscale); + + + if (type == "") { + FG_LOG(FG_INPUT, FG_ALERT, + "No type supplied for layer " << name + << " assuming \"texture\""); + type = "texture"; + } - CroppedTexture texture = readTexture(node.getSubNode("texture")); - l = new FGTexturedLayer(texture, - node.getIntValue("w", -1), - node.getIntValue("h", -1)); + + // A textured instrument layer. + if (type == "texture") { + FGCroppedTexture texture = readTexture(node->getNode("texture")); + layer = new FGTexturedLayer(texture, w, h); + } + + + // A textual instrument layer. + else if (type == "text") { + FGTextLayer * tlayer = new FGTextLayer(w, h); // FIXME + + // Set the text color. + float red = node->getFloatValue("color/red", 0.0); + float green = node->getFloatValue("color/green", 0.0); + float blue = node->getFloatValue("color/blue", 0.0); + tlayer->setColor(red, green, blue); + + // Set the point size. + float pointSize = node->getFloatValue("point-size", 10.0) * hscale; + tlayer->setPointSize(pointSize); + + // Set the font. + // TODO + + const SGPropertyNode * chunk_group = node->getNode("chunks"); + if (chunk_group != 0) { + int nChunks = chunk_group->nChildren(); + for (int i = 0; i < nChunks; i++) { + FGTextLayer::Chunk * chunk = readTextChunk(chunk_group->getChild(i)); + if (chunk == 0) { + delete layer; + return 0; + } + tlayer->addChunk(chunk); + } + layer = tlayer; + } + } + + // A switch instrument layer. + else if (type == "switch") { + SGValue * value = + fgGetValue(node->getStringValue("property"), true); + FGInstrumentLayer * layer1 = + readLayer(node->getNode("layer1"), hscale, vscale); + FGInstrumentLayer * layer2 = + readLayer(node->getNode("layer2"), hscale, vscale); + layer = new FGSwitchLayer(w, h, value, layer1, layer2); + } + + // A built-in instrument layer. + else if (type == "built-in") { + string layerclass = node->getStringValue("class"); + + if (layerclass == "mag-ribbon") { + layer = new FGMagRibbon(w, h); + } + + else if (layerclass == "") { + FG_LOG(FG_INPUT, FG_ALERT, "No class provided for built-in layer " + << name); + return 0; + } + + else { + FG_LOG(FG_INPUT, FG_ALERT, "Unknown built-in layer class " + << layerclass); + return 0; + } + } + + // An unknown type. + else { + FG_LOG(FG_INPUT, FG_ALERT, "Unrecognized layer type " << type); + delete layer; + return 0; + } // // Get the transformations for each layer. // - SGPropertyNode trans_group = node.getSubNode("transformations"); - int nTransformations = trans_group.size(); - for (int k = 0; k < nTransformations; k++) { - FGPanelTransformation * t = readTransformation(trans_group.getChild(k)); - if (t == 0) { - delete l; - return 0; + const SGPropertyNode * trans_group = node->getNode("transformations"); + if (trans_group != 0) { + int nTransformations = trans_group->nChildren(); + for (int i = 0; i < nTransformations; i++) { + FGPanelTransformation * t = readTransformation(trans_group->getChild(i), + hscale, vscale); + if (t == 0) { + delete layer; + return 0; + } + layer->addTransformation(t); } - l->addTransformation(t); } FG_LOG(FG_INPUT, FG_INFO, "Read layer " << name); - return l; + return layer; } /** - * Read an instrument. + * Read an instrument from a property list. + * + * The instrument consists of a preferred width and height + * (the panel may override these), together with a list of layers + * and a list of actions to be performed when the user clicks + * the mouse over the instrument. All co-ordinates are relative + * to the instrument's position, so instruments are fully relocatable; + * likewise, co-ordinates for actions and transformations will be + * scaled automatically if the instrument is not at its preferred size. */ static FGPanelInstrument * -readInstrument (SGPropertyNode node) +readInstrument (const SGPropertyNode * node, int x, int y, + int real_w, int real_h) { - const string &name = node.getStringValue("name"); + int w = node->getIntValue("w"); + int h = node->getIntValue("h"); + const string &name = node->getStringValue("name"); + + float hscale = 1.0; + float vscale = 1.0; + if (real_w != -1) { + hscale = float(real_w) / float(w); + w = real_w; + } + if (real_h != -1) { + vscale = float(real_h) / float(h); + h = real_h; + } FG_LOG(FG_INPUT, FG_INFO, "Reading instrument " << name); FGLayeredInstrument * instrument = - new FGLayeredInstrument(0, 0, - node.getIntValue("w"), - node.getIntValue("h")); + new FGLayeredInstrument(x, y, w, h); // // Get the actions for the instrument. // - SGPropertyNode action_group = node.getSubNode("actions"); - int nActions = action_group.size(); - cerr << "There are " << nActions << " actions\n"; - for (int j = 0; j < nActions; j++) { - FGPanelAction * action = readAction(action_group.getChild(j)); - if (action == 0) { - delete instrument; - return 0; + const SGPropertyNode * action_group = node->getNode("actions"); + if (action_group != 0) { + int nActions = action_group->nChildren(); + for (int i = 0; i < nActions; i++) { + FGPanelAction * action = readAction(action_group->getChild(i), + hscale, vscale); + if (action == 0) { + delete instrument; + return new DefaultInstrument(x, y, w, h); + } + instrument->addAction(action); } - instrument->addAction(action); } // // Get the layers for the instrument. // - SGPropertyNode layer_group = node.getSubNode("layers"); - int nLayers = layer_group.size(); - for (int j = 0; j < nLayers; j++) { - FGInstrumentLayer * layer = readLayer(layer_group.getChild(j)); - if (layer == 0) { - delete instrument; - return 0; + const SGPropertyNode * layer_group = node->getNode("layers"); + if (layer_group != 0) { + int nLayers = layer_group->nChildren(); + for (int i = 0; i < nLayers; i++) { + FGInstrumentLayer * layer = readLayer(layer_group->getChild(i), + hscale, vscale); + if (layer == 0) { + delete instrument; + return new DefaultInstrument(x, y, w, h); + } + instrument->addLayer(layer); } - instrument->addLayer(layer); } - + FG_LOG(FG_INPUT, FG_INFO, "Done reading instrument " << name); return instrument; } /** - * Read a property list defining an instrument panel. + * Read a panel from a property list. + * + * Each panel instrument will appear in its own, separate + * property list. The top level simply names the panel and + * places the instruments in their appropriate locations (and + * optionally resizes them if necessary). * * Returns 0 if the read fails for any reason. */ FGPanel * fgReadPanel (istream &input) { - SGPropertyList props; + SGPropertyNode root; // // Read the property list from disk. // - if (!readPropertyList(input, &props)) { + if (!readProperties(input, &root)) { FG_LOG(FG_INPUT, FG_ALERT, "Malformed property list for panel."); return 0; } FG_LOG(FG_INPUT, FG_INFO, "Read properties for panel " << - props.getStringValue("/name")); + root.getStringValue("name")); // // Construct a new, empty panel. // FGPanel * panel = new FGPanel(0, 0, 1024, 768);// FIXME: use variable size + + // + // Grab the panel's dimensions, default to 1024x443. + // + int panel_w = (root.hasValue("w") ? root.getIntValue("w") : 1024); + int panel_h = (root.hasValue("h") ? root.getIntValue("h") : 443); + panel->setWidth(panel_w); + panel->setHeight(panel_h); + + // + // Grab the visible external viewing area, default to + // + panel->setViewHeight(root.hasValue("view-height") ? + root.getIntValue("view-height") : + 768 - panel_h + 2); + + // + // Grab the panel's initial offsets, default to 0, 0. + // + int xoffset = (root.hasValue("x-offset") ? + root.getIntValue("x-offset") : + 0); + int yoffset = (root.hasValue("y-offset") ? + root.getIntValue("y-offset") : + 0); + panel->setXOffset(xoffset); + panel->setYOffset(yoffset); + // // Assign the background texture, if any, or a bogus chequerboard. // - string bgTexture = props.getStringValue("/background"); + string bgTexture = root.getStringValue("background"); if (bgTexture == "") bgTexture = "FOO"; panel->setBackground(FGTextureManager::createTexture(bgTexture.c_str())); @@ -293,37 +754,46 @@ fgReadPanel (istream &input) // Create each instrument. // FG_LOG(FG_INPUT, FG_INFO, "Reading panel instruments"); - SGPropertyNode instrument_group("/instruments", &props); - int nInstruments = instrument_group.size(); - for (int i = 0; i < nInstruments; i++) { - SGPropertyList props2; - SGPropertyNode node = instrument_group.getChild(i); - - string path = node.getStringValue("path"); - int x = node.getIntValue("x"); - int y = node.getIntValue("y"); - int w = node.getIntValue("w"); - int h = node.getIntValue("h"); - - FG_LOG(FG_INPUT, FG_INFO, "Reading instrument " - << node.getName() - << " from " - << path); - - if (!readPropertyList(path, &props2)) { - delete panel; - return 0; - } - - FGPanelInstrument * instrument = - readInstrument(SGPropertyNode("/", &props2)); - if (instrument == 0) { - delete instrument; - delete panel; - return 0; + const SGPropertyNode * instrument_group = root.getChild("instruments"); + if (instrument_group != 0) { + int nInstruments = instrument_group->nChildren(); + for (int i = 0; i < nInstruments; i++) { + const SGPropertyNode * node = instrument_group->getChild(i); + + FGPath path( globals->get_fg_root() ); + path.append(node->getStringValue("path")); + + FG_LOG(FG_INPUT, FG_INFO, "Reading instrument " + << node->getName() + << " from " + << path.str()); + + int x = node->getIntValue("x", -1); + int y = node->getIntValue("y", -1); + int w = node->getIntValue("w", -1); + int h = node->getIntValue("h", -1); + + if (x == -1 || y == -1) { + FG_LOG(FG_INPUT, FG_ALERT, "x and y positions must be specified and >0"); + delete panel; + return 0; + } + + // Read the instrument from + // a separate file. + FGPanelInstrument * instrument = 0; + + SGPropertyNode root2; + + if (readProperties(path.str(), &root2)) { + cerr << "Read " << root2.nChildren() << " top-level nodes from " + << path.c_str() << endl; + instrument = readInstrument(&root2, x, y, w, h); + } + if (instrument == 0) + instrument = new DefaultInstrument(x, y, w, h); + panel->addInstrument(instrument); } - instrument->setPosition(x, y); - panel->addInstrument(instrument); } FG_LOG(FG_INPUT, FG_INFO, "Done reading panel instruments"); @@ -335,4 +805,31 @@ fgReadPanel (istream &input) } +/** + * Read a panel from a property list. + * + * This function opens a stream to a file, then invokes the + * main fgReadPanel() function. + */ +FGPanel * +fgReadPanel (const string &relative_path) +{ + FGPanel * panel = 0; + FGPath path(globals->get_fg_root()); + path.append(relative_path); + ifstream input(path.c_str()); + if (!input.good()) { + FG_LOG(FG_INPUT, FG_ALERT, + "Cannot read panel configuration from " << path.str()); + } else { + panel = fgReadPanel(input); + input.close(); + } + if (panel == 0) + panel = new DefaultPanel(0, 0, 1024, 768); + return panel; +} + + + // end of panel_io.cxx