Code

Node tool: fix moving multiple nodes along handles (Ctrl+Alt)
[inkscape.git] / src / xml / repr-util.cpp
index 75ae692af5c39ba2291ac47ee508f6488e180087..9405cde0186a63e57ced039c34786087b6f09536 100644 (file)
@@ -1,5 +1,3 @@
-#define __SP_REPR_UTIL_C__
-
 /** \file
  * Miscellaneous helpers for reprs.
  */
@@ -7,6 +5,7 @@
 /*
  * Authors:
  *   Lauris Kaplinski <lauris@ximian.com>
+ *   Jon A. Cruz <jon@joncruz.org>
  *
  * Copyright (C) 1999-2000 Lauris Kaplinski
  * Copyright (C) 2000-2001 Ximian, Inc.
 #include <math.h>
 
 #if HAVE_STRING_H
-# include <string.h>
+# include <cstring>
 #endif
 
 #if HAVE_STDLIB_H
-# include <stdlib.h>
+# include <cstdlib>
 #endif
 
 
 #include <glib.h>
-
+#include <2geom/point.h>
 #include "svg/stringstream.h"
 #include "svg/css-ostringstream.h"
 
 #include "xml/repr.h"
 #include "xml/repr-sorting.h"
 
+
+#define OSB_NS_URI "http://www.openswatchbook.org/uri/2009/osb"
+
+
 struct SPXMLNs {
     SPXMLNs *next;
     unsigned int uri, prefix;
@@ -149,7 +152,7 @@ static SPXMLNs *namespaces=NULL;
 static void
 sp_xml_ns_register_defaults()
 {
-    static SPXMLNs defaults[7];
+    static SPXMLNs defaults[11];
 
     defaults[0].uri = g_quark_from_static_string(SP_SODIPODI_NS_URI);
     defaults[0].prefix = g_quark_from_static_string("sodipodi");
@@ -177,7 +180,34 @@ sp_xml_ns_register_defaults()
 
     defaults[6].uri = g_quark_from_static_string(SP_DC_NS_URI);
     defaults[6].prefix = g_quark_from_static_string("dc");
-    defaults[6].next = NULL;
+    defaults[6].next = &defaults[7];
+
+    defaults[7].uri = g_quark_from_static_string(OSB_NS_URI);
+    defaults[7].prefix = g_quark_from_static_string("osb");
+    defaults[7].next = &defaults[8];
+
+    // Inkscape versions prior to 0.44 would write this namespace
+    // URI instead of the correct sodipodi namespace; by adding this
+    // entry to the table last (where it gets used for URI -> prefix
+    // lookups, but not prefix -> URI lookups), we effectively transfer
+    // elements in this namespace to the correct sodipodi namespace:
+
+    defaults[8].uri = g_quark_from_static_string(SP_BROKEN_SODIPODI_NS_URI);
+    defaults[8].prefix = g_quark_from_static_string("sodipodi");
+    defaults[8].next = &defaults[9];
+
+    // "Duck prion"
+    // This URL became widespread due to a bug in versions <= 0.43
+
+    defaults[9].uri = g_quark_from_static_string("http://inkscape.sourceforge.net/DTD/s odipodi-0.dtd");
+    defaults[9].prefix = g_quark_from_static_string("sodipodi");
+    defaults[9].next = &defaults[10];
+
+    // This namespace URI is being phased out by Creative Commons
+
+    defaults[10].uri = g_quark_from_static_string(SP_OLD_CC_NS_URI);
+    defaults[10].prefix = g_quark_from_static_string("cc");
+    defaults[10].next = NULL;
 
     namespaces = &defaults[0];
 }
@@ -212,7 +242,6 @@ sp_xml_ns_auto_prefix(char const *uri)
 gchar const *
 sp_xml_ns_uri_prefix(gchar const *uri, gchar const *suggested)
 {
-    SPXMLNs *iter;
     char const *prefix;
 
     if (!uri) return NULL;
@@ -223,32 +252,46 @@ sp_xml_ns_uri_prefix(gchar const *uri, gchar const *suggested)
 
     GQuark const key = g_quark_from_string(uri);
     prefix = NULL;
-    for ( iter = namespaces ; iter ; iter = iter->next ) {
+    for ( SPXMLNs *iter=namespaces ; iter ; iter = iter->next ) {
         if ( iter->uri == key ) {
             prefix = g_quark_to_string(iter->prefix);
             break;
         }
     }
+
     if (!prefix) {
-        char const *new_prefix;
+        char *new_prefix;
         SPXMLNs *ns;
         if (suggested) {
-            new_prefix = suggested;
+            GQuark const prefix_key=g_quark_from_string(suggested);
+
+            SPXMLNs *found=namespaces;
+            while ( found && found->prefix != prefix_key ) {
+                found = found->next;
+            }
+
+            if (found) { // prefix already used?
+                new_prefix = sp_xml_ns_auto_prefix(uri);
+            } else { // safe to use suggested
+                new_prefix = g_strdup(suggested);
+            }
         } else {
             new_prefix = sp_xml_ns_auto_prefix(uri);
         }
+
         ns = g_new(SPXMLNs, 1);
-        if (ns) {
-            ns->uri = g_quark_from_string(uri);
-            ns->prefix = g_quark_from_string(new_prefix);
-            ns->next = namespaces;
-            namespaces = ns;
-            prefix = g_quark_to_string(ns->prefix);
-        }
-        if (!suggested) {
-            g_free((char *)new_prefix);
-        }
+        g_assert( ns != NULL );
+        ns->uri = g_quark_from_string(uri);
+        ns->prefix = g_quark_from_string(new_prefix);
+
+        g_free(new_prefix);
+
+        ns->next = namespaces;
+        namespaces = ns;
+
+        prefix = g_quark_to_string(ns->prefix);
     }
+
     return prefix;
 }
 
@@ -289,7 +332,7 @@ double sp_repr_get_double_attribute(Inkscape::XML::Node *repr, char const *key,
     return g_ascii_strtod(result, NULL);
 }
 
-int sp_repr_get_int_attribute(Inkscape::XML::Node *repr, char const *key, int def)
+long long int sp_repr_get_int_attribute(Inkscape::XML::Node *repr, char const *key, long long int def)
 {
     char *result;
 
@@ -300,7 +343,7 @@ int sp_repr_get_int_attribute(Inkscape::XML::Node *repr, char const *key, int de
 
     if (result == NULL) return def;
 
-    return atoi(result);
+    return atoll(result);
 }
 
 /** 
@@ -308,9 +351,9 @@ int sp_repr_get_int_attribute(Inkscape::XML::Node *repr, char const *key, int de
  *    0    positions are equivalent
  *    1    first object's position is greater than the second
  *   -1    first object's position is less than the second
+ * @todo Rewrite this function's description to be understandable
  */
-int
-sp_repr_compare_position(Inkscape::XML::Node *first, Inkscape::XML::Node *second)
+int sp_repr_compare_position(Inkscape::XML::Node const *first, Inkscape::XML::Node const *second)
 {
     int p1, p2;
     if (sp_repr_parent(first) == sp_repr_parent(second)) {
@@ -323,7 +366,7 @@ sp_repr_compare_position(Inkscape::XML::Node *first, Inkscape::XML::Node *second
            instance. */
 
         // Find the lowest common ancestor(LCA)
-        Inkscape::XML::Node *ancestor = LCA(first, second);
+        Inkscape::XML::Node const *ancestor = LCA(first, second);
         g_assert(ancestor != NULL);
 
         if (ancestor == first) {
@@ -361,7 +404,15 @@ sp_repr_compare_position(Inkscape::XML::Node *first, Inkscape::XML::Node *second
 }
 
 /**
- * lookup child by \a key, \a value.
+ * @brief Find an element node using an unique attribute
+ *
+ * This function returns the first child of the specified node that has the attribute
+ * @c key equal to @c value. Note that this function does not recurse.
+ *
+ * @param repr The node to start from
+ * @param key The name of the attribute to use for comparisons
+ * @param value The value of the attribute to look for
+ * @relatesalso Inkscape::XML::Node
  */
 Inkscape::XML::Node *
 sp_repr_lookup_child(Inkscape::XML::Node *repr,
@@ -371,8 +422,8 @@ sp_repr_lookup_child(Inkscape::XML::Node *repr,
     g_return_val_if_fail(repr != NULL, NULL);
     for ( Inkscape::XML::Node *child = repr->firstChild() ; child ; child = child->next() ) {
         gchar const *child_value = child->attribute(key);
-        if ( child_value == value ||
-             value && child_value && !strcmp(child_value, value) )
+        if ( (child_value == value) ||
+             (value && child_value && !strcmp(child_value, value)) )
         {
             return child;
         }
@@ -380,35 +431,51 @@ sp_repr_lookup_child(Inkscape::XML::Node *repr,
     return NULL;
 }
 
-/**
- *  \brief   Recursively find the Inkscape::XML::Node matching the given XML name.
- *  \return  A pointer to the matching Inkscape::XML::Node
- *  \param   repr    The Inkscape::XML::Node to start from
- *  \param   name    The desired XML name
- *  
- */
-Inkscape::XML::Node *
-sp_repr_lookup_name( Inkscape::XML::Node *repr, gchar const *name, gint maxdepth )
+Inkscape::XML::Node const *sp_repr_lookup_name( Inkscape::XML::Node const *repr, gchar const *name, gint maxdepth )
 {
+    Inkscape::XML::Node const *found = 0;
     g_return_val_if_fail(repr != NULL, NULL);
     g_return_val_if_fail(name != NULL, NULL);
 
     GQuark const quark = g_quark_from_string(name);
 
-    if ( (GQuark)repr->code() == quark ) return repr;
-    if ( maxdepth == 0 ) return NULL;
-
-    // maxdepth == -1 means unlimited
-    if ( maxdepth == -1 ) maxdepth = 0;
+    if ( (GQuark)repr->code() == quark ) {
+        found = repr;
+    } else if ( maxdepth != 0 ) {
+        // maxdepth == -1 means unlimited
+        if ( maxdepth == -1 ) {
+            maxdepth = 0;
+        }
 
-    Inkscape::XML::Node *found = NULL;
-    for (Inkscape::XML::Node *child = repr->firstChild() ; child && !found; child = child->next() ) {
-        found = sp_repr_lookup_name( child, name, maxdepth-1 );
+        for (Inkscape::XML::Node const *child = repr->firstChild() ; child && !found; child = child->next() ) {
+            found = sp_repr_lookup_name( child, name, maxdepth - 1 );
+        }
     }
-
     return found;
 }
 
+Inkscape::XML::Node *sp_repr_lookup_name( Inkscape::XML::Node *repr, gchar const *name, gint maxdepth )
+{
+    Inkscape::XML::Node const *found = sp_repr_lookup_name( const_cast<Inkscape::XML::Node const *>(repr), name, maxdepth );
+    return const_cast<Inkscape::XML::Node *>(found);
+}
+
+/**
+ * Determine if the node is a 'title', 'desc' or 'metadata' element.
+ */
+bool
+sp_repr_is_meta_element(const Inkscape::XML::Node *node)
+{
+    if (node == NULL) return false;
+    if (node->type() != Inkscape::XML::ELEMENT_NODE) return false;
+    gchar const *name = node->name();
+    if (name == NULL) return false;
+    if (!std::strcmp(name, "svg:title")) return true;
+    if (!std::strcmp(name, "svg:desc")) return true;
+    if (!std::strcmp(name, "svg:metadata")) return true;
+    return false;
+}
+
 /**
  * Parses the boolean value of an attribute "key" in repr and sets val accordingly, or to FALSE if
  * the attr is not set.
@@ -540,6 +607,41 @@ sp_repr_set_svg_double(Inkscape::XML::Node *repr, gchar const *key, double val)
     return true;
 }
 
+unsigned sp_repr_set_point(Inkscape::XML::Node *repr, gchar const *key, Geom::Point const & val)
+{
+    g_return_val_if_fail(repr != NULL, FALSE);
+    g_return_val_if_fail(key != NULL, FALSE);
+
+    Inkscape::SVGOStringStream os;
+    os << val[Geom::X] << "," << val[Geom::Y];
+
+    repr->setAttribute(key, os.str().c_str());
+    return true;
+}
+
+unsigned int
+sp_repr_get_point(Inkscape::XML::Node *repr, gchar const *key, Geom::Point *val)
+{
+    g_return_val_if_fail(repr != NULL, FALSE);
+    g_return_val_if_fail(key != NULL, FALSE);
+    g_return_val_if_fail(val != NULL, FALSE);
+
+    gchar const *v = repr->attribute(key);
+
+    gchar ** strarray = g_strsplit(v, ",", 2);
+
+    if (strarray && strarray[0] && strarray[1]) {
+        double newx, newy;
+        newx = g_ascii_strtod(strarray[0], NULL);
+        newy = g_ascii_strtod(strarray[1], NULL);
+        g_strfreev (strarray);
+        *val = Geom::Point(newx, newy);
+        return TRUE;
+    }
+
+    g_strfreev (strarray);
+    return FALSE;
+}
 
 /*
   Local Variables:
@@ -550,4 +652,4 @@ sp_repr_set_svg_double(Inkscape::XML::Node *repr, gchar const *key, double val)
   fill-column:99
   End:
 */
-// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 :