From b512043bef12ffb35aeaf2d718f07d538afa1726 Mon Sep 17 00:00:00 2001 From: Josh Andler Date: Thu, 4 Feb 2010 11:50:48 -0800 Subject: [PATCH] Patch by Martin Sucha to add preferences to for grouping on Clip/Mask --- src/enums.h | 8 ++ src/selection-chemistry.cpp | 146 ++++++++++++++++++++----- src/selection-chemistry.h | 1 + src/sp-item-group.cpp | 6 +- src/sp-item-group.h | 2 +- src/ui/dialog/inkscape-preferences.cpp | 22 ++++ src/ui/dialog/inkscape-preferences.h | 2 + 7 files changed, 158 insertions(+), 29 deletions(-) diff --git a/src/enums.h b/src/enums.h index 4d3c75c20..8b9dcc71f 100644 --- a/src/enums.h +++ b/src/enums.h @@ -81,5 +81,13 @@ enum PrefsSelectionContext { PREFS_SELECTION_LAYER_RECURSIVE = 2, }; +/* clip/mask group enclosing behavior preference values */ + +enum PrefsMaskobjectGrouping { + PREFS_MASKOBJECT_GROUPING_NONE = 0, + PREFS_MASKOBJECT_GROUPING_SEPARATE = 1, + PREFS_MASKOBJECT_GROUPING_ALL = 2, +}; + #endif diff --git a/src/selection-chemistry.cpp b/src/selection-chemistry.cpp index e81d133c2..a5c6ae961 100644 --- a/src/selection-chemistry.cpp +++ b/src/selection-chemistry.cpp @@ -94,6 +94,9 @@ SPCycleType SP_CYCLING = SP_CYCLE_FOCUS; #include "ui/tool/control-point-selection.h" #include "ui/tool/multi-path-manipulator.h" +#include "enums.h" +#include "sp-item-group.h" + // For clippath editing #include "tools-switch.h" #include "ui/tool/node-tool.h" @@ -551,36 +554,15 @@ void sp_edit_invert_in_all_layers(SPDesktop *desktop) sp_edit_select_all_full(desktop, true, true); } -void sp_selection_group(SPDesktop *desktop) -{ - if (desktop == NULL) - return; - - SPDocument *doc = sp_desktop_document(desktop); - Inkscape::XML::Document *xml_doc = sp_document_repr_doc(doc); - - Inkscape::Selection *selection = sp_desktop_selection(desktop); - - // Check if something is selected. - if (selection->isEmpty()) { - desktop->messageStack()->flash(Inkscape::WARNING_MESSAGE, _("Select some objects to group.")); - return; - } - - GSList const *l = (GSList *) selection->reprList(); - - GSList *p = g_slist_copy((GSList *) l); - - selection->clear(); - +void sp_selection_group_impl(GSList const *reprs_to_group, Inkscape::XML::Node *group, Inkscape::XML::Document *xml_doc, SPDocument *doc) { + GSList *p = g_slist_copy((GSList *) reprs_to_group); + p = g_slist_sort(p, (GCompareFunc) sp_repr_compare_position); - + // Remember the position and parent of the topmost object. gint topmost = ((Inkscape::XML::Node *) g_slist_last(p)->data)->position(); Inkscape::XML::Node *topmost_parent = ((Inkscape::XML::Node *) g_slist_last(p)->data)->parent(); - - Inkscape::XML::Node *group = xml_doc->createElement("svg:g"); - + while (p) { Inkscape::XML::Node *current = (Inkscape::XML::Node *) p->data; @@ -634,10 +616,35 @@ void sp_selection_group(SPDesktop *desktop) // Move to the position of the topmost, reduced by the number of items deleted from topmost_parent group->setPosition(topmost + 1); +} + +void sp_selection_group(SPDesktop *desktop) +{ + if (desktop == NULL) + return; + + SPDocument *doc = sp_desktop_document(desktop); + Inkscape::XML::Document *xml_doc = sp_document_repr_doc(doc); + + Inkscape::Selection *selection = sp_desktop_selection(desktop); + + // Check if something is selected. + if (selection->isEmpty()) { + desktop->messageStack()->flash(Inkscape::WARNING_MESSAGE, _("Select some objects to group.")); + return; + } + + GSList const *l = (GSList *) selection->reprList(); + + + Inkscape::XML::Node *group = xml_doc->createElement("svg:g"); + + sp_selection_group_impl(l, group, xml_doc, doc); sp_document_done(sp_desktop_document(desktop), SP_VERB_SELECTION_GROUP, _("Group")); + selection->clear(); selection->set(group); Inkscape::GC::release(group); } @@ -2856,6 +2863,7 @@ sp_selection_set_mask(SPDesktop *desktop, bool apply_clip_path, bool apply_to_la Inkscape::Preferences *prefs = Inkscape::Preferences::get(); bool topmost = prefs->getBool("/options/maskobject/topmost", true); bool remove_original = prefs->getBool("/options/maskobject/remove", true); + int grouping = prefs->getInt("/options/maskobject/grouping", PREFS_MASKOBJECT_GROUPING_NONE); if (apply_to_layer) { // all selected items are used for mask, which is applied to a layer @@ -2901,6 +2909,36 @@ sp_selection_set_mask(SPDesktop *desktop, bool apply_clip_path, bool apply_to_la g_slist_free(items); items = NULL; + + if (apply_to_items && grouping == PREFS_MASKOBJECT_GROUPING_ALL) { + // group all those objects into one group + // and apply mask to that + Inkscape::XML::Node *group = xml_doc->createElement("svg:g"); + + // make a note we should ungroup this when unsetting mask + group->setAttribute("inkscape:groupmode", "maskhelper"); + + GSList *reprs_to_group = NULL; + + for (GSList *i = apply_to_items ; NULL != i ; i = i->next) { + reprs_to_group = g_slist_prepend(reprs_to_group, SP_OBJECT_REPR(i->data)); + selection->remove(SP_OBJECT(i->data)); + } + reprs_to_group = g_slist_reverse(reprs_to_group); + + sp_selection_group_impl(reprs_to_group, group, xml_doc, doc); + + g_slist_free(reprs_to_group); + + // apply clip/mask only to newly created group + g_slist_free(apply_to_items); + apply_to_items = NULL; + apply_to_items = g_slist_prepend(apply_to_items, doc->getObjectByRepr(group)); + + selection->add(group); + + Inkscape::GC::release(group); + } gchar const *attributeName = apply_clip_path ? "clip-path" : "mask"; for (GSList *i = apply_to_items; NULL != i; i = i->next) { @@ -2925,7 +2963,34 @@ sp_selection_set_mask(SPDesktop *desktop, bool apply_clip_path, bool apply_to_la g_slist_free(mask_items_dup); mask_items_dup = NULL; - SP_OBJECT_REPR(i->data)->setAttribute(attributeName, g_strdup_printf("url(#%s)", mask_id)); + Inkscape::XML::Node *current = SP_OBJECT_REPR(i->data); + // Node to apply mask to + Inkscape::XML::Node *apply_mask_to = current; + + if (grouping == PREFS_MASKOBJECT_GROUPING_SEPARATE) { + // enclose current node in group, and apply crop/mask on that + Inkscape::XML::Node *group = xml_doc->createElement("svg:g"); + // make a note we should ungroup this when unsetting mask + group->setAttribute("inkscape:groupmode", "maskhelper"); + + Inkscape::XML::Node *spnew = current->duplicate(xml_doc); + gint position = current->position(); + selection->remove(current); + current->parent()->appendChild(group); + sp_repr_unparent(current); + group->appendChild(spnew); + group->setPosition(position); + + // Apply clip/mask to group instead + apply_mask_to = group; + + selection->add(group); + Inkscape::GC::release(spnew); + Inkscape::GC::release(group); + } + + apply_mask_to->setAttribute(attributeName, g_strdup_printf("url(#%s)", mask_id)); + } g_slist_free(mask_items); @@ -2959,10 +3024,14 @@ void sp_selection_unset_mask(SPDesktop *desktop, bool apply_clip_path) { Inkscape::Preferences *prefs = Inkscape::Preferences::get(); bool remove_original = prefs->getBool("/options/maskobject/remove", true); + bool ungroup_masked = prefs->getBool("/options/maskobject/ungrouping", true); sp_document_ensure_up_to_date(doc); gchar const *attributeName = apply_clip_path ? "clip-path" : "mask"; std::map referenced_objects; + + GSList *items_to_ungroup = NULL; + // SPObject* refers to a group containing the clipped path or mask itself, // whereas SPItem* refers to the item being clipped or masked for (GSList const *i = selection->itemList(); NULL != i; i = i->next) { @@ -2984,6 +3053,18 @@ void sp_selection_unset_mask(SPDesktop *desktop, bool apply_clip_path) { } SP_OBJECT_REPR(i->data)->setAttribute(attributeName, "none"); + + if (ungroup_masked && SP_IS_GROUP(i->data)) { + // if we had previously enclosed masked object in group, + // add it to list so we can ungroup it later + SPGroup *item = SP_GROUP(i->data); + + // ungroup only groups we created when setting clip/mask + if (item->layerMode() == SPGroup::MASK_HELPER) { + items_to_ungroup = g_slist_prepend(items_to_ungroup, item); + } + + } } // restore mask objects into a document @@ -3024,6 +3105,17 @@ void sp_selection_unset_mask(SPDesktop *desktop, bool apply_clip_path) { g_slist_free(items_to_move); } + + // ungroup marked groups added when setting mask + for (GSList *i = items_to_ungroup ; NULL != i ; i = i->next) { + selection->remove(SP_GROUP(i->data)); + GSList *children = NULL; + sp_item_group_ungroup(SP_GROUP(i->data), &children, false); + selection->addList(children); + g_slist_free(children); + } + + g_slist_free(items_to_ungroup); if (apply_clip_path) sp_document_done(doc, SP_VERB_OBJECT_UNSET_CLIPPATH, _("Release clipping path")); diff --git a/src/selection-chemistry.h b/src/selection-chemistry.h index 60d64ced9..e06c32e72 100644 --- a/src/selection-chemistry.h +++ b/src/selection-chemistry.h @@ -63,6 +63,7 @@ void sp_selection_to_guides(SPDesktop *desktop); void sp_selection_tile(SPDesktop *desktop, bool apply = true); void sp_selection_untile(SPDesktop *desktop); +//void sp_selection_group_impl(GSList const *reprs_to_group, Inkscape::XML::Node *group, Inkscape::XML::Document *xml_doc, SPDocument *doc); void sp_selection_group(SPDesktop *desktop); void sp_selection_ungroup(SPDesktop *desktop); diff --git a/src/sp-item-group.cpp b/src/sp-item-group.cpp index a773bd4a2..588427752 100644 --- a/src/sp-item-group.cpp +++ b/src/sp-item-group.cpp @@ -258,6 +258,8 @@ sp_group_write (SPObject *object, Inkscape::XML::Document *xml_doc, Inkscape::XM const char *value; if ( group->_layer_mode == SPGroup::LAYER ) { value = "layer"; + } else if ( group->_layer_mode == SPGroup::MASK_HELPER ) { + value = "maskhelper"; } else if ( flags & SP_OBJECT_WRITE_ALL ) { value = "group"; } else { @@ -296,6 +298,8 @@ static void sp_group_set(SPObject *object, unsigned key, char const *value) { case SP_ATTR_INKSCAPE_GROUPMODE: if ( value && !strcmp(value, "layer") ) { group->setLayerMode(SPGroup::LAYER); + } else if ( value && !strcmp(value, "maskhelper") ) { + group->setLayerMode(SPGroup::MASK_HELPER); } else { group->setLayerMode(SPGroup::GROUP); } @@ -532,7 +536,7 @@ void SPGroup::setLayerMode(LayerMode mode) { if ( _layer_mode != mode ) { if ( mode == LAYER ) { sp_document_add_resource(SP_OBJECT_DOCUMENT(this), "layer", this); - } else { + } else if ( _layer_mode == LAYER ) { sp_document_remove_resource(SP_OBJECT_DOCUMENT(this), "layer", this); } _layer_mode = mode; diff --git a/src/sp-item-group.h b/src/sp-item-group.h index 65a51055a..932241a42 100644 --- a/src/sp-item-group.h +++ b/src/sp-item-group.h @@ -27,7 +27,7 @@ class CGroup; struct SPGroup : public SPLPEItem { - enum LayerMode { GROUP, LAYER }; + enum LayerMode { GROUP, LAYER, MASK_HELPER }; LayerMode _layer_mode; std::map _display_modes; diff --git a/src/ui/dialog/inkscape-preferences.cpp b/src/ui/dialog/inkscape-preferences.cpp index 961c7dff7..b2257a3fe 100644 --- a/src/ui/dialog/inkscape-preferences.cpp +++ b/src/ui/dialog/inkscape-preferences.cpp @@ -668,6 +668,28 @@ void InkscapePreferences::initPageMasks() _mask_mask_remove.init ( _("Remove clippath/mask object after applying"), "/options/maskobject/remove", true); _page_mask.add_line(true, "", _mask_mask_remove, "", _("After applying, remove the object used as the clipping path or mask from the drawing")); + + _page_mask.add_group_header( _("Before applying clippath/mask:")); + + _mask_grouping_none.init( _("Do not group clipped/masked objects"), "/options/maskobject/grouping", PREFS_MASKOBJECT_GROUPING_NONE, true, 0); + _mask_grouping_separate.init( _("Enclose every clipped/masked object in its own group"), "/options/maskobject/grouping", PREFS_MASKOBJECT_GROUPING_SEPARATE, false, &_mask_grouping_none); + _mask_grouping_all.init( _("Put all clipped/masked objects into one group"), "/options/maskobject/grouping", PREFS_MASKOBJECT_GROUPING_ALL, false, &_mask_grouping_none); + + _page_mask.add_line(true, "", _mask_grouping_none, "", + _("Apply clippath/mask to every object")); + + _page_mask.add_line(true, "", _mask_grouping_separate, "", + _("Apply clippath/mask to groups containing single object")); + + _page_mask.add_line(true, "", _mask_grouping_all, "", + _("Apply clippath/mask to group containing all objects")); + + _page_mask.add_group_header( _("After releasing clippath/mask:")); + + _mask_ungrouping.init ( _("Ungroup automatically created groups"), "/options/maskobject/ungrouping", true); + _page_mask.add_line(true, "", _mask_ungrouping, "", + _("Ungroup groups created when setting clip/mask")); + this->AddPage(_page_mask, _("Clippaths and masks"), PREFS_PAGE_MASKS); } diff --git a/src/ui/dialog/inkscape-preferences.h b/src/ui/dialog/inkscape-preferences.h index 49c847287..e3f76b753 100644 --- a/src/ui/dialog/inkscape-preferences.h +++ b/src/ui/dialog/inkscape-preferences.h @@ -172,6 +172,8 @@ protected: PrefCheckButton _mask_mask_on_top; PrefCheckButton _mask_mask_remove; + PrefRadioButton _mask_grouping_none, _mask_grouping_separate, _mask_grouping_all; + PrefCheckButton _mask_ungrouping; PrefRadioButton _blur_quality_best, _blur_quality_better, _blur_quality_normal, _blur_quality_worse, _blur_quality_worst; PrefRadioButton _filter_quality_best, _filter_quality_better, _filter_quality_normal, _filter_quality_worse, _filter_quality_worst; -- 2.30.2