Code

excise never-used code and stale comments
[inkscape.git] / src / selection-chemistry.cpp
index cd6fb2823d161ed8379b4a1e4682c9fb268f796c..4da9b33b86e15680b3588c2cee35c1ab2826a41d 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 <map>
 using NR::X;
 using NR::Y;
 
@@ -70,6 +74,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);
 
@@ -786,14 +791,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."));
@@ -838,10 +843,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);
 }
 
 
@@ -880,7 +885,7 @@ void sp_copy_stuff_used_by_item (GSList **defs_clip, SPItem *item, const GSList
         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 +894,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))
@@ -1024,6 +1043,8 @@ void sp_selection_copy()
         g_free (query);
     }
 
+    size_clipboard = selection->bounds();
+
     g_slist_free ((GSList *) items);
 }
 
@@ -1103,6 +1124,81 @@ void sp_selection_paste_style()
     sp_document_done(SP_DT_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_DT_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 <b>object(s)</b> 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_DT_DOCUMENT (desktop));
+}
+
+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_DT_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 <b>object(s)</b> 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_DT_DOCUMENT (desktop));
+}
+
 void sp_selection_to_next_layer ()
 {
     SPDesktop *dt = SP_ACTIVE_DESKTOP;
@@ -1432,7 +1528,7 @@ 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);
 
@@ -1452,7 +1548,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;
@@ -1799,6 +1896,9 @@ sp_selection_clone()
         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")));
+
+        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);
@@ -2231,6 +2331,196 @@ 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, 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<SPItem *>(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<Inkscape::XML::Node *>(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_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::map<SPObject*,SPItem*> 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;
+            }
+
+            // 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<SPObject*,SPItem*>::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);
+        }
+
+        for (GSList *i = items_to_move; NULL != i; i = i->next) {
+            SPItem *item = SP_ITEM(desktop->currentLayer()->appendChildRepr((Inkscape::XML::Node *)i->data));
+            selection->add((Inkscape::XML::Node *)i->data);
+
+            // transform mask, so it is moved the same spot there mask was applied
+            NR::Matrix transform (item->transform);
+            transform *= (*it).second->transform;
+            sp_item_write_transform(item, SP_OBJECT_REPR(item), transform);
+        }
+
+        g_slist_free (items_to_move);
+    }
+
+    sp_document_done (document);
+}
 
 /*
   Local Variables: