Code

* Remove unused variables
[inkscape.git] / src / selection-chemistry.cpp
index 0ba60a91cac30868e47ed6d025261d3e3b23fd5e..1225a66f369dcbef3c0d23e87a79407ff4e2bcfd 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
@@ -38,6 +39,9 @@
 #include "sp-flowregion.h"
 #include "text-editing.h"
 #include "text-context.h"
+#include "connector-context.h"
+#include "sp-path.h"
+#include "sp-conn-end.h"
 #include "dropper-context.h"
 #include <glibmm/i18n.h>
 #include "libnr/nr-matrix-rotate-ops.h"
 #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>
+#include "helper/units.h"
+#include "sp-item.h"
 using NR::X;
 using NR::Y;
 
@@ -70,6 +79,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);
 
@@ -129,7 +139,7 @@ void sp_selection_copy_impl (const GSList *items, GSList **clip, GSList **defs_c
 /**
 Add gradients/patterns/markers referenced by copied objects to defs
 */
-void 
+void
 paste_defs (GSList **defs_clip, SPDocument *doc)
 {
     if (!defs_clip)
@@ -203,11 +213,11 @@ void sp_selection_delete()
 
     if (tools_isactive (desktop, TOOLS_TEXT))
         if (sp_text_delete_selection(desktop->event_context)) {
-            sp_document_done(SP_DT_DOCUMENT(desktop));
+            sp_document_done(sp_desktop_document(desktop));
             return;
         }
 
-    Inkscape::Selection *selection = SP_DT_SELECTION(desktop);
+    Inkscape::Selection *selection = sp_desktop_selection(desktop);
 
     // check if something is selected
     if (selection->isEmpty()) {
@@ -228,7 +238,7 @@ void sp_selection_delete()
      */
     tools_switch ( desktop, tools_active ( desktop ) );
 
-    sp_document_done(SP_DT_DOCUMENT(desktop));
+    sp_document_done(sp_desktop_document(desktop));
 }
 
 /* fixme: sequencing */
@@ -238,7 +248,7 @@ void sp_selection_duplicate()
     if (desktop == NULL)
         return;
 
-    Inkscape::Selection *selection = SP_DT_SELECTION(desktop);
+    Inkscape::Selection *selection = sp_desktop_selection(desktop);
 
     // check if something is selected
     if (selection->isEmpty()) {
@@ -266,7 +276,7 @@ void sp_selection_duplicate()
         Inkscape::GC::release(copy);
     }
 
-    sp_document_done(SP_DT_DOCUMENT(desktop));
+    sp_document_done(sp_desktop_document(desktop));
 
     selection->setReprList(newsel);
 
@@ -279,8 +289,8 @@ void sp_edit_clear_all()
     if (!dt)
         return;
 
-    SPDocument *doc = SP_DT_DOCUMENT(dt);
-    SP_DT_SELECTION(dt)->clear();
+    SPDocument *doc = sp_desktop_document(dt);
+    sp_desktop_selection(dt)->clear();
 
     g_return_if_fail(SP_IS_GROUP(dt->currentLayer()));
     GSList *items = sp_item_group_item_list(SP_GROUP(dt->currentLayer()));
@@ -299,7 +309,7 @@ get_all_items (GSList *list, SPObject *from, SPDesktop *desktop, bool onlyvisibl
     for (SPObject *child = sp_object_first_child(SP_OBJECT(from)) ; child != NULL; child = SP_OBJECT_NEXT(child) ) {
         if (SP_IS_ITEM(child) &&
             !desktop->isLayer(SP_ITEM(child)) &&
-            (!onlysensitive || !SP_ITEM(child)->isLocked()) && 
+            (!onlysensitive || !SP_ITEM(child)->isLocked()) &&
             (!onlyvisible || !desktop->itemIsHidden(SP_ITEM(child))) &&
             (!exclude || !g_slist_find ((GSList *) exclude, child))
             )
@@ -321,11 +331,11 @@ void sp_edit_select_all_full (bool force_all_layers, bool invert)
     if (!dt)
         return;
 
-    Inkscape::Selection *selection = SP_DT_SELECTION(dt);
+    Inkscape::Selection *selection = sp_desktop_selection(dt);
 
     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);
 
@@ -336,8 +346,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;
@@ -359,9 +372,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);
@@ -397,9 +417,9 @@ void sp_selection_group()
     if (desktop == NULL)
         return;
 
-    SPDocument *document = SP_DT_DOCUMENT (desktop);
+    SPDocument *document = sp_desktop_document (desktop);
 
-    Inkscape::Selection *selection = SP_DT_SELECTION(desktop);
+    Inkscape::Selection *selection = sp_desktop_selection(desktop);
 
     // Check if something is selected.
     if (selection->isEmpty()) {
@@ -481,7 +501,7 @@ void sp_selection_group()
     // Move to the position of the topmost, reduced by the number of items deleted from topmost_parent
     group->setPosition(topmost + 1);
 
-    sp_document_done(SP_DT_DOCUMENT(desktop));
+    sp_document_done(sp_desktop_document(desktop));
 
     selection->set(group);
     Inkscape::GC::release(group);
@@ -493,7 +513,7 @@ void sp_selection_ungroup()
     if (desktop == NULL)
         return;
 
-    Inkscape::Selection *selection = SP_DT_SELECTION(desktop);
+    Inkscape::Selection *selection = sp_desktop_selection(desktop);
 
     if (selection->isEmpty()) {
         desktop->messageStack()->flash(Inkscape::WARNING_MESSAGE, _("Select a <b>group</b> to ungroup."));
@@ -542,7 +562,7 @@ void sp_selection_ungroup()
 
     g_slist_free(items);
 
-    sp_document_done(SP_DT_DOCUMENT(desktop));
+    sp_document_done(sp_desktop_document(desktop));
 }
 
 static SPGroup *
@@ -602,7 +622,7 @@ sp_selection_raise()
     if (!desktop)
         return;
 
-    Inkscape::Selection *selection = SP_DT_SELECTION(desktop);
+    Inkscape::Selection *selection = sp_desktop_selection(desktop);
 
     GSList const *items = (GSList *) selection->itemList();
     if (!items) {
@@ -643,7 +663,7 @@ sp_selection_raise()
         rev = g_slist_remove(rev, child);
     }
 
-    sp_document_done(SP_DT_DOCUMENT(desktop));
+    sp_document_done(sp_desktop_document(desktop));
 }
 
 void sp_selection_raise_to_top()
@@ -652,8 +672,8 @@ void sp_selection_raise_to_top()
     if (desktop == NULL)
         return;
 
-    SPDocument *document = SP_DT_DOCUMENT(desktop);
-    Inkscape::Selection *selection = SP_DT_SELECTION(desktop);
+    SPDocument *document = sp_desktop_document(desktop);
+    Inkscape::Selection *selection = sp_desktop_selection(desktop);
 
     if (selection->isEmpty()) {
         desktop->messageStack()->flash(Inkscape::WARNING_MESSAGE, _("Select <b>object(s)</b> to raise to top."));
@@ -688,7 +708,7 @@ sp_selection_lower()
     if (desktop == NULL)
         return;
 
-    Inkscape::Selection *selection = SP_DT_SELECTION(desktop);
+    Inkscape::Selection *selection = sp_desktop_selection(desktop);
 
     GSList const *items = (GSList *) selection->itemList();
     if (!items) {
@@ -734,7 +754,7 @@ sp_selection_lower()
         rev = g_slist_remove(rev, child);
     }
 
-    sp_document_done(SP_DT_DOCUMENT(desktop));
+    sp_document_done(sp_desktop_document(desktop));
 
 }
 
@@ -744,8 +764,8 @@ void sp_selection_lower_to_bottom()
     if (desktop == NULL)
         return;
 
-    SPDocument *document = SP_DT_DOCUMENT(desktop);
-    Inkscape::Selection *selection = SP_DT_SELECTION(desktop);
+    SPDocument *document = sp_desktop_document(desktop);
+    Inkscape::Selection *selection = sp_desktop_selection(desktop);
 
     if (selection->isEmpty()) {
         desktop->messageStack()->flash(Inkscape::WARNING_MESSAGE, _("Select <b>object(s)</b> to lower to bottom."));
@@ -786,16 +806,16 @@ 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)))
+        if (!sp_document_undo(sp_desktop_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)))
+        if (!sp_document_redo(sp_desktop_document(desktop)))
             desktop->messageStack()->flash(Inkscape::WARNING_MESSAGE, _("Nothing to redo."));
 }
 
@@ -809,7 +829,7 @@ void sp_copy_gradient (GSList **defs_clip, SPGradient *gradient)
 {
     SPGradient *ref = gradient;
 
-    while (ref) { 
+    while (ref) {
         // climb up the refs, copying each one in the chain
         Inkscape::XML::Node *grad_repr = SP_OBJECT_REPR(ref)->duplicate();
         *defs_clip = g_slist_prepend (*defs_clip, grad_repr);
@@ -838,10 +858,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);
 }
 
 
@@ -858,9 +878,9 @@ void sp_copy_textpath_path (GSList **defs_clip, SPTextPath *tp, const GSList *it
 
 void sp_copy_stuff_used_by_item (GSList **defs_clip, SPItem *item, const GSList *items)
 {
-    SPStyle *style = SP_OBJECT_STYLE (item); 
+    SPStyle *style = SP_OBJECT_STYLE (item);
 
-    if (style && (style->fill.type == SP_PAINT_TYPE_PAINTSERVER)) { 
+    if (style && (style->fill.type == SP_PAINT_TYPE_PAINTSERVER)) {
         SPObject *server = SP_OBJECT_STYLE_FILL_SERVER(item);
         if (SP_IS_LINEARGRADIENT (server) || SP_IS_RADIALGRADIENT (server))
             sp_copy_gradient (defs_clip, SP_GRADIENT(server));
@@ -868,7 +888,7 @@ void sp_copy_stuff_used_by_item (GSList **defs_clip, SPItem *item, const GSList
             sp_copy_pattern (defs_clip, SP_PATTERN(server));
     }
 
-    if (style && (style->stroke.type == SP_PAINT_TYPE_PAINTSERVER)) { 
+    if (style && (style->stroke.type == SP_PAINT_TYPE_PAINTSERVER)) {
         SPObject *server = SP_OBJECT_STYLE_STROKE_SERVER(item);
         if (SP_IS_LINEARGRADIENT (server) || SP_IS_RADIALGRADIENT (server))
             sp_copy_gradient (defs_clip, SP_GRADIENT(server));
@@ -876,11 +896,11 @@ void sp_copy_stuff_used_by_item (GSList **defs_clip, SPItem *item, const GSList
             sp_copy_pattern (defs_clip, SP_PATTERN(server));
     }
 
-    if (SP_IS_SHAPE (item)) { 
+    if (SP_IS_SHAPE (item)) {
         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 +909,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))
@@ -943,7 +977,7 @@ void sp_selection_copy()
     if (desktop == NULL)
         return;
 
-    Inkscape::Selection *selection = SP_DT_SELECTION(desktop);
+    Inkscape::Selection *selection = sp_desktop_selection(desktop);
 
     if (tools_isactive (desktop, TOOLS_DROPPER)) {
         sp_dropper_context_copy(desktop->event_context);
@@ -959,7 +993,7 @@ void sp_selection_copy()
     const GSList *items = g_slist_copy ((GSList *) selection->itemList());
 
     // 0. Copy text to system clipboard
-    // FIXME: for non-texts, put serialized XML as text to the clipboard; 
+    // FIXME: for non-texts, put serialized XML as text to the clipboard;
     //for this sp_repr_write_stream needs to be rewritten with iostream instead of FILE
     Glib::ustring text;
     if (tools_isactive (desktop, TOOLS_TEXT)) {
@@ -998,8 +1032,8 @@ void sp_selection_copy()
         sp_repr_css_attr_unref (style_clipboard);
         style_clipboard = NULL;
     }
-  
-    //clear main clipboard 
+
+    //clear main clipboard
     while (clipboard) {
         Inkscape::GC::release((Inkscape::XML::Node *) clipboard->data);
         clipboard = g_slist_remove(clipboard, clipboard->data);
@@ -1024,24 +1058,26 @@ void sp_selection_copy()
         g_free (query);
     }
 
+    size_clipboard = selection->bounds();
+
     g_slist_free ((GSList *) items);
 }
 
 void sp_selection_paste(bool in_place)
 {
     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
-    
+
     if (desktop == NULL) {
         return;
     }
 
-    SPDocument *document = SP_DT_DOCUMENT(desktop);
+    SPDocument *document = sp_desktop_document(desktop);
 
     if (Inkscape::have_viable_layer(desktop, desktop->messageStack()) == false) {
         return;
     }
 
-    Inkscape::Selection *selection = SP_DT_SELECTION(desktop);
+    Inkscape::Selection *selection = sp_desktop_selection(desktop);
 
     if (tools_isactive (desktop, TOOLS_TEXT)) {
         if (sp_text_paste_inline(desktop->event_context))
@@ -1066,7 +1102,7 @@ void sp_selection_paste(bool in_place)
 
         /* Snap the offset of the new item(s) to the grid */
         /* FIXME: this gridsnap fiddling is a hack. */
-        Inkscape::GridSnapper &s = desktop->namedview->grid_snapper;
+        Inkscape::GridSnapper &s = desktop->namedview->snap_manager.grid;
         gdouble const curr_gridsnap = s.getDistance();
         s.setDistance(NR_HUGE);
         m = s.freeSnap(Inkscape::Snapper::SNAP_POINT, m, NULL).getPoint();
@@ -1082,7 +1118,7 @@ void sp_selection_paste_style()
     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
     if (desktop == NULL) return;
 
-    Inkscape::Selection *selection = SP_DT_SELECTION(desktop);
+    Inkscape::Selection *selection = sp_desktop_selection(desktop);
 
     // check if something is in the clipboard
     if (clipboard == NULL) {
@@ -1096,18 +1132,93 @@ void sp_selection_paste_style()
         return;
     }
 
-    paste_defs (&defs_clipboard, SP_DT_DOCUMENT(desktop));
+    paste_defs (&defs_clipboard, sp_desktop_document(desktop));
 
     sp_desktop_set_style (desktop, style_clipboard);
 
-    sp_document_done(SP_DT_DOCUMENT (desktop));
+    sp_document_done(sp_desktop_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_desktop_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_desktop_document (desktop));
 }
 
-void sp_selection_to_next_layer () 
+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_desktop_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_desktop_document (desktop));
+}
+
+void sp_selection_to_next_layer ()
 {
     SPDesktop *dt = SP_ACTIVE_DESKTOP;
 
-    Inkscape::Selection *selection = SP_DT_SELECTION(dt);
+    Inkscape::Selection *selection = sp_desktop_selection(dt);
 
     // check if something is selected
     if (selection->isEmpty()) {
@@ -1117,29 +1228,41 @@ void sp_selection_to_next_layer ()
 
     const GSList *items = g_slist_copy ((GSList *) selection->itemList());
 
+    bool no_more = false; // Set to true, if no more layers above
     SPObject *next=Inkscape::next_layer(dt->currentRoot(), dt->currentLayer());
     if (next) {
         GSList *temp_clip = NULL;
         sp_selection_copy_impl (items, &temp_clip, NULL, NULL); // we're in the same doc, so no need to copy defs
         sp_selection_delete_impl (items);
-        GSList *copied = sp_selection_paste_impl (SP_DT_DOCUMENT (dt), next, &temp_clip, NULL);
+        next=Inkscape::next_layer(dt->currentRoot(), dt->currentLayer()); // Fixes bug 1482973: crash while moving layers
+        GSList *copied;
+        if(next) {
+            copied = sp_selection_paste_impl (sp_desktop_document (dt), next, &temp_clip, NULL);
+        } else {
+            copied = sp_selection_paste_impl (sp_desktop_document (dt), dt->currentLayer(), &temp_clip, NULL);
+            no_more = true;
+        }
         selection->setReprList((GSList const *) copied);
         g_slist_free (copied);
         if (temp_clip) g_slist_free (temp_clip);
-        dt->setCurrentLayer(next);
-        sp_document_done(SP_DT_DOCUMENT (dt));
+        if (next) dt->setCurrentLayer(next);
+        sp_document_done(sp_desktop_document (dt));
     } else {
+        no_more = true;
+    }
+
+    if (no_more) {
         dt->messageStack()->flash(Inkscape::WARNING_MESSAGE, _("No more layers above."));
     }
 
     g_slist_free ((GSList *) items);
 }
 
-void sp_selection_to_prev_layer () 
+void sp_selection_to_prev_layer ()
 {
     SPDesktop *dt = SP_ACTIVE_DESKTOP;
 
-    Inkscape::Selection *selection = SP_DT_SELECTION(dt);
+    Inkscape::Selection *selection = sp_desktop_selection(dt);
 
     // check if something is selected
     if (selection->isEmpty()) {
@@ -1149,18 +1272,30 @@ void sp_selection_to_prev_layer ()
 
     const GSList *items = g_slist_copy ((GSList *) selection->itemList());
 
+    bool no_more = false; // Set to true, if no more layers below
     SPObject *next=Inkscape::previous_layer(dt->currentRoot(), dt->currentLayer());
     if (next) {
         GSList *temp_clip = NULL;
         sp_selection_copy_impl (items, &temp_clip, NULL, NULL); // we're in the same doc, so no need to copy defs
-        sp_selection_delete_impl (items); 
-        GSList *copied = sp_selection_paste_impl (SP_DT_DOCUMENT (dt), next, &temp_clip, NULL);
+        sp_selection_delete_impl (items);
+        next=Inkscape::previous_layer(dt->currentRoot(), dt->currentLayer()); // Fixes bug 1482973: crash while moving layers
+        GSList *copied;
+        if(next) {
+            copied = sp_selection_paste_impl (sp_desktop_document (dt), next, &temp_clip, NULL);
+        } else {
+            copied = sp_selection_paste_impl (sp_desktop_document (dt), dt->currentLayer(), &temp_clip, NULL);
+            no_more = true;
+        }
         selection->setReprList((GSList const *) copied);
         g_slist_free (copied);
         if (temp_clip) g_slist_free (temp_clip);
-        dt->setCurrentLayer(next);
-        sp_document_done(SP_DT_DOCUMENT (dt));
+        if (next) dt->setCurrentLayer(next);
+        sp_document_done(sp_desktop_document (dt));
     } else {
+        no_more = true;
+    }
+
+    if (no_more) {
         dt->messageStack()->flash(Inkscape::WARNING_MESSAGE, _("No more layers below."));
     }
 
@@ -1182,7 +1317,7 @@ void sp_selection_apply_affine(Inkscape::Selection *selection, NR::Matrix const
         SPItem *item = SP_ITEM(l->data);
 
         NR::Point old_center(0,0);
-        if (set_i2d && item->isCenterSet()) 
+        if (set_i2d && item->isCenterSet())
             old_center = item->getCenter();
 
 #if 0 /* Re-enable this once persistent guides have a graphical indication.
@@ -1195,7 +1330,21 @@ void sp_selection_apply_affine(Inkscape::Selection *selection, NR::Matrix const
         bool transform_textpath_with_path = (SP_IS_TEXT_TEXTPATH(item) && selection->includes( sp_textpath_get_path_item (SP_TEXTPATH(sp_object_first_child(SP_OBJECT(item)))) ));
         bool transform_flowtext_with_frame = (SP_IS_FLOWTEXT(item) && selection->includes( SP_FLOWTEXT(item)->get_frame (NULL))); // only the first frame so far
         bool transform_offset_with_source = (SP_IS_OFFSET(item) && SP_OFFSET (item)->sourceHref) && selection->includes( sp_offset_get_source (SP_OFFSET(item)) );
-
+       
+        // If we're moving a connector, we want to detach it
+        // from shapes that aren't part of the selection, but
+        // leave it attached if they are
+        if (cc_item_is_connector(item)) {
+            SPItem *attItem[2];
+            SP_PATH(item)->connEndPair.getAttachedItems(attItem);
+            
+            for (int n = 0; n < 2; ++n) {
+                if (!selection->includes(attItem[n])) {
+                    sp_conn_end_detach(item, n);
+                }
+            }
+        }
+        
         // "clones are unmoved when original is moved" preference
         int compensation = prefs_get_int_attribute("options.clonecompensation", "value", SP_CLONE_COMPENSATION_UNMOVED);
         bool prefs_unmoved = (compensation == SP_CLONE_COMPENSATION_UNMOVED);
@@ -1232,7 +1381,7 @@ void sp_selection_apply_affine(Inkscape::Selection *selection, NR::Matrix const
 
             // calculate the matrix we need to apply to the clone to cancel its induced transform from its original
             NR::Matrix t = matrix_to_desktop (matrix_from_desktop (affine, item), item);
-            NR::Matrix t_inv = matrix_to_desktop (matrix_from_desktop (affine.inverse(), item), item); 
+            NR::Matrix t_inv = matrix_to_desktop (matrix_from_desktop (affine.inverse(), item), item);
             NR::Matrix result = t_inv * item->transform * t;
 
             if ((prefs_parallel || prefs_unmoved) && affine.is_translation()) {
@@ -1280,7 +1429,7 @@ void sp_selection_remove_transform()
     if (desktop == NULL)
         return;
 
-    Inkscape::Selection *selection = SP_DT_SELECTION(desktop);
+    Inkscape::Selection *selection = sp_desktop_selection(desktop);
 
     GSList const *l = (GSList *) selection->reprList();
     while (l != NULL) {
@@ -1288,7 +1437,7 @@ void sp_selection_remove_transform()
         l = l->next;
     }
 
-    sp_document_done(SP_DT_DOCUMENT(desktop));
+    sp_document_done(sp_desktop_document(desktop));
 }
 
 void
@@ -1301,7 +1450,7 @@ sp_selection_scale_absolute(Inkscape::Selection *selection,
 
     NR::Rect const bbox(selection->bounds());
     if (bbox.isEmpty()) {
-        return; 
+        return;
     }
 
     NR::translate const p2o(-bbox.min());
@@ -1384,7 +1533,7 @@ void sp_selection_rotate_90_cw()
 {
     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
 
-    Inkscape::Selection *selection = SP_DT_SELECTION(desktop);
+    Inkscape::Selection *selection = sp_desktop_selection(desktop);
 
     if (selection->isEmpty())
         return;
@@ -1396,7 +1545,7 @@ void sp_selection_rotate_90_cw()
         sp_item_rotate_rel(item, rot_neg_90);
     }
 
-    sp_document_done(SP_DT_DOCUMENT(desktop));
+    sp_document_done(sp_desktop_document(desktop));
 }
 
 
@@ -1411,7 +1560,7 @@ void sp_selection_rotate_90_ccw()
 {
     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
 
-    Inkscape::Selection *selection = SP_DT_SELECTION(desktop);
+    Inkscape::Selection *selection = sp_desktop_selection(desktop);
 
     if (selection->isEmpty())
         return;
@@ -1423,7 +1572,7 @@ void sp_selection_rotate_90_ccw()
         sp_item_rotate_rel(item, rot_neg_90);
     }
 
-    sp_document_done(SP_DT_DOCUMENT(desktop));
+    sp_document_done(sp_desktop_document(desktop));
 }
 
 void
@@ -1432,11 +1581,11 @@ 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);
 
-    sp_document_maybe_done(SP_DT_DOCUMENT(selection->desktop()),
+    sp_document_maybe_done(sp_desktop_document(selection->desktop()),
                            ( ( angle_degrees > 0 )
                              ? "selector:rotate:ccw"
                              : "selector:rotate:cw" ));
@@ -1452,7 +1601,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;
@@ -1462,7 +1612,7 @@ sp_selection_rotate_screen(Inkscape::Selection *selection, gdouble angle)
 
     sp_selection_rotate_relative(selection, center, zangle);
 
-    sp_document_maybe_done(SP_DT_DOCUMENT(selection->desktop()),
+    sp_document_maybe_done(sp_desktop_document(selection->desktop()),
                            ( (angle > 0)
                              ? "selector:rotate:ccw"
                              : "selector:rotate:cw" ));
@@ -1486,7 +1636,7 @@ sp_selection_scale(Inkscape::Selection *selection, gdouble grow)
     double const times = 1.0 + grow / max_len;
     sp_selection_scale_relative(selection, center, NR::scale(times, times));
 
-    sp_document_maybe_done(SP_DT_DOCUMENT(selection->desktop()),
+    sp_document_maybe_done(sp_desktop_document(selection->desktop()),
                            ( (grow > 0)
                              ? "selector:scale:larger"
                              : "selector:scale:smaller" ));
@@ -1507,14 +1657,14 @@ sp_selection_scale_times(Inkscape::Selection *selection, gdouble times)
 
     NR::Point const center(selection->bounds().midpoint());
     sp_selection_scale_relative(selection, center, NR::scale(times, times));
-    sp_document_done(SP_DT_DOCUMENT(selection->desktop()));
+    sp_document_done(sp_desktop_document(selection->desktop()));
 }
 
 void
 sp_selection_move(gdouble dx, gdouble dy)
 {
     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
-    Inkscape::Selection *selection = SP_DT_SELECTION(desktop);
+    Inkscape::Selection *selection = sp_desktop_selection(desktop);
     if (selection->isEmpty()) {
         return;
     }
@@ -1522,11 +1672,11 @@ sp_selection_move(gdouble dx, gdouble dy)
     sp_selection_move_relative(selection, dx, dy);
 
     if (dx == 0) {
-        sp_document_maybe_done(SP_DT_DOCUMENT(desktop), "selector:move:vertical");
+        sp_document_maybe_done(sp_desktop_document(desktop), "selector:move:vertical");
     } else if (dy == 0) {
-        sp_document_maybe_done(SP_DT_DOCUMENT(desktop), "selector:move:horizontal");
+        sp_document_maybe_done(sp_desktop_document(desktop), "selector:move:horizontal");
     } else {
-        sp_document_done(SP_DT_DOCUMENT(desktop));
+        sp_document_done(sp_desktop_document(desktop));
     }
 }
 
@@ -1535,7 +1685,7 @@ sp_selection_move_screen(gdouble dx, gdouble dy)
 {
     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
 
-    Inkscape::Selection *selection = SP_DT_SELECTION(desktop);
+    Inkscape::Selection *selection = sp_desktop_selection(desktop);
     if (selection->isEmpty()) {
         return;
     }
@@ -1547,11 +1697,11 @@ sp_selection_move_screen(gdouble dx, gdouble dy)
     sp_selection_move_relative(selection, zdx, zdy);
 
     if (dx == 0) {
-        sp_document_maybe_done(SP_DT_DOCUMENT(desktop), "selector:move:vertical");
+        sp_document_maybe_done(sp_desktop_document(desktop), "selector:move:vertical");
     } else if (dy == 0) {
-        sp_document_maybe_done(SP_DT_DOCUMENT(desktop), "selector:move:horizontal");
+        sp_document_maybe_done(sp_desktop_document(desktop), "selector:move:horizontal");
     } else {
-        sp_document_done(SP_DT_DOCUMENT(desktop));
+        sp_document_done(sp_desktop_document(desktop));
     }
 }
 
@@ -1559,11 +1709,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);
+SPItem *next_item_from_list(SPDesktop *desktop, GSList const *items, SPObject *root,
+                  bool only_in_viewport, PrefsSelectionContext inlayer, bool onlyvisible, bool onlysensitive);
 
 struct Forward {
     typedef SPObject *Iterator;
@@ -1612,15 +1762,15 @@ sp_selection_item_next(void)
 {
     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
     g_return_if_fail(desktop != NULL);
-    Inkscape::Selection *selection = SP_DT_SELECTION(desktop);
+    Inkscape::Selection *selection = sp_desktop_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();
     }
@@ -1628,7 +1778,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);
         }
@@ -1642,15 +1792,15 @@ sp_selection_item_prev(void)
     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
     g_return_if_fail(document != NULL);
     g_return_if_fail(desktop != NULL);
-    Inkscape::Selection *selection = SP_DT_SELECTION(desktop);
+    Inkscape::Selection *selection = sp_desktop_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();
     }
@@ -1658,7 +1808,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);
         }
@@ -1669,7 +1819,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) {
@@ -1703,7 +1853,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;
@@ -1724,7 +1874,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) &&
@@ -1774,7 +1924,7 @@ sp_selection_clone()
     if (desktop == NULL)
         return;
 
-    Inkscape::Selection *selection = SP_DT_SELECTION(desktop);
+    Inkscape::Selection *selection = sp_desktop_selection(desktop);
 
     // check if something is selected
     if (selection->isEmpty()) {
@@ -1782,27 +1932,40 @@ sp_selection_clone()
         return;
     }
 
-    // Check if more than one object is selected.
-    if (g_slist_length((GSList *) selection->itemList()) > 1) {
-        desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE, _("If you want to clone several objects, <b>group</b> them and <b>clone the group</b>."));
-        return;
-    }
+    GSList *reprs = g_slist_copy((GSList *) selection->reprList());
+  
+    selection->clear();
+  
+    // sorting items from different parents sorts each parent's subset without possibly mixing them, just what we need
+    reprs = g_slist_sort(reprs, (GCompareFunc) sp_repr_compare_position);
 
-    Inkscape::XML::Node *sel_repr = SP_OBJECT_REPR(selection->singleItem());
-    Inkscape::XML::Node *parent = sp_repr_parent(sel_repr);
+    GSList *newsel = NULL;
+    while (reprs) {
+        Inkscape::XML::Node *sel_repr = (Inkscape::XML::Node *) reprs->data;
+        Inkscape::XML::Node *parent = sp_repr_parent(sel_repr);
 
-    Inkscape::XML::Node *clone = sp_repr_new("svg:use");
-    clone->setAttribute("x", "0");
-    clone->setAttribute("y", "0");
-    clone->setAttribute("xlink:href", g_strdup_printf("#%s", sel_repr->attribute("id")));
+        Inkscape::XML::Node *clone = sp_repr_new("svg:use");
+        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")));
 
-    // add the new clone to the top of the original's parent
-    parent->appendChild(clone);
+        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);
 
-    sp_document_done(SP_DT_DOCUMENT(desktop));
+        newsel = g_slist_prepend(newsel, clone);
+        reprs = g_slist_remove(reprs, sel_repr);
+        Inkscape::GC::release(clone);
+    }
+    
+    sp_document_done(sp_desktop_document(desktop));
 
-    selection->set(clone);
-    Inkscape::GC::release(clone);
+    selection->setReprList(newsel);
+    g_slist_free(newsel);
 }
 
 void
@@ -1812,7 +1975,7 @@ sp_selection_unlink()
     if (!desktop)
         return;
 
-    Inkscape::Selection *selection = SP_DT_SELECTION(desktop);
+    Inkscape::Selection *selection = sp_desktop_selection(desktop);
 
     if (selection->isEmpty()) {
         desktop->messageStack()->flash(Inkscape::WARNING_MESSAGE, _("Select a <b>clone</b> to unlink."));
@@ -1849,7 +2012,7 @@ sp_selection_unlink()
         desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE, _("<b>No clones to unlink</b> in the selection."));
     }
 
-    sp_document_done(SP_DT_DOCUMENT(desktop));
+    sp_document_done(sp_desktop_document(desktop));
 }
 
 void
@@ -1859,7 +2022,7 @@ sp_select_clone_original()
     if (desktop == NULL)
         return;
 
-    Inkscape::Selection *selection = SP_DT_SELECTION(desktop);
+    Inkscape::Selection *selection = sp_desktop_selection(desktop);
 
     SPItem *item = selection->singleItem();
 
@@ -1913,9 +2076,9 @@ sp_selection_tile(bool apply)
     if (desktop == NULL)
         return;
 
-    SPDocument *document = SP_DT_DOCUMENT(desktop);
+    SPDocument *document = sp_desktop_document(desktop);
 
-    Inkscape::Selection *selection = SP_DT_SELECTION(desktop);
+    Inkscape::Selection *selection = sp_desktop_selection(desktop);
 
     // check if something is selected
     if (selection->isEmpty()) {
@@ -1984,7 +2147,7 @@ sp_selection_tile(bool apply)
         // restore parent and position
         SP_OBJECT_REPR (parent)->appendChild(rect);
         rect->setPosition(pos > 0 ? pos : 0);
-        SPItem *rectangle = (SPItem *) SP_DT_DOCUMENT (desktop)->getObjectByRepr(rect);
+        SPItem *rectangle = (SPItem *) sp_desktop_document (desktop)->getObjectByRepr(rect);
 
         Inkscape::GC::release(rect);
 
@@ -2004,9 +2167,9 @@ sp_selection_untile()
     if (desktop == NULL)
         return;
 
-    SPDocument *document = SP_DT_DOCUMENT(desktop);
+    SPDocument *document = sp_desktop_document(desktop);
 
-    Inkscape::Selection *selection = SP_DT_SELECTION(desktop);
+    Inkscape::Selection *selection = sp_desktop_selection(desktop);
 
     // check if something is selected
     if (selection->isEmpty()) {
@@ -2026,7 +2189,7 @@ sp_selection_untile()
 
         SPStyle *style = SP_OBJECT_STYLE (item);
 
-        if (!style || style->fill.type != SP_PAINT_TYPE_PAINTSERVER) 
+        if (!style || style->fill.type != SP_PAINT_TYPE_PAINTSERVER)
             continue;
 
         SPObject *server = SP_OBJECT_STYLE_FILL_SERVER(item);
@@ -2046,7 +2209,7 @@ sp_selection_untile()
             SPItem *i = SP_ITEM (desktop->currentLayer()->appendChildRepr(copy));
 
            // FIXME: relink clones to the new canvas objects
-           // use SPObject::setid when mental finishes it to steal ids of 
+           // use SPObject::setid when mental finishes it to steal ids of
 
             // this is needed to make sure the new item has curve (simply requestDisplayUpdate does not work)
             sp_document_ensure_up_to_date (document);
@@ -2065,7 +2228,7 @@ sp_selection_untile()
     if (!did) {
         desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE, _("<b>No pattern fills</b> in the selection."));
     } else {
-        sp_document_done(SP_DT_DOCUMENT(desktop));
+        sp_document_done(sp_desktop_document(desktop));
         selection->setList(new_select);
     }
 }
@@ -2077,9 +2240,9 @@ sp_selection_create_bitmap_copy ()
     if (desktop == NULL)
         return;
 
-    SPDocument *document = SP_DT_DOCUMENT(desktop);
+    SPDocument *document = sp_desktop_document(desktop);
 
-    Inkscape::Selection *selection = SP_DT_SELECTION(desktop);
+    Inkscape::Selection *selection = sp_desktop_selection(desktop);
 
     // check if something is selected
     if (selection->isEmpty()) {
@@ -2105,7 +2268,7 @@ sp_selection_create_bitmap_copy ()
     // multiple times, and this is done so that they don't clash)
     GTimeVal cu;
     g_get_current_time (&cu);
-    guint current = (int) (cu.tv_sec * 1000000 + cu.tv_usec) % 1024; 
+    guint current = (int) (cu.tv_sec * 1000000 + cu.tv_usec) % 1024;
 
     // Create the filename
     gchar *filename = g_strdup_printf ("%s-%s-%u.png", document->name, SP_OBJECT_REPR(items->data)->attribute("id"), current);
@@ -2164,7 +2327,7 @@ sp_selection_create_bitmap_copy ()
 
     // Calculate the matrix that will be applied to the image so that it exactly overlaps the source objects
     NR::Matrix eek = sp_item_i2d_affine (SP_ITEM(parent_object));
-    NR::Matrix t = NR::scale (1/res, -1/res) * NR::translate (bbox.x0, bbox.y1) * eek.inverse(); 
+    NR::Matrix t = NR::scale (1/res, -1/res) * NR::translate (bbox.x0, bbox.y1) * eek.inverse();
 
     // Do the export
     sp_export_png_file(document, filepath,
@@ -2197,12 +2360,12 @@ sp_selection_create_bitmap_copy ()
         gchar c[256];
         if (sp_svg_transform_write(c, 256, t)) {
             repr->setAttribute("transform", c);
-        } 
+        }
 
         // add the new repr to the parent
         parent->appendChild(repr);
 
-        // move to the saved position 
+        // move to the saved position
         repr->setPosition(pos > 0 ? pos + 1 : 1);
 
         // Set selection to the new image
@@ -2221,6 +2384,249 @@ 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_desktop_document(desktop);
+    
+    Inkscape::Selection *selection = sp_desktop_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 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 clippath or 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_desktop_document(desktop);    
+    Inkscape::Selection *selection = sp_desktop_selection(desktop);
+
+    // check if something is selected
+    if (selection->isEmpty()) {
+        desktop->messageStack()->flash(Inkscape::WARNING_MESSAGE, _("Select <b>object(s)</b> to remove clippath or 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);
+        }
+
+        // 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);
+
+            SPItem *mask_item = (SPItem *) sp_desktop_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);
+}
+
+void fit_canvas_to_selection(SPDesktop *desktop) {
+    g_return_if_fail(desktop != NULL);
+    SPDocument *doc = sp_desktop_document(desktop);
+
+    g_return_if_fail(doc != NULL);
+    g_return_if_fail(desktop->selection != NULL);
+    g_return_if_fail(!desktop->selection->isEmpty());
+    NRRect bbox = {0,0,0,0};
+
+    desktop->selection->bounds(&bbox);
+    if (!empty(bbox)) {
+        doc->fitToRect(bbox);
+    }
+};
+
+void fit_canvas_to_drawing(SPDocument *doc) {
+    g_return_if_fail(doc != NULL);
+    NRRect bbox = {0,0,0,0};
+
+    sp_document_ensure_up_to_date (doc);
+    sp_item_invoke_bbox(SP_ITEM(doc->root), &bbox, sp_item_i2r_affine(SP_ITEM(doc->root)), TRUE);
+
+    if (!empty(bbox)) {
+        doc->fitToRect(bbox);
+    }
+};
+
+void fit_canvas_to_selection_or_drawing(SPDesktop *desktop) {
+    g_return_if_fail(desktop != NULL);
+    SPDocument *doc = sp_desktop_document(desktop);
+
+    g_return_if_fail(doc != NULL);
+    g_return_if_fail(desktop->selection != NULL);
+
+    if (desktop->selection->isEmpty()) {
+        fit_canvas_to_drawing(doc);
+    } else {
+        fit_canvas_to_selection(desktop);
+    }
+
+    sp_document_done(doc);
+};
 
 /*
   Local Variables: