Code

Merge and cleanup of GSoC C++-ification project.
[inkscape.git] / src / extension / dbus / document-interface.cpp
index 1cdff0133bc439e9d10355e3df735560f5495a10..836ad33976cb284358636fa65a722587a175ac3f 100644 (file)
@@ -1,3 +1,20 @@
+/*
+ * This is where the implementation of the DBus based document API lives.
+ * All the methods in here (except in the helper section) are 
+ * designed to be called remotly via DBus. application-interface.cpp
+ * has the methods used to connect to the bus and get a document instance.
+ *
+ * Documentation for these methods is in document-interface.xml
+ * which is the "gold standard" as to how the interface should work.
+ *
+ * Authors:
+ *   Soren Berg <Glimmer07@gmail.com>
+ *
+ * Copyright (C) 2009 Soren Berg
+ *
+ * Released under GNU GPL, read the file 'COPYING' for more information
+ */
+
 #include "document-interface.h"
 #include <string.h>
 
@@ -11,7 +28,7 @@
 
 #include "sp-object.h"
 
-#include "document.h" // sp_document_repr_doc
+#include "document.h" // getReprDoc()
 
 #include "desktop-style.h" //sp_desktop_get_style
 
 
 #include "print.h" //IO
 
+#include "live_effects/parameter/text.h" //text
+#include "display/canvas-text.h" //text
+
+//#include "2geom/svg-path-parser.h" //get_node_coordinates
+
 /****************************************************************************
      HELPER / SHORTCUT FUNCTIONS
 ****************************************************************************/
 
-const gchar* intToCString(int i)
+/* 
+ * This function or the one below it translates the user input for an object
+ * into Inkscapes internal representation.  It is called by almost every
+ * method so it should be as fast as possible.
+ *
+ * (eg turns "rect2234" to an SPObject or Inkscape::XML::Node)
+ *
+ * If the internal representation changes (No more 'id' attributes) this is the
+ * place to adjust things.
+ */
+Inkscape::XML::Node *
+get_repr_by_name (SPDesktop *desk, gchar *name, GError **error)
 {
-    std::stringstream ss;
-    ss << i;
-    return ss.str().c_str();
+    /* ALTERNATIVE (is this faster if only repr is needed?)
+    Inkscape::XML::Node *node = sp_repr_lookup_name((doc->root)->repr, name);
+    */
+    Inkscape::XML::Node * node = sp_desktop_document(desk)->getObjectById(name)->repr;
+    if (!node)
+    {
+        g_set_error(error, INKSCAPE_ERROR, INKSCAPE_ERROR_OBJECT, "Object '%s' not found in document.", name);
+        return NULL;
+    }
+    return node;
 }
 
+/* 
+ * See comment for get_repr_by_name, above.
+ */
 SPObject *
-get_object_by_name (SPDesktop *desk, gchar *name)
+get_object_by_name (SPDesktop *desk, gchar *name, GError **error)
+{
+    SPObject * obj = sp_desktop_document(desk)->getObjectById(name);
+    if (!obj)
+    {
+        g_set_error(error, INKSCAPE_ERROR, INKSCAPE_ERROR_OBJECT, "Object '%s' not found in document.", name);
+        return NULL;
+    }
+    return obj;
+}
+
+/*
+ * Tests for NULL strings and throws an appropriate error.
+ * Every method that takes a string parameter (other than the 
+ * name of an object, that's tested seperatly) should call this.
+ */
+gboolean
+dbus_check_string (gchar *string, GError ** error, const gchar * errorstr)
 {
-    return sp_desktop_document(desk)->getObjectById(name);
+    if (string == NULL)
+    {
+        g_set_error(error, INKSCAPE_ERROR, INKSCAPE_ERROR_OTHER, "%s", errorstr);
+        return FALSE;
+    }
+    return TRUE;
 }
 
+/* 
+ * This is used to return object values to the user
+ */
 const gchar *
 get_name_from_object (SPObject * obj)
 {
-    return obj->repr->attribute("id");
+    return obj->repr->attribute("id"); 
 }
 
+/*
+ * Some verbs (cut, paste) only work on the active layer.
+ * This makes sure that the document that is about to recive a command is active.
+ */
 void
 desktop_ensure_active (SPDesktop* desk) {
     if (desk != SP_ACTIVE_DESKTOP)
@@ -62,11 +134,6 @@ desktop_ensure_active (SPDesktop* desk) {
     return;
 }
 
-Inkscape::XML::Node *
-document_retrive_node (SPDocument *doc, gchar *name) {
-    return (doc->getObjectById(name))->repr;
-}
-
 gdouble
 selection_get_center_x (Inkscape::Selection *sel){
     NRRect *box = g_new(NRRect, 1);;
@@ -80,17 +147,34 @@ selection_get_center_y (Inkscape::Selection *sel){
     box = sel->boundsInDocument(box);
     return box->y0 + ((box->y1 - box->y0)/2);
 }
-//move_to etc
+
+/* 
+ * This function is used along with selection_restore to
+ * take advantage of functionality provided by a selection
+ * for a single object.
+ *
+ * It saves the current selection and sets the selection to 
+ * the object specified.  Any selection verb can be used on the
+ * object and then selection_restore is called, restoring the 
+ * original selection.
+ *
+ * This should be mostly transparent to the user who need never
+ * know we never bothered to implement it seperatly.  Although
+ * they might see the selection box flicker if used in a loop.
+ */
 const GSList *
-selection_swap(SPDesktop *desk, gchar *name)
+selection_swap(SPDesktop *desk, gchar *name, GError **error)
 {
     Inkscape::Selection *sel = sp_desktop_selection(desk);
     const GSList *oldsel = g_slist_copy((GSList *)sel->list());
     
-    sel->set(get_object_by_name(desk, name));
+    sel->set(get_object_by_name(desk, name, error));
     return oldsel;
 }
 
+/*
+ * See selection_swap, above
+ */
 void
 selection_restore(SPDesktop *desk, const GSList * oldsel)
 {
@@ -98,19 +182,25 @@ selection_restore(SPDesktop *desk, const GSList * oldsel)
     sel->setList(oldsel);
 }
 
+/*
+ * Shortcut for creating a Node.
+ */
 Inkscape::XML::Node *
-dbus_create_node (SPDesktop *desk, gboolean isrect)
+dbus_create_node (SPDesktop *desk, const gchar *type)
 {
     SPDocument * doc = sp_desktop_document (desk);
-    Inkscape::XML::Document *xml_doc = sp_document_repr_doc(doc);
-    gchar *type;
-    if (isrect)
-        type = (gchar *)"svg:rect";
-    else
-        type = (gchar *)"svg:path";
+    Inkscape::XML::Document *xml_doc = doc->getReprDoc();
+
     return xml_doc->createElement(type);
 }
 
+/*
+ * Called by the shape creation functions.  Gets the default style for the doc
+ * or sets it arbitrarily if none.
+ *
+ * There is probably a better way to do this (use the shape tools default styles)
+ * but I'm not sure how.
+ */
 gchar *
 finish_create_shape (DocumentInterface *object, GError **error, Inkscape::XML::Node *newNode, gchar *desc)
 {
@@ -129,30 +219,43 @@ finish_create_shape (DocumentInterface *object, GError **error, Inkscape::XML::N
 
     if (object->updates)
         sp_document_done(sp_desktop_document(object->desk), 0, (gchar *)desc);
-    else
-        document_interface_pause_updates(object, error);
+    //else
+        //document_interface_pause_updates(object, error);
 
     return strdup(newNode->attribute("id"));
 }
 
+/*
+ * This is the code used internally to call all the verbs.
+ *
+ * It handles error reporting and update pausing (which needs some work.)
+ * This is a good place to improve efficiency as it is called a lot.
+ *
+ * document_interface_call_verb is similar but is called by the user.
+ */
 gboolean
 dbus_call_verb (DocumentInterface *object, int verbid, GError **error)
 {    
     SPDesktop *desk2 = object->desk;
-
+    desktop_ensure_active (desk2);
+    
     if ( desk2 ) {
         Inkscape::Verb *verb = Inkscape::Verb::get( verbid );
         if ( verb ) {
             SPAction *action = verb->get_action(desk2);
             if ( action ) {
+                //if (!object->updates)
+                    //document_interface_pause_updates (object, error);
                 sp_action_perform( action, NULL );
-                if (object->updates) {
+                if (object->updates)
                     sp_document_done(sp_desktop_document(desk2), verb->get_code(), g_strdup(verb->get_tip()));
-                }
+                //if (!object->updates)
+                    //document_interface_pause_updates (object, error);
                 return TRUE;
             }
         }
     }
+    g_set_error(error, INKSCAPE_ERROR, INKSCAPE_ERROR_VERB, "Verb failed to execute");
     return FALSE;
 }
 
@@ -190,6 +293,46 @@ document_interface_new (void)
         return (DocumentInterface*)g_object_new (TYPE_DOCUMENT_INTERFACE, NULL);
 }
 
+/* 
+ * Error stuff...
+ *
+ * To add a new error type, edit here and in the .h InkscapeError enum.
+ */
+GQuark
+inkscape_error_quark (void)
+{
+  static GQuark quark = 0;
+  if (!quark)
+    quark = g_quark_from_static_string ("inkscape_error");
+
+  return quark;
+}
+
+#define ENUM_ENTRY(NAME, DESC) { NAME, "" #NAME "", DESC }
+
+GType
+inkscape_error_get_type (void)
+{
+       static GType etype = 0;
+
+       if (etype == 0)
+       {
+               static const GEnumValue values[] =
+               {
+
+                       ENUM_ENTRY (INKSCAPE_ERROR_SELECTION, "Incompatible_Selection"),
+                       ENUM_ENTRY (INKSCAPE_ERROR_OBJECT, "Incompatible_Object"),
+                       ENUM_ENTRY (INKSCAPE_ERROR_VERB, "Failed_Verb"),
+                       ENUM_ENTRY (INKSCAPE_ERROR_OTHER, "Generic_Error"),
+                       { 0, 0, 0 }
+               };
+
+               etype = g_enum_register_static ("InkscapeError", values);
+       }
+
+       return etype;
+}
+
 /****************************************************************************
      MISC FUNCTIONS
 ****************************************************************************/
@@ -201,7 +344,7 @@ document_interface_delete_all (DocumentInterface *object, GError **error)
     return TRUE;
 }
 
-void
+gboolean
 document_interface_call_verb (DocumentInterface *object, gchar *verbid, GError **error)
 {
     SPDesktop *desk2 = object->desk;
@@ -218,6 +361,8 @@ document_interface_call_verb (DocumentInterface *object, gchar *verbid, GError *
             }
         }
     }
+    g_set_error(error, INKSCAPE_ERROR, INKSCAPE_ERROR_VERB, "Verb '%s' failed to execute or was not found.", verbid);
+    return FALSE;
 }
 
 
@@ -231,7 +376,7 @@ document_interface_rectangle (DocumentInterface *object, int x, int y,
 {
 
 
-    Inkscape::XML::Node *newNode = dbus_create_node(object->desk, TRUE);
+    Inkscape::XML::Node *newNode = dbus_create_node(object->desk, "svg:rect");
     sp_repr_set_int(newNode, "x", x);  //could also use newNode->setAttribute()
     sp_repr_set_int(newNode, "y", y);
     sp_repr_set_int(newNode, "width", width);
@@ -243,7 +388,7 @@ gchar*
 document_interface_ellipse_center (DocumentInterface *object, int cx, int cy, 
                                    int rx, int ry, GError **error)
 {
-    Inkscape::XML::Node *newNode = dbus_create_node(object->desk, FALSE);
+    Inkscape::XML::Node *newNode = dbus_create_node(object->desk, "svg:path");
     newNode->setAttribute("sodipodi:type", "arc");
     sp_repr_set_int(newNode, "sodipodi:cx", cx);
     sp_repr_set_int(newNode, "sodipodi:cy", cy);
@@ -258,7 +403,7 @@ document_interface_polygon (DocumentInterface *object, int cx, int cy,
                             GError **error)
 {
     gdouble rot = ((rotation / 180.0) * 3.14159265) - ( 3.14159265 / 2.0);
-    Inkscape::XML::Node *newNode = dbus_create_node(object->desk, FALSE);
+    Inkscape::XML::Node *newNode = dbus_create_node(object->desk, "svg:path");
     newNode->setAttribute("inkscape:flatsided", "true");
     newNode->setAttribute("sodipodi:type", "star");
     sp_repr_set_int(newNode, "sodipodi:cx", cx);
@@ -279,7 +424,7 @@ document_interface_star (DocumentInterface *object, int cx, int cy,
                          int r1, int r2, int sides, gdouble rounded,
                          gdouble arg1, gdouble arg2, GError **error)
 {
-    Inkscape::XML::Node *newNode = dbus_create_node(object->desk, FALSE);
+    Inkscape::XML::Node *newNode = dbus_create_node(object->desk, "svg:path");
     newNode->setAttribute("inkscape:flatsided", "false");
     newNode->setAttribute("sodipodi:type", "star");
     sp_repr_set_int(newNode, "sodipodi:cx", cx);
@@ -308,11 +453,10 @@ gchar*
 document_interface_line (DocumentInterface *object, int x, int y, 
                               int x2, int y2, GError **error)
 {
-    Inkscape::XML::Node *newNode = dbus_create_node(object->desk, FALSE);
+    Inkscape::XML::Node *newNode = dbus_create_node(object->desk, "svg:path");
     std::stringstream out;
-    printf("X2: %d\nY2 %d\n", x2, y2);
-       out << "m " << x << "," << y << " " << x2 << "," << y2;
-    printf ("PATH: %s\n", out.str().c_str());
+    // Not sure why this works.
+       out << "m " << x << "," << y << " " << x2 - x << "," << y2 - y;
     newNode->setAttribute("d", out.str().c_str());
     return finish_create_shape (object, error, newNode, (gchar *)"create line");
 }
@@ -321,7 +465,7 @@ gchar*
 document_interface_spiral (DocumentInterface *object, int cx, int cy, 
                            int r, int revolutions, GError **error)
 {
-    Inkscape::XML::Node *newNode = dbus_create_node(object->desk, FALSE);
+    Inkscape::XML::Node *newNode = dbus_create_node(object->desk, "svg:path");
     newNode->setAttribute("sodipodi:type", "spiral");
     sp_repr_set_int(newNode, "sodipodi:cx", cx);
     sp_repr_set_int(newNode, "sodipodi:cy", cy);
@@ -331,22 +475,52 @@ document_interface_spiral (DocumentInterface *object, int cx, int cy,
     sp_repr_set_int(newNode, "sodipodi:argument", 0);
     sp_repr_set_int(newNode, "sodipodi:expansion", 1);
     gchar * retval = finish_create_shape (object, error, newNode, (gchar *)"create spiral");
-    newNode->setAttribute("style", "fill:none");
+    //Makes sure there is no fill for spirals by default.
+    gchar* newString = g_strconcat(newNode->attribute("style"), ";fill:none", NULL);
+    newNode->setAttribute("style", newString);
+    g_free(newString);
     return retval;
 }
 
-gchar* 
-document_interface_text (DocumentInterface *object, gchar *text, GError **error)
+gboolean
+document_interface_text (DocumentInterface *object, int x, int y, gchar *text, GError **error)
 {
-    //FIXME: implement.
-    return NULL;
+    //FIXME: Not selectable (aka broken).  Needs to be rewritten completely.
+
+    SPDesktop *desktop = object->desk;
+    SPCanvasText * canvas_text = (SPCanvasText *) sp_canvastext_new(sp_desktop_tempgroup(desktop), desktop, Geom::Point(0,0), "");
+    sp_canvastext_set_text (canvas_text, text);
+    sp_canvastext_set_coords (canvas_text, x, y);
+
+    return TRUE;
 }
 
-gchar* 
-document_interface_node (DocumentInterface *object, gchar *type, GError **error)
+gchar *
+document_interface_image (DocumentInterface *object, int x, int y, gchar *filename, GError **error)
+{
+    gchar * uri = g_filename_to_uri (filename, FALSE, error);
+    if (!uri)
+        return FALSE;
+    
+    Inkscape::XML::Node *newNode = dbus_create_node(object->desk, "svg:image");
+    sp_repr_set_int(newNode, "x", x);
+    sp_repr_set_int(newNode, "y", y);
+    newNode->setAttribute("xlink:href", uri);
+    
+    object->desk->currentLayer()->appendChildRepr(newNode);
+    object->desk->currentLayer()->updateRepr();
+
+    if (object->updates)
+        sp_document_done(sp_desktop_document(object->desk), 0, "Imported bitmap.");
+
+    //g_free(uri);
+    return strdup(newNode->attribute("id"));
+}
+
+gchar *document_interface_node (DocumentInterface *object, gchar *type, GError **error)
 {
     SPDocument * doc = sp_desktop_document (object->desk);
-    Inkscape::XML::Document *xml_doc = sp_document_repr_doc(doc);
+    Inkscape::XML::Document *xml_doc = doc->getReprDoc();
 
     Inkscape::XML::Node *newNode =  xml_doc->createElement(type);
 
@@ -355,8 +529,8 @@ document_interface_node (DocumentInterface *object, gchar *type, GError **error)
 
     if (object->updates)
         sp_document_done(sp_desktop_document(object->desk), 0, (gchar *)"created empty node");
-    else
-        document_interface_pause_updates(object, error);
+    //else
+        //document_interface_pause_updates(object, error);
 
     return strdup(newNode->attribute("id"));
 }
@@ -408,7 +582,7 @@ gboolean
 document_interface_document_resize_to_fit_selection (DocumentInterface *object,
                                                      GError **error)
 {
-    dbus_call_verb (object, SP_VERB_FIT_CANVAS_TO_SELECTION, error);
+    return dbus_call_verb (object, SP_VERB_FIT_CANVAS_TO_SELECTION, error);
     return TRUE;
 }
 
@@ -420,62 +594,72 @@ gboolean
 document_interface_set_attribute (DocumentInterface *object, char *shape, 
                                   char *attribute, char *newval, GError **error)
 {
-    Inkscape::XML::Node *newNode = get_object_by_name(object->desk, shape)->repr;
+    Inkscape::XML::Node *newNode = get_repr_by_name(object->desk, shape, error);
 
-    /* ALTERNATIVE
-    Inkscape::XML::Node *repr2 = sp_repr_lookup_name((doc->root)->repr, name);
+    /* ALTERNATIVE (is this faster?)
+    Inkscape::XML::Node *newnode = sp_repr_lookup_name((doc->root)->repr, name);
     */
-    if (newNode)
-    {
-        newNode->setAttribute(attribute, newval, TRUE);
-        return TRUE;
-    }
-    return FALSE;
+    if (!dbus_check_string(newval, error, "New value string was empty."))
+        return FALSE;
+        
+    if (!newNode)
+        return FALSE;
+        
+    newNode->setAttribute(attribute, newval, TRUE);
+    return TRUE;
 }
 
-void 
+gboolean 
 document_interface_set_int_attribute (DocumentInterface *object, 
                                       char *shape, char *attribute, 
                                       int newval, GError **error)
 {
-    Inkscape::XML::Node *newNode = get_object_by_name (object->desk, shape)->repr;
-    if (newNode)
-        sp_repr_set_int (newNode, attribute, newval);
+    Inkscape::XML::Node *newNode = get_repr_by_name (object->desk, shape, error);
+    if (!newNode)
+        return FALSE;
+        
+    sp_repr_set_int (newNode, attribute, newval);
+    return TRUE;
 }
 
 
-void 
+gboolean
 document_interface_set_double_attribute (DocumentInterface *object, 
                                          char *shape, char *attribute, 
                                          double newval, GError **error)
 {
-    Inkscape::XML::Node *newNode = get_object_by_name (object->desk, shape)->repr;
-    if (newNode)
-        sp_repr_set_svg_double (newNode, attribute, newval);
+    Inkscape::XML::Node *newNode = get_repr_by_name (object->desk, shape, error);
+    
+    if (!dbus_check_string (attribute, error, "New value string was empty."))
+        return FALSE;
+    if (!newNode)
+        return FALSE;
+    
+    sp_repr_set_svg_double (newNode, attribute, newval);
+    return TRUE;
 }
 
 gchar *
 document_interface_get_attribute (DocumentInterface *object, char *shape, 
                                   char *attribute, GError **error)
 {
-    SPDesktop *desk2 = object->desk;
-    SPDocument * doc = sp_desktop_document (desk2);
+    Inkscape::XML::Node *newNode = get_repr_by_name(object->desk, shape, error);
 
-    Inkscape::XML::Node *newNode = doc->getObjectById(shape)->repr;
-
-    /* WORKS
-    Inkscape::XML::Node *repr2 = sp_repr_lookup_name((doc->root)->repr, name);
-    */
-    if (newNode)
-        return g_strdup(newNode->attribute(attribute));
-    return FALSE;
+    if (!dbus_check_string (attribute, error, "Attribute name empty."))
+        return NULL;
+    if (!newNode)
+        return NULL;
+        
+    return g_strdup(newNode->attribute(attribute));
 }
 
 gboolean
 document_interface_move (DocumentInterface *object, gchar *name, gdouble x, 
                          gdouble y, GError **error)
 {
-    const GSList *oldsel = selection_swap(object->desk, name);
+    const GSList *oldsel = selection_swap(object->desk, name, error);
+    if (!oldsel)
+        return FALSE;
     sp_selection_move (object->desk, x, 0 - y);
     selection_restore(object->desk, oldsel);
     return TRUE;
@@ -485,7 +669,9 @@ gboolean
 document_interface_move_to (DocumentInterface *object, gchar *name, gdouble x, 
                          gdouble y, GError **error)
 {
-    const GSList *oldsel = selection_swap(object->desk, name);
+    const GSList *oldsel = selection_swap(object->desk, name, error);
+    if (!oldsel)
+        return FALSE;
     Inkscape::Selection * sel = sp_desktop_selection(object->desk);
     sp_selection_move (object->desk, x - selection_get_center_x(sel),
                                      0 - (y - selection_get_center_y(sel)));
@@ -493,32 +679,32 @@ document_interface_move_to (DocumentInterface *object, gchar *name, gdouble x,
     return TRUE;
 }
 
-void 
+gboolean
 document_interface_object_to_path (DocumentInterface *object, 
                                    char *shape, GError **error)
 {
-    const GSList *oldsel = selection_swap(object->desk, shape);
+    const GSList *oldsel = selection_swap(object->desk, shape, error);
+    if (!oldsel)
+        return FALSE;
     dbus_call_verb (object, SP_VERB_OBJECT_TO_CURVE, error);
     selection_restore(object->desk, oldsel);
+    return TRUE;
 }
 
-gboolean
+gchar *
 document_interface_get_path (DocumentInterface *object, char *pathname, GError **error)
 {
-    Inkscape::XML::Node *node = document_retrive_node (sp_desktop_document (object->desk), pathname);
-    if (node == NULL || node->attribute("d") == NULL) {
-        g_set_error(error, DBUS_GERROR, DBUS_GERROR_REMOTE_EXCEPTION, "Object is not a path or does not exist.");
-        return FALSE;
+    Inkscape::XML::Node *node = get_repr_by_name(object->desk, pathname, error);
+    
+    if (!node)
+        return NULL;
+        
+    if (node->attribute("d") == NULL)
+    {
+        g_set_error(error, INKSCAPE_ERROR, INKSCAPE_ERROR_OBJECT, "Object is not a path.");
+        return NULL;
     }
-    gchar *pathstr = strdup(node->attribute("d"));
-    printf("PATH: %s\n", pathstr);
-    //gfree (pathstr);
-    std::istringstream iss(pathstr);
-    std::copy(std::istream_iterator<std::string>(iss),
-             std::istream_iterator<std::string>(),
-             std::ostream_iterator<std::string>(std::cout, "\n"));
-
-    return TRUE;
+    return strdup(node->attribute("d"));
 }
 
 gboolean 
@@ -543,8 +729,15 @@ gboolean
 document_interface_modify_css (DocumentInterface *object, gchar *shape,
                                gchar *cssattrb, gchar *newval, GError **error)
 {
+    // Doesn't like non-variable strings for some reason.
     gchar style[] = "style";
-    Inkscape::XML::Node *node = get_object_by_name(object->desk, shape)->repr;
+    Inkscape::XML::Node *node = get_repr_by_name(object->desk, shape, error);
+    
+    if (!dbus_check_string (cssattrb, error, "Attribute string empty."))
+        return FALSE;
+    if (!node)
+        return FALSE;
+        
     SPCSSAttr * oldstyle = sp_repr_css_attr (node, style);
     sp_repr_css_set_property(oldstyle, cssattrb, newval);
     node->setAttribute (style, sp_repr_css_write_string (oldstyle), TRUE);
@@ -555,11 +748,18 @@ gboolean
 document_interface_merge_css (DocumentInterface *object, gchar *shape,
                                gchar *stylestring, GError **error)
 {
+    gchar style[] = "style";
+    
+    Inkscape::XML::Node *node = get_repr_by_name(object->desk, shape, error);
+    
+    if (!dbus_check_string (stylestring, error, "Style string empty."))
+        return FALSE;
+    if (!node)
+        return FALSE;
+        
     SPCSSAttr * newstyle = sp_repr_css_attr_new();
     sp_repr_css_attr_add_from_string (newstyle, stylestring);
 
-    gchar style[] = "style";
-    Inkscape::XML::Node *node = get_object_by_name(object->desk, shape)->repr;
     SPCSSAttr * oldstyle = sp_repr_css_attr (node, style);
 
     sp_repr_css_merge(oldstyle, newstyle);
@@ -567,20 +767,56 @@ document_interface_merge_css (DocumentInterface *object, gchar *shape,
     return TRUE;
 }
 
+gboolean 
+document_interface_set_color (DocumentInterface *object, gchar *shape,
+                              int r, int g, int b, gboolean fill, GError **error)
+{
+    gchar style[15];
+    if (r<0 || r>255 || g<0 || g>255 || b<0 || b>255)
+    {
+        g_set_error(error, INKSCAPE_ERROR, INKSCAPE_ERROR_OTHER, "Given (%d,%d,%d).  All values must be between 0-255 inclusive.", r, g, b);
+        return FALSE;
+    }
+    
+    if (fill)
+        snprintf(style, 15, "fill:#%.2x%.2x%.2x", r, g, b);
+    else
+        snprintf(style, 15, "stroke:#%.2x%.2x%.2x", r, g, b);
+    
+    if (strcmp(shape, "document") == 0)
+        return document_interface_document_merge_css (object, style, error);
+    
+    return document_interface_merge_css (object, shape, style, error);
+}
+
 gboolean 
 document_interface_move_to_layer (DocumentInterface *object, gchar *shape, 
                               gchar *layerstr, GError **error)
 {
-    const GSList *oldsel = selection_swap(object->desk, shape);
+    const GSList *oldsel = selection_swap(object->desk, shape, error);
+    if (!oldsel)
+        return FALSE;
+        
     document_interface_selection_move_to_layer(object, layerstr, error);
     selection_restore(object->desk, oldsel);
     return TRUE;
 }
 
-DBUSPoint ** 
+GArray *
 document_interface_get_node_coordinates (DocumentInterface *object, gchar *shape)
 {
-    //FIXME: implement.
+    //FIXME: Needs lot's of work.
+/*
+    Inkscape::XML::Node *shapenode = get_repr_by_name (object->desk, shape, error);
+    if (shapenode == NULL || shapenode->attribute("d") == NULL) {
+        return FALSE;
+    }
+    char * path = strdup(shapenode->attribute("d"));
+    printf("PATH: %s\n", path);
+    
+    Geom::parse_svg_path (path);
+    return NULL;
+    */
     return NULL;
 }
 
@@ -625,7 +861,7 @@ document_interface_save_as (DocumentInterface *object,
 
     try {
         Inkscape::Extension::save(NULL, doc, filename,
-                 false, false, true);
+                 false, false, true, Inkscape::Extension::FILE_SAVE_METHOD_SAVE_AS);
     } catch (...) {
         //SP_ACTIVE_DESKTOP->messageStack()->flash(Inkscape::ERROR_MESSAGE, _("Document not saved."));
         return false;
@@ -635,6 +871,16 @@ document_interface_save_as (DocumentInterface *object,
     //SP_ACTIVE_DESKTOP->messageStack()->flash(Inkscape::NORMAL_MESSAGE, "Document saved.");
     return true;
 }
+
+gboolean
+document_interface_mark_as_unmodified (DocumentInterface *object, GError **error)
+{
+    SPDocument * doc = sp_desktop_document(object->desk);
+    if (doc)
+        doc->modified_since_save = FALSE;
+    return TRUE;
+}
+
 /*
 gboolean 
 document_interface_print_to_file (DocumentInterface *object, GError **error)
@@ -676,24 +922,34 @@ document_interface_redo (DocumentInterface *object, GError **error)
 
 
 /****************************************************************************
-     UPDATE FUNCTIONS
+     UPDATE FUNCTIONS 
+     FIXME: This would work better by adding a flag to SPDesktop to prevent
+     updating but that would be very intrusive so for now there is a workaround.
+     Need to make sure it plays well with verbs because they are used so much.
 ****************************************************************************/
 
 void
 document_interface_pause_updates (DocumentInterface *object, GError **error)
 {
     object->updates = FALSE;
-    sp_desktop_document(object->desk)->root->uflags = FALSE;
-    sp_desktop_document(object->desk)->root->mflags = FALSE;
+    object->desk->canvas->drawing_disabled = 1;
+    //object->desk->canvas->need_redraw = 0;
+    //object->desk->canvas->need_repick = 0;
+    //sp_desktop_document(object->desk)->root->uflags = FALSE;
+    //sp_desktop_document(object->desk)->root->mflags = FALSE;
 }
 
 void
 document_interface_resume_updates (DocumentInterface *object, GError **error)
 {
     object->updates = TRUE;
-    sp_desktop_document(object->desk)->root->uflags = TRUE;
-    sp_desktop_document(object->desk)->root->mflags = TRUE;
+    object->desk->canvas->drawing_disabled = 0;
+    //object->desk->canvas->need_redraw = 1;
+    //object->desk->canvas->need_repick = 1;
+    //sp_desktop_document(object->desk)->root->uflags = TRUE;
+    //sp_desktop_document(object->desk)->root->mflags = TRUE;
     //sp_desktop_document(object->desk)->_updateDocument();
+    //FIXME: use better verb than rect.
     sp_document_done(sp_desktop_document(object->desk), SP_VERB_CONTEXT_RECT, "Multiple actions");
 }
 
@@ -702,56 +958,48 @@ document_interface_update (DocumentInterface *object, GError **error)
 {
     sp_desktop_document(object->desk)->root->uflags = TRUE;
     sp_desktop_document(object->desk)->root->mflags = TRUE;
+    object->desk->enableInteraction();
     sp_desktop_document(object->desk)->_updateDocument();
+    object->desk->disableInteraction();
     sp_desktop_document(object->desk)->root->uflags = FALSE;
     sp_desktop_document(object->desk)->root->mflags = FALSE;
     //sp_document_done(sp_desktop_document(object->desk), SP_VERB_CONTEXT_RECT, "Multiple actions");
 }
 
 /****************************************************************************
-     SELECTION FUNCTIONS FIXME: use call_verb where appropriate.
+     SELECTION FUNCTIONS FIXME: use call_verb where appropriate (once update system is tested.)
 ****************************************************************************/
 
-gchar **
-document_interface_selection_get (DocumentInterface *object)
+gboolean
+document_interface_selection_get (DocumentInterface *object, char ***out, GError **error)
 {
     Inkscape::Selection * sel = sp_desktop_selection(object->desk);
     GSList const *oldsel = sel->list();
 
     int size = g_slist_length((GSList *) oldsel);
-    int i;
-    printf("premalloc\n");
-    gchar **list = (gchar **)malloc(size);
-        printf("postmalloc\n");
-    for(i = 0; i < size; i++)
-        list[i] = (gchar *)malloc(sizeof(gchar) * 10);
-        printf("postmalloc2\n");
-    i=0;
-        printf("prealloc\n");
+
+    *out = g_new0 (char *, size + 1);
+
+    int i = 0;
     for (GSList const *iter = oldsel; iter != NULL; iter = iter->next) {
-        strcpy (list[i], SP_OBJECT(iter->data)->repr->attribute("id"));
+        (*out)[i] = g_strdup(SP_OBJECT(iter->data)->repr->attribute("id"));
         i++;
     }
-            printf("postalloc\n");
-    for (i=0; i<size; i++) {
-        printf("%d = %s\n", i, list[i]);
-    }
-    
-    return list;
+    (*out)[i] = NULL;
+
+    return TRUE;
 }
 
 gboolean
 document_interface_selection_add (DocumentInterface *object, char *name, GError **error)
 {
-    if (name == NULL) 
+    SPObject * obj = get_object_by_name(object->desk, name, error);
+    if (!obj)
         return FALSE;
-    SPDocument * doc = sp_desktop_document (object->desk);
+    
     Inkscape::Selection *selection = sp_desktop_selection(object->desk);
-    /* WORKS
-    Inkscape::XML::Node *repr2 = sp_repr_lookup_name((doc->root)->repr, name);
-    selection->add(repr2, TRUE);
-    */
-    selection->add(doc->getObjectById(name));
+
+    selection->add(obj);
     return TRUE;
 }
 
@@ -759,8 +1007,11 @@ gboolean
 document_interface_selection_add_list (DocumentInterface *object, 
                                        char **names, GError **error)
 {
-    //FIXME: implement.
-    return FALSE;
+    int i;
+    for (i=0;names[i] != NULL;i++) {
+        document_interface_selection_add(object, names[i], error);       
+    }
+    return TRUE;
 }
 
 gboolean
@@ -776,11 +1027,9 @@ gboolean
 document_interface_selection_set_list (DocumentInterface *object, 
                                        gchar **names, GError **error)
 {
-    //FIXME: broken array passing.
     sp_desktop_selection(object->desk)->clear();
     int i;
-    for (i=0;((i<30000) && (names[i] != NULL));i++) {
-        printf("NAME: %s\n", names[i]);
+    for (i=0;names[i] != NULL;i++) {
         document_interface_selection_add(object, names[i], error);       
     }
     return TRUE;
@@ -797,8 +1046,8 @@ document_interface_selection_rotate (DocumentInterface *object, int angle, GErro
 gboolean
 document_interface_selection_delete (DocumentInterface *object, GError **error)
 {
-    sp_selection_delete (object->desk);
-    return TRUE;
+    //sp_selection_delete (object->desk);
+    return dbus_call_verb (object, SP_VERB_EDIT_DELETE, error);
 }
 
 gboolean
@@ -811,16 +1060,16 @@ document_interface_selection_clear (DocumentInterface *object, GError **error)
 gboolean
 document_interface_select_all (DocumentInterface *object, GError **error)
 {
-    sp_edit_select_all (object->desk);
-    return TRUE;
+    //sp_edit_select_all (object->desk);
+    return dbus_call_verb (object, SP_VERB_EDIT_SELECT_ALL, error);
 }
 
 gboolean
 document_interface_select_all_in_all_layers(DocumentInterface *object, 
                                             GError **error)
 {
-    sp_edit_select_all_in_all_layers (object->desk);
-    return TRUE;
+    //sp_edit_select_all_in_all_layers (object->desk);
+    return dbus_call_verb (object, SP_VERB_EDIT_SELECT_ALL_IN_ALL_LAYERS, error);
 }
 
 gboolean
@@ -835,48 +1084,66 @@ document_interface_selection_box (DocumentInterface *object, int x, int y,
 gboolean
 document_interface_selection_invert (DocumentInterface *object, GError **error)
 {
-    sp_edit_invert (object->desk);
-    return TRUE;
+    //sp_edit_invert (object->desk);
+    return dbus_call_verb (object, SP_VERB_EDIT_INVERT, error);
 }
 
 gboolean
 document_interface_selection_group (DocumentInterface *object, GError **error)
 {
-    sp_selection_group (object->desk);
-    return TRUE;
+    //sp_selection_group (object->desk);
+    return dbus_call_verb (object, SP_VERB_SELECTION_GROUP, error);
 }
 gboolean
 document_interface_selection_ungroup (DocumentInterface *object, GError **error)
 {
-    sp_selection_ungroup (object->desk);
-    return TRUE;
+    //sp_selection_ungroup (object->desk);
+    return dbus_call_verb (object, SP_VERB_SELECTION_UNGROUP, error);
 }
  
 gboolean
 document_interface_selection_cut (DocumentInterface *object, GError **error)
 {
-    sp_selection_cut (object->desk);
-    return TRUE;
+    //desktop_ensure_active (object->desk);
+    //sp_selection_cut (object->desk);
+    return dbus_call_verb (object, SP_VERB_EDIT_CUT, error);
 }
+
 gboolean
 document_interface_selection_copy (DocumentInterface *object, GError **error)
 {
-    desktop_ensure_active (object->desk);
-    sp_selection_copy ();
-    return TRUE;
+    //desktop_ensure_active (object->desk);
+    //sp_selection_copy ();
+    return dbus_call_verb (object, SP_VERB_EDIT_COPY, error);
 }
+/*
 gboolean
 document_interface_selection_paste (DocumentInterface *object, GError **error)
 {
     desktop_ensure_active (object->desk);
+                    if (!object->updates)
+                    document_interface_pause_updates (object, error);
     sp_selection_paste (object->desk, TRUE);
+                    if (!object->updates)
+                    document_interface_pause_updates (object, error);
     return TRUE;
+    //return dbus_call_verb (object, SP_VERB_EDIT_PASTE, error);
+}
+*/
+gboolean
+document_interface_selection_paste (DocumentInterface *object, GError **error)
+{
+    return dbus_call_verb (object, SP_VERB_EDIT_PASTE, error);
 }
 
 gboolean
 document_interface_selection_scale (DocumentInterface *object, gdouble grow, GError **error)
 {
     Inkscape::Selection *selection = sp_desktop_selection(object->desk);
+    if (!selection)
+    {
+        return FALSE;
+    }     
     sp_selection_scale (selection, grow);
     return TRUE;
 }
@@ -895,7 +1162,6 @@ document_interface_selection_move_to (DocumentInterface *object, gdouble x, gdou
 
     Geom::OptRect sel_bbox = sel->bounds();
     if (sel_bbox) {
-        //Geom::Point m( (object->desk)->point() - sel_bbox->midpoint() );
         Geom::Point m( x - selection_get_center_x(sel) , 0 - (y - selection_get_center_y(sel)) );
         sp_selection_move_relative(sel, m, true);
     }
@@ -903,6 +1169,8 @@ document_interface_selection_move_to (DocumentInterface *object, gdouble x, gdou
 }
 
 //FIXME: does not paste in new layer.
+// This needs to use lower level cut_impl and paste_impl (messy)
+// See the built-in sp_selection_to_next_layer and duplicate.
 gboolean 
 document_interface_selection_move_to_layer (DocumentInterface *object,
                                             gchar *layerstr, GError **error)
@@ -915,9 +1183,12 @@ document_interface_selection_move_to_layer (DocumentInterface *object,
     if (selection->isEmpty())
         return FALSE;
 
-    SPObject *next = get_object_by_name(object->desk, layerstr);
+    SPObject *next = get_object_by_name(object->desk, layerstr, error);
+    
+    if (!next)
+        return FALSE;
 
-    if (next && (strcmp("layer", (next->repr)->attribute("inkscape:groupmode")) == 0)) {
+    if (strcmp("layer", (next->repr)->attribute("inkscape:groupmode")) == 0) {
 
         sp_selection_cut(dt);
 
@@ -928,11 +1199,23 @@ document_interface_selection_move_to_layer (DocumentInterface *object,
     return TRUE;
 }
 
-gboolean 
+GArray *
 document_interface_selection_get_center (DocumentInterface *object)
 {
-    //FIXME: implement: pass struct.
-    return FALSE;
+    Inkscape::Selection * sel = sp_desktop_selection(object->desk);
+
+    if (sel) 
+    {
+        gdouble x = selection_get_center_x(sel);
+        gdouble y = selection_get_center_y(sel);
+        GArray * intArr = g_array_new (TRUE, TRUE, sizeof(double));
+
+        g_array_append_val (intArr, x);
+        g_array_append_val (intArr, y);
+        return intArr;
+    }
+
+    return NULL;
 }
 
 gboolean 
@@ -946,7 +1229,6 @@ gchar *
 document_interface_selection_combine (DocumentInterface *object, gchar *cmd,
                                       GError **error)
 {
-
     if (strcmp(cmd, "union") == 0)
         dbus_call_verb (object, SP_VERB_SELECTION_UNION, error);
     else if (strcmp(cmd, "intersection") == 0)
@@ -955,13 +1237,20 @@ document_interface_selection_combine (DocumentInterface *object, gchar *cmd,
         dbus_call_verb (object, SP_VERB_SELECTION_DIFF, error);
     else if (strcmp(cmd, "exclusion") == 0)
         dbus_call_verb (object, SP_VERB_SELECTION_SYMDIFF, error);
-    else if (strcmp(cmd, "division") == 0)
-        dbus_call_verb (object, SP_VERB_SELECTION_CUT, error);
     else
         return NULL;
 
-    //FIXME: this WILL cause problems with division
-    return g_strdup((sp_desktop_selection(object->desk)->singleRepr())->attribute("id"));
+    if (sp_desktop_selection(object->desk)->singleRepr() != NULL)
+        return g_strdup((sp_desktop_selection(object->desk)->singleRepr())->attribute("id"));
+    return NULL;
+}
+
+gboolean
+document_interface_selection_divide (DocumentInterface *object, char ***out, GError **error)
+{
+    dbus_call_verb (object, SP_VERB_SELECTION_CUT, error);
+
+    return document_interface_selection_get (object, out, error);
 }
 
 gboolean
@@ -996,7 +1285,12 @@ gboolean
 document_interface_layer_set (DocumentInterface *object,
                               gchar *layerstr, GError **error)
 {
-    object->desk->setCurrentLayer (get_object_by_name (object->desk, layerstr));
+    SPObject * obj = get_object_by_name (object->desk, layerstr, error);
+    
+    if (!obj)
+        return FALSE;
+        
+    object->desk->setCurrentLayer (obj);
     return TRUE;
 }