diff --git a/garmin_fs.cc b/garmin_fs.cc index 1827fe120..c6846efce 100644 --- a/garmin_fs.cc +++ b/garmin_fs.cc @@ -20,17 +20,17 @@ */ -#include // for snprintf, sscanf -#include // for strtod +#include // for snprintf, sscanf +#include // for strtod -#include // for QString, QStringLiteral -#include // for QXmlStreamWriter -#include // for CaseInsensitive +#include // for QString, QStringLiteral +#include // for CaseInsensitive #include "defs.h" #include "garmin_fs.h" -#include "garmin_tables.h" // for gt_switch_display_mode_value, gt_display_mode_symbol, gt_display_mode_symbol_and_comment, gt_display_mode_symbol_and_name -#include "inifile.h" // for inifile_readstr +#include "garmin_tables.h" // for gt_switch_display_mode_value, gt_display_mode_symbol, gt_display_mode_symbol_and_comment, gt_display_mode_symbol_and_name +#include "inifile.h" // for inifile_readstr +#include "src/core/xmlstreamwriter.h" // for XmlStreamWriter #define MYNAME "garmin_fs" @@ -80,113 +80,75 @@ garmin_fs_t::~garmin_fs_t() void garmin_fs_xml_fprint(const Waypoint* waypt, - QXmlStreamWriter* writer) + gpsbabel::XmlStreamWriter* writer) { garmin_fs_t* gmsd = garmin_fs_t::find(waypt); - if (gmsd == nullptr) { - return; - } + writer->stackOptionalStartElement(QStringLiteral("extensions")); + writer->stackOptionalStartElement(QStringLiteral("gpxx:WaypointExtension")); + writer->stackNamespace(QStringLiteral("http://www.garmin.com/xmlschemas/GpxExtensions/v3"), + "gpxx"); - /* Find out if there is at least one field set */ - QString addr = garmin_fs_t::get_addr(gmsd, ""); - if (addr.isEmpty()) { - addr = garmin_fs_t::get_city(gmsd, ""); - } - if (addr.isEmpty()) { - addr = garmin_fs_t::get_country(gmsd, ""); + if (waypt->proximity_has_value()) { + writer->stackTextElement(QStringLiteral("gpxx:Proximity"), QString::number(waypt->proximity_value(), 'f', 6)); } - if (addr.isEmpty()) { - addr = garmin_fs_t::get_postal_code(gmsd, ""); + + if (waypt->temperature_has_value()) { + writer->stackTextElement(QStringLiteral("gpxx:Temperature"), QString::number(waypt->temperature_value(), 'f', 6)); } - if (addr.isEmpty()) { - addr = garmin_fs_t::get_state(gmsd, ""); + + if (waypt->depth_has_value()) { + writer->stackTextElement(QStringLiteral("gpxx:Depth"), QString::number(waypt->depth_value(), 'f', 6)); } - QString phone = garmin_fs_t::get_phone_nr(gmsd, ""); - - if (!addr.isEmpty() || !phone.isEmpty() || - (gmsd->flags.category && gmsd->category) || - waypt->depth_has_value() || - waypt->proximity_has_value() || - waypt->temperature_has_value() || - gmsd->flags.display) { - writer->writeStartElement(QStringLiteral("extensions")); - writer->writeStartElement(QStringLiteral("gpxx:WaypointExtension")); - writer->writeNamespace(QStringLiteral("http://www.garmin.com/xmlschemas/GpxExtensions/v3"), - "gpxx"); - if (waypt->proximity_has_value()) { - writer->writeTextElement(QStringLiteral("gpxx:Proximity"), QString::number(waypt->proximity_value(), 'f', 6)); - } - if (waypt->temperature_has_value()) { - writer->writeTextElement(QStringLiteral("gpxx:Temperature"), QString::number(waypt->temperature_value(), 'f', 6)); - } - if (waypt->depth_has_value()) { - writer->writeTextElement(QStringLiteral("gpxx:Depth"), QString::number(waypt->depth_value(), 'f', 6)); - } - if (gmsd->flags.display) { - const char* cx; - switch (gmsd->display) { - case gt_display_mode_symbol: - cx = "SymbolOnly"; - break; - case gt_display_mode_symbol_and_comment: - cx = "SymbolAndDescription"; - break; - default: - cx = "SymbolAndName"; - break; - } - writer->writeTextElement(QStringLiteral("gpxx:DisplayMode"), cx); + if (garmin_fs_t::has_display(gmsd)) { + const char* cx; + switch (gmsd->display) { + case gt_display_mode_symbol: + cx = "SymbolOnly"; + break; + case gt_display_mode_symbol_and_comment: + cx = "SymbolAndDescription"; + break; + default: + cx = "SymbolAndName"; + break; } - if (gmsd->flags.category && gmsd->category) { - uint16_t cx = gmsd->category; - writer->writeStartElement(QStringLiteral("gpxx:Categories")); - for (int i = 0; i < 16; i++) { - if (cx & 1) { - writer->writeTextElement(QStringLiteral("gpxx:Category"), QStringLiteral("Category %1").arg(i+1)); - } - cx = cx >> 1; - } - writer->writeEndElement(); // gpxx:Categories - } - if (!addr.isEmpty()) { - QString str; - writer->writeStartElement(QStringLiteral("gpxx:Address")); + writer->stackTextElement(QStringLiteral("gpxx:DisplayMode"), cx); + } - if (!(str = garmin_fs_t::get_addr(gmsd, nullptr)).isEmpty()) { - writer->writeTextElement(QStringLiteral("gpxx:StreetAddress"), str); - } - if (!(str = garmin_fs_t::get_city(gmsd, nullptr)).isEmpty()) { - writer->writeTextElement(QStringLiteral("gpxx:City"), str); - } - if (!(str = garmin_fs_t::get_state(gmsd, nullptr)).isEmpty()) { - writer->writeTextElement(QStringLiteral("gpxx:State"), str); - } - if (!(str = garmin_fs_t::get_country(gmsd, nullptr)).isEmpty()) { - writer->writeTextElement(QStringLiteral("gpxx:Country"), str); + if (garmin_fs_t::has_category(gmsd)) { + uint16_t cx = gmsd->category; + writer->stackStartElement(QStringLiteral("gpxx:Categories")); + for (int i = 0; i < 16; i++) { + if (cx & 1) { + writer->stackTextElement(QStringLiteral("gpxx:Category"), QStringLiteral("Category %1").arg(i+1)); } - if (!(str = garmin_fs_t::get_postal_code(gmsd, nullptr)).isEmpty()) { - writer->writeTextElement(QStringLiteral("gpxx:PostalCode"), str); - } - writer->writeEndElement(); // /gpxx::Address + cx = cx >> 1; } + writer->stackEndElement(); // gpxx:Categories + } - if (!phone.isEmpty()) { - writer->writeTextElement(QStringLiteral("gpxx:PhoneNumber"), phone); - } + writer->stackOptionalStartElement(QStringLiteral("gpxx:Address")); + writer->stackOptionalTextElement(QStringLiteral("gpxx:StreetAddress"), garmin_fs_t::get_addr(gmsd, nullptr)); + writer->stackOptionalTextElement(QStringLiteral("gpxx:City"), garmin_fs_t::get_city(gmsd, nullptr)); + writer->stackOptionalTextElement(QStringLiteral("gpxx:State"), garmin_fs_t::get_state(gmsd, nullptr)); + writer->stackOptionalTextElement(QStringLiteral("gpxx:Country"), garmin_fs_t::get_country(gmsd, nullptr)); + writer->stackOptionalTextElement(QStringLiteral("gpxx:PostalCode"), garmin_fs_t::get_postal_code(gmsd, nullptr)); + writer->stackEndElement(); // gpxx:Address + + writer->stackOptionalTextElement(QStringLiteral("gpxx:PhoneNumber"), garmin_fs_t::get_phone_nr(gmsd, nullptr)); - writer->writeEndElement(); // /gpxx::WaypointExtension - writer->writeEndElement(); // /extensions. - } + writer->stackEndElement(); // gpxx:WaypointExtension + writer->stackEndElement(); // extensions } void garmin_fs_xml_convert(const int base_tag, int tag, const QString& qstr, Waypoint* waypt) { // FIXME: eliminate C string copy/use here: - const char *cdatastr = xstrdup(qstr); + const char* cdatastr = xstrdup(qstr); garmin_fs_t* gmsd = garmin_fs_t::find(waypt); if (gmsd == nullptr) { gmsd = garmin_fs_alloc(-1); diff --git a/garmin_fs.h b/garmin_fs.h index 25d3bbb18..d225339dc 100644 --- a/garmin_fs.h +++ b/garmin_fs.h @@ -24,14 +24,13 @@ #ifndef GARMIN_FS_H #define GARMIN_FS_H -#include // for size_t -#include // for int32_t, int16_t, uint16_t +#include // for int32_t, int16_t, uint16_t -#include // for QString -#include // for QXmlStreamWriter +#include // for QString #include "defs.h" -#include "formspec.h" // for FsChainFind, kFsGmsd, FormatSpecificData +#include "formspec.h" // for FsChainFind, kFsGmsd, FormatSpecificData +#include "src/core/xmlstreamwriter.h" // for XmlStreamWriter /* this order is used by most devices */ @@ -222,7 +221,7 @@ void garmin_fs_copy(void** dest, const void* src); /* for GPX */ void garmin_fs_xml_convert(int base_tag, int tag, const QString& qstr, Waypoint* waypt); -void garmin_fs_xml_fprint(const Waypoint* waypt, QXmlStreamWriter*); +void garmin_fs_xml_fprint(const Waypoint* waypt, gpsbabel::XmlStreamWriter*); /* common garmin_fs utilities */ diff --git a/gpx.cc b/gpx.cc index 9a127c7c4..ad5bfcaaa 100644 --- a/gpx.cc +++ b/gpx.cc @@ -21,6 +21,7 @@ #include "gpx.h" +#include // for assert #include // for lround #include // for sscanf #include // for strchr, strncpy @@ -573,10 +574,10 @@ GpxFormat::gpx_end(QStringView /*unused*/) gc_log_date = gpsbabel::DateTime(); break; case tt_cache_favorite_points: - wpt_tmp->AllocGCData()->favorite_points = cdatastr.toInt(); + wpt_tmp->AllocGCData()->favorite_points = cdatastr.toInt(); break; case tt_cache_personal_note: - wpt_tmp->AllocGCData()->personal_note = cdatastr; + wpt_tmp->AllocGCData()->personal_note = cdatastr; break; /* @@ -1047,7 +1048,7 @@ GpxFormat::qualifiedName() const void GpxFormat::read() { - for (bool atEnd = false; !reader->atEnd() && !atEnd;) { + for (bool atEnd = false; !reader->atEnd() && !atEnd;) { reader->readNext(); // do processing switch (reader->tokenType()) { @@ -1097,7 +1098,7 @@ GpxFormat::read() } } - if (reader->hasError()) { + if (reader->hasError()) { fatal(FatalMsg() << MYNAME << "Read error:" << reader->errorString() << "File:" << iqfile->fileName() << "Line:" << reader->lineNumber() @@ -1263,87 +1264,80 @@ GpxFormat::gpx_write_common_position(const Waypoint* waypointp, const gpx_point_ void GpxFormat::gpx_write_common_extensions(const Waypoint* waypointp, const gpx_point_type point_type) const { - // gpx version we are writing is >= 1.1. - garmin_fs_t* gmsd = (opt_garminext) ? garmin_fs_t::find(waypointp) : nullptr; // only needed if garmin extensions selected + assert(gpx_write_version >= gpx_1_1); - if ((opt_humminbirdext && (waypointp->depth_has_value() || waypointp->temperature_has_value())) || - (opt_garminext && gpxpt_route==point_type && gmsd != nullptr && gmsd->ilinks != nullptr) || - (opt_garminext && gpxpt_waypoint==point_type && (waypointp->proximity_has_value() || waypointp->temperature_has_value() || waypointp->depth_has_value())) || - (opt_garminext && gpxpt_track==point_type && (waypointp->temperature_has_value() || waypointp->depth_has_value() || waypointp->heartrate != 0 || waypointp->cadence != 0))) { - writer->writeStartElement(QStringLiteral("extensions")); - if (opt_humminbirdext) { - if (waypointp->depth_has_value()) { - writer->writeTextElement(QStringLiteral("h:depth"), toString(waypointp->depth_value() * 100.0)); + writer->stackOptionalStartElement(QStringLiteral("extensions")); + + if (opt_humminbirdext) { + if (waypointp->depth_has_value()) { + writer->stackTextElement(QStringLiteral("h:depth"), toString(waypointp->depth_value() * 100.0)); + } + if (waypointp->temperature_has_value()) { + writer->stackTextElement(QStringLiteral("h:temperature"), toString(waypointp->temperature_value())); + } + } + + if (opt_garminext) { + // Although not required by the schema we assume that gpxx:WaypointExtension must be a child of gpx:wpt. + // Although not required by the schema we assume that gpxx:RoutePointExtension must be a child of gpx:rtept. + // Although not required by the schema we assume that gpxx:TrackPointExtension must be a child of gpx:trkpt. + // Although not required by the schema we assume that gpxtpx:TrackPointExtension must be a child of gpx:trkpt. + switch (point_type) { + case gpxpt_waypoint: + writer->stackOptionalStartElement(QStringLiteral("gpxx:WaypointExtension")); + if (waypointp->proximity_has_value()) { + writer->stackTextElement(QStringLiteral("gpxx:Proximity"), toString(waypointp->proximity_value())); } if (waypointp->temperature_has_value()) { - writer->writeTextElement(QStringLiteral("h:temperature"), toString(waypointp->temperature_value())); + writer->stackTextElement(QStringLiteral("gpxx:Temperature"), toString(waypointp->temperature_value())); } - } - - if (opt_garminext) { - // Although not required by the schema we assume that gpxx:WaypointExtension must be a child of gpx:wpt. - // Although not required by the schema we assume that gpxx:RoutePointExtension must be a child of gpx:rtept. - // Although not required by the schema we assume that gpxx:TrackPointExtension must be a child of gpx:trkpt. - // Although not required by the schema we assume that gpxtpx:TrackPointExtension must be a child of gpx:trkpt. - switch (point_type) { - case gpxpt_waypoint: - if (waypointp->proximity_has_value() || waypointp->temperature_has_value() || waypointp->depth_has_value()) { - writer->writeStartElement(QStringLiteral("gpxx:WaypointExtension")); - if (waypointp->proximity_has_value()) { - writer->writeTextElement(QStringLiteral("gpxx:Proximity"), toString(waypointp->proximity_value())); - } - if (waypointp->temperature_has_value()) { - writer->writeTextElement(QStringLiteral("gpxx:Temperature"), toString(waypointp->temperature_value())); - } - if (waypointp->depth_has_value()) { - writer->writeTextElement(QStringLiteral("gpxx:Depth"), toString(waypointp->depth_value())); - } - writer->writeEndElement(); // "gpxx:WaypointExtension" - } - break; - case gpxpt_route: - if (gmsd != nullptr && gpxpt_route==point_type && gmsd->ilinks != nullptr) { - writer->writeStartElement(QStringLiteral("gpxx:RoutePointExtension")); - garmin_ilink_t* link = gmsd->ilinks; - garmin_ilink_t* prior = nullptr; // GDB files sometime contain repeated point; omit them - while (link != nullptr) { - if (prior == nullptr || prior->lat != link->lat || prior->lon != link->lon) { - writer->writeStartElement(QStringLiteral("gpxx:rpt")); - writer->writeAttribute(QStringLiteral("lat"), toString(link->lat)); - writer->writeAttribute(QStringLiteral("lon"), toString(link->lon)); - writer->writeEndElement(); // "gpxx:rpt" - } - prior = link; - link = link->next; - } - writer->writeEndElement(); // "gpxx:RoutePointExtension" - } - break; - case gpxpt_track: - if (waypointp->temperature_has_value() || waypointp->depth_has_value() || waypointp->heartrate != 0 || waypointp->cadence != 0) { - // gpxtpx:TrackPointExtension is a replacement for gpxx:TrackPointExtension. - writer->writeStartElement(QStringLiteral("gpxtpx:TrackPointExtension")); - if (waypointp->temperature_has_value()) { - writer->writeTextElement(QStringLiteral("gpxtpx:atemp"), toString(waypointp->temperature_value())); - } - if (waypointp->depth_has_value()) { - writer->writeTextElement(QStringLiteral("gpxtpx:depth"), toString(waypointp->depth_value())); - } - if (waypointp->heartrate != 0) { - writer->writeTextElement(QStringLiteral("gpxtpx:hr"), QString::number(waypointp->heartrate)); - } - if (waypointp->cadence != 0) { - writer->writeTextElement(QStringLiteral("gpxtpx:cad"), QString::number(waypointp->cadence)); + if (waypointp->depth_has_value()) { + writer->stackTextElement(QStringLiteral("gpxx:Depth"), toString(waypointp->depth_value())); + } + writer->stackEndElement(); // gpxx:WaypointExtension + break; + case gpxpt_route: { + garmin_fs_t* gmsd = garmin_fs_t::find(waypointp); + if (gmsd != nullptr && gmsd->ilinks != nullptr) { + writer->stackOptionalStartElement(QStringLiteral("gpxx:RoutePointExtension")); + garmin_ilink_t* link = gmsd->ilinks; + garmin_ilink_t* prior = nullptr; // GDB files sometime contain repeated point; omit them + while (link != nullptr) { + if (prior == nullptr || prior->lat != link->lat || prior->lon != link->lon) { + writer->stackStartElement(QStringLiteral("gpxx:rpt")); + writer->stackAttribute(QStringLiteral("lat"), toString(link->lat)); + writer->stackAttribute(QStringLiteral("lon"), toString(link->lon)); + writer->stackEndElement(); // "gpxx:rpt" } - writer->writeEndElement(); // "gpxtpx:TrackPointExtension" + prior = link; + link = link->next; } - break; + writer->stackEndElement(); // gpxx:RoutePointExtension } } - - writer->writeEndElement(); // "extensions" + break; + case gpxpt_track: + // gpxtpx:TrackPointExtension is a replacement for gpxx:TrackPointExtension. + writer->stackOptionalStartElement(QStringLiteral("gpxtpx:TrackPointExtension")); + if (waypointp->temperature_has_value()) { + writer->stackTextElement(QStringLiteral("gpxtpx:atemp"), toString(waypointp->temperature_value())); + } + if (waypointp->depth_has_value()) { + writer->stackTextElement(QStringLiteral("gpxtpx:depth"), toString(waypointp->depth_value())); + } + if (waypointp->heartrate != 0) { + writer->stackTextElement(QStringLiteral("gpxtpx:hr"), QString::number(waypointp->heartrate)); + } + if (waypointp->cadence != 0) { + writer->stackTextElement(QStringLiteral("gpxtpx:cad"), QString::number(waypointp->cadence)); + } + writer->stackEndElement(); // gpxtpx:TrackPointExtension + break; + } } + + writer->stackEndElement(); // "extensions" } void diff --git a/src/core/xmlstreamwriter.cc b/src/core/xmlstreamwriter.cc index 307dc6ffb..1ce72f1c3 100644 --- a/src/core/xmlstreamwriter.cc +++ b/src/core/xmlstreamwriter.cc @@ -23,6 +23,8 @@ #include // for QXmlStreamWriter #include // for QT_VERSION, QT_VERSION_CHECK +#include "defs.h" + // As this code began in C, we have several hundred places that write // c strings. Add a test that the string contains anything useful // before serializing an empty tag. @@ -41,7 +43,96 @@ namespace gpsbabel { -// Dont emit the element if there's nothing interesting in it. +XmlStreamWriter::xml_stack_list_entry_t& XmlStreamWriter::activeStack() +{ + if (stack_list.isEmpty()) { + fatal("xmlstreamwriter: programming error: the stack* functions are used incorrectly."); + } + return stack_list.last(); +} + +void XmlStreamWriter::stackAttribute(const QString& name, const QString& value) +{ + activeStack().append(xml_command(xml_wrt_cmd_t::attribute, name, value)); +} + +void XmlStreamWriter::stackEndElement() +{ + xml_stack_list_entry_t& active_stack = activeStack(); + active_stack.append(xml_command(xml_wrt_cmd_t::end_element)); + + // Has the active_stack OptionalStartElement been paired with an EndElement? + if (active_stack.element_depth == 0) { // yes + const xml_stack_list_entry_t completed_stack = stack_list.takeLast(); + // Does the completed_stack OptionalStartElement have any content? + if (completed_stack.element_count > 1) { // yes + // Is this the initial OptionalStartElement? + if (!stack_list.isEmpty()) { // no. append stack contents to parent. + stack_list.last().append(completed_stack); + } else { // yes. write the stack contents. + for (const auto& command: completed_stack.stack) { + switch (command.type) { + case xml_wrt_cmd_t::start_element: + QXmlStreamWriter::writeStartElement(command.name); + break; + case xml_wrt_cmd_t::attribute: + QXmlStreamWriter::writeAttribute(command.name, command.value); + break; + case xml_wrt_cmd_t::name_space: + QXmlStreamWriter::writeNamespace(command.name, command.value); + break; + case xml_wrt_cmd_t::text_element: + QXmlStreamWriter::writeTextElement(command.name, command.value); + break; + case xml_wrt_cmd_t::end_element: + QXmlStreamWriter::writeEndElement(); + break; + } + } + } + } // else {no. empty OptionalStartElement is discarded.} + } +} + +void XmlStreamWriter::stackNamespace(const QString& namespaceUri, const QString& prefix) +{ + activeStack().append(xml_command(xml_wrt_cmd_t::name_space, namespaceUri, prefix)); +} + +/* + * Start an element that will be written if and only if it has children. + * + * Usage: + * 1. stackOptionalStartElement must be the first stack*() method called. + * 2. stackOptionalStartElement must be paired with a subsequent + * stackEndElement. + * 3. write*() methods should not be called until the initial optional start + * element has paired with a subsequent stackEndElement. + */ +void XmlStreamWriter::stackOptionalStartElement(const QString& name) +{ + stack_list.append(xml_stack_list_entry_t()); + stackStartElement(name); +} + +void XmlStreamWriter::stackOptionalTextElement(const QString& name, const QString& text) +{ + if (!text.isEmpty()) { + stackTextElement(name, text); + } +} + +void XmlStreamWriter::stackStartElement(const QString& name) +{ + activeStack().append(xml_command(xml_wrt_cmd_t::start_element, name)); +} + +void XmlStreamWriter::stackTextElement(const QString& name, const QString& text) +{ + activeStack().append(xml_command(xml_wrt_cmd_t::text_element, name, text)); +} + +// Don't emit the element if there's nothing interesting in it. void XmlStreamWriter::writeOptionalTextElement(const QString& qualifiedName, const QString& text) { if (!text.isEmpty()) { diff --git a/src/core/xmlstreamwriter.h b/src/core/xmlstreamwriter.h index 24097da6b..87b236e80 100644 --- a/src/core/xmlstreamwriter.h +++ b/src/core/xmlstreamwriter.h @@ -20,8 +20,10 @@ #ifndef XMLSTREAMWRITER_H #define XMLSTREAMWRITER_H -#include // for QString -#include // for QXmlStreamWriter +#include // for QList +#include // for QString +#include // for QXmlStreamWriter +#include namespace gpsbabel { @@ -31,7 +33,82 @@ class XmlStreamWriter : public QXmlStreamWriter public: using QXmlStreamWriter::QXmlStreamWriter; + /* Member Functions */ + + void stackAttribute(const QString& name, const QString& value); + void stackEndElement(); + void stackNamespace(const QString& namespaceUri, const QString& prefix); + void stackOptionalStartElement(const QString& name); + void stackOptionalTextElement(const QString& name, const QString& text); + void stackStartElement(const QString& name); + void stackTextElement(const QString& name, const QString& text); + void writeOptionalTextElement(const QString& qualifiedName, const QString& text); + +private: + /* Types */ + + enum class xml_wrt_cmd_t { + start_element, + attribute, + name_space, + text_element, + end_element + }; + + struct xml_command { + explicit xml_command(xml_wrt_cmd_t t, + QString n = QString(), + QString v = QString()) + : type(t), name(std::move(n)), value(std::move(v)) {}; + + xml_wrt_cmd_t type; + QString name; + QString value; + }; + + using xml_stack_t = QList; + + struct xml_stack_list_entry_t { + void append(const xml_stack_list_entry_t& other) + { + stack.append(other.stack); + element_count += other.element_count; + }; + + void append(const xml_command& cmd) + { + stack.append(cmd); + switch (cmd.type) { + case xml_wrt_cmd_t::start_element: + ++element_count; + ++element_depth; + break; + case xml_wrt_cmd_t::text_element: + ++element_count; + break; + case xml_wrt_cmd_t::end_element: + --element_depth; + break; + case xml_wrt_cmd_t::attribute: + case xml_wrt_cmd_t::name_space: + break; + } + }; + + xml_stack_t stack; + int element_count{0}; + int element_depth{0}; + }; + + /* Member Functions */ + + xml_stack_list_entry_t& activeStack(); + + /* Data Members */ + + QList stack_list; + }; } // namespace gpsbabel