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 ()
118 {
119 _drop[0] = _drop[1] = 0;
120 _dropEnabled[0] = _dropEnabled[1] = false;
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]);
366 }
368 SelectedStyle::~SelectedStyle()
369 {
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];
386 }
388 void
389 SelectedStyle::setDesktop(SPDesktop *desktop)
390 {
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;
413 }
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 )
422 {
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 }
451 }
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"));
460 }
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"));
469 }
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"));
478 }
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"));
494 }
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"));
503 }
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"));
512 }
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"));
524 }
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"));
536 }
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"));
547 }
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"));
558 }
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"));
578 }
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"));
610 }
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"));
622 }
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"));
634 }
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"));
646 }
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 }
659 }
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 }
672 }
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 }
690 }
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 }
708 }
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"));
761 }
763 void SelectedStyle::on_fill_edit() {
764 sp_object_properties_fill();
765 }
767 void SelectedStyle::on_stroke_edit() {
768 sp_object_properties_stroke();
769 }
771 bool
772 SelectedStyle::on_fill_click(GdkEventButton *event)
773 {
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;
786 }
788 bool
789 SelectedStyle::on_stroke_click(GdkEventButton *event)
790 {
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;
803 }
805 bool
806 SelectedStyle::on_sw_click(GdkEventButton *event)
807 {
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;
816 }
818 bool
819 SelectedStyle::on_opacity_click(GdkEventButton *event)
820 {
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;
833 }
835 void SelectedStyle::on_popup_px() {
836 _sw_unit = (SPUnit *) &(sp_unit_get_by_id(SP_UNIT_PX));
837 update();
838 }
839 void SelectedStyle::on_popup_pt() {
840 _sw_unit = (SPUnit *) &(sp_unit_get_by_id(SP_UNIT_PT));
841 update();
842 }
843 void SelectedStyle::on_popup_mm() {
844 _sw_unit = (SPUnit *) &(sp_unit_get_by_id(SP_UNIT_MM));
845 update();
846 }
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"));
864 }
866 void
867 SelectedStyle::update()
868 {
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->type == SP_PAINT_TYPE_COLOR) {
921 guint32 color = sp_color_get_rgba32_falpha (&(paint->value.color),
922 SP_SCALE24_TO_FLOAT ((i == SS_FILL)? query->fill_opacity.value : query->stroke_opacity.value));
923 _lastselected[i] = _thisselected[i];
924 _thisselected[i] = color | 0xff; // only color, opacity === 1
925 ((Inkscape::UI::Widget::ColorPreview*)_color_preview[i])->setRgba32 (color);
926 _color_preview[i]->show_all();
927 place->add(*_color_preview[i]);
928 gchar c_string[64];
929 g_snprintf (c_string, 64, "%06x/%.3g", color >> 8, SP_RGBA32_A_F(color));
930 _tooltips.set_tip(*place, __color[i] + ": " + c_string);
931 _mode[i] = SS_COLOR;
932 _popup_copy[i].set_sensitive(true);
934 } else if (paint->set && paint->type == SP_PAINT_TYPE_PAINTSERVER) {
935 SPPaintServer *server = (i == SS_FILL)? SP_STYLE_FILL_SERVER (query) : SP_STYLE_STROKE_SERVER (query);
936 if ( server ) {
937 Inkscape::XML::Node *srepr = SP_OBJECT_REPR(server);
938 _paintserver_id[i] += "url(#";
939 _paintserver_id[i] += srepr->attribute("id");
940 _paintserver_id[i] += ")";
942 if (SP_IS_LINEARGRADIENT (server)) {
943 SPGradient *vector = sp_gradient_get_vector(SP_GRADIENT(server), false);
944 sp_gradient_image_set_gradient ((SPGradientImage *) _gradient_preview_l[i], vector);
945 place->add(_gradient_box_l[i]);
946 _tooltips.set_tip(*place, __lgradient[i]);
947 _mode[i] = SS_LGRADIENT;
948 } else if (SP_IS_RADIALGRADIENT (server)) {
949 SPGradient *vector = sp_gradient_get_vector(SP_GRADIENT(server), false);
950 sp_gradient_image_set_gradient ((SPGradientImage *) _gradient_preview_r[i], vector);
951 place->add(_gradient_box_r[i]);
952 _tooltips.set_tip(*place, __rgradient[i]);
953 _mode[i] = SS_RGRADIENT;
954 } else if (SP_IS_PATTERN (server)) {
955 place->add(_pattern[i]);
956 _tooltips.set_tip(*place, __pattern[i]);
957 _mode[i] = SS_PATTERN;
958 }
959 } else {
960 g_warning ("file %s: line %d: Unknown paint server", __FILE__, __LINE__);
961 }
963 } else if (paint->set && paint->type == SP_PAINT_TYPE_NONE) {
964 place->add(_none[i]);
965 _tooltips.set_tip(*place, __none[i]);
966 _mode[i] = SS_NONE;
967 } else if (!paint->set) {
968 place->add(_unset[i]);
969 _tooltips.set_tip(*place, __unset[i]);
970 _mode[i] = SS_UNSET;
971 }
972 if (result == QUERY_STYLE_MULTIPLE_AVERAGED) {
973 flag_place->add(_averaged[i]);
974 _tooltips.set_tip(*flag_place, __averaged[i]);
975 } else if (result == QUERY_STYLE_MULTIPLE_SAME) {
976 flag_place->add(_multiple[i]);
977 _tooltips.set_tip(*flag_place, __multiple[i]);
978 }
979 break;
980 case QUERY_STYLE_MULTIPLE_DIFFERENT:
981 place->add(_many[i]);
982 _tooltips.set_tip(*place, __many[i]);
983 _mode[i] = SS_MANY;
984 break;
985 default:
986 break;
987 }
988 }
990 // Now query opacity
991 _tooltips.unset_tip(_opacity_place);
992 _tooltips.unset_tip(_opacity_sb);
994 int result = sp_desktop_query_style (_desktop, query, QUERY_STYLE_PROPERTY_MASTEROPACITY);
996 switch (result) {
997 case QUERY_STYLE_NOTHING:
998 _tooltips.set_tip(_opacity_place, _("Nothing selected"));
999 _tooltips.set_tip(_opacity_sb, _("Nothing selected"));
1000 _opacity_sb.set_sensitive(false);
1001 break;
1002 case QUERY_STYLE_SINGLE:
1003 case QUERY_STYLE_MULTIPLE_AVERAGED:
1004 case QUERY_STYLE_MULTIPLE_SAME:
1005 _tooltips.set_tip(_opacity_place, _("Master opacity, %"));
1006 _tooltips.set_tip(_opacity_sb, _("Master opacity, %"));
1007 if (_opacity_blocked) break;
1008 _opacity_blocked = true;
1009 _opacity_sb.set_sensitive(true);
1010 _opacity_adjustment.set_value(SP_SCALE24_TO_FLOAT(query->opacity.value) * 100);
1011 _opacity_blocked = false;
1012 break;
1013 }
1015 // Now query stroke_width
1016 int result_sw = sp_desktop_query_style (_desktop, query, QUERY_STYLE_PROPERTY_STROKEWIDTH);
1017 switch (result_sw) {
1018 case QUERY_STYLE_NOTHING:
1019 _stroke_width.set_markup("");
1020 break;
1021 case QUERY_STYLE_SINGLE:
1022 case QUERY_STYLE_MULTIPLE_AVERAGED:
1023 case QUERY_STYLE_MULTIPLE_SAME:
1024 {
1025 double w;
1026 if (_sw_unit) {
1027 w = sp_pixels_get_units(query->stroke_width.computed, *_sw_unit);
1028 } else {
1029 w = query->stroke_width.computed;
1030 }
1031 {
1032 gchar *str = g_strdup_printf(" %.3g", w);
1033 _stroke_width.set_markup(str);
1034 g_free (str);
1035 }
1036 {
1037 gchar *str = g_strdup_printf(_("Stroke width: %.5g%s%s"),
1038 w,
1039 _sw_unit? sp_unit_get_abbreviation(_sw_unit) : "px",
1040 (result_sw == QUERY_STYLE_MULTIPLE_AVERAGED)?
1041 _(" (averaged)") : "");
1042 _tooltips.set_tip(_stroke_width_place, str);
1043 g_free (str);
1044 }
1045 break;
1046 }
1047 default:
1048 break;
1049 }
1051 sp_style_unref(query);
1052 }
1054 void SelectedStyle::opacity_0(void) {_opacity_sb.set_value(0);}
1055 void SelectedStyle::opacity_025(void) {_opacity_sb.set_value(25);}
1056 void SelectedStyle::opacity_05(void) {_opacity_sb.set_value(50);}
1057 void SelectedStyle::opacity_075(void) {_opacity_sb.set_value(75);}
1058 void SelectedStyle::opacity_1(void) {_opacity_sb.set_value(100);}
1060 void SelectedStyle::on_opacity_menu (Gtk::Menu *menu) {
1062 Glib::ListHandle<Gtk::Widget *> children = menu->get_children();
1063 for (Glib::ListHandle<Gtk::Widget *>::iterator iter = children.begin(); iter != children.end(); iter++) {
1064 menu->remove(*(*iter));
1065 }
1067 {
1068 Gtk::MenuItem *item = new Gtk::MenuItem;
1069 item->add(*(new Gtk::Label(_("0 (transparent)"), 0, 0)));
1070 item->signal_activate().connect(sigc::mem_fun(*this, &SelectedStyle::opacity_0 ));
1071 menu->add(*item);
1072 }
1073 {
1074 Gtk::MenuItem *item = new Gtk::MenuItem;
1075 item->add(*(new Gtk::Label("25%", 0, 0)));
1076 item->signal_activate().connect(sigc::mem_fun(*this, &SelectedStyle::opacity_025 ));
1077 menu->add(*item);
1078 }
1079 {
1080 Gtk::MenuItem *item = new Gtk::MenuItem;
1081 item->add(*(new Gtk::Label("50%", 0, 0)));
1082 item->signal_activate().connect(sigc::mem_fun(*this, &SelectedStyle::opacity_05 ));
1083 menu->add(*item);
1084 }
1085 {
1086 Gtk::MenuItem *item = new Gtk::MenuItem;
1087 item->add(*(new Gtk::Label("75%", 0, 0)));
1088 item->signal_activate().connect(sigc::mem_fun(*this, &SelectedStyle::opacity_075 ));
1089 menu->add(*item);
1090 }
1091 {
1092 Gtk::MenuItem *item = new Gtk::MenuItem;
1093 item->add(*(new Gtk::Label(_("100% (opaque)"), 0, 0)));
1094 item->signal_activate().connect(sigc::mem_fun(*this, &SelectedStyle::opacity_1 ));
1095 menu->add(*item);
1096 }
1098 menu->show_all();
1099 }
1101 void SelectedStyle::on_opacity_changed () {
1102 if (_opacity_blocked)
1103 return;
1104 _opacity_blocked = true;
1105 SPCSSAttr *css = sp_repr_css_attr_new ();
1106 Inkscape::CSSOStringStream os;
1107 os << CLAMP ((_opacity_adjustment.get_value() / 100), 0.0, 1.0);
1108 sp_repr_css_set_property (css, "opacity", os.str().c_str());
1109 // FIXME: workaround for GTK breakage: display interruptibility sometimes results in GTK
1110 // sending multiple value-changed events. As if when Inkscape interrupts redraw for main loop
1111 // iterations, GTK discovers that this callback hasn't finished yet, and for some weird reason
1112 // decides to add yet another value-changed event to the queue. Totally braindead if you ask
1113 // me. As a result, scrolling the spinbutton once results in runaway change until it hits 1.0
1114 // or 0.0. (And no, this is not a race with ::update, I checked that.)
1115 // Sigh. So we disable interruptibility while we're setting the new value.
1116 sp_canvas_force_full_redraw_after_interruptions(sp_desktop_canvas(_desktop), 0);
1117 sp_desktop_set_style (_desktop, css);
1118 sp_repr_css_attr_unref (css);
1119 sp_document_maybe_done (sp_desktop_document (_desktop), "fillstroke:opacity", SP_VERB_DIALOG_FILL_STROKE,
1120 _("Change opacity"));
1121 // resume interruptibility
1122 sp_canvas_end_forced_full_redraws(sp_desktop_canvas(_desktop));
1123 spinbutton_defocus(GTK_OBJECT(_opacity_sb.gobj()));
1124 _opacity_blocked = false;
1125 }
1127 } // namespace Widget
1128 } // namespace UI
1129 } // namespace Inkscape
1131 /*
1132 Local Variables:
1133 mode:c++
1134 c-file-style:"stroustrup"
1135 c-file-offsets:((innamespace . 0)(inline-open . 0))
1136 indent-tabs-mode:nil
1137 fill-column:99
1138 End:
1139 */
1140 // vim: filetype=c++:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 :