Code

Fixed unsafe hardcoding of buffer size
[inkscape.git] / src / ui / widget / selected-style.cpp
1 /**
2  * \brief Selected style indicator (fill, stroke, opacity)
3  *
4  * Author:
5  *   buliabyak@gmail.com
6  *
7  * Copyright (C) 2005 author
8  *
9  * Released under GNU GPL.  Read the file 'COPYING' for more information.
10  */
12 #ifdef HAVE_CONFIG_H
13 # include <config.h>
14 #endif
16 #include <gtk/gtkdnd.h>
18 #include "selected-style.h"
20 #include "widgets/spw-utilities.h"
21 #include "ui/widget/color-preview.h"
23 #include "selection.h"
24 #include "desktop-handles.h"
25 #include "style.h"
26 #include "desktop-style.h"
27 #include "sp-linear-gradient-fns.h"
28 #include "sp-radial-gradient-fns.h"
29 #include "sp-pattern.h"
30 #include "ui/dialog/dialog-manager.h"
31 #include "ui/dialog/fill-and-stroke.h"
32 #include "xml/repr.h"
33 #include "document.h"
34 #include "widgets/widget-sizes.h"
35 #include "widgets/spinbutton-events.h"
36 #include "widgets/gradient-image.h"
37 #include "sp-gradient.h"
38 #include "svg/svg-color.h"
39 #include "svg/css-ostringstream.h"
40 #include "helper/units.h"
41 #include "verbs.h"
42 #include <display/sp-canvas.h>
44 static gdouble const _sw_presets[]     = { 32 ,  16 ,  10 ,  8 ,  6 ,  4 ,  3 ,  2 ,  1.5 ,  1 ,  0.75 ,  0.5 ,  0.25 ,  0.1 };
45 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"};
47 static void 
48 ss_selection_changed (Inkscape::Selection *, gpointer data)
49 {
50     Inkscape::UI::Widget::SelectedStyle *ss = (Inkscape::UI::Widget::SelectedStyle *) data;
51     ss->update();
52 }
54 static void
55 ss_selection_modified (Inkscape::Selection *selection, guint flags, gpointer data)
56 {
57     ss_selection_changed (selection, data);
58 }
60 static void
61 ss_subselection_changed (gpointer dragger, gpointer data)
62 {
63     ss_selection_changed (NULL, data);
64 }
66 namespace Inkscape {
67 namespace UI {
68 namespace Widget {
71 typedef struct {
72     SelectedStyle* parent;
73     int item;
74 } DropTracker;
76 /* Drag and Drop */
77 typedef enum {
78     APP_X_COLOR
79 } ui_drop_target_info;
81 static GtkTargetEntry ui_drop_target_entries [] = {
82     {"application/x-color", 0, APP_X_COLOR}
83 };
85 #define ENTRIES_SIZE(n) sizeof(n)/sizeof(n[0])
86 static guint nui_drop_target_entries = ENTRIES_SIZE(ui_drop_target_entries);
89 SelectedStyle::SelectedStyle(bool layout)
90     : _desktop (NULL),
92       _table(2, 6),
93       _fill_label (_("Fill:")),
94       _stroke_label (_("Stroke:")),
95       _opacity_label (_("O:")),
96       _fill_place (),
97       _stroke_place (),
99       _fill_flag_place (),
100       _stroke_flag_place (),
102       _opacity_place (),
103       _opacity_adjustment (100, 0.0, 100, 1.0, 10.0),
104       _opacity_sb (0.02, 0),
106       _stroke (),
107       _stroke_width (""),
109       _opacity_blocked (false),
111       _popup_px(_sw_group),
112       _popup_pt(_sw_group),
113       _popup_mm(_sw_group),
115       _sw_unit(NULL),
117       _tooltips ()
120     _drop[0] = _drop[1] = 0;
121     _dropEnabled[0] = _dropEnabled[1] = false;
122     
123     _fill_label.set_alignment(0.0, 0.5);
124     _fill_label.set_padding(0, 0);
125     _stroke_label.set_alignment(0.0, 0.5);
126     _stroke_label.set_padding(0, 0);
127     _opacity_label.set_alignment(0.0, 0.5);
128     _opacity_label.set_padding(0, 0);
130     _table.set_col_spacings (2);
131     _table.set_row_spacings (0);
133     for (int i = SS_FILL; i <= SS_STROKE; i++) {
135         _na[i].set_markup (_("N/A"));
136         sp_set_font_size_smaller (GTK_WIDGET(_na[i].gobj()));
137         _na[i].show_all();
138         __na[i] = (_("Nothing selected"));
140         _none[i].set_markup (_("<i>None</i>"));
141         sp_set_font_size_smaller (GTK_WIDGET(_none[i].gobj()));
142         _none[i].show_all();
143         __none[i] = (i == SS_FILL)? (_("No fill")) : (_("No stroke"));
145         _pattern[i].set_markup (_("Pattern"));
146         sp_set_font_size_smaller (GTK_WIDGET(_pattern[i].gobj()));
147         _pattern[i].show_all();
148         __pattern[i] = (i == SS_FILL)? (_("Pattern fill")) : (_("Pattern stroke"));
150         _lgradient[i].set_markup (_("<b>L</b>"));
151         sp_set_font_size_smaller (GTK_WIDGET(_lgradient[i].gobj()));
152         _lgradient[i].show_all();
153         __lgradient[i] = (i == SS_FILL)? (_("Linear gradient fill")) : (_("Linear gradient stroke"));
155         _gradient_preview_l[i] =  GTK_WIDGET(sp_gradient_image_new (NULL));
156         _gradient_box_l[i].pack_start(_lgradient[i]);
157         _gradient_box_l[i].pack_start(*(Glib::wrap(_gradient_preview_l[i])));
158         _gradient_box_l[i].show_all();
160         _rgradient[i].set_markup (_("<b>R</b>"));
161         sp_set_font_size_smaller (GTK_WIDGET(_rgradient[i].gobj()));
162         _rgradient[i].show_all();
163         __rgradient[i] = (i == SS_FILL)? (_("Radial gradient fill")) : (_("Radial gradient stroke"));
165         _gradient_preview_r[i] = GTK_WIDGET(sp_gradient_image_new (NULL));
166         _gradient_box_r[i].pack_start(_rgradient[i]);
167         _gradient_box_r[i].pack_start(*(Glib::wrap(_gradient_preview_r[i])));
168         _gradient_box_r[i].show_all();
170         _many[i].set_markup (_("Different"));
171         sp_set_font_size_smaller (GTK_WIDGET(_many[i].gobj()));
172         _many[i].show_all();
173         __many[i] = (i == SS_FILL)? (_("Different fills")) : (_("Different strokes"));
175         _unset[i].set_markup (_("<b>Unset</b>"));
176         sp_set_font_size_smaller (GTK_WIDGET(_unset[i].gobj()));
177         _unset[i].show_all();
178         __unset[i] = (i == SS_FILL)? (_("Unset fill")) : (_("Unset stroke"));
180         _color_preview[i] = new Inkscape::UI::Widget::ColorPreview (0);
181         __color[i] = (i == SS_FILL)? (_("Flat color fill")) : (_("Flat color stroke"));
183         // TRANSLATOR COMMENT: A means "Averaged"
184         _averaged[i].set_markup (_("<b>a</b>"));
185         sp_set_font_size_smaller (GTK_WIDGET(_averaged[i].gobj()));
186         _averaged[i].show_all();
187         __averaged[i] = (i == SS_FILL)? (_("Fill is averaged over selected objects")) : (_("Stroke is averaged over selected objects"));
189         // TRANSLATOR COMMENT: M means "Multiple"
190         _multiple[i].set_markup (_("<b>m</b>"));
191         sp_set_font_size_smaller (GTK_WIDGET(_multiple[i].gobj()));
192         _multiple[i].show_all();
193         __multiple[i] = (i == SS_FILL)? (_("Multiple selected objects have the same fill")) : (_("Multiple selected objects have the same stroke"));
195         _popup_edit[i].add(*(new Gtk::Label((i == SS_FILL)? _("Edit fill...") : _("Edit stroke..."), 0.0, 0.5)));
196         _popup_edit[i].signal_activate().connect(sigc::mem_fun(*this, 
197                                (i == SS_FILL)? &SelectedStyle::on_fill_edit : &SelectedStyle::on_stroke_edit ));
199         _popup_lastused[i].add(*(new Gtk::Label(_("Last set color"), 0.0, 0.5)));
200         _popup_lastused[i].signal_activate().connect(sigc::mem_fun(*this, 
201                                (i == SS_FILL)? &SelectedStyle::on_fill_lastused : &SelectedStyle::on_stroke_lastused ));
203         _popup_lastselected[i].add(*(new Gtk::Label(_("Last selected color"), 0.0, 0.5)));
204         _popup_lastselected[i].signal_activate().connect(sigc::mem_fun(*this, 
205                                (i == SS_FILL)? &SelectedStyle::on_fill_lastselected : &SelectedStyle::on_stroke_lastselected ));
207         _popup_invert[i].add(*(new Gtk::Label(_("Invert"), 0.0, 0.5)));
208         _popup_invert[i].signal_activate().connect(sigc::mem_fun(*this, 
209                                (i == SS_FILL)? &SelectedStyle::on_fill_invert : &SelectedStyle::on_stroke_invert ));
211         _popup_white[i].add(*(new Gtk::Label(_("White"), 0.0, 0.5)));
212         _popup_white[i].signal_activate().connect(sigc::mem_fun(*this, 
213                                (i == SS_FILL)? &SelectedStyle::on_fill_white : &SelectedStyle::on_stroke_white ));
215         _popup_black[i].add(*(new Gtk::Label(_("Black"), 0.0, 0.5)));
216         _popup_black[i].signal_activate().connect(sigc::mem_fun(*this, 
217                                (i == SS_FILL)? &SelectedStyle::on_fill_black : &SelectedStyle::on_stroke_black ));
219         _popup_copy[i].add(*(new Gtk::Label(_("Copy color"), 0.0, 0.5)));
220         _popup_copy[i].signal_activate().connect(sigc::mem_fun(*this, 
221                                (i == SS_FILL)? &SelectedStyle::on_fill_copy : &SelectedStyle::on_stroke_copy ));
223         _popup_paste[i].add(*(new Gtk::Label(_("Paste color"), 0.0, 0.5)));
224         _popup_paste[i].signal_activate().connect(sigc::mem_fun(*this, 
225                                (i == SS_FILL)? &SelectedStyle::on_fill_paste : &SelectedStyle::on_stroke_paste ));
227         _popup_swap[i].add(*(new Gtk::Label(_("Swap fill and stroke"), 0.0, 0.5)));
228         _popup_swap[i].signal_activate().connect(sigc::mem_fun(*this, 
229                                &SelectedStyle::on_fillstroke_swap));
231         _popup_opaque[i].add(*(new Gtk::Label((i == SS_FILL)? _("Make fill opaque") : _("Make stroke opaque"), 0.0, 0.5)));
232         _popup_opaque[i].signal_activate().connect(sigc::mem_fun(*this, 
233                                (i == SS_FILL)? &SelectedStyle::on_fill_opaque : &SelectedStyle::on_stroke_opaque ));
235         //TRANSLATORS COMMENT: unset is a verb here
236         _popup_unset[i].add(*(new Gtk::Label((i == SS_FILL)? _("Unset fill") : _("Unset stroke"), 0.0, 0.5)));
237         _popup_unset[i].signal_activate().connect(sigc::mem_fun(*this, 
238                                (i == SS_FILL)? &SelectedStyle::on_fill_unset : &SelectedStyle::on_stroke_unset ));
240         _popup_remove[i].add(*(new Gtk::Label((i == SS_FILL)? _("Remove fill") : _("Remove stroke"), 0.0, 0.5)));
241         _popup_remove[i].signal_activate().connect(sigc::mem_fun(*this, 
242                                (i == SS_FILL)? &SelectedStyle::on_fill_remove : &SelectedStyle::on_stroke_remove ));
244         _popup[i].attach(_popup_edit[i], 0,1, 0,1);
245           _popup[i].attach(*(new Gtk::SeparatorMenuItem()), 0,1, 1,2);
246         _popup[i].attach(_popup_lastused[i], 0,1, 2,3);
247         _popup[i].attach(_popup_lastselected[i], 0,1, 3,4);
248           _popup[i].attach(*(new Gtk::SeparatorMenuItem()), 0,1, 4,5);
249         _popup[i].attach(_popup_invert[i], 0,1, 5,6);
250           _popup[i].attach(*(new Gtk::SeparatorMenuItem()), 0,1, 6,7);
251         _popup[i].attach(_popup_white[i], 0,1, 7,8);
252         _popup[i].attach(_popup_black[i], 0,1, 8,9);
253           _popup[i].attach(*(new Gtk::SeparatorMenuItem()), 0,1, 9,10);
254         _popup[i].attach(_popup_copy[i], 0,1, 10,11);
255         _popup_copy[i].set_sensitive(false);
256         _popup[i].attach(_popup_paste[i], 0,1, 11,12);
257         _popup[i].attach(_popup_swap[i], 0,1, 12,13);
258           _popup[i].attach(*(new Gtk::SeparatorMenuItem()), 0,1, 13,14); 
259         _popup[i].attach(_popup_opaque[i], 0,1, 14,15);
260         _popup[i].attach(_popup_unset[i], 0,1, 15,16);
261         _popup[i].attach(_popup_remove[i], 0,1, 16,17);
262         _popup[i].show_all();
264         _mode[i] = SS_NA;
265     }
267     {
268         _popup_px.add(*(new Gtk::Label(_("px"), 0.0, 0.5)));
269         _popup_px.signal_activate().connect(sigc::mem_fun(*this, &SelectedStyle::on_popup_px));
270         _popup_sw.attach(_popup_px, 0,1, 0,1);
272         _popup_pt.add(*(new Gtk::Label(_("pt"), 0.0, 0.5)));
273         _popup_pt.signal_activate().connect(sigc::mem_fun(*this, &SelectedStyle::on_popup_pt));
274         _popup_sw.attach(_popup_pt, 0,1, 1,2);
276         _popup_mm.add(*(new Gtk::Label(_("mm"), 0.0, 0.5)));
277         _popup_mm.signal_activate().connect(sigc::mem_fun(*this, &SelectedStyle::on_popup_mm));
278         _popup_sw.attach(_popup_mm, 0,1, 2,3);
280         _popup_sw.attach(*(new Gtk::SeparatorMenuItem()), 0,1, 3,4);
282         for (guint i = 0; i < G_N_ELEMENTS(_sw_presets_str); ++i) {
283             Gtk::MenuItem *mi = Gtk::manage(new Gtk::MenuItem());
284             mi->add(*(new Gtk::Label(_sw_presets_str[i], 0.0, 0.5)));
285             mi->signal_activate().connect(sigc::bind<int>(sigc::mem_fun(*this, &SelectedStyle::on_popup_preset), i));
286             _popup_sw.attach(*mi, 0,1, 4+i, 5+i);
287         }
289         guint i = G_N_ELEMENTS(_sw_presets_str) + 5;
291         _popup_sw.attach(*(new Gtk::SeparatorMenuItem()), 0,1, i,i+1);
293         _popup_sw_remove.add(*(new Gtk::Label(_("Remove"), 0.0, 0.5)));
294         _popup_sw_remove.signal_activate().connect(sigc::mem_fun(*this, &SelectedStyle::on_stroke_remove));
295         _popup_sw.attach(_popup_sw_remove, 0,1, i+1,i+2);
297         _popup_sw.show_all();
298     }
300     _fill_place.signal_button_press_event().connect(sigc::mem_fun(*this, &SelectedStyle::on_fill_click));
301     _stroke_place.signal_button_press_event().connect(sigc::mem_fun(*this, &SelectedStyle::on_stroke_click));
302     _opacity_place.signal_button_press_event().connect(sigc::mem_fun(*this, &SelectedStyle::on_opacity_click));
303     _stroke_width_place.signal_button_press_event().connect(sigc::mem_fun(*this, &SelectedStyle::on_sw_click));
305     _opacity_sb.signal_populate_popup().connect(sigc::mem_fun(*this, &SelectedStyle::on_opacity_menu));
306     _opacity_sb.signal_value_changed().connect(sigc::mem_fun(*this, &SelectedStyle::on_opacity_changed));
308     _fill_place.add(_na[SS_FILL]);
309     _tooltips.set_tip(_fill_place, __na[SS_FILL]);
311     _stroke_place.add(_na[SS_STROKE]);
312     _tooltips.set_tip(_stroke_place, __na[SS_STROKE]);
314     _stroke.pack_start(_stroke_place);
315     _stroke_width_place.add(_stroke_width);
316     _stroke.pack_start(_stroke_width_place, Gtk::PACK_SHRINK);
318     _opacity_sb.set_adjustment(_opacity_adjustment);
319     sp_set_font_size_smaller (GTK_WIDGET(_opacity_sb.gobj()));
320     _opacity_sb.set_size_request (SELECTED_STYLE_SB_WIDTH, -1);
321     _opacity_sb.set_sensitive (false);
323     _table.attach(_fill_label, 0,1, 0,1, Gtk::FILL, Gtk::SHRINK);
324     _table.attach(_stroke_label, 0,1, 1,2, Gtk::FILL, Gtk::SHRINK);
326     _table.attach(_fill_flag_place, 1,2, 0,1, Gtk::SHRINK, Gtk::SHRINK);
327     _table.attach(_stroke_flag_place, 1,2, 1,2, Gtk::SHRINK, Gtk::SHRINK);
329     _table.attach(_fill_place, 2,3, 0,1);
330     _table.attach(_stroke, 2,3, 1,2);
332     _opacity_place.add(_opacity_label);
333     _table.attach(_opacity_place, 4,5, 0,2, Gtk::SHRINK, Gtk::SHRINK);
334     _table.attach(_opacity_sb, 5,6, 0,2, Gtk::SHRINK, Gtk::SHRINK);
336     pack_start(_table, true, true, 2);
338     set_size_request (SELECTED_STYLE_WIDTH, -1);
340     sp_set_font_size_smaller (GTK_WIDGET(_opacity_label.gobj()));
341     sp_set_font_size_smaller (GTK_WIDGET(_opacity_sb.gobj()));
342     sp_set_font_size_smaller (GTK_WIDGET(_fill_place.gobj()));
343     sp_set_font_size_smaller (GTK_WIDGET(_fill_flag_place.gobj()));
344     sp_set_font_size_smaller (GTK_WIDGET(_stroke_place.gobj()));
345     sp_set_font_size_smaller (GTK_WIDGET(_stroke_flag_place.gobj()));
346     sp_set_font_size_smaller (GTK_WIDGET(_stroke_width.gobj()));
347     sp_set_font_size_smaller (GTK_WIDGET(_fill_label.gobj()));
348     sp_set_font_size_smaller (GTK_WIDGET(_stroke_label.gobj()));
350     _drop[SS_FILL] = new DropTracker();
351     ((DropTracker*)_drop[SS_FILL])->parent = this;
352     ((DropTracker*)_drop[SS_FILL])->item = SS_FILL;
354     _drop[SS_STROKE] = new DropTracker();
355     ((DropTracker*)_drop[SS_STROKE])->parent = this;
356     ((DropTracker*)_drop[SS_STROKE])->item = SS_STROKE;
358     g_signal_connect(_stroke_place.gobj(),
359                      "drag_data_received",
360                      G_CALLBACK(dragDataReceived),
361                      _drop[SS_STROKE]);
363     g_signal_connect(_fill_place.gobj(),
364                      "drag_data_received",
365                      G_CALLBACK(dragDataReceived),
366                      _drop[SS_FILL]);
369 SelectedStyle::~SelectedStyle()
371     selection_changed_connection->disconnect();
372     delete selection_changed_connection;
373     selection_modified_connection->disconnect();
374     delete selection_modified_connection;
375     subselection_changed_connection->disconnect();
376     delete subselection_changed_connection;
378     for (int i = SS_FILL; i <= SS_STROKE; i++) {
379         delete _color_preview[i];
380         // FIXME: do we need this? the destroy methods are not exported 
381         //sp_gradient_image_destroy(GTK_OBJECT(_gradient_preview_l[i]));
382         //sp_gradient_image_destroy(GTK_OBJECT(_gradient_preview_r[i]));
383     }
385     delete (DropTracker*)_drop[SS_FILL];
386     delete (DropTracker*)_drop[SS_STROKE];
389 void
390 SelectedStyle::setDesktop(SPDesktop *desktop)
392     _desktop = desktop;
393     gtk_object_set_data (GTK_OBJECT(_opacity_sb.gobj()), "dtw", _desktop->canvas);
395     Inkscape::Selection *selection = sp_desktop_selection (desktop);
397     selection_changed_connection = new sigc::connection (selection->connectChanged(
398         sigc::bind (
399             sigc::ptr_fun(&ss_selection_changed),
400             this )
401     ));
402     selection_modified_connection = new sigc::connection (selection->connectModified(
403         sigc::bind (
404             sigc::ptr_fun(&ss_selection_modified),
405             this )
406     ));
407     subselection_changed_connection = new sigc::connection (desktop->connectToolSubselectionChanged(
408         sigc::bind (
409             sigc::ptr_fun(&ss_subselection_changed),
410             this )
411     ));
413     //_sw_unit = (SPUnit *) sp_desktop_namedview(desktop)->doc_units;
416 void SelectedStyle::dragDataReceived( GtkWidget *widget,
417                                       GdkDragContext *drag_context,
418                                       gint x, gint y,
419                                       GtkSelectionData *data,
420                                       guint info,
421                                       guint event_time,
422                                       gpointer user_data )
424     DropTracker* tracker = (DropTracker*)user_data;
426     switch ( (int)tracker->item ) {
427         case SS_FILL:
428         case SS_STROKE:
429         {
430             if ( data->length == 8 ) {
431                 gchar c[64];
432                 // Careful about endian issues.
433                 guint16* dataVals = (guint16*)data->data;
434                 sp_svg_write_color( c, sizeof(c),
435                                     SP_RGBA32_U_COMPOSE(
436                                         0x0ff & (dataVals[0] >> 8),
437                                         0x0ff & (dataVals[1] >> 8),
438                                         0x0ff & (dataVals[2] >> 8),
439                                         0xff // can't have transparency in the color itself
440                                         //0x0ff & (data->data[3] >> 8),
441                                         ));
442                 SPCSSAttr *css = sp_repr_css_attr_new();
443                 sp_repr_css_set_property( css, (tracker->item == SS_FILL) ? "fill":"stroke", c );
444                 sp_desktop_set_style( tracker->parent->_desktop, css );
445                 sp_repr_css_attr_unref( css );
446                 sp_document_done( sp_desktop_document(tracker->parent->_desktop) , SP_VERB_NONE, 
447                                   _("Drop color"));
448             }
449         }
450         break;
451     }
454 void SelectedStyle::on_fill_remove() {
455     SPCSSAttr *css = sp_repr_css_attr_new ();
456     sp_repr_css_set_property (css, "fill", "none");
457     sp_desktop_set_style (_desktop, css, true, true); 
458     sp_repr_css_attr_unref (css);
459     sp_document_done (sp_desktop_document(_desktop), SP_VERB_DIALOG_FILL_STROKE,
460                       _("Remove fill"));
463 void SelectedStyle::on_stroke_remove() {
464     SPCSSAttr *css = sp_repr_css_attr_new ();
465     sp_repr_css_set_property (css, "stroke", "none");
466     sp_desktop_set_style (_desktop, css, true, true); 
467     sp_repr_css_attr_unref (css);
468     sp_document_done (sp_desktop_document(_desktop), SP_VERB_DIALOG_FILL_STROKE,
469                       _("Remove stroke"));
472 void SelectedStyle::on_fill_unset() {
473     SPCSSAttr *css = sp_repr_css_attr_new ();
474     sp_repr_css_unset_property (css, "fill");
475     sp_desktop_set_style (_desktop, css, true, true); 
476     sp_repr_css_attr_unref (css);
477     sp_document_done (sp_desktop_document(_desktop), SP_VERB_DIALOG_FILL_STROKE,
478                       _("Unset fill"));
481 void SelectedStyle::on_stroke_unset() {
482     SPCSSAttr *css = sp_repr_css_attr_new ();
483     sp_repr_css_unset_property (css, "stroke");
484     sp_repr_css_unset_property (css, "stroke-opacity");
485     sp_repr_css_unset_property (css, "stroke-width");
486     sp_repr_css_unset_property (css, "stroke-miterlimit");
487     sp_repr_css_unset_property (css, "stroke-linejoin");
488     sp_repr_css_unset_property (css, "stroke-linecap");
489     sp_repr_css_unset_property (css, "stroke-dashoffset");
490     sp_repr_css_unset_property (css, "stroke-dasharray");
491     sp_desktop_set_style (_desktop, css, true, true);
492     sp_repr_css_attr_unref (css);
493     sp_document_done (sp_desktop_document(_desktop), SP_VERB_DIALOG_FILL_STROKE,
494                       _("Unset stroke"));
497 void SelectedStyle::on_fill_opaque() {
498     SPCSSAttr *css = sp_repr_css_attr_new ();
499     sp_repr_css_set_property (css, "fill-opacity", "1");
500     sp_desktop_set_style (_desktop, css, true);
501     sp_repr_css_attr_unref (css);
502     sp_document_done (sp_desktop_document(_desktop), SP_VERB_DIALOG_FILL_STROKE,
503                       _("Make fill opaque"));
506 void SelectedStyle::on_stroke_opaque() {
507     SPCSSAttr *css = sp_repr_css_attr_new ();
508     sp_repr_css_set_property (css, "stroke-opacity", "1");
509     sp_desktop_set_style (_desktop, css, true);
510     sp_repr_css_attr_unref (css);
511     sp_document_done (sp_desktop_document(_desktop), SP_VERB_DIALOG_FILL_STROKE,
512                       _("Make fill opaque"));
515 void SelectedStyle::on_fill_lastused() {
516     SPCSSAttr *css = sp_repr_css_attr_new ();
517     guint32 color = sp_desktop_get_color(_desktop, true);
518     gchar c[64];
519     sp_svg_write_color (c, sizeof(c), color);
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 set color to fill"));
527 void SelectedStyle::on_stroke_lastused() {
528     SPCSSAttr *css = sp_repr_css_attr_new ();
529     guint32 color = sp_desktop_get_color(_desktop, false);
530     gchar c[64];
531     sp_svg_write_color (c, sizeof(c), color);
532     sp_repr_css_set_property (css, "stroke", c);
533     sp_desktop_set_style (_desktop, css);
534     sp_repr_css_attr_unref (css);
535     sp_document_done (sp_desktop_document(_desktop), SP_VERB_DIALOG_FILL_STROKE, 
536                       _("Apply last set color to stroke"));
539 void SelectedStyle::on_fill_lastselected() {
540     SPCSSAttr *css = sp_repr_css_attr_new ();
541     gchar c[64];
542     sp_svg_write_color (c, sizeof(c), _lastselected[SS_FILL]);
543     sp_repr_css_set_property (css, "fill", c);
544     sp_desktop_set_style (_desktop, css);
545     sp_repr_css_attr_unref (css);
546     sp_document_done (sp_desktop_document(_desktop), SP_VERB_DIALOG_FILL_STROKE,
547                       _("Apply last selected color to fill"));
550 void SelectedStyle::on_stroke_lastselected() {
551     SPCSSAttr *css = sp_repr_css_attr_new ();
552     gchar c[64];
553     sp_svg_write_color (c, sizeof(c), _lastselected[SS_STROKE]);
554     sp_repr_css_set_property (css, "stroke", c);
555     sp_desktop_set_style (_desktop, css);
556     sp_repr_css_attr_unref (css);
557     sp_document_done (sp_desktop_document(_desktop), SP_VERB_DIALOG_FILL_STROKE, 
558                       _("Apply last selected color to stroke"));
561 void SelectedStyle::on_fill_invert() {
562     SPCSSAttr *css = sp_repr_css_attr_new ();
563     guint32 color = _thisselected[SS_FILL];
564     gchar c[64];
565     if (_mode[SS_FILL] != SS_COLOR) return;
566     sp_svg_write_color (c, sizeof(c),
567         SP_RGBA32_U_COMPOSE(
568                 (255 - SP_RGBA32_R_U(color)),
569                 (255 - SP_RGBA32_G_U(color)),
570                 (255 - SP_RGBA32_B_U(color)),
571                 SP_RGBA32_A_U(color)
572         )
573     );
574     sp_repr_css_set_property (css, "fill", c);
575     sp_desktop_set_style (_desktop, css);
576     sp_repr_css_attr_unref (css);
577     sp_document_done (sp_desktop_document(_desktop), SP_VERB_DIALOG_FILL_STROKE,
578                       _("Invert fill"));
581 void SelectedStyle::on_stroke_invert() {
582     SPCSSAttr *css = sp_repr_css_attr_new ();
583     guint32 color = _thisselected[SS_STROKE];
584     gchar c[64];
585     if (_mode[SS_STROKE] != SS_COLOR) return;
586     sp_svg_write_color (c, sizeof(c),
587         SP_RGBA32_U_COMPOSE(
588                 (255 - SP_RGBA32_R_U(color)),
589                 (255 - SP_RGBA32_G_U(color)),
590                 (255 - SP_RGBA32_B_U(color)),
591                 SP_RGBA32_A_U(color)
592         )
593     );
594     sp_repr_css_set_property (css, "stroke", c);
595     sp_desktop_set_style (_desktop, css);
596     sp_repr_css_attr_unref (css);
597     sp_document_done (sp_desktop_document(_desktop), SP_VERB_DIALOG_FILL_STROKE,
598                       _("Invert stroke"));
599
601 void SelectedStyle::on_fill_white() {
602     SPCSSAttr *css = sp_repr_css_attr_new ();
603     gchar c[64];
604     sp_svg_write_color (c, sizeof(c), 0xffffffff);
605     sp_repr_css_set_property (css, "fill", c);
606     sp_repr_css_set_property (css, "fill-opacity", "1");
607     sp_desktop_set_style (_desktop, css);
608     sp_repr_css_attr_unref (css);
609     sp_document_done (sp_desktop_document(_desktop), SP_VERB_DIALOG_FILL_STROKE,
610                       _("White fill"));
613 void SelectedStyle::on_stroke_white() {
614     SPCSSAttr *css = sp_repr_css_attr_new ();
615     gchar c[64];
616     sp_svg_write_color (c, sizeof(c), 0xffffffff);
617     sp_repr_css_set_property (css, "stroke", c);
618     sp_repr_css_set_property (css, "stroke-opacity", "1");
619     sp_desktop_set_style (_desktop, css);
620     sp_repr_css_attr_unref (css);
621     sp_document_done (sp_desktop_document(_desktop), SP_VERB_DIALOG_FILL_STROKE, 
622                       _("White stroke"));
625 void SelectedStyle::on_fill_black() {
626     SPCSSAttr *css = sp_repr_css_attr_new ();
627     gchar c[64];
628     sp_svg_write_color (c, sizeof(c), 0x000000ff);
629     sp_repr_css_set_property (css, "fill", c);
630     sp_repr_css_set_property (css, "fill-opacity", "1.0");
631     sp_desktop_set_style (_desktop, css);
632     sp_repr_css_attr_unref (css);
633     sp_document_done (sp_desktop_document(_desktop), SP_VERB_DIALOG_FILL_STROKE, 
634                       _("Black fill"));
637 void SelectedStyle::on_stroke_black() {
638     SPCSSAttr *css = sp_repr_css_attr_new ();
639     gchar c[64];
640     sp_svg_write_color (c, sizeof(c), 0x000000ff);
641     sp_repr_css_set_property (css, "stroke", c);
642     sp_repr_css_set_property (css, "stroke-opacity", "1.0");
643     sp_desktop_set_style (_desktop, css);
644     sp_repr_css_attr_unref (css);
645     sp_document_done (sp_desktop_document(_desktop), SP_VERB_DIALOG_FILL_STROKE, 
646                       _("Black stroke"));
649 void SelectedStyle::on_fill_copy() {
650     if (_mode[SS_FILL] == SS_COLOR) {
651         gchar c[64];
652         sp_svg_write_color (c, sizeof(c), _thisselected[SS_FILL]);
653         Glib::ustring text;
654         text += c;
655         if (!text.empty()) {
656             Glib::RefPtr<Gtk::Clipboard> refClipboard = Gtk::Clipboard::get();
657             refClipboard->set_text(text);
658         }
659     }
662 void SelectedStyle::on_stroke_copy() {
663     if (_mode[SS_STROKE] == SS_COLOR) {
664         gchar c[64];
665         sp_svg_write_color (c, sizeof(c), _thisselected[SS_STROKE]);
666         Glib::ustring text;
667         text += c;
668         if (!text.empty()) {
669             Glib::RefPtr<Gtk::Clipboard> refClipboard = Gtk::Clipboard::get();
670             refClipboard->set_text(text);
671         }
672     }
675 void SelectedStyle::on_fill_paste() {
676     Glib::RefPtr<Gtk::Clipboard> refClipboard = Gtk::Clipboard::get();
677     Glib::ustring const text = refClipboard->wait_for_text();
679     if (!text.empty()) {
680         guint32 color = sp_svg_read_color(text.c_str(), 0x000000ff); // impossible value, as SVG color cannot have opacity
681         if (color == 0x000000ff) // failed to parse color string
682             return;
684         SPCSSAttr *css = sp_repr_css_attr_new ();
685         sp_repr_css_set_property (css, "fill", text.c_str());
686         sp_desktop_set_style (_desktop, css);
687         sp_repr_css_attr_unref (css);
688         sp_document_done (sp_desktop_document(_desktop), SP_VERB_DIALOG_FILL_STROKE, 
689                       _("Paste fill"));
690     }
693 void SelectedStyle::on_stroke_paste() {
694     Glib::RefPtr<Gtk::Clipboard> refClipboard = Gtk::Clipboard::get();
695     Glib::ustring const text = refClipboard->wait_for_text();
697     if (!text.empty()) {
698         guint32 color = sp_svg_read_color(text.c_str(), 0x000000ff); // impossible value, as SVG color cannot have opacity
699         if (color == 0x000000ff) // failed to parse color string
700             return;
702         SPCSSAttr *css = sp_repr_css_attr_new ();
703         sp_repr_css_set_property (css, "stroke", text.c_str());
704         sp_desktop_set_style (_desktop, css);
705         sp_repr_css_attr_unref (css);
706         sp_document_done (sp_desktop_document(_desktop), SP_VERB_DIALOG_FILL_STROKE,
707                       _("Paste stroke"));
708     }
711 void SelectedStyle::on_fillstroke_swap() {
712     SPCSSAttr *css = sp_repr_css_attr_new ();
714     switch (_mode[SS_FILL]) {
715     case SS_NA:
716     case SS_MANY:
717         break;
718     case SS_NONE:
719         sp_repr_css_set_property (css, "stroke", "none");
720         break;
721     case SS_UNSET:
722         sp_repr_css_unset_property (css, "stroke");
723         break;
724     case SS_COLOR:
725         gchar c[64];
726         sp_svg_write_color (c, sizeof(c), _thisselected[SS_FILL]);
727         sp_repr_css_set_property (css, "stroke", c);
728         break;
729     case SS_LGRADIENT:
730     case SS_RGRADIENT:
731     case SS_PATTERN:
732         sp_repr_css_set_property (css, "stroke", _paintserver_id[SS_FILL].c_str());
733         break;
734     }
736     switch (_mode[SS_STROKE]) {
737     case SS_NA:
738     case SS_MANY:
739         break;
740     case SS_NONE:
741         sp_repr_css_set_property (css, "fill", "none");
742         break;
743     case SS_UNSET:
744         sp_repr_css_unset_property (css, "fill");
745         break;
746     case SS_COLOR:
747         gchar c[64];
748         sp_svg_write_color (c, sizeof(c), _thisselected[SS_STROKE]);
749         sp_repr_css_set_property (css, "fill", c);
750         break;
751     case SS_LGRADIENT:
752     case SS_RGRADIENT:
753     case SS_PATTERN:
754         sp_repr_css_set_property (css, "fill", _paintserver_id[SS_STROKE].c_str());
755         break;
756     }
758     sp_desktop_set_style (_desktop, css);
759     sp_repr_css_attr_unref (css);
760     sp_document_done (sp_desktop_document(_desktop), SP_VERB_DIALOG_FILL_STROKE, 
761                       _("Swap fill and stroke"));
764 void SelectedStyle::on_fill_edit() {
765     if (Dialog::FillAndStroke *dialog = dynamic_cast<Dialog::FillAndStroke *>(
766             _desktop->_dlg_mgr->getDialog("FillAndStroke")))
767         dialog->showPageFill();
770 void SelectedStyle::on_stroke_edit() {
771     if (Dialog::FillAndStroke *dialog = dynamic_cast<Dialog::FillAndStroke *>(
772             _desktop->_dlg_mgr->getDialog("FillAndStroke")))
773         dialog->showPageStrokePaint();
776 bool 
777 SelectedStyle::on_fill_click(GdkEventButton *event)
779     if (event->button == 1) { // click, open fill&stroke
781         if (Dialog::FillAndStroke *dialog = dynamic_cast<Dialog::FillAndStroke *>(
782                 _desktop->_dlg_mgr->getDialog("FillAndStroke")))
783             dialog->showPageFill();
785     } else if (event->button == 3) { // right-click, popup menu
786         _popup[SS_FILL].popup(event->button, event->time);
787     } else if (event->button == 2) { // middle click, toggle none/lastcolor
788         if (_mode[SS_FILL] == SS_NONE) {
789             on_fill_lastused();
790         } else {
791             on_fill_remove();
792         }
793     }
794     return true;
797 bool 
798 SelectedStyle::on_stroke_click(GdkEventButton *event)
800     if (event->button == 1) { // click, open fill&stroke
801         if (Dialog::FillAndStroke *dialog = dynamic_cast<Dialog::FillAndStroke *>(
802                 _desktop->_dlg_mgr->getDialog("FillAndStroke")))
803             dialog->showPageStrokePaint();
804     } else if (event->button == 3) { // right-click, popup menu
805         _popup[SS_STROKE].popup(event->button, event->time);
806     } else if (event->button == 2) { // middle click, toggle none/lastcolor
807         if (_mode[SS_STROKE] == SS_NONE) {
808             on_stroke_lastused();
809         } else {
810             on_stroke_remove();
811         }
812     }
813     return true;
816 bool 
817 SelectedStyle::on_sw_click(GdkEventButton *event)
819     if (event->button == 1) { // click, open fill&stroke
820         if (Dialog::FillAndStroke *dialog = dynamic_cast<Dialog::FillAndStroke *>(
821                 _desktop->_dlg_mgr->getDialog("FillAndStroke")))
822             dialog->showPageStrokeStyle();
823     } else if (event->button == 3) { // right-click, popup menu
824         _popup_sw.popup(event->button, event->time);
825     } else if (event->button == 2) { // middle click, toggle none/lastwidth?
826         //
827     }
828     return true;
831 bool 
832 SelectedStyle::on_opacity_click(GdkEventButton *event)
834     if (event->button == 2) { // middle click
835         const char* opacity = _opacity_sb.get_value() < 50? "0.5" : (_opacity_sb.get_value() == 100? "0" : "1");
836         SPCSSAttr *css = sp_repr_css_attr_new ();
837         sp_repr_css_set_property (css, "opacity", opacity);
838         sp_desktop_set_style (_desktop, css);
839         sp_repr_css_attr_unref (css);
840         sp_document_done (sp_desktop_document (_desktop), SP_VERB_DIALOG_FILL_STROKE,
841                       _("Change opacity"));
842         return true;
843     }
845     return false;
848 void SelectedStyle::on_popup_px() {
849     _sw_unit = (SPUnit *) &(sp_unit_get_by_id(SP_UNIT_PX));
850     update();
852 void SelectedStyle::on_popup_pt() {
853     _sw_unit = (SPUnit *) &(sp_unit_get_by_id(SP_UNIT_PT));
854     update();
856 void SelectedStyle::on_popup_mm() {
857     _sw_unit = (SPUnit *) &(sp_unit_get_by_id(SP_UNIT_MM));
858     update();
861 void SelectedStyle::on_popup_preset(int i) {
862     SPCSSAttr *css = sp_repr_css_attr_new ();
863     gdouble w;
864     if (_sw_unit) {
865         w = sp_units_get_pixels (_sw_presets[i], *_sw_unit);
866     } else {
867         w = _sw_presets[i];
868     }
869     Inkscape::CSSOStringStream os;
870     os << w;
871     sp_repr_css_set_property (css, "stroke-width", os.str().c_str());
872     // FIXME: update dash patterns!
873     sp_desktop_set_style (_desktop, css, true);
874     sp_repr_css_attr_unref (css);
875     sp_document_done (sp_desktop_document(_desktop), SP_VERB_DIALOG_SWATCHES,
876                       _("Change stroke width"));
879 void
880 SelectedStyle::update()
882     if (_desktop == NULL)
883         return;
885     // create temporary style
886     SPStyle *query = sp_style_new (sp_desktop_document(_desktop));
888     for (int i = SS_FILL; i <= SS_STROKE; i++) {
889         Gtk::EventBox *place = (i == SS_FILL)? &_fill_place : &_stroke_place;
890         Gtk::EventBox *flag_place = (i == SS_FILL)? &_fill_flag_place : &_stroke_flag_place;
892         place->remove();
893         flag_place->remove();
895         _tooltips.unset_tip(*place);
896         _tooltips.unset_tip(*flag_place);
898         _mode[i] = SS_NA;
899         _paintserver_id[i].clear();
901         _popup_copy[i].set_sensitive(false);
903         // query style from desktop. This returns a result flag and fills query with the style of subselection, if any, or selection
904         int result = sp_desktop_query_style (_desktop, query, 
905                                              (i == SS_FILL)? QUERY_STYLE_PROPERTY_FILL : QUERY_STYLE_PROPERTY_STROKE);
906         switch (result) {
907         case QUERY_STYLE_NOTHING:
908             place->add(_na[i]);
909             _tooltips.set_tip(*place, __na[i]);
910             _mode[i] = SS_NA;
911             if ( _dropEnabled[i] ) {
912                 gtk_drag_dest_unset( GTK_WIDGET((i==SS_FILL) ? _fill_place.gobj():_stroke_place.gobj()) );
913                 _dropEnabled[i] = false;
914             }
915             break;
916         case QUERY_STYLE_SINGLE:
917         case QUERY_STYLE_MULTIPLE_AVERAGED:
918         case QUERY_STYLE_MULTIPLE_SAME: 
919             if ( !_dropEnabled[i] ) {
920                 gtk_drag_dest_set( GTK_WIDGET( (i==SS_FILL) ? _fill_place.gobj():_stroke_place.gobj()),
921                                    GTK_DEST_DEFAULT_ALL,
922                                    ui_drop_target_entries,
923                                    nui_drop_target_entries,
924                                    GdkDragAction(GDK_ACTION_COPY | GDK_ACTION_MOVE) );
925                 _dropEnabled[i] = true;
926             }
927             SPIPaint *paint;
928             if (i == SS_FILL) {
929                 paint = &(query->fill);
930             } else {
931                 paint = &(query->stroke);
932             }
933             if (paint->set && paint->isPaintserver()) {
934                 SPPaintServer *server = (i == SS_FILL)? SP_STYLE_FILL_SERVER (query) : SP_STYLE_STROKE_SERVER (query);
935                 if ( server ) {
936                     Inkscape::XML::Node *srepr = SP_OBJECT_REPR(server);
937                     _paintserver_id[i] += "url(#";
938                     _paintserver_id[i] += srepr->attribute("id");
939                     _paintserver_id[i] += ")";
941                     if (SP_IS_LINEARGRADIENT (server)) {
942                         SPGradient *vector = sp_gradient_get_vector(SP_GRADIENT(server), false);
943                         sp_gradient_image_set_gradient ((SPGradientImage *) _gradient_preview_l[i], vector);
944                         place->add(_gradient_box_l[i]);
945                         _tooltips.set_tip(*place, __lgradient[i]);
946                         _mode[i] = SS_LGRADIENT;
947                     } else if (SP_IS_RADIALGRADIENT (server)) {
948                         SPGradient *vector = sp_gradient_get_vector(SP_GRADIENT(server), false);
949                         sp_gradient_image_set_gradient ((SPGradientImage *) _gradient_preview_r[i], vector);
950                         place->add(_gradient_box_r[i]);
951                         _tooltips.set_tip(*place, __rgradient[i]);
952                         _mode[i] = SS_RGRADIENT;
953                     } else if (SP_IS_PATTERN (server)) {
954                         place->add(_pattern[i]);
955                         _tooltips.set_tip(*place, __pattern[i]);
956                         _mode[i] = SS_PATTERN;
957                     }
958                 } else {
959                     g_warning ("file %s: line %d: Unknown paint server", __FILE__, __LINE__);
960                 }
961             } else if (paint->set && paint->isColor()) {
962                 guint32 color = paint->value.color.toRGBA32(
963                                      SP_SCALE24_TO_FLOAT ((i == SS_FILL)? query->fill_opacity.value : query->stroke_opacity.value));
964                 _lastselected[i] = _thisselected[i];
965                 _thisselected[i] = color | 0xff; // only color, opacity === 1
966                 ((Inkscape::UI::Widget::ColorPreview*)_color_preview[i])->setRgba32 (color);
967                 _color_preview[i]->show_all();
968                 place->add(*_color_preview[i]);
969                 gchar c_string[64];
970                 g_snprintf (c_string, 64, "%06x/%.3g", color >> 8, SP_RGBA32_A_F(color));
971                 _tooltips.set_tip(*place, __color[i] + ": " + c_string);
972                 _mode[i] = SS_COLOR;
973                 _popup_copy[i].set_sensitive(true);
975             } else if (paint->set && paint->isNone()) {
976                 place->add(_none[i]);
977                 _tooltips.set_tip(*place, __none[i]);
978                 _mode[i] = SS_NONE;
979             } else if (!paint->set) {
980                 place->add(_unset[i]);
981                 _tooltips.set_tip(*place, __unset[i]);
982                 _mode[i] = SS_UNSET;
983             }
984             if (result == QUERY_STYLE_MULTIPLE_AVERAGED) {
985                 flag_place->add(_averaged[i]);
986                 _tooltips.set_tip(*flag_place, __averaged[i]);
987             } else if (result == QUERY_STYLE_MULTIPLE_SAME) {
988                 flag_place->add(_multiple[i]);
989                 _tooltips.set_tip(*flag_place, __multiple[i]);
990             }
991             break;
992         case QUERY_STYLE_MULTIPLE_DIFFERENT:
993             place->add(_many[i]);
994             _tooltips.set_tip(*place, __many[i]);
995             _mode[i] = SS_MANY;
996             break;
997         default:
998             break;
999         }
1000     }
1002 // Now query opacity
1003     _tooltips.unset_tip(_opacity_place);
1004     _tooltips.unset_tip(_opacity_sb);
1006     int result = sp_desktop_query_style (_desktop, query, QUERY_STYLE_PROPERTY_MASTEROPACITY);
1008     switch (result) {
1009     case QUERY_STYLE_NOTHING:
1010         _tooltips.set_tip(_opacity_place, _("Nothing selected"));
1011         _tooltips.set_tip(_opacity_sb, _("Nothing selected"));
1012         _opacity_sb.set_sensitive(false);
1013         break;
1014     case QUERY_STYLE_SINGLE:
1015     case QUERY_STYLE_MULTIPLE_AVERAGED:
1016     case QUERY_STYLE_MULTIPLE_SAME:
1017         _tooltips.set_tip(_opacity_place, _("Master opacity, %"));
1018         _tooltips.set_tip(_opacity_sb, _("Master opacity, %"));
1019         if (_opacity_blocked) break;
1020         _opacity_blocked = true;
1021         _opacity_sb.set_sensitive(true);
1022         _opacity_adjustment.set_value(SP_SCALE24_TO_FLOAT(query->opacity.value) * 100);
1023         _opacity_blocked = false;
1024         break;
1025     }
1027 // Now query stroke_width
1028     int result_sw = sp_desktop_query_style (_desktop, query, QUERY_STYLE_PROPERTY_STROKEWIDTH);
1029     switch (result_sw) {
1030     case QUERY_STYLE_NOTHING:
1031         _stroke_width.set_markup("");
1032         break;
1033     case QUERY_STYLE_SINGLE:
1034     case QUERY_STYLE_MULTIPLE_AVERAGED:
1035     case QUERY_STYLE_MULTIPLE_SAME: 
1036     {
1037         double w;
1038         if (_sw_unit) {
1039             w = sp_pixels_get_units(query->stroke_width.computed, *_sw_unit);
1040         } else {
1041             w = query->stroke_width.computed;
1042         }
1043         {
1044             gchar *str = g_strdup_printf(" %.3g", w);
1045             _stroke_width.set_markup(str);
1046             g_free (str);
1047         }
1048         {
1049             gchar *str = g_strdup_printf(_("Stroke width: %.5g%s%s"), 
1050                                          w, 
1051                                          _sw_unit? sp_unit_get_abbreviation(_sw_unit) : "px", 
1052                                          (result_sw == QUERY_STYLE_MULTIPLE_AVERAGED)?
1053                                          _(" (averaged)") : "");
1054             _tooltips.set_tip(_stroke_width_place, str);
1055             g_free (str);
1056         }
1057         break;
1058     }
1059     default:
1060         break;
1061     }
1063     sp_style_unref(query);
1066 void SelectedStyle::opacity_0(void) {_opacity_sb.set_value(0);}
1067 void SelectedStyle::opacity_025(void) {_opacity_sb.set_value(25);}
1068 void SelectedStyle::opacity_05(void) {_opacity_sb.set_value(50);}
1069 void SelectedStyle::opacity_075(void) {_opacity_sb.set_value(75);}
1070 void SelectedStyle::opacity_1(void) {_opacity_sb.set_value(100);}
1072 void SelectedStyle::on_opacity_menu (Gtk::Menu *menu) {
1074     Glib::ListHandle<Gtk::Widget *> children = menu->get_children();
1075     for (Glib::ListHandle<Gtk::Widget *>::iterator iter = children.begin(); iter != children.end(); iter++) {
1076         menu->remove(*(*iter));
1077     }
1079     {
1080         Gtk::MenuItem *item = new Gtk::MenuItem;
1081         item->add(*(new Gtk::Label(_("0 (transparent)"), 0, 0)));
1082         item->signal_activate().connect(sigc::mem_fun(*this, &SelectedStyle::opacity_0 ));
1083         menu->add(*item);
1084     }
1085     {
1086         Gtk::MenuItem *item = new Gtk::MenuItem;
1087         item->add(*(new Gtk::Label("25%", 0, 0)));
1088         item->signal_activate().connect(sigc::mem_fun(*this, &SelectedStyle::opacity_025 ));
1089         menu->add(*item);
1090     }
1091     {
1092         Gtk::MenuItem *item = new Gtk::MenuItem;
1093         item->add(*(new Gtk::Label("50%", 0, 0)));
1094         item->signal_activate().connect(sigc::mem_fun(*this, &SelectedStyle::opacity_05 ));
1095         menu->add(*item);
1096     }
1097     {
1098         Gtk::MenuItem *item = new Gtk::MenuItem;
1099         item->add(*(new Gtk::Label("75%", 0, 0)));
1100         item->signal_activate().connect(sigc::mem_fun(*this, &SelectedStyle::opacity_075 ));
1101         menu->add(*item);
1102     }
1103     {
1104         Gtk::MenuItem *item = new Gtk::MenuItem;
1105         item->add(*(new Gtk::Label(_("100% (opaque)"), 0, 0)));
1106         item->signal_activate().connect(sigc::mem_fun(*this, &SelectedStyle::opacity_1 ));
1107         menu->add(*item);
1108     }
1110     menu->show_all();
1113 void SelectedStyle::on_opacity_changed () {
1114     if (_opacity_blocked)
1115         return;
1116     _opacity_blocked = true;
1117     SPCSSAttr *css = sp_repr_css_attr_new ();
1118     Inkscape::CSSOStringStream os;
1119     os << CLAMP ((_opacity_adjustment.get_value() / 100), 0.0, 1.0);
1120     sp_repr_css_set_property (css, "opacity", os.str().c_str());
1121     // FIXME: workaround for GTK breakage: display interruptibility sometimes results in GTK
1122     // sending multiple value-changed events. As if when Inkscape interrupts redraw for main loop
1123     // iterations, GTK discovers that this callback hasn't finished yet, and for some weird reason
1124     // decides to add yet another value-changed event to the queue. Totally braindead if you ask
1125     // me. As a result, scrolling the spinbutton once results in runaway change until it hits 1.0
1126     // or 0.0. (And no, this is not a race with ::update, I checked that.)
1127     // Sigh. So we disable interruptibility while we're setting the new value.
1128     sp_canvas_force_full_redraw_after_interruptions(sp_desktop_canvas(_desktop), 0);
1129     sp_desktop_set_style (_desktop, css);
1130     sp_repr_css_attr_unref (css);
1131     sp_document_maybe_done (sp_desktop_document (_desktop), "fillstroke:opacity", SP_VERB_DIALOG_FILL_STROKE,
1132                       _("Change opacity"));
1133     // resume interruptibility
1134     sp_canvas_end_forced_full_redraws(sp_desktop_canvas(_desktop));
1135     spinbutton_defocus(GTK_OBJECT(_opacity_sb.gobj()));
1136     _opacity_blocked = false;
1139 } // namespace Widget
1140 } // namespace UI
1141 } // namespace Inkscape
1143 /* 
1144   Local Variables:
1145   mode:c++
1146   c-file-style:"stroustrup"
1147   c-file-offsets:((innamespace . 0)(inline-open . 0))
1148   indent-tabs-mode:nil
1149   fill-column:99
1150   End:
1151 */
1152 // vim: filetype=c++:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 :