Code

Refactoring SPColor to C++ and removing legacy CMYK implementation
[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 "widgets/gradient-image.h"
36 #include "sp-gradient.h"
37 #include "svg/svg-color.h"
38 #include "svg/css-ostringstream.h"
39 #include "helper/units.h"
40 #include "verbs.h"
41 #include <display/sp-canvas.h>
43 static gdouble const _sw_presets[]     = { 32 ,  16 ,  10 ,  8 ,  6 ,  4 ,  3 ,  2 ,  1.5 ,  1 ,  0.75 ,  0.5 ,  0.25 ,  0.1 };
44 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"};
46 static void 
47 ss_selection_changed (Inkscape::Selection *, gpointer data)
48 {
49     Inkscape::UI::Widget::SelectedStyle *ss = (Inkscape::UI::Widget::SelectedStyle *) data;
50     ss->update();
51 }
53 static void
54 ss_selection_modified (Inkscape::Selection *selection, guint flags, gpointer data)
55 {
56     ss_selection_changed (selection, data);
57 }
59 static void
60 ss_subselection_changed (gpointer dragger, gpointer data)
61 {
62     ss_selection_changed (NULL, data);
63 }
65 namespace Inkscape {
66 namespace UI {
67 namespace Widget {
70 typedef struct {
71     SelectedStyle* parent;
72     int item;
73 } DropTracker;
75 /* Drag and Drop */
76 typedef enum {
77     APP_X_COLOR
78 } ui_drop_target_info;
80 static GtkTargetEntry ui_drop_target_entries [] = {
81     {"application/x-color", 0, APP_X_COLOR}
82 };
84 #define ENTRIES_SIZE(n) sizeof(n)/sizeof(n[0])
85 static guint nui_drop_target_entries = ENTRIES_SIZE(ui_drop_target_entries);
88 SelectedStyle::SelectedStyle(bool layout)
89     : _desktop (NULL),
91       _table(2, 6),
92       _fill_label (_("Fill:")),
93       _stroke_label (_("Stroke:")),
94       _opacity_label (_("O:")),
95       _fill_place (),
96       _stroke_place (),
98       _fill_flag_place (),
99       _stroke_flag_place (),
101       _opacity_place (),
102       _opacity_adjustment (100, 0.0, 100, 1.0, 10.0),
103       _opacity_sb (0.02, 0),
105       _stroke (),
106       _stroke_width (""),
108       _opacity_blocked (false),
110       _popup_px(_sw_group),
111       _popup_pt(_sw_group),
112       _popup_mm(_sw_group),
114       _sw_unit(NULL),
116       _tooltips ()
119     _drop[0] = _drop[1] = 0;
120     _dropEnabled[0] = _dropEnabled[1] = false;
121     
122     _fill_label.set_alignment(0.0, 0.5);
123     _fill_label.set_padding(0, 0);
124     _stroke_label.set_alignment(0.0, 0.5);
125     _stroke_label.set_padding(0, 0);
126     _opacity_label.set_alignment(0.0, 0.5);
127     _opacity_label.set_padding(0, 0);
129     _table.set_col_spacings (2);
130     _table.set_row_spacings (0);
132     for (int i = SS_FILL; i <= SS_STROKE; i++) {
134         _na[i].set_markup (_("N/A"));
135         sp_set_font_size_smaller (GTK_WIDGET(_na[i].gobj()));
136         _na[i].show_all();
137         __na[i] = (_("Nothing selected"));
139         _none[i].set_markup (_("<i>None</i>"));
140         sp_set_font_size_smaller (GTK_WIDGET(_none[i].gobj()));
141         _none[i].show_all();
142         __none[i] = (i == SS_FILL)? (_("No fill")) : (_("No stroke"));
144         _pattern[i].set_markup (_("Pattern"));
145         sp_set_font_size_smaller (GTK_WIDGET(_pattern[i].gobj()));
146         _pattern[i].show_all();
147         __pattern[i] = (i == SS_FILL)? (_("Pattern fill")) : (_("Pattern stroke"));
149         _lgradient[i].set_markup (_("<b>L</b>"));
150         sp_set_font_size_smaller (GTK_WIDGET(_lgradient[i].gobj()));
151         _lgradient[i].show_all();
152         __lgradient[i] = (i == SS_FILL)? (_("Linear gradient fill")) : (_("Linear gradient stroke"));
154         _gradient_preview_l[i] =  GTK_WIDGET(sp_gradient_image_new (NULL));
155         _gradient_box_l[i].pack_start(_lgradient[i]);
156         _gradient_box_l[i].pack_start(*(Glib::wrap(_gradient_preview_l[i])));
157         _gradient_box_l[i].show_all();
159         _rgradient[i].set_markup (_("<b>R</b>"));
160         sp_set_font_size_smaller (GTK_WIDGET(_rgradient[i].gobj()));
161         _rgradient[i].show_all();
162         __rgradient[i] = (i == SS_FILL)? (_("Radial gradient fill")) : (_("Radial gradient stroke"));
164         _gradient_preview_r[i] = GTK_WIDGET(sp_gradient_image_new (NULL));
165         _gradient_box_r[i].pack_start(_rgradient[i]);
166         _gradient_box_r[i].pack_start(*(Glib::wrap(_gradient_preview_r[i])));
167         _gradient_box_r[i].show_all();
169         _many[i].set_markup (_("Different"));
170         sp_set_font_size_smaller (GTK_WIDGET(_many[i].gobj()));
171         _many[i].show_all();
172         __many[i] = (i == SS_FILL)? (_("Different fills")) : (_("Different strokes"));
174         _unset[i].set_markup (_("<b>Unset</b>"));
175         sp_set_font_size_smaller (GTK_WIDGET(_unset[i].gobj()));
176         _unset[i].show_all();
177         __unset[i] = (i == SS_FILL)? (_("Unset fill")) : (_("Unset stroke"));
179         _color_preview[i] = new Inkscape::UI::Widget::ColorPreview (0);
180         __color[i] = (i == SS_FILL)? (_("Flat color fill")) : (_("Flat color stroke"));
182         // TRANSLATOR COMMENT: A means "Averaged"
183         _averaged[i].set_markup (_("<b>a</b>"));
184         sp_set_font_size_smaller (GTK_WIDGET(_averaged[i].gobj()));
185         _averaged[i].show_all();
186         __averaged[i] = (i == SS_FILL)? (_("Fill is averaged over selected objects")) : (_("Stroke is averaged over selected objects"));
188         // TRANSLATOR COMMENT: M means "Multiple"
189         _multiple[i].set_markup (_("<b>m</b>"));
190         sp_set_font_size_smaller (GTK_WIDGET(_multiple[i].gobj()));
191         _multiple[i].show_all();
192         __multiple[i] = (i == SS_FILL)? (_("Multiple selected objects have the same fill")) : (_("Multiple selected objects have the same stroke"));
194         _popup_edit[i].add(*(new Gtk::Label((i == SS_FILL)? _("Edit fill...") : _("Edit stroke..."), 0.0, 0.5)));
195         _popup_edit[i].signal_activate().connect(sigc::mem_fun(*this, 
196                                (i == SS_FILL)? &SelectedStyle::on_fill_edit : &SelectedStyle::on_stroke_edit ));
198         _popup_lastused[i].add(*(new Gtk::Label(_("Last set color"), 0.0, 0.5)));
199         _popup_lastused[i].signal_activate().connect(sigc::mem_fun(*this, 
200                                (i == SS_FILL)? &SelectedStyle::on_fill_lastused : &SelectedStyle::on_stroke_lastused ));
202         _popup_lastselected[i].add(*(new Gtk::Label(_("Last selected color"), 0.0, 0.5)));
203         _popup_lastselected[i].signal_activate().connect(sigc::mem_fun(*this, 
204                                (i == SS_FILL)? &SelectedStyle::on_fill_lastselected : &SelectedStyle::on_stroke_lastselected ));
206         _popup_invert[i].add(*(new Gtk::Label(_("Invert"), 0.0, 0.5)));
207         _popup_invert[i].signal_activate().connect(sigc::mem_fun(*this, 
208                                (i == SS_FILL)? &SelectedStyle::on_fill_invert : &SelectedStyle::on_stroke_invert ));
210         _popup_white[i].add(*(new Gtk::Label(_("White"), 0.0, 0.5)));
211         _popup_white[i].signal_activate().connect(sigc::mem_fun(*this, 
212                                (i == SS_FILL)? &SelectedStyle::on_fill_white : &SelectedStyle::on_stroke_white ));
214         _popup_black[i].add(*(new Gtk::Label(_("Black"), 0.0, 0.5)));
215         _popup_black[i].signal_activate().connect(sigc::mem_fun(*this, 
216                                (i == SS_FILL)? &SelectedStyle::on_fill_black : &SelectedStyle::on_stroke_black ));
218         _popup_copy[i].add(*(new Gtk::Label(_("Copy color"), 0.0, 0.5)));
219         _popup_copy[i].signal_activate().connect(sigc::mem_fun(*this, 
220                                (i == SS_FILL)? &SelectedStyle::on_fill_copy : &SelectedStyle::on_stroke_copy ));
222         _popup_paste[i].add(*(new Gtk::Label(_("Paste color"), 0.0, 0.5)));
223         _popup_paste[i].signal_activate().connect(sigc::mem_fun(*this, 
224                                (i == SS_FILL)? &SelectedStyle::on_fill_paste : &SelectedStyle::on_stroke_paste ));
226         _popup_swap[i].add(*(new Gtk::Label(_("Swap fill and stroke"), 0.0, 0.5)));
227         _popup_swap[i].signal_activate().connect(sigc::mem_fun(*this, 
228                                &SelectedStyle::on_fillstroke_swap));
230         _popup_opaque[i].add(*(new Gtk::Label((i == SS_FILL)? _("Make fill opaque") : _("Make stroke opaque"), 0.0, 0.5)));
231         _popup_opaque[i].signal_activate().connect(sigc::mem_fun(*this, 
232                                (i == SS_FILL)? &SelectedStyle::on_fill_opaque : &SelectedStyle::on_stroke_opaque ));
234         //TRANSLATORS COMMENT: unset is a verb here
235         _popup_unset[i].add(*(new Gtk::Label((i == SS_FILL)? _("Unset fill") : _("Unset stroke"), 0.0, 0.5)));
236         _popup_unset[i].signal_activate().connect(sigc::mem_fun(*this, 
237                                (i == SS_FILL)? &SelectedStyle::on_fill_unset : &SelectedStyle::on_stroke_unset ));
239         _popup_remove[i].add(*(new Gtk::Label((i == SS_FILL)? _("Remove fill") : _("Remove stroke"), 0.0, 0.5)));
240         _popup_remove[i].signal_activate().connect(sigc::mem_fun(*this, 
241                                (i == SS_FILL)? &SelectedStyle::on_fill_remove : &SelectedStyle::on_stroke_remove ));
243         _popup[i].attach(_popup_edit[i], 0,1, 0,1);
244           _popup[i].attach(*(new Gtk::SeparatorMenuItem()), 0,1, 1,2);
245         _popup[i].attach(_popup_lastused[i], 0,1, 2,3);
246         _popup[i].attach(_popup_lastselected[i], 0,1, 3,4);
247           _popup[i].attach(*(new Gtk::SeparatorMenuItem()), 0,1, 4,5);
248         _popup[i].attach(_popup_invert[i], 0,1, 5,6);
249           _popup[i].attach(*(new Gtk::SeparatorMenuItem()), 0,1, 6,7);
250         _popup[i].attach(_popup_white[i], 0,1, 7,8);
251         _popup[i].attach(_popup_black[i], 0,1, 8,9);
252           _popup[i].attach(*(new Gtk::SeparatorMenuItem()), 0,1, 9,10);
253         _popup[i].attach(_popup_copy[i], 0,1, 10,11);
254         _popup_copy[i].set_sensitive(false);
255         _popup[i].attach(_popup_paste[i], 0,1, 11,12);
256         _popup[i].attach(_popup_swap[i], 0,1, 12,13);
257           _popup[i].attach(*(new Gtk::SeparatorMenuItem()), 0,1, 13,14); 
258         _popup[i].attach(_popup_opaque[i], 0,1, 14,15);
259         _popup[i].attach(_popup_unset[i], 0,1, 15,16);
260         _popup[i].attach(_popup_remove[i], 0,1, 16,17);
261         _popup[i].show_all();
263         _mode[i] = SS_NA;
264     }
266     {
267         _popup_px.add(*(new Gtk::Label(_("px"), 0.0, 0.5)));
268         _popup_px.signal_activate().connect(sigc::mem_fun(*this, &SelectedStyle::on_popup_px));
269         _popup_sw.attach(_popup_px, 0,1, 0,1);
271         _popup_pt.add(*(new Gtk::Label(_("pt"), 0.0, 0.5)));
272         _popup_pt.signal_activate().connect(sigc::mem_fun(*this, &SelectedStyle::on_popup_pt));
273         _popup_sw.attach(_popup_pt, 0,1, 1,2);
275         _popup_mm.add(*(new Gtk::Label(_("mm"), 0.0, 0.5)));
276         _popup_mm.signal_activate().connect(sigc::mem_fun(*this, &SelectedStyle::on_popup_mm));
277         _popup_sw.attach(_popup_mm, 0,1, 2,3);
279         _popup_sw.attach(*(new Gtk::SeparatorMenuItem()), 0,1, 3,4);
281         for (guint i = 0; i < G_N_ELEMENTS(_sw_presets_str); ++i) {
282             Gtk::MenuItem *mi = Gtk::manage(new Gtk::MenuItem());
283             mi->add(*(new Gtk::Label(_sw_presets_str[i], 0.0, 0.5)));
284             mi->signal_activate().connect(sigc::bind<int>(sigc::mem_fun(*this, &SelectedStyle::on_popup_preset), i));
285             _popup_sw.attach(*mi, 0,1, 4+i, 5+i);
286         }
288         guint i = G_N_ELEMENTS(_sw_presets_str) + 5;
290         _popup_sw.attach(*(new Gtk::SeparatorMenuItem()), 0,1, i,i+1);
292         _popup_sw_remove.add(*(new Gtk::Label(_("Remove"), 0.0, 0.5)));
293         _popup_sw_remove.signal_activate().connect(sigc::mem_fun(*this, &SelectedStyle::on_stroke_remove));
294         _popup_sw.attach(_popup_sw_remove, 0,1, i+1,i+2);
296         _popup_sw.show_all();
297     }
299     _fill_place.signal_button_press_event().connect(sigc::mem_fun(*this, &SelectedStyle::on_fill_click));
300     _stroke_place.signal_button_press_event().connect(sigc::mem_fun(*this, &SelectedStyle::on_stroke_click));
301     _opacity_place.signal_button_press_event().connect(sigc::mem_fun(*this, &SelectedStyle::on_opacity_click));
302     _stroke_width_place.signal_button_press_event().connect(sigc::mem_fun(*this, &SelectedStyle::on_sw_click));
304     _opacity_sb.signal_populate_popup().connect(sigc::mem_fun(*this, &SelectedStyle::on_opacity_menu));
305     _opacity_sb.signal_value_changed().connect(sigc::mem_fun(*this, &SelectedStyle::on_opacity_changed));
307     _fill_place.add(_na[SS_FILL]);
308     _tooltips.set_tip(_fill_place, __na[SS_FILL]);
310     _stroke_place.add(_na[SS_STROKE]);
311     _tooltips.set_tip(_stroke_place, __na[SS_STROKE]);
313     _stroke.pack_start(_stroke_place);
314     _stroke_width_place.add(_stroke_width);
315     _stroke.pack_start(_stroke_width_place, Gtk::PACK_SHRINK);
317     _opacity_sb.set_adjustment(_opacity_adjustment);
318     sp_set_font_size_smaller (GTK_WIDGET(_opacity_sb.gobj()));
319     _opacity_sb.set_size_request (SELECTED_STYLE_SB_WIDTH, -1);
320     _opacity_sb.set_sensitive (false);
322     _table.attach(_fill_label, 0,1, 0,1, Gtk::FILL, Gtk::SHRINK);
323     _table.attach(_stroke_label, 0,1, 1,2, Gtk::FILL, Gtk::SHRINK);
325     _table.attach(_fill_flag_place, 1,2, 0,1, Gtk::SHRINK, Gtk::SHRINK);
326     _table.attach(_stroke_flag_place, 1,2, 1,2, Gtk::SHRINK, Gtk::SHRINK);
328     _table.attach(_fill_place, 2,3, 0,1);
329     _table.attach(_stroke, 2,3, 1,2);
331     _opacity_place.add(_opacity_label);
332     _table.attach(_opacity_place, 4,5, 0,2, Gtk::SHRINK, Gtk::SHRINK);
333     _table.attach(_opacity_sb, 5,6, 0,2, Gtk::SHRINK, Gtk::SHRINK);
335     pack_start(_table, true, true, 2);
337     set_size_request (SELECTED_STYLE_WIDTH, -1);
339     sp_set_font_size_smaller (GTK_WIDGET(_opacity_label.gobj()));
340     sp_set_font_size_smaller (GTK_WIDGET(_opacity_sb.gobj()));
341     sp_set_font_size_smaller (GTK_WIDGET(_fill_place.gobj()));
342     sp_set_font_size_smaller (GTK_WIDGET(_fill_flag_place.gobj()));
343     sp_set_font_size_smaller (GTK_WIDGET(_stroke_place.gobj()));
344     sp_set_font_size_smaller (GTK_WIDGET(_stroke_flag_place.gobj()));
345     sp_set_font_size_smaller (GTK_WIDGET(_stroke_width.gobj()));
346     sp_set_font_size_smaller (GTK_WIDGET(_fill_label.gobj()));
347     sp_set_font_size_smaller (GTK_WIDGET(_stroke_label.gobj()));
349     _drop[SS_FILL] = new DropTracker();
350     ((DropTracker*)_drop[SS_FILL])->parent = this;
351     ((DropTracker*)_drop[SS_FILL])->item = SS_FILL;
353     _drop[SS_STROKE] = new DropTracker();
354     ((DropTracker*)_drop[SS_STROKE])->parent = this;
355     ((DropTracker*)_drop[SS_STROKE])->item = SS_STROKE;
357     g_signal_connect(_stroke_place.gobj(),
358                      "drag_data_received",
359                      G_CALLBACK(dragDataReceived),
360                      _drop[SS_STROKE]);
362     g_signal_connect(_fill_place.gobj(),
363                      "drag_data_received",
364                      G_CALLBACK(dragDataReceived),
365                      _drop[SS_FILL]);
368 SelectedStyle::~SelectedStyle()
370     selection_changed_connection->disconnect();
371     delete selection_changed_connection;
372     selection_modified_connection->disconnect();
373     delete selection_modified_connection;
374     subselection_changed_connection->disconnect();
375     delete subselection_changed_connection;
377     for (int i = SS_FILL; i <= SS_STROKE; i++) {
378         delete _color_preview[i];
379         // FIXME: do we need this? the destroy methods are not exported 
380         //sp_gradient_image_destroy(GTK_OBJECT(_gradient_preview_l[i]));
381         //sp_gradient_image_destroy(GTK_OBJECT(_gradient_preview_r[i]));
382     }
384     delete (DropTracker*)_drop[SS_FILL];
385     delete (DropTracker*)_drop[SS_STROKE];
388 void
389 SelectedStyle::setDesktop(SPDesktop *desktop)
391     _desktop = desktop;
392     gtk_object_set_data (GTK_OBJECT(_opacity_sb.gobj()), "dtw", _desktop->canvas);
394     Inkscape::Selection *selection = sp_desktop_selection (desktop);
396     selection_changed_connection = new sigc::connection (selection->connectChanged(
397         sigc::bind (
398             sigc::ptr_fun(&ss_selection_changed),
399             this )
400     ));
401     selection_modified_connection = new sigc::connection (selection->connectModified(
402         sigc::bind (
403             sigc::ptr_fun(&ss_selection_modified),
404             this )
405     ));
406     subselection_changed_connection = new sigc::connection (desktop->connectToolSubselectionChanged(
407         sigc::bind (
408             sigc::ptr_fun(&ss_subselection_changed),
409             this )
410     ));
412     //_sw_unit = (SPUnit *) sp_desktop_namedview(desktop)->doc_units;
415 void SelectedStyle::dragDataReceived( GtkWidget *widget,
416                                       GdkDragContext *drag_context,
417                                       gint x, gint y,
418                                       GtkSelectionData *data,
419                                       guint info,
420                                       guint event_time,
421                                       gpointer user_data )
423     DropTracker* tracker = (DropTracker*)user_data;
425     switch ( (int)tracker->item ) {
426         case SS_FILL:
427         case SS_STROKE:
428         {
429             if ( data->length == 8 ) {
430                 gchar c[64];
431                 // Careful about endian issues.
432                 guint16* dataVals = (guint16*)data->data;
433                 sp_svg_write_color( c, 64,
434                                     SP_RGBA32_U_COMPOSE(
435                                         0x0ff & (dataVals[0] >> 8),
436                                         0x0ff & (dataVals[1] >> 8),
437                                         0x0ff & (dataVals[2] >> 8),
438                                         0xff // can't have transparency in the color itself
439                                         //0x0ff & (data->data[3] >> 8),
440                                         ));
441                 SPCSSAttr *css = sp_repr_css_attr_new();
442                 sp_repr_css_set_property( css, (tracker->item == SS_FILL) ? "fill":"stroke", c );
443                 sp_desktop_set_style( tracker->parent->_desktop, css );
444                 sp_repr_css_attr_unref( css );
445                 sp_document_done( sp_desktop_document(tracker->parent->_desktop) , SP_VERB_NONE, 
446                                   _("Drop color"));
447             }
448         }
449         break;
450     }
453 void SelectedStyle::on_fill_remove() {
454     SPCSSAttr *css = sp_repr_css_attr_new ();
455     sp_repr_css_set_property (css, "fill", "none");
456     sp_desktop_set_style (_desktop, css, true, true); 
457     sp_repr_css_attr_unref (css);
458     sp_document_done (sp_desktop_document(_desktop), SP_VERB_DIALOG_FILL_STROKE,
459                       _("Remove fill"));
462 void SelectedStyle::on_stroke_remove() {
463     SPCSSAttr *css = sp_repr_css_attr_new ();
464     sp_repr_css_set_property (css, "stroke", "none");
465     sp_desktop_set_style (_desktop, css, true, true); 
466     sp_repr_css_attr_unref (css);
467     sp_document_done (sp_desktop_document(_desktop), SP_VERB_DIALOG_FILL_STROKE,
468                       _("Remove stroke"));
471 void SelectedStyle::on_fill_unset() {
472     SPCSSAttr *css = sp_repr_css_attr_new ();
473     sp_repr_css_unset_property (css, "fill");
474     sp_desktop_set_style (_desktop, css, true, true); 
475     sp_repr_css_attr_unref (css);
476     sp_document_done (sp_desktop_document(_desktop), SP_VERB_DIALOG_FILL_STROKE,
477                       _("Unset fill"));
480 void SelectedStyle::on_stroke_unset() {
481     SPCSSAttr *css = sp_repr_css_attr_new ();
482     sp_repr_css_unset_property (css, "stroke");
483     sp_repr_css_unset_property (css, "stroke-opacity");
484     sp_repr_css_unset_property (css, "stroke-width");
485     sp_repr_css_unset_property (css, "stroke-miterlimit");
486     sp_repr_css_unset_property (css, "stroke-linejoin");
487     sp_repr_css_unset_property (css, "stroke-linecap");
488     sp_repr_css_unset_property (css, "stroke-dashoffset");
489     sp_repr_css_unset_property (css, "stroke-dasharray");
490     sp_desktop_set_style (_desktop, css, true, true);
491     sp_repr_css_attr_unref (css);
492     sp_document_done (sp_desktop_document(_desktop), SP_VERB_DIALOG_FILL_STROKE,
493                       _("Unset stroke"));
496 void SelectedStyle::on_fill_opaque() {
497     SPCSSAttr *css = sp_repr_css_attr_new ();
498     sp_repr_css_set_property (css, "fill-opacity", "1");
499     sp_desktop_set_style (_desktop, css, true);
500     sp_repr_css_attr_unref (css);
501     sp_document_done (sp_desktop_document(_desktop), SP_VERB_DIALOG_FILL_STROKE,
502                       _("Make fill opaque"));
505 void SelectedStyle::on_stroke_opaque() {
506     SPCSSAttr *css = sp_repr_css_attr_new ();
507     sp_repr_css_set_property (css, "stroke-opacity", "1");
508     sp_desktop_set_style (_desktop, css, true);
509     sp_repr_css_attr_unref (css);
510     sp_document_done (sp_desktop_document(_desktop), SP_VERB_DIALOG_FILL_STROKE,
511                       _("Make fill opaque"));
514 void SelectedStyle::on_fill_lastused() {
515     SPCSSAttr *css = sp_repr_css_attr_new ();
516     guint32 color = sp_desktop_get_color(_desktop, true);
517     gchar c[64];
518     sp_svg_write_color (c, 64, color);
519     sp_repr_css_set_property (css, "fill", c);
520     sp_desktop_set_style (_desktop, css);
521     sp_repr_css_attr_unref (css);
522     sp_document_done (sp_desktop_document(_desktop), SP_VERB_DIALOG_FILL_STROKE, 
523                       _("Apply last set color to fill"));
526 void SelectedStyle::on_stroke_lastused() {
527     SPCSSAttr *css = sp_repr_css_attr_new ();
528     guint32 color = sp_desktop_get_color(_desktop, false);
529     gchar c[64];
530     sp_svg_write_color (c, 64, color);
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 set color to stroke"));
538 void SelectedStyle::on_fill_lastselected() {
539     SPCSSAttr *css = sp_repr_css_attr_new ();
540     gchar c[64];
541     sp_svg_write_color (c, 64, _lastselected[SS_FILL]);
542     sp_repr_css_set_property (css, "fill", c);
543     sp_desktop_set_style (_desktop, css);
544     sp_repr_css_attr_unref (css);
545     sp_document_done (sp_desktop_document(_desktop), SP_VERB_DIALOG_FILL_STROKE,
546                       _("Apply last selected color to fill"));
549 void SelectedStyle::on_stroke_lastselected() {
550     SPCSSAttr *css = sp_repr_css_attr_new ();
551     gchar c[64];
552     sp_svg_write_color (c, 64, _lastselected[SS_STROKE]);
553     sp_repr_css_set_property (css, "stroke", c);
554     sp_desktop_set_style (_desktop, css);
555     sp_repr_css_attr_unref (css);
556     sp_document_done (sp_desktop_document(_desktop), SP_VERB_DIALOG_FILL_STROKE, 
557                       _("Apply last selected color to stroke"));
560 void SelectedStyle::on_fill_invert() {
561     SPCSSAttr *css = sp_repr_css_attr_new ();
562     guint32 color = _thisselected[SS_FILL];
563     gchar c[64];
564     if (_mode[SS_FILL] != SS_COLOR) return;
565     sp_svg_write_color (c, 64,
566         SP_RGBA32_U_COMPOSE(
567                 (255 - SP_RGBA32_R_U(color)),
568                 (255 - SP_RGBA32_G_U(color)),
569                 (255 - SP_RGBA32_B_U(color)),
570                 SP_RGBA32_A_U(color)
571         )
572     );
573     sp_repr_css_set_property (css, "fill", c);
574     sp_desktop_set_style (_desktop, css);
575     sp_repr_css_attr_unref (css);
576     sp_document_done (sp_desktop_document(_desktop), SP_VERB_DIALOG_FILL_STROKE,
577                       _("Invert fill"));
580 void SelectedStyle::on_stroke_invert() {
581     SPCSSAttr *css = sp_repr_css_attr_new ();
582     guint32 color = _thisselected[SS_STROKE];
583     gchar c[64];
584     if (_mode[SS_STROKE] != SS_COLOR) return;
585     sp_svg_write_color (c, 64,
586         SP_RGBA32_U_COMPOSE(
587                 (255 - SP_RGBA32_R_U(color)),
588                 (255 - SP_RGBA32_G_U(color)),
589                 (255 - SP_RGBA32_B_U(color)),
590                 SP_RGBA32_A_U(color)
591         )
592     );
593     sp_repr_css_set_property (css, "stroke", c);
594     sp_desktop_set_style (_desktop, css);
595     sp_repr_css_attr_unref (css);
596     sp_document_done (sp_desktop_document(_desktop), SP_VERB_DIALOG_FILL_STROKE,
597                       _("Invert stroke"));
598
600 void SelectedStyle::on_fill_white() {
601     SPCSSAttr *css = sp_repr_css_attr_new ();
602     gchar c[64];
603     sp_svg_write_color (c, 64, 0xffffffff);
604     sp_repr_css_set_property (css, "fill", c);
605     sp_repr_css_set_property (css, "fill-opacity", "1");
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                       _("White fill"));
612 void SelectedStyle::on_stroke_white() {
613     SPCSSAttr *css = sp_repr_css_attr_new ();
614     gchar c[64];
615     sp_svg_write_color (c, 64, 0xffffffff);
616     sp_repr_css_set_property (css, "stroke", c);
617     sp_repr_css_set_property (css, "stroke-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 stroke"));
624 void SelectedStyle::on_fill_black() {
625     SPCSSAttr *css = sp_repr_css_attr_new ();
626     gchar c[64];
627     sp_svg_write_color (c, 64, 0x000000ff);
628     sp_repr_css_set_property (css, "fill", c);
629     sp_repr_css_set_property (css, "fill-opacity", "1.0");
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                       _("Black fill"));
636 void SelectedStyle::on_stroke_black() {
637     SPCSSAttr *css = sp_repr_css_attr_new ();
638     gchar c[64];
639     sp_svg_write_color (c, 64, 0x000000ff);
640     sp_repr_css_set_property (css, "stroke", c);
641     sp_repr_css_set_property (css, "stroke-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 stroke"));
648 void SelectedStyle::on_fill_copy() {
649     if (_mode[SS_FILL] == SS_COLOR) {
650         gchar c[64];
651         sp_svg_write_color (c, 64, _thisselected[SS_FILL]);
652         Glib::ustring text;
653         text += c;
654         if (!text.empty()) {
655             Glib::RefPtr<Gtk::Clipboard> refClipboard = Gtk::Clipboard::get();
656             refClipboard->set_text(text);
657         }
658     }
661 void SelectedStyle::on_stroke_copy() {
662     if (_mode[SS_STROKE] == SS_COLOR) {
663         gchar c[64];
664         sp_svg_write_color (c, 64, _thisselected[SS_STROKE]);
665         Glib::ustring text;
666         text += c;
667         if (!text.empty()) {
668             Glib::RefPtr<Gtk::Clipboard> refClipboard = Gtk::Clipboard::get();
669             refClipboard->set_text(text);
670         }
671     }
674 void SelectedStyle::on_fill_paste() {
675     Glib::RefPtr<Gtk::Clipboard> refClipboard = Gtk::Clipboard::get();
676     Glib::ustring const text = refClipboard->wait_for_text();
678     if (!text.empty()) {
679         guint32 color = sp_svg_read_color(text.c_str(), 0x000000ff); // impossible value, as SVG color cannot have opacity
680         if (color == 0x000000ff) // failed to parse color string
681             return;
683         SPCSSAttr *css = sp_repr_css_attr_new ();
684         sp_repr_css_set_property (css, "fill", text.c_str());
685         sp_desktop_set_style (_desktop, css);
686         sp_repr_css_attr_unref (css);
687         sp_document_done (sp_desktop_document(_desktop), SP_VERB_DIALOG_FILL_STROKE, 
688                       _("Paste fill"));
689     }
692 void SelectedStyle::on_stroke_paste() {
693     Glib::RefPtr<Gtk::Clipboard> refClipboard = Gtk::Clipboard::get();
694     Glib::ustring const text = refClipboard->wait_for_text();
696     if (!text.empty()) {
697         guint32 color = sp_svg_read_color(text.c_str(), 0x000000ff); // impossible value, as SVG color cannot have opacity
698         if (color == 0x000000ff) // failed to parse color string
699             return;
701         SPCSSAttr *css = sp_repr_css_attr_new ();
702         sp_repr_css_set_property (css, "stroke", text.c_str());
703         sp_desktop_set_style (_desktop, css);
704         sp_repr_css_attr_unref (css);
705         sp_document_done (sp_desktop_document(_desktop), SP_VERB_DIALOG_FILL_STROKE,
706                       _("Paste stroke"));
707     }
710 void SelectedStyle::on_fillstroke_swap() {
711     SPCSSAttr *css = sp_repr_css_attr_new ();
713     switch (_mode[SS_FILL]) {
714     case SS_NA:
715     case SS_MANY:
716         break;
717     case SS_NONE:
718         sp_repr_css_set_property (css, "stroke", "none");
719         break;
720     case SS_UNSET:
721         sp_repr_css_unset_property (css, "stroke");
722         break;
723     case SS_COLOR:
724         gchar c[64];
725         sp_svg_write_color (c, 64, _thisselected[SS_FILL]);
726         sp_repr_css_set_property (css, "stroke", c);
727         break;
728     case SS_LGRADIENT:
729     case SS_RGRADIENT:
730     case SS_PATTERN:
731         sp_repr_css_set_property (css, "stroke", _paintserver_id[SS_FILL].c_str());
732         break;
733     }
735     switch (_mode[SS_STROKE]) {
736     case SS_NA:
737     case SS_MANY:
738         break;
739     case SS_NONE:
740         sp_repr_css_set_property (css, "fill", "none");
741         break;
742     case SS_UNSET:
743         sp_repr_css_unset_property (css, "fill");
744         break;
745     case SS_COLOR:
746         gchar c[64];
747         sp_svg_write_color (c, 64, _thisselected[SS_STROKE]);
748         sp_repr_css_set_property (css, "fill", c);
749         break;
750     case SS_LGRADIENT:
751     case SS_RGRADIENT:
752     case SS_PATTERN:
753         sp_repr_css_set_property (css, "fill", _paintserver_id[SS_STROKE].c_str());
754         break;
755     }
757     sp_desktop_set_style (_desktop, css);
758     sp_repr_css_attr_unref (css);
759     sp_document_done (sp_desktop_document(_desktop), SP_VERB_DIALOG_FILL_STROKE, 
760                       _("Swap fill and stroke"));
763 void SelectedStyle::on_fill_edit() {
764     sp_object_properties_fill();
767 void SelectedStyle::on_stroke_edit() {
768     sp_object_properties_stroke();
771 bool 
772 SelectedStyle::on_fill_click(GdkEventButton *event)
774     if (event->button == 1) { // click, open fill&stroke
775         sp_object_properties_fill();
776     } else if (event->button == 3) { // right-click, popup menu
777         _popup[SS_FILL].popup(event->button, event->time);
778     } else if (event->button == 2) { // middle click, toggle none/lastcolor
779         if (_mode[SS_FILL] == SS_NONE) {
780             on_fill_lastused();
781         } else {
782             on_fill_remove();
783         }
784     }
785     return true;
788 bool 
789 SelectedStyle::on_stroke_click(GdkEventButton *event)
791     if (event->button == 1) { // click, open fill&stroke
792         sp_object_properties_stroke();
793     } else if (event->button == 3) { // right-click, popup menu
794         _popup[SS_STROKE].popup(event->button, event->time);
795     } else if (event->button == 2) { // middle click, toggle none/lastcolor
796         if (_mode[SS_STROKE] == SS_NONE) {
797             on_stroke_lastused();
798         } else {
799             on_stroke_remove();
800         }
801     }
802     return true;
805 bool 
806 SelectedStyle::on_sw_click(GdkEventButton *event)
808     if (event->button == 1) { // click, open fill&stroke
809         sp_object_properties_stroke_style ();
810     } else if (event->button == 3) { // right-click, popup menu
811         _popup_sw.popup(event->button, event->time);
812     } else if (event->button == 2) { // middle click, toggle none/lastwidth?
813         //
814     }
815     return true;
818 bool 
819 SelectedStyle::on_opacity_click(GdkEventButton *event)
821     if (event->button == 2) { // middle click
822         const char* opacity = _opacity_sb.get_value() < 50? "0.5" : (_opacity_sb.get_value() == 100? "0" : "1");
823         SPCSSAttr *css = sp_repr_css_attr_new ();
824         sp_repr_css_set_property (css, "opacity", opacity);
825         sp_desktop_set_style (_desktop, css);
826         sp_repr_css_attr_unref (css);
827         sp_document_done (sp_desktop_document (_desktop), SP_VERB_DIALOG_FILL_STROKE,
828                       _("Change opacity"));
829         return true;
830     }
832     return false;
835 void SelectedStyle::on_popup_px() {
836     _sw_unit = (SPUnit *) &(sp_unit_get_by_id(SP_UNIT_PX));
837     update();
839 void SelectedStyle::on_popup_pt() {
840     _sw_unit = (SPUnit *) &(sp_unit_get_by_id(SP_UNIT_PT));
841     update();
843 void SelectedStyle::on_popup_mm() {
844     _sw_unit = (SPUnit *) &(sp_unit_get_by_id(SP_UNIT_MM));
845     update();
848 void SelectedStyle::on_popup_preset(int i) {
849     SPCSSAttr *css = sp_repr_css_attr_new ();
850     gdouble w;
851     if (_sw_unit) {
852         w = sp_units_get_pixels (_sw_presets[i], *_sw_unit);
853     } else {
854         w = _sw_presets[i];
855     }
856     Inkscape::CSSOStringStream os;
857     os << w;
858     sp_repr_css_set_property (css, "stroke-width", os.str().c_str());
859     // FIXME: update dash patterns!
860     sp_desktop_set_style (_desktop, css, true);
861     sp_repr_css_attr_unref (css);
862     sp_document_done (sp_desktop_document(_desktop), SP_VERB_DIALOG_SWATCHES,
863                       _("Change stroke width"));
866 void
867 SelectedStyle::update()
869     if (_desktop == NULL)
870         return;
872     // create temporary style
873     SPStyle *query = sp_style_new (sp_desktop_document(_desktop));
875     for (int i = SS_FILL; i <= SS_STROKE; i++) {
876         Gtk::EventBox *place = (i == SS_FILL)? &_fill_place : &_stroke_place;
877         Gtk::EventBox *flag_place = (i == SS_FILL)? &_fill_flag_place : &_stroke_flag_place;
879         place->remove();
880         flag_place->remove();
882         _tooltips.unset_tip(*place);
883         _tooltips.unset_tip(*flag_place);
885         _mode[i] = SS_NA;
886         _paintserver_id[i].clear();
888         _popup_copy[i].set_sensitive(false);
890         // query style from desktop. This returns a result flag and fills query with the style of subselection, if any, or selection
891         int result = sp_desktop_query_style (_desktop, query, 
892                                              (i == SS_FILL)? QUERY_STYLE_PROPERTY_FILL : QUERY_STYLE_PROPERTY_STROKE);
893         switch (result) {
894         case QUERY_STYLE_NOTHING:
895             place->add(_na[i]);
896             _tooltips.set_tip(*place, __na[i]);
897             _mode[i] = SS_NA;
898             if ( _dropEnabled[i] ) {
899                 gtk_drag_dest_unset( GTK_WIDGET((i==SS_FILL) ? _fill_place.gobj():_stroke_place.gobj()) );
900                 _dropEnabled[i] = false;
901             }
902             break;
903         case QUERY_STYLE_SINGLE:
904         case QUERY_STYLE_MULTIPLE_AVERAGED:
905         case QUERY_STYLE_MULTIPLE_SAME: 
906             if ( !_dropEnabled[i] ) {
907                 gtk_drag_dest_set( GTK_WIDGET( (i==SS_FILL) ? _fill_place.gobj():_stroke_place.gobj()),
908                                    GTK_DEST_DEFAULT_ALL,
909                                    ui_drop_target_entries,
910                                    nui_drop_target_entries,
911                                    GdkDragAction(GDK_ACTION_COPY | GDK_ACTION_MOVE) );
912                 _dropEnabled[i] = true;
913             }
914             SPIPaint *paint;
915             if (i == SS_FILL) {
916                 paint = &(query->fill);
917             } else {
918                 paint = &(query->stroke);
919             }
920             if (paint->set && paint->isPaintserver()) {
921                 SPPaintServer *server = (i == SS_FILL)? SP_STYLE_FILL_SERVER (query) : SP_STYLE_STROKE_SERVER (query);
922                 if ( server ) {
923                     Inkscape::XML::Node *srepr = SP_OBJECT_REPR(server);
924                     _paintserver_id[i] += "url(#";
925                     _paintserver_id[i] += srepr->attribute("id");
926                     _paintserver_id[i] += ")";
928                     if (SP_IS_LINEARGRADIENT (server)) {
929                         SPGradient *vector = sp_gradient_get_vector(SP_GRADIENT(server), false);
930                         sp_gradient_image_set_gradient ((SPGradientImage *) _gradient_preview_l[i], vector);
931                         place->add(_gradient_box_l[i]);
932                         _tooltips.set_tip(*place, __lgradient[i]);
933                         _mode[i] = SS_LGRADIENT;
934                     } else if (SP_IS_RADIALGRADIENT (server)) {
935                         SPGradient *vector = sp_gradient_get_vector(SP_GRADIENT(server), false);
936                         sp_gradient_image_set_gradient ((SPGradientImage *) _gradient_preview_r[i], vector);
937                         place->add(_gradient_box_r[i]);
938                         _tooltips.set_tip(*place, __rgradient[i]);
939                         _mode[i] = SS_RGRADIENT;
940                     } else if (SP_IS_PATTERN (server)) {
941                         place->add(_pattern[i]);
942                         _tooltips.set_tip(*place, __pattern[i]);
943                         _mode[i] = SS_PATTERN;
944                     }
945                 } else {
946                     g_warning ("file %s: line %d: Unknown paint server", __FILE__, __LINE__);
947                 }
948             } else if (paint->set && paint->isColor()) {
949                 guint32 color = paint->value.color.toRGBA32(
950                                      SP_SCALE24_TO_FLOAT ((i == SS_FILL)? query->fill_opacity.value : query->stroke_opacity.value));
951                 _lastselected[i] = _thisselected[i];
952                 _thisselected[i] = color | 0xff; // only color, opacity === 1
953                 ((Inkscape::UI::Widget::ColorPreview*)_color_preview[i])->setRgba32 (color);
954                 _color_preview[i]->show_all();
955                 place->add(*_color_preview[i]);
956                 gchar c_string[64];
957                 g_snprintf (c_string, 64, "%06x/%.3g", color >> 8, SP_RGBA32_A_F(color));
958                 _tooltips.set_tip(*place, __color[i] + ": " + c_string);
959                 _mode[i] = SS_COLOR;
960                 _popup_copy[i].set_sensitive(true);
962             } else if (paint->set && paint->isNone()) {
963                 place->add(_none[i]);
964                 _tooltips.set_tip(*place, __none[i]);
965                 _mode[i] = SS_NONE;
966             } else if (!paint->set) {
967                 place->add(_unset[i]);
968                 _tooltips.set_tip(*place, __unset[i]);
969                 _mode[i] = SS_UNSET;
970             }
971             if (result == QUERY_STYLE_MULTIPLE_AVERAGED) {
972                 flag_place->add(_averaged[i]);
973                 _tooltips.set_tip(*flag_place, __averaged[i]);
974             } else if (result == QUERY_STYLE_MULTIPLE_SAME) {
975                 flag_place->add(_multiple[i]);
976                 _tooltips.set_tip(*flag_place, __multiple[i]);
977             }
978             break;
979         case QUERY_STYLE_MULTIPLE_DIFFERENT:
980             place->add(_many[i]);
981             _tooltips.set_tip(*place, __many[i]);
982             _mode[i] = SS_MANY;
983             break;
984         default:
985             break;
986         }
987     }
989 // Now query opacity
990     _tooltips.unset_tip(_opacity_place);
991     _tooltips.unset_tip(_opacity_sb);
993     int result = sp_desktop_query_style (_desktop, query, QUERY_STYLE_PROPERTY_MASTEROPACITY);
995     switch (result) {
996     case QUERY_STYLE_NOTHING:
997         _tooltips.set_tip(_opacity_place, _("Nothing selected"));
998         _tooltips.set_tip(_opacity_sb, _("Nothing selected"));
999         _opacity_sb.set_sensitive(false);
1000         break;
1001     case QUERY_STYLE_SINGLE:
1002     case QUERY_STYLE_MULTIPLE_AVERAGED:
1003     case QUERY_STYLE_MULTIPLE_SAME:
1004         _tooltips.set_tip(_opacity_place, _("Master opacity, %"));
1005         _tooltips.set_tip(_opacity_sb, _("Master opacity, %"));
1006         if (_opacity_blocked) break;
1007         _opacity_blocked = true;
1008         _opacity_sb.set_sensitive(true);
1009         _opacity_adjustment.set_value(SP_SCALE24_TO_FLOAT(query->opacity.value) * 100);
1010         _opacity_blocked = false;
1011         break;
1012     }
1014 // Now query stroke_width
1015     int result_sw = sp_desktop_query_style (_desktop, query, QUERY_STYLE_PROPERTY_STROKEWIDTH);
1016     switch (result_sw) {
1017     case QUERY_STYLE_NOTHING:
1018         _stroke_width.set_markup("");
1019         break;
1020     case QUERY_STYLE_SINGLE:
1021     case QUERY_STYLE_MULTIPLE_AVERAGED:
1022     case QUERY_STYLE_MULTIPLE_SAME: 
1023     {
1024         double w;
1025         if (_sw_unit) {
1026             w = sp_pixels_get_units(query->stroke_width.computed, *_sw_unit);
1027         } else {
1028             w = query->stroke_width.computed;
1029         }
1030         {
1031             gchar *str = g_strdup_printf(" %.3g", w);
1032             _stroke_width.set_markup(str);
1033             g_free (str);
1034         }
1035         {
1036             gchar *str = g_strdup_printf(_("Stroke width: %.5g%s%s"), 
1037                                          w, 
1038                                          _sw_unit? sp_unit_get_abbreviation(_sw_unit) : "px", 
1039                                          (result_sw == QUERY_STYLE_MULTIPLE_AVERAGED)?
1040                                          _(" (averaged)") : "");
1041             _tooltips.set_tip(_stroke_width_place, str);
1042             g_free (str);
1043         }
1044         break;
1045     }
1046     default:
1047         break;
1048     }
1050     sp_style_unref(query);
1053 void SelectedStyle::opacity_0(void) {_opacity_sb.set_value(0);}
1054 void SelectedStyle::opacity_025(void) {_opacity_sb.set_value(25);}
1055 void SelectedStyle::opacity_05(void) {_opacity_sb.set_value(50);}
1056 void SelectedStyle::opacity_075(void) {_opacity_sb.set_value(75);}
1057 void SelectedStyle::opacity_1(void) {_opacity_sb.set_value(100);}
1059 void SelectedStyle::on_opacity_menu (Gtk::Menu *menu) {
1061     Glib::ListHandle<Gtk::Widget *> children = menu->get_children();
1062     for (Glib::ListHandle<Gtk::Widget *>::iterator iter = children.begin(); iter != children.end(); iter++) {
1063         menu->remove(*(*iter));
1064     }
1066     {
1067         Gtk::MenuItem *item = new Gtk::MenuItem;
1068         item->add(*(new Gtk::Label(_("0 (transparent)"), 0, 0)));
1069         item->signal_activate().connect(sigc::mem_fun(*this, &SelectedStyle::opacity_0 ));
1070         menu->add(*item);
1071     }
1072     {
1073         Gtk::MenuItem *item = new Gtk::MenuItem;
1074         item->add(*(new Gtk::Label("25%", 0, 0)));
1075         item->signal_activate().connect(sigc::mem_fun(*this, &SelectedStyle::opacity_025 ));
1076         menu->add(*item);
1077     }
1078     {
1079         Gtk::MenuItem *item = new Gtk::MenuItem;
1080         item->add(*(new Gtk::Label("50%", 0, 0)));
1081         item->signal_activate().connect(sigc::mem_fun(*this, &SelectedStyle::opacity_05 ));
1082         menu->add(*item);
1083     }
1084     {
1085         Gtk::MenuItem *item = new Gtk::MenuItem;
1086         item->add(*(new Gtk::Label("75%", 0, 0)));
1087         item->signal_activate().connect(sigc::mem_fun(*this, &SelectedStyle::opacity_075 ));
1088         menu->add(*item);
1089     }
1090     {
1091         Gtk::MenuItem *item = new Gtk::MenuItem;
1092         item->add(*(new Gtk::Label(_("100% (opaque)"), 0, 0)));
1093         item->signal_activate().connect(sigc::mem_fun(*this, &SelectedStyle::opacity_1 ));
1094         menu->add(*item);
1095     }
1097     menu->show_all();
1100 void SelectedStyle::on_opacity_changed () {
1101     if (_opacity_blocked)
1102         return;
1103     _opacity_blocked = true;
1104     SPCSSAttr *css = sp_repr_css_attr_new ();
1105     Inkscape::CSSOStringStream os;
1106     os << CLAMP ((_opacity_adjustment.get_value() / 100), 0.0, 1.0);
1107     sp_repr_css_set_property (css, "opacity", os.str().c_str());
1108     // FIXME: workaround for GTK breakage: display interruptibility sometimes results in GTK
1109     // sending multiple value-changed events. As if when Inkscape interrupts redraw for main loop
1110     // iterations, GTK discovers that this callback hasn't finished yet, and for some weird reason
1111     // decides to add yet another value-changed event to the queue. Totally braindead if you ask
1112     // me. As a result, scrolling the spinbutton once results in runaway change until it hits 1.0
1113     // or 0.0. (And no, this is not a race with ::update, I checked that.)
1114     // Sigh. So we disable interruptibility while we're setting the new value.
1115     sp_canvas_force_full_redraw_after_interruptions(sp_desktop_canvas(_desktop), 0);
1116     sp_desktop_set_style (_desktop, css);
1117     sp_repr_css_attr_unref (css);
1118     sp_document_maybe_done (sp_desktop_document (_desktop), "fillstroke:opacity", SP_VERB_DIALOG_FILL_STROKE,
1119                       _("Change opacity"));
1120     // resume interruptibility
1121     sp_canvas_end_forced_full_redraws(sp_desktop_canvas(_desktop));
1122     spinbutton_defocus(GTK_OBJECT(_opacity_sb.gobj()));
1123     _opacity_blocked = false;
1126 } // namespace Widget
1127 } // namespace UI
1128 } // namespace Inkscape
1130 /* 
1131   Local Variables:
1132   mode:c++
1133   c-file-style:"stroustrup"
1134   c-file-offsets:((innamespace . 0)(inline-open . 0))
1135   indent-tabs-mode:nil
1136   fill-column:99
1137   End:
1138 */
1139 // vim: filetype=c++:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 :