Code

Tweaked smaller size to be 3/4ths the menu size
[inkscape.git] / src / selection-chemistry.cpp
index 91686d277f6c7751fef4629f500893d91b58bce4..e6684010e6b705c5a1f8a039e1b4674dd21129f5 100644 (file)
@@ -64,7 +64,7 @@
 #include "file.h"
 #include "layer-fns.h"
 #include "context-fns.h"
-#include <set>
+#include <map>
 using NR::X;
 using NR::Y;
 
@@ -74,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);
 
@@ -329,7 +330,7 @@ void sp_edit_select_all_full (bool force_all_layers, bool invert)
 
     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);
 
@@ -340,8 +341,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;
@@ -363,9 +367,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);
@@ -1042,6 +1053,8 @@ void sp_selection_copy()
         g_free (query);
     }
 
+    size_clipboard = selection->bounds();
+
     g_slist_free ((GSList *) items);
 }
 
@@ -1121,6 +1134,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;
@@ -1450,7 +1538,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);
 
@@ -1470,7 +1558,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;
@@ -1577,11 +1666,11 @@ namespace {
 
 template <typename D>
 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 <typename D>
 SPItem *next_item_from_list(SPDesktop *desktop, GSList const *items, SPObject *root,
-                  bool only_in_viewport, bool inlayer, bool onlyvisible, bool onlysensitive);
+                  bool only_in_viewport, PrefsSelectionContext inlayer, bool onlyvisible, bool onlysensitive);
 
 struct Forward {
     typedef SPObject *Iterator;
@@ -1632,13 +1721,13 @@ sp_selection_item_next(void)
     g_return_if_fail(desktop != NULL);
     Inkscape::Selection *selection = SP_DT_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();
     }
@@ -1646,7 +1735,7 @@ sp_selection_item_next(void)
     SPItem *item=next_item_from_list<Forward>(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);
         }
@@ -1662,13 +1751,13 @@ sp_selection_item_prev(void)
     g_return_if_fail(desktop != NULL);
     Inkscape::Selection *selection = SP_DT_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();
     }
@@ -1676,7 +1765,7 @@ sp_selection_item_prev(void)
     SPItem *item=next_item_from_list<Reverse>(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);
         }
@@ -1687,7 +1776,7 @@ namespace {
 
 template <typename D>
 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) {
@@ -1721,7 +1810,7 @@ SPItem *next_item_from_list(SPDesktop *desktop, GSList const *items,
 
 template <typename D>
 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;
@@ -1742,7 +1831,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<D>(desktop, NULL, object, only_in_viewport, inlayer, onlyvisible, onlysensitive);
             }
         } else if ( SP_IS_ITEM(object) &&
@@ -1817,6 +1906,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);
@@ -2274,10 +2366,10 @@ sp_selection_set_mask(bool apply_clip_path, bool apply_to_layer)
     // 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."));
+        desktop->messageStack()->flash(Inkscape::WARNING_MESSAGE, _("Select <b>object(s)</b> 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 <b>object(s)</b> to apply mask to."));
+        desktop->messageStack()->flash(Inkscape::WARNING_MESSAGE, _("Select mask object and <b>object(s)</b> to apply clippath or mask to."));
         return;
     }
     
@@ -2295,7 +2387,7 @@ sp_selection_set_mask(bool apply_clip_path, bool apply_to_layer)
     
     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()));
+        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();
@@ -2318,12 +2410,12 @@ sp_selection_set_mask(bool apply_clip_path, bool apply_to_layer)
         }
         
         for (i = i->next; i != NULL; i = i->next) {
-            apply_to_items = g_slist_prepend (apply_to_items, SP_OBJECT_REPR (i->data));
+            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, SP_OBJECT_REPR (i->data));
+            apply_to_items = g_slist_prepend (apply_to_items, i->data);
         }
 
         Inkscape::XML::Node *dup = (SP_OBJECT_REPR (i->data))->duplicate();
@@ -2336,20 +2428,35 @@ sp_selection_set_mask(bool apply_clip_path, bool apply_to_layer)
     }
     
     g_slist_free (items);
+    items = NULL;
             
-    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));
+        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);
@@ -2365,7 +2472,7 @@ void sp_selection_unset_mask(bool apply_clip_path) {
 
     // check if something is selected
     if (selection->isEmpty()) {
-        desktop->messageStack()->flash(Inkscape::WARNING_MESSAGE, _("Select <b>object(s)</b> to remove mask from."));
+        desktop->messageStack()->flash(Inkscape::WARNING_MESSAGE, _("Select <b>object(s)</b> to remove clippath or mask from."));
         return;
     }
     
@@ -2373,7 +2480,7 @@ void sp_selection_unset_mask(bool apply_clip_path) {
     sp_document_ensure_up_to_date(document);
 
     gchar const* attributeName = apply_clip_path ? "clip-path" : "mask";
-    std::set<SPObject*> referenced_objects;
+    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
@@ -2385,32 +2492,51 @@ void sp_selection_unset_mask(bool apply_clip_path) {
             } 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());
+
+            // 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");
     }
 
-    for ( std::set<SPObject*>::iterator it = referenced_objects.begin() ; it != referenced_objects.end() ; ++it) {
-        SPObject *obj = (*it);
+    // 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()) {
-            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);
-            }
-            
+            // delete from defs if no other object references this mask
             obj->deleteObject(false);
+        }
 
-            for (GSList *i = items_to_move; NULL != i; i = i->next) {
-                desktop->currentLayer()->appendChildRepr((Inkscape::XML::Node *)i->data);
-            }
+        // 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);
 
-            g_slist_free (items_to_move);
+            SPItem *mask_item = (SPItem *) SP_DT_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);