X-Git-Url: https://git.tokkee.org/?a=blobdiff_plain;f=src%2Fselection-chemistry.cpp;h=1225a66f369dcbef3c0d23e87a79407ff4e2bcfd;hb=4832d71a0d49fe090b917e233370be20d1d72884;hp=0ba60a91cac30868e47ed6d025261d3e3b23fd5e;hpb=8aa49e31e078deab4a1383d7adf83e96f9979533;p=inkscape.git diff --git a/src/selection-chemistry.cpp b/src/selection-chemistry.cpp index 0ba60a91c..1225a66f3 100644 --- a/src/selection-chemistry.cpp +++ b/src/selection-chemistry.cpp @@ -8,8 +8,9 @@ * Frank Felfe * MenTaLguY * bulia byak + * Andrius R. * - * Copyright (C) 1999-2005 authors + * Copyright (C) 1999-2006 authors * Copyright (C) 2001-2002 Ximian, Inc. * * Released under GNU GPL, read the file 'COPYING' for more information @@ -38,6 +39,9 @@ #include "sp-flowregion.h" #include "text-editing.h" #include "text-context.h" +#include "connector-context.h" +#include "sp-path.h" +#include "sp-conn-end.h" #include "dropper-context.h" #include #include "libnr/nr-matrix-rotate-ops.h" @@ -58,9 +62,14 @@ #include "sp-namedview.h" #include "prefs-utils.h" #include "sp-offset.h" +#include "sp-clippath.h" +#include "sp-mask.h" #include "file.h" #include "layer-fns.h" #include "context-fns.h" +#include +#include "helper/units.h" +#include "sp-item.h" using NR::X; using NR::Y; @@ -70,6 +79,7 @@ using NR::Y; GSList *clipboard = NULL; GSList *defs_clipboard = NULL; SPCSSAttr *style_clipboard = NULL; +NR::Rect size_clipboard(NR::Point(0,0), NR::Point(0,0)); static void sp_copy_stuff_used_by_item(GSList **defs_clip, SPItem *item, const GSList *items); @@ -129,7 +139,7 @@ void sp_selection_copy_impl (const GSList *items, GSList **clip, GSList **defs_c /** Add gradients/patterns/markers referenced by copied objects to defs */ -void +void paste_defs (GSList **defs_clip, SPDocument *doc) { if (!defs_clip) @@ -203,11 +213,11 @@ void sp_selection_delete() if (tools_isactive (desktop, TOOLS_TEXT)) if (sp_text_delete_selection(desktop->event_context)) { - sp_document_done(SP_DT_DOCUMENT(desktop)); + sp_document_done(sp_desktop_document(desktop)); return; } - Inkscape::Selection *selection = SP_DT_SELECTION(desktop); + Inkscape::Selection *selection = sp_desktop_selection(desktop); // check if something is selected if (selection->isEmpty()) { @@ -228,7 +238,7 @@ void sp_selection_delete() */ tools_switch ( desktop, tools_active ( desktop ) ); - sp_document_done(SP_DT_DOCUMENT(desktop)); + sp_document_done(sp_desktop_document(desktop)); } /* fixme: sequencing */ @@ -238,7 +248,7 @@ void sp_selection_duplicate() if (desktop == NULL) return; - Inkscape::Selection *selection = SP_DT_SELECTION(desktop); + Inkscape::Selection *selection = sp_desktop_selection(desktop); // check if something is selected if (selection->isEmpty()) { @@ -266,7 +276,7 @@ void sp_selection_duplicate() Inkscape::GC::release(copy); } - sp_document_done(SP_DT_DOCUMENT(desktop)); + sp_document_done(sp_desktop_document(desktop)); selection->setReprList(newsel); @@ -279,8 +289,8 @@ void sp_edit_clear_all() if (!dt) return; - SPDocument *doc = SP_DT_DOCUMENT(dt); - SP_DT_SELECTION(dt)->clear(); + SPDocument *doc = sp_desktop_document(dt); + sp_desktop_selection(dt)->clear(); g_return_if_fail(SP_IS_GROUP(dt->currentLayer())); GSList *items = sp_item_group_item_list(SP_GROUP(dt->currentLayer())); @@ -299,7 +309,7 @@ get_all_items (GSList *list, SPObject *from, SPDesktop *desktop, bool onlyvisibl for (SPObject *child = sp_object_first_child(SP_OBJECT(from)) ; child != NULL; child = SP_OBJECT_NEXT(child) ) { if (SP_IS_ITEM(child) && !desktop->isLayer(SP_ITEM(child)) && - (!onlysensitive || !SP_ITEM(child)->isLocked()) && + (!onlysensitive || !SP_ITEM(child)->isLocked()) && (!onlyvisible || !desktop->itemIsHidden(SP_ITEM(child))) && (!exclude || !g_slist_find ((GSList *) exclude, child)) ) @@ -321,11 +331,11 @@ void sp_edit_select_all_full (bool force_all_layers, bool invert) if (!dt) return; - Inkscape::Selection *selection = SP_DT_SELECTION(dt); + Inkscape::Selection *selection = sp_desktop_selection(dt); g_return_if_fail(SP_IS_GROUP(dt->currentLayer())); - bool inlayer = prefs_get_int_attribute ("options.kbselection", "inlayer", 1); + PrefsSelectionContext inlayer = (PrefsSelectionContext)prefs_get_int_attribute ("options.kbselection", "inlayer", PREFS_SELECTION_LAYER); bool onlyvisible = prefs_get_int_attribute ("options.kbselection", "onlyvisible", 1); bool onlysensitive = prefs_get_int_attribute ("options.kbselection", "onlysensitive", 1); @@ -336,8 +346,11 @@ void sp_edit_select_all_full (bool force_all_layers, bool invert) exclude = selection->itemList(); } - if (inlayer && !force_all_layers) { + if (force_all_layers) + inlayer = PREFS_SELECTION_ALL; + switch (inlayer) { + case PREFS_SELECTION_LAYER: { if ( (onlysensitive && SP_ITEM(dt->currentLayer())->isLocked()) || (onlyvisible && dt->itemIsHidden(SP_ITEM(dt->currentLayer()))) ) return; @@ -359,9 +372,16 @@ void sp_edit_select_all_full (bool force_all_layers, bool invert) } g_slist_free (all_items); - - } else { + break; + } + case PREFS_SELECTION_LAYER_RECURSIVE: { + items = get_all_items (NULL, dt->currentLayer(), dt, onlyvisible, onlysensitive, exclude); + break; + } + default: { items = get_all_items (NULL, dt->currentRoot(), dt, onlyvisible, onlysensitive, exclude); + break; + } } selection->setList (items); @@ -397,9 +417,9 @@ void sp_selection_group() if (desktop == NULL) return; - SPDocument *document = SP_DT_DOCUMENT (desktop); + SPDocument *document = sp_desktop_document (desktop); - Inkscape::Selection *selection = SP_DT_SELECTION(desktop); + Inkscape::Selection *selection = sp_desktop_selection(desktop); // Check if something is selected. if (selection->isEmpty()) { @@ -481,7 +501,7 @@ void sp_selection_group() // Move to the position of the topmost, reduced by the number of items deleted from topmost_parent group->setPosition(topmost + 1); - sp_document_done(SP_DT_DOCUMENT(desktop)); + sp_document_done(sp_desktop_document(desktop)); selection->set(group); Inkscape::GC::release(group); @@ -493,7 +513,7 @@ void sp_selection_ungroup() if (desktop == NULL) return; - Inkscape::Selection *selection = SP_DT_SELECTION(desktop); + Inkscape::Selection *selection = sp_desktop_selection(desktop); if (selection->isEmpty()) { desktop->messageStack()->flash(Inkscape::WARNING_MESSAGE, _("Select a group to ungroup.")); @@ -542,7 +562,7 @@ void sp_selection_ungroup() g_slist_free(items); - sp_document_done(SP_DT_DOCUMENT(desktop)); + sp_document_done(sp_desktop_document(desktop)); } static SPGroup * @@ -602,7 +622,7 @@ sp_selection_raise() if (!desktop) return; - Inkscape::Selection *selection = SP_DT_SELECTION(desktop); + Inkscape::Selection *selection = sp_desktop_selection(desktop); GSList const *items = (GSList *) selection->itemList(); if (!items) { @@ -643,7 +663,7 @@ sp_selection_raise() rev = g_slist_remove(rev, child); } - sp_document_done(SP_DT_DOCUMENT(desktop)); + sp_document_done(sp_desktop_document(desktop)); } void sp_selection_raise_to_top() @@ -652,8 +672,8 @@ void sp_selection_raise_to_top() if (desktop == NULL) return; - SPDocument *document = SP_DT_DOCUMENT(desktop); - Inkscape::Selection *selection = SP_DT_SELECTION(desktop); + SPDocument *document = sp_desktop_document(desktop); + Inkscape::Selection *selection = sp_desktop_selection(desktop); if (selection->isEmpty()) { desktop->messageStack()->flash(Inkscape::WARNING_MESSAGE, _("Select object(s) to raise to top.")); @@ -688,7 +708,7 @@ sp_selection_lower() if (desktop == NULL) return; - Inkscape::Selection *selection = SP_DT_SELECTION(desktop); + Inkscape::Selection *selection = sp_desktop_selection(desktop); GSList const *items = (GSList *) selection->itemList(); if (!items) { @@ -734,7 +754,7 @@ sp_selection_lower() rev = g_slist_remove(rev, child); } - sp_document_done(SP_DT_DOCUMENT(desktop)); + sp_document_done(sp_desktop_document(desktop)); } @@ -744,8 +764,8 @@ void sp_selection_lower_to_bottom() if (desktop == NULL) return; - SPDocument *document = SP_DT_DOCUMENT(desktop); - Inkscape::Selection *selection = SP_DT_SELECTION(desktop); + SPDocument *document = sp_desktop_document(desktop); + Inkscape::Selection *selection = sp_desktop_selection(desktop); if (selection->isEmpty()) { desktop->messageStack()->flash(Inkscape::WARNING_MESSAGE, _("Select object(s) to lower to bottom.")); @@ -786,16 +806,16 @@ void sp_selection_lower_to_bottom() } void -sp_undo(SPDesktop *desktop, SPDocument *doc) +sp_undo(SPDesktop *desktop, SPDocument *) { - if (!sp_document_undo(SP_DT_DOCUMENT(desktop))) + if (!sp_document_undo(sp_desktop_document(desktop))) desktop->messageStack()->flash(Inkscape::WARNING_MESSAGE, _("Nothing to undo.")); } void -sp_redo(SPDesktop *desktop, SPDocument *doc) +sp_redo(SPDesktop *desktop, SPDocument *) { - if (!sp_document_redo(SP_DT_DOCUMENT(desktop))) + if (!sp_document_redo(sp_desktop_document(desktop))) desktop->messageStack()->flash(Inkscape::WARNING_MESSAGE, _("Nothing to redo.")); } @@ -809,7 +829,7 @@ void sp_copy_gradient (GSList **defs_clip, SPGradient *gradient) { SPGradient *ref = gradient; - while (ref) { + while (ref) { // climb up the refs, copying each one in the chain Inkscape::XML::Node *grad_repr = SP_OBJECT_REPR(ref)->duplicate(); *defs_clip = g_slist_prepend (*defs_clip, grad_repr); @@ -838,10 +858,10 @@ void sp_copy_pattern (GSList **defs_clip, SPPattern *pattern) } } -void sp_copy_marker (GSList **defs_clip, SPMarker *marker) +void sp_copy_single (GSList **defs_clip, SPObject *thing) { - Inkscape::XML::Node *marker_repr = SP_OBJECT_REPR(marker)->duplicate(); - *defs_clip = g_slist_prepend (*defs_clip, marker_repr); + Inkscape::XML::Node *duplicate_repr = SP_OBJECT_REPR(thing)->duplicate(); + *defs_clip = g_slist_prepend (*defs_clip, duplicate_repr); } @@ -858,9 +878,9 @@ void sp_copy_textpath_path (GSList **defs_clip, SPTextPath *tp, const GSList *it void sp_copy_stuff_used_by_item (GSList **defs_clip, SPItem *item, const GSList *items) { - SPStyle *style = SP_OBJECT_STYLE (item); + SPStyle *style = SP_OBJECT_STYLE (item); - if (style && (style->fill.type == SP_PAINT_TYPE_PAINTSERVER)) { + if (style && (style->fill.type == SP_PAINT_TYPE_PAINTSERVER)) { SPObject *server = SP_OBJECT_STYLE_FILL_SERVER(item); if (SP_IS_LINEARGRADIENT (server) || SP_IS_RADIALGRADIENT (server)) sp_copy_gradient (defs_clip, SP_GRADIENT(server)); @@ -868,7 +888,7 @@ void sp_copy_stuff_used_by_item (GSList **defs_clip, SPItem *item, const GSList sp_copy_pattern (defs_clip, SP_PATTERN(server)); } - if (style && (style->stroke.type == SP_PAINT_TYPE_PAINTSERVER)) { + if (style && (style->stroke.type == SP_PAINT_TYPE_PAINTSERVER)) { SPObject *server = SP_OBJECT_STYLE_STROKE_SERVER(item); if (SP_IS_LINEARGRADIENT (server) || SP_IS_RADIALGRADIENT (server)) sp_copy_gradient (defs_clip, SP_GRADIENT(server)); @@ -876,11 +896,11 @@ void sp_copy_stuff_used_by_item (GSList **defs_clip, SPItem *item, const GSList sp_copy_pattern (defs_clip, SP_PATTERN(server)); } - if (SP_IS_SHAPE (item)) { + if (SP_IS_SHAPE (item)) { SPShape *shape = SP_SHAPE (item); for (int i = 0 ; i < SP_MARKER_LOC_QTY ; i++) { if (shape->marker[i]) { - sp_copy_marker (defs_clip, SP_MARKER (shape->marker[i])); + sp_copy_single (defs_clip, SP_OBJECT (shape->marker[i])); } } } @@ -889,6 +909,20 @@ void sp_copy_stuff_used_by_item (GSList **defs_clip, SPItem *item, const GSList sp_copy_textpath_path (defs_clip, SP_TEXTPATH(sp_object_first_child(SP_OBJECT(item))), items); } + if (item->clip_ref->getObject()) { + sp_copy_single (defs_clip, item->clip_ref->getObject()); + } + + if (item->mask_ref->getObject()) { + SPObject *mask = item->mask_ref->getObject(); + sp_copy_single (defs_clip, mask); + // recurse into the mask for its gradients etc. + for (SPObject *o = SP_OBJECT(mask)->children; o != NULL; o = o->next) { + if (SP_IS_ITEM(o)) + sp_copy_stuff_used_by_item (defs_clip, SP_ITEM (o), items); + } + } + // recurse for (SPObject *o = SP_OBJECT(item)->children; o != NULL; o = o->next) { if (SP_IS_ITEM(o)) @@ -943,7 +977,7 @@ void sp_selection_copy() if (desktop == NULL) return; - Inkscape::Selection *selection = SP_DT_SELECTION(desktop); + Inkscape::Selection *selection = sp_desktop_selection(desktop); if (tools_isactive (desktop, TOOLS_DROPPER)) { sp_dropper_context_copy(desktop->event_context); @@ -959,7 +993,7 @@ void sp_selection_copy() const GSList *items = g_slist_copy ((GSList *) selection->itemList()); // 0. Copy text to system clipboard - // FIXME: for non-texts, put serialized XML as text to the clipboard; + // FIXME: for non-texts, put serialized XML as text to the clipboard; //for this sp_repr_write_stream needs to be rewritten with iostream instead of FILE Glib::ustring text; if (tools_isactive (desktop, TOOLS_TEXT)) { @@ -998,8 +1032,8 @@ void sp_selection_copy() sp_repr_css_attr_unref (style_clipboard); style_clipboard = NULL; } - - //clear main clipboard + + //clear main clipboard while (clipboard) { Inkscape::GC::release((Inkscape::XML::Node *) clipboard->data); clipboard = g_slist_remove(clipboard, clipboard->data); @@ -1024,24 +1058,26 @@ void sp_selection_copy() g_free (query); } + size_clipboard = selection->bounds(); + g_slist_free ((GSList *) items); } void sp_selection_paste(bool in_place) { SPDesktop *desktop = SP_ACTIVE_DESKTOP; - + if (desktop == NULL) { return; } - SPDocument *document = SP_DT_DOCUMENT(desktop); + SPDocument *document = sp_desktop_document(desktop); if (Inkscape::have_viable_layer(desktop, desktop->messageStack()) == false) { return; } - Inkscape::Selection *selection = SP_DT_SELECTION(desktop); + Inkscape::Selection *selection = sp_desktop_selection(desktop); if (tools_isactive (desktop, TOOLS_TEXT)) { if (sp_text_paste_inline(desktop->event_context)) @@ -1066,7 +1102,7 @@ void sp_selection_paste(bool in_place) /* Snap the offset of the new item(s) to the grid */ /* FIXME: this gridsnap fiddling is a hack. */ - Inkscape::GridSnapper &s = desktop->namedview->grid_snapper; + Inkscape::GridSnapper &s = desktop->namedview->snap_manager.grid; gdouble const curr_gridsnap = s.getDistance(); s.setDistance(NR_HUGE); m = s.freeSnap(Inkscape::Snapper::SNAP_POINT, m, NULL).getPoint(); @@ -1082,7 +1118,7 @@ void sp_selection_paste_style() SPDesktop *desktop = SP_ACTIVE_DESKTOP; if (desktop == NULL) return; - Inkscape::Selection *selection = SP_DT_SELECTION(desktop); + Inkscape::Selection *selection = sp_desktop_selection(desktop); // check if something is in the clipboard if (clipboard == NULL) { @@ -1096,18 +1132,93 @@ void sp_selection_paste_style() return; } - paste_defs (&defs_clipboard, SP_DT_DOCUMENT(desktop)); + paste_defs (&defs_clipboard, sp_desktop_document(desktop)); sp_desktop_set_style (desktop, style_clipboard); - sp_document_done(SP_DT_DOCUMENT (desktop)); + sp_document_done(sp_desktop_document (desktop)); +} + +void sp_selection_paste_size (bool apply_x, bool apply_y) +{ + SPDesktop *desktop = SP_ACTIVE_DESKTOP; + if (desktop == NULL) return; + + Inkscape::Selection *selection = sp_desktop_selection(desktop); + + // check if something is in the clipboard + if (size_clipboard.extent(NR::X) < 1e-6 || size_clipboard.extent(NR::Y) < 1e-6) { + desktop->messageStack()->flash(Inkscape::WARNING_MESSAGE, _("Nothing on the clipboard.")); + return; + } + + // check if something is selected + if (selection->isEmpty()) { + desktop->messageStack()->flash(Inkscape::WARNING_MESSAGE, _("Select object(s) to paste size to.")); + return; + } + + NR::Rect current = selection->bounds(); + if (current.extent(NR::X) < 1e-6 || current.extent(NR::Y) < 1e-6) { + return; + } + + double scale_x = size_clipboard.extent(NR::X) / current.extent(NR::X); + double scale_y = size_clipboard.extent(NR::Y) / current.extent(NR::Y); + + sp_selection_scale_relative (selection, current.midpoint(), + NR::scale( + apply_x? scale_x : (desktop->isToolboxButtonActive ("lock")? scale_y : 1.0), + apply_y? scale_y : (desktop->isToolboxButtonActive ("lock")? scale_x : 1.0))); + + sp_document_done(sp_desktop_document (desktop)); } -void sp_selection_to_next_layer () +void sp_selection_paste_size_separately (bool apply_x, bool apply_y) +{ + SPDesktop *desktop = SP_ACTIVE_DESKTOP; + if (desktop == NULL) return; + + Inkscape::Selection *selection = sp_desktop_selection(desktop); + + // check if something is in the clipboard + if (size_clipboard.extent(NR::X) < 1e-6 || size_clipboard.extent(NR::Y) < 1e-6) { + desktop->messageStack()->flash(Inkscape::WARNING_MESSAGE, _("Nothing on the clipboard.")); + return; + } + + // check if something is selected + if (selection->isEmpty()) { + desktop->messageStack()->flash(Inkscape::WARNING_MESSAGE, _("Select object(s) to paste size to.")); + return; + } + + for (GSList const *l = selection->itemList(); l != NULL; l = l->next) { + SPItem *item = SP_ITEM(l->data); + + NR::Rect current = sp_item_bbox_desktop(item); + if (current.extent(NR::X) < 1e-6 || current.extent(NR::Y) < 1e-6) { + continue; + } + + double scale_x = size_clipboard.extent(NR::X) / current.extent(NR::X); + double scale_y = size_clipboard.extent(NR::Y) / current.extent(NR::Y); + + sp_item_scale_rel (item, + NR::scale( + apply_x? scale_x : (desktop->isToolboxButtonActive ("lock")? scale_y : 1.0), + apply_y? scale_y : (desktop->isToolboxButtonActive ("lock")? scale_x : 1.0))); + + } + + sp_document_done(sp_desktop_document (desktop)); +} + +void sp_selection_to_next_layer () { SPDesktop *dt = SP_ACTIVE_DESKTOP; - Inkscape::Selection *selection = SP_DT_SELECTION(dt); + Inkscape::Selection *selection = sp_desktop_selection(dt); // check if something is selected if (selection->isEmpty()) { @@ -1117,29 +1228,41 @@ void sp_selection_to_next_layer () const GSList *items = g_slist_copy ((GSList *) selection->itemList()); + bool no_more = false; // Set to true, if no more layers above SPObject *next=Inkscape::next_layer(dt->currentRoot(), dt->currentLayer()); if (next) { GSList *temp_clip = NULL; sp_selection_copy_impl (items, &temp_clip, NULL, NULL); // we're in the same doc, so no need to copy defs sp_selection_delete_impl (items); - GSList *copied = sp_selection_paste_impl (SP_DT_DOCUMENT (dt), next, &temp_clip, NULL); + next=Inkscape::next_layer(dt->currentRoot(), dt->currentLayer()); // Fixes bug 1482973: crash while moving layers + GSList *copied; + if(next) { + copied = sp_selection_paste_impl (sp_desktop_document (dt), next, &temp_clip, NULL); + } else { + copied = sp_selection_paste_impl (sp_desktop_document (dt), dt->currentLayer(), &temp_clip, NULL); + no_more = true; + } selection->setReprList((GSList const *) copied); g_slist_free (copied); if (temp_clip) g_slist_free (temp_clip); - dt->setCurrentLayer(next); - sp_document_done(SP_DT_DOCUMENT (dt)); + if (next) dt->setCurrentLayer(next); + sp_document_done(sp_desktop_document (dt)); } else { + no_more = true; + } + + if (no_more) { dt->messageStack()->flash(Inkscape::WARNING_MESSAGE, _("No more layers above.")); } g_slist_free ((GSList *) items); } -void sp_selection_to_prev_layer () +void sp_selection_to_prev_layer () { SPDesktop *dt = SP_ACTIVE_DESKTOP; - Inkscape::Selection *selection = SP_DT_SELECTION(dt); + Inkscape::Selection *selection = sp_desktop_selection(dt); // check if something is selected if (selection->isEmpty()) { @@ -1149,18 +1272,30 @@ void sp_selection_to_prev_layer () const GSList *items = g_slist_copy ((GSList *) selection->itemList()); + bool no_more = false; // Set to true, if no more layers below SPObject *next=Inkscape::previous_layer(dt->currentRoot(), dt->currentLayer()); if (next) { GSList *temp_clip = NULL; sp_selection_copy_impl (items, &temp_clip, NULL, NULL); // we're in the same doc, so no need to copy defs - sp_selection_delete_impl (items); - GSList *copied = sp_selection_paste_impl (SP_DT_DOCUMENT (dt), next, &temp_clip, NULL); + sp_selection_delete_impl (items); + next=Inkscape::previous_layer(dt->currentRoot(), dt->currentLayer()); // Fixes bug 1482973: crash while moving layers + GSList *copied; + if(next) { + copied = sp_selection_paste_impl (sp_desktop_document (dt), next, &temp_clip, NULL); + } else { + copied = sp_selection_paste_impl (sp_desktop_document (dt), dt->currentLayer(), &temp_clip, NULL); + no_more = true; + } selection->setReprList((GSList const *) copied); g_slist_free (copied); if (temp_clip) g_slist_free (temp_clip); - dt->setCurrentLayer(next); - sp_document_done(SP_DT_DOCUMENT (dt)); + if (next) dt->setCurrentLayer(next); + sp_document_done(sp_desktop_document (dt)); } else { + no_more = true; + } + + if (no_more) { dt->messageStack()->flash(Inkscape::WARNING_MESSAGE, _("No more layers below.")); } @@ -1182,7 +1317,7 @@ void sp_selection_apply_affine(Inkscape::Selection *selection, NR::Matrix const SPItem *item = SP_ITEM(l->data); NR::Point old_center(0,0); - if (set_i2d && item->isCenterSet()) + if (set_i2d && item->isCenterSet()) old_center = item->getCenter(); #if 0 /* Re-enable this once persistent guides have a graphical indication. @@ -1195,7 +1330,21 @@ void sp_selection_apply_affine(Inkscape::Selection *selection, NR::Matrix const bool transform_textpath_with_path = (SP_IS_TEXT_TEXTPATH(item) && selection->includes( sp_textpath_get_path_item (SP_TEXTPATH(sp_object_first_child(SP_OBJECT(item)))) )); bool transform_flowtext_with_frame = (SP_IS_FLOWTEXT(item) && selection->includes( SP_FLOWTEXT(item)->get_frame (NULL))); // only the first frame so far bool transform_offset_with_source = (SP_IS_OFFSET(item) && SP_OFFSET (item)->sourceHref) && selection->includes( sp_offset_get_source (SP_OFFSET(item)) ); - + + // If we're moving a connector, we want to detach it + // from shapes that aren't part of the selection, but + // leave it attached if they are + if (cc_item_is_connector(item)) { + SPItem *attItem[2]; + SP_PATH(item)->connEndPair.getAttachedItems(attItem); + + for (int n = 0; n < 2; ++n) { + if (!selection->includes(attItem[n])) { + sp_conn_end_detach(item, n); + } + } + } + // "clones are unmoved when original is moved" preference int compensation = prefs_get_int_attribute("options.clonecompensation", "value", SP_CLONE_COMPENSATION_UNMOVED); bool prefs_unmoved = (compensation == SP_CLONE_COMPENSATION_UNMOVED); @@ -1232,7 +1381,7 @@ void sp_selection_apply_affine(Inkscape::Selection *selection, NR::Matrix const // calculate the matrix we need to apply to the clone to cancel its induced transform from its original NR::Matrix t = matrix_to_desktop (matrix_from_desktop (affine, item), item); - NR::Matrix t_inv = matrix_to_desktop (matrix_from_desktop (affine.inverse(), item), item); + NR::Matrix t_inv = matrix_to_desktop (matrix_from_desktop (affine.inverse(), item), item); NR::Matrix result = t_inv * item->transform * t; if ((prefs_parallel || prefs_unmoved) && affine.is_translation()) { @@ -1280,7 +1429,7 @@ void sp_selection_remove_transform() if (desktop == NULL) return; - Inkscape::Selection *selection = SP_DT_SELECTION(desktop); + Inkscape::Selection *selection = sp_desktop_selection(desktop); GSList const *l = (GSList *) selection->reprList(); while (l != NULL) { @@ -1288,7 +1437,7 @@ void sp_selection_remove_transform() l = l->next; } - sp_document_done(SP_DT_DOCUMENT(desktop)); + sp_document_done(sp_desktop_document(desktop)); } void @@ -1301,7 +1450,7 @@ sp_selection_scale_absolute(Inkscape::Selection *selection, NR::Rect const bbox(selection->bounds()); if (bbox.isEmpty()) { - return; + return; } NR::translate const p2o(-bbox.min()); @@ -1384,7 +1533,7 @@ void sp_selection_rotate_90_cw() { SPDesktop *desktop = SP_ACTIVE_DESKTOP; - Inkscape::Selection *selection = SP_DT_SELECTION(desktop); + Inkscape::Selection *selection = sp_desktop_selection(desktop); if (selection->isEmpty()) return; @@ -1396,7 +1545,7 @@ void sp_selection_rotate_90_cw() sp_item_rotate_rel(item, rot_neg_90); } - sp_document_done(SP_DT_DOCUMENT(desktop)); + sp_document_done(sp_desktop_document(desktop)); } @@ -1411,7 +1560,7 @@ void sp_selection_rotate_90_ccw() { SPDesktop *desktop = SP_ACTIVE_DESKTOP; - Inkscape::Selection *selection = SP_DT_SELECTION(desktop); + Inkscape::Selection *selection = sp_desktop_selection(desktop); if (selection->isEmpty()) return; @@ -1423,7 +1572,7 @@ void sp_selection_rotate_90_ccw() sp_item_rotate_rel(item, rot_neg_90); } - sp_document_done(SP_DT_DOCUMENT(desktop)); + sp_document_done(sp_desktop_document(desktop)); } void @@ -1432,11 +1581,11 @@ sp_selection_rotate(Inkscape::Selection *selection, gdouble const angle_degrees) if (selection->isEmpty()) return; - NR::Point const center(selection->bounds().midpoint()); + NR::Point center = selection->center(); sp_selection_rotate_relative(selection, center, angle_degrees); - sp_document_maybe_done(SP_DT_DOCUMENT(selection->desktop()), + sp_document_maybe_done(sp_desktop_document(selection->desktop()), ( ( angle_degrees > 0 ) ? "selector:rotate:ccw" : "selector:rotate:cw" )); @@ -1452,7 +1601,8 @@ sp_selection_rotate_screen(Inkscape::Selection *selection, gdouble angle) return; NR::Rect const bbox(selection->bounds()); - NR::Point const center(bbox.midpoint()); + + NR::Point center = selection->center(); gdouble const zoom = selection->desktop()->current_zoom(); gdouble const zmove = angle / zoom; @@ -1462,7 +1612,7 @@ sp_selection_rotate_screen(Inkscape::Selection *selection, gdouble angle) sp_selection_rotate_relative(selection, center, zangle); - sp_document_maybe_done(SP_DT_DOCUMENT(selection->desktop()), + sp_document_maybe_done(sp_desktop_document(selection->desktop()), ( (angle > 0) ? "selector:rotate:ccw" : "selector:rotate:cw" )); @@ -1486,7 +1636,7 @@ sp_selection_scale(Inkscape::Selection *selection, gdouble grow) double const times = 1.0 + grow / max_len; sp_selection_scale_relative(selection, center, NR::scale(times, times)); - sp_document_maybe_done(SP_DT_DOCUMENT(selection->desktop()), + sp_document_maybe_done(sp_desktop_document(selection->desktop()), ( (grow > 0) ? "selector:scale:larger" : "selector:scale:smaller" )); @@ -1507,14 +1657,14 @@ sp_selection_scale_times(Inkscape::Selection *selection, gdouble times) NR::Point const center(selection->bounds().midpoint()); sp_selection_scale_relative(selection, center, NR::scale(times, times)); - sp_document_done(SP_DT_DOCUMENT(selection->desktop())); + sp_document_done(sp_desktop_document(selection->desktop())); } void sp_selection_move(gdouble dx, gdouble dy) { SPDesktop *desktop = SP_ACTIVE_DESKTOP; - Inkscape::Selection *selection = SP_DT_SELECTION(desktop); + Inkscape::Selection *selection = sp_desktop_selection(desktop); if (selection->isEmpty()) { return; } @@ -1522,11 +1672,11 @@ sp_selection_move(gdouble dx, gdouble dy) sp_selection_move_relative(selection, dx, dy); if (dx == 0) { - sp_document_maybe_done(SP_DT_DOCUMENT(desktop), "selector:move:vertical"); + sp_document_maybe_done(sp_desktop_document(desktop), "selector:move:vertical"); } else if (dy == 0) { - sp_document_maybe_done(SP_DT_DOCUMENT(desktop), "selector:move:horizontal"); + sp_document_maybe_done(sp_desktop_document(desktop), "selector:move:horizontal"); } else { - sp_document_done(SP_DT_DOCUMENT(desktop)); + sp_document_done(sp_desktop_document(desktop)); } } @@ -1535,7 +1685,7 @@ sp_selection_move_screen(gdouble dx, gdouble dy) { SPDesktop *desktop = SP_ACTIVE_DESKTOP; - Inkscape::Selection *selection = SP_DT_SELECTION(desktop); + Inkscape::Selection *selection = sp_desktop_selection(desktop); if (selection->isEmpty()) { return; } @@ -1547,11 +1697,11 @@ sp_selection_move_screen(gdouble dx, gdouble dy) sp_selection_move_relative(selection, zdx, zdy); if (dx == 0) { - sp_document_maybe_done(SP_DT_DOCUMENT(desktop), "selector:move:vertical"); + sp_document_maybe_done(sp_desktop_document(desktop), "selector:move:vertical"); } else if (dy == 0) { - sp_document_maybe_done(SP_DT_DOCUMENT(desktop), "selector:move:horizontal"); + sp_document_maybe_done(sp_desktop_document(desktop), "selector:move:horizontal"); } else { - sp_document_done(SP_DT_DOCUMENT(desktop)); + sp_document_done(sp_desktop_document(desktop)); } } @@ -1559,11 +1709,11 @@ namespace { template SPItem *next_item(SPDesktop *desktop, GSList *path, SPObject *root, - bool only_in_viewport, bool inlayer, bool onlyvisible, bool onlysensitive); + bool only_in_viewport, PrefsSelectionContext inlayer, bool onlyvisible, bool onlysensitive); template -SPItem *next_item_from_list(SPDesktop *desktop, GSList const *items, SPObject *root, - bool only_in_viewport, bool inlayer, bool onlyvisible, bool onlysensitive); +SPItem *next_item_from_list(SPDesktop *desktop, GSList const *items, SPObject *root, + bool only_in_viewport, PrefsSelectionContext inlayer, bool onlyvisible, bool onlysensitive); struct Forward { typedef SPObject *Iterator; @@ -1612,15 +1762,15 @@ sp_selection_item_next(void) { SPDesktop *desktop = SP_ACTIVE_DESKTOP; g_return_if_fail(desktop != NULL); - Inkscape::Selection *selection = SP_DT_SELECTION(desktop); + Inkscape::Selection *selection = sp_desktop_selection(desktop); - bool inlayer = prefs_get_int_attribute ("options.kbselection", "inlayer", 1); + PrefsSelectionContext inlayer = (PrefsSelectionContext)prefs_get_int_attribute ("options.kbselection", "inlayer", PREFS_SELECTION_LAYER); bool onlyvisible = prefs_get_int_attribute ("options.kbselection", "onlyvisible", 1); bool onlysensitive = prefs_get_int_attribute ("options.kbselection", "onlysensitive", 1); SPObject *root; - if (inlayer) { - root = desktop->currentLayer(); + if (PREFS_SELECTION_ALL != inlayer) { + root = selection->activeContext(); } else { root = desktop->currentRoot(); } @@ -1628,7 +1778,7 @@ sp_selection_item_next(void) SPItem *item=next_item_from_list(desktop, selection->itemList(), root, SP_CYCLING == SP_CYCLE_VISIBLE, inlayer, onlyvisible, onlysensitive); if (item) { - selection->set(item); + selection->set(item, PREFS_SELECTION_LAYER_RECURSIVE == inlayer); if ( SP_CYCLING == SP_CYCLE_FOCUS ) { scroll_to_show_item(desktop, item); } @@ -1642,15 +1792,15 @@ sp_selection_item_prev(void) SPDesktop *desktop = SP_ACTIVE_DESKTOP; g_return_if_fail(document != NULL); g_return_if_fail(desktop != NULL); - Inkscape::Selection *selection = SP_DT_SELECTION(desktop); + Inkscape::Selection *selection = sp_desktop_selection(desktop); - bool inlayer = prefs_get_int_attribute ("options.kbselection", "inlayer", 1); + PrefsSelectionContext inlayer = (PrefsSelectionContext)prefs_get_int_attribute ("options.kbselection", "inlayer", PREFS_SELECTION_LAYER); bool onlyvisible = prefs_get_int_attribute ("options.kbselection", "onlyvisible", 1); bool onlysensitive = prefs_get_int_attribute ("options.kbselection", "onlysensitive", 1); SPObject *root; - if (inlayer) { - root = desktop->currentLayer(); + if (PREFS_SELECTION_ALL != inlayer) { + root = selection->activeContext(); } else { root = desktop->currentRoot(); } @@ -1658,7 +1808,7 @@ sp_selection_item_prev(void) SPItem *item=next_item_from_list(desktop, selection->itemList(), root, SP_CYCLING == SP_CYCLE_VISIBLE, inlayer, onlyvisible, onlysensitive); if (item) { - selection->set(item); + selection->set(item, PREFS_SELECTION_LAYER_RECURSIVE == inlayer); if ( SP_CYCLING == SP_CYCLE_FOCUS ) { scroll_to_show_item(desktop, item); } @@ -1669,7 +1819,7 @@ namespace { template SPItem *next_item_from_list(SPDesktop *desktop, GSList const *items, - SPObject *root, bool only_in_viewport, bool inlayer, bool onlyvisible, bool onlysensitive) + SPObject *root, bool only_in_viewport, PrefsSelectionContext inlayer, bool onlyvisible, bool onlysensitive) { SPObject *current=root; while (items) { @@ -1703,7 +1853,7 @@ SPItem *next_item_from_list(SPDesktop *desktop, GSList const *items, template SPItem *next_item(SPDesktop *desktop, GSList *path, SPObject *root, - bool only_in_viewport, bool inlayer, bool onlyvisible, bool onlysensitive) + bool only_in_viewport, PrefsSelectionContext inlayer, bool onlyvisible, bool onlysensitive) { typename D::Iterator children; typename D::Iterator iter; @@ -1724,7 +1874,7 @@ SPItem *next_item(SPDesktop *desktop, GSList *path, SPObject *root, while ( iter && !found ) { SPObject *object=D::object(iter); if (desktop->isLayer(object)) { - if (!inlayer) { // recurse into sublayers + if (PREFS_SELECTION_LAYER != inlayer) { // recurse into sublayers found = next_item(desktop, NULL, object, only_in_viewport, inlayer, onlyvisible, onlysensitive); } } else if ( SP_IS_ITEM(object) && @@ -1774,7 +1924,7 @@ sp_selection_clone() if (desktop == NULL) return; - Inkscape::Selection *selection = SP_DT_SELECTION(desktop); + Inkscape::Selection *selection = sp_desktop_selection(desktop); // check if something is selected if (selection->isEmpty()) { @@ -1782,27 +1932,40 @@ sp_selection_clone() return; } - // Check if more than one object is selected. - if (g_slist_length((GSList *) selection->itemList()) > 1) { - desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE, _("If you want to clone several objects, group them and clone the group.")); - return; - } + GSList *reprs = g_slist_copy((GSList *) selection->reprList()); + + selection->clear(); + + // sorting items from different parents sorts each parent's subset without possibly mixing them, just what we need + reprs = g_slist_sort(reprs, (GCompareFunc) sp_repr_compare_position); - Inkscape::XML::Node *sel_repr = SP_OBJECT_REPR(selection->singleItem()); - Inkscape::XML::Node *parent = sp_repr_parent(sel_repr); + GSList *newsel = NULL; + + while (reprs) { + Inkscape::XML::Node *sel_repr = (Inkscape::XML::Node *) reprs->data; + Inkscape::XML::Node *parent = sp_repr_parent(sel_repr); - Inkscape::XML::Node *clone = sp_repr_new("svg:use"); - clone->setAttribute("x", "0"); - clone->setAttribute("y", "0"); - clone->setAttribute("xlink:href", g_strdup_printf("#%s", sel_repr->attribute("id"))); + Inkscape::XML::Node *clone = sp_repr_new("svg:use"); + sp_repr_set_attr(clone, "x", "0"); + sp_repr_set_attr(clone, "y", "0"); + sp_repr_set_attr(clone, "xlink:href", g_strdup_printf("#%s", sel_repr->attribute("id"))); - // add the new clone to the top of the original's parent - parent->appendChild(clone); + sp_repr_set_attr(clone, "inkscape:transform-center-x", sel_repr->attribute("inkscape:transform-center-x")); + sp_repr_set_attr(clone, "inkscape:transform-center-y", sel_repr->attribute("inkscape:transform-center-y")); + + // add the new clone to the top of the original's parent + parent->appendChild(clone); - sp_document_done(SP_DT_DOCUMENT(desktop)); + newsel = g_slist_prepend(newsel, clone); + reprs = g_slist_remove(reprs, sel_repr); + Inkscape::GC::release(clone); + } + + sp_document_done(sp_desktop_document(desktop)); - selection->set(clone); - Inkscape::GC::release(clone); + selection->setReprList(newsel); + + g_slist_free(newsel); } void @@ -1812,7 +1975,7 @@ sp_selection_unlink() if (!desktop) return; - Inkscape::Selection *selection = SP_DT_SELECTION(desktop); + Inkscape::Selection *selection = sp_desktop_selection(desktop); if (selection->isEmpty()) { desktop->messageStack()->flash(Inkscape::WARNING_MESSAGE, _("Select a clone to unlink.")); @@ -1849,7 +2012,7 @@ sp_selection_unlink() desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE, _("No clones to unlink in the selection.")); } - sp_document_done(SP_DT_DOCUMENT(desktop)); + sp_document_done(sp_desktop_document(desktop)); } void @@ -1859,7 +2022,7 @@ sp_select_clone_original() if (desktop == NULL) return; - Inkscape::Selection *selection = SP_DT_SELECTION(desktop); + Inkscape::Selection *selection = sp_desktop_selection(desktop); SPItem *item = selection->singleItem(); @@ -1913,9 +2076,9 @@ sp_selection_tile(bool apply) if (desktop == NULL) return; - SPDocument *document = SP_DT_DOCUMENT(desktop); + SPDocument *document = sp_desktop_document(desktop); - Inkscape::Selection *selection = SP_DT_SELECTION(desktop); + Inkscape::Selection *selection = sp_desktop_selection(desktop); // check if something is selected if (selection->isEmpty()) { @@ -1984,7 +2147,7 @@ sp_selection_tile(bool apply) // restore parent and position SP_OBJECT_REPR (parent)->appendChild(rect); rect->setPosition(pos > 0 ? pos : 0); - SPItem *rectangle = (SPItem *) SP_DT_DOCUMENT (desktop)->getObjectByRepr(rect); + SPItem *rectangle = (SPItem *) sp_desktop_document (desktop)->getObjectByRepr(rect); Inkscape::GC::release(rect); @@ -2004,9 +2167,9 @@ sp_selection_untile() if (desktop == NULL) return; - SPDocument *document = SP_DT_DOCUMENT(desktop); + SPDocument *document = sp_desktop_document(desktop); - Inkscape::Selection *selection = SP_DT_SELECTION(desktop); + Inkscape::Selection *selection = sp_desktop_selection(desktop); // check if something is selected if (selection->isEmpty()) { @@ -2026,7 +2189,7 @@ sp_selection_untile() SPStyle *style = SP_OBJECT_STYLE (item); - if (!style || style->fill.type != SP_PAINT_TYPE_PAINTSERVER) + if (!style || style->fill.type != SP_PAINT_TYPE_PAINTSERVER) continue; SPObject *server = SP_OBJECT_STYLE_FILL_SERVER(item); @@ -2046,7 +2209,7 @@ sp_selection_untile() SPItem *i = SP_ITEM (desktop->currentLayer()->appendChildRepr(copy)); // FIXME: relink clones to the new canvas objects - // use SPObject::setid when mental finishes it to steal ids of + // use SPObject::setid when mental finishes it to steal ids of // this is needed to make sure the new item has curve (simply requestDisplayUpdate does not work) sp_document_ensure_up_to_date (document); @@ -2065,7 +2228,7 @@ sp_selection_untile() if (!did) { desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE, _("No pattern fills in the selection.")); } else { - sp_document_done(SP_DT_DOCUMENT(desktop)); + sp_document_done(sp_desktop_document(desktop)); selection->setList(new_select); } } @@ -2077,9 +2240,9 @@ sp_selection_create_bitmap_copy () if (desktop == NULL) return; - SPDocument *document = SP_DT_DOCUMENT(desktop); + SPDocument *document = sp_desktop_document(desktop); - Inkscape::Selection *selection = SP_DT_SELECTION(desktop); + Inkscape::Selection *selection = sp_desktop_selection(desktop); // check if something is selected if (selection->isEmpty()) { @@ -2105,7 +2268,7 @@ sp_selection_create_bitmap_copy () // multiple times, and this is done so that they don't clash) GTimeVal cu; g_get_current_time (&cu); - guint current = (int) (cu.tv_sec * 1000000 + cu.tv_usec) % 1024; + guint current = (int) (cu.tv_sec * 1000000 + cu.tv_usec) % 1024; // Create the filename gchar *filename = g_strdup_printf ("%s-%s-%u.png", document->name, SP_OBJECT_REPR(items->data)->attribute("id"), current); @@ -2164,7 +2327,7 @@ sp_selection_create_bitmap_copy () // Calculate the matrix that will be applied to the image so that it exactly overlaps the source objects NR::Matrix eek = sp_item_i2d_affine (SP_ITEM(parent_object)); - NR::Matrix t = NR::scale (1/res, -1/res) * NR::translate (bbox.x0, bbox.y1) * eek.inverse(); + NR::Matrix t = NR::scale (1/res, -1/res) * NR::translate (bbox.x0, bbox.y1) * eek.inverse(); // Do the export sp_export_png_file(document, filepath, @@ -2197,12 +2360,12 @@ sp_selection_create_bitmap_copy () gchar c[256]; if (sp_svg_transform_write(c, 256, t)) { repr->setAttribute("transform", c); - } + } // add the new repr to the parent parent->appendChild(repr); - // move to the saved position + // move to the saved position repr->setPosition(pos > 0 ? pos + 1 : 1); // Set selection to the new image @@ -2221,6 +2384,249 @@ sp_selection_create_bitmap_copy () g_free (filepath); } +/** + * \brief sp_selection_set_mask + * + * This function creates a mask or clipPath from selection + * Two different modes: + * if applyToLayer, all selection is moved to DEFS as mask/clippath + * and is applied to current layer + * otherwise, topmost object is used as mask for other objects + * If \a apply_clip_path parameter is true, clipPath is created, otherwise mask + * + */ +void +sp_selection_set_mask(bool apply_clip_path, bool apply_to_layer) +{ + SPDesktop *desktop = SP_ACTIVE_DESKTOP; + if (desktop == NULL) + return; + + SPDocument *document = sp_desktop_document(desktop); + + Inkscape::Selection *selection = sp_desktop_selection(desktop); + + // check if something is selected + bool is_empty = selection->isEmpty(); + if ( apply_to_layer && is_empty) { + desktop->messageStack()->flash(Inkscape::WARNING_MESSAGE, _("Select object(s) to create clippath or mask from.")); + return; + } else if (!apply_to_layer && ( is_empty || NULL == selection->itemList()->next )) { + desktop->messageStack()->flash(Inkscape::WARNING_MESSAGE, _("Select mask object and object(s) to apply clippath or mask to.")); + return; + } + + sp_document_ensure_up_to_date(document); + + GSList *items = g_slist_copy((GSList *) selection->itemList()); + + items = g_slist_sort (items, (GCompareFunc) sp_object_compare_position); + + // create a list of duplicates + GSList *mask_items = NULL; + GSList *apply_to_items = NULL; + bool topmost = prefs_get_int_attribute ("options.maskobject", "topmost", 1); + bool remove_original = prefs_get_int_attribute ("options.maskobject", "remove", 1); + + if (apply_to_layer) { + // all selected items are used for mask, which is applied to a layer + apply_to_items = g_slist_prepend (apply_to_items, desktop->currentLayer()); + + for (GSList *i = items; i != NULL; i = i->next) { + Inkscape::XML::Node *dup = (SP_OBJECT_REPR (i->data))->duplicate(); + mask_items = g_slist_prepend (mask_items, dup); + + if (remove_original) { + SPObject *item = SP_OBJECT (i->data); + item->deleteObject (false); + } + } + } else if (!topmost) { + // topmost item is used as a mask, which is applied to other items in a selection + GSList *i = items; + Inkscape::XML::Node *dup = (SP_OBJECT_REPR (i->data))->duplicate(); + mask_items = g_slist_prepend (mask_items, dup); + + if (remove_original) { + SPObject *item = SP_OBJECT (i->data); + item->deleteObject (false); + } + + for (i = i->next; i != NULL; i = i->next) { + apply_to_items = g_slist_prepend (apply_to_items, i->data); + } + } else { + GSList *i = NULL; + for (i = items; NULL != i->next; i = i->next) { + apply_to_items = g_slist_prepend (apply_to_items, i->data); + } + + Inkscape::XML::Node *dup = (SP_OBJECT_REPR (i->data))->duplicate(); + mask_items = g_slist_prepend (mask_items, dup); + + if (remove_original) { + SPObject *item = SP_OBJECT (i->data); + item->deleteObject (false); + } + } + + g_slist_free (items); + items = NULL; + + gchar const* attributeName = apply_clip_path ? "clip-path" : "mask"; + for (GSList *i = apply_to_items; NULL != i; i = i->next) { + SPItem *item = reinterpret_cast(i->data); + // inverted object transform should be applied to a mask object, + // as mask is calculated in user space (after applying transform) + NR::Matrix maskTransform (item->transform.inverse()); + + GSList *mask_items_dup = NULL; + for (GSList *mask_item = mask_items; NULL != mask_item; mask_item = mask_item->next) { + Inkscape::XML::Node *dup = reinterpret_cast(mask_item->data)->duplicate(); + mask_items_dup = g_slist_prepend (mask_items_dup, dup); + } + + const gchar *mask_id = NULL; + if (apply_clip_path) { + mask_id = sp_clippath_create(mask_items_dup, document, &maskTransform); + } else { + mask_id = sp_mask_create(mask_items_dup, document, &maskTransform); + } + + g_slist_free (mask_items_dup); + mask_items_dup = NULL; + + SP_OBJECT_REPR(i->data)->setAttribute(attributeName, g_strdup_printf("url(#%s)", mask_id)); + } + + g_slist_free (mask_items); + g_slist_free (apply_to_items); + + sp_document_done (document); +} + +void sp_selection_unset_mask(bool apply_clip_path) { + SPDesktop *desktop = SP_ACTIVE_DESKTOP; + if (desktop == NULL) + return; + + SPDocument *document = sp_desktop_document(desktop); + Inkscape::Selection *selection = sp_desktop_selection(desktop); + + // check if something is selected + if (selection->isEmpty()) { + desktop->messageStack()->flash(Inkscape::WARNING_MESSAGE, _("Select object(s) to remove clippath or mask from.")); + return; + } + + bool remove_original = prefs_get_int_attribute ("options.maskobject", "remove", 1); + sp_document_ensure_up_to_date(document); + + gchar const* attributeName = apply_clip_path ? "clip-path" : "mask"; + std::map referenced_objects; + for (GSList const*i = selection->itemList(); NULL != i; i = i->next) { + if (remove_original) { + // remember referenced mask/clippath, so orphaned masks can be moved back to document + SPItem *item = reinterpret_cast(i->data); + Inkscape::URIReference *uri_ref = NULL; + + if (apply_clip_path) { + uri_ref = item->clip_ref; + } else { + uri_ref = item->mask_ref; + } + + // collect distinct mask object (and associate with item to apply transform) + if (NULL != uri_ref && NULL != uri_ref->getObject()) { + referenced_objects[uri_ref->getObject()] = item; + } + } + + SP_OBJECT_REPR(i->data)->setAttribute(attributeName, "none"); + } + + // restore mask objects into a document + for ( std::map::iterator it = referenced_objects.begin() ; it != referenced_objects.end() ; ++it) { + SPObject *obj = (*it).first; + GSList *items_to_move = NULL; + for (SPObject *child = sp_object_first_child(obj) ; child != NULL; child = SP_OBJECT_NEXT(child) ) { + Inkscape::XML::Node *copy = SP_OBJECT_REPR(child)->duplicate(); + items_to_move = g_slist_prepend (items_to_move, copy); + } + + if (!obj->isReferenced()) { + // delete from defs if no other object references this mask + obj->deleteObject(false); + } + + // remember parent and position of the item to which the clippath/mask was applied + Inkscape::XML::Node *parent = SP_OBJECT_REPR((*it).second)->parent(); + gint pos = SP_OBJECT_REPR((*it).second)->position(); + + for (GSList *i = items_to_move; NULL != i; i = i->next) { + Inkscape::XML::Node *repr = (Inkscape::XML::Node *)i->data; + + // insert into parent, restore pos + parent->appendChild(repr); + repr->setPosition((pos + 1) > 0 ? (pos + 1) : 0); + + SPItem *mask_item = (SPItem *) sp_desktop_document (desktop)->getObjectByRepr(repr); + selection->add(repr); + + // transform mask, so it is moved the same spot where mask was applied + NR::Matrix transform (mask_item->transform); + transform *= (*it).second->transform; + sp_item_write_transform(mask_item, SP_OBJECT_REPR(mask_item), transform); + } + + g_slist_free (items_to_move); + } + + sp_document_done (document); +} + +void fit_canvas_to_selection(SPDesktop *desktop) { + g_return_if_fail(desktop != NULL); + SPDocument *doc = sp_desktop_document(desktop); + + g_return_if_fail(doc != NULL); + g_return_if_fail(desktop->selection != NULL); + g_return_if_fail(!desktop->selection->isEmpty()); + NRRect bbox = {0,0,0,0}; + + desktop->selection->bounds(&bbox); + if (!empty(bbox)) { + doc->fitToRect(bbox); + } +}; + +void fit_canvas_to_drawing(SPDocument *doc) { + g_return_if_fail(doc != NULL); + NRRect bbox = {0,0,0,0}; + + sp_document_ensure_up_to_date (doc); + sp_item_invoke_bbox(SP_ITEM(doc->root), &bbox, sp_item_i2r_affine(SP_ITEM(doc->root)), TRUE); + + if (!empty(bbox)) { + doc->fitToRect(bbox); + } +}; + +void fit_canvas_to_selection_or_drawing(SPDesktop *desktop) { + g_return_if_fail(desktop != NULL); + SPDocument *doc = sp_desktop_document(desktop); + + g_return_if_fail(doc != NULL); + g_return_if_fail(desktop->selection != NULL); + + if (desktop->selection->isEmpty()) { + fit_canvas_to_drawing(doc); + } else { + fit_canvas_to_selection(desktop); + } + + sp_document_done(doc); +}; /* Local Variables: