Code

d44431b080430c822a419da6499dd036045e8391
[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 "dialogs/object-properties.h"
31 #include "xml/repr.h"
32 #include "document.h"
33 #include "widgets/widget-sizes.h"
34 #include "widgets/spinbutton-events.h"
35 #include "svg/svg-color.h"
36 #include "svg/css-ostringstream.h"
37 #include "helper/units.h"
38 #include "verbs.h"
39 #include <display/sp-canvas.h>
41 static gdouble const _sw_presets[]     = { 32 ,  16 ,  10 ,  8 ,  6 ,  4 ,  3 ,  2 ,  1.5 ,  1 ,  0.75 ,  0.5 ,  0.25 ,  0.1 };
42 static gchar* const _sw_presets_str[] = {"32", "16", "10", "8", "6", "4", "3", "2", "1.5", "1", "0.75", "0.5", "0.25", "0.1"};
44 static void 
45 ss_selection_changed (Inkscape::Selection *, gpointer data)
46 {
47     Inkscape::UI::Widget::SelectedStyle *ss = (Inkscape::UI::Widget::SelectedStyle *) data;
48     ss->update();
49 }
51 static void
52 ss_selection_modified (Inkscape::Selection *selection, guint flags, gpointer data)
53 {
54     ss_selection_changed (selection, data);
55 }
57 static void
58 ss_subselection_changed (gpointer dragger, gpointer data)
59 {
60     ss_selection_changed (NULL, data);
61 }
63 namespace Inkscape {
64 namespace UI {
65 namespace Widget {
68 typedef struct {
69     SelectedStyle* parent;
70     int item;
71 } DropTracker;
73 /* Drag and Drop */
74 typedef enum {
75     APP_X_COLOR
76 } ui_drop_target_info;
78 static GtkTargetEntry ui_drop_target_entries [] = {
79     {"application/x-color", 0, APP_X_COLOR}
80 };
82 #define ENTRIES_SIZE(n) sizeof(n)/sizeof(n[0])
83 static guint nui_drop_target_entries = ENTRIES_SIZE(ui_drop_target_entries);
86 SelectedStyle::SelectedStyle(bool layout)
87     : _desktop (NULL),
89       _table(2, 6),
90       _fill_label (_("F:")),
91       _stroke_label (_("S:")),
92       _opacity_label (_("O:")),
93       _fill_place (),
94       _stroke_place (),
96       _fill_flag_place (),
97       _stroke_flag_place (),
99       _opacity_place (),
100       _opacity_adjustment (100, 0.0, 100, 1.0, 10.0),
101       _opacity_sb (0.02, 0),
103       _stroke (),
104       _stroke_width (""),
106       _opacity_blocked (false),
108       _popup_px(_sw_group),
109       _popup_pt(_sw_group),
110       _popup_mm(_sw_group),
112       _sw_unit(NULL),
114       _tooltips ()
117     _drop[0] = _drop[1] = 0;
118     _dropEnabled[0] = _dropEnabled[1] = false;
119     
120     _fill_label.set_alignment(0.0, 0.5);
121     _fill_label.set_padding(0, 0);
122     _stroke_label.set_alignment(0.0, 0.5);
123     _stroke_label.set_padding(0, 0);
124     _opacity_label.set_alignment(0.0, 0.5);
125     _opacity_label.set_padding(0, 0);
127     _table.set_col_spacings (2);
128     _table.set_row_spacings (0);
130     for (int i = SS_FILL; i <= SS_STROKE; i++) {
132         _na[i].set_markup (_("N/A"));
133         sp_set_font_size_smaller (GTK_WIDGET(_na[i].gobj()));
134         _na[i].show_all();
135         __na[i] = (_("Nothing selected"));
137         _none[i].set_markup (_("None"));
138         sp_set_font_size_smaller (GTK_WIDGET(_none[i].gobj()));
139         _none[i].show_all();
140         __none[i] = (i == SS_FILL)? (_("No fill")) : (_("No stroke"));
142         _pattern[i].set_markup (_("Pattern"));
143         sp_set_font_size_smaller (GTK_WIDGET(_pattern[i].gobj()));
144         _pattern[i].show_all();
145         __pattern[i] = (i == SS_FILL)? (_("Pattern fill")) : (_("Pattern stroke"));
147         _lgradient[i].set_markup (_("L Gradient"));
148         sp_set_font_size_smaller (GTK_WIDGET(_lgradient[i].gobj()));
149         _lgradient[i].show_all();
150         __lgradient[i] = (i == SS_FILL)? (_("Linear gradient fill")) : (_("Linear gradient stroke"));
152         _rgradient[i].set_markup (_("R Gradient"));
153         sp_set_font_size_smaller (GTK_WIDGET(_rgradient[i].gobj()));
154         _rgradient[i].show_all();
155         __rgradient[i] = (i == SS_FILL)? (_("Radial gradient fill")) : (_("Radial gradient stroke"));
157         _many[i].set_markup (_("Different"));
158         sp_set_font_size_smaller (GTK_WIDGET(_many[i].gobj()));
159         _many[i].show_all();
160         __many[i] = (i == SS_FILL)? (_("Different fills")) : (_("Different strokes"));
162         _unset[i].set_markup (_("Unset"));
163         sp_set_font_size_smaller (GTK_WIDGET(_unset[i].gobj()));
164         _unset[i].show_all();
165         __unset[i] = (i == SS_FILL)? (_("Unset fill")) : (_("Unset stroke"));
167         _color_preview[i] = new Inkscape::UI::Widget::ColorPreview (0);
168         __color[i] = (i == SS_FILL)? (_("Flat color fill")) : (_("Flat color stroke"));
170         // TRANSLATOR COMMENT: A means "Averaged"
171         _averaged[i].set_markup (_("<b>a</b>"));
172         sp_set_font_size_smaller (GTK_WIDGET(_averaged[i].gobj()));
173         _averaged[i].show_all();
174         __averaged[i] = (i == SS_FILL)? (_("Fill is averaged over selected objects")) : (_("Stroke is averaged over selected objects"));
176         // TRANSLATOR COMMENT: M means "Multiple"
177         _multiple[i].set_markup (_("<b>m</b>"));
178         sp_set_font_size_smaller (GTK_WIDGET(_multiple[i].gobj()));
179         _multiple[i].show_all();
180         __multiple[i] = (i == SS_FILL)? (_("Multiple selected objects have the same fill")) : (_("Multiple selected objects have the same stroke"));
182         _popup_edit[i].add(*(new Gtk::Label((i == SS_FILL)? _("Edit fill...") : _("Edit stroke..."), 0.0, 0.5)));
183         _popup_edit[i].signal_activate().connect(sigc::mem_fun(*this, 
184                                (i == SS_FILL)? &SelectedStyle::on_fill_edit : &SelectedStyle::on_stroke_edit ));
186         _popup_lastused[i].add(*(new Gtk::Label(_("Last set color"), 0.0, 0.5)));
187         _popup_lastused[i].signal_activate().connect(sigc::mem_fun(*this, 
188                                (i == SS_FILL)? &SelectedStyle::on_fill_lastused : &SelectedStyle::on_stroke_lastused ));
190         _popup_lastselected[i].add(*(new Gtk::Label(_("Last selected color"), 0.0, 0.5)));
191         _popup_lastselected[i].signal_activate().connect(sigc::mem_fun(*this, 
192                                (i == SS_FILL)? &SelectedStyle::on_fill_lastselected : &SelectedStyle::on_stroke_lastselected ));
194         _popup_invert[i].add(*(new Gtk::Label(_("Invert"), 0.0, 0.5)));
195         _popup_invert[i].signal_activate().connect(sigc::mem_fun(*this, 
196                                (i == SS_FILL)? &SelectedStyle::on_fill_invert : &SelectedStyle::on_stroke_invert ));
198         _popup_white[i].add(*(new Gtk::Label(_("White"), 0.0, 0.5)));
199         _popup_white[i].signal_activate().connect(sigc::mem_fun(*this, 
200                                (i == SS_FILL)? &SelectedStyle::on_fill_white : &SelectedStyle::on_stroke_white ));
202         _popup_black[i].add(*(new Gtk::Label(_("Black"), 0.0, 0.5)));
203         _popup_black[i].signal_activate().connect(sigc::mem_fun(*this, 
204                                (i == SS_FILL)? &SelectedStyle::on_fill_black : &SelectedStyle::on_stroke_black ));
206         _popup_copy[i].add(*(new Gtk::Label(_("Copy color"), 0.0, 0.5)));
207         _popup_copy[i].signal_activate().connect(sigc::mem_fun(*this, 
208                                (i == SS_FILL)? &SelectedStyle::on_fill_copy : &SelectedStyle::on_stroke_copy ));
210         _popup_paste[i].add(*(new Gtk::Label(_("Paste color"), 0.0, 0.5)));
211         _popup_paste[i].signal_activate().connect(sigc::mem_fun(*this, 
212                                (i == SS_FILL)? &SelectedStyle::on_fill_paste : &SelectedStyle::on_stroke_paste ));
214         _popup_swap[i].add(*(new Gtk::Label(_("Swap fill and stroke"), 0.0, 0.5)));
215         _popup_swap[i].signal_activate().connect(sigc::mem_fun(*this, 
216                                &SelectedStyle::on_fillstroke_swap));
218         _popup_opaque[i].add(*(new Gtk::Label((i == SS_FILL)? _("Make fill opaque") : _("Make stroke opaque"), 0.0, 0.5)));
219         _popup_opaque[i].signal_activate().connect(sigc::mem_fun(*this, 
220                                (i == SS_FILL)? &SelectedStyle::on_fill_opaque : &SelectedStyle::on_stroke_opaque ));
222         //TRANSLATORS COMMENT: unset is a verb here
223         _popup_unset[i].add(*(new Gtk::Label((i == SS_FILL)? _("Unset fill") : _("Unset stroke"), 0.0, 0.5)));
224         _popup_unset[i].signal_activate().connect(sigc::mem_fun(*this, 
225                                (i == SS_FILL)? &SelectedStyle::on_fill_unset : &SelectedStyle::on_stroke_unset ));
227         _popup_remove[i].add(*(new Gtk::Label((i == SS_FILL)? _("Remove fill") : _("Remove stroke"), 0.0, 0.5)));
228         _popup_remove[i].signal_activate().connect(sigc::mem_fun(*this, 
229                                (i == SS_FILL)? &SelectedStyle::on_fill_remove : &SelectedStyle::on_stroke_remove ));
231         _popup[i].attach(_popup_edit[i], 0,1, 0,1);
232           _popup[i].attach(*(new Gtk::SeparatorMenuItem()), 0,1, 1,2);
233         _popup[i].attach(_popup_lastused[i], 0,1, 2,3);
234         _popup[i].attach(_popup_lastselected[i], 0,1, 3,4);
235           _popup[i].attach(*(new Gtk::SeparatorMenuItem()), 0,1, 4,5);
236         _popup[i].attach(_popup_invert[i], 0,1, 5,6);
237           _popup[i].attach(*(new Gtk::SeparatorMenuItem()), 0,1, 6,7);
238         _popup[i].attach(_popup_white[i], 0,1, 7,8);
239         _popup[i].attach(_popup_black[i], 0,1, 8,9);
240           _popup[i].attach(*(new Gtk::SeparatorMenuItem()), 0,1, 9,10);
241         _popup[i].attach(_popup_copy[i], 0,1, 10,11);
242         _popup_copy[i].set_sensitive(false);
243         _popup[i].attach(_popup_paste[i], 0,1, 11,12);
244         _popup[i].attach(_popup_swap[i], 0,1, 12,13);
245           _popup[i].attach(*(new Gtk::SeparatorMenuItem()), 0,1, 13,14); 
246         _popup[i].attach(_popup_opaque[i], 0,1, 14,15);
247         _popup[i].attach(_popup_unset[i], 0,1, 15,16);
248         _popup[i].attach(_popup_remove[i], 0,1, 16,17);
249         _popup[i].show_all();
251         _mode[i] = SS_NA;
252     }
254     {
255         _popup_px.add(*(new Gtk::Label(_("px"), 0.0, 0.5)));
256         _popup_px.signal_activate().connect(sigc::mem_fun(*this, &SelectedStyle::on_popup_px));
257         _popup_sw.attach(_popup_px, 0,1, 0,1);
259         _popup_pt.add(*(new Gtk::Label(_("pt"), 0.0, 0.5)));
260         _popup_pt.signal_activate().connect(sigc::mem_fun(*this, &SelectedStyle::on_popup_pt));
261         _popup_sw.attach(_popup_pt, 0,1, 1,2);
263         _popup_mm.add(*(new Gtk::Label(_("mm"), 0.0, 0.5)));
264         _popup_mm.signal_activate().connect(sigc::mem_fun(*this, &SelectedStyle::on_popup_mm));
265         _popup_sw.attach(_popup_mm, 0,1, 2,3);
267         _popup_sw.attach(*(new Gtk::SeparatorMenuItem()), 0,1, 3,4);
269         for (guint i = 0; i < G_N_ELEMENTS(_sw_presets_str); ++i) {
270             Gtk::MenuItem *mi = Gtk::manage(new Gtk::MenuItem());
271             mi->add(*(new Gtk::Label(_sw_presets_str[i], 0.0, 0.5)));
272             mi->signal_activate().connect(sigc::bind<int>(sigc::mem_fun(*this, &SelectedStyle::on_popup_preset), i));
273             _popup_sw.attach(*mi, 0,1, 4+i, 5+i);
274         }
276         guint i = G_N_ELEMENTS(_sw_presets_str) + 5;
278         _popup_sw.attach(*(new Gtk::SeparatorMenuItem()), 0,1, i,i+1);
280         _popup_sw_remove.add(*(new Gtk::Label(_("Remove"), 0.0, 0.5)));
281         _popup_sw_remove.signal_activate().connect(sigc::mem_fun(*this, &SelectedStyle::on_stroke_remove));
282         _popup_sw.attach(_popup_sw_remove, 0,1, i+1,i+2);
284         _popup_sw.show_all();
285     }
287     _fill_place.signal_button_press_event().connect(sigc::mem_fun(*this, &SelectedStyle::on_fill_click));
288     _stroke_place.signal_button_press_event().connect(sigc::mem_fun(*this, &SelectedStyle::on_stroke_click));
289     _opacity_place.signal_button_press_event().connect(sigc::mem_fun(*this, &SelectedStyle::on_opacity_click));
290     _stroke_width_place.signal_button_press_event().connect(sigc::mem_fun(*this, &SelectedStyle::on_sw_click));
292     _opacity_sb.signal_populate_popup().connect(sigc::mem_fun(*this, &SelectedStyle::on_opacity_menu));
293     _opacity_sb.signal_value_changed().connect(sigc::mem_fun(*this, &SelectedStyle::on_opacity_changed));
295     _fill_place.add(_na[SS_FILL]);
296     _tooltips.set_tip(_fill_place, __na[SS_FILL]);
298     _stroke_place.add(_na[SS_STROKE]);
299     _tooltips.set_tip(_stroke_place, __na[SS_STROKE]);
301     _stroke.pack_start(_stroke_place);
302     _stroke_width_place.add(_stroke_width);
303     _stroke.pack_start(_stroke_width_place, Gtk::PACK_SHRINK);
305     _opacity_sb.set_adjustment(_opacity_adjustment);
306     sp_set_font_size_smaller (GTK_WIDGET(_opacity_sb.gobj()));
307     _opacity_sb.set_size_request (SELECTED_STYLE_SB_WIDTH, -1);
308     _opacity_sb.set_sensitive (false);
310     _table.attach(_fill_label, 0,1, 0,1, Gtk::SHRINK, Gtk::SHRINK);
311     _table.attach(_stroke_label, 0,1, 1,2, Gtk::SHRINK, Gtk::SHRINK);
313     _table.attach(_fill_flag_place, 1,2, 0,1, Gtk::SHRINK, Gtk::SHRINK);
314     _table.attach(_stroke_flag_place, 1,2, 1,2, Gtk::SHRINK, Gtk::SHRINK);
316     _table.attach(_fill_place, 2,3, 0,1);
317     _table.attach(_stroke, 2,3, 1,2);
319     _opacity_place.add(_opacity_label);
320     _table.attach(_opacity_place, 4,5, 0,2, Gtk::SHRINK, Gtk::SHRINK);
321     _table.attach(_opacity_sb, 5,6, 0,2, Gtk::SHRINK, Gtk::SHRINK);
323     pack_start(_table, true, true, 2);
325     set_size_request (SELECTED_STYLE_WIDTH, -1);
327     sp_set_font_size_smaller (GTK_WIDGET(_opacity_label.gobj()));
328     sp_set_font_size_smaller (GTK_WIDGET(_opacity_sb.gobj()));
329     sp_set_font_size_smaller (GTK_WIDGET(_fill_place.gobj()));
330     sp_set_font_size_smaller (GTK_WIDGET(_fill_flag_place.gobj()));
331     sp_set_font_size_smaller (GTK_WIDGET(_stroke_place.gobj()));
332     sp_set_font_size_smaller (GTK_WIDGET(_stroke_flag_place.gobj()));
333     sp_set_font_size_smaller (GTK_WIDGET(_stroke_width.gobj()));
334     sp_set_font_size_smaller (GTK_WIDGET(_fill_label.gobj()));
335     sp_set_font_size_smaller (GTK_WIDGET(_stroke_label.gobj()));
337     _drop[SS_FILL] = new DropTracker();
338     ((DropTracker*)_drop[SS_FILL])->parent = this;
339     ((DropTracker*)_drop[SS_FILL])->item = SS_FILL;
341     _drop[SS_STROKE] = new DropTracker();
342     ((DropTracker*)_drop[SS_STROKE])->parent = this;
343     ((DropTracker*)_drop[SS_STROKE])->item = SS_STROKE;
345     g_signal_connect(_stroke_place.gobj(),
346                      "drag_data_received",
347                      G_CALLBACK(dragDataReceived),
348                      _drop[SS_STROKE]);
350     g_signal_connect(_fill_place.gobj(),
351                      "drag_data_received",
352                      G_CALLBACK(dragDataReceived),
353                      _drop[SS_FILL]);
356 SelectedStyle::~SelectedStyle()
358     selection_changed_connection->disconnect();
359     delete selection_changed_connection;
360     selection_modified_connection->disconnect();
361     delete selection_modified_connection;
362     subselection_changed_connection->disconnect();
363     delete subselection_changed_connection;
365     for (int i = SS_FILL; i <= SS_STROKE; i++) {
366         delete _color_preview[i];
367     }
369     delete (DropTracker*)_drop[SS_FILL];
370     delete (DropTracker*)_drop[SS_STROKE];
373 void
374 SelectedStyle::setDesktop(SPDesktop *desktop)
376     _desktop = desktop;
377     gtk_object_set_data (GTK_OBJECT(_opacity_sb.gobj()), "dtw", _desktop->canvas);
379     Inkscape::Selection *selection = sp_desktop_selection (desktop);
381     selection_changed_connection = new sigc::connection (selection->connectChanged(
382         sigc::bind (
383             sigc::ptr_fun(&ss_selection_changed),
384             this )
385     ));
386     selection_modified_connection = new sigc::connection (selection->connectModified(
387         sigc::bind (
388             sigc::ptr_fun(&ss_selection_modified),
389             this )
390     ));
391     subselection_changed_connection = new sigc::connection (desktop->connectToolSubselectionChanged(
392         sigc::bind (
393             sigc::ptr_fun(&ss_subselection_changed),
394             this )
395     ));
397     //_sw_unit = (SPUnit *) sp_desktop_namedview(desktop)->doc_units;
400 void SelectedStyle::dragDataReceived( GtkWidget *widget,
401                                       GdkDragContext *drag_context,
402                                       gint x, gint y,
403                                       GtkSelectionData *data,
404                                       guint info,
405                                       guint event_time,
406                                       gpointer user_data )
408     DropTracker* tracker = (DropTracker*)user_data;
410     switch ( (int)tracker->item ) {
411         case SS_FILL:
412         case SS_STROKE:
413         {
414             if ( data->length == 8 ) {
415                 gchar c[64];
416                 // Careful about endian issues.
417                 guint16* dataVals = (guint16*)data->data;
418                 sp_svg_write_color( c, 64,
419                                     SP_RGBA32_U_COMPOSE(
420                                         0x0ff & (dataVals[0] >> 8),
421                                         0x0ff & (dataVals[1] >> 8),
422                                         0x0ff & (dataVals[2] >> 8),
423                                         0xff // can't have transparency in the color itself
424                                         //0x0ff & (data->data[3] >> 8),
425                                         ));
426                 SPCSSAttr *css = sp_repr_css_attr_new();
427                 sp_repr_css_set_property( css, (tracker->item == SS_FILL) ? "fill":"stroke", c );
428                 sp_desktop_set_style( tracker->parent->_desktop, css );
429                 sp_repr_css_attr_unref( css );
430                 sp_document_done( sp_desktop_document(tracker->parent->_desktop) , SP_VERB_NONE, 
431                                   _("Drop color"));
432             }
433         }
434         break;
435     }
438 void SelectedStyle::on_fill_remove() {
439     SPCSSAttr *css = sp_repr_css_attr_new ();
440     sp_repr_css_set_property (css, "fill", "none");
441     sp_desktop_set_style (_desktop, css, true, true); 
442     sp_repr_css_attr_unref (css);
443     sp_document_done (sp_desktop_document(_desktop), SP_VERB_DIALOG_FILL_STROKE,
444                       _("Remove fill"));
447 void SelectedStyle::on_stroke_remove() {
448     SPCSSAttr *css = sp_repr_css_attr_new ();
449     sp_repr_css_set_property (css, "stroke", "none");
450     sp_desktop_set_style (_desktop, css, true, true); 
451     sp_repr_css_attr_unref (css);
452     sp_document_done (sp_desktop_document(_desktop), SP_VERB_DIALOG_FILL_STROKE,
453                       _("Remove stroke"));
456 void SelectedStyle::on_fill_unset() {
457     SPCSSAttr *css = sp_repr_css_attr_new ();
458     sp_repr_css_unset_property (css, "fill");
459     sp_desktop_set_style (_desktop, css, true, true); 
460     sp_repr_css_attr_unref (css);
461     sp_document_done (sp_desktop_document(_desktop), SP_VERB_DIALOG_FILL_STROKE,
462                       _("Unset fill"));
465 void SelectedStyle::on_stroke_unset() {
466     SPCSSAttr *css = sp_repr_css_attr_new ();
467     sp_repr_css_unset_property (css, "stroke");
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                       _("Unset stroke"));
474 void SelectedStyle::on_fill_opaque() {
475     SPCSSAttr *css = sp_repr_css_attr_new ();
476     sp_repr_css_set_property (css, "fill-opacity", "1");
477     sp_desktop_set_style (_desktop, css, true);
478     sp_repr_css_attr_unref (css);
479     sp_document_done (sp_desktop_document(_desktop), SP_VERB_DIALOG_FILL_STROKE,
480                       _("Make fill opaque"));
483 void SelectedStyle::on_stroke_opaque() {
484     SPCSSAttr *css = sp_repr_css_attr_new ();
485     sp_repr_css_set_property (css, "stroke-opacity", "1");
486     sp_desktop_set_style (_desktop, css, true);
487     sp_repr_css_attr_unref (css);
488     sp_document_done (sp_desktop_document(_desktop), SP_VERB_DIALOG_FILL_STROKE,
489                       _("Make fill opaque"));
492 void SelectedStyle::on_fill_lastused() {
493     SPCSSAttr *css = sp_repr_css_attr_new ();
494     guint32 color = sp_desktop_get_color(_desktop, true);
495     gchar c[64];
496     sp_svg_write_color (c, 64, color);
497     sp_repr_css_set_property (css, "fill", c);
498     sp_desktop_set_style (_desktop, css);
499     sp_repr_css_attr_unref (css);
500     sp_document_done (sp_desktop_document(_desktop), SP_VERB_DIALOG_FILL_STROKE, 
501                       _("Apply last set color to fill"));
504 void SelectedStyle::on_stroke_lastused() {
505     SPCSSAttr *css = sp_repr_css_attr_new ();
506     guint32 color = sp_desktop_get_color(_desktop, false);
507     gchar c[64];
508     sp_svg_write_color (c, 64, color);
509     sp_repr_css_set_property (css, "stroke", c);
510     sp_desktop_set_style (_desktop, css);
511     sp_repr_css_attr_unref (css);
512     sp_document_done (sp_desktop_document(_desktop), SP_VERB_DIALOG_FILL_STROKE, 
513                       _("Apply last set color to stroke"));
516 void SelectedStyle::on_fill_lastselected() {
517     SPCSSAttr *css = sp_repr_css_attr_new ();
518     gchar c[64];
519     sp_svg_write_color (c, 64, _lastselected[SS_FILL]);
520     sp_repr_css_set_property (css, "fill", c);
521     sp_desktop_set_style (_desktop, css);
522     sp_repr_css_attr_unref (css);
523     sp_document_done (sp_desktop_document(_desktop), SP_VERB_DIALOG_FILL_STROKE,
524                       _("Apply last selected color to fill"));
527 void SelectedStyle::on_stroke_lastselected() {
528     SPCSSAttr *css = sp_repr_css_attr_new ();
529     gchar c[64];
530     sp_svg_write_color (c, 64, _lastselected[SS_STROKE]);
531     sp_repr_css_set_property (css, "stroke", 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 selected color to stroke"));
538 void SelectedStyle::on_fill_invert() {
539     SPCSSAttr *css = sp_repr_css_attr_new ();
540     guint32 color = _thisselected[SS_FILL];
541     gchar c[64];
542     if (_mode[SS_FILL] != SS_COLOR) return;
543     sp_svg_write_color (c, 64,
544         SP_RGBA32_U_COMPOSE(
545                 (255 - SP_RGBA32_R_U(color)),
546                 (255 - SP_RGBA32_G_U(color)),
547                 (255 - SP_RGBA32_B_U(color)),
548                 SP_RGBA32_A_U(color)
549         )
550     );
551     sp_repr_css_set_property (css, "fill", c);
552     sp_desktop_set_style (_desktop, css);
553     sp_repr_css_attr_unref (css);
554     sp_document_done (sp_desktop_document(_desktop), SP_VERB_DIALOG_FILL_STROKE,
555                       _("Invert fill"));
558 void SelectedStyle::on_stroke_invert() {
559     SPCSSAttr *css = sp_repr_css_attr_new ();
560     guint32 color = _thisselected[SS_STROKE];
561     gchar c[64];
562     if (_mode[SS_STROKE] != SS_COLOR) return;
563     sp_svg_write_color (c, 64,
564         SP_RGBA32_U_COMPOSE(
565                 (255 - SP_RGBA32_R_U(color)),
566                 (255 - SP_RGBA32_G_U(color)),
567                 (255 - SP_RGBA32_B_U(color)),
568                 SP_RGBA32_A_U(color)
569         )
570     );
571     sp_repr_css_set_property (css, "stroke", c);
572     sp_desktop_set_style (_desktop, css);
573     sp_repr_css_attr_unref (css);
574     sp_document_done (sp_desktop_document(_desktop), SP_VERB_DIALOG_FILL_STROKE,
575                       _("Invert stroke"));
576
578 void SelectedStyle::on_fill_white() {
579     SPCSSAttr *css = sp_repr_css_attr_new ();
580     gchar c[64];
581     sp_svg_write_color (c, 64, 0xffffffff);
582     sp_repr_css_set_property (css, "fill", c);
583     sp_repr_css_set_property (css, "fill-opacity", "1");
584     sp_desktop_set_style (_desktop, css);
585     sp_repr_css_attr_unref (css);
586     sp_document_done (sp_desktop_document(_desktop), SP_VERB_DIALOG_FILL_STROKE,
587                       _("White fill"));
590 void SelectedStyle::on_stroke_white() {
591     SPCSSAttr *css = sp_repr_css_attr_new ();
592     gchar c[64];
593     sp_svg_write_color (c, 64, 0xffffffff);
594     sp_repr_css_set_property (css, "stroke", c);
595     sp_repr_css_set_property (css, "stroke-opacity", "1");
596     sp_desktop_set_style (_desktop, css);
597     sp_repr_css_attr_unref (css);
598     sp_document_done (sp_desktop_document(_desktop), SP_VERB_DIALOG_FILL_STROKE, 
599                       _("White stroke"));
602 void SelectedStyle::on_fill_black() {
603     SPCSSAttr *css = sp_repr_css_attr_new ();
604     gchar c[64];
605     sp_svg_write_color (c, 64, 0x000000ff);
606     sp_repr_css_set_property (css, "fill", c);
607     sp_repr_css_set_property (css, "fill-opacity", "1.0");
608     sp_desktop_set_style (_desktop, css);
609     sp_repr_css_attr_unref (css);
610     sp_document_done (sp_desktop_document(_desktop), SP_VERB_DIALOG_FILL_STROKE, 
611                       _("Black fill"));
614 void SelectedStyle::on_stroke_black() {
615     SPCSSAttr *css = sp_repr_css_attr_new ();
616     gchar c[64];
617     sp_svg_write_color (c, 64, 0x000000ff);
618     sp_repr_css_set_property (css, "stroke", c);
619     sp_repr_css_set_property (css, "stroke-opacity", "1.0");
620     sp_desktop_set_style (_desktop, css);
621     sp_repr_css_attr_unref (css);
622     sp_document_done (sp_desktop_document(_desktop), SP_VERB_DIALOG_FILL_STROKE, 
623                       _("Black stroke"));
626 void SelectedStyle::on_fill_copy() {
627     if (_mode[SS_FILL] == SS_COLOR) {
628         gchar c[64];
629         sp_svg_write_color (c, 64, _thisselected[SS_FILL]);
630         Glib::ustring text;
631         text += c;
632         if (!text.empty()) {
633             Glib::RefPtr<Gtk::Clipboard> refClipboard = Gtk::Clipboard::get();
634             refClipboard->set_text(text);
635         }
636     }
639 void SelectedStyle::on_stroke_copy() {
640     if (_mode[SS_STROKE] == SS_COLOR) {
641         gchar c[64];
642         sp_svg_write_color (c, 64, _thisselected[SS_STROKE]);
643         Glib::ustring text;
644         text += c;
645         if (!text.empty()) {
646             Glib::RefPtr<Gtk::Clipboard> refClipboard = Gtk::Clipboard::get();
647             refClipboard->set_text(text);
648         }
649     }
652 void SelectedStyle::on_fill_paste() {
653     Glib::RefPtr<Gtk::Clipboard> refClipboard = Gtk::Clipboard::get();
654     Glib::ustring const text = refClipboard->wait_for_text();
656     if (!text.empty()) {
657         guint32 color = sp_svg_read_color(text.c_str(), 0x000000ff); // impossible value, as SVG color cannot have opacity
658         if (color == 0x000000ff) // failed to parse color string
659             return;
661         SPCSSAttr *css = sp_repr_css_attr_new ();
662         sp_repr_css_set_property (css, "fill", text.c_str());
663         sp_desktop_set_style (_desktop, css);
664         sp_repr_css_attr_unref (css);
665         sp_document_done (sp_desktop_document(_desktop), SP_VERB_DIALOG_FILL_STROKE, 
666                       _("Paste fill"));
667     }
670 void SelectedStyle::on_stroke_paste() {
671     Glib::RefPtr<Gtk::Clipboard> refClipboard = Gtk::Clipboard::get();
672     Glib::ustring const text = refClipboard->wait_for_text();
674     if (!text.empty()) {
675         guint32 color = sp_svg_read_color(text.c_str(), 0x000000ff); // impossible value, as SVG color cannot have opacity
676         if (color == 0x000000ff) // failed to parse color string
677             return;
679         SPCSSAttr *css = sp_repr_css_attr_new ();
680         sp_repr_css_set_property (css, "stroke", text.c_str());
681         sp_desktop_set_style (_desktop, css);
682         sp_repr_css_attr_unref (css);
683         sp_document_done (sp_desktop_document(_desktop), SP_VERB_DIALOG_FILL_STROKE,
684                       _("Paste stroke"));
685     }
688 void SelectedStyle::on_fillstroke_swap() {
689     SPCSSAttr *css = sp_repr_css_attr_new ();
691     switch (_mode[SS_FILL]) {
692     case SS_NA:
693     case SS_MANY:
694         break;
695     case SS_NONE:
696         sp_repr_css_set_property (css, "stroke", "none");
697         break;
698     case SS_UNSET:
699         sp_repr_css_unset_property (css, "stroke");
700         break;
701     case SS_COLOR:
702         gchar c[64];
703         sp_svg_write_color (c, 64, _thisselected[SS_FILL]);
704         sp_repr_css_set_property (css, "stroke", c);
705         break;
706     case SS_LGRADIENT:
707     case SS_RGRADIENT:
708     case SS_PATTERN:
709         sp_repr_css_set_property (css, "stroke", _paintserver_id[SS_FILL].c_str());
710         break;
711     }
713     switch (_mode[SS_STROKE]) {
714     case SS_NA:
715     case SS_MANY:
716         break;
717     case SS_NONE:
718         sp_repr_css_set_property (css, "fill", "none");
719         break;
720     case SS_UNSET:
721         sp_repr_css_unset_property (css, "fill");
722         break;
723     case SS_COLOR:
724         gchar c[64];
725         sp_svg_write_color (c, 64, _thisselected[SS_STROKE]);
726         sp_repr_css_set_property (css, "fill", c);
727         break;
728     case SS_LGRADIENT:
729     case SS_RGRADIENT:
730     case SS_PATTERN:
731         sp_repr_css_set_property (css, "fill", _paintserver_id[SS_STROKE].c_str());
732         break;
733     }
735     sp_desktop_set_style (_desktop, css);
736     sp_repr_css_attr_unref (css);
737     sp_document_done (sp_desktop_document(_desktop), SP_VERB_DIALOG_FILL_STROKE, 
738                       _("Swap fill and stroke"));
741 void SelectedStyle::on_fill_edit() {
742     sp_object_properties_fill();
745 void SelectedStyle::on_stroke_edit() {
746     sp_object_properties_stroke();
749 bool 
750 SelectedStyle::on_fill_click(GdkEventButton *event)
752     if (event->button == 1) { // click, open fill&stroke
753         sp_object_properties_fill();
754     } else if (event->button == 3) { // right-click, popup menu
755         _popup[SS_FILL].popup(event->button, event->time);
756     } else if (event->button == 2) { // middle click, toggle none/lastcolor
757         if (_mode[SS_FILL] == SS_NONE) {
758             on_fill_lastused();
759         } else {
760             on_fill_remove();
761         }
762     }
763     return true;
766 bool 
767 SelectedStyle::on_stroke_click(GdkEventButton *event)
769     if (event->button == 1) { // click, open fill&stroke
770         sp_object_properties_stroke();
771     } else if (event->button == 3) { // right-click, popup menu
772         _popup[SS_STROKE].popup(event->button, event->time);
773     } else if (event->button == 2) { // middle click, toggle none/lastcolor
774         if (_mode[SS_STROKE] == SS_NONE) {
775             on_stroke_lastused();
776         } else {
777             on_stroke_remove();
778         }
779     }
780     return true;
783 bool 
784 SelectedStyle::on_sw_click(GdkEventButton *event)
786     if (event->button == 1) { // click, open fill&stroke
787         sp_object_properties_stroke_style ();
788     } else if (event->button == 3) { // right-click, popup menu
789         _popup_sw.popup(event->button, event->time);
790     } else if (event->button == 2) { // middle click, toggle none/lastwidth?
791         //
792     }
793     return true;
796 bool 
797 SelectedStyle::on_opacity_click(GdkEventButton *event)
799     if (event->button == 2) { // middle click
800         const char* opacity = _opacity_sb.get_value() < 50? "0.5" : (_opacity_sb.get_value() == 100? "0" : "1");
801         SPCSSAttr *css = sp_repr_css_attr_new ();
802         sp_repr_css_set_property (css, "opacity", opacity);
803         sp_desktop_set_style (_desktop, css);
804         sp_repr_css_attr_unref (css);
805         sp_document_done (sp_desktop_document (_desktop), SP_VERB_DIALOG_FILL_STROKE,
806                       _("Change opacity"));
807         return true;
808     }
810     return false;
813 void SelectedStyle::on_popup_px() {
814     _sw_unit = (SPUnit *) &(sp_unit_get_by_id(SP_UNIT_PX));
815     update();
817 void SelectedStyle::on_popup_pt() {
818     _sw_unit = (SPUnit *) &(sp_unit_get_by_id(SP_UNIT_PT));
819     update();
821 void SelectedStyle::on_popup_mm() {
822     _sw_unit = (SPUnit *) &(sp_unit_get_by_id(SP_UNIT_MM));
823     update();
826 void SelectedStyle::on_popup_preset(int i) {
827     SPCSSAttr *css = sp_repr_css_attr_new ();
828     gdouble w;
829     if (_sw_unit) {
830         w = sp_units_get_pixels (_sw_presets[i], *_sw_unit);
831     } else {
832         w = _sw_presets[i];
833     }
834     Inkscape::CSSOStringStream os;
835     os << w;
836     sp_repr_css_set_property (css, "stroke-width", os.str().c_str());
837     sp_desktop_set_style (_desktop, css, true);
838     sp_repr_css_attr_unref (css);
839     sp_document_done (sp_desktop_document(_desktop), SP_VERB_DIALOG_SWATCHES,
840                       _("Change opacity"));
843 void
844 SelectedStyle::update()
846     if (_desktop == NULL)
847         return;
849     // create temporary style
850     SPStyle *query = sp_style_new ();
852     for (int i = SS_FILL; i <= SS_STROKE; i++) {
853         Gtk::EventBox *place = (i == SS_FILL)? &_fill_place : &_stroke_place;
854         Gtk::EventBox *flag_place = (i == SS_FILL)? &_fill_flag_place : &_stroke_flag_place;
856         place->remove();
857         flag_place->remove();
859         _tooltips.unset_tip(*place);
860         _tooltips.unset_tip(*flag_place);
862         _mode[i] = SS_NA;
863         _paintserver_id[i].clear();
865         _popup_copy[i].set_sensitive(false);
867         // query style from desktop. This returns a result flag and fills query with the style of subselection, if any, or selection
868         int result = sp_desktop_query_style (_desktop, query, 
869                                              (i == SS_FILL)? QUERY_STYLE_PROPERTY_FILL : QUERY_STYLE_PROPERTY_STROKE);
870         switch (result) {
871         case QUERY_STYLE_NOTHING:
872             place->add(_na[i]);
873             _tooltips.set_tip(*place, __na[i]);
874             _mode[i] = SS_NA;
875             if ( _dropEnabled[i] ) {
876                 gtk_drag_dest_unset( GTK_WIDGET((i==SS_FILL) ? _fill_place.gobj():_stroke_place.gobj()) );
877                 _dropEnabled[i] = false;
878             }
879             break;
880         case QUERY_STYLE_SINGLE:
881         case QUERY_STYLE_MULTIPLE_AVERAGED:
882         case QUERY_STYLE_MULTIPLE_SAME: 
883             if ( !_dropEnabled[i] ) {
884                 gtk_drag_dest_set( GTK_WIDGET( (i==SS_FILL) ? _fill_place.gobj():_stroke_place.gobj()),
885                                    GTK_DEST_DEFAULT_ALL,
886                                    ui_drop_target_entries,
887                                    nui_drop_target_entries,
888                                    GdkDragAction(GDK_ACTION_COPY | GDK_ACTION_MOVE) );
889                 _dropEnabled[i] = true;
890             }
891             SPIPaint *paint;
892             if (i == SS_FILL) {
893                 paint = &(query->fill);
894             } else {
895                 paint = &(query->stroke);
896             }
897             if (paint->set && paint->type == SP_PAINT_TYPE_COLOR) {
898                 guint32 color = sp_color_get_rgba32_falpha (&(paint->value.color), 
899                                      SP_SCALE24_TO_FLOAT ((i == SS_FILL)? query->fill_opacity.value : query->stroke_opacity.value));
900                 _lastselected[i] = _thisselected[i];
901                 _thisselected[i] = color | 0xff; // only color, opacity === 1
902                 ((Inkscape::UI::Widget::ColorPreview*)_color_preview[i])->setRgba32 (color);
903                 _color_preview[i]->show_all();
904                 place->add(*_color_preview[i]);
905                 gchar c_string[64];
906                 g_snprintf (c_string, 64, "%06x/%.3g", color >> 8, SP_RGBA32_A_F(color));
907                 _tooltips.set_tip(*place, __color[i] + ": " + c_string);
908                 _mode[i] = SS_COLOR;
909                 _popup_copy[i].set_sensitive(true);
911             } else if (paint->set && paint->type == SP_PAINT_TYPE_PAINTSERVER) {
912                 SPPaintServer *server = (i == SS_FILL)? SP_STYLE_FILL_SERVER (query) : SP_STYLE_STROKE_SERVER (query);
913                 if ( server ) {
914                     Inkscape::XML::Node *srepr = SP_OBJECT_REPR(server);
915                     _paintserver_id[i] += "url(#";
916                     _paintserver_id[i] += srepr->attribute("id");
917                     _paintserver_id[i] += ")";
919                     if (SP_IS_LINEARGRADIENT (server)) {
920                         place->add(_lgradient[i]);
921                         _tooltips.set_tip(*place, __lgradient[i]);
922                         _mode[i] = SS_LGRADIENT;
923                     } else if (SP_IS_RADIALGRADIENT (server)) {
924                         place->add(_rgradient[i]);
925                         _tooltips.set_tip(*place, __rgradient[i]);
926                         _mode[i] = SS_RGRADIENT;
927                     } else if (SP_IS_PATTERN (server)) {
928                         place->add(_pattern[i]);
929                         _tooltips.set_tip(*place, __pattern[i]);
930                         _mode[i] = SS_PATTERN;
931                     }
932                 } else {
933                     g_warning ("file %s: line %d: Unknown paint server", __FILE__, __LINE__);
934                 }
936             } else if (paint->set && paint->type == SP_PAINT_TYPE_NONE) {
937                 place->add(_none[i]);
938                 _tooltips.set_tip(*place, __none[i]);
939                 _mode[i] = SS_NONE;
940             } else if (!paint->set) {
941                 place->add(_unset[i]);
942                 _tooltips.set_tip(*place, __unset[i]);
943                 _mode[i] = SS_UNSET;
944             }
945             if (result == QUERY_STYLE_MULTIPLE_AVERAGED) {
946                 flag_place->add(_averaged[i]);
947                 _tooltips.set_tip(*flag_place, __averaged[i]);
948             } else if (result == QUERY_STYLE_MULTIPLE_SAME) {
949                 flag_place->add(_multiple[i]);
950                 _tooltips.set_tip(*flag_place, __multiple[i]);
951             }
952             break;
953         case QUERY_STYLE_MULTIPLE_DIFFERENT:
954             place->add(_many[i]);
955             _tooltips.set_tip(*place, __many[i]);
956             _mode[i] = SS_MANY;
957             break;
958         default:
959             break;
960         }
961     }
963 // Now query opacity
964     _tooltips.unset_tip(_opacity_place);
965     _tooltips.unset_tip(_opacity_sb);
967     int result = sp_desktop_query_style (_desktop, query, QUERY_STYLE_PROPERTY_MASTEROPACITY);
969     switch (result) {
970     case QUERY_STYLE_NOTHING:
971         _tooltips.set_tip(_opacity_place, _("Nothing selected"));
972         _tooltips.set_tip(_opacity_sb, _("Nothing selected"));
973         _opacity_sb.set_sensitive(false);
974         break;
975     case QUERY_STYLE_SINGLE:
976     case QUERY_STYLE_MULTIPLE_AVERAGED:
977     case QUERY_STYLE_MULTIPLE_SAME:
978         _tooltips.set_tip(_opacity_place, _("Master opacity, %"));
979         _tooltips.set_tip(_opacity_sb, _("Master opacity, %"));
980         if (_opacity_blocked) break;
981         _opacity_blocked = true;
982         _opacity_sb.set_sensitive(true);
983         _opacity_adjustment.set_value(SP_SCALE24_TO_FLOAT(query->opacity.value) * 100);
984         _opacity_blocked = false;
985         break;
986     }
988 // Now query stroke_width
989     int result_sw = sp_desktop_query_style (_desktop, query, QUERY_STYLE_PROPERTY_STROKEWIDTH);
990     switch (result_sw) {
991     case QUERY_STYLE_NOTHING:
992         _stroke_width.set_markup("");
993         break;
994     case QUERY_STYLE_SINGLE:
995     case QUERY_STYLE_MULTIPLE_AVERAGED:
996     case QUERY_STYLE_MULTIPLE_SAME: 
997     {
998         double w;
999         if (_sw_unit) {
1000             w = sp_pixels_get_units(query->stroke_width.computed, *_sw_unit);
1001         } else {
1002             w = query->stroke_width.computed;
1003         }
1004         {
1005             gchar *str = g_strdup_printf(" %.3g", w);
1006             _stroke_width.set_markup(str);
1007             g_free (str);
1008         }
1009         {
1010             gchar *str = g_strdup_printf(_("Stroke width: %.5g%s%s"), 
1011                                          w, 
1012                                          _sw_unit? sp_unit_get_abbreviation(_sw_unit) : "px", 
1013                                          (result_sw == QUERY_STYLE_MULTIPLE_AVERAGED)?
1014                                          _(" (averaged)") : "");
1015             _tooltips.set_tip(_stroke_width_place, str);
1016             g_free (str);
1017         }
1018         break;
1019     }
1020     default:
1021         break;
1022     }
1024     g_free (query);
1027 void SelectedStyle::opacity_0(void) {_opacity_sb.set_value(0);}
1028 void SelectedStyle::opacity_025(void) {_opacity_sb.set_value(25);}
1029 void SelectedStyle::opacity_05(void) {_opacity_sb.set_value(50);}
1030 void SelectedStyle::opacity_075(void) {_opacity_sb.set_value(75);}
1031 void SelectedStyle::opacity_1(void) {_opacity_sb.set_value(100);}
1033 void SelectedStyle::on_opacity_menu (Gtk::Menu *menu) {
1035     Glib::ListHandle<Gtk::Widget *> children = menu->get_children();
1036     for (Glib::ListHandle<Gtk::Widget *>::iterator iter = children.begin(); iter != children.end(); iter++) {
1037         menu->remove(*(*iter));
1038     }
1040     {
1041         Gtk::MenuItem *item = new Gtk::MenuItem;
1042         item->add(*(new Gtk::Label(_("0 (transparent)"), 0, 0)));
1043         item->signal_activate().connect(sigc::mem_fun(*this, &SelectedStyle::opacity_0 ));
1044         menu->add(*item);
1045     }
1046     {
1047         Gtk::MenuItem *item = new Gtk::MenuItem;
1048         item->add(*(new Gtk::Label("25%", 0, 0)));
1049         item->signal_activate().connect(sigc::mem_fun(*this, &SelectedStyle::opacity_025 ));
1050         menu->add(*item);
1051     }
1052     {
1053         Gtk::MenuItem *item = new Gtk::MenuItem;
1054         item->add(*(new Gtk::Label("50%", 0, 0)));
1055         item->signal_activate().connect(sigc::mem_fun(*this, &SelectedStyle::opacity_05 ));
1056         menu->add(*item);
1057     }
1058     {
1059         Gtk::MenuItem *item = new Gtk::MenuItem;
1060         item->add(*(new Gtk::Label("75%", 0, 0)));
1061         item->signal_activate().connect(sigc::mem_fun(*this, &SelectedStyle::opacity_075 ));
1062         menu->add(*item);
1063     }
1064     {
1065         Gtk::MenuItem *item = new Gtk::MenuItem;
1066         item->add(*(new Gtk::Label(_("100% (opaque)"), 0, 0)));
1067         item->signal_activate().connect(sigc::mem_fun(*this, &SelectedStyle::opacity_1 ));
1068         menu->add(*item);
1069     }
1071     menu->show_all();
1074 void SelectedStyle::on_opacity_changed () {
1075     if (_opacity_blocked)
1076         return;
1077     _opacity_blocked = true;
1078     SPCSSAttr *css = sp_repr_css_attr_new ();
1079     Inkscape::CSSOStringStream os;
1080     os << CLAMP ((_opacity_adjustment.get_value() / 100), 0.0, 1.0);
1081     sp_repr_css_set_property (css, "opacity", os.str().c_str());
1082     // FIXME: workaround for GTK breakage: display interruptibility sometimes results in GTK
1083     // sending multiple value-changed events. As if when Inkscape interrupts redraw for main loop
1084     // iterations, GTK discovers that this callback hasn't finished yet, and for some weird reason
1085     // decides to add yet another value-changed event to the queue. Totally braindead if you ask
1086     // me. As a result, scrolling the spinbutton once results in runaway change until it hits 1.0
1087     // or 0.0. (And no, this is not a race with ::update, I checked that.)
1088     // Sigh. So we disable interruptibility while we're setting the new value.
1089     sp_canvas_force_full_redraw_after_interruptions(sp_desktop_canvas(_desktop), 0);
1090     sp_desktop_set_style (_desktop, css);
1091     sp_repr_css_attr_unref (css);
1092     sp_document_maybe_done (sp_desktop_document (_desktop), "fillstroke:opacity", SP_VERB_DIALOG_FILL_STROKE,
1093                       _("Change opacity"));
1094     // resume interruptibility
1095     sp_canvas_end_forced_full_redraws(sp_desktop_canvas(_desktop));
1096     spinbutton_defocus(GTK_OBJECT(_opacity_sb.gobj()));
1097     _opacity_blocked = false;
1100 } // namespace Widget
1101 } // namespace UI
1102 } // namespace Inkscape
1104 /* 
1105   Local Variables:
1106   mode:c++
1107   c-file-style:"stroustrup"
1108   c-file-offsets:((innamespace . 0)(inline-open . 0))
1109   indent-tabs-mode:nil
1110   fill-column:99
1111   End:
1112 */
1113 // vim: filetype=c++:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 :