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" // getReprDoc()
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 "display/sp-canvas.h" //text
57 //#include "2geom/svg-path-parser.h" //get_node_coordinates
59 /****************************************************************************
60 HELPER / SHORTCUT FUNCTIONS
61 ****************************************************************************/
63 /*
64 * This function or the one below it translates the user input for an object
65 * into Inkscapes internal representation. It is called by almost every
66 * method so it should be as fast as possible.
67 *
68 * (eg turns "rect2234" to an SPObject or Inkscape::XML::Node)
69 *
70 * If the internal representation changes (No more 'id' attributes) this is the
71 * place to adjust things.
72 */
73 Inkscape::XML::Node *
74 get_repr_by_name (SPDesktop *desk, gchar *name, GError **error)
75 {
76 /* ALTERNATIVE (is this faster if only repr is needed?)
77 Inkscape::XML::Node *node = sp_repr_lookup_name((doc->root)->repr, name);
78 */
79 Inkscape::XML::Node * node = sp_desktop_document(desk)->getObjectById(name)->getRepr();
80 if (!node)
81 {
82 g_set_error(error, INKSCAPE_ERROR, INKSCAPE_ERROR_OBJECT, "Object '%s' not found in document.", name);
83 return NULL;
84 }
85 return node;
86 }
88 /*
89 * See comment for get_repr_by_name, above.
90 */
91 SPObject *
92 get_object_by_name (SPDesktop *desk, gchar *name, GError **error)
93 {
94 SPObject * obj = sp_desktop_document(desk)->getObjectById(name);
95 if (!obj)
96 {
97 g_set_error(error, INKSCAPE_ERROR, INKSCAPE_ERROR_OBJECT, "Object '%s' not found in document.", name);
98 return NULL;
99 }
100 return obj;
101 }
103 /*
104 * Tests for NULL strings and throws an appropriate error.
105 * Every method that takes a string parameter (other than the
106 * name of an object, that's tested seperatly) should call this.
107 */
108 gboolean
109 dbus_check_string (gchar *string, GError ** error, const gchar * errorstr)
110 {
111 if (string == NULL)
112 {
113 g_set_error(error, INKSCAPE_ERROR, INKSCAPE_ERROR_OTHER, "%s", errorstr);
114 return FALSE;
115 }
116 return TRUE;
117 }
119 /*
120 * This is used to return object values to the user
121 */
122 const gchar *
123 get_name_from_object (SPObject * obj)
124 {
125 return obj->getRepr()->attribute("id");
126 }
128 /*
129 * Some verbs (cut, paste) only work on the active layer.
130 * This makes sure that the document that is about to recive a command is active.
131 */
132 void
133 desktop_ensure_active (SPDesktop* desk) {
134 if (desk != SP_ACTIVE_DESKTOP)
135 inkscape_activate_desktop (desk);
136 return;
137 }
139 gdouble
140 selection_get_center_x (Inkscape::Selection *sel){
141 NRRect *box = g_new(NRRect, 1);;
142 box = sel->boundsInDocument(box);
143 return box->x0 + ((box->x1 - box->x0)/2);
144 }
146 gdouble
147 selection_get_center_y (Inkscape::Selection *sel){
148 NRRect *box = g_new(NRRect, 1);;
149 box = sel->boundsInDocument(box);
150 return box->y0 + ((box->y1 - box->y0)/2);
151 }
153 /*
154 * This function is used along with selection_restore to
155 * take advantage of functionality provided by a selection
156 * for a single object.
157 *
158 * It saves the current selection and sets the selection to
159 * the object specified. Any selection verb can be used on the
160 * object and then selection_restore is called, restoring the
161 * original selection.
162 *
163 * This should be mostly transparent to the user who need never
164 * know we never bothered to implement it seperatly. Although
165 * they might see the selection box flicker if used in a loop.
166 */
167 const GSList *
168 selection_swap(SPDesktop *desk, gchar *name, GError **error)
169 {
170 Inkscape::Selection *sel = sp_desktop_selection(desk);
171 const GSList *oldsel = g_slist_copy((GSList *)sel->list());
173 sel->set(get_object_by_name(desk, name, error));
174 return oldsel;
175 }
177 /*
178 * See selection_swap, above
179 */
180 void
181 selection_restore(SPDesktop *desk, const GSList * oldsel)
182 {
183 Inkscape::Selection *sel = sp_desktop_selection(desk);
184 sel->setList(oldsel);
185 }
187 /*
188 * Shortcut for creating a Node.
189 */
190 Inkscape::XML::Node *
191 dbus_create_node (SPDesktop *desk, const gchar *type)
192 {
193 SPDocument * doc = sp_desktop_document (desk);
194 Inkscape::XML::Document *xml_doc = doc->getReprDoc();
196 return xml_doc->createElement(type);
197 }
199 /*
200 * Called by the shape creation functions. Gets the default style for the doc
201 * or sets it arbitrarily if none.
202 *
203 * There is probably a better way to do this (use the shape tools default styles)
204 * but I'm not sure how.
205 */
206 gchar *
207 finish_create_shape (DocumentInterface *object, GError **error, Inkscape::XML::Node *newNode, gchar *desc)
208 {
210 SPCSSAttr *style = sp_desktop_get_style(object->desk, TRUE);
212 if (style) {
213 newNode->setAttribute("style", sp_repr_css_write_string(style), TRUE);
214 }
215 else {
216 newNode->setAttribute("style", "fill:#0000ff;fill-opacity:1;stroke:#c900b9;stroke-width:0;stroke-miterlimit:0;stroke-opacity:1;stroke-dasharray:none", TRUE);
217 }
219 object->desk->currentLayer()->appendChildRepr(newNode);
220 object->desk->currentLayer()->updateRepr();
222 if (object->updates)
224 Inkscape::DocumentUndo::done(sp_desktop_document(object->desk), 0, (gchar *)desc);
225 //else
226 //document_interface_pause_updates(object, error);
228 return strdup(newNode->attribute("id"));
229 }
231 /*
232 * This is the code used internally to call all the verbs.
233 *
234 * It handles error reporting and update pausing (which needs some work.)
235 * This is a good place to improve efficiency as it is called a lot.
236 *
237 * document_interface_call_verb is similar but is called by the user.
238 */
239 gboolean
240 dbus_call_verb (DocumentInterface *object, int verbid, GError **error)
241 {
242 SPDesktop *desk2 = object->desk;
243 desktop_ensure_active (desk2);
245 if ( desk2 ) {
246 Inkscape::Verb *verb = Inkscape::Verb::get( verbid );
247 if ( verb ) {
248 SPAction *action = verb->get_action(desk2);
249 if ( action ) {
250 //if (!object->updates)
251 //document_interface_pause_updates (object, error);
252 sp_action_perform( action, NULL );
253 if (object->updates)
254 Inkscape::DocumentUndo::done(sp_desktop_document(desk2), verb->get_code(), g_strdup(verb->get_tip()));
255 //if (!object->updates)
256 //document_interface_pause_updates (object, error);
257 return TRUE;
258 }
259 }
260 }
261 g_set_error(error, INKSCAPE_ERROR, INKSCAPE_ERROR_VERB, "Verb failed to execute");
262 return FALSE;
263 }
265 /****************************************************************************
266 DOCUMENT INTERFACE CLASS STUFF
267 ****************************************************************************/
269 G_DEFINE_TYPE(DocumentInterface, document_interface, G_TYPE_OBJECT)
271 static void
272 document_interface_finalize (GObject *object)
273 {
274 G_OBJECT_CLASS (document_interface_parent_class)->finalize (object);
275 }
278 static void
279 document_interface_class_init (DocumentInterfaceClass *klass)
280 {
281 GObjectClass *object_class;
282 object_class = G_OBJECT_CLASS (klass);
283 object_class->finalize = document_interface_finalize;
284 }
286 static void
287 document_interface_init (DocumentInterface *object)
288 {
289 object->desk = NULL;
290 }
293 DocumentInterface *
294 document_interface_new (void)
295 {
296 return (DocumentInterface*)g_object_new (TYPE_DOCUMENT_INTERFACE, NULL);
297 }
299 /*
300 * Error stuff...
301 *
302 * To add a new error type, edit here and in the .h InkscapeError enum.
303 */
304 GQuark
305 inkscape_error_quark (void)
306 {
307 static GQuark quark = 0;
308 if (!quark)
309 quark = g_quark_from_static_string ("inkscape_error");
311 return quark;
312 }
314 #define ENUM_ENTRY(NAME, DESC) { NAME, "" #NAME "", DESC }
316 GType
317 inkscape_error_get_type (void)
318 {
319 static GType etype = 0;
321 if (etype == 0)
322 {
323 static const GEnumValue values[] =
324 {
326 ENUM_ENTRY (INKSCAPE_ERROR_SELECTION, "Incompatible_Selection"),
327 ENUM_ENTRY (INKSCAPE_ERROR_OBJECT, "Incompatible_Object"),
328 ENUM_ENTRY (INKSCAPE_ERROR_VERB, "Failed_Verb"),
329 ENUM_ENTRY (INKSCAPE_ERROR_OTHER, "Generic_Error"),
330 { 0, 0, 0 }
331 };
333 etype = g_enum_register_static ("InkscapeError", values);
334 }
336 return etype;
337 }
339 /****************************************************************************
340 MISC FUNCTIONS
341 ****************************************************************************/
343 gboolean
344 document_interface_delete_all (DocumentInterface *object, GError **error)
345 {
346 sp_edit_clear_all (object->desk);
347 return TRUE;
348 }
350 gboolean
351 document_interface_call_verb (DocumentInterface *object, gchar *verbid, GError **error)
352 {
353 SPDesktop *desk2 = object->desk;
354 desktop_ensure_active (object->desk);
355 if ( desk2 ) {
356 Inkscape::Verb *verb = Inkscape::Verb::getbyid( verbid );
357 if ( verb ) {
358 SPAction *action = verb->get_action(desk2);
359 if ( action ) {
360 sp_action_perform( action, NULL );
361 if (object->updates) {
362 Inkscape::DocumentUndo::done(sp_desktop_document(desk2), verb->get_code(), g_strdup(verb->get_tip()));
363 }
364 }
365 }
366 }
367 g_set_error(error, INKSCAPE_ERROR, INKSCAPE_ERROR_VERB, "Verb '%s' failed to execute or was not found.", verbid);
368 return FALSE;
369 }
372 /****************************************************************************
373 CREATION FUNCTIONS
374 ****************************************************************************/
376 gchar*
377 document_interface_rectangle (DocumentInterface *object, int x, int y,
378 int width, int height, GError **error)
379 {
382 Inkscape::XML::Node *newNode = dbus_create_node(object->desk, "svg:rect");
383 sp_repr_set_int(newNode, "x", x); //could also use newNode->setAttribute()
384 sp_repr_set_int(newNode, "y", y);
385 sp_repr_set_int(newNode, "width", width);
386 sp_repr_set_int(newNode, "height", height);
387 return finish_create_shape (object, error, newNode, (gchar *)"create rectangle");
388 }
390 gchar*
391 document_interface_ellipse_center (DocumentInterface *object, int cx, int cy,
392 int rx, int ry, GError **error)
393 {
394 Inkscape::XML::Node *newNode = dbus_create_node(object->desk, "svg:path");
395 newNode->setAttribute("sodipodi:type", "arc");
396 sp_repr_set_int(newNode, "sodipodi:cx", cx);
397 sp_repr_set_int(newNode, "sodipodi:cy", cy);
398 sp_repr_set_int(newNode, "sodipodi:rx", rx);
399 sp_repr_set_int(newNode, "sodipodi:ry", ry);
400 return finish_create_shape (object, error, newNode, (gchar *)"create circle");
401 }
403 gchar*
404 document_interface_polygon (DocumentInterface *object, int cx, int cy,
405 int radius, int rotation, int sides,
406 GError **error)
407 {
408 gdouble rot = ((rotation / 180.0) * 3.14159265) - ( 3.14159265 / 2.0);
409 Inkscape::XML::Node *newNode = dbus_create_node(object->desk, "svg:path");
410 newNode->setAttribute("inkscape:flatsided", "true");
411 newNode->setAttribute("sodipodi:type", "star");
412 sp_repr_set_int(newNode, "sodipodi:cx", cx);
413 sp_repr_set_int(newNode, "sodipodi:cy", cy);
414 sp_repr_set_int(newNode, "sodipodi:r1", radius);
415 sp_repr_set_int(newNode, "sodipodi:r2", radius);
416 sp_repr_set_int(newNode, "sodipodi:sides", sides);
417 sp_repr_set_int(newNode, "inkscape:randomized", 0);
418 sp_repr_set_svg_double(newNode, "sodipodi:arg1", rot);
419 sp_repr_set_svg_double(newNode, "sodipodi:arg2", rot);
420 sp_repr_set_svg_double(newNode, "inkscape:rounded", 0);
422 return finish_create_shape (object, error, newNode, (gchar *)"create polygon");
423 }
425 gchar*
426 document_interface_star (DocumentInterface *object, int cx, int cy,
427 int r1, int r2, int sides, gdouble rounded,
428 gdouble arg1, gdouble arg2, GError **error)
429 {
430 Inkscape::XML::Node *newNode = dbus_create_node(object->desk, "svg:path");
431 newNode->setAttribute("inkscape:flatsided", "false");
432 newNode->setAttribute("sodipodi:type", "star");
433 sp_repr_set_int(newNode, "sodipodi:cx", cx);
434 sp_repr_set_int(newNode, "sodipodi:cy", cy);
435 sp_repr_set_int(newNode, "sodipodi:r1", r1);
436 sp_repr_set_int(newNode, "sodipodi:r2", r2);
437 sp_repr_set_int(newNode, "sodipodi:sides", sides);
438 sp_repr_set_int(newNode, "inkscape:randomized", 0);
439 sp_repr_set_svg_double(newNode, "sodipodi:arg1", arg1);
440 sp_repr_set_svg_double(newNode, "sodipodi:arg2", arg2);
441 sp_repr_set_svg_double(newNode, "inkscape:rounded", rounded);
443 return finish_create_shape (object, error, newNode, (gchar *)"create star");
444 }
446 gchar*
447 document_interface_ellipse (DocumentInterface *object, int x, int y,
448 int width, int height, GError **error)
449 {
450 int rx = width/2;
451 int ry = height/2;
452 return document_interface_ellipse_center (object, x+rx, y+ry, rx, ry, error);
453 }
455 gchar*
456 document_interface_line (DocumentInterface *object, int x, int y,
457 int x2, int y2, GError **error)
458 {
459 Inkscape::XML::Node *newNode = dbus_create_node(object->desk, "svg:path");
460 std::stringstream out;
461 // Not sure why this works.
462 out << "m " << x << "," << y << " " << x2 - x << "," << y2 - y;
463 newNode->setAttribute("d", out.str().c_str());
464 return finish_create_shape (object, error, newNode, (gchar *)"create line");
465 }
467 gchar*
468 document_interface_spiral (DocumentInterface *object, int cx, int cy,
469 int r, int revolutions, GError **error)
470 {
471 Inkscape::XML::Node *newNode = dbus_create_node(object->desk, "svg:path");
472 newNode->setAttribute("sodipodi:type", "spiral");
473 sp_repr_set_int(newNode, "sodipodi:cx", cx);
474 sp_repr_set_int(newNode, "sodipodi:cy", cy);
475 sp_repr_set_int(newNode, "sodipodi:radius", r);
476 sp_repr_set_int(newNode, "sodipodi:revolution", revolutions);
477 sp_repr_set_int(newNode, "sodipodi:t0", 0);
478 sp_repr_set_int(newNode, "sodipodi:argument", 0);
479 sp_repr_set_int(newNode, "sodipodi:expansion", 1);
480 gchar * retval = finish_create_shape (object, error, newNode, (gchar *)"create spiral");
481 //Makes sure there is no fill for spirals by default.
482 gchar* newString = g_strconcat(newNode->attribute("style"), ";fill:none", NULL);
483 newNode->setAttribute("style", newString);
484 g_free(newString);
485 return retval;
486 }
488 gboolean
489 document_interface_text (DocumentInterface *object, int x, int y, gchar *text, GError **error)
490 {
491 //FIXME: Not selectable (aka broken). Needs to be rewritten completely.
493 SPDesktop *desktop = object->desk;
494 SPCanvasText * canvas_text = (SPCanvasText *) sp_canvastext_new(sp_desktop_tempgroup(desktop), desktop, Geom::Point(0,0), "");
495 sp_canvastext_set_text (canvas_text, text);
496 sp_canvastext_set_coords (canvas_text, x, y);
498 return TRUE;
499 }
501 gchar *
502 document_interface_image (DocumentInterface *object, int x, int y, gchar *filename, GError **error)
503 {
504 gchar * uri = g_filename_to_uri (filename, FALSE, error);
505 if (!uri)
506 return FALSE;
508 Inkscape::XML::Node *newNode = dbus_create_node(object->desk, "svg:image");
509 sp_repr_set_int(newNode, "x", x);
510 sp_repr_set_int(newNode, "y", y);
511 newNode->setAttribute("xlink:href", uri);
513 object->desk->currentLayer()->appendChildRepr(newNode);
514 object->desk->currentLayer()->updateRepr();
516 if (object->updates)
517 Inkscape::DocumentUndo::done(sp_desktop_document(object->desk), 0, "Imported bitmap.");
519 //g_free(uri);
520 return strdup(newNode->attribute("id"));
521 }
523 gchar *document_interface_node (DocumentInterface *object, gchar *type, GError **error)
524 {
525 SPDocument * doc = sp_desktop_document (object->desk);
526 Inkscape::XML::Document *xml_doc = doc->getReprDoc();
528 Inkscape::XML::Node *newNode = xml_doc->createElement(type);
530 object->desk->currentLayer()->appendChildRepr(newNode);
531 object->desk->currentLayer()->updateRepr();
533 if (object->updates)
534 Inkscape::DocumentUndo::done(sp_desktop_document(object->desk), 0, (gchar *)"created empty node");
535 //else
536 //document_interface_pause_updates(object, error);
538 return strdup(newNode->attribute("id"));
539 }
541 /****************************************************************************
542 ENVIORNMENT FUNCTIONS
543 ****************************************************************************/
544 gdouble
545 document_interface_document_get_width (DocumentInterface *object)
546 {
547 return sp_desktop_document(object->desk)->getWidth();
548 }
550 gdouble
551 document_interface_document_get_height (DocumentInterface *object)
552 {
553 return sp_desktop_document(object->desk)->getHeight();
554 }
556 gchar *
557 document_interface_document_get_css (DocumentInterface *object, GError **error)
558 {
559 SPCSSAttr *current = (object->desk)->current;
560 return sp_repr_css_write_string(current);
561 }
563 gboolean
564 document_interface_document_merge_css (DocumentInterface *object,
565 gchar *stylestring, GError **error)
566 {
567 SPCSSAttr * style = sp_repr_css_attr_new();
568 sp_repr_css_attr_add_from_string (style, stylestring);
569 sp_desktop_set_style (object->desk, style);
570 return TRUE;
571 }
573 gboolean
574 document_interface_document_set_css (DocumentInterface *object,
575 gchar *stylestring, GError **error)
576 {
577 SPCSSAttr * style = sp_repr_css_attr_new();
578 sp_repr_css_attr_add_from_string (style, stylestring);
579 //Memory leak?
580 object->desk->current = style;
581 return TRUE;
582 }
584 gboolean
585 document_interface_document_resize_to_fit_selection (DocumentInterface *object,
586 GError **error)
587 {
588 return dbus_call_verb (object, SP_VERB_FIT_CANVAS_TO_SELECTION, error);
589 return TRUE;
590 }
592 /****************************************************************************
593 OBJECT FUNCTIONS
594 ****************************************************************************/
596 gboolean
597 document_interface_set_attribute (DocumentInterface *object, char *shape,
598 char *attribute, char *newval, GError **error)
599 {
600 Inkscape::XML::Node *newNode = get_repr_by_name(object->desk, shape, error);
602 /* ALTERNATIVE (is this faster?)
603 Inkscape::XML::Node *newnode = sp_repr_lookup_name((doc->root)->repr, name);
604 */
605 if (!dbus_check_string(newval, error, "New value string was empty."))
606 return FALSE;
608 if (!newNode)
609 return FALSE;
611 newNode->setAttribute(attribute, newval, TRUE);
612 return TRUE;
613 }
615 gboolean
616 document_interface_set_int_attribute (DocumentInterface *object,
617 char *shape, char *attribute,
618 int newval, GError **error)
619 {
620 Inkscape::XML::Node *newNode = get_repr_by_name (object->desk, shape, error);
621 if (!newNode)
622 return FALSE;
624 sp_repr_set_int (newNode, attribute, newval);
625 return TRUE;
626 }
629 gboolean
630 document_interface_set_double_attribute (DocumentInterface *object,
631 char *shape, char *attribute,
632 double newval, GError **error)
633 {
634 Inkscape::XML::Node *newNode = get_repr_by_name (object->desk, shape, error);
636 if (!dbus_check_string (attribute, error, "New value string was empty."))
637 return FALSE;
638 if (!newNode)
639 return FALSE;
641 sp_repr_set_svg_double (newNode, attribute, newval);
642 return TRUE;
643 }
645 gchar *
646 document_interface_get_attribute (DocumentInterface *object, char *shape,
647 char *attribute, GError **error)
648 {
649 Inkscape::XML::Node *newNode = get_repr_by_name(object->desk, shape, error);
651 if (!dbus_check_string (attribute, error, "Attribute name empty."))
652 return NULL;
653 if (!newNode)
654 return NULL;
656 return g_strdup(newNode->attribute(attribute));
657 }
659 gboolean
660 document_interface_move (DocumentInterface *object, gchar *name, gdouble x,
661 gdouble y, GError **error)
662 {
663 const GSList *oldsel = selection_swap(object->desk, name, error);
664 if (!oldsel)
665 return FALSE;
666 sp_selection_move (object->desk, x, 0 - y);
667 selection_restore(object->desk, oldsel);
668 return TRUE;
669 }
671 gboolean
672 document_interface_move_to (DocumentInterface *object, gchar *name, gdouble x,
673 gdouble y, GError **error)
674 {
675 const GSList *oldsel = selection_swap(object->desk, name, error);
676 if (!oldsel)
677 return FALSE;
678 Inkscape::Selection * sel = sp_desktop_selection(object->desk);
679 sp_selection_move (object->desk, x - selection_get_center_x(sel),
680 0 - (y - selection_get_center_y(sel)));
681 selection_restore(object->desk, oldsel);
682 return TRUE;
683 }
685 gboolean
686 document_interface_object_to_path (DocumentInterface *object,
687 char *shape, GError **error)
688 {
689 const GSList *oldsel = selection_swap(object->desk, shape, error);
690 if (!oldsel)
691 return FALSE;
692 dbus_call_verb (object, SP_VERB_OBJECT_TO_CURVE, error);
693 selection_restore(object->desk, oldsel);
694 return TRUE;
695 }
697 gchar *
698 document_interface_get_path (DocumentInterface *object, char *pathname, GError **error)
699 {
700 Inkscape::XML::Node *node = get_repr_by_name(object->desk, pathname, error);
702 if (!node)
703 return NULL;
705 if (node->attribute("d") == NULL)
706 {
707 g_set_error(error, INKSCAPE_ERROR, INKSCAPE_ERROR_OBJECT, "Object is not a path.");
708 return NULL;
709 }
710 return strdup(node->attribute("d"));
711 }
713 gboolean
714 document_interface_transform (DocumentInterface *object, gchar *shape,
715 gchar *transformstr, GError **error)
716 {
717 //FIXME: This should merge transformations.
718 gchar trans[] = "transform";
719 document_interface_set_attribute (object, shape, trans, transformstr, error);
720 return TRUE;
721 }
723 gchar *
724 document_interface_get_css (DocumentInterface *object, gchar *shape,
725 GError **error)
726 {
727 gchar style[] = "style";
728 return document_interface_get_attribute (object, shape, style, error);
729 }
731 gboolean
732 document_interface_modify_css (DocumentInterface *object, gchar *shape,
733 gchar *cssattrb, gchar *newval, GError **error)
734 {
735 // Doesn't like non-variable strings for some reason.
736 gchar style[] = "style";
737 Inkscape::XML::Node *node = get_repr_by_name(object->desk, shape, error);
739 if (!dbus_check_string (cssattrb, error, "Attribute string empty."))
740 return FALSE;
741 if (!node)
742 return FALSE;
744 SPCSSAttr * oldstyle = sp_repr_css_attr (node, style);
745 sp_repr_css_set_property(oldstyle, cssattrb, newval);
746 node->setAttribute (style, sp_repr_css_write_string (oldstyle), TRUE);
747 return TRUE;
748 }
750 gboolean
751 document_interface_merge_css (DocumentInterface *object, gchar *shape,
752 gchar *stylestring, GError **error)
753 {
754 gchar style[] = "style";
756 Inkscape::XML::Node *node = get_repr_by_name(object->desk, shape, error);
758 if (!dbus_check_string (stylestring, error, "Style string empty."))
759 return FALSE;
760 if (!node)
761 return FALSE;
763 SPCSSAttr * newstyle = sp_repr_css_attr_new();
764 sp_repr_css_attr_add_from_string (newstyle, stylestring);
766 SPCSSAttr * oldstyle = sp_repr_css_attr (node, style);
768 sp_repr_css_merge(oldstyle, newstyle);
769 node->setAttribute (style, sp_repr_css_write_string (oldstyle), TRUE);
770 return TRUE;
771 }
773 gboolean
774 document_interface_set_color (DocumentInterface *object, gchar *shape,
775 int r, int g, int b, gboolean fill, GError **error)
776 {
777 gchar style[15];
778 if (r<0 || r>255 || g<0 || g>255 || b<0 || b>255)
779 {
780 g_set_error(error, INKSCAPE_ERROR, INKSCAPE_ERROR_OTHER, "Given (%d,%d,%d). All values must be between 0-255 inclusive.", r, g, b);
781 return FALSE;
782 }
784 if (fill)
785 snprintf(style, 15, "fill:#%.2x%.2x%.2x", r, g, b);
786 else
787 snprintf(style, 15, "stroke:#%.2x%.2x%.2x", r, g, b);
789 if (strcmp(shape, "document") == 0)
790 return document_interface_document_merge_css (object, style, error);
792 return document_interface_merge_css (object, shape, style, error);
793 }
795 gboolean
796 document_interface_move_to_layer (DocumentInterface *object, gchar *shape,
797 gchar *layerstr, GError **error)
798 {
799 const GSList *oldsel = selection_swap(object->desk, shape, error);
800 if (!oldsel)
801 return FALSE;
803 document_interface_selection_move_to_layer(object, layerstr, error);
804 selection_restore(object->desk, oldsel);
805 return TRUE;
806 }
808 GArray *
809 document_interface_get_node_coordinates (DocumentInterface *object, gchar *shape)
810 {
811 //FIXME: Needs lot's of work.
812 /*
813 Inkscape::XML::Node *shapenode = get_repr_by_name (object->desk, shape, error);
814 if (shapenode == NULL || shapenode->attribute("d") == NULL) {
815 return FALSE;
816 }
817 char * path = strdup(shapenode->attribute("d"));
818 printf("PATH: %s\n", path);
820 Geom::parse_svg_path (path);
821 return NULL;
822 */
823 return NULL;
824 }
827 /****************************************************************************
828 FILE I/O FUNCTIONS
829 ****************************************************************************/
831 gboolean
832 document_interface_save (DocumentInterface *object, GError **error)
833 {
834 SPDocument * doc = sp_desktop_document(object->desk);
835 printf("1: %s\n2: %s\n3: %s\n", doc->getURI(), doc->getBase(), doc->getName());
836 if (doc->getURI())
837 return document_interface_save_as (object, doc->getURI(), error);
838 return FALSE;
839 }
841 gboolean
842 document_interface_load (DocumentInterface *object,
843 gchar *filename, GError **error)
844 {
845 desktop_ensure_active (object->desk);
846 const Glib::ustring file(filename);
847 sp_file_open(file, NULL, TRUE, TRUE);
848 if (object->updates)
849 Inkscape::DocumentUndo::done(sp_desktop_document(object->desk), SP_VERB_FILE_OPEN, "Opened File");
850 return TRUE;
851 }
853 gboolean
854 document_interface_save_as (DocumentInterface *object,
855 const gchar *filename, GError **error)
856 {
857 SPDocument * doc = sp_desktop_document(object->desk);
858 #ifdef WITH_GNOME_VFS
859 const Glib::ustring file(filename);
860 return file_save_remote(doc, file, NULL, TRUE, TRUE);
861 #endif
862 if (!doc || strlen(filename)<1) //Safety check
863 return false;
865 try {
866 Inkscape::Extension::save(NULL, doc, filename,
867 false, false, true, Inkscape::Extension::FILE_SAVE_METHOD_SAVE_AS);
868 } catch (...) {
869 //SP_ACTIVE_DESKTOP->messageStack()->flash(Inkscape::ERROR_MESSAGE, _("Document not saved."));
870 return false;
871 }
873 //SP_ACTIVE_DESKTOP->event_log->rememberFileSave();
874 //SP_ACTIVE_DESKTOP->messageStack()->flash(Inkscape::NORMAL_MESSAGE, "Document saved.");
875 return true;
876 }
878 gboolean
879 document_interface_mark_as_unmodified (DocumentInterface *object, GError **error)
880 {
881 SPDocument * doc = sp_desktop_document(object->desk);
882 if (doc)
883 doc->modified_since_save = FALSE;
884 return TRUE;
885 }
887 /*
888 gboolean
889 document_interface_print_to_file (DocumentInterface *object, GError **error)
890 {
891 SPDocument * doc = sp_desktop_document(object->desk);
892 sp_print_document_to_file (doc, g_strdup("/home/soren/test.pdf"));
894 return TRUE;
895 }
896 */
897 /****************************************************************************
898 PROGRAM CONTROL FUNCTIONS
899 ****************************************************************************/
901 gboolean
902 document_interface_close (DocumentInterface *object, GError **error)
903 {
904 return dbus_call_verb (object, SP_VERB_FILE_CLOSE_VIEW, error);
905 }
907 gboolean
908 document_interface_exit (DocumentInterface *object, GError **error)
909 {
910 return dbus_call_verb (object, SP_VERB_FILE_QUIT, error);
911 }
913 gboolean
914 document_interface_undo (DocumentInterface *object, GError **error)
915 {
916 return dbus_call_verb (object, SP_VERB_EDIT_UNDO, error);
917 }
919 gboolean
920 document_interface_redo (DocumentInterface *object, GError **error)
921 {
922 return dbus_call_verb (object, SP_VERB_EDIT_REDO, error);
923 }
927 /****************************************************************************
928 UPDATE FUNCTIONS
929 FIXME: This would work better by adding a flag to SPDesktop to prevent
930 updating but that would be very intrusive so for now there is a workaround.
931 Need to make sure it plays well with verbs because they are used so much.
932 ****************************************************************************/
934 void
935 document_interface_pause_updates (DocumentInterface *object, GError **error)
936 {
937 object->updates = FALSE;
938 object->desk->canvas->drawing_disabled = 1;
939 //object->desk->canvas->need_redraw = 0;
940 //object->desk->canvas->need_repick = 0;
941 //sp_desktop_document(object->desk)->root->uflags = FALSE;
942 //sp_desktop_document(object->desk)->root->mflags = FALSE;
943 }
945 void
946 document_interface_resume_updates (DocumentInterface *object, GError **error)
947 {
948 object->updates = TRUE;
949 object->desk->canvas->drawing_disabled = 0;
950 //object->desk->canvas->need_redraw = 1;
951 //object->desk->canvas->need_repick = 1;
952 //sp_desktop_document(object->desk)->root->uflags = TRUE;
953 //sp_desktop_document(object->desk)->root->mflags = TRUE;
954 //sp_desktop_document(object->desk)->_updateDocument();
955 //FIXME: use better verb than rect.
956 Inkscape::DocumentUndo::done(sp_desktop_document(object->desk), SP_VERB_CONTEXT_RECT, "Multiple actions");
957 }
959 void
960 document_interface_update (DocumentInterface *object, GError **error)
961 {
962 sp_desktop_document(object->desk)->root->uflags = TRUE;
963 sp_desktop_document(object->desk)->root->mflags = TRUE;
964 object->desk->enableInteraction();
965 sp_desktop_document(object->desk)->_updateDocument();
966 object->desk->disableInteraction();
967 sp_desktop_document(object->desk)->root->uflags = FALSE;
968 sp_desktop_document(object->desk)->root->mflags = FALSE;
969 //Inkscape::DocumentUndo::done(sp_desktop_document(object->desk), SP_VERB_CONTEXT_RECT, "Multiple actions");
970 }
972 /****************************************************************************
973 SELECTION FUNCTIONS FIXME: use call_verb where appropriate (once update system is tested.)
974 ****************************************************************************/
976 gboolean
977 document_interface_selection_get (DocumentInterface *object, char ***out, GError **error)
978 {
979 Inkscape::Selection * sel = sp_desktop_selection(object->desk);
980 GSList const *oldsel = sel->list();
982 int size = g_slist_length((GSList *) oldsel);
984 *out = g_new0 (char *, size + 1);
986 int i = 0;
987 for (GSList const *iter = oldsel; iter != NULL; iter = iter->next) {
988 (*out)[i] = g_strdup(SP_OBJECT(iter->data)->getRepr()->attribute("id"));
989 i++;
990 }
991 (*out)[i] = NULL;
993 return TRUE;
994 }
996 gboolean
997 document_interface_selection_add (DocumentInterface *object, char *name, GError **error)
998 {
999 SPObject * obj = get_object_by_name(object->desk, name, error);
1000 if (!obj)
1001 return FALSE;
1003 Inkscape::Selection *selection = sp_desktop_selection(object->desk);
1005 selection->add(obj);
1006 return TRUE;
1007 }
1009 gboolean
1010 document_interface_selection_add_list (DocumentInterface *object,
1011 char **names, GError **error)
1012 {
1013 int i;
1014 for (i=0;names[i] != NULL;i++) {
1015 document_interface_selection_add(object, names[i], error);
1016 }
1017 return TRUE;
1018 }
1020 gboolean
1021 document_interface_selection_set (DocumentInterface *object, char *name, GError **error)
1022 {
1023 SPDocument * doc = sp_desktop_document (object->desk);
1024 Inkscape::Selection *selection = sp_desktop_selection(object->desk);
1025 selection->set(doc->getObjectById(name));
1026 return TRUE;
1027 }
1029 gboolean
1030 document_interface_selection_set_list (DocumentInterface *object,
1031 gchar **names, GError **error)
1032 {
1033 sp_desktop_selection(object->desk)->clear();
1034 int i;
1035 for (i=0;names[i] != NULL;i++) {
1036 document_interface_selection_add(object, names[i], error);
1037 }
1038 return TRUE;
1039 }
1041 gboolean
1042 document_interface_selection_rotate (DocumentInterface *object, int angle, GError **error)
1043 {
1044 Inkscape::Selection *selection = sp_desktop_selection(object->desk);
1045 sp_selection_rotate(selection, angle);
1046 return TRUE;
1047 }
1049 gboolean
1050 document_interface_selection_delete (DocumentInterface *object, GError **error)
1051 {
1052 //sp_selection_delete (object->desk);
1053 return dbus_call_verb (object, SP_VERB_EDIT_DELETE, error);
1054 }
1056 gboolean
1057 document_interface_selection_clear (DocumentInterface *object, GError **error)
1058 {
1059 sp_desktop_selection(object->desk)->clear();
1060 return TRUE;
1061 }
1063 gboolean
1064 document_interface_select_all (DocumentInterface *object, GError **error)
1065 {
1066 //sp_edit_select_all (object->desk);
1067 return dbus_call_verb (object, SP_VERB_EDIT_SELECT_ALL, error);
1068 }
1070 gboolean
1071 document_interface_select_all_in_all_layers(DocumentInterface *object,
1072 GError **error)
1073 {
1074 //sp_edit_select_all_in_all_layers (object->desk);
1075 return dbus_call_verb (object, SP_VERB_EDIT_SELECT_ALL_IN_ALL_LAYERS, error);
1076 }
1078 gboolean
1079 document_interface_selection_box (DocumentInterface *object, int x, int y,
1080 int x2, int y2, gboolean replace,
1081 GError **error)
1082 {
1083 //FIXME: implement.
1084 return FALSE;
1085 }
1087 gboolean
1088 document_interface_selection_invert (DocumentInterface *object, GError **error)
1089 {
1090 //sp_edit_invert (object->desk);
1091 return dbus_call_verb (object, SP_VERB_EDIT_INVERT, error);
1092 }
1094 gboolean
1095 document_interface_selection_group (DocumentInterface *object, GError **error)
1096 {
1097 //sp_selection_group (object->desk);
1098 return dbus_call_verb (object, SP_VERB_SELECTION_GROUP, error);
1099 }
1100 gboolean
1101 document_interface_selection_ungroup (DocumentInterface *object, GError **error)
1102 {
1103 //sp_selection_ungroup (object->desk);
1104 return dbus_call_verb (object, SP_VERB_SELECTION_UNGROUP, error);
1105 }
1107 gboolean
1108 document_interface_selection_cut (DocumentInterface *object, GError **error)
1109 {
1110 //desktop_ensure_active (object->desk);
1111 //sp_selection_cut (object->desk);
1112 return dbus_call_verb (object, SP_VERB_EDIT_CUT, error);
1113 }
1115 gboolean
1116 document_interface_selection_copy (DocumentInterface *object, GError **error)
1117 {
1118 //desktop_ensure_active (object->desk);
1119 //sp_selection_copy ();
1120 return dbus_call_verb (object, SP_VERB_EDIT_COPY, error);
1121 }
1122 /*
1123 gboolean
1124 document_interface_selection_paste (DocumentInterface *object, GError **error)
1125 {
1126 desktop_ensure_active (object->desk);
1127 if (!object->updates)
1128 document_interface_pause_updates (object, error);
1129 sp_selection_paste (object->desk, TRUE);
1130 if (!object->updates)
1131 document_interface_pause_updates (object, error);
1132 return TRUE;
1133 //return dbus_call_verb (object, SP_VERB_EDIT_PASTE, error);
1134 }
1135 */
1136 gboolean
1137 document_interface_selection_paste (DocumentInterface *object, GError **error)
1138 {
1139 return dbus_call_verb (object, SP_VERB_EDIT_PASTE, error);
1140 }
1142 gboolean
1143 document_interface_selection_scale (DocumentInterface *object, gdouble grow, GError **error)
1144 {
1145 Inkscape::Selection *selection = sp_desktop_selection(object->desk);
1146 if (!selection)
1147 {
1148 return FALSE;
1149 }
1150 sp_selection_scale (selection, grow);
1151 return TRUE;
1152 }
1154 gboolean
1155 document_interface_selection_move (DocumentInterface *object, gdouble x, gdouble y, GError **error)
1156 {
1157 sp_selection_move (object->desk, x, 0 - y); //switching coordinate systems.
1158 return TRUE;
1159 }
1161 gboolean
1162 document_interface_selection_move_to (DocumentInterface *object, gdouble x, gdouble y, GError **error)
1163 {
1164 Inkscape::Selection * sel = sp_desktop_selection(object->desk);
1166 Geom::OptRect sel_bbox = sel->bounds();
1167 if (sel_bbox) {
1168 Geom::Point m( x - selection_get_center_x(sel) , 0 - (y - selection_get_center_y(sel)) );
1169 sp_selection_move_relative(sel, m, true);
1170 }
1171 return TRUE;
1172 }
1174 //FIXME: does not paste in new layer.
1175 // This needs to use lower level cut_impl and paste_impl (messy)
1176 // See the built-in sp_selection_to_next_layer and duplicate.
1177 gboolean
1178 document_interface_selection_move_to_layer (DocumentInterface *object,
1179 gchar *layerstr, GError **error)
1180 {
1181 SPDesktop * dt = object->desk;
1183 Inkscape::Selection *selection = sp_desktop_selection(dt);
1185 // check if something is selected
1186 if (selection->isEmpty())
1187 return FALSE;
1189 SPObject *next = get_object_by_name(object->desk, layerstr, error);
1191 if (!next)
1192 return FALSE;
1194 if (strcmp("layer", (next->getRepr())->attribute("inkscape:groupmode")) == 0) {
1196 sp_selection_cut(dt);
1198 dt->setCurrentLayer(next);
1200 sp_selection_paste(dt, TRUE);
1201 }
1202 return TRUE;
1203 }
1205 GArray *
1206 document_interface_selection_get_center (DocumentInterface *object)
1207 {
1208 Inkscape::Selection * sel = sp_desktop_selection(object->desk);
1210 if (sel)
1211 {
1212 gdouble x = selection_get_center_x(sel);
1213 gdouble y = selection_get_center_y(sel);
1214 GArray * intArr = g_array_new (TRUE, TRUE, sizeof(double));
1216 g_array_append_val (intArr, x);
1217 g_array_append_val (intArr, y);
1218 return intArr;
1219 }
1221 return NULL;
1222 }
1224 gboolean
1225 document_interface_selection_to_path (DocumentInterface *object, GError **error)
1226 {
1227 return dbus_call_verb (object, SP_VERB_OBJECT_TO_CURVE, error);
1228 }
1231 gchar *
1232 document_interface_selection_combine (DocumentInterface *object, gchar *cmd,
1233 GError **error)
1234 {
1235 if (strcmp(cmd, "union") == 0)
1236 dbus_call_verb (object, SP_VERB_SELECTION_UNION, error);
1237 else if (strcmp(cmd, "intersection") == 0)
1238 dbus_call_verb (object, SP_VERB_SELECTION_INTERSECT, error);
1239 else if (strcmp(cmd, "difference") == 0)
1240 dbus_call_verb (object, SP_VERB_SELECTION_DIFF, error);
1241 else if (strcmp(cmd, "exclusion") == 0)
1242 dbus_call_verb (object, SP_VERB_SELECTION_SYMDIFF, error);
1243 else
1244 return NULL;
1246 if (sp_desktop_selection(object->desk)->singleRepr() != NULL)
1247 return g_strdup((sp_desktop_selection(object->desk)->singleRepr())->attribute("id"));
1248 return NULL;
1249 }
1251 gboolean
1252 document_interface_selection_divide (DocumentInterface *object, char ***out, GError **error)
1253 {
1254 dbus_call_verb (object, SP_VERB_SELECTION_CUT, error);
1256 return document_interface_selection_get (object, out, error);
1257 }
1259 gboolean
1260 document_interface_selection_change_level (DocumentInterface *object, gchar *cmd,
1261 GError **error)
1262 {
1263 if (strcmp(cmd, "raise") == 0)
1264 return dbus_call_verb (object, SP_VERB_SELECTION_RAISE, error);
1265 if (strcmp(cmd, "lower") == 0)
1266 return dbus_call_verb (object, SP_VERB_SELECTION_LOWER, error);
1267 if ((strcmp(cmd, "to_top") == 0) || (strcmp(cmd, "to_front") == 0))
1268 return dbus_call_verb (object, SP_VERB_SELECTION_TO_FRONT, error);
1269 if ((strcmp(cmd, "to_bottom") == 0) || (strcmp(cmd, "to_back") == 0))
1270 return dbus_call_verb (object, SP_VERB_SELECTION_TO_BACK, error);
1271 return TRUE;
1272 }
1274 /****************************************************************************
1275 LAYER FUNCTIONS
1276 ****************************************************************************/
1278 gchar *
1279 document_interface_layer_new (DocumentInterface *object, GError **error)
1280 {
1281 SPDesktop * dt = object->desk;
1282 SPObject *new_layer = Inkscape::create_layer(dt->currentRoot(), dt->currentLayer(), Inkscape::LPOS_BELOW);
1283 dt->setCurrentLayer(new_layer);
1284 return g_strdup(get_name_from_object (new_layer));
1285 }
1287 gboolean
1288 document_interface_layer_set (DocumentInterface *object,
1289 gchar *layerstr, GError **error)
1290 {
1291 SPObject * obj = get_object_by_name (object->desk, layerstr, error);
1293 if (!obj)
1294 return FALSE;
1296 object->desk->setCurrentLayer (obj);
1297 return TRUE;
1298 }
1300 gchar **
1301 document_interface_layer_get_all (DocumentInterface *object)
1302 {
1303 //FIXME: implement.
1304 return NULL;
1305 }
1307 gboolean
1308 document_interface_layer_change_level (DocumentInterface *object,
1309 gchar *cmd, GError **error)
1310 {
1311 if (strcmp(cmd, "raise") == 0)
1312 return dbus_call_verb (object, SP_VERB_LAYER_RAISE, error);
1313 if (strcmp(cmd, "lower") == 0)
1314 return dbus_call_verb (object, SP_VERB_LAYER_LOWER, error);
1315 if ((strcmp(cmd, "to_top") == 0) || (strcmp(cmd, "to_front") == 0))
1316 return dbus_call_verb (object, SP_VERB_LAYER_TO_TOP, error);
1317 if ((strcmp(cmd, "to_bottom") == 0) || (strcmp(cmd, "to_back") == 0))
1318 return dbus_call_verb (object, SP_VERB_LAYER_TO_BOTTOM, error);
1319 return TRUE;
1320 }
1322 gboolean
1323 document_interface_layer_next (DocumentInterface *object, GError **error)
1324 {
1325 return dbus_call_verb (object, SP_VERB_LAYER_NEXT, error);
1326 }
1328 gboolean
1329 document_interface_layer_previous (DocumentInterface *object, GError **error)
1330 {
1331 return dbus_call_verb (object, SP_VERB_LAYER_PREV, error);
1332 }