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_desktop_set_style (_desktop, css, true, true);
484 sp_repr_css_attr_unref (css);
485 sp_document_done (sp_desktop_document(_desktop), SP_VERB_DIALOG_FILL_STROKE,
486 _("Unset stroke"));
487 }
489 void SelectedStyle::on_fill_opaque() {
490 SPCSSAttr *css = sp_repr_css_attr_new ();
491 sp_repr_css_set_property (css, "fill-opacity", "1");
492 sp_desktop_set_style (_desktop, css, true);
493 sp_repr_css_attr_unref (css);
494 sp_document_done (sp_desktop_document(_desktop), SP_VERB_DIALOG_FILL_STROKE,
495 _("Make fill opaque"));
496 }
498 void SelectedStyle::on_stroke_opaque() {
499 SPCSSAttr *css = sp_repr_css_attr_new ();
500 sp_repr_css_set_property (css, "stroke-opacity", "1");
501 sp_desktop_set_style (_desktop, css, true);
502 sp_repr_css_attr_unref (css);
503 sp_document_done (sp_desktop_document(_desktop), SP_VERB_DIALOG_FILL_STROKE,
504 _("Make fill opaque"));
505 }
507 void SelectedStyle::on_fill_lastused() {
508 SPCSSAttr *css = sp_repr_css_attr_new ();
509 guint32 color = sp_desktop_get_color(_desktop, true);
510 gchar c[64];
511 sp_svg_write_color (c, 64, color);
512 sp_repr_css_set_property (css, "fill", c);
513 sp_desktop_set_style (_desktop, css);
514 sp_repr_css_attr_unref (css);
515 sp_document_done (sp_desktop_document(_desktop), SP_VERB_DIALOG_FILL_STROKE,
516 _("Apply last set color to fill"));
517 }
519 void SelectedStyle::on_stroke_lastused() {
520 SPCSSAttr *css = sp_repr_css_attr_new ();
521 guint32 color = sp_desktop_get_color(_desktop, false);
522 gchar c[64];
523 sp_svg_write_color (c, 64, color);
524 sp_repr_css_set_property (css, "stroke", c);
525 sp_desktop_set_style (_desktop, css);
526 sp_repr_css_attr_unref (css);
527 sp_document_done (sp_desktop_document(_desktop), SP_VERB_DIALOG_FILL_STROKE,
528 _("Apply last set color to stroke"));
529 }
531 void SelectedStyle::on_fill_lastselected() {
532 SPCSSAttr *css = sp_repr_css_attr_new ();
533 gchar c[64];
534 sp_svg_write_color (c, 64, _lastselected[SS_FILL]);
535 sp_repr_css_set_property (css, "fill", c);
536 sp_desktop_set_style (_desktop, css);
537 sp_repr_css_attr_unref (css);
538 sp_document_done (sp_desktop_document(_desktop), SP_VERB_DIALOG_FILL_STROKE,
539 _("Apply last selected color to fill"));
540 }
542 void SelectedStyle::on_stroke_lastselected() {
543 SPCSSAttr *css = sp_repr_css_attr_new ();
544 gchar c[64];
545 sp_svg_write_color (c, 64, _lastselected[SS_STROKE]);
546 sp_repr_css_set_property (css, "stroke", c);
547 sp_desktop_set_style (_desktop, css);
548 sp_repr_css_attr_unref (css);
549 sp_document_done (sp_desktop_document(_desktop), SP_VERB_DIALOG_FILL_STROKE,
550 _("Apply last selected color to stroke"));
551 }
553 void SelectedStyle::on_fill_invert() {
554 SPCSSAttr *css = sp_repr_css_attr_new ();
555 guint32 color = _thisselected[SS_FILL];
556 gchar c[64];
557 if (_mode[SS_FILL] != SS_COLOR) return;
558 sp_svg_write_color (c, 64,
559 SP_RGBA32_U_COMPOSE(
560 (255 - SP_RGBA32_R_U(color)),
561 (255 - SP_RGBA32_G_U(color)),
562 (255 - SP_RGBA32_B_U(color)),
563 SP_RGBA32_A_U(color)
564 )
565 );
566 sp_repr_css_set_property (css, "fill", c);
567 sp_desktop_set_style (_desktop, css);
568 sp_repr_css_attr_unref (css);
569 sp_document_done (sp_desktop_document(_desktop), SP_VERB_DIALOG_FILL_STROKE,
570 _("Invert fill"));
571 }
573 void SelectedStyle::on_stroke_invert() {
574 SPCSSAttr *css = sp_repr_css_attr_new ();
575 guint32 color = _thisselected[SS_STROKE];
576 gchar c[64];
577 if (_mode[SS_STROKE] != SS_COLOR) return;
578 sp_svg_write_color (c, 64,
579 SP_RGBA32_U_COMPOSE(
580 (255 - SP_RGBA32_R_U(color)),
581 (255 - SP_RGBA32_G_U(color)),
582 (255 - SP_RGBA32_B_U(color)),
583 SP_RGBA32_A_U(color)
584 )
585 );
586 sp_repr_css_set_property (css, "stroke", c);
587 sp_desktop_set_style (_desktop, css);
588 sp_repr_css_attr_unref (css);
589 sp_document_done (sp_desktop_document(_desktop), SP_VERB_DIALOG_FILL_STROKE,
590 _("Invert stroke"));
591 }
593 void SelectedStyle::on_fill_white() {
594 SPCSSAttr *css = sp_repr_css_attr_new ();
595 gchar c[64];
596 sp_svg_write_color (c, 64, 0xffffffff);
597 sp_repr_css_set_property (css, "fill", c);
598 sp_repr_css_set_property (css, "fill-opacity", "1");
599 sp_desktop_set_style (_desktop, css);
600 sp_repr_css_attr_unref (css);
601 sp_document_done (sp_desktop_document(_desktop), SP_VERB_DIALOG_FILL_STROKE,
602 _("White fill"));
603 }
605 void SelectedStyle::on_stroke_white() {
606 SPCSSAttr *css = sp_repr_css_attr_new ();
607 gchar c[64];
608 sp_svg_write_color (c, 64, 0xffffffff);
609 sp_repr_css_set_property (css, "stroke", c);
610 sp_repr_css_set_property (css, "stroke-opacity", "1");
611 sp_desktop_set_style (_desktop, css);
612 sp_repr_css_attr_unref (css);
613 sp_document_done (sp_desktop_document(_desktop), SP_VERB_DIALOG_FILL_STROKE,
614 _("White stroke"));
615 }
617 void SelectedStyle::on_fill_black() {
618 SPCSSAttr *css = sp_repr_css_attr_new ();
619 gchar c[64];
620 sp_svg_write_color (c, 64, 0x000000ff);
621 sp_repr_css_set_property (css, "fill", c);
622 sp_repr_css_set_property (css, "fill-opacity", "1.0");
623 sp_desktop_set_style (_desktop, css);
624 sp_repr_css_attr_unref (css);
625 sp_document_done (sp_desktop_document(_desktop), SP_VERB_DIALOG_FILL_STROKE,
626 _("Black fill"));
627 }
629 void SelectedStyle::on_stroke_black() {
630 SPCSSAttr *css = sp_repr_css_attr_new ();
631 gchar c[64];
632 sp_svg_write_color (c, 64, 0x000000ff);
633 sp_repr_css_set_property (css, "stroke", c);
634 sp_repr_css_set_property (css, "stroke-opacity", "1.0");
635 sp_desktop_set_style (_desktop, css);
636 sp_repr_css_attr_unref (css);
637 sp_document_done (sp_desktop_document(_desktop), SP_VERB_DIALOG_FILL_STROKE,
638 _("Black stroke"));
639 }
641 void SelectedStyle::on_fill_copy() {
642 if (_mode[SS_FILL] == SS_COLOR) {
643 gchar c[64];
644 sp_svg_write_color (c, 64, _thisselected[SS_FILL]);
645 Glib::ustring text;
646 text += c;
647 if (!text.empty()) {
648 Glib::RefPtr<Gtk::Clipboard> refClipboard = Gtk::Clipboard::get();
649 refClipboard->set_text(text);
650 }
651 }
652 }
654 void SelectedStyle::on_stroke_copy() {
655 if (_mode[SS_STROKE] == SS_COLOR) {
656 gchar c[64];
657 sp_svg_write_color (c, 64, _thisselected[SS_STROKE]);
658 Glib::ustring text;
659 text += c;
660 if (!text.empty()) {
661 Glib::RefPtr<Gtk::Clipboard> refClipboard = Gtk::Clipboard::get();
662 refClipboard->set_text(text);
663 }
664 }
665 }
667 void SelectedStyle::on_fill_paste() {
668 Glib::RefPtr<Gtk::Clipboard> refClipboard = Gtk::Clipboard::get();
669 Glib::ustring const text = refClipboard->wait_for_text();
671 if (!text.empty()) {
672 guint32 color = sp_svg_read_color(text.c_str(), 0x000000ff); // impossible value, as SVG color cannot have opacity
673 if (color == 0x000000ff) // failed to parse color string
674 return;
676 SPCSSAttr *css = sp_repr_css_attr_new ();
677 sp_repr_css_set_property (css, "fill", text.c_str());
678 sp_desktop_set_style (_desktop, css);
679 sp_repr_css_attr_unref (css);
680 sp_document_done (sp_desktop_document(_desktop), SP_VERB_DIALOG_FILL_STROKE,
681 _("Paste fill"));
682 }
683 }
685 void SelectedStyle::on_stroke_paste() {
686 Glib::RefPtr<Gtk::Clipboard> refClipboard = Gtk::Clipboard::get();
687 Glib::ustring const text = refClipboard->wait_for_text();
689 if (!text.empty()) {
690 guint32 color = sp_svg_read_color(text.c_str(), 0x000000ff); // impossible value, as SVG color cannot have opacity
691 if (color == 0x000000ff) // failed to parse color string
692 return;
694 SPCSSAttr *css = sp_repr_css_attr_new ();
695 sp_repr_css_set_property (css, "stroke", text.c_str());
696 sp_desktop_set_style (_desktop, css);
697 sp_repr_css_attr_unref (css);
698 sp_document_done (sp_desktop_document(_desktop), SP_VERB_DIALOG_FILL_STROKE,
699 _("Paste stroke"));
700 }
701 }
703 void SelectedStyle::on_fillstroke_swap() {
704 SPCSSAttr *css = sp_repr_css_attr_new ();
706 switch (_mode[SS_FILL]) {
707 case SS_NA:
708 case SS_MANY:
709 break;
710 case SS_NONE:
711 sp_repr_css_set_property (css, "stroke", "none");
712 break;
713 case SS_UNSET:
714 sp_repr_css_unset_property (css, "stroke");
715 break;
716 case SS_COLOR:
717 gchar c[64];
718 sp_svg_write_color (c, 64, _thisselected[SS_FILL]);
719 sp_repr_css_set_property (css, "stroke", c);
720 break;
721 case SS_LGRADIENT:
722 case SS_RGRADIENT:
723 case SS_PATTERN:
724 sp_repr_css_set_property (css, "stroke", _paintserver_id[SS_FILL].c_str());
725 break;
726 }
728 switch (_mode[SS_STROKE]) {
729 case SS_NA:
730 case SS_MANY:
731 break;
732 case SS_NONE:
733 sp_repr_css_set_property (css, "fill", "none");
734 break;
735 case SS_UNSET:
736 sp_repr_css_unset_property (css, "fill");
737 break;
738 case SS_COLOR:
739 gchar c[64];
740 sp_svg_write_color (c, 64, _thisselected[SS_STROKE]);
741 sp_repr_css_set_property (css, "fill", c);
742 break;
743 case SS_LGRADIENT:
744 case SS_RGRADIENT:
745 case SS_PATTERN:
746 sp_repr_css_set_property (css, "fill", _paintserver_id[SS_STROKE].c_str());
747 break;
748 }
750 sp_desktop_set_style (_desktop, css);
751 sp_repr_css_attr_unref (css);
752 sp_document_done (sp_desktop_document(_desktop), SP_VERB_DIALOG_FILL_STROKE,
753 _("Swap fill and stroke"));
754 }
756 void SelectedStyle::on_fill_edit() {
757 sp_object_properties_fill();
758 }
760 void SelectedStyle::on_stroke_edit() {
761 sp_object_properties_stroke();
762 }
764 bool
765 SelectedStyle::on_fill_click(GdkEventButton *event)
766 {
767 if (event->button == 1) { // click, open fill&stroke
768 sp_object_properties_fill();
769 } else if (event->button == 3) { // right-click, popup menu
770 _popup[SS_FILL].popup(event->button, event->time);
771 } else if (event->button == 2) { // middle click, toggle none/lastcolor
772 if (_mode[SS_FILL] == SS_NONE) {
773 on_fill_lastused();
774 } else {
775 on_fill_remove();
776 }
777 }
778 return true;
779 }
781 bool
782 SelectedStyle::on_stroke_click(GdkEventButton *event)
783 {
784 if (event->button == 1) { // click, open fill&stroke
785 sp_object_properties_stroke();
786 } else if (event->button == 3) { // right-click, popup menu
787 _popup[SS_STROKE].popup(event->button, event->time);
788 } else if (event->button == 2) { // middle click, toggle none/lastcolor
789 if (_mode[SS_STROKE] == SS_NONE) {
790 on_stroke_lastused();
791 } else {
792 on_stroke_remove();
793 }
794 }
795 return true;
796 }
798 bool
799 SelectedStyle::on_sw_click(GdkEventButton *event)
800 {
801 if (event->button == 1) { // click, open fill&stroke
802 sp_object_properties_stroke_style ();
803 } else if (event->button == 3) { // right-click, popup menu
804 _popup_sw.popup(event->button, event->time);
805 } else if (event->button == 2) { // middle click, toggle none/lastwidth?
806 //
807 }
808 return true;
809 }
811 bool
812 SelectedStyle::on_opacity_click(GdkEventButton *event)
813 {
814 if (event->button == 2) { // middle click
815 const char* opacity = _opacity_sb.get_value() < 50? "0.5" : (_opacity_sb.get_value() == 100? "0" : "1");
816 SPCSSAttr *css = sp_repr_css_attr_new ();
817 sp_repr_css_set_property (css, "opacity", opacity);
818 sp_desktop_set_style (_desktop, css);
819 sp_repr_css_attr_unref (css);
820 sp_document_done (sp_desktop_document (_desktop), SP_VERB_DIALOG_FILL_STROKE,
821 _("Change opacity"));
822 return true;
823 }
825 return false;
826 }
828 void SelectedStyle::on_popup_px() {
829 _sw_unit = (SPUnit *) &(sp_unit_get_by_id(SP_UNIT_PX));
830 update();
831 }
832 void SelectedStyle::on_popup_pt() {
833 _sw_unit = (SPUnit *) &(sp_unit_get_by_id(SP_UNIT_PT));
834 update();
835 }
836 void SelectedStyle::on_popup_mm() {
837 _sw_unit = (SPUnit *) &(sp_unit_get_by_id(SP_UNIT_MM));
838 update();
839 }
841 void SelectedStyle::on_popup_preset(int i) {
842 SPCSSAttr *css = sp_repr_css_attr_new ();
843 gdouble w;
844 if (_sw_unit) {
845 w = sp_units_get_pixels (_sw_presets[i], *_sw_unit);
846 } else {
847 w = _sw_presets[i];
848 }
849 Inkscape::CSSOStringStream os;
850 os << w;
851 sp_repr_css_set_property (css, "stroke-width", os.str().c_str());
852 // FIXME: update dash patterns!
853 sp_desktop_set_style (_desktop, css, true);
854 sp_repr_css_attr_unref (css);
855 sp_document_done (sp_desktop_document(_desktop), SP_VERB_DIALOG_SWATCHES,
856 _("Change stroke width"));
857 }
859 void
860 SelectedStyle::update()
861 {
862 if (_desktop == NULL)
863 return;
865 // create temporary style
866 SPStyle *query = sp_style_new ();
868 for (int i = SS_FILL; i <= SS_STROKE; i++) {
869 Gtk::EventBox *place = (i == SS_FILL)? &_fill_place : &_stroke_place;
870 Gtk::EventBox *flag_place = (i == SS_FILL)? &_fill_flag_place : &_stroke_flag_place;
872 place->remove();
873 flag_place->remove();
875 _tooltips.unset_tip(*place);
876 _tooltips.unset_tip(*flag_place);
878 _mode[i] = SS_NA;
879 _paintserver_id[i].clear();
881 _popup_copy[i].set_sensitive(false);
883 // query style from desktop. This returns a result flag and fills query with the style of subselection, if any, or selection
884 int result = sp_desktop_query_style (_desktop, query,
885 (i == SS_FILL)? QUERY_STYLE_PROPERTY_FILL : QUERY_STYLE_PROPERTY_STROKE);
886 switch (result) {
887 case QUERY_STYLE_NOTHING:
888 place->add(_na[i]);
889 _tooltips.set_tip(*place, __na[i]);
890 _mode[i] = SS_NA;
891 if ( _dropEnabled[i] ) {
892 gtk_drag_dest_unset( GTK_WIDGET((i==SS_FILL) ? _fill_place.gobj():_stroke_place.gobj()) );
893 _dropEnabled[i] = false;
894 }
895 break;
896 case QUERY_STYLE_SINGLE:
897 case QUERY_STYLE_MULTIPLE_AVERAGED:
898 case QUERY_STYLE_MULTIPLE_SAME:
899 if ( !_dropEnabled[i] ) {
900 gtk_drag_dest_set( GTK_WIDGET( (i==SS_FILL) ? _fill_place.gobj():_stroke_place.gobj()),
901 GTK_DEST_DEFAULT_ALL,
902 ui_drop_target_entries,
903 nui_drop_target_entries,
904 GdkDragAction(GDK_ACTION_COPY | GDK_ACTION_MOVE) );
905 _dropEnabled[i] = true;
906 }
907 SPIPaint *paint;
908 if (i == SS_FILL) {
909 paint = &(query->fill);
910 } else {
911 paint = &(query->stroke);
912 }
913 if (paint->set && paint->type == SP_PAINT_TYPE_COLOR) {
914 guint32 color = sp_color_get_rgba32_falpha (&(paint->value.color),
915 SP_SCALE24_TO_FLOAT ((i == SS_FILL)? query->fill_opacity.value : query->stroke_opacity.value));
916 _lastselected[i] = _thisselected[i];
917 _thisselected[i] = color | 0xff; // only color, opacity === 1
918 ((Inkscape::UI::Widget::ColorPreview*)_color_preview[i])->setRgba32 (color);
919 _color_preview[i]->show_all();
920 place->add(*_color_preview[i]);
921 gchar c_string[64];
922 g_snprintf (c_string, 64, "%06x/%.3g", color >> 8, SP_RGBA32_A_F(color));
923 _tooltips.set_tip(*place, __color[i] + ": " + c_string);
924 _mode[i] = SS_COLOR;
925 _popup_copy[i].set_sensitive(true);
927 } else if (paint->set && paint->type == SP_PAINT_TYPE_PAINTSERVER) {
928 SPPaintServer *server = (i == SS_FILL)? SP_STYLE_FILL_SERVER (query) : SP_STYLE_STROKE_SERVER (query);
929 if ( server ) {
930 Inkscape::XML::Node *srepr = SP_OBJECT_REPR(server);
931 _paintserver_id[i] += "url(#";
932 _paintserver_id[i] += srepr->attribute("id");
933 _paintserver_id[i] += ")";
935 if (SP_IS_LINEARGRADIENT (server)) {
936 SPGradient *vector = sp_gradient_get_vector(SP_GRADIENT(server), false);
937 sp_gradient_image_set_gradient ((SPGradientImage *) _gradient_preview_l[i], vector);
938 place->add(_gradient_box_l[i]);
939 _tooltips.set_tip(*place, __lgradient[i]);
940 _mode[i] = SS_LGRADIENT;
941 } else if (SP_IS_RADIALGRADIENT (server)) {
942 SPGradient *vector = sp_gradient_get_vector(SP_GRADIENT(server), false);
943 sp_gradient_image_set_gradient ((SPGradientImage *) _gradient_preview_r[i], vector);
944 place->add(_gradient_box_r[i]);
945 _tooltips.set_tip(*place, __rgradient[i]);
946 _mode[i] = SS_RGRADIENT;
947 } else if (SP_IS_PATTERN (server)) {
948 place->add(_pattern[i]);
949 _tooltips.set_tip(*place, __pattern[i]);
950 _mode[i] = SS_PATTERN;
951 }
952 } else {
953 g_warning ("file %s: line %d: Unknown paint server", __FILE__, __LINE__);
954 }
956 } else if (paint->set && paint->type == SP_PAINT_TYPE_NONE) {
957 place->add(_none[i]);
958 _tooltips.set_tip(*place, __none[i]);
959 _mode[i] = SS_NONE;
960 } else if (!paint->set) {
961 place->add(_unset[i]);
962 _tooltips.set_tip(*place, __unset[i]);
963 _mode[i] = SS_UNSET;
964 }
965 if (result == QUERY_STYLE_MULTIPLE_AVERAGED) {
966 flag_place->add(_averaged[i]);
967 _tooltips.set_tip(*flag_place, __averaged[i]);
968 } else if (result == QUERY_STYLE_MULTIPLE_SAME) {
969 flag_place->add(_multiple[i]);
970 _tooltips.set_tip(*flag_place, __multiple[i]);
971 }
972 break;
973 case QUERY_STYLE_MULTIPLE_DIFFERENT:
974 place->add(_many[i]);
975 _tooltips.set_tip(*place, __many[i]);
976 _mode[i] = SS_MANY;
977 break;
978 default:
979 break;
980 }
981 }
983 // Now query opacity
984 _tooltips.unset_tip(_opacity_place);
985 _tooltips.unset_tip(_opacity_sb);
987 int result = sp_desktop_query_style (_desktop, query, QUERY_STYLE_PROPERTY_MASTEROPACITY);
989 switch (result) {
990 case QUERY_STYLE_NOTHING:
991 _tooltips.set_tip(_opacity_place, _("Nothing selected"));
992 _tooltips.set_tip(_opacity_sb, _("Nothing selected"));
993 _opacity_sb.set_sensitive(false);
994 break;
995 case QUERY_STYLE_SINGLE:
996 case QUERY_STYLE_MULTIPLE_AVERAGED:
997 case QUERY_STYLE_MULTIPLE_SAME:
998 _tooltips.set_tip(_opacity_place, _("Master opacity, %"));
999 _tooltips.set_tip(_opacity_sb, _("Master opacity, %"));
1000 if (_opacity_blocked) break;
1001 _opacity_blocked = true;
1002 _opacity_sb.set_sensitive(true);
1003 _opacity_adjustment.set_value(SP_SCALE24_TO_FLOAT(query->opacity.value) * 100);
1004 _opacity_blocked = false;
1005 break;
1006 }
1008 // Now query stroke_width
1009 int result_sw = sp_desktop_query_style (_desktop, query, QUERY_STYLE_PROPERTY_STROKEWIDTH);
1010 switch (result_sw) {
1011 case QUERY_STYLE_NOTHING:
1012 _stroke_width.set_markup("");
1013 break;
1014 case QUERY_STYLE_SINGLE:
1015 case QUERY_STYLE_MULTIPLE_AVERAGED:
1016 case QUERY_STYLE_MULTIPLE_SAME:
1017 {
1018 double w;
1019 if (_sw_unit) {
1020 w = sp_pixels_get_units(query->stroke_width.computed, *_sw_unit);
1021 } else {
1022 w = query->stroke_width.computed;
1023 }
1024 {
1025 gchar *str = g_strdup_printf(" %.3g", w);
1026 _stroke_width.set_markup(str);
1027 g_free (str);
1028 }
1029 {
1030 gchar *str = g_strdup_printf(_("Stroke width: %.5g%s%s"),
1031 w,
1032 _sw_unit? sp_unit_get_abbreviation(_sw_unit) : "px",
1033 (result_sw == QUERY_STYLE_MULTIPLE_AVERAGED)?
1034 _(" (averaged)") : "");
1035 _tooltips.set_tip(_stroke_width_place, str);
1036 g_free (str);
1037 }
1038 break;
1039 }
1040 default:
1041 break;
1042 }
1044 g_free (query);
1045 }
1047 void SelectedStyle::opacity_0(void) {_opacity_sb.set_value(0);}
1048 void SelectedStyle::opacity_025(void) {_opacity_sb.set_value(25);}
1049 void SelectedStyle::opacity_05(void) {_opacity_sb.set_value(50);}
1050 void SelectedStyle::opacity_075(void) {_opacity_sb.set_value(75);}
1051 void SelectedStyle::opacity_1(void) {_opacity_sb.set_value(100);}
1053 void SelectedStyle::on_opacity_menu (Gtk::Menu *menu) {
1055 Glib::ListHandle<Gtk::Widget *> children = menu->get_children();
1056 for (Glib::ListHandle<Gtk::Widget *>::iterator iter = children.begin(); iter != children.end(); iter++) {
1057 menu->remove(*(*iter));
1058 }
1060 {
1061 Gtk::MenuItem *item = new Gtk::MenuItem;
1062 item->add(*(new Gtk::Label(_("0 (transparent)"), 0, 0)));
1063 item->signal_activate().connect(sigc::mem_fun(*this, &SelectedStyle::opacity_0 ));
1064 menu->add(*item);
1065 }
1066 {
1067 Gtk::MenuItem *item = new Gtk::MenuItem;
1068 item->add(*(new Gtk::Label("25%", 0, 0)));
1069 item->signal_activate().connect(sigc::mem_fun(*this, &SelectedStyle::opacity_025 ));
1070 menu->add(*item);
1071 }
1072 {
1073 Gtk::MenuItem *item = new Gtk::MenuItem;
1074 item->add(*(new Gtk::Label("50%", 0, 0)));
1075 item->signal_activate().connect(sigc::mem_fun(*this, &SelectedStyle::opacity_05 ));
1076 menu->add(*item);
1077 }
1078 {
1079 Gtk::MenuItem *item = new Gtk::MenuItem;
1080 item->add(*(new Gtk::Label("75%", 0, 0)));
1081 item->signal_activate().connect(sigc::mem_fun(*this, &SelectedStyle::opacity_075 ));
1082 menu->add(*item);
1083 }
1084 {
1085 Gtk::MenuItem *item = new Gtk::MenuItem;
1086 item->add(*(new Gtk::Label(_("100% (opaque)"), 0, 0)));
1087 item->signal_activate().connect(sigc::mem_fun(*this, &SelectedStyle::opacity_1 ));
1088 menu->add(*item);
1089 }
1091 menu->show_all();
1092 }
1094 void SelectedStyle::on_opacity_changed () {
1095 if (_opacity_blocked)
1096 return;
1097 _opacity_blocked = true;
1098 SPCSSAttr *css = sp_repr_css_attr_new ();
1099 Inkscape::CSSOStringStream os;
1100 os << CLAMP ((_opacity_adjustment.get_value() / 100), 0.0, 1.0);
1101 sp_repr_css_set_property (css, "opacity", os.str().c_str());
1102 // FIXME: workaround for GTK breakage: display interruptibility sometimes results in GTK
1103 // sending multiple value-changed events. As if when Inkscape interrupts redraw for main loop
1104 // iterations, GTK discovers that this callback hasn't finished yet, and for some weird reason
1105 // decides to add yet another value-changed event to the queue. Totally braindead if you ask
1106 // me. As a result, scrolling the spinbutton once results in runaway change until it hits 1.0
1107 // or 0.0. (And no, this is not a race with ::update, I checked that.)
1108 // Sigh. So we disable interruptibility while we're setting the new value.
1109 sp_canvas_force_full_redraw_after_interruptions(sp_desktop_canvas(_desktop), 0);
1110 sp_desktop_set_style (_desktop, css);
1111 sp_repr_css_attr_unref (css);
1112 sp_document_maybe_done (sp_desktop_document (_desktop), "fillstroke:opacity", SP_VERB_DIALOG_FILL_STROKE,
1113 _("Change opacity"));
1114 // resume interruptibility
1115 sp_canvas_end_forced_full_redraws(sp_desktop_canvas(_desktop));
1116 spinbutton_defocus(GTK_OBJECT(_opacity_sb.gobj()));
1117 _opacity_blocked = false;
1118 }
1120 } // namespace Widget
1121 } // namespace UI
1122 } // namespace Inkscape
1124 /*
1125 Local Variables:
1126 mode:c++
1127 c-file-style:"stroustrup"
1128 c-file-offsets:((innamespace . 0)(inline-open . 0))
1129 indent-tabs-mode:nil
1130 fill-column:99
1131 End:
1132 */
1133 // vim: filetype=c++:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 :