Code

Connector tool: make connectors avoid the convex hull of shapes.
[inkscape.git] / src / interface.cpp
index 1ad90c58c5746f1dfde172fc477b812407357fd3..415593b7885ba76dbe76fb259af0cd718708bb4e 100644 (file)
@@ -1,9 +1,9 @@
 #define __SP_INTERFACE_C__
 
-/**
- * Main UI stuff
- *
- * Authors:
+/** @file
+ * @brief Main UI stuff
+ */
+/* Authors:
  *   Lauris Kaplinski <lauris@kaplinski.com>
  *   Frank Felfe <innerspace@iname.com>
  *   bulia byak <buliabyak@users.sf.net>
 #endif
 
 #include <gtk/gtk.h>
+#include <glib.h>
+
 #include "inkscape-private.h"
 #include "extension/effect.h"
 #include "widgets/icon.h"
-#include "prefs-utils.h"
+#include "preferences.h"
 #include "path-prefix.h"
-
 #include "shortcuts.h"
-
 #include "document.h"
 #include "desktop-handles.h"
 #include "file.h"
 #include "widgets/desktop-widget.h"
 #include "sp-item-group.h"
 #include "sp-text.h"
+#include "sp-gradient-fns.h"
+#include "sp-gradient.h"
 #include "sp-flowtext.h"
 #include "sp-namedview.h"
 #include "ui/view/view.h"
-
 #include "helper/action.h"
 #include "helper/gnome-utils.h"
 #include "helper/window.h"
-
 #include "io/sys.h"
-#include "io/stringstream.h"
-#include "io/base64stream.h"
-
 #include "dialogs/dialog-events.h"
-
 #include "message-context.h"
 
 // Added for color drag-n-drop
 #include "style.h"
 #include "event-context.h"
 #include "gradient-drag.h"
+#include "widgets/ege-paint-def.h"
 
 // Include Mac OS X menu synchronization on native OSX build
 #ifdef GDK_WINDOWING_QUARTZ
 #include "ige-mac-menu.h"
 #endif
 
-using Inkscape::IO::StringOutputStream;
-using Inkscape::IO::Base64OutputStream;
-
 /* Drag and Drop */
 typedef enum {
     URI_LIST,
@@ -85,7 +79,8 @@ typedef enum {
     JPEG_DATA,
     IMAGE_DATA,
     APP_X_INKY_COLOR,
-    APP_X_COLOR
+    APP_X_COLOR,
+    APP_OSWB_COLOR,
 } ui_drop_target_info;
 
 static GtkTargetEntry ui_drop_target_entries [] = {
@@ -97,11 +92,13 @@ static GtkTargetEntry ui_drop_target_entries [] = {
 #if ENABLE_MAGIC_COLORS
     {(gchar *)"application/x-inkscape-color", 0, APP_X_INKY_COLOR},
 #endif // ENABLE_MAGIC_COLORS
+    {(gchar *)"application/x-oswb-color",     0, APP_OSWB_COLOR  },
     {(gchar *)"application/x-color",          0, APP_X_COLOR     }
 };
 
 static GtkTargetEntry *completeDropTargets = 0;
 static int completeDropTargetsCount = 0;
+static bool temporarily_block_actions = false;
 
 #define ENTRIES_SIZE(n) sizeof(n)/sizeof(n[0])
 static guint nui_drop_target_entries = ENTRIES_SIZE(ui_drop_target_entries);
@@ -129,9 +126,10 @@ static void sp_ui_drag_leave( GtkWidget *widget,
 static void sp_ui_menu_item_set_sensitive(SPAction *action,
                                           unsigned int sensitive,
                                           void *data);
-static void sp_ui_menu_item_set_name(SPAction *action, 
+static void sp_ui_menu_item_set_name(SPAction *action,
                                      Glib::ustring name,
                                      void *data);
+static void sp_recent_open(GtkRecentChooser *, gpointer);
 
 SPActionEventVector menu_item_event_vector = {
     {NULL},
@@ -152,40 +150,42 @@ sp_create_window(SPViewWidget *vw, gboolean editable)
 
     Gtk::Window *win = Inkscape::UI::window_new("", TRUE);
 
+    gtk_container_add(GTK_CONTAINER(win->gobj()), GTK_WIDGET(vw));
+    gtk_widget_show(GTK_WIDGET(vw));
+
     if (editable) {
-               g_object_set_data(G_OBJECT(vw), "window", win);
-               
-               SPDesktopWidget *desktop_widget = reinterpret_cast<SPDesktopWidget*>(vw);
-               SPDesktop* desktop = desktop_widget->desktop;
-               
-               desktop_widget->window = win;
+        g_object_set_data(G_OBJECT(vw), "window", win);
+
+        SPDesktopWidget *desktop_widget = reinterpret_cast<SPDesktopWidget*>(vw);
+        SPDesktop* desktop = desktop_widget->desktop;
+
+        desktop_widget->window = win;
 
         win->set_data("desktop", desktop);
         win->set_data("desktopwidget", desktop_widget);
-               
+
         win->signal_delete_event().connect(sigc::mem_fun(*(SPDesktop*)vw->view, &SPDesktop::onDeleteUI));
-               win->signal_window_state_event().connect(sigc::mem_fun(*desktop, &SPDesktop::onWindowStateEvent));
-               win->signal_focus_in_event().connect(sigc::mem_fun(*desktop_widget, &SPDesktopWidget::onFocusInEvent));
-               
-        gint prefs_geometry = 
-            (2==prefs_get_int_attribute("options.savewindowgeometry", "value", 0));
+        win->signal_window_state_event().connect(sigc::mem_fun(*desktop, &SPDesktop::onWindowStateEvent));
+        win->signal_focus_in_event().connect(sigc::mem_fun(*desktop_widget, &SPDesktopWidget::onFocusInEvent));
+
+        Inkscape::Preferences *prefs = Inkscape::Preferences::get();
+        gint prefs_geometry =
+            (2==prefs->getInt("/options/savewindowgeometry/value", 0));
         if (prefs_geometry) {
-            gint pw = prefs_get_int_attribute("desktop.geometry", "width", -1);
-            gint ph = prefs_get_int_attribute("desktop.geometry", "height", -1);
-            gint px = prefs_get_int_attribute("desktop.geometry", "x", -1);
-            gint py = prefs_get_int_attribute("desktop.geometry", "y", -1);
-            gint full = prefs_get_int_attribute("desktop.geometry", "fullscreen", 0);
-            gint maxed = prefs_get_int_attribute("desktop.geometry", "maximized", 0);
+            gint pw = prefs->getInt("/desktop/geometry/width", -1);
+            gint ph = prefs->getInt("/desktop/geometry/height", -1);
+            gint px = prefs->getInt("/desktop/geometry/x", -1);
+            gint py = prefs->getInt("/desktop/geometry/y", -1);
+            gint full = prefs->getBool("/desktop/geometry/fullscreen");
+            gint maxed = prefs->getBool("/desktop/geometry/maximized");
             if (pw>0 && ph>0) {
                 gint w = MIN(gdk_screen_width(), pw);
                 gint h = MIN(gdk_screen_height(), ph);
                 gint x = MIN(gdk_screen_width() - MIN_ONSCREEN_DISTANCE, px);
                 gint y = MIN(gdk_screen_height() - MIN_ONSCREEN_DISTANCE, py);
-                if (w>0 && h>0 && x>0 && y>0) {
+                if (w>0 && h>0) {
                     x = MIN(gdk_screen_width() - w, x);
                     y = MIN(gdk_screen_height() - h, y);
-                }
-                if (w>0 && h>0) {
                     desktop->setWindowSize(w, h);
                 }
 
@@ -195,11 +195,9 @@ sp_create_window(SPViewWidget *vw, gboolean editable)
 
                 // Empirically it seems that active_desktop==this desktop only the first time a
                 // desktop is created.
-                if (x>0 && y>0) {
-                    SPDesktop *active_desktop = SP_ACTIVE_DESKTOP;
-                    if (active_desktop == desktop || active_desktop==NULL) {
-                        desktop->setWindowPosition(NR::Point(x, y));
-                    }
+                SPDesktop *active_desktop = SP_ACTIVE_DESKTOP;
+                if (active_desktop == desktop || active_desktop==NULL) {
+                    desktop->setWindowPosition(Geom::Point(x, y));
                 }
             }
             if (maxed) {
@@ -214,9 +212,6 @@ sp_create_window(SPViewWidget *vw, gboolean editable)
         gtk_window_set_policy(GTK_WINDOW(win->gobj()), TRUE, TRUE, TRUE);
     }
 
-    gtk_container_add(GTK_CONTAINER(win->gobj()), GTK_WIDGET(vw));
-    gtk_widget_show(GTK_WIDGET(vw));
-
     if ( completeDropTargets == 0 || completeDropTargetsCount == 0 )
     {
         std::vector<gchar*> types;
@@ -314,13 +309,19 @@ sp_ui_new_view_preview()
 void
 sp_ui_close_view(GtkWidget */*widget*/)
 {
-    if (SP_ACTIVE_DESKTOP == NULL) {
+       SPDesktop *dt = SP_ACTIVE_DESKTOP;
+
+       if (dt == NULL) {
         return;
     }
-    if ((SP_ACTIVE_DESKTOP)->shutdown()) {
-        return;
+
+    if (dt->shutdown()) {
+        return; // Shutdown operation has been canceled, so do nothing
     }
-    SP_ACTIVE_DESKTOP->destroyWidget();
+
+    // Shutdown can proceed; use the stored reference to the desktop here instead of the current SP_ACTIVE_DESKTOP,
+    // because the user might have changed the focus in the meantime (see bug #381357 on Launchpad)
+    dt->destroyWidget();
 }
 
 
@@ -341,11 +342,14 @@ sp_ui_close_all(void)
     /* Iterate through all the windows, destroying each in the order they
        become active */
     while (SP_ACTIVE_DESKTOP) {
-        if ((SP_ACTIVE_DESKTOP)->shutdown()) {
-            /* The user cancelled the operation, so end doing the close */
+       SPDesktop *dt = SP_ACTIVE_DESKTOP;
+       if (dt->shutdown()) {
+            /* The user canceled the operation, so end doing the close */
             return FALSE;
         }
-        SP_ACTIVE_DESKTOP->destroyWidget();
+       // Shutdown can proceed; use the stored reference to the desktop here instead of the current SP_ACTIVE_DESKTOP,
+       // because the user might have changed the focus in the meantime (see bug #381357 on Launchpad)
+       dt->destroyWidget();
     }
 
     return TRUE;
@@ -361,7 +365,9 @@ sp_ui_close_all(void)
 static void
 sp_ui_menu_activate(void */*object*/, SPAction *action)
 {
-    sp_action_perform(action, NULL);
+    if (!temporarily_block_actions) {
+       sp_action_perform(action, NULL);
+    }
 }
 
 static void
@@ -612,9 +618,8 @@ sp_ui_menu_append_item_from_verb(GtkMenu *menu, Inkscape::Verb *verb, Inkscape::
             sp_ui_menuitem_add_icon(item, action->image);
         }
         gtk_widget_set_events(item, GDK_KEY_PRESS_MASK);
-        g_signal_connect( G_OBJECT(item), "activate",
-                          G_CALLBACK(sp_ui_menu_activate), action );
-
+        g_object_set_data(G_OBJECT(item), "view", (gpointer) view);
+        g_signal_connect( G_OBJECT(item), "activate", G_CALLBACK(sp_ui_menu_activate), action );
         g_signal_connect( G_OBJECT(item), "select", G_CALLBACK(sp_ui_menu_select_action), action );
         g_signal_connect( G_OBJECT(item), "deselect", G_CALLBACK(sp_ui_menu_deselect_action), action );
     }
@@ -633,14 +638,20 @@ checkitem_toggled(GtkCheckMenuItem *menuitem, gpointer user_data)
     gchar const *pref = (gchar const *) user_data;
     Inkscape::UI::View::View *view = (Inkscape::UI::View::View *) g_object_get_data(G_OBJECT(menuitem), "view");
 
-    gchar const *pref_path;
-    if (reinterpret_cast<SPDesktop*>(view)->is_fullscreen())
-        pref_path = g_strconcat("fullscreen.", pref, NULL);
-    else
-        pref_path = g_strconcat("window.", pref, NULL);
+    Glib::ustring pref_path;
+    if (reinterpret_cast<SPDesktop*>(view)->is_focusMode()) {
+        pref_path = "/focus/";
+    } else if (reinterpret_cast<SPDesktop*>(view)->is_fullscreen()) {
+        pref_path = "/fullscreen/";
+    } else {
+        pref_path = "/window/";
+    }
+    pref_path += pref;
+    pref_path += "/state";
 
+    Inkscape::Preferences *prefs = Inkscape::Preferences::get();
     gboolean checked = gtk_check_menu_item_get_active(menuitem);
-    prefs_set_int_attribute(pref_path, "state", checked);
+    prefs->setBool(pref_path, checked);
 
     reinterpret_cast<SPDesktop*>(view)->layoutWidget();
 }
@@ -653,13 +664,16 @@ checkitem_update(GtkWidget *widget, GdkEventExpose */*event*/, gpointer user_dat
     gchar const *pref = (gchar const *) user_data;
     Inkscape::UI::View::View *view = (Inkscape::UI::View::View *) g_object_get_data(G_OBJECT(menuitem), "view");
 
-    gchar const *pref_path;
-    if ((static_cast<SPDesktop*>(view))->is_fullscreen())
-        pref_path = g_strconcat("fullscreen.", pref, NULL);
-    else
-        pref_path = g_strconcat("window.", pref, NULL);
+    Glib::ustring pref_path;
+    if ((static_cast<SPDesktop*>(view))->is_fullscreen()) {
+        pref_path = "/fullscreen/";
+    } else {
+        pref_path = "/window/";
+    }
+    pref_path += pref;
 
-    gint ison = prefs_get_int_attribute_limited(pref_path, "state", 1, 0, 1);
+    Inkscape::Preferences *prefs = Inkscape::Preferences::get();
+    bool ison = prefs->getBool(pref_path + "/state", true);
 
     g_signal_handlers_block_by_func(G_OBJECT(menuitem), (gpointer)(GCallback)checkitem_toggled, user_data);
     gtk_check_menu_item_set_active(menuitem, ison);
@@ -668,6 +682,44 @@ checkitem_update(GtkWidget *widget, GdkEventExpose */*event*/, gpointer user_dat
     return FALSE;
 }
 
+/**
+ *  \brief Callback function to update the status of the radio buttons in the View -> Display mode menu (Normal, No Filters, Outline)
+ */
+
+static gboolean
+update_view_menu(GtkWidget *widget, GdkEventExpose */*event*/, gpointer user_data)
+{
+       SPAction *action = (SPAction *) user_data;
+       g_assert(action->id != NULL);
+
+       Inkscape::UI::View::View *view = (Inkscape::UI::View::View *) g_object_get_data(G_OBJECT(widget), "view");
+    SPDesktop *dt = static_cast<SPDesktop*>(view);
+       Inkscape::RenderMode mode = dt->getMode();
+
+       bool new_state = false;
+       if (!strcmp(action->id, "ViewModeNormal")) {
+       new_state = mode == Inkscape::RENDERMODE_NORMAL;
+       } else if (!strcmp(action->id, "ViewModeNoFilters")) {
+       new_state = mode == Inkscape::RENDERMODE_NO_FILTERS;
+    } else if (!strcmp(action->id, "ViewModeOutline")) {
+       new_state = mode == Inkscape::RENDERMODE_OUTLINE;
+    } else {
+       g_warning("update_view_menu does not handle this verb");
+    }
+
+       if (new_state) { //only one of the radio buttons has to be activated; the others will automatically be deactivated
+               if (!gtk_check_menu_item_get_active (GTK_CHECK_MENU_ITEM (widget))) {
+                       // When the GtkMenuItem version of the "activate" signal has been emitted by a GtkRadioMenuItem, there is a second
+                       // emission as the most recently active item is toggled to inactive. This is dealt with before the original signal is handled.
+                       // This emission however should not invoke any actions, hence we block it here:
+                       temporarily_block_actions = true;
+                       gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (widget), TRUE);
+                       temporarily_block_actions = false;
+               }
+       }
+
+       return FALSE;
+}
 
 void
 sp_ui_menu_append_check_item_from_verb(GtkMenu *menu, Inkscape::UI::View::View *view, gchar const *label, gchar const *tip, gchar const *pref,
@@ -733,9 +785,16 @@ sp_ui_menu_append_check_item_from_verb(GtkMenu *menu, Inkscape::UI::View::View *
 }
 
 static void
-sp_recent_open(GtkWidget */*widget*/, gchar const *uri)
+sp_recent_open(GtkRecentChooser *recent_menu, gpointer /*user_data*/)
 {
-    sp_file_open(uri, NULL);
+    // dealing with the bizarre filename convention in Inkscape for now
+    gchar *uri = gtk_recent_chooser_get_current_uri(GTK_RECENT_CHOOSER(recent_menu));
+    gchar *local_fn = g_filename_from_uri(uri, NULL, NULL);
+    gchar *utf8_fn = g_filename_to_utf8(local_fn, -1, NULL, NULL, NULL);
+    sp_file_open(utf8_fn, NULL);
+    g_free(utf8_fn);
+    g_free(local_fn);
+    g_free(uri);
 }
 
 static void
@@ -801,35 +860,6 @@ sp_menu_append_new_templates(GtkWidget *menu, Inkscape::UI::View::View *view)
     }
 }
 
-void
-sp_menu_append_recent_documents(GtkWidget *menu, Inkscape::UI::View::View* /* view */)
-{
-    gchar const **recent = prefs_get_recent_files();
-    if (recent) {
-        int i;
-
-        for (i = 0; recent[i] != NULL; i += 2) {
-            gchar const *uri = recent[i];
-            gchar const *name = recent[i + 1];
-
-            GtkWidget *item = gtk_menu_item_new_with_label(name);
-            gtk_widget_show(item);
-            g_signal_connect(G_OBJECT(item),
-                             "activate",
-                             G_CALLBACK(sp_recent_open),
-                             (gpointer)uri);
-            gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
-        }
-
-        g_free(recent);
-    } else {
-        GtkWidget *item = gtk_menu_item_new_with_label(_("None"));
-        gtk_widget_show(item);
-        gtk_widget_set_sensitive(item, FALSE);
-        gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
-    }
-}
-
 void
 sp_ui_checkboxes_menus(GtkMenu *m, Inkscape::UI::View::View *view)
 {
@@ -837,6 +867,8 @@ sp_ui_checkboxes_menus(GtkMenu *m, Inkscape::UI::View::View *view)
     //                                       checkitem_toggled, checkitem_update, 0);
     sp_ui_menu_append_check_item_from_verb(m, view, _("Commands Bar"), _("Show or hide the Commands bar (under the menu)"), "commands",
                                            checkitem_toggled, checkitem_update, 0);
+    sp_ui_menu_append_check_item_from_verb(m, view, _("Snap Controls Bar"), _("Show or hide the snapping controls"), "snaptoolbox",
+                                           checkitem_toggled, checkitem_update, 0);
     sp_ui_menu_append_check_item_from_verb(m, view, _("Tool Controls Bar"), _("Show or hide the Tool Controls bar"), "toppanel",
                                            checkitem_toggled, checkitem_update, 0);
     sp_ui_menu_append_check_item_from_verb(m, view, _("_Toolbox"), _("Show or hide the main toolbox (on the left)"), "toolbox",
@@ -851,6 +883,22 @@ sp_ui_checkboxes_menus(GtkMenu *m, Inkscape::UI::View::View *view)
                                            checkitem_toggled, checkitem_update, 0);
 }
 
+/** @brief Observer that updates the recent list's max document count */
+class MaxRecentObserver : public Inkscape::Preferences::Observer {
+public:
+    MaxRecentObserver(GtkWidget *recent_menu) :
+        Observer("/options/maxrecentdocuments/value"),
+        _rm(recent_menu)
+    {}
+    virtual void notify(Inkscape::Preferences::Entry const &e) {
+        gtk_recent_chooser_set_limit(GTK_RECENT_CHOOSER(_rm), e.getInt());
+        // hack: the recent menu doesn't repopulate after changing the limit, so we force it
+        g_signal_emit_by_name((gpointer) gtk_recent_manager_get_default(), "changed");
+    }
+private:
+    GtkWidget *_rm;
+};
+
 /** \brief  This function turns XML into a menu
     \param  menus  This is the XML that defines the menu
     \param  menu   Menu to be added to
@@ -862,7 +910,7 @@ sp_ui_checkboxes_menus(GtkMenu *m, Inkscape::UI::View::View *view)
     a couple of submenus, it is unlikely this will go more than two or
     three times.
 
-    In the case of an unreconginzed verb, a menu item is made to identify
+    In the case of an unrecognized verb, a menu item is made to identify
     the verb that is missing, and display that.  The menu item is also made
     insensitive.
 */
@@ -895,6 +943,10 @@ sp_ui_build_dyn_menus(Inkscape::XML::Node *menus, GtkWidget *menu, Inkscape::UI:
                     if (menu_pntr->attribute("default") != NULL) {
                         gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (item), TRUE);
                     }
+                    if (verb->get_code() != SP_VERB_NONE) {
+                       SPAction *action = verb->get_action(view);
+                       g_signal_connect( G_OBJECT(item), "expose_event", (GCallback) update_view_menu, (void *) action);
+                    }
                 } else {
                     sp_ui_menu_append_item_from_verb(GTK_MENU(menu), verb, view);
                     group = NULL;
@@ -925,7 +977,31 @@ sp_ui_build_dyn_menus(Inkscape::XML::Node *menus, GtkWidget *menu, Inkscape::UI:
             continue;
         }
         if (!strcmp(menu_pntr->name(), "recent-file-list")) {
-            sp_menu_append_recent_documents(menu, view);
+            Inkscape::Preferences *prefs = Inkscape::Preferences::get();
+
+            // create recent files menu
+            int max_recent = prefs->getInt("/options/maxrecentdocuments/value");
+            GtkWidget *recent_menu = gtk_recent_chooser_menu_new_for_manager(gtk_recent_manager_get_default());
+            gtk_recent_chooser_set_limit(GTK_RECENT_CHOOSER(recent_menu), max_recent);
+            // sort most recently used documents first to preserve previous behavior
+            gtk_recent_chooser_set_sort_type(GTK_RECENT_CHOOSER(recent_menu), GTK_RECENT_SORT_MRU);
+            g_signal_connect(G_OBJECT(recent_menu), "item-activated", G_CALLBACK(sp_recent_open), (gpointer) NULL);
+
+            // add filter to only open files added by Inkscape
+            GtkRecentFilter *inkscape_only_filter = gtk_recent_filter_new();
+            gtk_recent_filter_add_application(inkscape_only_filter, g_get_prgname());
+            gtk_recent_chooser_add_filter(GTK_RECENT_CHOOSER(recent_menu), inkscape_only_filter);
+
+            gtk_recent_chooser_set_show_tips (GTK_RECENT_CHOOSER(recent_menu), TRUE);
+            gtk_recent_chooser_set_show_not_found (GTK_RECENT_CHOOSER(recent_menu), FALSE);
+
+            GtkWidget *recent_item = gtk_menu_item_new_with_mnemonic(_("Open _Recent"));
+            gtk_menu_item_set_submenu(GTK_MENU_ITEM(recent_item), recent_menu);
+
+            gtk_menu_append(GTK_MENU(menu), GTK_WIDGET(recent_item));
+            // this will just sit and update the list's item count
+            static MaxRecentObserver *mro = new MaxRecentObserver(recent_menu);
+            prefs->addObserver(*mro);
             continue;
         }
         if (!strcmp(menu_pntr->name(), "objects-checkboxes")) {
@@ -948,13 +1024,13 @@ sp_ui_main_menubar(Inkscape::UI::View::View *view)
     GtkWidget *mbar = gtk_menu_bar_new();
 
 #ifdef GDK_WINDOWING_QUARTZ
-       ige_mac_menu_set_menu_bar(GTK_MENU_SHELL(mbar));
+    ige_mac_menu_set_menu_bar(GTK_MENU_SHELL(mbar));
 #endif
 
     sp_ui_build_dyn_menus(inkscape_get_menus(INKSCAPE), mbar, view);
 
 #ifdef GDK_WINDOWING_QUARTZ
-       return NULL;
+    return NULL;
 #else
     return mbar;
 #endif
@@ -1062,7 +1138,7 @@ sp_ui_drag_data_received(GtkWidget *widget,
             int destX = 0;
             int destY = 0;
             gtk_widget_translate_coordinates( widget, &(desktop->canvas->widget), x, y, &destX, &destY );
-            NR::Point where( sp_canvas_window_to_world( desktop->canvas, NR::Point( destX, destY ) ) );
+            Geom::Point where( sp_canvas_window_to_world( desktop->canvas, Geom::Point( destX, destY ) ) );
 
             SPItem *item = desktop->item_at_point( where, true );
             if ( item )
@@ -1121,7 +1197,7 @@ sp_ui_drag_data_received(GtkWidget *widget,
                     sp_desktop_apply_css_recursive( item, css, true );
                     item->updateRepr();
 
-                    sp_document_done( doc , SP_VERB_NONE, 
+                    sp_document_done( doc , SP_VERB_NONE,
                                       _("Drop color"));
 
                     if ( srgbProf ) {
@@ -1138,15 +1214,15 @@ sp_ui_drag_data_received(GtkWidget *widget,
             int destX = 0;
             int destY = 0;
             gtk_widget_translate_coordinates( widget, &(desktop->canvas->widget), x, y, &destX, &destY );
-            NR::Point where( sp_canvas_window_to_world( desktop->canvas, NR::Point( destX, destY ) ) );
-            NR::Point const button_dt(desktop->w2d(where));
-            NR::Point const button_doc(desktop->dt2doc(button_dt));
+            Geom::Point where( sp_canvas_window_to_world( desktop->canvas, Geom::Point( destX, destY ) ) );
+            Geom::Point const button_dt(desktop->w2d(where));
+            Geom::Point const button_doc(desktop->dt2doc(button_dt));
 
             if ( data->length == 8 ) {
-                gchar c[64] = {0};
+                gchar colorspec[64] = {0};
                 // Careful about endian issues.
                 guint16* dataVals = (guint16*)data->data;
-                sp_svg_write_color( c, 64,
+                sp_svg_write_color( colorspec, sizeof(colorspec),
                                     SP_RGBA32_U_COMPOSE(
                                         0x0ff & (dataVals[0] >> 8),
                                         0x0ff & (dataVals[1] >> 8),
@@ -1159,7 +1235,7 @@ sp_ui_drag_data_received(GtkWidget *widget,
 
                 bool consumed = false;
                 if (desktop->event_context && desktop->event_context->get_drag()) {
-                    consumed = desktop->event_context->get_drag()->dropColor(item, c, button_dt);
+                    consumed = desktop->event_context->get_drag()->dropColor(item, colorspec, button_dt);
                     if (consumed) {
                         sp_document_done( doc , SP_VERB_NONE, _("Drop color on gradient"));
                         desktop->event_context->get_drag()->updateDraggers();
@@ -1175,25 +1251,129 @@ sp_ui_drag_data_received(GtkWidget *widget,
 
                 if (!consumed && item) {
                     bool fillnotstroke = (drag_context->action != GDK_ACTION_MOVE);
-                    if (fillnotstroke && 
+                    if (fillnotstroke &&
+                        (SP_IS_SHAPE(item) || SP_IS_TEXT(item) || SP_IS_FLOWTEXT(item))) {
+                        Path *livarot_path = Path_for_item(item, true, true);
+                        livarot_path->ConvertWithBackData(0.04);
+
+                        boost::optional<Path::cut_position> position = get_nearest_position_on_Path(livarot_path, button_doc);
+                        if (position) {
+                            Geom::Point nearest = get_point_on_Path(livarot_path, position->piece, position->t);
+                            Geom::Point delta = nearest - button_doc;
+                            Inkscape::Preferences *prefs = Inkscape::Preferences::get();
+                            delta = desktop->d2w(delta);
+                            double stroke_tolerance =
+                                ( !SP_OBJECT_STYLE(item)->stroke.isNone() ?
+                                  desktop->current_zoom() *
+                                  SP_OBJECT_STYLE (item)->stroke_width.computed *
+                                  to_2geom(sp_item_i2d_affine(item)).descrim() * 0.5
+                                  : 0.0)
+                                + prefs->getIntLimited("/options/dragtolerance/value", 0, 0, 100);
+
+                            if (Geom::L2 (delta) < stroke_tolerance) {
+                                fillnotstroke = false;
+                            }
+                        }
+                        delete livarot_path;
+                    }
+
+                    SPCSSAttr *css = sp_repr_css_attr_new();
+                    sp_repr_css_set_property( css, fillnotstroke ? "fill":"stroke", colorspec );
+
+                    sp_desktop_apply_css_recursive( item, css, true );
+                    item->updateRepr();
+
+                    sp_document_done( doc , SP_VERB_NONE,
+                                      _("Drop color"));
+                }
+            }
+        }
+        break;
+
+        case APP_OSWB_COLOR:
+        {
+            bool worked = false;
+            Glib::ustring colorspec;
+            if ( data->format == 8 ) {
+                ege::PaintDef color;
+                worked = color.fromMIMEData("application/x-oswb-color",
+                                            reinterpret_cast<char*>(data->data),
+                                            data->length,
+                                            data->format);
+                if ( worked ) {
+                    if ( color.getType() == ege::PaintDef::CLEAR ) {
+                        colorspec = ""; // TODO check if this is sufficient
+                    } else if ( color.getType() == ege::PaintDef::NONE ) {
+                        colorspec = "none";
+                    } else {
+                        unsigned int r = color.getR();
+                        unsigned int g = color.getG();
+                        unsigned int b = color.getB();
+
+                        SPGradient* matches = 0;
+                        const GSList *gradients = sp_document_get_resource_list(doc, "gradient");
+                        for (const GSList *item = gradients; item; item = item->next) {
+                            SPGradient* grad = SP_GRADIENT(item->data);
+                            if ( color.descr == grad->id ) {
+                                if ( grad->has_stops ) {
+                                    matches = grad;
+                                    break;
+                                }
+                            }
+                        }
+                        if (matches) {
+                            colorspec = "url(#";
+                            colorspec += matches->id;
+                            colorspec += ")";
+                        } else {
+                            gchar* tmp = g_strdup_printf("#%02x%02x%02x", r, g, b);
+                            colorspec = tmp;
+                            g_free(tmp);
+                        }
+                    }
+                }
+            }
+            if ( worked ) {
+                int destX = 0;
+                int destY = 0;
+                gtk_widget_translate_coordinates( widget, &(desktop->canvas->widget), x, y, &destX, &destY );
+                Geom::Point where( sp_canvas_window_to_world( desktop->canvas, Geom::Point( destX, destY ) ) );
+                Geom::Point const button_dt(desktop->w2d(where));
+                Geom::Point const button_doc(desktop->dt2doc(button_dt));
+
+                SPItem *item = desktop->item_at_point( where, true );
+
+                bool consumed = false;
+                if (desktop->event_context && desktop->event_context->get_drag()) {
+                    consumed = desktop->event_context->get_drag()->dropColor(item, colorspec.c_str(), button_dt);
+                    if (consumed) {
+                        sp_document_done( doc , SP_VERB_NONE, _("Drop color on gradient"));
+                        desktop->event_context->get_drag()->updateDraggers();
+                    }
+                }
+
+                if (!consumed && item) {
+                    bool fillnotstroke = (drag_context->action != GDK_ACTION_MOVE);
+                    if (fillnotstroke &&
                         (SP_IS_SHAPE(item) || SP_IS_TEXT(item) || SP_IS_FLOWTEXT(item))) {
                         Path *livarot_path = Path_for_item(item, true, true);
                         livarot_path->ConvertWithBackData(0.04);
 
-                        NR::Maybe<Path::cut_position> position = get_nearest_position_on_Path(livarot_path, button_doc);
+                        boost::optional<Path::cut_position> position = get_nearest_position_on_Path(livarot_path, button_doc);
                         if (position) {
-                            NR::Point nearest = get_point_on_Path(livarot_path, position->piece, position->t);
-                            NR::Point delta = nearest - button_doc;
+                            Geom::Point nearest = get_point_on_Path(livarot_path, position->piece, position->t);
+                            Geom::Point delta = nearest - button_doc;
+                            Inkscape::Preferences *prefs = Inkscape::Preferences::get();
                             delta = desktop->d2w(delta);
                             double stroke_tolerance =
                                 ( !SP_OBJECT_STYLE(item)->stroke.isNone() ?
                                   desktop->current_zoom() *
                                   SP_OBJECT_STYLE (item)->stroke_width.computed *
-                                  NR::expansion(from_2geom(sp_item_i2d_affine(item))) * 0.5
+                                  to_2geom(sp_item_i2d_affine(item)).descrim() * 0.5
                                   : 0.0)
-                                + prefs_get_int_attribute_limited("options.dragtolerance", "value", 0, 0, 100); 
+                                + prefs->getIntLimited("/options/dragtolerance/value", 0, 0, 100);
 
-                            if (NR::L2 (delta) < stroke_tolerance) {
+                            if (Geom::L2 (delta) < stroke_tolerance) {
                                 fillnotstroke = false;
                             }
                         }
@@ -1201,12 +1381,12 @@ sp_ui_drag_data_received(GtkWidget *widget,
                     }
 
                     SPCSSAttr *css = sp_repr_css_attr_new();
-                    sp_repr_css_set_property( css, fillnotstroke ? "fill":"stroke", c );
+                    sp_repr_css_set_property( css, fillnotstroke ? "fill":"stroke", colorspec.c_str() );
 
                     sp_desktop_apply_css_recursive( item, css, true );
                     item->updateRepr();
 
-                    sp_document_done( doc , SP_VERB_NONE, 
+                    sp_document_done( doc , SP_VERB_NONE,
                                       _("Drop color"));
                 }
             }
@@ -1246,22 +1426,19 @@ sp_ui_drag_data_received(GtkWidget *widget,
 
             Inkscape::Selection *selection = sp_desktop_selection(desktop);
             selection->set(SP_ITEM(new_obj));
-            // To move the imported object, we must temporarily set the "transform pattern with
-            // object" option.
+
+            // move to mouse pointer
             {
-                int const saved_pref = prefs_get_int_attribute("options.transform", "pattern", 1);
-                prefs_set_int_attribute("options.transform", "pattern", 1);
                 sp_document_ensure_up_to_date(sp_desktop_document(desktop));
-                NR::Maybe<NR::Rect> sel_bbox = selection->bounds();
+                Geom::OptRect sel_bbox = selection->bounds();
                 if (sel_bbox) {
-                    NR::Point m( desktop->point() - sel_bbox->midpoint() );
-                    sp_selection_move_relative(selection, m);
+                    Geom::Point m( desktop->point() - sel_bbox->midpoint() );
+                    sp_selection_move_relative(selection, m, false);
                 }
-                prefs_set_int_attribute("options.transform", "pattern", saved_pref);
             }
 
             Inkscape::GC::release(newgroup);
-            sp_document_done(doc, SP_VERB_NONE, 
+            sp_document_done(doc, SP_VERB_NONE,
                              _("Drop SVG"));
             break;
         }
@@ -1275,27 +1452,23 @@ sp_ui_drag_data_received(GtkWidget *widget,
         case PNG_DATA:
         case JPEG_DATA:
         case IMAGE_DATA: {
-            char tmp[1024];
-
-            StringOutputStream outs;
-            Base64OutputStream b64out(outs);
-            b64out.setColumnWidth(0);
-
             Inkscape::XML::Document *xml_doc = sp_document_repr_doc(doc);
-
             Inkscape::XML::Node *newImage = xml_doc->createElement("svg:image");
+            gchar *atom_name = gdk_atom_name(data->type);
 
-            for ( int i = 0; i < data->length; i++ ) {
-                b64out.put( data->data[i] );
-            }
-            b64out.close();
+            // this formula taken from Glib docs
+            guint needed_size = data->length * 4 / 3 + data->length * 4 / (3 * 72) + 7;
+            needed_size += 5 + 8 + strlen(atom_name); // 5 bytes for data:, 8 for ;base64,
 
+            gchar *buffer = (gchar *) g_malloc(needed_size), *buf_work = buffer;
+            buf_work += g_sprintf(buffer, "data:%s;base64,", atom_name);
 
-            Glib::ustring str = outs.getString();
+            gint state = 0, save = 0;
+            g_base64_encode_step(data->data, data->length, TRUE, buf_work, &state, &save);
+            g_base64_encode_close(TRUE, buf_work, &state, &save);
 
-            snprintf( tmp, sizeof(tmp), "data:%s;base64,", gdk_atom_name(data->type) );
-            str.insert( 0, tmp );
-            newImage->setAttribute("xlink:href", str.c_str());
+            newImage->setAttribute("xlink:href", buffer);
+            g_free(buffer);
 
             GError *error = NULL;
             GdkPixbufLoader *loader = gdk_pixbuf_loader_new_with_mime_type( gdk_atom_name(data->type), &error );
@@ -1304,6 +1477,7 @@ sp_ui_drag_data_received(GtkWidget *widget,
                 if ( gdk_pixbuf_loader_write( loader, data->data, data->length, &error) ) {
                     GdkPixbuf *pbuf = gdk_pixbuf_loader_get_pixbuf(loader);
                     if ( pbuf ) {
+                        char tmp[1024];
                         int width = gdk_pixbuf_get_width(pbuf);
                         int height = gdk_pixbuf_get_height(pbuf);
                         snprintf( tmp, sizeof(tmp), "%d", width );
@@ -1314,12 +1488,13 @@ sp_ui_drag_data_received(GtkWidget *widget,
                     }
                 }
             }
+            g_free(atom_name);
 
             // Add it to the current layer
             desktop->currentLayer()->appendChildRepr(newImage);
 
             Inkscape::GC::release(newImage);
-            sp_document_done( doc , SP_VERB_NONE, 
+            sp_document_done( doc , SP_VERB_NONE,
                               _("Drop bitmap image"));
             break;
         }
@@ -1456,7 +1631,7 @@ sp_ui_menu_item_set_name(SPAction */*action*/, Glib::ustring name, void *data)
         gtk_label_set_markup_with_mnemonic(GTK_LABEL (child), name.c_str());
     } else if (GTK_IS_HBOX(child)) {
         gtk_label_set_markup_with_mnemonic(
-        GTK_LABEL (gtk_container_get_children(GTK_CONTAINER (child))->data), 
+        GTK_LABEL (gtk_container_get_children(GTK_CONTAINER (child))->data),
         name.c_str());
     }//else sp_ui_menu_append_item_from_verb has been modified and can set
     //a menu item in yet another way...