Code

add alt dragging for no change mode
[inkscape.git] / src / ui / widget / selected-style.cpp
1 /**
2  * \brief Selected style indicator (fill, stroke, opacity)
3  *
4  * Author:
5  *   buliabyak@gmail.com
6  *
7  * Copyright (C) 2005 author
8  *
9  * Released under GNU GPL.  Read the file 'COPYING' for more information.
10  */
12 #ifdef HAVE_CONFIG_H
13 # include <config.h>
14 #endif
16 #include <gtk/gtkdnd.h>
18 #include "selected-style.h"
20 #include "widgets/spw-utilities.h"
21 #include "ui/widget/color-preview.h"
23 #include "selection.h"
24 #include "desktop-handles.h"
25 #include "style.h"
26 #include "desktop-style.h"
27 #include "sp-linear-gradient-fns.h"
28 #include "sp-radial-gradient-fns.h"
29 #include "sp-pattern.h"
30 #include "ui/dialog/dialog-manager.h"
31 #include "ui/dialog/fill-and-stroke.h"
32 #include "xml/repr.h"
33 #include "document.h"
34 #include "widgets/widget-sizes.h"
35 #include "widgets/spinbutton-events.h"
36 #include "widgets/gradient-image.h"
37 #include "sp-gradient.h"
38 #include "svg/svg-color.h"
39 #include "svg/css-ostringstream.h"
40 #include "helper/units.h"
41 #include "event-context.h"
42 #include "message-context.h"
43 #include "verbs.h"
44 #include "color.h"
45 #include <display/sp-canvas.h>
46 #include "pixmaps/cursor-adj-h.xpm"
47 #include "pixmaps/cursor-adj-s.xpm"
48 #include "pixmaps/cursor-adj-l.xpm"
49 #include "sp-cursor.h"
51 static gdouble const _sw_presets[]     = { 32 ,  16 ,  10 ,  8 ,  6 ,  4 ,  3 ,  2 ,  1.5 ,  1 ,  0.75 ,  0.5 ,  0.25 ,  0.1 };
52 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"};
54 static void 
55 ss_selection_changed (Inkscape::Selection *, gpointer data)
56 {
57     Inkscape::UI::Widget::SelectedStyle *ss = (Inkscape::UI::Widget::SelectedStyle *) data;
58     ss->update();
59 }
61 static void
62 ss_selection_modified (Inkscape::Selection *selection, guint flags, gpointer data)
63 {
64     ss_selection_changed (selection, data);
65 }
67 static void
68 ss_subselection_changed (gpointer dragger, gpointer data)
69 {
70     ss_selection_changed (NULL, data);
71 }
73 namespace Inkscape {
74 namespace UI {
75 namespace Widget {
78 typedef struct {
79     SelectedStyle* parent;
80     int item;
81 } DropTracker;
83 /* Drag and Drop */
84 typedef enum {
85     APP_X_COLOR
86 } ui_drop_target_info;
88 static GtkTargetEntry ui_drop_target_entries [] = {
89     {"application/x-color", 0, APP_X_COLOR}
90 };
92 #define ENTRIES_SIZE(n) sizeof(n)/sizeof(n[0])
93 static guint nui_drop_target_entries = ENTRIES_SIZE(ui_drop_target_entries);
96 SelectedStyle::SelectedStyle(bool layout)
97     : _desktop (NULL),
99       _table(2, 6),
100       _fill_label (_("Fill:")),
101       _stroke_label (_("Stroke:")),
102       _opacity_label (_("O:")),
104       _fill_place (SS_FILL),
105       _stroke_place (SS_STROKE),
107       _fill_flag_place (),
108       _stroke_flag_place (),
110       _opacity_place (),
111       _opacity_adjustment (100, 0.0, 100, 1.0, 10.0),
112       _opacity_sb (0.02, 0),
114       _stroke (),
115       _stroke_width (""),
117       _opacity_blocked (false),
119       _popup_px(_sw_group),
120       _popup_pt(_sw_group),
121       _popup_mm(_sw_group),
123       _sw_unit(NULL),
125       _tooltips ()
128     _drop[0] = _drop[1] = 0;
129     _dropEnabled[0] = _dropEnabled[1] = false;
131     _fill_label.set_alignment(0.0, 0.5);
132     _fill_label.set_padding(0, 0);
133     _stroke_label.set_alignment(0.0, 0.5);
134     _stroke_label.set_padding(0, 0);
135     _opacity_label.set_alignment(0.0, 0.5);
136     _opacity_label.set_padding(0, 0);
138     _table.set_col_spacings (2);
139     _table.set_row_spacings (0);
141     for (int i = SS_FILL; i <= SS_STROKE; i++) {
143         _na[i].set_markup (_("N/A"));
144         sp_set_font_size_smaller (GTK_WIDGET(_na[i].gobj()));
145         _na[i].show_all();
146         __na[i] = (_("Nothing selected"));
148         _none[i].set_markup (_("<i>None</i>"));
149         sp_set_font_size_smaller (GTK_WIDGET(_none[i].gobj()));
150         _none[i].show_all();
151         __none[i] = (i == SS_FILL)? (_("No fill")) : (_("No stroke"));
153         _pattern[i].set_markup (_("Pattern"));
154         sp_set_font_size_smaller (GTK_WIDGET(_pattern[i].gobj()));
155         _pattern[i].show_all();
156         __pattern[i] = (i == SS_FILL)? (_("Pattern fill")) : (_("Pattern stroke"));
158         _lgradient[i].set_markup (_("<b>L</b>"));
159         sp_set_font_size_smaller (GTK_WIDGET(_lgradient[i].gobj()));
160         _lgradient[i].show_all();
161         __lgradient[i] = (i == SS_FILL)? (_("Linear gradient fill")) : (_("Linear gradient stroke"));
163         _gradient_preview_l[i] =  GTK_WIDGET(sp_gradient_image_new (NULL));
164         _gradient_box_l[i].pack_start(_lgradient[i]);
165         _gradient_box_l[i].pack_start(*(Glib::wrap(_gradient_preview_l[i])));
166         _gradient_box_l[i].show_all();
168         _rgradient[i].set_markup (_("<b>R</b>"));
169         sp_set_font_size_smaller (GTK_WIDGET(_rgradient[i].gobj()));
170         _rgradient[i].show_all();
171         __rgradient[i] = (i == SS_FILL)? (_("Radial gradient fill")) : (_("Radial gradient stroke"));
173         _gradient_preview_r[i] = GTK_WIDGET(sp_gradient_image_new (NULL));
174         _gradient_box_r[i].pack_start(_rgradient[i]);
175         _gradient_box_r[i].pack_start(*(Glib::wrap(_gradient_preview_r[i])));
176         _gradient_box_r[i].show_all();
178         _many[i].set_markup (_("Different"));
179         sp_set_font_size_smaller (GTK_WIDGET(_many[i].gobj()));
180         _many[i].show_all();
181         __many[i] = (i == SS_FILL)? (_("Different fills")) : (_("Different strokes"));
183         _unset[i].set_markup (_("<b>Unset</b>"));
184         sp_set_font_size_smaller (GTK_WIDGET(_unset[i].gobj()));
185         _unset[i].show_all();
186         __unset[i] = (i == SS_FILL)? (_("Unset fill")) : (_("Unset stroke"));
188         _color_preview[i] = new Inkscape::UI::Widget::ColorPreview (0);
189         __color[i] = (i == SS_FILL)? (_("Flat color fill")) : (_("Flat color stroke"));
191         // TRANSLATOR COMMENT: A means "Averaged"
192         _averaged[i].set_markup (_("<b>a</b>"));
193         sp_set_font_size_smaller (GTK_WIDGET(_averaged[i].gobj()));
194         _averaged[i].show_all();
195         __averaged[i] = (i == SS_FILL)? (_("Fill is averaged over selected objects")) : (_("Stroke is averaged over selected objects"));
197         // TRANSLATOR COMMENT: M means "Multiple"
198         _multiple[i].set_markup (_("<b>m</b>"));
199         sp_set_font_size_smaller (GTK_WIDGET(_multiple[i].gobj()));
200         _multiple[i].show_all();
201         __multiple[i] = (i == SS_FILL)? (_("Multiple selected objects have the same fill")) : (_("Multiple selected objects have the same stroke"));
203         _popup_edit[i].add(*(new Gtk::Label((i == SS_FILL)? _("Edit fill...") : _("Edit stroke..."), 0.0, 0.5)));
204         _popup_edit[i].signal_activate().connect(sigc::mem_fun(*this, 
205                                (i == SS_FILL)? &SelectedStyle::on_fill_edit : &SelectedStyle::on_stroke_edit ));
207         _popup_lastused[i].add(*(new Gtk::Label(_("Last set color"), 0.0, 0.5)));
208         _popup_lastused[i].signal_activate().connect(sigc::mem_fun(*this, 
209                                (i == SS_FILL)? &SelectedStyle::on_fill_lastused : &SelectedStyle::on_stroke_lastused ));
211         _popup_lastselected[i].add(*(new Gtk::Label(_("Last selected color"), 0.0, 0.5)));
212         _popup_lastselected[i].signal_activate().connect(sigc::mem_fun(*this, 
213                                (i == SS_FILL)? &SelectedStyle::on_fill_lastselected : &SelectedStyle::on_stroke_lastselected ));
215         _popup_invert[i].add(*(new Gtk::Label(_("Invert"), 0.0, 0.5)));
216         _popup_invert[i].signal_activate().connect(sigc::mem_fun(*this, 
217                                (i == SS_FILL)? &SelectedStyle::on_fill_invert : &SelectedStyle::on_stroke_invert ));
219         _popup_white[i].add(*(new Gtk::Label(_("White"), 0.0, 0.5)));
220         _popup_white[i].signal_activate().connect(sigc::mem_fun(*this, 
221                                (i == SS_FILL)? &SelectedStyle::on_fill_white : &SelectedStyle::on_stroke_white ));
223         _popup_black[i].add(*(new Gtk::Label(_("Black"), 0.0, 0.5)));
224         _popup_black[i].signal_activate().connect(sigc::mem_fun(*this, 
225                                (i == SS_FILL)? &SelectedStyle::on_fill_black : &SelectedStyle::on_stroke_black ));
227         _popup_copy[i].add(*(new Gtk::Label(_("Copy color"), 0.0, 0.5)));
228         _popup_copy[i].signal_activate().connect(sigc::mem_fun(*this, 
229                                (i == SS_FILL)? &SelectedStyle::on_fill_copy : &SelectedStyle::on_stroke_copy ));
231         _popup_paste[i].add(*(new Gtk::Label(_("Paste color"), 0.0, 0.5)));
232         _popup_paste[i].signal_activate().connect(sigc::mem_fun(*this, 
233                                (i == SS_FILL)? &SelectedStyle::on_fill_paste : &SelectedStyle::on_stroke_paste ));
235         _popup_swap[i].add(*(new Gtk::Label(_("Swap fill and stroke"), 0.0, 0.5)));
236         _popup_swap[i].signal_activate().connect(sigc::mem_fun(*this, 
237                                &SelectedStyle::on_fillstroke_swap));
239         _popup_opaque[i].add(*(new Gtk::Label((i == SS_FILL)? _("Make fill opaque") : _("Make stroke opaque"), 0.0, 0.5)));
240         _popup_opaque[i].signal_activate().connect(sigc::mem_fun(*this, 
241                                (i == SS_FILL)? &SelectedStyle::on_fill_opaque : &SelectedStyle::on_stroke_opaque ));
243         //TRANSLATORS COMMENT: unset is a verb here
244         _popup_unset[i].add(*(new Gtk::Label((i == SS_FILL)? _("Unset fill") : _("Unset stroke"), 0.0, 0.5)));
245         _popup_unset[i].signal_activate().connect(sigc::mem_fun(*this, 
246                                (i == SS_FILL)? &SelectedStyle::on_fill_unset : &SelectedStyle::on_stroke_unset ));
248         _popup_remove[i].add(*(new Gtk::Label((i == SS_FILL)? _("Remove fill") : _("Remove stroke"), 0.0, 0.5)));
249         _popup_remove[i].signal_activate().connect(sigc::mem_fun(*this, 
250                                (i == SS_FILL)? &SelectedStyle::on_fill_remove : &SelectedStyle::on_stroke_remove ));
252         _popup[i].attach(_popup_edit[i], 0,1, 0,1);
253           _popup[i].attach(*(new Gtk::SeparatorMenuItem()), 0,1, 1,2);
254         _popup[i].attach(_popup_lastused[i], 0,1, 2,3);
255         _popup[i].attach(_popup_lastselected[i], 0,1, 3,4);
256           _popup[i].attach(*(new Gtk::SeparatorMenuItem()), 0,1, 4,5);
257         _popup[i].attach(_popup_invert[i], 0,1, 5,6);
258           _popup[i].attach(*(new Gtk::SeparatorMenuItem()), 0,1, 6,7);
259         _popup[i].attach(_popup_white[i], 0,1, 7,8);
260         _popup[i].attach(_popup_black[i], 0,1, 8,9);
261           _popup[i].attach(*(new Gtk::SeparatorMenuItem()), 0,1, 9,10);
262         _popup[i].attach(_popup_copy[i], 0,1, 10,11);
263         _popup_copy[i].set_sensitive(false);
264         _popup[i].attach(_popup_paste[i], 0,1, 11,12);
265         _popup[i].attach(_popup_swap[i], 0,1, 12,13);
266           _popup[i].attach(*(new Gtk::SeparatorMenuItem()), 0,1, 13,14); 
267         _popup[i].attach(_popup_opaque[i], 0,1, 14,15);
268         _popup[i].attach(_popup_unset[i], 0,1, 15,16);
269         _popup[i].attach(_popup_remove[i], 0,1, 16,17);
270         _popup[i].show_all();
272         _mode[i] = SS_NA;
273     }
275     {
276         _popup_px.add(*(new Gtk::Label(_("px"), 0.0, 0.5)));
277         _popup_px.signal_activate().connect(sigc::mem_fun(*this, &SelectedStyle::on_popup_px));
278         _popup_sw.attach(_popup_px, 0,1, 0,1);
280         _popup_pt.add(*(new Gtk::Label(_("pt"), 0.0, 0.5)));
281         _popup_pt.signal_activate().connect(sigc::mem_fun(*this, &SelectedStyle::on_popup_pt));
282         _popup_sw.attach(_popup_pt, 0,1, 1,2);
284         _popup_mm.add(*(new Gtk::Label(_("mm"), 0.0, 0.5)));
285         _popup_mm.signal_activate().connect(sigc::mem_fun(*this, &SelectedStyle::on_popup_mm));
286         _popup_sw.attach(_popup_mm, 0,1, 2,3);
288         _popup_sw.attach(*(new Gtk::SeparatorMenuItem()), 0,1, 3,4);
290         for (guint i = 0; i < G_N_ELEMENTS(_sw_presets_str); ++i) {
291             Gtk::MenuItem *mi = Gtk::manage(new Gtk::MenuItem());
292             mi->add(*(new Gtk::Label(_sw_presets_str[i], 0.0, 0.5)));
293             mi->signal_activate().connect(sigc::bind<int>(sigc::mem_fun(*this, &SelectedStyle::on_popup_preset), i));
294             _popup_sw.attach(*mi, 0,1, 4+i, 5+i);
295         }
297         guint i = G_N_ELEMENTS(_sw_presets_str) + 5;
299         _popup_sw.attach(*(new Gtk::SeparatorMenuItem()), 0,1, i,i+1);
301         _popup_sw_remove.add(*(new Gtk::Label(_("Remove"), 0.0, 0.5)));
302         _popup_sw_remove.signal_activate().connect(sigc::mem_fun(*this, &SelectedStyle::on_stroke_remove));
303         _popup_sw.attach(_popup_sw_remove, 0,1, i+1,i+2);
305         _popup_sw.show_all();
306     }
308     _fill_place.signal_button_release_event().connect(sigc::mem_fun(*this, &SelectedStyle::on_fill_click));
309     _stroke_place.signal_button_release_event().connect(sigc::mem_fun(*this, &SelectedStyle::on_stroke_click));
310     _opacity_place.signal_button_press_event().connect(sigc::mem_fun(*this, &SelectedStyle::on_opacity_click));
311     _stroke_width_place.signal_button_press_event().connect(sigc::mem_fun(*this, &SelectedStyle::on_sw_click));
313     _opacity_sb.signal_populate_popup().connect(sigc::mem_fun(*this, &SelectedStyle::on_opacity_menu));
314     _opacity_sb.signal_value_changed().connect(sigc::mem_fun(*this, &SelectedStyle::on_opacity_changed));
316     _fill_place.add(_na[SS_FILL]);
317     _tooltips.set_tip(_fill_place, __na[SS_FILL]);
319     _stroke_place.add(_na[SS_STROKE]);
320     _tooltips.set_tip(_stroke_place, __na[SS_STROKE]);
322     _stroke.pack_start(_stroke_place);
323     _stroke_width_place.add(_stroke_width);
324     _stroke.pack_start(_stroke_width_place, Gtk::PACK_SHRINK);
326     _opacity_sb.set_adjustment(_opacity_adjustment);
327     sp_set_font_size_smaller (GTK_WIDGET(_opacity_sb.gobj()));
328     _opacity_sb.set_size_request (SELECTED_STYLE_SB_WIDTH, -1);
329     _opacity_sb.set_sensitive (false);
331     _table.attach(_fill_label, 0,1, 0,1, Gtk::FILL, Gtk::SHRINK);
332     _table.attach(_stroke_label, 0,1, 1,2, Gtk::FILL, Gtk::SHRINK);
334     _table.attach(_fill_flag_place, 1,2, 0,1, Gtk::SHRINK, Gtk::SHRINK);
335     _table.attach(_stroke_flag_place, 1,2, 1,2, Gtk::SHRINK, Gtk::SHRINK);
337     _table.attach(_fill_place, 2,3, 0,1);
338     _table.attach(_stroke, 2,3, 1,2);
340     _opacity_place.add(_opacity_label);
341     _table.attach(_opacity_place, 4,5, 0,2, Gtk::SHRINK, Gtk::SHRINK);
342     _table.attach(_opacity_sb, 5,6, 0,2, Gtk::SHRINK, Gtk::SHRINK);
344     pack_start(_table, true, true, 2);
346     set_size_request (SELECTED_STYLE_WIDTH, -1);
348     sp_set_font_size_smaller (GTK_WIDGET(_opacity_label.gobj()));
349     sp_set_font_size_smaller (GTK_WIDGET(_opacity_sb.gobj()));
350     sp_set_font_size_smaller (GTK_WIDGET(_fill_place.gobj()));
351     sp_set_font_size_smaller (GTK_WIDGET(_fill_flag_place.gobj()));
352     sp_set_font_size_smaller (GTK_WIDGET(_stroke_place.gobj()));
353     sp_set_font_size_smaller (GTK_WIDGET(_stroke_flag_place.gobj()));
354     sp_set_font_size_smaller (GTK_WIDGET(_stroke_width.gobj()));
355     sp_set_font_size_smaller (GTK_WIDGET(_fill_label.gobj()));
356     sp_set_font_size_smaller (GTK_WIDGET(_stroke_label.gobj()));
358     _drop[SS_FILL] = new DropTracker();
359     ((DropTracker*)_drop[SS_FILL])->parent = this;
360     ((DropTracker*)_drop[SS_FILL])->item = SS_FILL;
362     _drop[SS_STROKE] = new DropTracker();
363     ((DropTracker*)_drop[SS_STROKE])->parent = this;
364     ((DropTracker*)_drop[SS_STROKE])->item = SS_STROKE;
366     g_signal_connect(_stroke_place.gobj(),
367                      "drag_data_received",
368                      G_CALLBACK(dragDataReceived),
369                      _drop[SS_STROKE]);
371     g_signal_connect(_fill_place.gobj(),
372                      "drag_data_received",
373                      G_CALLBACK(dragDataReceived),
374                      _drop[SS_FILL]);
376     _fill_place.parent = this;
377     _stroke_place.parent = this;
380 SelectedStyle::~SelectedStyle()
382     selection_changed_connection->disconnect();
383     delete selection_changed_connection;
384     selection_modified_connection->disconnect();
385     delete selection_modified_connection;
386     subselection_changed_connection->disconnect();
387     delete subselection_changed_connection;
389     for (int i = SS_FILL; i <= SS_STROKE; i++) {
390         delete _color_preview[i];
391         // FIXME: do we need this? the destroy methods are not exported 
392         //sp_gradient_image_destroy(GTK_OBJECT(_gradient_preview_l[i]));
393         //sp_gradient_image_destroy(GTK_OBJECT(_gradient_preview_r[i]));
394     }
396     delete (DropTracker*)_drop[SS_FILL];
397     delete (DropTracker*)_drop[SS_STROKE];
400 void
401 SelectedStyle::setDesktop(SPDesktop *desktop)
403     _desktop = desktop;
404     gtk_object_set_data (GTK_OBJECT(_opacity_sb.gobj()), "dtw", _desktop->canvas);
406     Inkscape::Selection *selection = sp_desktop_selection (desktop);
408     selection_changed_connection = new sigc::connection (selection->connectChanged(
409         sigc::bind (
410             sigc::ptr_fun(&ss_selection_changed),
411             this )
412     ));
413     selection_modified_connection = new sigc::connection (selection->connectModified(
414         sigc::bind (
415             sigc::ptr_fun(&ss_selection_modified),
416             this )
417     ));
418     subselection_changed_connection = new sigc::connection (desktop->connectToolSubselectionChanged(
419         sigc::bind (
420             sigc::ptr_fun(&ss_subselection_changed),
421             this )
422     ));
424     //_sw_unit = (SPUnit *) sp_desktop_namedview(desktop)->doc_units;
427 void SelectedStyle::dragDataReceived( GtkWidget *widget,
428                                       GdkDragContext *drag_context,
429                                       gint x, gint y,
430                                       GtkSelectionData *data,
431                                       guint info,
432                                       guint event_time,
433                                       gpointer user_data )
435     DropTracker* tracker = (DropTracker*)user_data;
437     switch ( (int)tracker->item ) {
438         case SS_FILL:
439         case SS_STROKE:
440         {
441             if ( data->length == 8 ) {
442                 gchar c[64];
443                 // Careful about endian issues.
444                 guint16* dataVals = (guint16*)data->data;
445                 sp_svg_write_color( c, sizeof(c),
446                                     SP_RGBA32_U_COMPOSE(
447                                         0x0ff & (dataVals[0] >> 8),
448                                         0x0ff & (dataVals[1] >> 8),
449                                         0x0ff & (dataVals[2] >> 8),
450                                         0xff // can't have transparency in the color itself
451                                         //0x0ff & (data->data[3] >> 8),
452                                         ));
453                 SPCSSAttr *css = sp_repr_css_attr_new();
454                 sp_repr_css_set_property( css, (tracker->item == SS_FILL) ? "fill":"stroke", c );
455                 sp_desktop_set_style( tracker->parent->_desktop, css );
456                 sp_repr_css_attr_unref( css );
457                 sp_document_done( sp_desktop_document(tracker->parent->_desktop) , SP_VERB_NONE, 
458                                   _("Drop color"));
459             }
460         }
461         break;
462     }
465 void SelectedStyle::on_fill_remove() {
466     SPCSSAttr *css = sp_repr_css_attr_new ();
467     sp_repr_css_set_property (css, "fill", "none");
468     sp_desktop_set_style (_desktop, css, true, true); 
469     sp_repr_css_attr_unref (css);
470     sp_document_done (sp_desktop_document(_desktop), SP_VERB_DIALOG_FILL_STROKE,
471                       _("Remove fill"));
474 void SelectedStyle::on_stroke_remove() {
475     SPCSSAttr *css = sp_repr_css_attr_new ();
476     sp_repr_css_set_property (css, "stroke", "none");
477     sp_desktop_set_style (_desktop, css, true, true); 
478     sp_repr_css_attr_unref (css);
479     sp_document_done (sp_desktop_document(_desktop), SP_VERB_DIALOG_FILL_STROKE,
480                       _("Remove stroke"));
483 void SelectedStyle::on_fill_unset() {
484     SPCSSAttr *css = sp_repr_css_attr_new ();
485     sp_repr_css_unset_property (css, "fill");
486     sp_desktop_set_style (_desktop, css, true, true); 
487     sp_repr_css_attr_unref (css);
488     sp_document_done (sp_desktop_document(_desktop), SP_VERB_DIALOG_FILL_STROKE,
489                       _("Unset fill"));
492 void SelectedStyle::on_stroke_unset() {
493     SPCSSAttr *css = sp_repr_css_attr_new ();
494     sp_repr_css_unset_property (css, "stroke");
495     sp_repr_css_unset_property (css, "stroke-opacity");
496     sp_repr_css_unset_property (css, "stroke-width");
497     sp_repr_css_unset_property (css, "stroke-miterlimit");
498     sp_repr_css_unset_property (css, "stroke-linejoin");
499     sp_repr_css_unset_property (css, "stroke-linecap");
500     sp_repr_css_unset_property (css, "stroke-dashoffset");
501     sp_repr_css_unset_property (css, "stroke-dasharray");
502     sp_desktop_set_style (_desktop, css, true, true);
503     sp_repr_css_attr_unref (css);
504     sp_document_done (sp_desktop_document(_desktop), SP_VERB_DIALOG_FILL_STROKE,
505                       _("Unset stroke"));
508 void SelectedStyle::on_fill_opaque() {
509     SPCSSAttr *css = sp_repr_css_attr_new ();
510     sp_repr_css_set_property (css, "fill-opacity", "1");
511     sp_desktop_set_style (_desktop, css, true);
512     sp_repr_css_attr_unref (css);
513     sp_document_done (sp_desktop_document(_desktop), SP_VERB_DIALOG_FILL_STROKE,
514                       _("Make fill opaque"));
517 void SelectedStyle::on_stroke_opaque() {
518     SPCSSAttr *css = sp_repr_css_attr_new ();
519     sp_repr_css_set_property (css, "stroke-opacity", "1");
520     sp_desktop_set_style (_desktop, css, true);
521     sp_repr_css_attr_unref (css);
522     sp_document_done (sp_desktop_document(_desktop), SP_VERB_DIALOG_FILL_STROKE,
523                       _("Make fill opaque"));
526 void SelectedStyle::on_fill_lastused() {
527     SPCSSAttr *css = sp_repr_css_attr_new ();
528     guint32 color = sp_desktop_get_color(_desktop, true);
529     gchar c[64];
530     sp_svg_write_color (c, sizeof(c), color);
531     sp_repr_css_set_property (css, "fill", c);
532     sp_desktop_set_style (_desktop, css);
533     sp_repr_css_attr_unref (css);
534     sp_document_done (sp_desktop_document(_desktop), SP_VERB_DIALOG_FILL_STROKE, 
535                       _("Apply last set color to fill"));
538 void SelectedStyle::on_stroke_lastused() {
539     SPCSSAttr *css = sp_repr_css_attr_new ();
540     guint32 color = sp_desktop_get_color(_desktop, false);
541     gchar c[64];
542     sp_svg_write_color (c, sizeof(c), color);
543     sp_repr_css_set_property (css, "stroke", c);
544     sp_desktop_set_style (_desktop, css);
545     sp_repr_css_attr_unref (css);
546     sp_document_done (sp_desktop_document(_desktop), SP_VERB_DIALOG_FILL_STROKE, 
547                       _("Apply last set color to stroke"));
550 void SelectedStyle::on_fill_lastselected() {
551     SPCSSAttr *css = sp_repr_css_attr_new ();
552     gchar c[64];
553     sp_svg_write_color (c, sizeof(c), _lastselected[SS_FILL]);
554     sp_repr_css_set_property (css, "fill", c);
555     sp_desktop_set_style (_desktop, css);
556     sp_repr_css_attr_unref (css);
557     sp_document_done (sp_desktop_document(_desktop), SP_VERB_DIALOG_FILL_STROKE,
558                       _("Apply last selected color to fill"));
561 void SelectedStyle::on_stroke_lastselected() {
562     SPCSSAttr *css = sp_repr_css_attr_new ();
563     gchar c[64];
564     sp_svg_write_color (c, sizeof(c), _lastselected[SS_STROKE]);
565     sp_repr_css_set_property (css, "stroke", c);
566     sp_desktop_set_style (_desktop, css);
567     sp_repr_css_attr_unref (css);
568     sp_document_done (sp_desktop_document(_desktop), SP_VERB_DIALOG_FILL_STROKE, 
569                       _("Apply last selected color to stroke"));
572 void SelectedStyle::on_fill_invert() {
573     SPCSSAttr *css = sp_repr_css_attr_new ();
574     guint32 color = _thisselected[SS_FILL];
575     gchar c[64];
576     if (_mode[SS_FILL] != SS_COLOR) return;
577     sp_svg_write_color (c, sizeof(c),
578         SP_RGBA32_U_COMPOSE(
579                 (255 - SP_RGBA32_R_U(color)),
580                 (255 - SP_RGBA32_G_U(color)),
581                 (255 - SP_RGBA32_B_U(color)),
582                 SP_RGBA32_A_U(color)
583         )
584     );
585     sp_repr_css_set_property (css, "fill", c);
586     sp_desktop_set_style (_desktop, css);
587     sp_repr_css_attr_unref (css);
588     sp_document_done (sp_desktop_document(_desktop), SP_VERB_DIALOG_FILL_STROKE,
589                       _("Invert fill"));
592 void SelectedStyle::on_stroke_invert() {
593     SPCSSAttr *css = sp_repr_css_attr_new ();
594     guint32 color = _thisselected[SS_STROKE];
595     gchar c[64];
596     if (_mode[SS_STROKE] != SS_COLOR) return;
597     sp_svg_write_color (c, sizeof(c),
598         SP_RGBA32_U_COMPOSE(
599                 (255 - SP_RGBA32_R_U(color)),
600                 (255 - SP_RGBA32_G_U(color)),
601                 (255 - SP_RGBA32_B_U(color)),
602                 SP_RGBA32_A_U(color)
603         )
604     );
605     sp_repr_css_set_property (css, "stroke", c);
606     sp_desktop_set_style (_desktop, css);
607     sp_repr_css_attr_unref (css);
608     sp_document_done (sp_desktop_document(_desktop), SP_VERB_DIALOG_FILL_STROKE,
609                       _("Invert stroke"));
610
612 void SelectedStyle::on_fill_white() {
613     SPCSSAttr *css = sp_repr_css_attr_new ();
614     gchar c[64];
615     sp_svg_write_color (c, sizeof(c), 0xffffffff);
616     sp_repr_css_set_property (css, "fill", c);
617     sp_repr_css_set_property (css, "fill-opacity", "1");
618     sp_desktop_set_style (_desktop, css);
619     sp_repr_css_attr_unref (css);
620     sp_document_done (sp_desktop_document(_desktop), SP_VERB_DIALOG_FILL_STROKE,
621                       _("White fill"));
624 void SelectedStyle::on_stroke_white() {
625     SPCSSAttr *css = sp_repr_css_attr_new ();
626     gchar c[64];
627     sp_svg_write_color (c, sizeof(c), 0xffffffff);
628     sp_repr_css_set_property (css, "stroke", c);
629     sp_repr_css_set_property (css, "stroke-opacity", "1");
630     sp_desktop_set_style (_desktop, css);
631     sp_repr_css_attr_unref (css);
632     sp_document_done (sp_desktop_document(_desktop), SP_VERB_DIALOG_FILL_STROKE, 
633                       _("White stroke"));
636 void SelectedStyle::on_fill_black() {
637     SPCSSAttr *css = sp_repr_css_attr_new ();
638     gchar c[64];
639     sp_svg_write_color (c, sizeof(c), 0x000000ff);
640     sp_repr_css_set_property (css, "fill", c);
641     sp_repr_css_set_property (css, "fill-opacity", "1.0");
642     sp_desktop_set_style (_desktop, css);
643     sp_repr_css_attr_unref (css);
644     sp_document_done (sp_desktop_document(_desktop), SP_VERB_DIALOG_FILL_STROKE, 
645                       _("Black fill"));
648 void SelectedStyle::on_stroke_black() {
649     SPCSSAttr *css = sp_repr_css_attr_new ();
650     gchar c[64];
651     sp_svg_write_color (c, sizeof(c), 0x000000ff);
652     sp_repr_css_set_property (css, "stroke", c);
653     sp_repr_css_set_property (css, "stroke-opacity", "1.0");
654     sp_desktop_set_style (_desktop, css);
655     sp_repr_css_attr_unref (css);
656     sp_document_done (sp_desktop_document(_desktop), SP_VERB_DIALOG_FILL_STROKE, 
657                       _("Black stroke"));
660 void SelectedStyle::on_fill_copy() {
661     if (_mode[SS_FILL] == SS_COLOR) {
662         gchar c[64];
663         sp_svg_write_color (c, sizeof(c), _thisselected[SS_FILL]);
664         Glib::ustring text;
665         text += c;
666         if (!text.empty()) {
667             Glib::RefPtr<Gtk::Clipboard> refClipboard = Gtk::Clipboard::get();
668             refClipboard->set_text(text);
669         }
670     }
673 void SelectedStyle::on_stroke_copy() {
674     if (_mode[SS_STROKE] == SS_COLOR) {
675         gchar c[64];
676         sp_svg_write_color (c, sizeof(c), _thisselected[SS_STROKE]);
677         Glib::ustring text;
678         text += c;
679         if (!text.empty()) {
680             Glib::RefPtr<Gtk::Clipboard> refClipboard = Gtk::Clipboard::get();
681             refClipboard->set_text(text);
682         }
683     }
686 void SelectedStyle::on_fill_paste() {
687     Glib::RefPtr<Gtk::Clipboard> refClipboard = Gtk::Clipboard::get();
688     Glib::ustring const text = refClipboard->wait_for_text();
690     if (!text.empty()) {
691         guint32 color = sp_svg_read_color(text.c_str(), 0x000000ff); // impossible value, as SVG color cannot have opacity
692         if (color == 0x000000ff) // failed to parse color string
693             return;
695         SPCSSAttr *css = sp_repr_css_attr_new ();
696         sp_repr_css_set_property (css, "fill", text.c_str());
697         sp_desktop_set_style (_desktop, css);
698         sp_repr_css_attr_unref (css);
699         sp_document_done (sp_desktop_document(_desktop), SP_VERB_DIALOG_FILL_STROKE, 
700                       _("Paste fill"));
701     }
704 void SelectedStyle::on_stroke_paste() {
705     Glib::RefPtr<Gtk::Clipboard> refClipboard = Gtk::Clipboard::get();
706     Glib::ustring const text = refClipboard->wait_for_text();
708     if (!text.empty()) {
709         guint32 color = sp_svg_read_color(text.c_str(), 0x000000ff); // impossible value, as SVG color cannot have opacity
710         if (color == 0x000000ff) // failed to parse color string
711             return;
713         SPCSSAttr *css = sp_repr_css_attr_new ();
714         sp_repr_css_set_property (css, "stroke", text.c_str());
715         sp_desktop_set_style (_desktop, css);
716         sp_repr_css_attr_unref (css);
717         sp_document_done (sp_desktop_document(_desktop), SP_VERB_DIALOG_FILL_STROKE,
718                       _("Paste stroke"));
719     }
722 void SelectedStyle::on_fillstroke_swap() {
723     SPCSSAttr *css = sp_repr_css_attr_new ();
725     switch (_mode[SS_FILL]) {
726     case SS_NA:
727     case SS_MANY:
728         break;
729     case SS_NONE:
730         sp_repr_css_set_property (css, "stroke", "none");
731         break;
732     case SS_UNSET:
733         sp_repr_css_unset_property (css, "stroke");
734         break;
735     case SS_COLOR:
736         gchar c[64];
737         sp_svg_write_color (c, sizeof(c), _thisselected[SS_FILL]);
738         sp_repr_css_set_property (css, "stroke", c);
739         break;
740     case SS_LGRADIENT:
741     case SS_RGRADIENT:
742     case SS_PATTERN:
743         sp_repr_css_set_property (css, "stroke", _paintserver_id[SS_FILL].c_str());
744         break;
745     }
747     switch (_mode[SS_STROKE]) {
748     case SS_NA:
749     case SS_MANY:
750         break;
751     case SS_NONE:
752         sp_repr_css_set_property (css, "fill", "none");
753         break;
754     case SS_UNSET:
755         sp_repr_css_unset_property (css, "fill");
756         break;
757     case SS_COLOR:
758         gchar c[64];
759         sp_svg_write_color (c, sizeof(c), _thisselected[SS_STROKE]);
760         sp_repr_css_set_property (css, "fill", c);
761         break;
762     case SS_LGRADIENT:
763     case SS_RGRADIENT:
764     case SS_PATTERN:
765         sp_repr_css_set_property (css, "fill", _paintserver_id[SS_STROKE].c_str());
766         break;
767     }
769     sp_desktop_set_style (_desktop, css);
770     sp_repr_css_attr_unref (css);
771     sp_document_done (sp_desktop_document(_desktop), SP_VERB_DIALOG_FILL_STROKE, 
772                       _("Swap fill and stroke"));
775 void SelectedStyle::on_fill_edit() {
776     if (Dialog::FillAndStroke *dialog = dynamic_cast<Dialog::FillAndStroke *>(
777             _desktop->_dlg_mgr->getDialog("FillAndStroke")))
778         dialog->showPageFill();
781 void SelectedStyle::on_stroke_edit() {
782     if (Dialog::FillAndStroke *dialog = dynamic_cast<Dialog::FillAndStroke *>(
783             _desktop->_dlg_mgr->getDialog("FillAndStroke")))
784         dialog->showPageStrokePaint();
787 bool 
788 SelectedStyle::on_fill_click(GdkEventButton *event)
790     if (event->button == 1) { // click, open fill&stroke
792         if (Dialog::FillAndStroke *dialog = dynamic_cast<Dialog::FillAndStroke *>(
793                 _desktop->_dlg_mgr->getDialog("FillAndStroke")))
794             dialog->showPageFill();
796     } else if (event->button == 3) { // right-click, popup menu
797         _popup[SS_FILL].popup(event->button, event->time);
798     } else if (event->button == 2) { // middle click, toggle none/lastcolor
799         if (_mode[SS_FILL] == SS_NONE) {
800             on_fill_lastused();
801         } else {
802             on_fill_remove();
803         }
804     }
805     return true;
808 bool 
809 SelectedStyle::on_stroke_click(GdkEventButton *event)
811     if (event->button == 1) { // click, open fill&stroke
812         if (Dialog::FillAndStroke *dialog = dynamic_cast<Dialog::FillAndStroke *>(
813                 _desktop->_dlg_mgr->getDialog("FillAndStroke")))
814             dialog->showPageStrokePaint();
815     } else if (event->button == 3) { // right-click, popup menu
816         _popup[SS_STROKE].popup(event->button, event->time);
817     } else if (event->button == 2) { // middle click, toggle none/lastcolor
818         if (_mode[SS_STROKE] == SS_NONE) {
819             on_stroke_lastused();
820         } else {
821             on_stroke_remove();
822         }
823     }
824     return true;
827 bool 
828 SelectedStyle::on_sw_click(GdkEventButton *event)
830     if (event->button == 1) { // click, open fill&stroke
831         if (Dialog::FillAndStroke *dialog = dynamic_cast<Dialog::FillAndStroke *>(
832                 _desktop->_dlg_mgr->getDialog("FillAndStroke")))
833             dialog->showPageStrokeStyle();
834     } else if (event->button == 3) { // right-click, popup menu
835         _popup_sw.popup(event->button, event->time);
836     } else if (event->button == 2) { // middle click, toggle none/lastwidth?
837         //
838     }
839     return true;
842 bool 
843 SelectedStyle::on_opacity_click(GdkEventButton *event)
845     if (event->button == 2) { // middle click
846         const char* opacity = _opacity_sb.get_value() < 50? "0.5" : (_opacity_sb.get_value() == 100? "0" : "1");
847         SPCSSAttr *css = sp_repr_css_attr_new ();
848         sp_repr_css_set_property (css, "opacity", opacity);
849         sp_desktop_set_style (_desktop, css);
850         sp_repr_css_attr_unref (css);
851         sp_document_done (sp_desktop_document (_desktop), SP_VERB_DIALOG_FILL_STROKE,
852                       _("Change opacity"));
853         return true;
854     }
856     return false;
859 void SelectedStyle::on_popup_px() {
860     _sw_unit = (SPUnit *) &(sp_unit_get_by_id(SP_UNIT_PX));
861     update();
863 void SelectedStyle::on_popup_pt() {
864     _sw_unit = (SPUnit *) &(sp_unit_get_by_id(SP_UNIT_PT));
865     update();
867 void SelectedStyle::on_popup_mm() {
868     _sw_unit = (SPUnit *) &(sp_unit_get_by_id(SP_UNIT_MM));
869     update();
872 void SelectedStyle::on_popup_preset(int i) {
873     SPCSSAttr *css = sp_repr_css_attr_new ();
874     gdouble w;
875     if (_sw_unit) {
876         w = sp_units_get_pixels (_sw_presets[i], *_sw_unit);
877     } else {
878         w = _sw_presets[i];
879     }
880     Inkscape::CSSOStringStream os;
881     os << w;
882     sp_repr_css_set_property (css, "stroke-width", os.str().c_str());
883     // FIXME: update dash patterns!
884     sp_desktop_set_style (_desktop, css, true);
885     sp_repr_css_attr_unref (css);
886     sp_document_done (sp_desktop_document(_desktop), SP_VERB_DIALOG_SWATCHES,
887                       _("Change stroke width"));
890 void
891 SelectedStyle::update()
893     if (_desktop == NULL)
894         return;
896     // create temporary style
897     SPStyle *query = sp_style_new (sp_desktop_document(_desktop));
899     for (int i = SS_FILL; i <= SS_STROKE; i++) {
900         Gtk::EventBox *place = (i == SS_FILL)? &_fill_place : &_stroke_place;
901         Gtk::EventBox *flag_place = (i == SS_FILL)? &_fill_flag_place : &_stroke_flag_place;
903         place->remove();
904         flag_place->remove();
906         _tooltips.unset_tip(*place);
907         _tooltips.unset_tip(*flag_place);
909         _mode[i] = SS_NA;
910         _paintserver_id[i].clear();
912         _popup_copy[i].set_sensitive(false);
914         // query style from desktop. This returns a result flag and fills query with the style of subselection, if any, or selection
915         int result = sp_desktop_query_style (_desktop, query, 
916                                              (i == SS_FILL)? QUERY_STYLE_PROPERTY_FILL : QUERY_STYLE_PROPERTY_STROKE);
917         switch (result) {
918         case QUERY_STYLE_NOTHING:
919             place->add(_na[i]);
920             _tooltips.set_tip(*place, __na[i]);
921             _mode[i] = SS_NA;
922             if ( _dropEnabled[i] ) {
923                 gtk_drag_dest_unset( GTK_WIDGET((i==SS_FILL) ? _fill_place.gobj():_stroke_place.gobj()) );
924                 _dropEnabled[i] = false;
925             }
926             break;
927         case QUERY_STYLE_SINGLE:
928         case QUERY_STYLE_MULTIPLE_AVERAGED:
929         case QUERY_STYLE_MULTIPLE_SAME: 
930             if ( !_dropEnabled[i] ) {
931                 gtk_drag_dest_set( GTK_WIDGET( (i==SS_FILL) ? _fill_place.gobj():_stroke_place.gobj()),
932                                    GTK_DEST_DEFAULT_ALL,
933                                    ui_drop_target_entries,
934                                    nui_drop_target_entries,
935                                    GdkDragAction(GDK_ACTION_COPY | GDK_ACTION_MOVE) );
936                 _dropEnabled[i] = true;
937             }
938             SPIPaint *paint;
939             if (i == SS_FILL) {
940                 paint = &(query->fill);
941             } else {
942                 paint = &(query->stroke);
943             }
944             if (paint->set && paint->isPaintserver()) {
945                 SPPaintServer *server = (i == SS_FILL)? SP_STYLE_FILL_SERVER (query) : SP_STYLE_STROKE_SERVER (query);
946                 if ( server ) {
947                     Inkscape::XML::Node *srepr = SP_OBJECT_REPR(server);
948                     _paintserver_id[i] += "url(#";
949                     _paintserver_id[i] += srepr->attribute("id");
950                     _paintserver_id[i] += ")";
952                     if (SP_IS_LINEARGRADIENT (server)) {
953                         SPGradient *vector = sp_gradient_get_vector(SP_GRADIENT(server), false);
954                         sp_gradient_image_set_gradient ((SPGradientImage *) _gradient_preview_l[i], vector);
955                         place->add(_gradient_box_l[i]);
956                         _tooltips.set_tip(*place, __lgradient[i]);
957                         _mode[i] = SS_LGRADIENT;
958                     } else if (SP_IS_RADIALGRADIENT (server)) {
959                         SPGradient *vector = sp_gradient_get_vector(SP_GRADIENT(server), false);
960                         sp_gradient_image_set_gradient ((SPGradientImage *) _gradient_preview_r[i], vector);
961                         place->add(_gradient_box_r[i]);
962                         _tooltips.set_tip(*place, __rgradient[i]);
963                         _mode[i] = SS_RGRADIENT;
964                     } else if (SP_IS_PATTERN (server)) {
965                         place->add(_pattern[i]);
966                         _tooltips.set_tip(*place, __pattern[i]);
967                         _mode[i] = SS_PATTERN;
968                     }
969                 } else {
970                     g_warning ("file %s: line %d: Unknown paint server", __FILE__, __LINE__);
971                 }
972             } else if (paint->set && paint->isColor()) {
973                 guint32 color = paint->value.color.toRGBA32(
974                                      SP_SCALE24_TO_FLOAT ((i == SS_FILL)? query->fill_opacity.value : query->stroke_opacity.value));
975                 _lastselected[i] = _thisselected[i];
976                 _thisselected[i] = color | 0xff; // only color, opacity === 1
977                 ((Inkscape::UI::Widget::ColorPreview*)_color_preview[i])->setRgba32 (color);
978                 _color_preview[i]->show_all();
979                 place->add(*_color_preview[i]);
980                 gchar c_string[64];
981                 g_snprintf (c_string, 64, "%06x/%.3g", color >> 8, SP_RGBA32_A_F(color));
982                 _tooltips.set_tip(*place, __color[i] + ": " + c_string + _(", drag to adjust"));
983                 _mode[i] = SS_COLOR;
984                 _popup_copy[i].set_sensitive(true);
986             } else if (paint->set && paint->isNone()) {
987                 place->add(_none[i]);
988                 _tooltips.set_tip(*place, __none[i]);
989                 _mode[i] = SS_NONE;
990             } else if (!paint->set) {
991                 place->add(_unset[i]);
992                 _tooltips.set_tip(*place, __unset[i]);
993                 _mode[i] = SS_UNSET;
994             }
995             if (result == QUERY_STYLE_MULTIPLE_AVERAGED) {
996                 flag_place->add(_averaged[i]);
997                 _tooltips.set_tip(*flag_place, __averaged[i]);
998             } else if (result == QUERY_STYLE_MULTIPLE_SAME) {
999                 flag_place->add(_multiple[i]);
1000                 _tooltips.set_tip(*flag_place, __multiple[i]);
1001             }
1002             break;
1003         case QUERY_STYLE_MULTIPLE_DIFFERENT:
1004             place->add(_many[i]);
1005             _tooltips.set_tip(*place, __many[i]);
1006             _mode[i] = SS_MANY;
1007             break;
1008         default:
1009             break;
1010         }
1011     }
1013 // Now query opacity
1014     _tooltips.unset_tip(_opacity_place);
1015     _tooltips.unset_tip(_opacity_sb);
1017     int result = sp_desktop_query_style (_desktop, query, QUERY_STYLE_PROPERTY_MASTEROPACITY);
1019     switch (result) {
1020     case QUERY_STYLE_NOTHING:
1021         _tooltips.set_tip(_opacity_place, _("Nothing selected"));
1022         _tooltips.set_tip(_opacity_sb, _("Nothing selected"));
1023         _opacity_sb.set_sensitive(false);
1024         break;
1025     case QUERY_STYLE_SINGLE:
1026     case QUERY_STYLE_MULTIPLE_AVERAGED:
1027     case QUERY_STYLE_MULTIPLE_SAME:
1028         _tooltips.set_tip(_opacity_place, _("Opacity, %"));
1029         _tooltips.set_tip(_opacity_sb, _("Opacity, %"));
1030         if (_opacity_blocked) break;
1031         _opacity_blocked = true;
1032         _opacity_sb.set_sensitive(true);
1033         _opacity_adjustment.set_value(SP_SCALE24_TO_FLOAT(query->opacity.value) * 100);
1034         _opacity_blocked = false;
1035         break;
1036     }
1038 // Now query stroke_width
1039     int result_sw = sp_desktop_query_style (_desktop, query, QUERY_STYLE_PROPERTY_STROKEWIDTH);
1040     switch (result_sw) {
1041     case QUERY_STYLE_NOTHING:
1042         _stroke_width.set_markup("");
1043         break;
1044     case QUERY_STYLE_SINGLE:
1045     case QUERY_STYLE_MULTIPLE_AVERAGED:
1046     case QUERY_STYLE_MULTIPLE_SAME: 
1047     {
1048         double w;
1049         if (_sw_unit) {
1050             w = sp_pixels_get_units(query->stroke_width.computed, *_sw_unit);
1051         } else {
1052             w = query->stroke_width.computed;
1053         }
1054         {
1055             gchar *str = g_strdup_printf(" %.3g", w);
1056             _stroke_width.set_markup(str);
1057             g_free (str);
1058         }
1059         {
1060             gchar *str = g_strdup_printf(_("Stroke width: %.5g%s%s"), 
1061                                          w, 
1062                                          _sw_unit? sp_unit_get_abbreviation(_sw_unit) : "px", 
1063                                          (result_sw == QUERY_STYLE_MULTIPLE_AVERAGED)?
1064                                          _(" (averaged)") : "");
1065             _tooltips.set_tip(_stroke_width_place, str);
1066             g_free (str);
1067         }
1068         break;
1069     }
1070     default:
1071         break;
1072     }
1074     sp_style_unref(query);
1077 void SelectedStyle::opacity_0(void) {_opacity_sb.set_value(0);}
1078 void SelectedStyle::opacity_025(void) {_opacity_sb.set_value(25);}
1079 void SelectedStyle::opacity_05(void) {_opacity_sb.set_value(50);}
1080 void SelectedStyle::opacity_075(void) {_opacity_sb.set_value(75);}
1081 void SelectedStyle::opacity_1(void) {_opacity_sb.set_value(100);}
1083 void SelectedStyle::on_opacity_menu (Gtk::Menu *menu) {
1085     Glib::ListHandle<Gtk::Widget *> children = menu->get_children();
1086     for (Glib::ListHandle<Gtk::Widget *>::iterator iter = children.begin(); iter != children.end(); iter++) {
1087         menu->remove(*(*iter));
1088     }
1090     {
1091         Gtk::MenuItem *item = new Gtk::MenuItem;
1092         item->add(*(new Gtk::Label(_("0 (transparent)"), 0, 0)));
1093         item->signal_activate().connect(sigc::mem_fun(*this, &SelectedStyle::opacity_0 ));
1094         menu->add(*item);
1095     }
1096     {
1097         Gtk::MenuItem *item = new Gtk::MenuItem;
1098         item->add(*(new Gtk::Label("25%", 0, 0)));
1099         item->signal_activate().connect(sigc::mem_fun(*this, &SelectedStyle::opacity_025 ));
1100         menu->add(*item);
1101     }
1102     {
1103         Gtk::MenuItem *item = new Gtk::MenuItem;
1104         item->add(*(new Gtk::Label("50%", 0, 0)));
1105         item->signal_activate().connect(sigc::mem_fun(*this, &SelectedStyle::opacity_05 ));
1106         menu->add(*item);
1107     }
1108     {
1109         Gtk::MenuItem *item = new Gtk::MenuItem;
1110         item->add(*(new Gtk::Label("75%", 0, 0)));
1111         item->signal_activate().connect(sigc::mem_fun(*this, &SelectedStyle::opacity_075 ));
1112         menu->add(*item);
1113     }
1114     {
1115         Gtk::MenuItem *item = new Gtk::MenuItem;
1116         item->add(*(new Gtk::Label(_("100% (opaque)"), 0, 0)));
1117         item->signal_activate().connect(sigc::mem_fun(*this, &SelectedStyle::opacity_1 ));
1118         menu->add(*item);
1119     }
1121     menu->show_all();
1124 void SelectedStyle::on_opacity_changed () {
1125     if (_opacity_blocked)
1126         return;
1127     _opacity_blocked = true;
1128     SPCSSAttr *css = sp_repr_css_attr_new ();
1129     Inkscape::CSSOStringStream os;
1130     os << CLAMP ((_opacity_adjustment.get_value() / 100), 0.0, 1.0);
1131     sp_repr_css_set_property (css, "opacity", os.str().c_str());
1132     // FIXME: workaround for GTK breakage: display interruptibility sometimes results in GTK
1133     // sending multiple value-changed events. As if when Inkscape interrupts redraw for main loop
1134     // iterations, GTK discovers that this callback hasn't finished yet, and for some weird reason
1135     // decides to add yet another value-changed event to the queue. Totally braindead if you ask
1136     // me. As a result, scrolling the spinbutton once results in runaway change until it hits 1.0
1137     // or 0.0. (And no, this is not a race with ::update, I checked that.)
1138     // Sigh. So we disable interruptibility while we're setting the new value.
1139     sp_canvas_force_full_redraw_after_interruptions(sp_desktop_canvas(_desktop), 0);
1140     sp_desktop_set_style (_desktop, css);
1141     sp_repr_css_attr_unref (css);
1142     sp_document_maybe_done (sp_desktop_document (_desktop), "fillstroke:opacity", SP_VERB_DIALOG_FILL_STROKE,
1143                       _("Change opacity"));
1144     // resume interruptibility
1145     sp_canvas_end_forced_full_redraws(sp_desktop_canvas(_desktop));
1146     spinbutton_defocus(GTK_OBJECT(_opacity_sb.gobj()));
1147     _opacity_blocked = false;
1150 RotateableSwatch::RotateableSwatch(guint mode) {
1151     fillstroke = mode;
1152     startcolor_set = false;
1153     undokey = "ssrot1";
1154     cr = NULL;
1155     cr_set = false;
1158 RotateableSwatch::~RotateableSwatch() {
1161 double
1162 RotateableSwatch::color_adjust(float *hsl, double by, guint32 cc, guint modifier)
1164     sp_color_rgb_to_hsl_floatv (hsl, SP_RGBA32_R_F(cc), SP_RGBA32_G_F(cc), SP_RGBA32_B_F(cc));
1166     double diff = 0;
1167     if (modifier == 2) { // saturation
1168         double old = hsl[1];
1169         if (by > 0) {
1170             hsl[1] += by * (1 - hsl[1]);
1171         } else {
1172             hsl[1] += by * (hsl[1]);
1173         }
1174         diff = hsl[1] - old;
1175     } else if (modifier == 1) { // lightness
1176         double old = hsl[2];
1177         if (by > 0) {
1178             hsl[2] += by * (1 - hsl[2]);
1179         } else {
1180             hsl[2] += by * (hsl[2]);
1181         }
1182         diff = hsl[2] - old;
1183     } else { // hue
1184         double old = hsl[0];
1185         hsl[0] += by/2;
1186         while (hsl[0] < 0) 
1187             hsl[0] += 1;
1188         while (hsl[0] > 1) 
1189             hsl[0] -= 1;
1190         diff = hsl[0] - old;
1191     }
1193     float rgb[3];
1194     sp_color_hsl_to_rgb_floatv (rgb, hsl[0], hsl[1], hsl[2]);
1196     gchar c[64];
1197     sp_svg_write_color (c, sizeof(c),
1198         SP_RGBA32_U_COMPOSE(
1199                 (SP_COLOR_F_TO_U(rgb[0])),
1200                 (SP_COLOR_F_TO_U(rgb[1])),
1201                 (SP_COLOR_F_TO_U(rgb[2])),
1202                 0xff
1203         )
1204     );
1206     SPCSSAttr *css = sp_repr_css_attr_new ();
1207     if (fillstroke == SS_FILL)
1208         sp_repr_css_set_property (css, "fill", c);
1209     else 
1210         sp_repr_css_set_property (css, "stroke", c);
1211     sp_desktop_set_style (parent->getDesktop(), css);
1212     sp_repr_css_attr_unref (css);
1213     return diff;
1216 void
1217 RotateableSwatch::do_motion(double by, guint modifier) {
1218     if (parent->_mode[fillstroke] != SS_COLOR) 
1219         return;
1221     if (!cr_set && modifier != 3) {
1222         GtkWidget *w = GTK_WIDGET(gobj());
1224         GdkBitmap *bitmap = NULL;
1225         GdkBitmap *mask = NULL;
1226         if (modifier == 2) { // saturation
1227             sp_cursor_bitmap_and_mask_from_xpm(&bitmap, &mask, cursor_adj_s_xpm);
1228         } else if (modifier == 1) { // lightness
1229             sp_cursor_bitmap_and_mask_from_xpm(&bitmap, &mask, cursor_adj_l_xpm);
1230         } else { // hue
1231             sp_cursor_bitmap_and_mask_from_xpm(&bitmap, &mask, cursor_adj_h_xpm);
1232         }
1233         if ((bitmap != NULL) && (mask != NULL)) {
1234             cr = gdk_cursor_new_from_pixmap(bitmap, mask,
1235                                             &w->style->black,
1236                                             &w->style->white,
1237                                             16, 16);
1238             g_object_unref (bitmap);
1239             g_object_unref (mask);
1240             gdk_window_set_cursor(w->window, cr);
1241             cr_set = true;
1242         }
1243     }
1245     guint32 cc;
1246     if (!startcolor_set) {
1247         cc = startcolor = parent->_thisselected[fillstroke];
1248         startcolor_set = true;
1249     } else {
1250         cc = startcolor;
1251     }
1253     float hsl[3];
1254     double diff = 0;
1255     if (modifier != 3) {
1256         diff = color_adjust(hsl, by, cc, modifier);
1257     }
1259     if (modifier == 3) { // do nothing
1261     } else if (modifier == 2) { // saturation
1262         sp_document_maybe_done (sp_desktop_document(parent->getDesktop()), undokey, 
1263                                 SP_VERB_DIALOG_FILL_STROKE, ("Adjust saturation"));
1264         double ch = hsl[1];
1265         parent->getDesktop()->event_context->_message_context->setF(Inkscape::IMMEDIATE_MESSAGE, _("Adusting <b>saturation</b>: was %.3g, now <b>%.3g</b> (diff %.3g); without modifiers to adjust hue, with <b>Ctrl</b> to adjust lightness"), ch - diff, ch, diff);
1267     } else if (modifier == 1) { // lightness
1268         sp_document_maybe_done (sp_desktop_document(parent->getDesktop()), undokey, 
1269                                 SP_VERB_DIALOG_FILL_STROKE, ("Adjust lightness"));
1270         double ch = hsl[2];
1271         parent->getDesktop()->event_context->_message_context->setF(Inkscape::IMMEDIATE_MESSAGE, _("Adusting <b>lightness</b>: was %.3g, now <b>%.3g</b> (diff %.3g); without modifiers to adjust hue, with <b>Shift</b> to adjust saturation"), ch - diff, ch, diff);
1273     } else { // hue
1274         sp_document_maybe_done (sp_desktop_document(parent->getDesktop()), undokey, 
1275                                 SP_VERB_DIALOG_FILL_STROKE, ("Adjust hue"));
1276         double ch = hsl[0];
1277         parent->getDesktop()->event_context->_message_context->setF(Inkscape::IMMEDIATE_MESSAGE, _("Adusting <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);
1278     }
1281 void
1282 RotateableSwatch::do_release(double by, guint modifier) {
1283     if (parent->_mode[fillstroke] != SS_COLOR) 
1284         return;
1286     float hsl[3];
1287     if (modifier != 3) {
1288         color_adjust(hsl, by, startcolor, modifier);
1289     }
1291     if (cr_set) {
1292         GtkWidget *w = GTK_WIDGET(gobj());
1293         gdk_window_set_cursor(w->window, NULL);
1294         if (cr) {
1295            gdk_cursor_unref (cr);
1296            cr = NULL;
1297         }
1298         cr_set = false;
1299     }
1301     if (modifier == 3) { // nothing
1302     } else if (modifier == 2) { // saturation
1303         sp_document_maybe_done (sp_desktop_document(parent->getDesktop()), undokey, 
1304                                 SP_VERB_DIALOG_FILL_STROKE, ("Adjust saturation"));
1306     } else if (modifier == 1) { // lightness
1307         sp_document_maybe_done (sp_desktop_document(parent->getDesktop()), undokey, 
1308                                 SP_VERB_DIALOG_FILL_STROKE, ("Adjust lightness"));
1310     } else { // hue
1311         sp_document_maybe_done (sp_desktop_document(parent->getDesktop()), undokey, 
1312                                 SP_VERB_DIALOG_FILL_STROKE, ("Adjust hue"));
1313     }
1315     if (!strcmp(undokey, "ssrot1")) {
1316         undokey = "ssrot2";
1317     } else {
1318         undokey = "ssrot1";
1319     }
1321     parent->getDesktop()->event_context->_message_context->clear();
1322     startcolor_set = false;
1326 } // namespace Widget
1327 } // namespace UI
1328 } // namespace Inkscape
1330 /* 
1331   Local Variables:
1332   mode:c++
1333   c-file-style:"stroustrup"
1334   c-file-offsets:((innamespace . 0)(inline-open . 0))
1335   indent-tabs-mode:nil
1336   fill-column:99
1337   End:
1338 */
1339 // vim: filetype=c++:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 :