Skip to main content.
Find on this site:

Me as a baby.Mark A. Taff
Location: Home Code Viewer
  1.  /***************************************************************************
  2.   * Copyright (C) 2007 Mark A. Taff <kde@marktaff.com> *
  3.   * *
  4.   * This program is free software; you can redistribute it and/or modify *
  5.   * it under the terms of the GNU Library General Public License *
  6.   * version 2 as published by the Free Software Foundation *
  7.   * *
  8.   * This program is distributed in the hope that it will be useful, *
  9.   * but WITHOUT ANY WARRANTY; without even the implied warranty of *
  10.   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
  11.   * GNU Library General Public License for more details. *
  12.   * *
  13.   * You should have received a copy of the GNU Library General Public *
  14.   * License along with this program; if not, write to the *
  15.   * Free Software Foundation, Inc., *
  16.   * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
  17.   ***************************************************************************/
  18.  
  19.  #include "kgamesvgdocument.h"
  20.  #include "kgamesvgdocument_p.h"
  21.  
  22.  #include <kfilterdev.h>
  23.  #include <kdebug.h>
  24.  
  25.  #include <QtCore/QFile>
  26.  #include <QtCore/QString>
  27.  #include <QtCore/QStringList>
  28.  #include <QtXml/QDomElement>
  29.  #include <QtXml/QDomNode>
  30.  
  31.  #include <math.h>
  32.  
  33.  //
  34.  // Public
  35.  //
  36.  
  37.  /**
  38.   * @brief A class holding private members for KGameSvgDocument
  39.   *
  40.   * @see KGameSvgDocument
  41.   * @author Mark A. Taff \<kde@marktaff.com\>
  42.   * @version 0.1
  43.   */
  44.  class KGameSvgDocumentPrivate
  45.  {
  46.   public:
  47.  
  48.   /**
  49.   * @brief Instantiates a KGameSvgDocumentPrivate object
  50.   */
  51.   KGameSvgDocumentPrivate()
  52.   {}
  53.  
  54.   ~KGameSvgDocumentPrivate()
  55.   {}
  56.  
  57.   /**
  58.   * @brief Performs a preorder traversal of the DOM tree to find element matching @c attributeName & @c attributeValue
  59.   *
  60.   * @param attributeName The name of the attribute to find
  61.   * @param attributeValue The value of the @p attributeName attribute to find
  62.   * @param node The node to start the traversal from.
  63.   * @returns the node with id of @c elementId. If no node has that id, returns a null node.
  64.   */
  65.   QDomNode findElementById(const QString& attributeName, const QString& attributeValue, const QDomNode& node);
  66.  
  67.   /**
  68.   * @brief Returns the current element
  69.   * @returns The current element
  70.   */
  71.   QDomElement currentElement() const;
  72.  
  73.   /**
  74.   * @brief Sets the current element
  75.   *
  76.   * @returns nothing
  77.   */
  78.   void setCurrentElement();
  79.  
  80.   /**
  81.   * @brief Returns whether the original style attribute has a trailing semicolon
  82.   * @returns whether the original style attribute has a trailing semicolon
  83.   */
  84.   bool styleHasTrailingSemicolon() const;
  85.  
  86.   /**
  87.   * @brief Sets whether the original style attribute has a trailing semicolon
  88.   *
  89.   * @param hasSemicolon whether the original style attribute has a trailing semicolon
  90.   * @returns nothing
  91.   */
  92.   void setStyleHasTrailingSemicolon(bool hasSemicolon);
  93.  
  94.   /**
  95.   * @brief The last node found by elementById, or a null node if not found.
  96.   */
  97.   QDomNode m_currentNode;
  98.  
  99.   /**
  100.   * @brief The current node turned into an element.
  101.   */
  102.   QDomElement m_currentElement;
  103.  
  104.   /**
  105.   * @brief The order Inkscape write properties in the style attribute of an element.
  106.   *
  107.   * Inkscape order is defined as:
  108.   * "fill", "fill-opacity", "fill-rule", "stroke", "stroke-width", "stroke-linecap",
  109.   * "stroke-linejoin", "stroke-miterlimit", "stroke-dasharray", "stroke-opacity"
  110.   */
  111.   QStringList m_inkscapeOrder;
  112.  
  113.   /**
  114.   * @brief The xml that must be prepended to a node to make it a valid svg document
  115.   *
  116.   * Defined as: <?xml version="1.0" encoding="UTF-8" standalone="no"?>\<svg\>
  117.   */
  118.   static const QString SVG_XML_PREPEND;
  119.  
  120.   /**
  121.   * @brief The xml that must be appended to a node to make it a valid svg document
  122.   *
  123.   * Defined as: \</svg\>
  124.   */
  125.   static const QString SVG_XML_APPEND;
  126.  
  127.   /**
  128.   * @brief The filename of the SVG file to open.
  129.   */
  130.   QString m_svgFilename;
  131.  
  132.   /**
  133.   * @brief Whether the style attribute has a trailing semicolon
  134.   */
  135.   bool m_hasSemicolon;
  136.  
  137.  
  138.  };
  139.  
  140.  const QString KGameSvgDocumentPrivate::SVG_XML_PREPEND = QString("<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?><svg>");
  141.  const QString KGameSvgDocumentPrivate::SVG_XML_APPEND = QString("</svg>");
  142.  
  143.  KGameSvgDocument::KGameSvgDocument()
  144.   : QDomDocument(), d(new KGameSvgDocumentPrivate)
  145.  {}
  146.  
  147.  KGameSvgDocument::KGameSvgDocument(const KGameSvgDocument &doc)
  148.   : QDomDocument(), d(new KGameSvgDocumentPrivate(*doc.d))
  149.  {
  150.  }
  151.  
  152.  KGameSvgDocument::~KGameSvgDocument()
  153.  {
  154.   delete d;
  155.  }
  156.  
  157.  KGameSvgDocument& KGameSvgDocument::operator=(const KGameSvgDocument &doc)
  158.  {
  159.   QDomDocument::operator=(doc);
  160.   *d = *doc.d;
  161.   return *this;
  162.  }
  163.  
  164.  QDomNode KGameSvgDocument::elementByUniqueAttributeValue(const QString& attributeName, const QString& attributeValue)
  165.  {
  166.   /* DOM is always "live", so there maybe a new root node. We always have to ask for the
  167.   * root node instead of keeping a pointer to it.
  168.   */
  169.   QDomElement docElem = documentElement();
  170.   QDomNode n = docElem.firstChild();
  171.  
  172.   QDomNode node = d->findElementById(attributeName, attributeValue, n);
  173.   setCurrentNode(node);
  174.   return node;
  175.  }
  176.  
  177.  QDomNode KGameSvgDocument::elementById(const QString& attributeValue)
  178.  {
  179.   return elementByUniqueAttributeValue("id", attributeValue);
  180.  }
  181.  
  182.  void KGameSvgDocument::load()
  183.  {
  184.   if (d->m_svgFilename.isNull())
  185.   {
  186.   kDebug(11000) << "KGameSvgDocument::load(): Filename not specified." << endl;
  187.   return;
  188.   }
  189.  
  190.   QFile file(d->m_svgFilename);
  191.   if (!file.open(QIODevice::ReadOnly))
  192.   {
  193.   return;
  194.   }
  195.  
  196.   // Reads file whether it is compressed or not
  197.   QIODevice *filter = KFilterDev::device( &file, QString::fromUtf8("application/x-gzip"), false);
  198.   if (!filter)
  199.   {
  200.   return;
  201.   }
  202.   delete filter;
  203.  
  204.   if (!setContent(&file))
  205.   {
  206.   file.close();
  207.   kDebug(11000) << "DOM content not set." << endl;
  208.   return;
  209.   }
  210.   file.close();
  211.  }
  212.  
  213.  void KGameSvgDocument::load(const QString& svgFilename)
  214.  {
  215.   setSvgFilename(svgFilename);
  216.   load();
  217.  }
  218.  
  219.  void KGameSvgDocument::rotate(double degrees, const MatrixOptions& options)
  220.  {
  221.   QMatrix matrix;
  222.  
  223.   if (options == ApplyToCurrentMatrix)
  224.   {
  225.   matrix = transformMatrix().QMatrix::rotate(degrees);
  226.   }
  227.   else
  228.   {
  229.   matrix = QMatrix();
  230.   matrix.QMatrix::rotate(degrees);
  231.   }
  232.   setTransformMatrix(matrix, ReplaceCurrentMatrix);
  233.  }
  234.  
  235.  void KGameSvgDocument::translate(int xPixels, int yPixels, const MatrixOptions& options)
  236.  {
  237.   QMatrix matrix;
  238.  
  239.   if (options == ApplyToCurrentMatrix)
  240.   {
  241.   matrix = transformMatrix().QMatrix::translate(xPixels, yPixels);
  242.   }
  243.   else
  244.   {
  245.   matrix = QMatrix();
  246.   matrix.QMatrix::translate(xPixels, yPixels);
  247.   }
  248.   setTransformMatrix(matrix, ReplaceCurrentMatrix);
  249.  }
  250.  
  251.  void KGameSvgDocument::shear(double xRadians, double yRadians, const MatrixOptions& options)
  252.  {
  253.   QMatrix matrix;
  254.  
  255.   if (options == ApplyToCurrentMatrix)
  256.   {
  257.   matrix = transformMatrix().QMatrix::shear(xRadians, yRadians);
  258.   }
  259.   else
  260.   {
  261.   matrix = QMatrix();
  262.   matrix.QMatrix::shear(xRadians, yRadians);
  263.   }
  264.   setTransformMatrix(matrix, ReplaceCurrentMatrix);
  265.  }
  266.  
  267.  void KGameSvgDocument::skew(double xDegrees, double yDegrees, const MatrixOptions& options)
  268.  {
  269.   double xRadians = xDegrees * (M_PI / 180);
  270.   double yRadians = yDegrees * (M_PI / 180);
  271.  
  272.   shear(xRadians, yRadians, options);
  273.  }
  274.  
  275.  void KGameSvgDocument::scale(double xFactor, double yFactor, const MatrixOptions& options)
  276.  {
  277.   QMatrix matrix;
  278.   if ((xFactor == 0) || (yFactor == 0))
  279.   {
  280.   kWarning () << "KGameSvgDocument::scale: You cannnot scale by zero" << endl;
  281.   }
  282.  
  283.   if (options == ApplyToCurrentMatrix)
  284.   {
  285.   matrix = transformMatrix().QMatrix::scale(xFactor, yFactor);
  286.   }
  287.   else
  288.   {
  289.   matrix = QMatrix();
  290.   matrix.QMatrix::scale(xFactor, yFactor);
  291.   }
  292.   setTransformMatrix(matrix, ReplaceCurrentMatrix);
  293.  }
  294.  
  295.  QDomNode KGameSvgDocument::currentNode() const
  296.  {
  297.   return d->m_currentNode;
  298.  }
  299.  
  300.  void KGameSvgDocument::setCurrentNode(const QDomNode& node)
  301.  {
  302.   d->m_currentNode = node;
  303.   d->setCurrentElement();
  304.  }
  305.  
  306.  QString KGameSvgDocument::svgFilename() const
  307.  {
  308.   return d->m_svgFilename;
  309.  }
  310.  
  311.  void KGameSvgDocument::setSvgFilename(const QString& svgFilename)
  312.  {
  313.   d->m_svgFilename = svgFilename;
  314.  }
  315.  
  316.  QString KGameSvgDocument::styleProperty(const QString& propertyName) const
  317.  {
  318.   return styleProperties().value(propertyName);
  319.  }
  320.  
  321.  void KGameSvgDocument::setStyleProperty(const QString& propertyName, const QString& propertyValue)
  322.  {
  323.   QHash<QString, QString> properties;
  324.  
  325.   properties = styleProperties();
  326.   properties.insert(propertyName, propertyValue);
  327.  
  328.   setStyleProperties(properties, UseInkscapeOrder);
  329.  }
  330.  
  331.  QString KGameSvgDocument::nodeToSvg() const
  332.  {
  333.   QString s, t, xml, defs, pattern;
  334.   QTextStream str(&s);
  335.   QTextStream str_t(&t);
  336.   QStringList defsAdded;
  337.   int result = 0;
  338.   QRegExp rx;
  339.  
  340.   currentNode().save(str, 1);
  341.   xml = *str.string();
  342.  
  343.   // Find and add any required gradients or patterns
  344.   pattern = "url" + WSP_ASTERISK + OPEN_PARENS + WSP_ASTERISK + "#(.*)" + WSP_ASTERISK + CLOSE_PARENS;
  345.   rx.setPattern(pattern);
  346.   if (rx.indexIn(xml, result) != -1)
  347.   {
  348.   QDomNode node, nodeBase;
  349.   QString baseId;
  350.   QDomNode n = def();
  351.  
  352.   result = 0;
  353.   while ((result = rx.indexIn(xml, result)) != -1)
  354.   {
  355.   // Find the pattern or gradient referenced
  356.   result += rx.matchedLength();
  357.   if (!defsAdded.contains(rx.cap(1)))
  358.   {
  359.   node = d->findElementById("id", rx.cap(1), n);
  360.   node.save(str_t, 1);
  361.   defsAdded.append(rx.cap(1));
  362.   }
  363.  
  364.   // Find the gradient the above gradient is based on
  365.   baseId = node.toElement().attribute("xlink:href").mid(1);
  366.   if (!defsAdded.contains(baseId))
  367.   {
  368.   nodeBase = d->findElementById("id", baseId, n);
  369.   nodeBase.save(str_t, 1);
  370.   defsAdded.append(baseId);
  371.   }
  372.   }
  373.   defs = *str_t.string();
  374.   defs = "<defs>" + defs + "</defs>";
  375.   }
  376.  
  377.   // Need to make node be a real svg document, so prepend and append required tags.
  378.   xml = d->SVG_XML_PREPEND + defs + xml + d->SVG_XML_APPEND;
  379.   return xml;
  380.  }
  381.  
  382.  QByteArray KGameSvgDocument::nodeToByteArray() const
  383.  {
  384.   return nodeToSvg().toUtf8();
  385.  }
  386.  
  387.  QString KGameSvgDocument::style() const
  388.  {
  389.   return d->m_currentElement.attribute( "style", "Element has no style attribute.");
  390.  }
  391.  
  392.  void KGameSvgDocument::setStyle(const QString& styleAttribute)
  393.  {
  394.   d->m_currentElement.setAttribute("style", styleAttribute);
  395.  }
  396.  
  397.  QDomNodeList KGameSvgDocument::patterns() const
  398.  {
  399.   return elementsByTagName("pattern");
  400.  }
  401.  
  402.  QDomNodeList KGameSvgDocument::linearGradients() const
  403.  {
  404.   return elementsByTagName("linearGradient");
  405.  }
  406.  
  407.  QDomNodeList KGameSvgDocument::radialGradients() const
  408.  {
  409.   return elementsByTagName("radialGradient");
  410.  }
  411.  
  412.  QDomNodeList KGameSvgDocument::defs() const
  413.  {
  414.   return elementsByTagName("defs");
  415.  }
  416.  
  417.  QDomNode KGameSvgDocument::def() const
  418.  {
  419.   return defs().at(0);
  420.  }
  421.  
  422.  QString KGameSvgDocument::transform() const
  423.  {
  424.   return d->m_currentElement.attribute( "transform", "Element has no transform attribute.");
  425.  }
  426.  
  427.  void KGameSvgDocument::setTransform(const QString& transformAttribute)
  428.  {
  429.   d->m_currentElement.setAttribute("transform", transformAttribute);
  430.  }
  431.  
  432.  QHash<QString, QString> KGameSvgDocument::styleProperties() const
  433.  {
  434.   QHash<QString, QString> stylePropertiesHash;
  435.   QStringList styleProperties, keyValuePair;
  436.   QString styleProperty;
  437.  
  438.   styleProperties = style().split(";");
  439.  
  440.   /* The style attr may have a trailing semi-colon. If it does, split()
  441.   * gives us an empty final element. Remove it or we get 'index out of range' errors
  442.   */
  443.   if (styleProperties.at((styleProperties.count()-1)).isEmpty())
  444.   {
  445.   styleProperties.removeAt((styleProperties.count()-1));
  446.   d->setStyleHasTrailingSemicolon(true);
  447.   }
  448.   else {d->setStyleHasTrailingSemicolon(false);}
  449.  
  450.   for (int i = 0; i < styleProperties.size(); i++)
  451.   {
  452.   styleProperty = styleProperties.at(i);
  453.   keyValuePair = styleProperty.split(":");
  454.   stylePropertiesHash.insert(keyValuePair.at(0), keyValuePair.at(1));
  455.   }
  456.   return stylePropertiesHash;
  457.  }
  458.  
  459.  void KGameSvgDocument::setStyleProperties(const QHash<QString, QString>& _styleProperties, const StylePropertySortOptions& options)
  460.  {
  461.   QHash<QString, QString> styleProperties = _styleProperties;
  462.   QString styleBuffer, property;
  463.  
  464.   d->m_inkscapeOrder << "fill" << "fill-opacity" << "fill-rule" << "stroke" << "stroke-width" << "stroke-linecap"
  465.   << "stroke-linejoin" << "stroke-miterlimit" << "stroke-dasharray" << "stroke-opacity";
  466.  
  467.   if (options == UseInkscapeOrder)
  468.   {
  469.   for (int i = 0; i < d->m_inkscapeOrder.size(); i++)
  470.   {
  471.   property = d->m_inkscapeOrder.at(i);
  472.   if (styleProperties.contains(property))
  473.   {
  474.   styleBuffer += property + ':' + styleProperties.take(property) + ';';
  475.   }
  476.   else
  477.   {
  478.   // Do Nothing
  479.   }
  480.   }
  481.   }
  482.  
  483.   // Append any style properties
  484.   if (!styleProperties.isEmpty())
  485.   {
  486.   QHashIterator<QString, QString> it(styleProperties);
  487.   while (it.hasNext())
  488.   {
  489.   it.next();
  490.   styleBuffer += it.key() + ':' + it.value() + ';';
  491.   }
  492.   }
  493.  
  494.   // Remove trailing semicolon if original didn't have one
  495.   if (!d->styleHasTrailingSemicolon()) {styleBuffer.chop(1);}
  496.   setStyle(styleBuffer);
  497.  }
  498.  
  499.  QMatrix KGameSvgDocument::transformMatrix() const
  500.  {
  501.   /*
  502.   * Transform attributes can be quite complex. Here, we assemble this tangled web of
  503.   * complexity into an single matrix.
  504.   *
  505.   * The regex's that make this bearable live in kgamesvgdocument_p.h. As these regex's
  506.   * get quite complex, we have some code in tests/kgamesvgdocumenttest.cpp to help verify
  507.   * they are still correct after being edited.
  508.   *
  509.   * Warning: This code depends on the capturing parenthesis in the regex's not changing.
  510.   *
  511.   * For all the gory details, see http://www.w3.org/TR/SVG/coords.html#TransformAttribute
  512.   */
  513.   QRegExp rx;
  514.   QString transformAttribute;
  515.   int result;
  516.   int i = 0;
  517.   QMatrix baseMatrix = QMatrix();
  518.  
  519.   transformAttribute = transform();
  520.   if (transformAttribute == "Element has no transform attribute.")
  521.   {
  522.   return QMatrix();
  523.   }
  524.   transformAttribute.trimmed();
  525.  
  526.   rx.setPattern(TRANSFORMS);
  527.   if (!rx.exactMatch(transformAttribute))
  528.   {
  529.   kWarning () << "Transform attribute seems to be invalid. Check your SVG file." << endl;
  530.   return QMatrix();
  531.   }
  532.  
  533.   rx.setPattern(TRANSFORM);
  534.  
  535.   while (transformAttribute.size() > 0 && i < 32) // 32 is an arbitrary limit for the number of transforms for a single node
  536.   {
  537.   result = rx.indexIn(transformAttribute);
  538.   if (result != -1) // Found left-most transform
  539.   {
  540.   if (rx.cap(1) == "matrix")
  541.   {
  542.   // If the first transform found is a matrix, use it as the base,
  543.   // else we use a null matrix.
  544.   if (i == 0)
  545.   {
  546.   baseMatrix = QMatrix(rx.cap(2).toDouble(), rx.cap(3).toDouble(), rx.cap(4).toDouble(),
  547.   rx.cap(5).toDouble(), rx.cap(6).toDouble(), rx.cap(7).toDouble());
  548.   }
  549.   else
  550.   {
  551.   baseMatrix = QMatrix(rx.cap(2).toDouble(), rx.cap(3).toDouble(), rx.cap(4).toDouble(),
  552.   rx.cap(5).toDouble(), rx.cap(6).toDouble(), rx.cap(7).toDouble()) * baseMatrix;
  553.   }
  554.   }
  555.  
  556.   if (rx.cap(8) == "translate")
  557.   {
  558.   double x = rx.cap(9).toDouble();
  559.   double y = rx.cap(10).toDouble();
  560.   if (rx.cap(10).isEmpty()) // y defaults to zero per SVG standard
  561.   {
  562.   y = 0;
  563.   }
  564.   baseMatrix = baseMatrix.translate(x, y);
  565.   }
  566.  
  567.   if (rx.cap(11) == "scale")
  568.   {
  569.   double x = rx.cap(12).toDouble();
  570.   double y = rx.cap(12).toDouble();
  571.   if (rx.cap(13).isEmpty()) // y defaults to x per SVG standard
  572.   {
  573.   y = x;
  574.   }
  575.   baseMatrix = baseMatrix.scale(x, y);
  576.   }
  577.  
  578.   if (rx.cap(14) == "rotate")
  579.   {
  580.   double a = rx.cap(15).toDouble();
  581.   double cx = rx.cap(16).toDouble();
  582.   double cy = rx.cap(17).toDouble();
  583.  
  584.   if ((cx > 0) || (cy > 0)) // rotate around point (cx, cy)
  585.   {
  586.   baseMatrix.translate(cx, cy);
  587.   baseMatrix.rotate(a);
  588.   baseMatrix.translate((cx * -1), (cy * -1));
  589.   }
  590.   else
  591.   {
  592.   baseMatrix = baseMatrix.rotate(a); // rotate around origin
  593.   }
  594.   }
  595.  
  596.   if (rx.cap(18) == "skewX")
  597.   {
  598.   baseMatrix = baseMatrix.shear(rx.cap(19).toDouble() * (M_PI / 180), 0);
  599.   }
  600.  
  601.   if (rx.cap(20) == "skewY")
  602.   {
  603.   baseMatrix = baseMatrix.shear(0, rx.cap(21).toDouble() * (M_PI / 180));
  604.   }
  605.   }
  606.   transformAttribute = transformAttribute.mid(rx.matchedLength() + result);
  607.   i++;
  608.   }
  609.  
  610.   return baseMatrix;
  611.  }
  612.  
  613.  void KGameSvgDocument::setTransformMatrix(QMatrix& matrix, const MatrixOptions& options)
  614.  {
  615.   QString transformBuffer, tmp;
  616.   QMatrix null = QMatrix();
  617.  
  618.   if (options == ApplyToCurrentMatrix)
  619.   {
  620.   matrix = transformMatrix() * matrix;
  621.   }
  622.  
  623.   transformBuffer = "matrix(";
  624.   transformBuffer += tmp.setNum(matrix.m11(),'g',7) + ',';
  625.   transformBuffer += tmp.setNum(matrix.m12(),'g',7) + ',';
  626.   transformBuffer += tmp.setNum(matrix.m21(),'g',7) + ',';
  627.   transformBuffer += tmp.setNum(matrix.m22(),'g',7) + ',';
  628.   transformBuffer += tmp.setNum(matrix.dx(),'g',7) + ',';
  629.   transformBuffer += tmp.setNum(matrix.dy(),'g',7) + ')';
  630.  
  631.   if ((transform() == "Element has no transform attribute.") && (matrix == null))
  632.   {
  633.   // Do not write a meaningless matrix to DOM
  634.   }
  635.   else
  636.   {
  637.   setTransform(transformBuffer);
  638.   }
  639.  }
  640.  
  641.  
  642.  //
  643.  // Private
  644.  //
  645.  
  646.  QDomNode KGameSvgDocumentPrivate::findElementById(const QString& attributeName, const QString& attributeValue, const QDomNode& node)
  647.  {
  648.   QDomElement e = node.toElement(); // try to convert the node to an element.
  649.   QString value = e.attribute( attributeName, "Element has no attribute with that name.");
  650.  
  651.   if (value == attributeValue)
  652.   {
  653.   // We found our node. Stop recursion and return it.
  654.   return node;
  655.   }
  656.  
  657.   if (!node.firstChild().isNull())
  658.   {
  659.   QDomNode result = findElementById(attributeName, attributeValue, node.firstChild());
  660.   /** We have recursed, now we need to have this recursion end when
  661.   * the function call above returns
  662.   */
  663.   if (!result.isNull()) return result; // If we found the node with id, then return it
  664.   }
  665.   if (!node.nextSibling().isNull())
  666.   {
  667.   QDomNode result = findElementById(attributeName, attributeValue, node.nextSibling());
  668.   /** We have recursed, now we need to have this recursion end when
  669.   * the function call above returns */
  670.   if (!result.isNull()) return result;
  671.   }
  672.   if (!node.firstChild().isNull() && !node.nextSibling().isNull())
  673.   {
  674.   // Do Nothing
  675.   //kDebug(11000) << "No children or siblings." << endl;
  676.   }
  677.  
  678.   // Matching node not found, so return a null node.
  679.   return QDomNode();
  680.  }
  681.  
  682.  QDomElement KGameSvgDocumentPrivate::currentElement() const
  683.  {
  684.   return m_currentElement;
  685.  }
  686.  
  687.  void KGameSvgDocumentPrivate::setCurrentElement()
  688.  {
  689.   m_currentElement = m_currentNode.toElement();
  690.  }
  691.  
  692.  bool KGameSvgDocumentPrivate::styleHasTrailingSemicolon() const
  693.  {
  694.   return m_hasSemicolon;
  695.  }
  696.  
  697.  void KGameSvgDocumentPrivate::setStyleHasTrailingSemicolon(bool hasSemicolon)
  698.  {
  699.   m_hasSemicolon = hasSemicolon;
  700.  }
CC-GNU GPL
Photos & software licensed under the CC-GNU GPL
unless otherwise noted in the JPEG comments field or source code.