X-Git-Url: https://git.mxchange.org/?a=blobdiff_plain;f=src%2FCockpit%2Fpanel_io.cxx;h=64641b5f74c8f4db7aa569848e63216fe1644f74;hb=c0b4531d04215f76f9cf65afbdaed6b7c0b32634;hp=1175b0d7129f10937656babc6cba2408de4a1c21;hpb=a2cd367ab5061c62c1b6c2a0fd14b216956724af;p=flightgear.git diff --git a/src/Cockpit/panel_io.cxx b/src/Cockpit/panel_io.cxx index 1175b0d71..64641b5f7 100644 --- a/src/Cockpit/panel_io.cxx +++ b/src/Cockpit/panel_io.cxx @@ -26,306 +26,724 @@ # include #endif +#include // for strcmp() + #include +#include #include -#include +#include +#include + +#include STL_IOSTREAM +#include STL_FSTREAM +#include STL_STRING + +#include
+#include
-#include -#include +#include -#include "panel.hxx" +// #include "panel.hxx" +#include "panel_io.hxx" -using std::istream; -using std::string; +//built-in layers +#include "built_in/FGMagRibbon.hxx" + +SG_USING_STD(istream); +SG_USING_STD(ifstream); +SG_USING_STD(string); //////////////////////////////////////////////////////////////////////// // 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()); - return texture; + FGCroppedTexture texture(node->getStringValue("path"), + node->getFloatValue("x1"), + node->getFloatValue("y1"), + node->getFloatValue("x2", 1.0), + node->getFloatValue("y2", 1.0)); + SG_LOG(SG_COCKPIT, SG_DEBUG, "Read texture " << node->getName()); + return texture; } /** - * Read an action. + * Test for a condition in the current node. + */ + +//////////////////////////////////////////////////////////////////////// +// Read a condition and use it if necessary. +//////////////////////////////////////////////////////////////////////// + +static void +readConditions (SGConditional *component, const SGPropertyNode *node) +{ + const SGPropertyNode * conditionNode = node->getChild("condition"); + if (conditionNode != 0) + // The top level is implicitly AND + component->setCondition(sgReadCondition(globals->get_props(), + conditionNode) ); +} + + +/** + * 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 SGPropertyNode + * 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 w_scale, float h_scale) { - FGPanelAction * action = 0; - - cerr << "Reading action\n"; - - string type = node.getStringValue("type"); - - 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"); - - // 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); - if (min == max) - 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); - } + string name = node->getStringValue("name"); - // 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); - action = new FGSwapAction(button, x, y, w, h, value1, value2); - } + int button = node->getIntValue("button"); + int x = int(node->getIntValue("x") * w_scale); + int y = int(node->getIntValue("y") * h_scale); + int w = int(node->getIntValue("w") * w_scale); + int h = int(node->getIntValue("h") * h_scale); + bool repeatable = node->getBoolValue("repeatable", true); - // Toggle a boolean value - else if (type == "toggle") { - string propName = node.getStringValue("property"); - SGValue * value = current_properties.getValue(propName, true); - action = new FGToggleAction(button, x, y, w, h, value); - } + FGPanelAction * action = new FGPanelAction(button, x, y, w, h, repeatable); - // No type supplied - else if (type == "") { - FG_LOG(FG_INPUT, FG_ALERT, "No type specified for action " - << node.getName()); - return 0; - } + vectorbindings = node->getChildren("binding"); - // Unrecognized type - else { - FG_LOG(FG_INPUT, FG_ALERT, "Unrecognized action type " << node.getName()); - return 0; + unsigned int i; + for (i = 0; i < bindings.size(); i++) { + SG_LOG(SG_INPUT, SG_INFO, "Reading binding " + << bindings[i]->getStringValue("command")); + action->addBinding(new FGBinding(bindings[i]), 0); } + if (node->hasChild("mod-up")) { + bindings = node->getChild("mod-up")->getChildren("binding"); + for (i = 0; i < bindings.size(); i++) { + action->addBinding(new FGBinding(bindings[i]), 1); + } + } + + readConditions(action, node); return action; } /** - * 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 w_scale, float h_scale) { FGPanelTransformation * t = new FGPanelTransformation; - string name = node.getName(); - string type = node.getStringValue("type"); - string propName = node.getStringValue("property", ""); - SGValue * value = 0; + string name = node->getName(); + string type = node->getStringValue("type"); + string propName = node->getStringValue("property", ""); + SGPropertyNode * target = 0; - if (propName != "") { - value = current_properties.getValue(propName, true); + if (type.empty()) { + SG_LOG( SG_COCKPIT, SG_INFO, + "No type supplied for transformation " << name + << " assuming \"rotation\"" ); + type = "rotation"; } - 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); + if (!propName.empty()) { + target = fgGetNode(propName.c_str(), true); + } + + t->node = target; + t->min = node->getFloatValue("min", -9999999); + t->max = node->getFloatValue("max", 99999999); + t->has_mod = node->hasChild("modulator"); + if (t->has_mod) + t->mod = node->getFloatValue("modulator"); + t->factor = node->getFloatValue("scale", 1.0); + t->offset = node->getFloatValue("offset", 0.0); + + // Check for an interpolation table + const SGPropertyNode * trans_table = node->getNode("interpolation"); + if (trans_table != 0) { + SG_LOG( SG_COCKPIT, SG_INFO, "Found interpolation table with " + << trans_table->nChildren() << "children" ); + t->table = new SGInterpTable(); + for(int i = 0; i < trans_table->nChildren(); i++) { + const SGPropertyNode * node = trans_table->getChild(i); + if (!strcmp(node->getName(), "entry")) { + double ind = node->getDoubleValue("ind", 0.0); + double dep = node->getDoubleValue("dep", 0.0); + SG_LOG( SG_COCKPIT, SG_INFO, "Adding interpolation entry " + << ind << "==>" << dep ); + t->table->addEntry(ind, dep); + } else { + SG_LOG( SG_COCKPIT, SG_INFO, "Skipping " << node->getName() + << " in interpolation" ); + } + } + } else { + t->table = 0; + } + + // Move the layer horizontally. if (type == "x-shift") { t->type = FGPanelTransformation::XSHIFT; - } else if (type == "y-shift") { +// t->min *= w_scale; //removed by Martin Dressler +// t->max *= w_scale; //removed by Martin Dressler + t->offset *= w_scale; + t->factor *= w_scale; //Added by Martin Dressler + } + + // Move the layer vertically. + else if (type == "y-shift") { t->type = FGPanelTransformation::YSHIFT; - } else if (type == "rotation") { + //t->min *= h_scale; //removed + //t->max *= h_scale; //removed + t->offset *= h_scale; + t->factor *= h_scale; //Added + } + + // 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 { + SG_LOG( SG_COCKPIT, SG_ALERT, "Unrecognized transformation type " << type ); delete t; return 0; } - FG_LOG(FG_INPUT, FG_INFO, "Read transformation " << name); + readConditions(t, node); + SG_LOG( SG_COCKPIT, SG_DEBUG, "Read transformation " << name ); return t; } /** - * 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.empty()) { + SG_LOG( SG_COCKPIT, SG_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") { + SGPropertyNode * target = + fgGetNode(node->getStringValue("property"), true); + chunk = new FGTextLayer::Chunk(FGTextLayer::TEXT_VALUE, target, format); + } + + // The value of a float property. + else if (type == "number-value") { + string propName = node->getStringValue("property"); + float scale = node->getFloatValue("scale", 1.0); + float offset = node->getFloatValue("offset", 0.0); + bool truncation = node->getBoolValue("truncate", false); + SGPropertyNode * target = fgGetNode(propName.c_str(), true); + chunk = new FGTextLayer::Chunk(FGTextLayer::DOUBLE_VALUE, target, + format, scale, offset, truncation); + } + + // Unknown type. + else { + SG_LOG( SG_COCKPIT, SG_ALERT, "Unrecognized type " << type + << " for text chunk " << name ); + return 0; + } + + readConditions(chunk, node); + 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 w_scale, float h_scale) { - 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 * w_scale); + if (h != -1) + h = int(h * h_scale); + + + if (type.empty()) { + SG_LOG( SG_COCKPIT, SG_INFO, + "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 group of sublayers. + else if (type == "group") { + layer = new FGGroupLayer(); + for (int i = 0; i < node->nChildren(); i++) { + const SGPropertyNode * child = node->getChild(i); + if (!strcmp(child->getName(), "layer")) + ((FGGroupLayer *)layer)->addLayer(readLayer(child, w_scale, h_scale)); + } + } + + + // 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) * w_scale; + tlayer->setPointSize(pointSize); + + // Set the font. + string fontName = node->getStringValue("font", "default"); + tlayer->setFontName(fontName); + + const SGPropertyNode * chunk_group = node->getNode("chunks"); + if (chunk_group != 0) { + int nChunks = chunk_group->nChildren(); + for (int i = 0; i < nChunks; i++) { + const SGPropertyNode * node = chunk_group->getChild(i); + if (!strcmp(node->getName(), "chunk")) { + FGTextLayer::Chunk * chunk = readTextChunk(node); + if (chunk != 0) + tlayer->addChunk(chunk); + } else { + SG_LOG( SG_COCKPIT, SG_INFO, "Skipping " << node->getName() + << " in chunks" ); + } + } + layer = tlayer; + } + } + + // A switch instrument layer. + else if (type == "switch") { + layer = new FGSwitchLayer(); + for (int i = 0; i < node->nChildren(); i++) { + const SGPropertyNode * child = node->getChild(i); + if (!strcmp(child->getName(), "layer")) + ((FGGroupLayer *)layer)->addLayer(readLayer(child, w_scale, h_scale)); + } + } + + // 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.empty()) { + SG_LOG( SG_COCKPIT, SG_ALERT, "No class provided for built-in layer " + << name ); + return 0; + } + + else { + SG_LOG( SG_COCKPIT, SG_ALERT, "Unknown built-in layer class " + << layerclass); + return 0; + } + } + + // An unknown type. + else { + SG_LOG( SG_COCKPIT, SG_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++) { + const SGPropertyNode * node = trans_group->getChild(i); + if (!strcmp(node->getName(), "transformation")) { + FGPanelTransformation * t = readTransformation(node, w_scale, h_scale); + if (t != 0) + layer->addTransformation(t); + } else { + SG_LOG( SG_COCKPIT, SG_INFO, "Skipping " << node->getName() + << " in transformations" ); + } } - l->addTransformation(t); } - - FG_LOG(FG_INPUT, FG_INFO, "Read layer " << name); - return l; + + readConditions(layer, node); + SG_LOG( SG_COCKPIT, SG_DEBUG, "Read layer " << name ); + 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) { - const string &name = node.getStringValue("name"); + const string name = node->getStringValue("name"); + int x = node->getIntValue("x", -1); + int y = node->getIntValue("y", -1); + int real_w = node->getIntValue("w", -1); + int real_h = node->getIntValue("h", -1); + int w = node->getIntValue("w-base", -1); + int h = node->getIntValue("h-base", -1); + + if (x == -1 || y == -1) { + SG_LOG( SG_COCKPIT, SG_ALERT, + "x and y positions must be specified and > 0" ); + return 0; + } - FG_LOG(FG_INPUT, FG_INFO, "Reading instrument " << name); + float w_scale = 1.0; + float h_scale = 1.0; + if (real_w != -1) { + w_scale = float(real_w) / float(w); + w = real_w; + } + if (real_h != -1) { + h_scale = float(real_h) / float(h); + h = real_h; + } + + SG_LOG( SG_COCKPIT, SG_DEBUG, "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++) { + const SGPropertyNode * node = action_group->getChild(i); + if (!strcmp(node->getName(), "action")) { + FGPanelAction * action = readAction(node, w_scale, h_scale); + if (action != 0) + instrument->addAction(action); + } else { + SG_LOG( SG_COCKPIT, SG_INFO, "Skipping " << node->getName() + << " in actions" ); + } } - 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++) { + const SGPropertyNode * node = layer_group->getChild(i); + if (!strcmp(node->getName(), "layer")) { + FGInstrumentLayer * layer = readLayer(node, w_scale, h_scale); + if (layer != 0) + instrument->addLayer(layer); + } else { + SG_LOG( SG_COCKPIT, SG_INFO, "Skipping " << node->getName() + << " in layers" ); + } } - instrument->addLayer(layer); } - FG_LOG(FG_INPUT, FG_INFO, "Done reading instrument " << name); + readConditions(instrument, node); + SG_LOG( SG_COCKPIT, SG_DEBUG, "Done reading instrument " << name ); return instrument; } /** - * Read a property list defining an instrument panel. - * - * Returns 0 if the read fails for any reason. + * Construct the panel from a property tree. */ FGPanel * -fgReadPanel (istream &input) +readPanel (const SGPropertyNode * root) { - SGPropertyList props; + SG_LOG( SG_COCKPIT, SG_INFO, "Reading properties for panel " << + root->getStringValue("name", "[Unnamed Panel]") ); + FGPanel * panel = new FGPanel(); + panel->setWidth(root->getIntValue("w", 1024)); + panel->setHeight(root->getIntValue("h", 443)); // - // Read the property list from disk. + // Grab the visible external viewing area, default to // - if (!readPropertyList(input, &props)) { - 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")); + panel->setViewHeight(root->getIntValue("view-height", + 768 - panel->getHeight() + 2)); // - // Construct a new, empty panel. + // Grab the panel's initial offsets, default to 0, 0. // - FGPanel * panel = new FGPanel(0, 0, 1024, 768);// FIXME: use variable size + if (!fgHasNode("/sim/panel/x-offset")) + fgSetInt("/sim/panel/x-offset", root->getIntValue("x-offset", 0)); + + // conditional removed by jim wilson to allow panel xml code + // with y-offset defined to work... + if (!fgHasNode("/sim/panel/y-offset")) + fgSetInt("/sim/panel/y-offset", root->getIntValue("y-offset", 0)); // // Assign the background texture, if any, or a bogus chequerboard. // - string bgTexture = props.getStringValue("/background"); - if (bgTexture == "") + string bgTexture = root->getStringValue("background"); + if (bgTexture.empty()) bgTexture = "FOO"; panel->setBackground(FGTextureManager::createTexture(bgTexture.c_str())); - FG_LOG(FG_INPUT, FG_INFO, "Set background texture to " << bgTexture); + SG_LOG( SG_COCKPIT, SG_INFO, "Set background texture to " << bgTexture ); + + // + // Get multibackground if any... + // + string mbgTexture = root->getStringValue("multibackground[0]"); + if (!mbgTexture.empty()) { + panel->setMultiBackground(FGTextureManager::createTexture(mbgTexture.c_str()), 0); + SG_LOG( SG_COCKPIT, SG_INFO, "Set background texture to " << mbgTexture ); + + mbgTexture = root->getStringValue("multibackground[1]"); + if (mbgTexture.empty()) + mbgTexture = "FOO"; + panel->setMultiBackground(FGTextureManager::createTexture(mbgTexture.c_str()), 1); + SG_LOG( SG_COCKPIT, SG_INFO, "Set background texture to " << mbgTexture ); + + mbgTexture = root->getStringValue("multibackground[2]"); + if (mbgTexture.empty()) + mbgTexture = "FOO"; + panel->setMultiBackground(FGTextureManager::createTexture(mbgTexture.c_str()), 2); + SG_LOG( SG_COCKPIT, SG_INFO, "Set background texture to " << mbgTexture ); + + mbgTexture = root->getStringValue("multibackground[3]"); + if (mbgTexture.empty()) + mbgTexture = "FOO"; + panel->setMultiBackground(FGTextureManager::createTexture(mbgTexture.c_str()), 3); + SG_LOG( SG_COCKPIT, SG_INFO, "Set background texture to " << mbgTexture ); + + mbgTexture = root->getStringValue("multibackground[4]"); + if (mbgTexture.empty()) + mbgTexture = "FOO"; + panel->setMultiBackground(FGTextureManager::createTexture(mbgTexture.c_str()), 4); + SG_LOG( SG_COCKPIT, SG_INFO, "Set background texture to " << mbgTexture ); + + mbgTexture = root->getStringValue("multibackground[5]"); + if (mbgTexture.empty()) + mbgTexture = "FOO"; + panel->setMultiBackground(FGTextureManager::createTexture(mbgTexture.c_str()), 5); + SG_LOG( SG_COCKPIT, SG_INFO, "Set background texture to " << mbgTexture ); + + mbgTexture = root->getStringValue("multibackground[6]"); + if (mbgTexture.empty()) + mbgTexture = "FOO"; + panel->setMultiBackground(FGTextureManager::createTexture(mbgTexture.c_str()), 6); + SG_LOG( SG_COCKPIT, SG_INFO, "Set background texture to " << mbgTexture ); + + mbgTexture = root->getStringValue("multibackground[7]"); + if (mbgTexture.empty()) + mbgTexture = "FOO"; + panel->setMultiBackground(FGTextureManager::createTexture(mbgTexture.c_str()), 7); + SG_LOG( SG_COCKPIT, SG_INFO, "Set background texture to " << mbgTexture ); + + } + // // 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; + SG_LOG( SG_COCKPIT, SG_INFO, "Reading panel instruments" ); + 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); + if (!strcmp(node->getName(), "instrument")) { + FGPanelInstrument * instrument = readInstrument(node); + if (instrument != 0) + panel->addInstrument(instrument); + } else { + SG_LOG( SG_COCKPIT, SG_INFO, "Skipping " << node->getName() + << " in instruments section" ); + } } - instrument->setPosition(x, y); - panel->addInstrument(instrument); } - FG_LOG(FG_INPUT, FG_INFO, "Done reading panel instruments"); + SG_LOG( SG_COCKPIT, SG_INFO, "Done reading panel instruments" ); // @@ -335,4 +753,56 @@ fgReadPanel (istream &input) } +/** + * 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) +{ + SGPropertyNode root; + + try { + readProperties(input, &root); + } catch (const sg_exception &e) { + guiErrorMessage("Error reading panel: ", e); + return 0; + } + return readPanel(&root); +} + + +/** + * 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) +{ + SGPath path(globals->get_fg_root()); + path.append(relative_path); + SGPropertyNode root; + + try { + readProperties(path.str(), &root); + } catch (const sg_exception &e) { + guiErrorMessage("Error reading panel: ", e); + return 0; + } + return readPanel(&root); +} + + + // end of panel_io.cxx + + +