e2d7a41a2e3c01dd8e61bb485d966d49c543b249
1 /*
2 * This is where the implementation of the DBus based document API lives.
3 * All the methods in here (except in the helper section) are
4 * designed to be called remotly via DBus. application-interface.cpp
5 * has the methods used to connect to the bus and get a document instance.
6 *
7 * Documentation for these methods is in document-interface.xml
8 * which is the "gold standard" as to how the interface should work.
9 *
10 * Authors:
11 * Soren Berg <Glimmer07@gmail.com>
12 *
13 * Copyright (C) 2009 Soren Berg
14 *
15 * Released under GNU GPL, read the file 'COPYING' for more information
16 */
18 #include "document-interface.h"
19 #include <string.h>
21 #include "verbs.h"
22 #include "helper/action.h" //sp_action_perform
24 #include "inkscape.h" //inkscape_find_desktop_by_dkey, activate desktops
26 #include "desktop-handles.h" //sp_desktop_document()
27 #include "xml/repr.h" //sp_repr_document_new
29 #include "sp-object.h"
31 #include "document.h" // sp_document_repr_doc
33 #include "desktop-style.h" //sp_desktop_get_style
35 #include "selection.h" //selection struct
36 #include "selection-chemistry.h"// lots of selection functions
38 #include "sp-ellipse.h"
40 #include "layer-fns.h" //LPOS_BELOW
42 #include "style.h" //style_write
44 #include "file.h" //IO
46 #include "extension/system.h" //IO
48 #include "extension/output.h" //IO
50 #include "print.h" //IO
52 #include "live_effects/parameter/text.h" //text
53 #include "display/canvas-text.h" //text
55 //#include "2geom/svg-path-parser.h" //get_node_coordinates
57 /****************************************************************************
58 HELPER / SHORTCUT FUNCTIONS
59 ****************************************************************************/
61 /*
62 * This function or the one below it translates the user input for an object
63 * into Inkscapes internal representation. It is called by almost every
64 * method so it should be as fast as possible.
65 *
66 * (eg turns "rect2234" to an SPObject or Inkscape::XML::Node)
67 *
68 * If the internal representation changes (No more 'id' attributes) this is the
69 * place to adjust things.
70 */
71 Inkscape::XML::Node *
72 get_repr_by_name (SPDesktop *desk, gchar *name, GError **error)
73 {
74 /* ALTERNATIVE (is this faster if only repr is needed?)
75 Inkscape::XML::Node *node = sp_repr_lookup_name((doc->root)->repr, name);
76 */
77 Inkscape::XML::Node * node = sp_desktop_document(desk)->getObjectById(name)->repr;
78 if (!node)
79 {
80 g_set_error(error, INKSCAPE_ERROR, INKSCAPE_ERROR_OBJECT, "Object '%s' not found in document.", name);
81 return NULL;
82 }
83 return node;
84 }
86 /*
87 * See comment for get_repr_by_name, above.
88 */
89 SPObject *
90 get_object_by_name (SPDesktop *desk, gchar *name, GError **error)
91 {
92 SPObject * obj = sp_desktop_document(desk)->getObjectById(name);
93 if (!obj)
94 {
95 g_set_error(error, INKSCAPE_ERROR, INKSCAPE_ERROR_OBJECT, "Object '%s' not found in document.", name);
96 return NULL;
97 }
98 return obj;
99 }
101 /*
102 * Tests for NULL strings and throws an appropriate error.
103 * Every method that takes a string parameter (other than the
104 * name of an object, that's tested seperatly) should call this.
105 */
106 gboolean
107 dbus_check_string (gchar *string, GError ** error, const gchar * errorstr)
108 {
109 if (string == NULL)
110 {
111 g_set_error(error, INKSCAPE_ERROR, INKSCAPE_ERROR_OTHER, "%s", errorstr);
112 return FALSE;
113 }
114 return TRUE;
115 }
117 /*
118 * This is used to return object values to the user
119 */
120 const gchar *
121 get_name_from_object (SPObject * obj)
122 {
123 return obj->repr->attribute("id");
124 }
126 /*
127 * Some verbs (cut, paste) only work on the active layer.
128 * This makes sure that the document that is about to recive a command is active.
129 */
130 void
131 desktop_ensure_active (SPDesktop* desk) {
132 if (desk != SP_ACTIVE_DESKTOP)
133 inkscape_activate_desktop (desk);
134 return;
135 }
137 gdouble
138 selection_get_center_x (Inkscape::Selection *sel){
139 NRRect *box = g_new(NRRect, 1);;
140 box = sel->boundsInDocument(box);
141 return box->x0 + ((box->x1 - box->x0)/2);
142 }
144 gdouble
145 selection_get_center_y (Inkscape::Selection *sel){
146 NRRect *box = g_new(NRRect, 1);;
147 box = sel->boundsInDocument(box);
148 return box->y0 + ((box->y1 - box->y0)/2);
149 }
151 /*
152 * This function is used along with selection_restore to
153 * take advantage of functionality provided by a selection
154 * for a single object.
155 *
156 * It saves the current selection and sets the selection to
157 * the object specified. Any selection verb can be used on the
158 * object and then selection_restore is called, restoring the
159 * original selection.
160 *
161 * This should be mostly transparent to the user who need never
162 * know we never bothered to implement it seperatly. Although
163 * they might see the selection box flicker if used in a loop.
164 */
165 const GSList *
166 selection_swap(SPDesktop *desk, gchar *name, GError **error)
167 {
168 Inkscape::Selection *sel = sp_desktop_selection(desk);
169 const GSList *oldsel = g_slist_copy((GSList *)sel->list());
171 sel->set(get_object_by_name(desk, name, error));
172 return oldsel;
173 }
175 /*
176 * See selection_swap, above
177 */
178 void
179 selection_restore(SPDesktop *desk, const GSList * oldsel)
180 {
181 Inkscape::Selection *sel = sp_desktop_selection(desk);
182 sel->setList(oldsel);
183 }
185 /*
186 * Shortcut for creating a Node.
187 */
188 Inkscape::XML::Node *
189 dbus_create_node (SPDesktop *desk, const gchar *type)
190 {
191 SPDocument * doc = sp_desktop_document (desk);
192 Inkscape::XML::Document *xml_doc = sp_document_repr_doc(doc);
194 return xml_doc->createElement(type);
195 }
197 /*
198 * Called by the shape creation functions. Gets the default style for the doc
199 * or sets it arbitrarily if none.
200 *
201 * There is probably a better way to do this (use the shape tools default styles)
202 * but I'm not sure how.
203 */
204 gchar *
205 finish_create_shape (DocumentInterface *object, GError **error, Inkscape::XML::Node *newNode, gchar *desc)
206 {
208 SPCSSAttr *style = sp_desktop_get_style(object->desk, TRUE);
210 if (style) {
211 newNode->setAttribute("style", sp_repr_css_write_string(style), TRUE);
212 }
213 else {
214 newNode->setAttribute("style", "fill:#0000ff;fill-opacity:1;stroke:#c900b9;stroke-width:0;stroke-miterlimit:0;stroke-opacity:1;stroke-dasharray:none", TRUE);
215 }
217 object->desk->currentLayer()->appendChildRepr(newNode);
218 object->desk->currentLayer()->updateRepr();
220 if (object->updates)
221 sp_document_done(sp_desktop_document(object->desk), 0, (gchar *)desc);
222 //else
223 //document_interface_pause_updates(object, error);
225 return strdup(newNode->attribute("id"));
226 }
228 /*
229 * This is the code used internally to call all the verbs.
230 *
231 * It handles error reporting and update pausing (which needs some work.)
232 * This is a good place to improve efficiency as it is called a lot.
233 *
234 * document_interface_call_verb is similar but is called by the user.
235 */
236 gboolean
237 dbus_call_verb (DocumentInterface *object, int verbid, GError **error)
238 {
239 SPDesktop *desk2 = object->desk;
240 desktop_ensure_active (desk2);
242 if ( desk2 ) {
243 Inkscape::Verb *verb = Inkscape::Verb::get( verbid );
244 if ( verb ) {
245 SPAction *action = verb->get_action(desk2);
246 if ( action ) {
247 //if (!object->updates)
248 //document_interface_pause_updates (object, error);
249 sp_action_perform( action, NULL );
250 if (object->updates)
251 sp_document_done(sp_desktop_document(desk2), verb->get_code(), g_strdup(verb->get_tip()));
252 //if (!object->updates)
253 //document_interface_pause_updates (object, error);
254 return TRUE;
255 }
256 }
257 }
258 g_set_error(error, INKSCAPE_ERROR, INKSCAPE_ERROR_VERB, "Verb failed to execute");
259 return FALSE;
260 }
262 /****************************************************************************
263 DOCUMENT INTERFACE CLASS STUFF
264 ****************************************************************************/
266 G_DEFINE_TYPE(DocumentInterface, document_interface, G_TYPE_OBJECT)
268 static void
269 document_interface_finalize (GObject *object)
270 {
271 G_OBJECT_CLASS (document_interface_parent_class)->finalize (object);
272 }
275 static void
276 document_interface_class_init (DocumentInterfaceClass *klass)
277 {
278 GObjectClass *object_class;
279 object_class = G_OBJECT_CLASS (klass);
280 object_class->finalize = document_interface_finalize;
281 }
283 static void
284 document_interface_init (DocumentInterface *object)
285 {
286 object->desk = NULL;
287 }
290 DocumentInterface *
291 document_interface_new (void)
292 {
293 return (DocumentInterface*)g_object_new (TYPE_DOCUMENT_INTERFACE, NULL);
294 }
296 /*
297 * Error stuff...
298 *
299 * To add a new error type, edit here and in the .h InkscapeError enum.
300 */
301 GQuark
302 inkscape_error_quark (void)
303 {
304 static GQuark quark = 0;
305 if (!quark)
306 quark = g_quark_from_static_string ("inkscape_error");
308 return quark;
309 }
311 #define ENUM_ENTRY(NAME, DESC) { NAME, "" #NAME "", DESC }
313 GType
314 inkscape_error_get_type (void)
315 {
316 static GType etype = 0;
318 if (etype == 0)
319 {
320 static const GEnumValue values[] =
321 {
323 ENUM_ENTRY (INKSCAPE_ERROR_SELECTION, "Incompatible_Selection"),
324 ENUM_ENTRY (INKSCAPE_ERROR_OBJECT, "Incompatible_Object"),
325 ENUM_ENTRY (INKSCAPE_ERROR_VERB, "Failed_Verb"),
326 ENUM_ENTRY (INKSCAPE_ERROR_OTHER, "Generic_Error"),
327 { 0, 0, 0 }
328 };
330 etype = g_enum_register_static ("InkscapeError", values);
331 }
333 return etype;
334 }
336 /****************************************************************************
337 MISC FUNCTIONS
338 ****************************************************************************/
340 gboolean
341 document_interface_delete_all (DocumentInterface *object, GError **error)
342 {
343 sp_edit_clear_all (object->desk);
344 return TRUE;
345 }
347 gboolean
348 document_interface_call_verb (DocumentInterface *object, gchar *verbid, GError **error)
349 {
350 SPDesktop *desk2 = object->desk;
351 desktop_ensure_active (object->desk);
352 if ( desk2 ) {
353 Inkscape::Verb *verb = Inkscape::Verb::getbyid( verbid );
354 if ( verb ) {
355 SPAction *action = verb->get_action(desk2);
356 if ( action ) {
357 sp_action_perform( action, NULL );
358 if (object->updates) {
359 sp_document_done(sp_desktop_document(desk2), verb->get_code(), g_strdup(verb->get_tip()));
360 }
361 }
362 }
363 }
364 g_set_error(error, INKSCAPE_ERROR, INKSCAPE_ERROR_VERB, "Verb '%s' failed to execute or was not found.", verbid);
365 return FALSE;
366 }
369 /****************************************************************************
370 CREATION FUNCTIONS
371 ****************************************************************************/
373 gchar*
374 document_interface_rectangle (DocumentInterface *object, int x, int y,
375 int width, int height, GError **error)
376 {
379 Inkscape::XML::Node *newNode = dbus_create_node(object->desk, "svg:rect");
380 sp_repr_set_int(newNode, "x", x); //could also use newNode->setAttribute()
381 sp_repr_set_int(newNode, "y", y);
382 sp_repr_set_int(newNode, "width", width);
383 sp_repr_set_int(newNode, "height", height);
384 return finish_create_shape (object, error, newNode, (gchar *)"create rectangle");
385 }
387 gchar*
388 document_interface_ellipse_center (DocumentInterface *object, int cx, int cy,
389 int rx, int ry, GError **error)
390 {
391 Inkscape::XML::Node *newNode = dbus_create_node(object->desk, "svg:path");
392 newNode->setAttribute("sodipodi:type", "arc");
393 sp_repr_set_int(newNode, "sodipodi:cx", cx);
394 sp_repr_set_int(newNode, "sodipodi:cy", cy);
395 sp_repr_set_int(newNode, "sodipodi:rx", rx);
396 sp_repr_set_int(newNode, "sodipodi:ry", ry);
397 return finish_create_shape (object, error, newNode, (gchar *)"create circle");
398 }
400 gchar*
401 document_interface_polygon (DocumentInterface *object, int cx, int cy,
402 int radius, int rotation, int sides,
403 GError **error)
404 {
405 gdouble rot = ((rotation / 180.0) * 3.14159265) - ( 3.14159265 / 2.0);
406 Inkscape::XML::Node *newNode = dbus_create_node(object->desk, "svg:path");
407 newNode->setAttribute("inkscape:flatsided", "true");
408 newNode->setAttribute("sodipodi:type", "star");
409 sp_repr_set_int(newNode, "sodipodi:cx", cx);
410 sp_repr_set_int(newNode, "sodipodi:cy", cy);
411 sp_repr_set_int(newNode, "sodipodi:r1", radius);
412 sp_repr_set_int(newNode, "sodipodi:r2", radius);
413 sp_repr_set_int(newNode, "sodipodi:sides", sides);
414 sp_repr_set_int(newNode, "inkscape:randomized", 0);
415 sp_repr_set_svg_double(newNode, "sodipodi:arg1", rot);
416 sp_repr_set_svg_double(newNode, "sodipodi:arg2", rot);
417 sp_repr_set_svg_double(newNode, "inkscape:rounded", 0);
419 return finish_create_shape (object, error, newNode, (gchar *)"create polygon");
420 }
422 gchar*
423 document_interface_star (DocumentInterface *object, int cx, int cy,
424 int r1, int r2, int sides, gdouble rounded,
425 gdouble arg1, gdouble arg2, GError **error)
426 {
427 Inkscape::XML::Node *newNode = dbus_create_node(object->desk, "svg:path");
428 newNode->setAttribute("inkscape:flatsided", "false");
429 newNode->setAttribute("sodipodi:type", "star");
430 sp_repr_set_int(newNode, "sodipodi:cx", cx);
431 sp_repr_set_int(newNode, "sodipodi:cy", cy);
432 sp_repr_set_int(newNode, "sodipodi:r1", r1);
433 sp_repr_set_int(newNode, "sodipodi:r2", r2);
434 sp_repr_set_int(newNode, "sodipodi:sides", sides);
435 sp_repr_set_int(newNode, "inkscape:randomized", 0);
436 sp_repr_set_svg_double(newNode, "sodipodi:arg1", arg1);
437 sp_repr_set_svg_double(newNode, "sodipodi:arg2", arg2);
438 sp_repr_set_svg_double(newNode, "inkscape:rounded", rounded);
440 return finish_create_shape (object, error, newNode, (gchar *)"create star");
441 }
443 gchar*
444 document_interface_ellipse (DocumentInterface *object, int x, int y,
445 int width, int height, GError **error)
446 {
447 int rx = width/2;
448 int ry = height/2;
449 return document_interface_ellipse_center (object, x+rx, y+ry, rx, ry, error);
450 }
452 gchar*
453 document_interface_line (DocumentInterface *object, int x, int y,
454 int x2, int y2, GError **error)
455 {
456 Inkscape::XML::Node *newNode = dbus_create_node(object->desk, "svg:path");
457 std::stringstream out;
458 // Not sure why this works.
459 out << "m " << x << "," << y << " " << x2 - x << "," << y2 - y;
460 newNode->setAttribute("d", out.str().c_str());
461 return finish_create_shape (object, error, newNode, (gchar *)"create line");
462 }
464 gchar*
465 document_interface_spiral (DocumentInterface *object, int cx, int cy,
466 int r, int revolutions, GError **error)
467 {
468 Inkscape::XML::Node *newNode = dbus_create_node(object->desk, "svg:path");
469 newNode->setAttribute("sodipodi:type", "spiral");
470 sp_repr_set_int(newNode, "sodipodi:cx", cx);
471 sp_repr_set_int(newNode, "sodipodi:cy", cy);
472 sp_repr_set_int(newNode, "sodipodi:radius", r);
473 sp_repr_set_int(newNode, "sodipodi:revolution", revolutions);
474 sp_repr_set_int(newNode, "sodipodi:t0", 0);
475 sp_repr_set_int(newNode, "sodipodi:argument", 0);
476 sp_repr_set_int(newNode, "sodipodi:expansion", 1);
477 gchar * retval = finish_create_shape (object, error, newNode, (gchar *)"create spiral");
478 //Makes sure there is no fill for spirals by default.
479 gchar* newString = g_strconcat(newNode->attribute("style"), ";fill:none", NULL);
480 newNode->setAttribute("style", newString);
481 g_free(newString);
482 return retval;
483 }
485 gboolean
486 document_interface_text (DocumentInterface *object, int x, int y, gchar *text, GError **error)
487 {
488 //FIXME: Not selectable (aka broken). Needs to be rewritten completely.
490 SPDesktop *desktop = object->desk;
491 SPCanvasText * canvas_text = (SPCanvasText *) sp_canvastext_new(sp_desktop_tempgroup(desktop), desktop, Geom::Point(0,0), "");
492 sp_canvastext_set_text (canvas_text, text);
493 sp_canvastext_set_coords (canvas_text, x, y);
495 return TRUE;
496 }
498 gchar *
499 document_interface_image (DocumentInterface *object, int x, int y, gchar *filename, GError **error)
500 {
501 gchar * uri = g_filename_to_uri (filename, FALSE, error);
502 if (!uri)
503 return FALSE;
505 Inkscape::XML::Node *newNode = dbus_create_node(object->desk, "svg:image");
506 sp_repr_set_int(newNode, "x", x);
507 sp_repr_set_int(newNode, "y", y);
508 newNode->setAttribute("xlink:href", uri);
510 object->desk->currentLayer()->appendChildRepr(newNode);
511 object->desk->currentLayer()->updateRepr();
513 if (object->updates)
514 sp_document_done(sp_desktop_document(object->desk), 0, "Imported bitmap.");
516 //g_free(uri);
517 return strdup(newNode->attribute("id"));
518 }
520 gchar*
521 document_interface_node (DocumentInterface *object, gchar *type, GError **error)
522 {
523 SPDocument * doc = sp_desktop_document (object->desk);
524 Inkscape::XML::Document *xml_doc = sp_document_repr_doc(doc);
526 Inkscape::XML::Node *newNode = xml_doc->createElement(type);
528 object->desk->currentLayer()->appendChildRepr(newNode);
529 object->desk->currentLayer()->updateRepr();
531 if (object->updates)
532 sp_document_done(sp_desktop_document(object->desk), 0, (gchar *)"created empty node");
533 //else
534 //document_interface_pause_updates(object, error);
536 return strdup(newNode->attribute("id"));
537 }
539 /****************************************************************************
540 ENVIORNMENT FUNCTIONS
541 ****************************************************************************/
542 gdouble
543 document_interface_document_get_width (DocumentInterface *object)
544 {
545 return sp_document_width(sp_desktop_document(object->desk));
546 }
548 gdouble
549 document_interface_document_get_height (DocumentInterface *object)
550 {
551 return sp_document_height(sp_desktop_document(object->desk));
552 }
554 gchar *
555 document_interface_document_get_css (DocumentInterface *object, GError **error)
556 {
557 SPCSSAttr *current = (object->desk)->current;
558 return sp_repr_css_write_string(current);
559 }
561 gboolean
562 document_interface_document_merge_css (DocumentInterface *object,
563 gchar *stylestring, GError **error)
564 {
565 SPCSSAttr * style = sp_repr_css_attr_new();
566 sp_repr_css_attr_add_from_string (style, stylestring);
567 sp_desktop_set_style (object->desk, style);
568 return TRUE;
569 }
571 gboolean
572 document_interface_document_set_css (DocumentInterface *object,
573 gchar *stylestring, GError **error)
574 {
575 SPCSSAttr * style = sp_repr_css_attr_new();
576 sp_repr_css_attr_add_from_string (style, stylestring);
577 //Memory leak?
578 object->desk->current = style;
579 return TRUE;
580 }
582 gboolean
583 document_interface_document_resize_to_fit_selection (DocumentInterface *object,
584 GError **error)
585 {
586 return dbus_call_verb (object, SP_VERB_FIT_CANVAS_TO_SELECTION, error);
587 return TRUE;
588 }
590 /****************************************************************************
591 OBJECT FUNCTIONS
592 ****************************************************************************/
594 gboolean
595 document_interface_set_attribute (DocumentInterface *object, char *shape,
596 char *attribute, char *newval, GError **error)
597 {
598 Inkscape::XML::Node *newNode = get_repr_by_name(object->desk, shape, error);
600 /* ALTERNATIVE (is this faster?)
601 Inkscape::XML::Node *newnode = sp_repr_lookup_name((doc->root)->repr, name);
602 */
603 if (!dbus_check_string(newval, error, "New value string was empty."))
604 return FALSE;
606 if (!newNode)
607 return FALSE;
609 newNode->setAttribute(attribute, newval, TRUE);
610 return TRUE;
611 }
613 gboolean
614 document_interface_set_int_attribute (DocumentInterface *object,
615 char *shape, char *attribute,
616 int newval, GError **error)
617 {
618 Inkscape::XML::Node *newNode = get_repr_by_name (object->desk, shape, error);
619 if (!newNode)
620 return FALSE;
622 sp_repr_set_int (newNode, attribute, newval);
623 return TRUE;
624 }
627 gboolean
628 document_interface_set_double_attribute (DocumentInterface *object,
629 char *shape, char *attribute,
630 double newval, GError **error)
631 {
632 Inkscape::XML::Node *newNode = get_repr_by_name (object->desk, shape, error);
634 if (!dbus_check_string (attribute, error, "New value string was empty."))
635 return FALSE;
636 if (!newNode)
637 return FALSE;
639 sp_repr_set_svg_double (newNode, attribute, newval);
640 return TRUE;
641 }
643 gchar *
644 document_interface_get_attribute (DocumentInterface *object, char *shape,
645 char *attribute, GError **error)
646 {
647 Inkscape::XML::Node *newNode = get_repr_by_name(object->desk, shape, error);
649 if (!dbus_check_string (attribute, error, "Attribute name empty."))
650 return NULL;
651 if (!newNode)
652 return NULL;
654 return g_strdup(newNode->attribute(attribute));
655 }
657 gboolean
658 document_interface_move (DocumentInterface *object, gchar *name, gdouble x,
659 gdouble y, GError **error)
660 {
661 const GSList *oldsel = selection_swap(object->desk, name, error);
662 if (!oldsel)
663 return FALSE;
664 sp_selection_move (object->desk, x, 0 - y);
665 selection_restore(object->desk, oldsel);
666 return TRUE;
667 }
669 gboolean
670 document_interface_move_to (DocumentInterface *object, gchar *name, gdouble x,
671 gdouble y, GError **error)
672 {
673 const GSList *oldsel = selection_swap(object->desk, name, error);
674 if (!oldsel)
675 return FALSE;
676 Inkscape::Selection * sel = sp_desktop_selection(object->desk);
677 sp_selection_move (object->desk, x - selection_get_center_x(sel),
678 0 - (y - selection_get_center_y(sel)));
679 selection_restore(object->desk, oldsel);
680 return TRUE;
681 }
683 gboolean
684 document_interface_object_to_path (DocumentInterface *object,
685 char *shape, GError **error)
686 {
687 const GSList *oldsel = selection_swap(object->desk, shape, error);
688 if (!oldsel)
689 return FALSE;
690 dbus_call_verb (object, SP_VERB_OBJECT_TO_CURVE, error);
691 selection_restore(object->desk, oldsel);
692 return TRUE;
693 }
695 gchar *
696 document_interface_get_path (DocumentInterface *object, char *pathname, GError **error)
697 {
698 Inkscape::XML::Node *node = get_repr_by_name(object->desk, pathname, error);
700 if (!node)
701 return NULL;
703 if (node->attribute("d") == NULL)
704 {
705 g_set_error(error, INKSCAPE_ERROR, INKSCAPE_ERROR_OBJECT, "Object is not a path.");
706 return NULL;
707 }
708 return strdup(node->attribute("d"));
709 }
711 gboolean
712 document_interface_transform (DocumentInterface *object, gchar *shape,
713 gchar *transformstr, GError **error)
714 {
715 //FIXME: This should merge transformations.
716 gchar trans[] = "transform";
717 document_interface_set_attribute (object, shape, trans, transformstr, error);
718 return TRUE;
719 }
721 gchar *
722 document_interface_get_css (DocumentInterface *object, gchar *shape,
723 GError **error)
724 {
725 gchar style[] = "style";
726 return document_interface_get_attribute (object, shape, style, error);
727 }
729 gboolean
730 document_interface_modify_css (DocumentInterface *object, gchar *shape,
731 gchar *cssattrb, gchar *newval, GError **error)
732 {
733 // Doesn't like non-variable strings for some reason.
734 gchar style[] = "style";
735 Inkscape::XML::Node *node = get_repr_by_name(object->desk, shape, error);
737 if (!dbus_check_string (cssattrb, error, "Attribute string empty."))
738 return FALSE;
739 if (!node)
740 return FALSE;
742 SPCSSAttr * oldstyle = sp_repr_css_attr (node, style);
743 sp_repr_css_set_property(oldstyle, cssattrb, newval);
744 node->setAttribute (style, sp_repr_css_write_string (oldstyle), TRUE);
745 return TRUE;
746 }
748 gboolean
749 document_interface_merge_css (DocumentInterface *object, gchar *shape,
750 gchar *stylestring, GError **error)
751 {
752 gchar style[] = "style";
754 Inkscape::XML::Node *node = get_repr_by_name(object->desk, shape, error);
756 if (!dbus_check_string (stylestring, error, "Style string empty."))
757 return FALSE;
758 if (!node)
759 return FALSE;
761 SPCSSAttr * newstyle = sp_repr_css_attr_new();
762 sp_repr_css_attr_add_from_string (newstyle, stylestring);
764 SPCSSAttr * oldstyle = sp_repr_css_attr (node, style);
766 sp_repr_css_merge(oldstyle, newstyle);
767 node->setAttribute (style, sp_repr_css_write_string (oldstyle), TRUE);
768 return TRUE;
769 }
771 gboolean
772 document_interface_set_color (DocumentInterface *object, gchar *shape,
773 int r, int g, int b, gboolean fill, GError **error)
774 {
775 gchar style[15];
776 if (r<0 || r>255 || g<0 || g>255 || b<0 || b>255)
777 {
778 g_set_error(error, INKSCAPE_ERROR, INKSCAPE_ERROR_OTHER, "Given (%d,%d,%d). All values must be between 0-255 inclusive.", r, g, b);
779 return FALSE;
780 }
782 if (fill)
783 snprintf(style, 15, "fill:#%.2x%.2x%.2x", r, g, b);
784 else
785 snprintf(style, 15, "stroke:#%.2x%.2x%.2x", r, g, b);
787 if (strcmp(shape, "document") == 0)
788 return document_interface_document_merge_css (object, style, error);
790 return document_interface_merge_css (object, shape, style, error);
791 }
793 gboolean
794 document_interface_move_to_layer (DocumentInterface *object, gchar *shape,
795 gchar *layerstr, GError **error)
796 {
797 const GSList *oldsel = selection_swap(object->desk, shape, error);
798 if (!oldsel)
799 return FALSE;
801 document_interface_selection_move_to_layer(object, layerstr, error);
802 selection_restore(object->desk, oldsel);
803 return TRUE;
804 }
806 GArray *
807 document_interface_get_node_coordinates (DocumentInterface *object, gchar *shape)
808 {
809 //FIXME: Needs lot's of work.
810 /*
811 Inkscape::XML::Node *shapenode = get_repr_by_name (object->desk, shape, error);
812 if (shapenode == NULL || shapenode->attribute("d") == NULL) {
813 return FALSE;
814 }
815 char * path = strdup(shapenode->attribute("d"));
816 printf("PATH: %s\n", path);
818 Geom::parse_svg_path (path);
819 return NULL;
820 */
821 return NULL;
822 }
825 /****************************************************************************
826 FILE I/O FUNCTIONS
827 ****************************************************************************/
829 gboolean
830 document_interface_save (DocumentInterface *object, GError **error)
831 {
832 SPDocument * doc = sp_desktop_document(object->desk);
833 printf("1: %s\n2: %s\n3: %s\n", doc->uri, doc->base, doc->name);
834 if (doc->uri)
835 return document_interface_save_as (object, doc->uri, error);
836 return FALSE;
837 }
839 gboolean
840 document_interface_load (DocumentInterface *object,
841 gchar *filename, GError **error)
842 {
843 desktop_ensure_active (object->desk);
844 const Glib::ustring file(filename);
845 sp_file_open(file, NULL, TRUE, TRUE);
846 if (object->updates)
847 sp_document_done(sp_desktop_document(object->desk), SP_VERB_FILE_OPEN, "Opened File");
848 return TRUE;
849 }
851 gboolean
852 document_interface_save_as (DocumentInterface *object,
853 gchar *filename, GError **error)
854 {
855 SPDocument * doc = sp_desktop_document(object->desk);
856 #ifdef WITH_GNOME_VFS
857 const Glib::ustring file(filename);
858 return file_save_remote(doc, file, NULL, TRUE, TRUE);
859 #endif
860 if (!doc || strlen(filename)<1) //Safety check
861 return false;
863 try {
864 Inkscape::Extension::save(NULL, doc, filename,
865 false, false, true);
866 } catch (...) {
867 //SP_ACTIVE_DESKTOP->messageStack()->flash(Inkscape::ERROR_MESSAGE, _("Document not saved."));
868 return false;
869 }
871 //SP_ACTIVE_DESKTOP->event_log->rememberFileSave();
872 //SP_ACTIVE_DESKTOP->messageStack()->flash(Inkscape::NORMAL_MESSAGE, "Document saved.");
873 return true;
874 }
876 gboolean
877 document_interface_mark_as_unmodified (DocumentInterface *object, GError **error)
878 {
879 SPDocument * doc = sp_desktop_document(object->desk);
880 if (doc)
881 doc->modified_since_save = FALSE;
882 return TRUE;
883 }
885 /*
886 gboolean
887 document_interface_print_to_file (DocumentInterface *object, GError **error)
888 {
889 SPDocument * doc = sp_desktop_document(object->desk);
890 sp_print_document_to_file (doc, g_strdup("/home/soren/test.pdf"));
892 return TRUE;
893 }
894 */
895 /****************************************************************************
896 PROGRAM CONTROL FUNCTIONS
897 ****************************************************************************/
899 gboolean
900 document_interface_close (DocumentInterface *object, GError **error)
901 {
902 return dbus_call_verb (object, SP_VERB_FILE_CLOSE_VIEW, error);
903 }
905 gboolean
906 document_interface_exit (DocumentInterface *object, GError **error)
907 {
908 return dbus_call_verb (object, SP_VERB_FILE_QUIT, error);
909 }
911 gboolean
912 document_interface_undo (DocumentInterface *object, GError **error)
913 {
914 return dbus_call_verb (object, SP_VERB_EDIT_UNDO, error);
915 }
917 gboolean
918 document_interface_redo (DocumentInterface *object, GError **error)
919 {
920 return dbus_call_verb (object, SP_VERB_EDIT_REDO, error);
921 }
925 /****************************************************************************
926 UPDATE FUNCTIONS
927 FIXME: This would work better by adding a flag to SPDesktop to prevent
928 updating but that would be very intrusive so for now there is a workaround.
929 Need to make sure it plays well with verbs because they are used so much.
930 ****************************************************************************/
932 void
933 document_interface_pause_updates (DocumentInterface *object, GError **error)
934 {
935 object->updates = FALSE;
936 object->desk->canvas->drawing_disabled = 1;
937 //object->desk->canvas->need_redraw = 0;
938 //object->desk->canvas->need_repick = 0;
939 //sp_desktop_document(object->desk)->root->uflags = FALSE;
940 //sp_desktop_document(object->desk)->root->mflags = FALSE;
941 }
943 void
944 document_interface_resume_updates (DocumentInterface *object, GError **error)
945 {
946 object->updates = TRUE;
947 object->desk->canvas->drawing_disabled = 0;
948 //object->desk->canvas->need_redraw = 1;
949 //object->desk->canvas->need_repick = 1;
950 //sp_desktop_document(object->desk)->root->uflags = TRUE;
951 //sp_desktop_document(object->desk)->root->mflags = TRUE;
952 //sp_desktop_document(object->desk)->_updateDocument();
953 //FIXME: use better verb than rect.
954 sp_document_done(sp_desktop_document(object->desk), SP_VERB_CONTEXT_RECT, "Multiple actions");
955 }
957 void
958 document_interface_update (DocumentInterface *object, GError **error)
959 {
960 sp_desktop_document(object->desk)->root->uflags = TRUE;
961 sp_desktop_document(object->desk)->root->mflags = TRUE;
962 object->desk->enableInteraction();
963 sp_desktop_document(object->desk)->_updateDocument();
964 object->desk->disableInteraction();
965 sp_desktop_document(object->desk)->root->uflags = FALSE;
966 sp_desktop_document(object->desk)->root->mflags = FALSE;
967 //sp_document_done(sp_desktop_document(object->desk), SP_VERB_CONTEXT_RECT, "Multiple actions");
968 }
970 /****************************************************************************
971 SELECTION FUNCTIONS FIXME: use call_verb where appropriate (once update system is tested.)
972 ****************************************************************************/
974 gboolean
975 document_interface_selection_get (DocumentInterface *object, char ***out, GError **error)
976 {
977 Inkscape::Selection * sel = sp_desktop_selection(object->desk);
978 GSList const *oldsel = sel->list();
980 int size = g_slist_length((GSList *) oldsel);
982 *out = g_new0 (char *, size + 1);
984 int i = 0;
985 for (GSList const *iter = oldsel; iter != NULL; iter = iter->next) {
986 (*out)[i] = g_strdup(SP_OBJECT(iter->data)->repr->attribute("id"));
987 i++;
988 }
989 (*out)[i] = NULL;
991 return TRUE;
992 }
994 gboolean
995 document_interface_selection_add (DocumentInterface *object, char *name, GError **error)
996 {
997 SPObject * obj = get_object_by_name(object->desk, name, error);
998 if (!obj)
999 return FALSE;
1001 Inkscape::Selection *selection = sp_desktop_selection(object->desk);
1003 selection->add(obj);
1004 return TRUE;
1005 }
1007 gboolean
1008 document_interface_selection_add_list (DocumentInterface *object,
1009 char **names, GError **error)
1010 {
1011 int i;
1012 for (i=0;names[i] != NULL;i++) {
1013 document_interface_selection_add(object, names[i], error);
1014 }
1015 return TRUE;
1016 }
1018 gboolean
1019 document_interface_selection_set (DocumentInterface *object, char *name, GError **error)
1020 {
1021 SPDocument * doc = sp_desktop_document (object->desk);
1022 Inkscape::Selection *selection = sp_desktop_selection(object->desk);
1023 selection->set(doc->getObjectById(name));
1024 return TRUE;
1025 }
1027 gboolean
1028 document_interface_selection_set_list (DocumentInterface *object,
1029 gchar **names, GError **error)
1030 {
1031 sp_desktop_selection(object->desk)->clear();
1032 int i;
1033 for (i=0;names[i] != NULL;i++) {
1034 document_interface_selection_add(object, names[i], error);
1035 }
1036 return TRUE;
1037 }
1039 gboolean
1040 document_interface_selection_rotate (DocumentInterface *object, int angle, GError **error)
1041 {
1042 Inkscape::Selection *selection = sp_desktop_selection(object->desk);
1043 sp_selection_rotate(selection, angle);
1044 return TRUE;
1045 }
1047 gboolean
1048 document_interface_selection_delete (DocumentInterface *object, GError **error)
1049 {
1050 //sp_selection_delete (object->desk);
1051 return dbus_call_verb (object, SP_VERB_EDIT_DELETE, error);
1052 }
1054 gboolean
1055 document_interface_selection_clear (DocumentInterface *object, GError **error)
1056 {
1057 sp_desktop_selection(object->desk)->clear();
1058 return TRUE;
1059 }
1061 gboolean
1062 document_interface_select_all (DocumentInterface *object, GError **error)
1063 {
1064 //sp_edit_select_all (object->desk);
1065 return dbus_call_verb (object, SP_VERB_EDIT_SELECT_ALL, error);
1066 }
1068 gboolean
1069 document_interface_select_all_in_all_layers(DocumentInterface *object,
1070 GError **error)
1071 {
1072 //sp_edit_select_all_in_all_layers (object->desk);
1073 return dbus_call_verb (object, SP_VERB_EDIT_SELECT_ALL_IN_ALL_LAYERS, error);
1074 }
1076 gboolean
1077 document_interface_selection_box (DocumentInterface *object, int x, int y,
1078 int x2, int y2, gboolean replace,
1079 GError **error)
1080 {
1081 //FIXME: implement.
1082 return FALSE;
1083 }
1085 gboolean
1086 document_interface_selection_invert (DocumentInterface *object, GError **error)
1087 {
1088 //sp_edit_invert (object->desk);
1089 return dbus_call_verb (object, SP_VERB_EDIT_INVERT, error);
1090 }
1092 gboolean
1093 document_interface_selection_group (DocumentInterface *object, GError **error)
1094 {
1095 //sp_selection_group (object->desk);
1096 return dbus_call_verb (object, SP_VERB_SELECTION_GROUP, error);
1097 }
1098 gboolean
1099 document_interface_selection_ungroup (DocumentInterface *object, GError **error)
1100 {
1101 //sp_selection_ungroup (object->desk);
1102 return dbus_call_verb (object, SP_VERB_SELECTION_UNGROUP, error);
1103 }
1105 gboolean
1106 document_interface_selection_cut (DocumentInterface *object, GError **error)
1107 {
1108 //desktop_ensure_active (object->desk);
1109 //sp_selection_cut (object->desk);
1110 return dbus_call_verb (object, SP_VERB_EDIT_CUT, error);
1111 }
1113 gboolean
1114 document_interface_selection_copy (DocumentInterface *object, GError **error)
1115 {
1116 //desktop_ensure_active (object->desk);
1117 //sp_selection_copy ();
1118 return dbus_call_verb (object, SP_VERB_EDIT_COPY, error);
1119 }
1120 /*
1121 gboolean
1122 document_interface_selection_paste (DocumentInterface *object, GError **error)
1123 {
1124 desktop_ensure_active (object->desk);
1125 if (!object->updates)
1126 document_interface_pause_updates (object, error);
1127 sp_selection_paste (object->desk, TRUE);
1128 if (!object->updates)
1129 document_interface_pause_updates (object, error);
1130 return TRUE;
1131 //return dbus_call_verb (object, SP_VERB_EDIT_PASTE, error);
1132 }
1133 */
1134 gboolean
1135 document_interface_selection_paste (DocumentInterface *object, GError **error)
1136 {
1137 return dbus_call_verb (object, SP_VERB_EDIT_PASTE, error);
1138 }
1140 gboolean
1141 document_interface_selection_scale (DocumentInterface *object, gdouble grow, GError **error)
1142 {
1143 Inkscape::Selection *selection = sp_desktop_selection(object->desk);
1144 if (!selection)
1145 {
1146 return FALSE;
1147 }
1148 sp_selection_scale (selection, grow);
1149 return TRUE;
1150 }
1152 gboolean
1153 document_interface_selection_move (DocumentInterface *object, gdouble x, gdouble y, GError **error)
1154 {
1155 sp_selection_move (object->desk, x, 0 - y); //switching coordinate systems.
1156 return TRUE;
1157 }
1159 gboolean
1160 document_interface_selection_move_to (DocumentInterface *object, gdouble x, gdouble y, GError **error)
1161 {
1162 Inkscape::Selection * sel = sp_desktop_selection(object->desk);
1164 Geom::OptRect sel_bbox = sel->bounds();
1165 if (sel_bbox) {
1166 Geom::Point m( x - selection_get_center_x(sel) , 0 - (y - selection_get_center_y(sel)) );
1167 sp_selection_move_relative(sel, m, true);
1168 }
1169 return TRUE;
1170 }
1172 //FIXME: does not paste in new layer.
1173 // This needs to use lower level cut_impl and paste_impl (messy)
1174 // See the built-in sp_selection_to_next_layer and duplicate.
1175 gboolean
1176 document_interface_selection_move_to_layer (DocumentInterface *object,
1177 gchar *layerstr, GError **error)
1178 {
1179 SPDesktop * dt = object->desk;
1181 Inkscape::Selection *selection = sp_desktop_selection(dt);
1183 // check if something is selected
1184 if (selection->isEmpty())
1185 return FALSE;
1187 SPObject *next = get_object_by_name(object->desk, layerstr, error);
1189 if (!next)
1190 return FALSE;
1192 if (strcmp("layer", (next->repr)->attribute("inkscape:groupmode")) == 0) {
1194 sp_selection_cut(dt);
1196 dt->setCurrentLayer(next);
1198 sp_selection_paste(dt, TRUE);
1199 }
1200 return TRUE;
1201 }
1203 GArray *
1204 document_interface_selection_get_center (DocumentInterface *object)
1205 {
1206 Inkscape::Selection * sel = sp_desktop_selection(object->desk);
1208 if (sel)
1209 {
1210 gdouble x = selection_get_center_x(sel);
1211 gdouble y = selection_get_center_y(sel);
1212 GArray * intArr = g_array_new (TRUE, TRUE, sizeof(double));
1214 g_array_append_val (intArr, x);
1215 g_array_append_val (intArr, y);
1216 return intArr;
1217 }
1219 return NULL;
1220 }
1222 gboolean
1223 document_interface_selection_to_path (DocumentInterface *object, GError **error)
1224 {
1225 return dbus_call_verb (object, SP_VERB_OBJECT_TO_CURVE, error);
1226 }
1229 gchar *
1230 document_interface_selection_combine (DocumentInterface *object, gchar *cmd,
1231 GError **error)
1232 {
1233 if (strcmp(cmd, "union") == 0)
1234 dbus_call_verb (object, SP_VERB_SELECTION_UNION, error);
1235 else if (strcmp(cmd, "intersection") == 0)
1236 dbus_call_verb (object, SP_VERB_SELECTION_INTERSECT, error);
1237 else if (strcmp(cmd, "difference") == 0)
1238 dbus_call_verb (object, SP_VERB_SELECTION_DIFF, error);
1239 else if (strcmp(cmd, "exclusion") == 0)
1240 dbus_call_verb (object, SP_VERB_SELECTION_SYMDIFF, error);
1241 else
1242 return NULL;
1244 if (sp_desktop_selection(object->desk)->singleRepr() != NULL)
1245 return g_strdup((sp_desktop_selection(object->desk)->singleRepr())->attribute("id"));
1246 return NULL;
1247 }
1249 gboolean
1250 document_interface_selection_divide (DocumentInterface *object, char ***out, GError **error)
1251 {
1252 dbus_call_verb (object, SP_VERB_SELECTION_CUT, error);
1254 return document_interface_selection_get (object, out, error);
1255 }
1257 gboolean
1258 document_interface_selection_change_level (DocumentInterface *object, gchar *cmd,
1259 GError **error)
1260 {
1261 if (strcmp(cmd, "raise") == 0)
1262 return dbus_call_verb (object, SP_VERB_SELECTION_RAISE, error);
1263 if (strcmp(cmd, "lower") == 0)
1264 return dbus_call_verb (object, SP_VERB_SELECTION_LOWER, error);
1265 if ((strcmp(cmd, "to_top") == 0) || (strcmp(cmd, "to_front") == 0))
1266 return dbus_call_verb (object, SP_VERB_SELECTION_TO_FRONT, error);
1267 if ((strcmp(cmd, "to_bottom") == 0) || (strcmp(cmd, "to_back") == 0))
1268 return dbus_call_verb (object, SP_VERB_SELECTION_TO_BACK, error);
1269 return TRUE;
1270 }
1272 /****************************************************************************
1273 LAYER FUNCTIONS
1274 ****************************************************************************/
1276 gchar *
1277 document_interface_layer_new (DocumentInterface *object, GError **error)
1278 {
1279 SPDesktop * dt = object->desk;
1280 SPObject *new_layer = Inkscape::create_layer(dt->currentRoot(), dt->currentLayer(), Inkscape::LPOS_BELOW);
1281 dt->setCurrentLayer(new_layer);
1282 return g_strdup(get_name_from_object (new_layer));
1283 }
1285 gboolean
1286 document_interface_layer_set (DocumentInterface *object,
1287 gchar *layerstr, GError **error)
1288 {
1289 SPObject * obj = get_object_by_name (object->desk, layerstr, error);
1291 if (!obj)
1292 return FALSE;
1294 object->desk->setCurrentLayer (obj);
1295 return TRUE;
1296 }
1298 gchar **
1299 document_interface_layer_get_all (DocumentInterface *object)
1300 {
1301 //FIXME: implement.
1302 return NULL;
1303 }
1305 gboolean
1306 document_interface_layer_change_level (DocumentInterface *object,
1307 gchar *cmd, GError **error)
1308 {
1309 if (strcmp(cmd, "raise") == 0)
1310 return dbus_call_verb (object, SP_VERB_LAYER_RAISE, error);
1311 if (strcmp(cmd, "lower") == 0)
1312 return dbus_call_verb (object, SP_VERB_LAYER_LOWER, error);
1313 if ((strcmp(cmd, "to_top") == 0) || (strcmp(cmd, "to_front") == 0))
1314 return dbus_call_verb (object, SP_VERB_LAYER_TO_TOP, error);
1315 if ((strcmp(cmd, "to_bottom") == 0) || (strcmp(cmd, "to_back") == 0))
1316 return dbus_call_verb (object, SP_VERB_LAYER_TO_BOTTOM, error);
1317 return TRUE;
1318 }
1320 gboolean
1321 document_interface_layer_next (DocumentInterface *object, GError **error)
1322 {
1323 return dbus_call_verb (object, SP_VERB_LAYER_NEXT, error);
1324 }
1326 gboolean
1327 document_interface_layer_previous (DocumentInterface *object, GError **error)
1328 {
1329 return dbus_call_verb (object, SP_VERB_LAYER_PREV, error);
1330 }