Code

Patch by Martin Sucha to add preferences to for grouping on Clip/Mask
authorJosh Andler <scislac@gmail.com>
Thu, 4 Feb 2010 19:50:48 +0000 (11:50 -0800)
committerJosh Andler <scislac@gmail.com>
Thu, 4 Feb 2010 19:50:48 +0000 (11:50 -0800)
src/enums.h
src/selection-chemistry.cpp
src/selection-chemistry.h
src/sp-item-group.cpp
src/sp-item-group.h
src/ui/dialog/inkscape-preferences.cpp
src/ui/dialog/inkscape-preferences.h

index 4d3c75c208e1f108b1f97720bf58cfbd788ed299..8b9dcc71f691ab39dbfb13d9cd0df8955fda1cb4 100644 (file)
@@ -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
 
index e81d133c2dd9dc049790c801c7e8c72584e5719d..a5c6ae961609b839a04981a907df68585f8adfa0 100644 (file)
@@ -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 <b>some objects</b> 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 <b>some objects</b> 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<SPObject*,SPItem*> 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"));
index 60d64ced9c8c3e181ab4fddde046c33fa920ec00..e06c32e725e422c459e53e32ac8a7d6d8ed429db 100644 (file)
@@ -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);
 
index a773bd4a24df825b54f987dce126f3dace9c072a..5884277521c9a24fcfac82d519363029c005d18d 100644 (file)
@@ -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;
index 65a51055a79aab462eb760c4a9d1a7cff0dcce82..932241a42f6b8eaae25ed798764c01dd45ef7e93 100644 (file)
@@ -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<unsigned int, LayerMode> _display_modes;
index 961c7dff7f43d7426fd2c4a517f5677991edbef7..b2257a3fe098e70c2a548379dac789253d66e197 100644 (file)
@@ -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);
 }
 
index 49c84728759316d852c783bfdf5ed112e01c3a91..e3f76b753492d48b145ea6632a0b86da80fb6000 100644 (file)
@@ -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;