Code

Implement guide behaviour as discussed on the mailing list
[inkscape.git] / src / ui / widget / selected-style.cpp
index 8a5aa1f46b329ddfb1ced1bfd9ad9ec98ebb4ce2..e7b0188d81ae138fa505cf25e976125cd1e3cc8b 100644 (file)
 #include "sp-linear-gradient-fns.h"
 #include "sp-radial-gradient-fns.h"
 #include "sp-pattern.h"
-#include "dialogs/object-properties.h"
+#include "ui/dialog/dialog-manager.h"
+#include "ui/dialog/fill-and-stroke.h"
+#include "ui/dialog/panel-dialog.h"
 #include "xml/repr.h"
 #include "document.h"
 #include "widgets/widget-sizes.h"
 #include "widgets/spinbutton-events.h"
-#include "svg/svg.h"
+#include "widgets/gradient-image.h"
+#include "sp-gradient.h"
+#include "svg/svg-color.h"
 #include "svg/css-ostringstream.h"
 #include "helper/units.h"
+#include "event-context.h"
+#include "message-context.h"
+#include "verbs.h"
+#include "color.h"
+#include <display/sp-canvas.h>
+#include "pixmaps/cursor-adj-h.xpm"
+#include "pixmaps/cursor-adj-s.xpm"
+#include "pixmaps/cursor-adj-l.xpm"
+#include "sp-cursor.h"
 
 static gdouble const _sw_presets[]     = { 32 ,  16 ,  10 ,  8 ,  6 ,  4 ,  3 ,  2 ,  1.5 ,  1 ,  0.75 ,  0.5 ,  0.25 ,  0.1 };
-static gcharconst _sw_presets_str[] = {"32", "16", "10", "8", "6", "4", "3", "2", "1.5", "1", "0.75", "0.5", "0.25", "0.1"};
+static gchar const *const _sw_presets_str[] = {"32", "16", "10", "8", "6", "4", "3", "2", "1.5", "1", "0.75", "0.5", "0.25", "0.1"};
 
-static void 
+static void
 ss_selection_changed (Inkscape::Selection *, gpointer data)
 {
     Inkscape::UI::Widget::SelectedStyle *ss = (Inkscape::UI::Widget::SelectedStyle *) data;
@@ -47,13 +60,13 @@ ss_selection_changed (Inkscape::Selection *, gpointer data)
 }
 
 static void
-ss_selection_modified (Inkscape::Selection *selection, guint flags, gpointer data)
+ss_selection_modified( Inkscape::Selection *selection, guint /*flags*/, gpointer data )
 {
     ss_selection_changed (selection, data);
 }
 
 static void
-ss_subselection_changed (gpointer dragger, gpointer data)
+ss_subselection_changed( gpointer /*dragger*/, gpointer data )
 {
     ss_selection_changed (NULL, data);
 }
@@ -73,6 +86,12 @@ typedef enum {
     APP_X_COLOR
 } ui_drop_target_info;
 
+//TODO: warning: deprecated conversion from string constant to ‘gchar*’
+//
+//Turn out to be warnings that we should probably leave in place. The
+// pointers/types used need to be read-only. So until we correct the using
+// code, those warnings are actually desired. They say "Hey! Fix this". We
+// definitely don't want to hide/ignore them. --JonCruz
 static GtkTargetEntry ui_drop_target_entries [] = {
     {"application/x-color", 0, APP_X_COLOR}
 };
@@ -80,25 +99,32 @@ static GtkTargetEntry ui_drop_target_entries [] = {
 #define ENTRIES_SIZE(n) sizeof(n)/sizeof(n[0])
 static guint nui_drop_target_entries = ENTRIES_SIZE(ui_drop_target_entries);
 
+/* convenience function */
+static Dialog::FillAndStroke *get_fill_and_stroke_panel(SPDesktop *desktop);
+
+SelectedStyle::SelectedStyle(bool /*layout*/)
+    : 
+      current_stroke_width(0),
 
-SelectedStyle::SelectedStyle(bool layout)
-    : _desktop (NULL),
+      _desktop (NULL),
 
       _table(2, 6),
-      _fill_label (_("F:")),
-      _stroke_label (_("S:")),
+      _fill_label (_("Fill:")),
+      _stroke_label (_("Stroke:")),
       _opacity_label (_("O:")),
-      _fill_place (),
-      _stroke_place (),
+
+      _fill_place(this, SS_FILL),
+      _stroke_place(this, SS_STROKE),
 
       _fill_flag_place (),
       _stroke_flag_place (),
 
       _opacity_place (),
-      _opacity_adjustment (1.0, 0.0, 1.0, 0.01, 0.1),
-      _opacity_sb (0.02, 2),
+      _opacity_adjustment (100, 0.0, 100, 1.0, 10.0),
+      _opacity_sb (0.02, 0),
 
       _stroke (),
+      _stroke_width_place(this),
       _stroke_width (""),
 
       _opacity_blocked (false),
@@ -109,11 +135,12 @@ SelectedStyle::SelectedStyle(bool layout)
 
       _sw_unit(NULL),
 
-      _tooltips (),
+      _tooltips ()
 
-      _dropF(0),
-      _dropS(0)
 {
+    _drop[0] = _drop[1] = 0;
+    _dropEnabled[0] = _dropEnabled[1] = false;
+
     _fill_label.set_alignment(0.0, 0.5);
     _fill_label.set_padding(0, 0);
     _stroke_label.set_alignment(0.0, 0.5);
@@ -131,7 +158,7 @@ SelectedStyle::SelectedStyle(bool layout)
         _na[i].show_all();
         __na[i] = (_("Nothing selected"));
 
-        _none[i].set_markup (_("None"));
+        _none[i].set_markup (_("<i>None</i>"));
         sp_set_font_size_smaller (GTK_WIDGET(_none[i].gobj()));
         _none[i].show_all();
         __none[i] = (i == SS_FILL)? (_("No fill")) : (_("No stroke"));
@@ -141,22 +168,32 @@ SelectedStyle::SelectedStyle(bool layout)
         _pattern[i].show_all();
         __pattern[i] = (i == SS_FILL)? (_("Pattern fill")) : (_("Pattern stroke"));
 
-        _lgradient[i].set_markup (_("L Gradient"));
+        _lgradient[i].set_markup (_("<b>L</b>"));
         sp_set_font_size_smaller (GTK_WIDGET(_lgradient[i].gobj()));
         _lgradient[i].show_all();
         __lgradient[i] = (i == SS_FILL)? (_("Linear gradient fill")) : (_("Linear gradient stroke"));
 
-        _rgradient[i].set_markup (_("R Gradient"));
+        _gradient_preview_l[i] =  GTK_WIDGET(sp_gradient_image_new (NULL));
+        _gradient_box_l[i].pack_start(_lgradient[i]);
+        _gradient_box_l[i].pack_start(*(Glib::wrap(_gradient_preview_l[i])));
+        _gradient_box_l[i].show_all();
+
+        _rgradient[i].set_markup (_("<b>R</b>"));
         sp_set_font_size_smaller (GTK_WIDGET(_rgradient[i].gobj()));
         _rgradient[i].show_all();
         __rgradient[i] = (i == SS_FILL)? (_("Radial gradient fill")) : (_("Radial gradient stroke"));
 
+        _gradient_preview_r[i] = GTK_WIDGET(sp_gradient_image_new (NULL));
+        _gradient_box_r[i].pack_start(_rgradient[i]);
+        _gradient_box_r[i].pack_start(*(Glib::wrap(_gradient_preview_r[i])));
+        _gradient_box_r[i].show_all();
+
         _many[i].set_markup (_("Different"));
         sp_set_font_size_smaller (GTK_WIDGET(_many[i].gobj()));
         _many[i].show_all();
         __many[i] = (i == SS_FILL)? (_("Different fills")) : (_("Different strokes"));
 
-        _unset[i].set_markup (_("Unset"));
+        _unset[i].set_markup (_("<b>Unset</b>"));
         sp_set_font_size_smaller (GTK_WIDGET(_unset[i].gobj()));
         _unset[i].show_all();
         __unset[i] = (i == SS_FILL)? (_("Unset fill")) : (_("Unset stroke"));
@@ -177,52 +214,52 @@ SelectedStyle::SelectedStyle(bool layout)
         __multiple[i] = (i == SS_FILL)? (_("Multiple selected objects have the same fill")) : (_("Multiple selected objects have the same stroke"));
 
         _popup_edit[i].add(*(new Gtk::Label((i == SS_FILL)? _("Edit fill...") : _("Edit stroke..."), 0.0, 0.5)));
-        _popup_edit[i].signal_activate().connect(sigc::mem_fun(*this, 
+        _popup_edit[i].signal_activate().connect(sigc::mem_fun(*this,
                                (i == SS_FILL)? &SelectedStyle::on_fill_edit : &SelectedStyle::on_stroke_edit ));
 
         _popup_lastused[i].add(*(new Gtk::Label(_("Last set color"), 0.0, 0.5)));
-        _popup_lastused[i].signal_activate().connect(sigc::mem_fun(*this, 
+        _popup_lastused[i].signal_activate().connect(sigc::mem_fun(*this,
                                (i == SS_FILL)? &SelectedStyle::on_fill_lastused : &SelectedStyle::on_stroke_lastused ));
 
         _popup_lastselected[i].add(*(new Gtk::Label(_("Last selected color"), 0.0, 0.5)));
-        _popup_lastselected[i].signal_activate().connect(sigc::mem_fun(*this, 
+        _popup_lastselected[i].signal_activate().connect(sigc::mem_fun(*this,
                                (i == SS_FILL)? &SelectedStyle::on_fill_lastselected : &SelectedStyle::on_stroke_lastselected ));
 
         _popup_invert[i].add(*(new Gtk::Label(_("Invert"), 0.0, 0.5)));
-        _popup_invert[i].signal_activate().connect(sigc::mem_fun(*this, 
+        _popup_invert[i].signal_activate().connect(sigc::mem_fun(*this,
                                (i == SS_FILL)? &SelectedStyle::on_fill_invert : &SelectedStyle::on_stroke_invert ));
 
         _popup_white[i].add(*(new Gtk::Label(_("White"), 0.0, 0.5)));
-        _popup_white[i].signal_activate().connect(sigc::mem_fun(*this, 
+        _popup_white[i].signal_activate().connect(sigc::mem_fun(*this,
                                (i == SS_FILL)? &SelectedStyle::on_fill_white : &SelectedStyle::on_stroke_white ));
 
         _popup_black[i].add(*(new Gtk::Label(_("Black"), 0.0, 0.5)));
-        _popup_black[i].signal_activate().connect(sigc::mem_fun(*this, 
+        _popup_black[i].signal_activate().connect(sigc::mem_fun(*this,
                                (i == SS_FILL)? &SelectedStyle::on_fill_black : &SelectedStyle::on_stroke_black ));
 
         _popup_copy[i].add(*(new Gtk::Label(_("Copy color"), 0.0, 0.5)));
-        _popup_copy[i].signal_activate().connect(sigc::mem_fun(*this, 
+        _popup_copy[i].signal_activate().connect(sigc::mem_fun(*this,
                                (i == SS_FILL)? &SelectedStyle::on_fill_copy : &SelectedStyle::on_stroke_copy ));
 
         _popup_paste[i].add(*(new Gtk::Label(_("Paste color"), 0.0, 0.5)));
-        _popup_paste[i].signal_activate().connect(sigc::mem_fun(*this, 
+        _popup_paste[i].signal_activate().connect(sigc::mem_fun(*this,
                                (i == SS_FILL)? &SelectedStyle::on_fill_paste : &SelectedStyle::on_stroke_paste ));
 
         _popup_swap[i].add(*(new Gtk::Label(_("Swap fill and stroke"), 0.0, 0.5)));
-        _popup_swap[i].signal_activate().connect(sigc::mem_fun(*this, 
+        _popup_swap[i].signal_activate().connect(sigc::mem_fun(*this,
                                &SelectedStyle::on_fillstroke_swap));
 
         _popup_opaque[i].add(*(new Gtk::Label((i == SS_FILL)? _("Make fill opaque") : _("Make stroke opaque"), 0.0, 0.5)));
-        _popup_opaque[i].signal_activate().connect(sigc::mem_fun(*this, 
+        _popup_opaque[i].signal_activate().connect(sigc::mem_fun(*this,
                                (i == SS_FILL)? &SelectedStyle::on_fill_opaque : &SelectedStyle::on_stroke_opaque ));
 
         //TRANSLATORS COMMENT: unset is a verb here
         _popup_unset[i].add(*(new Gtk::Label((i == SS_FILL)? _("Unset fill") : _("Unset stroke"), 0.0, 0.5)));
-        _popup_unset[i].signal_activate().connect(sigc::mem_fun(*this, 
+        _popup_unset[i].signal_activate().connect(sigc::mem_fun(*this,
                                (i == SS_FILL)? &SelectedStyle::on_fill_unset : &SelectedStyle::on_stroke_unset ));
 
         _popup_remove[i].add(*(new Gtk::Label((i == SS_FILL)? _("Remove fill") : _("Remove stroke"), 0.0, 0.5)));
-        _popup_remove[i].signal_activate().connect(sigc::mem_fun(*this, 
+        _popup_remove[i].signal_activate().connect(sigc::mem_fun(*this,
                                (i == SS_FILL)? &SelectedStyle::on_fill_remove : &SelectedStyle::on_stroke_remove ));
 
         _popup[i].attach(_popup_edit[i], 0,1, 0,1);
@@ -239,7 +276,7 @@ SelectedStyle::SelectedStyle(bool layout)
         _popup_copy[i].set_sensitive(false);
         _popup[i].attach(_popup_paste[i], 0,1, 11,12);
         _popup[i].attach(_popup_swap[i], 0,1, 12,13);
-          _popup[i].attach(*(new Gtk::SeparatorMenuItem()), 0,1, 13,14); 
+          _popup[i].attach(*(new Gtk::SeparatorMenuItem()), 0,1, 13,14);
         _popup[i].attach(_popup_opaque[i], 0,1, 14,15);
         _popup[i].attach(_popup_unset[i], 0,1, 15,16);
         _popup[i].attach(_popup_remove[i], 0,1, 16,17);
@@ -281,8 +318,8 @@ SelectedStyle::SelectedStyle(bool layout)
         _popup_sw.show_all();
     }
 
-    _fill_place.signal_button_press_event().connect(sigc::mem_fun(*this, &SelectedStyle::on_fill_click));
-    _stroke_place.signal_button_press_event().connect(sigc::mem_fun(*this, &SelectedStyle::on_stroke_click));
+    _fill_place.signal_button_release_event().connect(sigc::mem_fun(*this, &SelectedStyle::on_fill_click));
+    _stroke_place.signal_button_release_event().connect(sigc::mem_fun(*this, &SelectedStyle::on_stroke_click));
     _opacity_place.signal_button_press_event().connect(sigc::mem_fun(*this, &SelectedStyle::on_opacity_click));
     _stroke_width_place.signal_button_press_event().connect(sigc::mem_fun(*this, &SelectedStyle::on_sw_click));
 
@@ -304,8 +341,8 @@ SelectedStyle::SelectedStyle(bool layout)
     _opacity_sb.set_size_request (SELECTED_STYLE_SB_WIDTH, -1);
     _opacity_sb.set_sensitive (false);
 
-    _table.attach(_fill_label, 0,1, 0,1, Gtk::SHRINK, Gtk::SHRINK);
-    _table.attach(_stroke_label, 0,1, 1,2, Gtk::SHRINK, Gtk::SHRINK);
+    _table.attach(_fill_label, 0,1, 0,1, Gtk::FILL, Gtk::SHRINK);
+    _table.attach(_stroke_label, 0,1, 1,2, Gtk::FILL, Gtk::SHRINK);
 
     _table.attach(_fill_flag_place, 1,2, 0,1, Gtk::SHRINK, Gtk::SHRINK);
     _table.attach(_stroke_flag_place, 1,2, 1,2, Gtk::SHRINK, Gtk::SHRINK);
@@ -331,35 +368,23 @@ SelectedStyle::SelectedStyle(bool layout)
     sp_set_font_size_smaller (GTK_WIDGET(_fill_label.gobj()));
     sp_set_font_size_smaller (GTK_WIDGET(_stroke_label.gobj()));
 
-    _dropF = new DropTracker();
-    ((DropTracker*)_dropF)->parent = this;
-    ((DropTracker*)_dropF)->item = SS_FILL;
+    _drop[SS_FILL] = new DropTracker();
+    ((DropTracker*)_drop[SS_FILL])->parent = this;
+    ((DropTracker*)_drop[SS_FILL])->item = SS_FILL;
 
-    _dropS = new DropTracker();
-    ((DropTracker*)_dropS)->parent = this;
-    ((DropTracker*)_dropS)->item = SS_STROKE;
+    _drop[SS_STROKE] = new DropTracker();
+    ((DropTracker*)_drop[SS_STROKE])->parent = this;
+    ((DropTracker*)_drop[SS_STROKE])->item = SS_STROKE;
 
-    {
-        gtk_drag_dest_set(GTK_WIDGET(_stroke_place.gobj()),
-                          GTK_DEST_DEFAULT_ALL,
-                          ui_drop_target_entries,
-                          nui_drop_target_entries,
-                          GdkDragAction(GDK_ACTION_COPY | GDK_ACTION_MOVE));
-        g_signal_connect(_stroke_place.gobj(),
-                         "drag_data_received",
-                         G_CALLBACK(dragDataReceived),
-                         _dropS);
-
-        gtk_drag_dest_set(GTK_WIDGET(_fill_place.gobj()),
-                          GTK_DEST_DEFAULT_ALL,
-                          ui_drop_target_entries,
-                          nui_drop_target_entries,
-                          GdkDragAction(GDK_ACTION_COPY | GDK_ACTION_MOVE));
-        g_signal_connect(_fill_place.gobj(),
-                         "drag_data_received",
-                         G_CALLBACK(dragDataReceived),
-                         _dropF);
-    }
+    g_signal_connect(_stroke_place.gobj(),
+                     "drag_data_received",
+                     G_CALLBACK(dragDataReceived),
+                     _drop[SS_STROKE]);
+
+    g_signal_connect(_fill_place.gobj(),
+                     "drag_data_received",
+                     G_CALLBACK(dragDataReceived),
+                     _drop[SS_FILL]);
 }
 
 SelectedStyle::~SelectedStyle()
@@ -373,10 +398,13 @@ SelectedStyle::~SelectedStyle()
 
     for (int i = SS_FILL; i <= SS_STROKE; i++) {
         delete _color_preview[i];
+        // FIXME: do we need this? the destroy methods are not exported
+        //sp_gradient_image_destroy(GTK_OBJECT(_gradient_preview_l[i]));
+        //sp_gradient_image_destroy(GTK_OBJECT(_gradient_preview_r[i]));
     }
 
-    delete (DropTracker*)_dropF;
-    delete (DropTracker*)_dropS;
+    delete (DropTracker*)_drop[SS_FILL];
+    delete (DropTracker*)_drop[SS_STROKE];
 }
 
 void
@@ -385,7 +413,7 @@ SelectedStyle::setDesktop(SPDesktop *desktop)
     _desktop = desktop;
     gtk_object_set_data (GTK_OBJECT(_opacity_sb.gobj()), "dtw", _desktop->canvas);
 
-    Inkscape::Selection *selection = SP_DT_SELECTION (desktop);
+    Inkscape::Selection *selection = sp_desktop_selection (desktop);
 
     selection_changed_connection = new sigc::connection (selection->connectChanged(
         sigc::bind (
@@ -403,15 +431,15 @@ SelectedStyle::setDesktop(SPDesktop *desktop)
             this )
     ));
 
-    //_sw_unit = (SPUnit *) SP_DT_NAMEDVIEW(desktop)->doc_units;
+    //_sw_unit = (SPUnit *) sp_desktop_namedview(desktop)->doc_units;
 }
 
-void SelectedStyle::dragDataReceived( GtkWidget *widget,
-                                      GdkDragContext *drag_context,
-                                      gint x, gint y,
+void SelectedStyle::dragDataReceived( GtkWidget */*widget*/,
+                                      GdkDragContext */*drag_context*/,
+                                      gint /*x*/, gint /*y*/,
                                       GtkSelectionData *data,
-                                      guint info,
-                                      guint event_time,
+                                      guint /*info*/,
+                                      guint /*event_time*/,
                                       gpointer user_data )
 {
     DropTracker* tracker = (DropTracker*)user_data;
@@ -424,7 +452,7 @@ void SelectedStyle::dragDataReceived( GtkWidget *widget,
                 gchar c[64];
                 // Careful about endian issues.
                 guint16* dataVals = (guint16*)data->data;
-                sp_svg_write_color( c, 64,
+                sp_svg_write_color( c, sizeof(c),
                                     SP_RGBA32_U_COMPOSE(
                                         0x0ff & (dataVals[0] >> 8),
                                         0x0ff & (dataVals[1] >> 8),
@@ -436,7 +464,8 @@ void SelectedStyle::dragDataReceived( GtkWidget *widget,
                 sp_repr_css_set_property( css, (tracker->item == SS_FILL) ? "fill":"stroke", c );
                 sp_desktop_set_style( tracker->parent->_desktop, css );
                 sp_repr_css_attr_unref( css );
-                sp_document_done( SP_DT_DOCUMENT(tracker->parent->_desktop) );
+                sp_document_done( sp_desktop_document(tracker->parent->_desktop) , SP_VERB_NONE,
+                                  _("Drop color"));
             }
         }
         break;
@@ -446,33 +475,44 @@ void SelectedStyle::dragDataReceived( GtkWidget *widget,
 void SelectedStyle::on_fill_remove() {
     SPCSSAttr *css = sp_repr_css_attr_new ();
     sp_repr_css_set_property (css, "fill", "none");
-    sp_desktop_set_style (_desktop, css, true, false); // do not write to current, to preserve current color
+    sp_desktop_set_style (_desktop, css, true, true);
     sp_repr_css_attr_unref (css);
-    sp_document_done (SP_DT_DOCUMENT(_desktop));
+    sp_document_done (sp_desktop_document(_desktop), SP_VERB_DIALOG_FILL_STROKE,
+                      _("Remove fill"));
 }
 
 void SelectedStyle::on_stroke_remove() {
     SPCSSAttr *css = sp_repr_css_attr_new ();
     sp_repr_css_set_property (css, "stroke", "none");
-    sp_desktop_set_style (_desktop, css, true, false); // do not write to current, to preserve current color
+    sp_desktop_set_style (_desktop, css, true, true);
     sp_repr_css_attr_unref (css);
-    sp_document_done (SP_DT_DOCUMENT(_desktop));
+    sp_document_done (sp_desktop_document(_desktop), SP_VERB_DIALOG_FILL_STROKE,
+                      _("Remove stroke"));
 }
 
 void SelectedStyle::on_fill_unset() {
     SPCSSAttr *css = sp_repr_css_attr_new ();
     sp_repr_css_unset_property (css, "fill");
-    sp_desktop_set_style (_desktop, css, true, false); // do not write to current, to preserve current color
+    sp_desktop_set_style (_desktop, css, true, true);
     sp_repr_css_attr_unref (css);
-    sp_document_done (SP_DT_DOCUMENT(_desktop));
+    sp_document_done (sp_desktop_document(_desktop), SP_VERB_DIALOG_FILL_STROKE,
+                      _("Unset fill"));
 }
 
 void SelectedStyle::on_stroke_unset() {
     SPCSSAttr *css = sp_repr_css_attr_new ();
     sp_repr_css_unset_property (css, "stroke");
-    sp_desktop_set_style (_desktop, css, true, false); // do not write to current, to preserve current color
+    sp_repr_css_unset_property (css, "stroke-opacity");
+    sp_repr_css_unset_property (css, "stroke-width");
+    sp_repr_css_unset_property (css, "stroke-miterlimit");
+    sp_repr_css_unset_property (css, "stroke-linejoin");
+    sp_repr_css_unset_property (css, "stroke-linecap");
+    sp_repr_css_unset_property (css, "stroke-dashoffset");
+    sp_repr_css_unset_property (css, "stroke-dasharray");
+    sp_desktop_set_style (_desktop, css, true, true);
     sp_repr_css_attr_unref (css);
-    sp_document_done (SP_DT_DOCUMENT(_desktop));
+    sp_document_done (sp_desktop_document(_desktop), SP_VERB_DIALOG_FILL_STROKE,
+                      _("Unset stroke"));
 }
 
 void SelectedStyle::on_fill_opaque() {
@@ -480,7 +520,8 @@ void SelectedStyle::on_fill_opaque() {
     sp_repr_css_set_property (css, "fill-opacity", "1");
     sp_desktop_set_style (_desktop, css, true);
     sp_repr_css_attr_unref (css);
-    sp_document_done (SP_DT_DOCUMENT(_desktop));
+    sp_document_done (sp_desktop_document(_desktop), SP_VERB_DIALOG_FILL_STROKE,
+                      _("Make fill opaque"));
 }
 
 void SelectedStyle::on_stroke_opaque() {
@@ -488,49 +529,54 @@ void SelectedStyle::on_stroke_opaque() {
     sp_repr_css_set_property (css, "stroke-opacity", "1");
     sp_desktop_set_style (_desktop, css, true);
     sp_repr_css_attr_unref (css);
-    sp_document_done (SP_DT_DOCUMENT(_desktop));
+    sp_document_done (sp_desktop_document(_desktop), SP_VERB_DIALOG_FILL_STROKE,
+                      _("Make fill opaque"));
 }
 
 void SelectedStyle::on_fill_lastused() {
     SPCSSAttr *css = sp_repr_css_attr_new ();
     guint32 color = sp_desktop_get_color(_desktop, true);
     gchar c[64];
-    sp_svg_write_color (c, 64, color);
+    sp_svg_write_color (c, sizeof(c), color);
     sp_repr_css_set_property (css, "fill", c);
     sp_desktop_set_style (_desktop, css);
     sp_repr_css_attr_unref (css);
-    sp_document_done (SP_DT_DOCUMENT(_desktop));
+    sp_document_done (sp_desktop_document(_desktop), SP_VERB_DIALOG_FILL_STROKE,
+                      _("Apply last set color to fill"));
 }
 
 void SelectedStyle::on_stroke_lastused() {
     SPCSSAttr *css = sp_repr_css_attr_new ();
     guint32 color = sp_desktop_get_color(_desktop, false);
     gchar c[64];
-    sp_svg_write_color (c, 64, color);
+    sp_svg_write_color (c, sizeof(c), color);
     sp_repr_css_set_property (css, "stroke", c);
     sp_desktop_set_style (_desktop, css);
     sp_repr_css_attr_unref (css);
-    sp_document_done (SP_DT_DOCUMENT(_desktop));
+    sp_document_done (sp_desktop_document(_desktop), SP_VERB_DIALOG_FILL_STROKE,
+                      _("Apply last set color to stroke"));
 }
 
 void SelectedStyle::on_fill_lastselected() {
     SPCSSAttr *css = sp_repr_css_attr_new ();
     gchar c[64];
-    sp_svg_write_color (c, 64, _lastselected[SS_FILL]);
+    sp_svg_write_color (c, sizeof(c), _lastselected[SS_FILL]);
     sp_repr_css_set_property (css, "fill", c);
     sp_desktop_set_style (_desktop, css);
     sp_repr_css_attr_unref (css);
-    sp_document_done (SP_DT_DOCUMENT(_desktop));
+    sp_document_done (sp_desktop_document(_desktop), SP_VERB_DIALOG_FILL_STROKE,
+                      _("Apply last selected color to fill"));
 }
 
 void SelectedStyle::on_stroke_lastselected() {
     SPCSSAttr *css = sp_repr_css_attr_new ();
     gchar c[64];
-    sp_svg_write_color (c, 64, _lastselected[SS_STROKE]);
+    sp_svg_write_color (c, sizeof(c), _lastselected[SS_STROKE]);
     sp_repr_css_set_property (css, "stroke", c);
     sp_desktop_set_style (_desktop, css);
     sp_repr_css_attr_unref (css);
-    sp_document_done (SP_DT_DOCUMENT(_desktop));
+    sp_document_done (sp_desktop_document(_desktop), SP_VERB_DIALOG_FILL_STROKE,
+                      _("Apply last selected color to stroke"));
 }
 
 void SelectedStyle::on_fill_invert() {
@@ -538,7 +584,7 @@ void SelectedStyle::on_fill_invert() {
     guint32 color = _thisselected[SS_FILL];
     gchar c[64];
     if (_mode[SS_FILL] != SS_COLOR) return;
-    sp_svg_write_color (c, 64,
+    sp_svg_write_color (c, sizeof(c),
         SP_RGBA32_U_COMPOSE(
                 (255 - SP_RGBA32_R_U(color)),
                 (255 - SP_RGBA32_G_U(color)),
@@ -549,7 +595,8 @@ void SelectedStyle::on_fill_invert() {
     sp_repr_css_set_property (css, "fill", c);
     sp_desktop_set_style (_desktop, css);
     sp_repr_css_attr_unref (css);
-    sp_document_done (SP_DT_DOCUMENT(_desktop));
+    sp_document_done (sp_desktop_document(_desktop), SP_VERB_DIALOG_FILL_STROKE,
+                      _("Invert fill"));
 }
 
 void SelectedStyle::on_stroke_invert() {
@@ -557,7 +604,7 @@ void SelectedStyle::on_stroke_invert() {
     guint32 color = _thisselected[SS_STROKE];
     gchar c[64];
     if (_mode[SS_STROKE] != SS_COLOR) return;
-    sp_svg_write_color (c, 64,
+    sp_svg_write_color (c, sizeof(c),
         SP_RGBA32_U_COMPOSE(
                 (255 - SP_RGBA32_R_U(color)),
                 (255 - SP_RGBA32_G_U(color)),
@@ -568,57 +615,62 @@ void SelectedStyle::on_stroke_invert() {
     sp_repr_css_set_property (css, "stroke", c);
     sp_desktop_set_style (_desktop, css);
     sp_repr_css_attr_unref (css);
-    sp_document_done (SP_DT_DOCUMENT(_desktop));
-} 
+    sp_document_done (sp_desktop_document(_desktop), SP_VERB_DIALOG_FILL_STROKE,
+                      _("Invert stroke"));
+}
 
 void SelectedStyle::on_fill_white() {
     SPCSSAttr *css = sp_repr_css_attr_new ();
     gchar c[64];
-    sp_svg_write_color (c, 64, 0xffffffff);
+    sp_svg_write_color (c, sizeof(c), 0xffffffff);
     sp_repr_css_set_property (css, "fill", c);
     sp_repr_css_set_property (css, "fill-opacity", "1");
     sp_desktop_set_style (_desktop, css);
     sp_repr_css_attr_unref (css);
-    sp_document_done (SP_DT_DOCUMENT(_desktop));
+    sp_document_done (sp_desktop_document(_desktop), SP_VERB_DIALOG_FILL_STROKE,
+                      _("White fill"));
 }
 
 void SelectedStyle::on_stroke_white() {
     SPCSSAttr *css = sp_repr_css_attr_new ();
     gchar c[64];
-    sp_svg_write_color (c, 64, 0xffffffff);
+    sp_svg_write_color (c, sizeof(c), 0xffffffff);
     sp_repr_css_set_property (css, "stroke", c);
     sp_repr_css_set_property (css, "stroke-opacity", "1");
     sp_desktop_set_style (_desktop, css);
     sp_repr_css_attr_unref (css);
-    sp_document_done (SP_DT_DOCUMENT(_desktop));
+    sp_document_done (sp_desktop_document(_desktop), SP_VERB_DIALOG_FILL_STROKE,
+                      _("White stroke"));
 }
 
 void SelectedStyle::on_fill_black() {
     SPCSSAttr *css = sp_repr_css_attr_new ();
     gchar c[64];
-    sp_svg_write_color (c, 64, 0x000000ff);
+    sp_svg_write_color (c, sizeof(c), 0x000000ff);
     sp_repr_css_set_property (css, "fill", c);
     sp_repr_css_set_property (css, "fill-opacity", "1.0");
     sp_desktop_set_style (_desktop, css);
     sp_repr_css_attr_unref (css);
-    sp_document_done (SP_DT_DOCUMENT(_desktop));
+    sp_document_done (sp_desktop_document(_desktop), SP_VERB_DIALOG_FILL_STROKE,
+                      _("Black fill"));
 }
 
 void SelectedStyle::on_stroke_black() {
     SPCSSAttr *css = sp_repr_css_attr_new ();
     gchar c[64];
-    sp_svg_write_color (c, 64, 0x000000ff);
+    sp_svg_write_color (c, sizeof(c), 0x000000ff);
     sp_repr_css_set_property (css, "stroke", c);
     sp_repr_css_set_property (css, "stroke-opacity", "1.0");
     sp_desktop_set_style (_desktop, css);
     sp_repr_css_attr_unref (css);
-    sp_document_done (SP_DT_DOCUMENT(_desktop));
+    sp_document_done (sp_desktop_document(_desktop), SP_VERB_DIALOG_FILL_STROKE,
+                      _("Black stroke"));
 }
 
 void SelectedStyle::on_fill_copy() {
     if (_mode[SS_FILL] == SS_COLOR) {
         gchar c[64];
-        sp_svg_write_color (c, 64, _thisselected[SS_FILL]);
+        sp_svg_write_color (c, sizeof(c), _thisselected[SS_FILL]);
         Glib::ustring text;
         text += c;
         if (!text.empty()) {
@@ -631,7 +683,7 @@ void SelectedStyle::on_fill_copy() {
 void SelectedStyle::on_stroke_copy() {
     if (_mode[SS_STROKE] == SS_COLOR) {
         gchar c[64];
-        sp_svg_write_color (c, 64, _thisselected[SS_STROKE]);
+        sp_svg_write_color (c, sizeof(c), _thisselected[SS_STROKE]);
         Glib::ustring text;
         text += c;
         if (!text.empty()) {
@@ -654,7 +706,8 @@ void SelectedStyle::on_fill_paste() {
         sp_repr_css_set_property (css, "fill", text.c_str());
         sp_desktop_set_style (_desktop, css);
         sp_repr_css_attr_unref (css);
-        sp_document_done (SP_DT_DOCUMENT(_desktop));
+        sp_document_done (sp_desktop_document(_desktop), SP_VERB_DIALOG_FILL_STROKE,
+                      _("Paste fill"));
     }
 }
 
@@ -671,7 +724,8 @@ void SelectedStyle::on_stroke_paste() {
         sp_repr_css_set_property (css, "stroke", text.c_str());
         sp_desktop_set_style (_desktop, css);
         sp_repr_css_attr_unref (css);
-        sp_document_done (SP_DT_DOCUMENT(_desktop));
+        sp_document_done (sp_desktop_document(_desktop), SP_VERB_DIALOG_FILL_STROKE,
+                      _("Paste stroke"));
     }
 }
 
@@ -690,7 +744,7 @@ void SelectedStyle::on_fillstroke_swap() {
         break;
     case SS_COLOR:
         gchar c[64];
-        sp_svg_write_color (c, 64, _thisselected[SS_FILL]);
+        sp_svg_write_color (c, sizeof(c), _thisselected[SS_FILL]);
         sp_repr_css_set_property (css, "stroke", c);
         break;
     case SS_LGRADIENT:
@@ -712,7 +766,7 @@ void SelectedStyle::on_fillstroke_swap() {
         break;
     case SS_COLOR:
         gchar c[64];
-        sp_svg_write_color (c, 64, _thisselected[SS_STROKE]);
+        sp_svg_write_color (c, sizeof(c), _thisselected[SS_STROKE]);
         sp_repr_css_set_property (css, "fill", c);
         break;
     case SS_LGRADIENT:
@@ -724,22 +778,28 @@ void SelectedStyle::on_fillstroke_swap() {
 
     sp_desktop_set_style (_desktop, css);
     sp_repr_css_attr_unref (css);
-    sp_document_done (SP_DT_DOCUMENT(_desktop));
+    sp_document_done (sp_desktop_document(_desktop), SP_VERB_DIALOG_FILL_STROKE,
+                      _("Swap fill and stroke"));
 }
 
 void SelectedStyle::on_fill_edit() {
-    sp_object_properties_fill();
+    if (Dialog::FillAndStroke *fs = get_fill_and_stroke_panel(_desktop))
+        fs->showPageFill();
 }
 
 void SelectedStyle::on_stroke_edit() {
-    sp_object_properties_stroke();
+    if (Dialog::FillAndStroke *fs = get_fill_and_stroke_panel(_desktop))
+        fs->showPageStrokePaint();
 }
 
-bool 
+bool
 SelectedStyle::on_fill_click(GdkEventButton *event)
 {
     if (event->button == 1) { // click, open fill&stroke
-        sp_object_properties_fill();
+
+        if (Dialog::FillAndStroke *fs = get_fill_and_stroke_panel(_desktop))
+            fs->showPageFill();
+
     } else if (event->button == 3) { // right-click, popup menu
         _popup[SS_FILL].popup(event->button, event->time);
     } else if (event->button == 2) { // middle click, toggle none/lastcolor
@@ -752,11 +812,12 @@ SelectedStyle::on_fill_click(GdkEventButton *event)
     return true;
 }
 
-bool 
+bool
 SelectedStyle::on_stroke_click(GdkEventButton *event)
 {
     if (event->button == 1) { // click, open fill&stroke
-        sp_object_properties_stroke();
+        if (Dialog::FillAndStroke *fs = get_fill_and_stroke_panel(_desktop))
+            fs->showPageStrokePaint();
     } else if (event->button == 3) { // right-click, popup menu
         _popup[SS_STROKE].popup(event->button, event->time);
     } else if (event->button == 2) { // middle click, toggle none/lastcolor
@@ -769,11 +830,12 @@ SelectedStyle::on_stroke_click(GdkEventButton *event)
     return true;
 }
 
-bool 
+bool
 SelectedStyle::on_sw_click(GdkEventButton *event)
 {
     if (event->button == 1) { // click, open fill&stroke
-        sp_object_properties_stroke_style ();
+        if (Dialog::FillAndStroke *fs = get_fill_and_stroke_panel(_desktop))
+            fs->showPageStrokeStyle();
     } else if (event->button == 3) { // right-click, popup menu
         _popup_sw.popup(event->button, event->time);
     } else if (event->button == 2) { // middle click, toggle none/lastwidth?
@@ -782,16 +844,17 @@ SelectedStyle::on_sw_click(GdkEventButton *event)
     return true;
 }
 
-bool 
+bool
 SelectedStyle::on_opacity_click(GdkEventButton *event)
 {
     if (event->button == 2) { // middle click
-        const char* opacity = _opacity_sb.get_value() < 0.5? "0.5" : (_opacity_sb.get_value() == 1? "0" : "1");
+        const char* opacity = _opacity_sb.get_value() < 50? "0.5" : (_opacity_sb.get_value() == 100? "0" : "1");
         SPCSSAttr *css = sp_repr_css_attr_new ();
         sp_repr_css_set_property (css, "opacity", opacity);
         sp_desktop_set_style (_desktop, css);
         sp_repr_css_attr_unref (css);
-        sp_document_done (SP_DT_DOCUMENT (_desktop));
+        sp_document_done (sp_desktop_document (_desktop), SP_VERB_DIALOG_FILL_STROKE,
+                      _("Change opacity"));
         return true;
     }
 
@@ -822,9 +885,11 @@ void SelectedStyle::on_popup_preset(int i) {
     Inkscape::CSSOStringStream os;
     os << w;
     sp_repr_css_set_property (css, "stroke-width", os.str().c_str());
+    // FIXME: update dash patterns!
     sp_desktop_set_style (_desktop, css, true);
     sp_repr_css_attr_unref (css);
-    sp_document_done (SP_DT_DOCUMENT(_desktop));
+    sp_document_done (sp_desktop_document(_desktop), SP_VERB_DIALOG_SWATCHES,
+                      _("Change stroke width"));
 }
 
 void
@@ -834,7 +899,7 @@ SelectedStyle::update()
         return;
 
     // create temporary style
-    SPStyle *query = sp_style_new ();
+    SPStyle *query = sp_style_new (sp_desktop_document(_desktop));
 
     for (int i = SS_FILL; i <= SS_STROKE; i++) {
         Gtk::EventBox *place = (i == SS_FILL)? &_fill_place : &_stroke_place;
@@ -852,25 +917,65 @@ SelectedStyle::update()
         _popup_copy[i].set_sensitive(false);
 
         // query style from desktop. This returns a result flag and fills query with the style of subselection, if any, or selection
-        int result = sp_desktop_query_style (_desktop, query, 
+        int result = sp_desktop_query_style (_desktop, query,
                                              (i == SS_FILL)? QUERY_STYLE_PROPERTY_FILL : QUERY_STYLE_PROPERTY_STROKE);
         switch (result) {
         case QUERY_STYLE_NOTHING:
             place->add(_na[i]);
             _tooltips.set_tip(*place, __na[i]);
             _mode[i] = SS_NA;
+            if ( _dropEnabled[i] ) {
+                gtk_drag_dest_unset( GTK_WIDGET((i==SS_FILL) ? _fill_place.gobj():_stroke_place.gobj()) );
+                _dropEnabled[i] = false;
+            }
             break;
         case QUERY_STYLE_SINGLE:
         case QUERY_STYLE_MULTIPLE_AVERAGED:
-        case QUERY_STYLE_MULTIPLE_SAME: 
+        case QUERY_STYLE_MULTIPLE_SAME:
+            if ( !_dropEnabled[i] ) {
+                gtk_drag_dest_set( GTK_WIDGET( (i==SS_FILL) ? _fill_place.gobj():_stroke_place.gobj()),
+                                   GTK_DEST_DEFAULT_ALL,
+                                   ui_drop_target_entries,
+                                   nui_drop_target_entries,
+                                   GdkDragAction(GDK_ACTION_COPY | GDK_ACTION_MOVE) );
+                _dropEnabled[i] = true;
+            }
             SPIPaint *paint;
             if (i == SS_FILL) {
                 paint = &(query->fill);
             } else {
                 paint = &(query->stroke);
             }
-            if (paint->set && paint->type == SP_PAINT_TYPE_COLOR) {
-                guint32 color = sp_color_get_rgba32_falpha (&(paint->value.color), 
+            if (paint->set && paint->isPaintserver()) {
+                SPPaintServer *server = (i == SS_FILL)? SP_STYLE_FILL_SERVER (query) : SP_STYLE_STROKE_SERVER (query);
+                if ( server ) {
+                    Inkscape::XML::Node *srepr = SP_OBJECT_REPR(server);
+                    _paintserver_id[i] += "url(#";
+                    _paintserver_id[i] += srepr->attribute("id");
+                    _paintserver_id[i] += ")";
+
+                    if (SP_IS_LINEARGRADIENT (server)) {
+                        SPGradient *vector = sp_gradient_get_vector(SP_GRADIENT(server), false);
+                        sp_gradient_image_set_gradient ((SPGradientImage *) _gradient_preview_l[i], vector);
+                        place->add(_gradient_box_l[i]);
+                        _tooltips.set_tip(*place, __lgradient[i]);
+                        _mode[i] = SS_LGRADIENT;
+                    } else if (SP_IS_RADIALGRADIENT (server)) {
+                        SPGradient *vector = sp_gradient_get_vector(SP_GRADIENT(server), false);
+                        sp_gradient_image_set_gradient ((SPGradientImage *) _gradient_preview_r[i], vector);
+                        place->add(_gradient_box_r[i]);
+                        _tooltips.set_tip(*place, __rgradient[i]);
+                        _mode[i] = SS_RGRADIENT;
+                    } else if (SP_IS_PATTERN (server)) {
+                        place->add(_pattern[i]);
+                        _tooltips.set_tip(*place, __pattern[i]);
+                        _mode[i] = SS_PATTERN;
+                    }
+                } else {
+                    g_warning ("file %s: line %d: Unknown paint server", __FILE__, __LINE__);
+                }
+            } else if (paint->set && paint->isColor()) {
+                guint32 color = paint->value.color.toRGBA32(
                                      SP_SCALE24_TO_FLOAT ((i == SS_FILL)? query->fill_opacity.value : query->stroke_opacity.value));
                 _lastselected[i] = _thisselected[i];
                 _thisselected[i] = color | 0xff; // only color, opacity === 1
@@ -879,33 +984,11 @@ SelectedStyle::update()
                 place->add(*_color_preview[i]);
                 gchar c_string[64];
                 g_snprintf (c_string, 64, "%06x/%.3g", color >> 8, SP_RGBA32_A_F(color));
-                _tooltips.set_tip(*place, __color[i] + ": " + c_string);
+                _tooltips.set_tip(*place, __color[i] + ": " + c_string + _(", drag to adjust"));
                 _mode[i] = SS_COLOR;
                 _popup_copy[i].set_sensitive(true);
 
-            } else if (paint->set && paint->type == SP_PAINT_TYPE_PAINTSERVER) {
-                SPPaintServer *server = (i == SS_FILL)? SP_STYLE_FILL_SERVER (query) : SP_STYLE_STROKE_SERVER (query);
-
-                Inkscape::XML::Node *srepr = SP_OBJECT_REPR(server);
-                _paintserver_id[i] += "url(#";
-                _paintserver_id[i] += srepr->attribute("id");
-                _paintserver_id[i] += ")";
-
-                if (SP_IS_LINEARGRADIENT (server)) {
-                    place->add(_lgradient[i]);
-                    _tooltips.set_tip(*place, __lgradient[i]);
-                    _mode[i] = SS_LGRADIENT;
-                } else if (SP_IS_RADIALGRADIENT (server)) {
-                    place->add(_rgradient[i]);
-                    _tooltips.set_tip(*place, __rgradient[i]);
-                    _mode[i] = SS_RGRADIENT;
-                } else if (SP_IS_PATTERN (server)) {
-                    place->add(_pattern[i]);
-                    _tooltips.set_tip(*place, __pattern[i]);
-                    _mode[i] = SS_PATTERN;
-                }
-
-            } else if (paint->set && paint->type == SP_PAINT_TYPE_NONE) {
+            } else if (paint->set && paint->isNone()) {
                 place->add(_none[i]);
                 _tooltips.set_tip(*place, __none[i]);
                 _mode[i] = SS_NONE;
@@ -934,21 +1017,25 @@ SelectedStyle::update()
 
 // Now query opacity
     _tooltips.unset_tip(_opacity_place);
+    _tooltips.unset_tip(_opacity_sb);
 
     int result = sp_desktop_query_style (_desktop, query, QUERY_STYLE_PROPERTY_MASTEROPACITY);
 
     switch (result) {
     case QUERY_STYLE_NOTHING:
         _tooltips.set_tip(_opacity_place, _("Nothing selected"));
+        _tooltips.set_tip(_opacity_sb, _("Nothing selected"));
         _opacity_sb.set_sensitive(false);
         break;
     case QUERY_STYLE_SINGLE:
     case QUERY_STYLE_MULTIPLE_AVERAGED:
     case QUERY_STYLE_MULTIPLE_SAME:
-        _tooltips.set_tip(_opacity_place, _("Master opacity"));
+        _tooltips.set_tip(_opacity_place, _("Opacity, %"));
+        _tooltips.set_tip(_opacity_sb, _("Opacity, %"));
+        if (_opacity_blocked) break;
         _opacity_blocked = true;
         _opacity_sb.set_sensitive(true);
-        _opacity_adjustment.set_value(SP_SCALE24_TO_FLOAT(query->opacity.value));
+        _opacity_adjustment.set_value(SP_SCALE24_TO_FLOAT(query->opacity.value) * 100);
         _opacity_blocked = false;
         break;
     }
@@ -958,10 +1045,11 @@ SelectedStyle::update()
     switch (result_sw) {
     case QUERY_STYLE_NOTHING:
         _stroke_width.set_markup("");
+        current_stroke_width = 0;
         break;
     case QUERY_STYLE_SINGLE:
     case QUERY_STYLE_MULTIPLE_AVERAGED:
-    case QUERY_STYLE_MULTIPLE_SAME: 
+    case QUERY_STYLE_MULTIPLE_SAME:
     {
         double w;
         if (_sw_unit) {
@@ -969,15 +1057,17 @@ SelectedStyle::update()
         } else {
             w = query->stroke_width.computed;
         }
+        current_stroke_width = w;
+
         {
             gchar *str = g_strdup_printf(" %.3g", w);
             _stroke_width.set_markup(str);
             g_free (str);
         }
         {
-            gchar *str = g_strdup_printf(_("Stroke width: %.5g%s%s"), 
-                                         w, 
-                                         _sw_unit? sp_unit_get_abbreviation(_sw_unit) : "px", 
+            gchar *str = g_strdup_printf(_("Stroke width: %.5g%s%s"),
+                                         w,
+                                         _sw_unit? sp_unit_get_abbreviation(_sw_unit) : "px",
                                          (result_sw == QUERY_STYLE_MULTIPLE_AVERAGED)?
                                          _(" (averaged)") : "");
             _tooltips.set_tip(_stroke_width_place, str);
@@ -989,14 +1079,14 @@ SelectedStyle::update()
         break;
     }
 
-    g_free (query);
+    sp_style_unref(query);
 }
 
 void SelectedStyle::opacity_0(void) {_opacity_sb.set_value(0);}
-void SelectedStyle::opacity_025(void) {_opacity_sb.set_value(0.25);}
-void SelectedStyle::opacity_05(void) {_opacity_sb.set_value(0.5);}
-void SelectedStyle::opacity_075(void) {_opacity_sb.set_value(0.75);}
-void SelectedStyle::opacity_1(void) {_opacity_sb.set_value(1.0);}
+void SelectedStyle::opacity_025(void) {_opacity_sb.set_value(25);}
+void SelectedStyle::opacity_05(void) {_opacity_sb.set_value(50);}
+void SelectedStyle::opacity_075(void) {_opacity_sb.set_value(75);}
+void SelectedStyle::opacity_1(void) {_opacity_sb.set_value(100);}
 
 void SelectedStyle::on_opacity_menu (Gtk::Menu *menu) {
 
@@ -1013,25 +1103,25 @@ void SelectedStyle::on_opacity_menu (Gtk::Menu *menu) {
     }
     {
         Gtk::MenuItem *item = new Gtk::MenuItem;
-        item->add(*(new Gtk::Label("0.25", 0, 0)));
+        item->add(*(new Gtk::Label("25%", 0, 0)));
         item->signal_activate().connect(sigc::mem_fun(*this, &SelectedStyle::opacity_025 ));
         menu->add(*item);
     }
     {
         Gtk::MenuItem *item = new Gtk::MenuItem;
-        item->add(*(new Gtk::Label("0.5", 0, 0)));
+        item->add(*(new Gtk::Label("50%", 0, 0)));
         item->signal_activate().connect(sigc::mem_fun(*this, &SelectedStyle::opacity_05 ));
         menu->add(*item);
     }
     {
         Gtk::MenuItem *item = new Gtk::MenuItem;
-        item->add(*(new Gtk::Label("0.75", 0, 0)));
+        item->add(*(new Gtk::Label("75%", 0, 0)));
         item->signal_activate().connect(sigc::mem_fun(*this, &SelectedStyle::opacity_075 ));
         menu->add(*item);
     }
     {
         Gtk::MenuItem *item = new Gtk::MenuItem;
-        item->add(*(new Gtk::Label(_("1.0 (opaque)"), 0, 0)));
+        item->add(*(new Gtk::Label(_("100% (opaque)"), 0, 0)));
         item->signal_activate().connect(sigc::mem_fun(*this, &SelectedStyle::opacity_1 ));
         menu->add(*item);
     }
@@ -1045,20 +1135,314 @@ void SelectedStyle::on_opacity_changed () {
     _opacity_blocked = true;
     SPCSSAttr *css = sp_repr_css_attr_new ();
     Inkscape::CSSOStringStream os;
-    os << CLAMP (_opacity_adjustment.get_value(), 0.0, 1.0);
+    os << CLAMP ((_opacity_adjustment.get_value() / 100), 0.0, 1.0);
     sp_repr_css_set_property (css, "opacity", os.str().c_str());
+    // FIXME: workaround for GTK breakage: display interruptibility sometimes results in GTK
+    // sending multiple value-changed events. As if when Inkscape interrupts redraw for main loop
+    // iterations, GTK discovers that this callback hasn't finished yet, and for some weird reason
+    // decides to add yet another value-changed event to the queue. Totally braindead if you ask
+    // me. As a result, scrolling the spinbutton once results in runaway change until it hits 1.0
+    // or 0.0. (And no, this is not a race with ::update, I checked that.)
+    // Sigh. So we disable interruptibility while we're setting the new value.
+    sp_canvas_force_full_redraw_after_interruptions(sp_desktop_canvas(_desktop), 0);
     sp_desktop_set_style (_desktop, css);
     sp_repr_css_attr_unref (css);
-    sp_document_maybe_done (SP_DT_DOCUMENT (_desktop), "fillstroke:opacity");
+    sp_document_maybe_done (sp_desktop_document (_desktop), "fillstroke:opacity", SP_VERB_DIALOG_FILL_STROKE,
+                      _("Change opacity"));
+    // resume interruptibility
+    sp_canvas_end_forced_full_redraws(sp_desktop_canvas(_desktop));
     spinbutton_defocus(GTK_OBJECT(_opacity_sb.gobj()));
     _opacity_blocked = false;
 }
 
+/* =============================================  RotateableSwatch  */
+
+RotateableSwatch::RotateableSwatch(SelectedStyle *parent, guint mode) :
+    fillstroke(mode),
+    parent(parent),
+    startcolor(0),
+    startcolor_set(false),
+    undokey("ssrot1"),
+    cr(0),
+    cr_set(false)
+    
+{
+}
+
+RotateableSwatch::~RotateableSwatch() {
+}
+
+double
+RotateableSwatch::color_adjust(float *hsl, double by, guint32 cc, guint modifier)
+{
+    sp_color_rgb_to_hsl_floatv (hsl, SP_RGBA32_R_F(cc), SP_RGBA32_G_F(cc), SP_RGBA32_B_F(cc));
+
+    double diff = 0;
+    if (modifier == 2) { // saturation
+        double old = hsl[1];
+        if (by > 0) {
+            hsl[1] += by * (1 - hsl[1]);
+        } else {
+            hsl[1] += by * (hsl[1]);
+        }
+        diff = hsl[1] - old;
+    } else if (modifier == 1) { // lightness
+        double old = hsl[2];
+        if (by > 0) {
+            hsl[2] += by * (1 - hsl[2]);
+        } else {
+            hsl[2] += by * (hsl[2]);
+        }
+        diff = hsl[2] - old;
+    } else { // hue
+        double old = hsl[0];
+        hsl[0] += by/2;
+        while (hsl[0] < 0)
+            hsl[0] += 1;
+        while (hsl[0] > 1)
+            hsl[0] -= 1;
+        diff = hsl[0] - old;
+    }
+
+    float rgb[3];
+    sp_color_hsl_to_rgb_floatv (rgb, hsl[0], hsl[1], hsl[2]);
+
+    gchar c[64];
+    sp_svg_write_color (c, sizeof(c),
+        SP_RGBA32_U_COMPOSE(
+                (SP_COLOR_F_TO_U(rgb[0])),
+                (SP_COLOR_F_TO_U(rgb[1])),
+                (SP_COLOR_F_TO_U(rgb[2])),
+                0xff
+        )
+    );
+
+    SPCSSAttr *css = sp_repr_css_attr_new ();
+    if (fillstroke == SS_FILL)
+        sp_repr_css_set_property (css, "fill", c);
+    else
+        sp_repr_css_set_property (css, "stroke", c);
+    sp_desktop_set_style (parent->getDesktop(), css);
+    sp_repr_css_attr_unref (css);
+    return diff;
+}
+
+void
+RotateableSwatch::do_motion(double by, guint modifier) {
+    if (parent->_mode[fillstroke] != SS_COLOR)
+        return;
+
+    if (!cr_set && modifier != 3) {
+        GtkWidget *w = GTK_WIDGET(gobj());
+
+        GdkBitmap *bitmap = NULL;
+        GdkBitmap *mask = NULL;
+        if (modifier == 2) { // saturation
+            sp_cursor_bitmap_and_mask_from_xpm(&bitmap, &mask, cursor_adj_s_xpm);
+        } else if (modifier == 1) { // lightness
+            sp_cursor_bitmap_and_mask_from_xpm(&bitmap, &mask, cursor_adj_l_xpm);
+        } else { // hue
+            sp_cursor_bitmap_and_mask_from_xpm(&bitmap, &mask, cursor_adj_h_xpm);
+        }
+        if ((bitmap != NULL) && (mask != NULL)) {
+            cr = gdk_cursor_new_from_pixmap(bitmap, mask,
+                                            &w->style->black,
+                                            &w->style->white,
+                                            16, 16);
+            g_object_unref (bitmap);
+            g_object_unref (mask);
+            gdk_window_set_cursor(w->window, cr);
+            gdk_cursor_unref(cr);
+            cr_set = true;
+        }
+    }
+
+    guint32 cc;
+    if (!startcolor_set) {
+        cc = startcolor = parent->_thisselected[fillstroke];
+        startcolor_set = true;
+    } else {
+        cc = startcolor;
+    }
+
+    float hsl[3];
+    double diff = 0;
+    if (modifier != 3) {
+        diff = color_adjust(hsl, by, cc, modifier);
+    }
+
+    if (modifier == 3) { // Alt, do nothing
+
+    } else if (modifier == 2) { // saturation
+        sp_document_maybe_done (sp_desktop_document(parent->getDesktop()), undokey,
+                                SP_VERB_DIALOG_FILL_STROKE, (_("Adjust saturation")));
+        double ch = hsl[1];
+        parent->getDesktop()->event_context->_message_context->setF(Inkscape::IMMEDIATE_MESSAGE, _("Adjusting <b>saturation</b>: was %.3g, now <b>%.3g</b> (diff %.3g); with <b>Ctrl</b> to adjust lightness, without modifiers to adjust hue"), ch - diff, ch, diff);
+
+    } else if (modifier == 1) { // lightness
+        sp_document_maybe_done (sp_desktop_document(parent->getDesktop()), undokey,
+                                SP_VERB_DIALOG_FILL_STROKE, (_("Adjust lightness")));
+        double ch = hsl[2];
+        parent->getDesktop()->event_context->_message_context->setF(Inkscape::IMMEDIATE_MESSAGE, _("Adjusting <b>lightness</b>: was %.3g, now <b>%.3g</b> (diff %.3g); with <b>Shift</b> to adjust saturation, without modifiers to adjust hue"), ch - diff, ch, diff);
+
+    } else { // hue
+        sp_document_maybe_done (sp_desktop_document(parent->getDesktop()), undokey,
+                                SP_VERB_DIALOG_FILL_STROKE, (_("Adjust hue")));
+        double ch = hsl[0];
+        parent->getDesktop()->event_context->_message_context->setF(Inkscape::IMMEDIATE_MESSAGE, _("Adjusting <b>hue</b>: was %.3g, now <b>%.3g</b> (diff %.3g); with <b>Shift</b> to adjust saturation, with <b>Ctrl</b> to adjust lightness"), ch - diff, ch, diff);
+    }
+}
+
+void
+RotateableSwatch::do_release(double by, guint modifier) {
+    if (parent->_mode[fillstroke] != SS_COLOR)
+        return;
+
+    float hsl[3];
+    if (modifier != 3) {
+        color_adjust(hsl, by, startcolor, modifier);
+    }
+
+    if (cr_set) {
+        GtkWidget *w = GTK_WIDGET(gobj());
+        gdk_window_set_cursor(w->window, NULL);
+        if (cr) {
+           gdk_cursor_unref (cr);
+           cr = NULL;
+        }
+        cr_set = false;
+    }
+
+    if (modifier == 3) { // Alt, do nothing
+    } else if (modifier == 2) { // saturation
+        sp_document_maybe_done (sp_desktop_document(parent->getDesktop()), undokey,
+                                SP_VERB_DIALOG_FILL_STROKE, ("Adjust saturation"));
+
+    } else if (modifier == 1) { // lightness
+        sp_document_maybe_done (sp_desktop_document(parent->getDesktop()), undokey,
+                                SP_VERB_DIALOG_FILL_STROKE, ("Adjust lightness"));
+
+    } else { // hue
+        sp_document_maybe_done (sp_desktop_document(parent->getDesktop()), undokey,
+                                SP_VERB_DIALOG_FILL_STROKE, ("Adjust hue"));
+    }
+
+    if (!strcmp(undokey, "ssrot1")) {
+        undokey = "ssrot2";
+    } else {
+        undokey = "ssrot1";
+    }
+
+    parent->getDesktop()->event_context->_message_context->clear();
+    startcolor_set = false;
+}
+
+/* =============================================  RotateableStrokeWidth  */
+
+RotateableStrokeWidth::RotateableStrokeWidth(SelectedStyle *parent) :
+    parent(parent),
+    startvalue(0),
+    startvalue_set(false),
+    undokey("swrot1"),
+    cr(0),
+    cr_set(false)
+{
+}
+
+RotateableStrokeWidth::~RotateableStrokeWidth() {
+}
+
+double
+RotateableStrokeWidth::value_adjust(double current, double by, guint /*modifier*/, bool final)
+{
+    double newval;
+    // by is -1..1
+    if (by < 0) {
+        // map negative 0..-1 to current..0
+        newval = current * (1  +  by);
+    } else {
+        // map positive 0..1 to current..4*current
+        newval = current * (1  +  by) * (1  +  by);
+    }
+
+    SPCSSAttr *css = sp_repr_css_attr_new ();
+    if (final && newval < 1e-6) {
+        // if dragged into zero and this is the final adjust on mouse release, delete stroke;
+        // if it's not final, leave it a chance to increase again (which is not possible with "none")
+        sp_repr_css_set_property (css, "stroke", "none");
+    } else {
+        Inkscape::CSSOStringStream os;
+        os << newval;
+        sp_repr_css_set_property (css, "stroke-width", os.str().c_str());
+    }
+
+    sp_desktop_set_style (parent->getDesktop(), css);
+    sp_repr_css_attr_unref (css);
+    return newval - current;
+}
+
+void
+RotateableStrokeWidth::do_motion(double by, guint modifier) {
+
+    // if this is the first motion after a mouse grab, remember the current width
+    if (!startvalue_set) {
+        startvalue = parent->current_stroke_width;
+        // if it's 0, adjusting (which uses multiplication) will not be able to change it, so we
+        // cheat and provide a non-zero value
+        if (startvalue == 0)
+            startvalue = 1;
+        startvalue_set = true;
+    }
+
+    if (modifier == 3) { // Alt, do nothing
+    } else {
+        double diff = value_adjust(startvalue, by, modifier, false);
+        sp_document_maybe_done (sp_desktop_document(parent->getDesktop()), undokey,
+                                SP_VERB_DIALOG_FILL_STROKE, (_("Adjust stroke width")));
+        parent->getDesktop()->event_context->_message_context->setF(Inkscape::IMMEDIATE_MESSAGE, _("Adjusting <b>stroke width</b>: was %.3g, now <b>%.3g</b> (diff %.3g)"), startvalue, startvalue + diff, diff);
+    }
+}
+
+void
+RotateableStrokeWidth::do_release(double by, guint modifier) {
+
+    if (modifier == 3) { // do nothing
+
+    } else {
+        value_adjust(startvalue, by, modifier, true);
+        startvalue_set = false;
+        sp_document_maybe_done (sp_desktop_document(parent->getDesktop()), undokey,
+                                SP_VERB_DIALOG_FILL_STROKE, (_("Adjust stroke width")));
+    }
+
+    if (!strcmp(undokey, "swrot1")) {
+        undokey = "swrot2";
+    } else {
+        undokey = "swrot1";
+    }
+    parent->getDesktop()->event_context->_message_context->clear();
+}
+
+
+Dialog::FillAndStroke *get_fill_and_stroke_panel(SPDesktop *desktop)
+{
+    if (Dialog::PanelDialogBase *panel_dialog = 
+        dynamic_cast<Dialog::PanelDialogBase *>(desktop->_dlg_mgr->getDialog("FillAndStroke"))) {
+        try {
+            Dialog::FillAndStroke &fill_and_stroke = 
+                dynamic_cast<Dialog::FillAndStroke &>(panel_dialog->getPanel());
+            return &fill_and_stroke;
+        } catch (std::exception e) { }
+    }        
+
+    return 0;
+}
+
 } // namespace Widget
 } // namespace UI
 } // namespace Inkscape
 
-/* 
+/*
   Local Variables:
   mode:c++
   c-file-style:"stroustrup"