Code

moving trunk for module inkscape
[inkscape.git] / src / path-chemistry.cpp
1 #define __SP_PATH_CHEMISTRY_C__
3 /*
4  * Here are handlers for modifying selections, specific to paths
5  *
6  * Authors:
7  *   Lauris Kaplinski <lauris@kaplinski.com>
8  *   bulia byak <buliabyak@users.sf.net>
9  *
10  * Copyright (C) 1999-2004 Authors
11  * Copyright (C) 2001-2002 Ximian, Inc.
12  *
13  * Released under GNU GPL, read the file 'COPYING' for more information
14  */
16 #ifdef HAVE_CONFIG_H
17 # include <config.h>
18 #endif
19 #include "xml/repr.h"
20 #include "svg/svg.h"
21 #include "display/curve.h"
22 #include <glibmm/i18n.h>
23 #include "sp-path.h"
24 #include "sp-text.h"
25 #include "sp-flowtext.h"
26 #include "libnr/nr-path.h"
27 #include "text-editing.h"
28 #include "style.h"
29 #include "inkscape.h"
30 #include "document.h"
31 #include "message-stack.h"
32 #include "selection.h"
33 #include "desktop-handles.h"
35 /* Helper functions for sp_selected_path_to_curves */
36 static void sp_selected_path_to_curves0 (gboolean do_document_done, guint32 text_grouping_policy);
37 static Inkscape::XML::Node * sp_selected_item_to_curved_repr(SPItem * item, guint32 text_grouping_policy);
38 enum {                          
39   /* Not used yet. This is the placeholder of Lauris's idea. */
40         SP_TOCURVE_INTERACTIVE       = 1 << 0,
41         SP_TOCURVE_GROUPING_BY_WORD  = 1 << 1,
42         SP_TOCURVE_GROUPING_BY_LINE  = 1 << 2,
43         SP_TOCURVE_GROUPING_BY_WHOLE = 1 << 3
44 };
46 void
47 sp_selected_path_combine (void)
48 {
49         SPDesktop *desktop = SP_ACTIVE_DESKTOP;
50         
51         Inkscape::Selection *selection = SP_DT_SELECTION (desktop);
52         GSList *items = (GSList *) selection->itemList();
54         if (g_slist_length (items) < 2) {
55             SP_DT_MSGSTACK(desktop)->flash(Inkscape::WARNING_MESSAGE, _("Select <b>at least two objects</b> to combine."));
56                 return;
57         }
59         for (GSList *i = items; i != NULL; i = i->next) {
60                 SPItem *item = (SPItem *) i->data;
61                 if (!SP_IS_SHAPE (item) && !SP_IS_TEXT(item)) {
62                     SP_DT_MSGSTACK(desktop)->flash(Inkscape::WARNING_MESSAGE, _("At least one of the objects is <b>not a path</b>, cannot combine."));
63                     return;
64                 }
65         }
67         Inkscape::XML::Node *parent = SP_OBJECT_REPR ((SPItem *) items->data)->parent();
68         for (GSList *i = items; i != NULL; i = i->next) {
69                 if ( SP_OBJECT_REPR ((SPItem *) i->data)->parent() != parent ) {
70                     SP_DT_MSGSTACK(desktop)->flash(Inkscape::ERROR_MESSAGE, _("You cannot combine objects from <b>different groups</b> or <b>layers</b>."));
71                     return;
72                 }
73         }
75         sp_selected_path_to_curves0 (FALSE, 0);
77         items = (GSList *) selection->itemList();
79         items = g_slist_copy (items);
81         items = g_slist_sort (items, (GCompareFunc) sp_item_repr_compare_position);
83         // remember the position of the topmost object
84         gint topmost = (SP_OBJECT_REPR ((SPItem *) g_slist_last(items)->data))->position();
86         // remember the id of the bottomost object
87         const char *id = SP_OBJECT_REPR ((SPItem *) items->data)->attribute("id");
89         // FIXME: merge styles of combined objects instead of using the first one's style
90         gchar *style = g_strdup (SP_OBJECT_REPR ((SPItem *) items->data)->attribute("style"));
92         GString *dstring = g_string_new("");
93         for (GSList *i = items; i != NULL; i = i->next) {
95                 SPPath *path = (SPPath *) i->data;
96                 SPCurve *c = sp_shape_get_curve (SP_SHAPE (path));
97                 
98                 NArtBpath *abp = nr_artpath_affine(c->bpath, SP_ITEM(path)->transform);
99                 sp_curve_unref (c);
100                 gchar *str = sp_svg_write_path (abp);
101                 nr_free (abp);
103                 dstring = g_string_append(dstring, str);
104                 g_free (str);
106                 // if this is the bottommost object,
107                 if (!strcmp (SP_OBJECT_REPR (path)->attribute("id"), id)) {
108                         // delete it so that its clones don't get alerted; this object will be restored shortly, with the same id
109                         SP_OBJECT (path)->deleteObject(false);
110                 } else {
111                         // delete the object for real, so that its clones can take appropriate action
112                         SP_OBJECT (path)->deleteObject();
113                 }
115                 topmost --;
116         }
118         g_slist_free (items);
120         Inkscape::XML::Node *repr = sp_repr_new ("svg:path");
122         // restore id
123         repr->setAttribute("id", id);
125         repr->setAttribute("style", style);
126         g_free (style);
128         repr->setAttribute("d", dstring->str);
129         g_string_free (dstring, TRUE);
131         // add the new group to the group members' common parent
132         parent->appendChild(repr);
134         // move to the position of the topmost, reduced by the number of deleted items
135         repr->setPosition(topmost > 0 ? topmost + 1 : 0);
137         sp_document_done (SP_DT_DOCUMENT (desktop));
139         selection->set(repr);
141         Inkscape::GC::release(repr);
144 void
145 sp_selected_path_break_apart (void)
147         SPDesktop *desktop = SP_ACTIVE_DESKTOP;
148         
149         Inkscape::Selection *selection = SP_DT_SELECTION (desktop);
151         if (selection->isEmpty()) {
152             SP_DT_MSGSTACK(desktop)->flash(Inkscape::WARNING_MESSAGE, _("Select <b>path(s)</b> to break apart."));
153                 return;
154         }
156         bool did = false;
158         for (GSList *items = g_slist_copy((GSList *) selection->itemList());
159                          items != NULL;
160                          items = items->next) {
162                 SPItem *item = (SPItem *) items->data;
164                 if (!SP_IS_PATH (item)) 
165                         continue;
167                 SPPath *path = SP_PATH (item);
169                 SPCurve *curve = sp_shape_get_curve (SP_SHAPE (path));
170                 if (curve == NULL) 
171                         continue;
173                 did = true;
175                 Inkscape::XML::Node *parent = SP_OBJECT_REPR (item)->parent();
176                 gint pos = SP_OBJECT_REPR (item)->position();
177                 const char *id = SP_OBJECT_REPR (item)->attribute("id");
179                 gchar *style = g_strdup (SP_OBJECT (item)->repr->attribute("style"));
181                 NArtBpath *abp = nr_artpath_affine (curve->bpath, (SP_ITEM (path))->transform);
183                 sp_curve_unref (curve);
185                 // it's going to resurrect as one of the pieces, so we delete without advertisement
186                 SP_OBJECT (item)->deleteObject(false);
188                 curve = sp_curve_new_from_bpath (abp);
189                 g_assert (curve != NULL);
191                 GSList *list = sp_curve_split (curve);
193                 sp_curve_unref (curve);
195                 for (GSList *l = g_slist_reverse(list); l != NULL; l = l->next) {
196                         curve = (SPCurve *) l->data;
198                         Inkscape::XML::Node *repr = sp_repr_new ("svg:path");
199                         repr->setAttribute("style", style);
201                         gchar *str = sp_svg_write_path (curve->bpath);
202                         repr->setAttribute("d", str);
203                         g_free (str);
205                         // add the new repr to the parent
206                         parent->appendChild(repr);
208                         // move to the saved position 
209                         repr->setPosition(pos > 0 ? pos : 0);
211                         // if it's the first one, restore id
212                         if (l == list)
213                                 repr->setAttribute("id", id);
215                         selection->add(repr);
217                         Inkscape::GC::release(repr);
218                 }
220                 g_slist_free (list);
221                 g_free (style);
223         }
225         if (did) {
226                 sp_document_done (SP_DT_DOCUMENT (desktop));
227         } else {
228             SP_DT_MSGSTACK(desktop)->flash(Inkscape::ERROR_MESSAGE, _("<b>No path(s)</b> to break apart in the selection."));
229             return;
230         } 
233 /* This function is an entry point from GUI */
234 void
235 sp_selected_path_to_curves (void)
237         sp_selected_path_to_curves0(TRUE, SP_TOCURVE_INTERACTIVE);
240 static void
241 sp_selected_path_to_curves0 (gboolean interactive, guint32 text_grouping_policy)
243         SPDesktop *desktop = SP_ACTIVE_DESKTOP;
245         Inkscape::Selection *selection = SP_DT_SELECTION (desktop);
247         if (selection->isEmpty()) {
248                 if (interactive)
249                     SP_DT_MSGSTACK(desktop)->flash(Inkscape::WARNING_MESSAGE, _("Select <b>object(s)</b> to convert to path."));
250                 return;
251         }
253         bool did = false;
255         for (GSList *items = g_slist_copy((GSList *) selection->itemList());
256                          items != NULL;
257                          items = items->next) {
259                 SPItem *item = SP_ITEM (items->data);
261                 Inkscape::XML::Node *repr = sp_selected_item_to_curved_repr (item, 0);
262                 if (!repr)
263                         continue;
264                 
265                 did = true;
267                 // remember the position of the item
268                 gint pos = SP_OBJECT_REPR (item)->position();
269                 // remember parent
270                 Inkscape::XML::Node *parent = SP_OBJECT_REPR (item)->parent();
271                 // remember id
272                 const char *id = SP_OBJECT_REPR (item)->attribute("id");
274                 selection->remove(item);
276                 // it's going to resurrect, so we delete without advertisement
277                 SP_OBJECT (item)->deleteObject(false);
279                 // restore id
280                 repr->setAttribute("id", id);
281                 // add the new repr to the parent
282                 parent->appendChild(repr);
283                 // move to the saved position 
284                 repr->setPosition(pos > 0 ? pos : 0);
286                 selection->add(repr);
287                 Inkscape::GC::release(repr);
288         }
290         if (interactive) {
291                 if (did) {
292                         sp_document_done (SP_DT_DOCUMENT (desktop));
293                 } else {
294                     SP_DT_MSGSTACK(desktop)->flash(Inkscape::ERROR_MESSAGE, _("<b>No objects</b> to convert to path in the selection."));
295                     return;
296                 }
297         }
300 static Inkscape::XML::Node *
301 sp_selected_item_to_curved_repr(SPItem * item, guint32 text_grouping_policy)
303         if (!item)
304           return NULL;
306         SPCurve *curve = NULL;
307         if (SP_IS_SHAPE (item)) {
308                 curve = sp_shape_get_curve (SP_SHAPE (item));
309         } else if (SP_IS_TEXT (item) || SP_IS_FLOWTEXT (item)) {
310                 curve = te_get_layout(item)->convertToCurves ();
311         }
312         
313         if (!curve)
314           return NULL;
315         
316         Inkscape::XML::Node *repr = sp_repr_new ("svg:path");
317         /* Transformation */
318         repr->setAttribute("transform", SP_OBJECT_REPR (item)->attribute("transform"));
319         /* Style */
320         gchar *style_str = sp_style_write_difference (SP_OBJECT_STYLE (item), 
321                                                SP_OBJECT_STYLE (SP_OBJECT_PARENT (item)));
322         repr->setAttribute("style", style_str);
323         g_free (style_str);
325         /* Definition */
326         gchar *def_str = sp_svg_write_path (curve->bpath);
327         repr->setAttribute("d", def_str);
328         g_free (def_str);
329         sp_curve_unref (curve);
330         return repr;
333 void
334 sp_selected_path_reverse ()
336         SPDesktop *desktop = SP_ACTIVE_DESKTOP;
337         
338         Inkscape::Selection *selection = SP_DT_SELECTION (desktop);
339         GSList *items = (GSList *) selection->itemList();
341         if (g_slist_length (items) == 0) {
342             SP_DT_MSGSTACK(desktop)->flash(Inkscape::WARNING_MESSAGE, _("Select <b>path(s)</b> to reverse."));
343             return;
344         }
347         bool did = false;
348         for (GSList *i = items; i != NULL; i = i->next) {
350                 if (!SP_IS_SHAPE (items->data))
351                         continue;
353                 did = true;
354                 SPShape *shape = SP_SHAPE (items->data);
356                 SPCurve *rcurve = sp_curve_reverse (shape->curve);
358                 char *str = sp_svg_write_path (rcurve->bpath);
359                 SP_OBJECT_REPR (shape)->setAttribute("d", str);
361                 sp_curve_unref (rcurve);
362         }
364         if (did) {
365                 sp_document_done (SP_DT_DOCUMENT (desktop));
366         } else {
367             SP_DT_MSGSTACK(desktop)->flash(Inkscape::ERROR_MESSAGE, _("<b>No paths</b> to reverse in the selection."));
368         }