Code

patch by Andrius R. for (un)clip and (un)mask commands
authorbuliabyak <buliabyak@users.sourceforge.net>
Sun, 19 Mar 2006 23:19:10 +0000 (23:19 +0000)
committerbuliabyak <buliabyak@users.sourceforge.net>
Sun, 19 Mar 2006 23:19:10 +0000 (23:19 +0000)
13 files changed:
src/menus-skeleton.h
src/preferences-skeleton.h
src/selection-chemistry.cpp
src/selection-chemistry.h
src/sp-clippath.cpp
src/sp-clippath.h
src/sp-mask.cpp
src/sp-mask.h
src/sp-object.h
src/ui/dialog/inkscape-preferences.cpp
src/ui/dialog/inkscape-preferences.h
src/verbs.cpp
src/verbs.h

index 0ecfb7cefcfc45b4458abce994f3a4dd52546f5d..7d8a5803dd00c76dea39716c6c207ffbcdc4d59e 100644 (file)
@@ -149,6 +149,15 @@ static char const menus_skeleton[] =
 "       <verb verb-id=\"SelectionGroup\" />\n"
 "       <verb verb-id=\"SelectionUnGroup\" />\n"
 "       <separator/>\n"
+"       <submenu name=\"" N_("Cli_p") "\">\n"
+"           <verb verb-id=\"ObjectSetClipPath\" />\n"
+"           <verb verb-id=\"ObjectUnSetClipPath\" />\n"
+"       </submenu>\n"
+"       <submenu name=\"" N_("Mas_k") "\">\n"
+"           <verb verb-id=\"ObjectSetMask\" />\n"
+"           <verb verb-id=\"ObjectUnSetMask\" />\n"
+"       </submenu>\n"
+"       <separator/>\n"
 "       <verb verb-id=\"SelectionRaise\" />\n"
 "       <verb verb-id=\"SelectionLower\" />\n"
 "       <verb verb-id=\"SelectionToFront\" />\n"
index 688a080fabafbbf65f73a297a078814d6c874538..5873622dd332c87485040d41a7a642613a108954 100644 (file)
@@ -181,6 +181,7 @@ static char const preferences_skeleton[] =
 "    <group id=\"kbselection\" inlayer=\"1\" onlyvisible=\"1\" onlysensitive=\"1\" />\n"
 "    <group id=\"createbitmap\" minsize=\"250\"/>\n"
 "    <group id=\"compassangledisplay\" value=\"0\"/>\n"
+"    <group id=\"maskobject\" topmost=\"1\" remove=\"1\"/>\n"
 "  </group>\n"
 "\n"
 "  <group id=\"extensions\" show-effects-menu=\"0\">"
index cd6fb2823d161ed8379b4a1e4682c9fb268f796c..a92cb1a73d8eff147d5aafe8e19f50067c0b811a 100644 (file)
@@ -8,8 +8,9 @@
  *   Frank Felfe <innerspace@iname.com>
  *   MenTaLguY <mental@rydia.net>
  *   bulia byak <buliabyak@users.sf.net>
+ *   Andrius R. <knutux@gmail.com>
  *
- * 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
 #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 <set>
 using NR::X;
 using NR::Y;
 
@@ -786,14 +790,14 @@ 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)))
             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)))
             desktop->messageStack()->flash(Inkscape::WARNING_MESSAGE, _("Nothing to redo."));
@@ -2231,6 +2235,172 @@ 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_DT_DOCUMENT(desktop);
+    
+    Inkscape::Selection *selection = SP_DT_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 <b>object(s)</b> to create mask from."));
+        return;
+    } else if (!apply_to_layer && ( is_empty || NULL == selection->itemList()->next )) {
+        desktop->messageStack()->flash(Inkscape::WARNING_MESSAGE, _("Select mask object and <b>object(s)</b> to apply 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, SP_OBJECT_REPR(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, SP_OBJECT_REPR (i->data));
+        }
+    } else {
+        GSList *i = NULL;
+        for (i = items; NULL != i->next; i = i->next) {
+            apply_to_items = g_slist_prepend (apply_to_items, SP_OBJECT_REPR (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);
+            
+    const gchar *mask_id = NULL;
+    if (apply_clip_path) {
+        mask_id = sp_clippath_create(mask_items, document);
+    } else {
+        mask_id = sp_mask_create(mask_items, document);
+    }
+    g_slist_free (mask_items);
+
+    gchar const* attributeName = apply_clip_path ? "clip-path" : "mask";
+    for (GSList *i = apply_to_items; NULL != i; i = i->next) {
+        ((Inkscape::XML::Node *)i->data)->setAttribute(attributeName, g_strdup_printf("url(#%s)", mask_id));
+    }
+
+    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_DT_DOCUMENT(desktop);    
+    Inkscape::Selection *selection = SP_DT_SELECTION(desktop);
+
+    // check if something is selected
+    if (selection->isEmpty()) {
+        desktop->messageStack()->flash(Inkscape::WARNING_MESSAGE, _("Select <b>object(s)</b> to remove 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::set<SPObject*> 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<SPItem *>(i->data);
+            Inkscape::URIReference *uri_ref = NULL;
+        
+            if (apply_clip_path) {
+                uri_ref = item->clip_ref;
+            } else {
+                uri_ref = item->mask_ref;
+            }
+    
+            if (NULL != uri_ref && referenced_objects.end() == referenced_objects.find(uri_ref->getObject())) {
+                referenced_objects.insert(uri_ref->getObject());
+            }
+        }
+
+        SP_OBJECT_REPR(i->data)->setAttribute(attributeName, "none");
+    }
+
+    for ( std::set<SPObject*>::iterator it = referenced_objects.begin() ; it != referenced_objects.end() ; ++it) {
+        SPObject *obj = (*it);
+        if (!obj->isReferenced()) {
+            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);
+            }
+            
+            obj->deleteObject(false);
+
+            for (GSList *i = items_to_move; NULL != i; i = i->next) {
+                desktop->currentLayer()->appendChildRepr((Inkscape::XML::Node *)i->data);
+            }
+
+            g_slist_free (items_to_move);
+        }
+    }
+
+    sp_document_done (document);
+}
 
 /*
   Local Variables:
index 9fbeaa9ecd0844441ea6013bb11e708cc8ffe4b0..df8b699317726bb29dbaf709ef42cfd7f0451696 100644 (file)
@@ -87,6 +87,9 @@ void sp_redo (SPDesktop *desktop, SPDocument *doc);
 
 void sp_selection_create_bitmap_copy ();
 
+void sp_selection_set_mask(bool apply_clip_path, bool apply_to_layer);
+void sp_selection_unset_mask(bool apply_clip_path);
+
 /* selection cycling */
 
 typedef enum
index 086b6dcfa3084f040af83d8265a4dae0944a8442..33feea8ce2eef50669b8a78841becde558822f69 100644 (file)
@@ -21,6 +21,7 @@
 #include "enums.h"
 #include "attributes.h"
 #include "document.h"
+#include "document-private.h"
 #include "sp-item.h"
 
 #include "sp-clippath.h"
@@ -368,6 +369,27 @@ sp_clippath_view_list_remove(SPClipPathView *list, SPClipPathView *view)
     return list;
 }
 
+// Create a mask element (using passed elements), add it to <defs>
+const gchar *
+sp_clippath_create (GSList *reprs, SPDocument *document)
+{
+    Inkscape::XML::Node *defsrepr = SP_OBJECT_REPR (SP_DOCUMENT_DEFS (document));
+
+    Inkscape::XML::Node *repr = sp_repr_new ("svg:clipPath");
+    repr->setAttribute("clipPathUnits", "userSpaceOnUse");
+    
+    defsrepr->appendChild(repr);
+    const gchar *id = repr->attribute("id");
+    SPObject *clip_path_object = document->getObjectById(id);
+    
+    for (GSList *it = reprs; it != NULL; it = it->next) {
+        Inkscape::XML::Node *node = (Inkscape::XML::Node *)(it->data);
+        clip_path_object->appendChildRepr(node);
+    }
+    
+    Inkscape::GC::release(repr);
+    return id;
+}
 
 /*
   Local Variables:
index 0a8b92fa908820cdd315a9b13377e9acd9a4ac48..0f977a3e8dbe533a5b6bb0f4b0ccd8dd5f0897cd 100644 (file)
@@ -59,4 +59,6 @@ void sp_clippath_hide(SPClipPath *cp, unsigned int key);
 void sp_clippath_set_bbox(SPClipPath *cp, unsigned int key, NRRect *bbox);
 void sp_clippath_get_bbox(SPClipPath *cp, NRRect *bbox, NR::Matrix const &transform, unsigned const flags);
 
+const gchar *sp_clippath_create (GSList *reprs, SPDocument *document);
+
 #endif
index 456dadc5fc29d709c524c1957bf34cf800f6f388..773169d68847d2edfc342c3a55596ed022082831 100644 (file)
@@ -19,6 +19,7 @@
 #include "enums.h"
 #include "attributes.h"
 #include "document.h"
+#include "document-private.h"
 #include "sp-item.h"
 
 #include "sp-mask.h"
@@ -266,6 +267,28 @@ sp_mask_write (SPObject *object, Inkscape::XML::Node *repr, guint flags)
        return repr;
 }
 
+// Create a mask element (using passed elements), add it to <defs>
+const gchar *
+sp_mask_create (GSList *reprs, SPDocument *document)
+{
+    Inkscape::XML::Node *defsrepr = SP_OBJECT_REPR (SP_DOCUMENT_DEFS (document));
+
+    Inkscape::XML::Node *repr = sp_repr_new ("svg:mask");
+    repr->setAttribute("maskUnits", "userSpaceOnUse");
+    
+    defsrepr->appendChild(repr);
+    const gchar *mask_id = repr->attribute("id");
+    SPObject *mask_object = document->getObjectById(mask_id);
+    
+    for (GSList *it = reprs; it != NULL; it = it->next) {
+        Inkscape::XML::Node *node = (Inkscape::XML::Node *)(it->data);
+        mask_object->appendChildRepr(node);
+    }
+    
+    Inkscape::GC::release(repr);
+    return mask_id;
+}
+
 NRArenaItem *
 sp_mask_show (SPMask *mask, NRArena *arena, unsigned int key)
 {
index 23239f55dd4bd52cc75af23c583c7efbddbeaf71..b17ac83997a2e231d187edeadc4790361346b9ac 100644 (file)
@@ -60,4 +60,6 @@ void sp_mask_hide (SPMask *mask, unsigned int key);
 
 void sp_mask_set_bbox (SPMask *mask, unsigned int key, NRRect *bbox);
 
+const gchar *sp_mask_create (GSList *reprs, SPDocument *document);
+
 #endif
index e853de097907055d0623422677b10fb8de7212d1..4879e5bea0fe3e7d71c4db7e2ff40f737dd7b79e 100644 (file)
@@ -287,6 +287,10 @@ struct SPObject : public GObject {
         }
     }
 
+    /** @brief Check if object is referenced by any other object.
+     */
+    bool isReferenced() { return ( _total_hrefcount > 0 ); }
+
     /** @brief Deletes an object.
      *
      * Detaches the object's repr, and optionally sends notification that the object has been
index 54977ac05d897555fee6c661e769a1c4cdf156d1..b90ed8d986df30b27c805e9c1b4fde911fc69c58 100644 (file)
@@ -511,6 +511,14 @@ void InkscapePreferences::initPageMisc()
     _misc_overs_bitmap.init("options.bitmapoversample", "value", labels, values, num_items, 1);
     _page_misc.add_line( false, _("Oversample bitmaps:"), _misc_overs_bitmap, "", "", false);
 
+    _page_misc.add_group_header( _("Clipping and masking:"));
+    _misc_mask_on_top.init ( _("Use the topmost selected object as a clipping path or mask"), "options.maskobject", "topmost", true);
+    _page_misc.add_line(true, "", _misc_mask_on_top, "", 
+                        _("Uncheck this to use the bottom selected object as the clipping path or mask"));
+    _misc_mask_remove.init ( _("Remove clipping path or mask after applying"), "options.maskobject", "remove", true);
+    _page_misc.add_line(true, "", _misc_mask_remove, "", 
+                        _("Affter applying, remove the object used as the clipping path or mask from the drawing"));
+
     this->AddPage(_page_misc, _("Misc"), PREFS_PAGE_MISC);
 }
 
index de2010f125d7503c8ac1e836a691b59612a2c5b7..af3dfa44b03ace56e9cc2ae49f29d850ed7c7a5a 100644 (file)
@@ -125,6 +125,8 @@ protected:
     PrefSpinButton  _misc_export, _misc_recent, _misc_simpl;
     PrefCheckButton _misc_imp_bitmap, _misc_comment, _misc_scripts;
     PrefCombo       _misc_overs_bitmap;
+    PrefCheckButton _misc_mask_on_top;
+    PrefCheckButton _misc_mask_remove;
 
     int _max_dialog_width;
     int _max_dialog_height;
index f262b93275668381b322ca53a06e18895ac5d886..84da8703bb902394a6467a9379eefc8ae7bb4235 100644 (file)
@@ -1196,6 +1196,18 @@ ObjectVerb::perform( SPAction *action, void *data, void *pdata )
             }
             sp_document_done(SP_DT_DOCUMENT(dt));
             break;
+        case SP_VERB_OBJECT_SET_MASK:
+            sp_selection_set_mask(false, false);
+            break;
+        case SP_VERB_OBJECT_UNSET_MASK:
+            sp_selection_unset_mask(false);
+            break;
+        case SP_VERB_OBJECT_SET_CLIPPATH:
+            sp_selection_set_mask(true, false);
+            break;
+        case SP_VERB_OBJECT_UNSET_CLIPPATH:
+            sp_selection_unset_mask(true);
+            break;
         default:
             break;
     }
@@ -2025,6 +2037,14 @@ Verb *Verb::_base_verbs[] = {
     new ObjectVerb(SP_VERB_OBJECT_FLIP_VERTICAL, "ObjectFlipVertically",
                    N_("Flip _Vertical"), N_("Flips selected objects vertically"),
                    "object_flip_ver"),
+    new ObjectVerb(SP_VERB_OBJECT_SET_MASK, "ObjectSetMask", N_("_Set"),
+                 N_("Apply mask to selection (using the topmost object as mask)"), NULL),
+    new ObjectVerb(SP_VERB_OBJECT_UNSET_MASK, "ObjectUnSetMask", N_("_Release"),
+                 N_("Remove mask from selection"), NULL),
+    new ObjectVerb(SP_VERB_OBJECT_SET_CLIPPATH, "ObjectSetClipPath", N_("_Set"),
+                 N_("Apply clipping path to selection (using the topmost object as clipping path)"), NULL),
+    new ObjectVerb(SP_VERB_OBJECT_UNSET_CLIPPATH, "ObjectUnSetClipPath", N_("_Release"),
+                 N_("Remove clipping path from selection"), NULL),
 
     /* Tools */
     new ContextVerb(SP_VERB_CONTEXT_SELECT, "DrawSelect", N_("Select"),
index acc6d7d17a9fdee7a9f4b7a069ebd5a25f9d12e6..426b4295f0236b6b38d1118caa552fb3d33e06d1 100644 (file)
@@ -115,6 +115,10 @@ enum {
     SP_VERB_OBJECT_FLOWTEXT_TO_TEXT,
     SP_VERB_OBJECT_FLIP_HORIZONTAL,
     SP_VERB_OBJECT_FLIP_VERTICAL,
+    SP_VERB_OBJECT_SET_MASK,
+    SP_VERB_OBJECT_UNSET_MASK,
+    SP_VERB_OBJECT_SET_CLIPPATH,
+    SP_VERB_OBJECT_UNSET_CLIPPATH,
     /* Tools */
     SP_VERB_CONTEXT_SELECT,
     SP_VERB_CONTEXT_NODE,