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 "event-context.h"
42 #include "message-context.h"
43 #include "verbs.h"
44 #include "color.h"
45 #include <display/sp-canvas.h>
46 #include "pixmaps/cursor-adj-h.xpm"
47 #include "pixmaps/cursor-adj-s.xpm"
48 #include "pixmaps/cursor-adj-l.xpm"
49 #include "sp-cursor.h"
51 static gdouble const _sw_presets[] = { 32 , 16 , 10 , 8 , 6 , 4 , 3 , 2 , 1.5 , 1 , 0.75 , 0.5 , 0.25 , 0.1 };
52 static gchar const *const _sw_presets_str[] = {"32", "16", "10", "8", "6", "4", "3", "2", "1.5", "1", "0.75", "0.5", "0.25", "0.1"};
54 static void
55 ss_selection_changed (Inkscape::Selection *, gpointer data)
56 {
57 Inkscape::UI::Widget::SelectedStyle *ss = (Inkscape::UI::Widget::SelectedStyle *) data;
58 ss->update();
59 }
61 static void
62 ss_selection_modified (Inkscape::Selection *selection, guint flags, gpointer data)
63 {
64 ss_selection_changed (selection, data);
65 }
67 static void
68 ss_subselection_changed (gpointer dragger, gpointer data)
69 {
70 ss_selection_changed (NULL, data);
71 }
73 namespace Inkscape {
74 namespace UI {
75 namespace Widget {
78 typedef struct {
79 SelectedStyle* parent;
80 int item;
81 } DropTracker;
83 /* Drag and Drop */
84 typedef enum {
85 APP_X_COLOR
86 } ui_drop_target_info;
88 static GtkTargetEntry ui_drop_target_entries [] = {
89 {"application/x-color", 0, APP_X_COLOR}
90 };
92 #define ENTRIES_SIZE(n) sizeof(n)/sizeof(n[0])
93 static guint nui_drop_target_entries = ENTRIES_SIZE(ui_drop_target_entries);
96 SelectedStyle::SelectedStyle(bool layout)
97 : _desktop (NULL),
99 _table(2, 6),
100 _fill_label (_("Fill:")),
101 _stroke_label (_("Stroke:")),
102 _opacity_label (_("O:")),
104 _fill_place (SS_FILL),
105 _stroke_place (SS_STROKE),
107 _fill_flag_place (),
108 _stroke_flag_place (),
110 _opacity_place (),
111 _opacity_adjustment (100, 0.0, 100, 1.0, 10.0),
112 _opacity_sb (0.02, 0),
114 _stroke (),
115 _stroke_width (""),
117 _opacity_blocked (false),
119 _popup_px(_sw_group),
120 _popup_pt(_sw_group),
121 _popup_mm(_sw_group),
123 _sw_unit(NULL),
125 _tooltips ()
127 {
128 _drop[0] = _drop[1] = 0;
129 _dropEnabled[0] = _dropEnabled[1] = false;
131 _fill_label.set_alignment(0.0, 0.5);
132 _fill_label.set_padding(0, 0);
133 _stroke_label.set_alignment(0.0, 0.5);
134 _stroke_label.set_padding(0, 0);
135 _opacity_label.set_alignment(0.0, 0.5);
136 _opacity_label.set_padding(0, 0);
138 _table.set_col_spacings (2);
139 _table.set_row_spacings (0);
141 for (int i = SS_FILL; i <= SS_STROKE; i++) {
143 _na[i].set_markup (_("N/A"));
144 sp_set_font_size_smaller (GTK_WIDGET(_na[i].gobj()));
145 _na[i].show_all();
146 __na[i] = (_("Nothing selected"));
148 _none[i].set_markup (_("<i>None</i>"));
149 sp_set_font_size_smaller (GTK_WIDGET(_none[i].gobj()));
150 _none[i].show_all();
151 __none[i] = (i == SS_FILL)? (_("No fill")) : (_("No stroke"));
153 _pattern[i].set_markup (_("Pattern"));
154 sp_set_font_size_smaller (GTK_WIDGET(_pattern[i].gobj()));
155 _pattern[i].show_all();
156 __pattern[i] = (i == SS_FILL)? (_("Pattern fill")) : (_("Pattern stroke"));
158 _lgradient[i].set_markup (_("<b>L</b>"));
159 sp_set_font_size_smaller (GTK_WIDGET(_lgradient[i].gobj()));
160 _lgradient[i].show_all();
161 __lgradient[i] = (i == SS_FILL)? (_("Linear gradient fill")) : (_("Linear gradient stroke"));
163 _gradient_preview_l[i] = GTK_WIDGET(sp_gradient_image_new (NULL));
164 _gradient_box_l[i].pack_start(_lgradient[i]);
165 _gradient_box_l[i].pack_start(*(Glib::wrap(_gradient_preview_l[i])));
166 _gradient_box_l[i].show_all();
168 _rgradient[i].set_markup (_("<b>R</b>"));
169 sp_set_font_size_smaller (GTK_WIDGET(_rgradient[i].gobj()));
170 _rgradient[i].show_all();
171 __rgradient[i] = (i == SS_FILL)? (_("Radial gradient fill")) : (_("Radial gradient stroke"));
173 _gradient_preview_r[i] = GTK_WIDGET(sp_gradient_image_new (NULL));
174 _gradient_box_r[i].pack_start(_rgradient[i]);
175 _gradient_box_r[i].pack_start(*(Glib::wrap(_gradient_preview_r[i])));
176 _gradient_box_r[i].show_all();
178 _many[i].set_markup (_("Different"));
179 sp_set_font_size_smaller (GTK_WIDGET(_many[i].gobj()));
180 _many[i].show_all();
181 __many[i] = (i == SS_FILL)? (_("Different fills")) : (_("Different strokes"));
183 _unset[i].set_markup (_("<b>Unset</b>"));
184 sp_set_font_size_smaller (GTK_WIDGET(_unset[i].gobj()));
185 _unset[i].show_all();
186 __unset[i] = (i == SS_FILL)? (_("Unset fill")) : (_("Unset stroke"));
188 _color_preview[i] = new Inkscape::UI::Widget::ColorPreview (0);
189 __color[i] = (i == SS_FILL)? (_("Flat color fill")) : (_("Flat color stroke"));
191 // TRANSLATOR COMMENT: A means "Averaged"
192 _averaged[i].set_markup (_("<b>a</b>"));
193 sp_set_font_size_smaller (GTK_WIDGET(_averaged[i].gobj()));
194 _averaged[i].show_all();
195 __averaged[i] = (i == SS_FILL)? (_("Fill is averaged over selected objects")) : (_("Stroke is averaged over selected objects"));
197 // TRANSLATOR COMMENT: M means "Multiple"
198 _multiple[i].set_markup (_("<b>m</b>"));
199 sp_set_font_size_smaller (GTK_WIDGET(_multiple[i].gobj()));
200 _multiple[i].show_all();
201 __multiple[i] = (i == SS_FILL)? (_("Multiple selected objects have the same fill")) : (_("Multiple selected objects have the same stroke"));
203 _popup_edit[i].add(*(new Gtk::Label((i == SS_FILL)? _("Edit fill...") : _("Edit stroke..."), 0.0, 0.5)));
204 _popup_edit[i].signal_activate().connect(sigc::mem_fun(*this,
205 (i == SS_FILL)? &SelectedStyle::on_fill_edit : &SelectedStyle::on_stroke_edit ));
207 _popup_lastused[i].add(*(new Gtk::Label(_("Last set color"), 0.0, 0.5)));
208 _popup_lastused[i].signal_activate().connect(sigc::mem_fun(*this,
209 (i == SS_FILL)? &SelectedStyle::on_fill_lastused : &SelectedStyle::on_stroke_lastused ));
211 _popup_lastselected[i].add(*(new Gtk::Label(_("Last selected color"), 0.0, 0.5)));
212 _popup_lastselected[i].signal_activate().connect(sigc::mem_fun(*this,
213 (i == SS_FILL)? &SelectedStyle::on_fill_lastselected : &SelectedStyle::on_stroke_lastselected ));
215 _popup_invert[i].add(*(new Gtk::Label(_("Invert"), 0.0, 0.5)));
216 _popup_invert[i].signal_activate().connect(sigc::mem_fun(*this,
217 (i == SS_FILL)? &SelectedStyle::on_fill_invert : &SelectedStyle::on_stroke_invert ));
219 _popup_white[i].add(*(new Gtk::Label(_("White"), 0.0, 0.5)));
220 _popup_white[i].signal_activate().connect(sigc::mem_fun(*this,
221 (i == SS_FILL)? &SelectedStyle::on_fill_white : &SelectedStyle::on_stroke_white ));
223 _popup_black[i].add(*(new Gtk::Label(_("Black"), 0.0, 0.5)));
224 _popup_black[i].signal_activate().connect(sigc::mem_fun(*this,
225 (i == SS_FILL)? &SelectedStyle::on_fill_black : &SelectedStyle::on_stroke_black ));
227 _popup_copy[i].add(*(new Gtk::Label(_("Copy color"), 0.0, 0.5)));
228 _popup_copy[i].signal_activate().connect(sigc::mem_fun(*this,
229 (i == SS_FILL)? &SelectedStyle::on_fill_copy : &SelectedStyle::on_stroke_copy ));
231 _popup_paste[i].add(*(new Gtk::Label(_("Paste color"), 0.0, 0.5)));
232 _popup_paste[i].signal_activate().connect(sigc::mem_fun(*this,
233 (i == SS_FILL)? &SelectedStyle::on_fill_paste : &SelectedStyle::on_stroke_paste ));
235 _popup_swap[i].add(*(new Gtk::Label(_("Swap fill and stroke"), 0.0, 0.5)));
236 _popup_swap[i].signal_activate().connect(sigc::mem_fun(*this,
237 &SelectedStyle::on_fillstroke_swap));
239 _popup_opaque[i].add(*(new Gtk::Label((i == SS_FILL)? _("Make fill opaque") : _("Make stroke opaque"), 0.0, 0.5)));
240 _popup_opaque[i].signal_activate().connect(sigc::mem_fun(*this,
241 (i == SS_FILL)? &SelectedStyle::on_fill_opaque : &SelectedStyle::on_stroke_opaque ));
243 //TRANSLATORS COMMENT: unset is a verb here
244 _popup_unset[i].add(*(new Gtk::Label((i == SS_FILL)? _("Unset fill") : _("Unset stroke"), 0.0, 0.5)));
245 _popup_unset[i].signal_activate().connect(sigc::mem_fun(*this,
246 (i == SS_FILL)? &SelectedStyle::on_fill_unset : &SelectedStyle::on_stroke_unset ));
248 _popup_remove[i].add(*(new Gtk::Label((i == SS_FILL)? _("Remove fill") : _("Remove stroke"), 0.0, 0.5)));
249 _popup_remove[i].signal_activate().connect(sigc::mem_fun(*this,
250 (i == SS_FILL)? &SelectedStyle::on_fill_remove : &SelectedStyle::on_stroke_remove ));
252 _popup[i].attach(_popup_edit[i], 0,1, 0,1);
253 _popup[i].attach(*(new Gtk::SeparatorMenuItem()), 0,1, 1,2);
254 _popup[i].attach(_popup_lastused[i], 0,1, 2,3);
255 _popup[i].attach(_popup_lastselected[i], 0,1, 3,4);
256 _popup[i].attach(*(new Gtk::SeparatorMenuItem()), 0,1, 4,5);
257 _popup[i].attach(_popup_invert[i], 0,1, 5,6);
258 _popup[i].attach(*(new Gtk::SeparatorMenuItem()), 0,1, 6,7);
259 _popup[i].attach(_popup_white[i], 0,1, 7,8);
260 _popup[i].attach(_popup_black[i], 0,1, 8,9);
261 _popup[i].attach(*(new Gtk::SeparatorMenuItem()), 0,1, 9,10);
262 _popup[i].attach(_popup_copy[i], 0,1, 10,11);
263 _popup_copy[i].set_sensitive(false);
264 _popup[i].attach(_popup_paste[i], 0,1, 11,12);
265 _popup[i].attach(_popup_swap[i], 0,1, 12,13);
266 _popup[i].attach(*(new Gtk::SeparatorMenuItem()), 0,1, 13,14);
267 _popup[i].attach(_popup_opaque[i], 0,1, 14,15);
268 _popup[i].attach(_popup_unset[i], 0,1, 15,16);
269 _popup[i].attach(_popup_remove[i], 0,1, 16,17);
270 _popup[i].show_all();
272 _mode[i] = SS_NA;
273 }
275 {
276 _popup_px.add(*(new Gtk::Label(_("px"), 0.0, 0.5)));
277 _popup_px.signal_activate().connect(sigc::mem_fun(*this, &SelectedStyle::on_popup_px));
278 _popup_sw.attach(_popup_px, 0,1, 0,1);
280 _popup_pt.add(*(new Gtk::Label(_("pt"), 0.0, 0.5)));
281 _popup_pt.signal_activate().connect(sigc::mem_fun(*this, &SelectedStyle::on_popup_pt));
282 _popup_sw.attach(_popup_pt, 0,1, 1,2);
284 _popup_mm.add(*(new Gtk::Label(_("mm"), 0.0, 0.5)));
285 _popup_mm.signal_activate().connect(sigc::mem_fun(*this, &SelectedStyle::on_popup_mm));
286 _popup_sw.attach(_popup_mm, 0,1, 2,3);
288 _popup_sw.attach(*(new Gtk::SeparatorMenuItem()), 0,1, 3,4);
290 for (guint i = 0; i < G_N_ELEMENTS(_sw_presets_str); ++i) {
291 Gtk::MenuItem *mi = Gtk::manage(new Gtk::MenuItem());
292 mi->add(*(new Gtk::Label(_sw_presets_str[i], 0.0, 0.5)));
293 mi->signal_activate().connect(sigc::bind<int>(sigc::mem_fun(*this, &SelectedStyle::on_popup_preset), i));
294 _popup_sw.attach(*mi, 0,1, 4+i, 5+i);
295 }
297 guint i = G_N_ELEMENTS(_sw_presets_str) + 5;
299 _popup_sw.attach(*(new Gtk::SeparatorMenuItem()), 0,1, i,i+1);
301 _popup_sw_remove.add(*(new Gtk::Label(_("Remove"), 0.0, 0.5)));
302 _popup_sw_remove.signal_activate().connect(sigc::mem_fun(*this, &SelectedStyle::on_stroke_remove));
303 _popup_sw.attach(_popup_sw_remove, 0,1, i+1,i+2);
305 _popup_sw.show_all();
306 }
308 _fill_place.signal_button_release_event().connect(sigc::mem_fun(*this, &SelectedStyle::on_fill_click));
309 _stroke_place.signal_button_release_event().connect(sigc::mem_fun(*this, &SelectedStyle::on_stroke_click));
310 _opacity_place.signal_button_press_event().connect(sigc::mem_fun(*this, &SelectedStyle::on_opacity_click));
311 _stroke_width_place.signal_button_press_event().connect(sigc::mem_fun(*this, &SelectedStyle::on_sw_click));
313 _opacity_sb.signal_populate_popup().connect(sigc::mem_fun(*this, &SelectedStyle::on_opacity_menu));
314 _opacity_sb.signal_value_changed().connect(sigc::mem_fun(*this, &SelectedStyle::on_opacity_changed));
316 _fill_place.add(_na[SS_FILL]);
317 _tooltips.set_tip(_fill_place, __na[SS_FILL]);
319 _stroke_place.add(_na[SS_STROKE]);
320 _tooltips.set_tip(_stroke_place, __na[SS_STROKE]);
322 _stroke.pack_start(_stroke_place);
323 _stroke_width_place.add(_stroke_width);
324 _stroke.pack_start(_stroke_width_place, Gtk::PACK_SHRINK);
326 _opacity_sb.set_adjustment(_opacity_adjustment);
327 sp_set_font_size_smaller (GTK_WIDGET(_opacity_sb.gobj()));
328 _opacity_sb.set_size_request (SELECTED_STYLE_SB_WIDTH, -1);
329 _opacity_sb.set_sensitive (false);
331 _table.attach(_fill_label, 0,1, 0,1, Gtk::FILL, Gtk::SHRINK);
332 _table.attach(_stroke_label, 0,1, 1,2, Gtk::FILL, Gtk::SHRINK);
334 _table.attach(_fill_flag_place, 1,2, 0,1, Gtk::SHRINK, Gtk::SHRINK);
335 _table.attach(_stroke_flag_place, 1,2, 1,2, Gtk::SHRINK, Gtk::SHRINK);
337 _table.attach(_fill_place, 2,3, 0,1);
338 _table.attach(_stroke, 2,3, 1,2);
340 _opacity_place.add(_opacity_label);
341 _table.attach(_opacity_place, 4,5, 0,2, Gtk::SHRINK, Gtk::SHRINK);
342 _table.attach(_opacity_sb, 5,6, 0,2, Gtk::SHRINK, Gtk::SHRINK);
344 pack_start(_table, true, true, 2);
346 set_size_request (SELECTED_STYLE_WIDTH, -1);
348 sp_set_font_size_smaller (GTK_WIDGET(_opacity_label.gobj()));
349 sp_set_font_size_smaller (GTK_WIDGET(_opacity_sb.gobj()));
350 sp_set_font_size_smaller (GTK_WIDGET(_fill_place.gobj()));
351 sp_set_font_size_smaller (GTK_WIDGET(_fill_flag_place.gobj()));
352 sp_set_font_size_smaller (GTK_WIDGET(_stroke_place.gobj()));
353 sp_set_font_size_smaller (GTK_WIDGET(_stroke_flag_place.gobj()));
354 sp_set_font_size_smaller (GTK_WIDGET(_stroke_width.gobj()));
355 sp_set_font_size_smaller (GTK_WIDGET(_fill_label.gobj()));
356 sp_set_font_size_smaller (GTK_WIDGET(_stroke_label.gobj()));
358 _drop[SS_FILL] = new DropTracker();
359 ((DropTracker*)_drop[SS_FILL])->parent = this;
360 ((DropTracker*)_drop[SS_FILL])->item = SS_FILL;
362 _drop[SS_STROKE] = new DropTracker();
363 ((DropTracker*)_drop[SS_STROKE])->parent = this;
364 ((DropTracker*)_drop[SS_STROKE])->item = SS_STROKE;
366 g_signal_connect(_stroke_place.gobj(),
367 "drag_data_received",
368 G_CALLBACK(dragDataReceived),
369 _drop[SS_STROKE]);
371 g_signal_connect(_fill_place.gobj(),
372 "drag_data_received",
373 G_CALLBACK(dragDataReceived),
374 _drop[SS_FILL]);
376 _fill_place.parent = this;
377 _stroke_place.parent = this;
378 }
380 SelectedStyle::~SelectedStyle()
381 {
382 selection_changed_connection->disconnect();
383 delete selection_changed_connection;
384 selection_modified_connection->disconnect();
385 delete selection_modified_connection;
386 subselection_changed_connection->disconnect();
387 delete subselection_changed_connection;
389 for (int i = SS_FILL; i <= SS_STROKE; i++) {
390 delete _color_preview[i];
391 // FIXME: do we need this? the destroy methods are not exported
392 //sp_gradient_image_destroy(GTK_OBJECT(_gradient_preview_l[i]));
393 //sp_gradient_image_destroy(GTK_OBJECT(_gradient_preview_r[i]));
394 }
396 delete (DropTracker*)_drop[SS_FILL];
397 delete (DropTracker*)_drop[SS_STROKE];
398 }
400 void
401 SelectedStyle::setDesktop(SPDesktop *desktop)
402 {
403 _desktop = desktop;
404 gtk_object_set_data (GTK_OBJECT(_opacity_sb.gobj()), "dtw", _desktop->canvas);
406 Inkscape::Selection *selection = sp_desktop_selection (desktop);
408 selection_changed_connection = new sigc::connection (selection->connectChanged(
409 sigc::bind (
410 sigc::ptr_fun(&ss_selection_changed),
411 this )
412 ));
413 selection_modified_connection = new sigc::connection (selection->connectModified(
414 sigc::bind (
415 sigc::ptr_fun(&ss_selection_modified),
416 this )
417 ));
418 subselection_changed_connection = new sigc::connection (desktop->connectToolSubselectionChanged(
419 sigc::bind (
420 sigc::ptr_fun(&ss_subselection_changed),
421 this )
422 ));
424 //_sw_unit = (SPUnit *) sp_desktop_namedview(desktop)->doc_units;
425 }
427 void SelectedStyle::dragDataReceived( GtkWidget *widget,
428 GdkDragContext *drag_context,
429 gint x, gint y,
430 GtkSelectionData *data,
431 guint info,
432 guint event_time,
433 gpointer user_data )
434 {
435 DropTracker* tracker = (DropTracker*)user_data;
437 switch ( (int)tracker->item ) {
438 case SS_FILL:
439 case SS_STROKE:
440 {
441 if ( data->length == 8 ) {
442 gchar c[64];
443 // Careful about endian issues.
444 guint16* dataVals = (guint16*)data->data;
445 sp_svg_write_color( c, sizeof(c),
446 SP_RGBA32_U_COMPOSE(
447 0x0ff & (dataVals[0] >> 8),
448 0x0ff & (dataVals[1] >> 8),
449 0x0ff & (dataVals[2] >> 8),
450 0xff // can't have transparency in the color itself
451 //0x0ff & (data->data[3] >> 8),
452 ));
453 SPCSSAttr *css = sp_repr_css_attr_new();
454 sp_repr_css_set_property( css, (tracker->item == SS_FILL) ? "fill":"stroke", c );
455 sp_desktop_set_style( tracker->parent->_desktop, css );
456 sp_repr_css_attr_unref( css );
457 sp_document_done( sp_desktop_document(tracker->parent->_desktop) , SP_VERB_NONE,
458 _("Drop color"));
459 }
460 }
461 break;
462 }
463 }
465 void SelectedStyle::on_fill_remove() {
466 SPCSSAttr *css = sp_repr_css_attr_new ();
467 sp_repr_css_set_property (css, "fill", "none");
468 sp_desktop_set_style (_desktop, css, true, true);
469 sp_repr_css_attr_unref (css);
470 sp_document_done (sp_desktop_document(_desktop), SP_VERB_DIALOG_FILL_STROKE,
471 _("Remove fill"));
472 }
474 void SelectedStyle::on_stroke_remove() {
475 SPCSSAttr *css = sp_repr_css_attr_new ();
476 sp_repr_css_set_property (css, "stroke", "none");
477 sp_desktop_set_style (_desktop, css, true, true);
478 sp_repr_css_attr_unref (css);
479 sp_document_done (sp_desktop_document(_desktop), SP_VERB_DIALOG_FILL_STROKE,
480 _("Remove stroke"));
481 }
483 void SelectedStyle::on_fill_unset() {
484 SPCSSAttr *css = sp_repr_css_attr_new ();
485 sp_repr_css_unset_property (css, "fill");
486 sp_desktop_set_style (_desktop, css, true, true);
487 sp_repr_css_attr_unref (css);
488 sp_document_done (sp_desktop_document(_desktop), SP_VERB_DIALOG_FILL_STROKE,
489 _("Unset fill"));
490 }
492 void SelectedStyle::on_stroke_unset() {
493 SPCSSAttr *css = sp_repr_css_attr_new ();
494 sp_repr_css_unset_property (css, "stroke");
495 sp_repr_css_unset_property (css, "stroke-opacity");
496 sp_repr_css_unset_property (css, "stroke-width");
497 sp_repr_css_unset_property (css, "stroke-miterlimit");
498 sp_repr_css_unset_property (css, "stroke-linejoin");
499 sp_repr_css_unset_property (css, "stroke-linecap");
500 sp_repr_css_unset_property (css, "stroke-dashoffset");
501 sp_repr_css_unset_property (css, "stroke-dasharray");
502 sp_desktop_set_style (_desktop, css, true, true);
503 sp_repr_css_attr_unref (css);
504 sp_document_done (sp_desktop_document(_desktop), SP_VERB_DIALOG_FILL_STROKE,
505 _("Unset stroke"));
506 }
508 void SelectedStyle::on_fill_opaque() {
509 SPCSSAttr *css = sp_repr_css_attr_new ();
510 sp_repr_css_set_property (css, "fill-opacity", "1");
511 sp_desktop_set_style (_desktop, css, true);
512 sp_repr_css_attr_unref (css);
513 sp_document_done (sp_desktop_document(_desktop), SP_VERB_DIALOG_FILL_STROKE,
514 _("Make fill opaque"));
515 }
517 void SelectedStyle::on_stroke_opaque() {
518 SPCSSAttr *css = sp_repr_css_attr_new ();
519 sp_repr_css_set_property (css, "stroke-opacity", "1");
520 sp_desktop_set_style (_desktop, css, true);
521 sp_repr_css_attr_unref (css);
522 sp_document_done (sp_desktop_document(_desktop), SP_VERB_DIALOG_FILL_STROKE,
523 _("Make fill opaque"));
524 }
526 void SelectedStyle::on_fill_lastused() {
527 SPCSSAttr *css = sp_repr_css_attr_new ();
528 guint32 color = sp_desktop_get_color(_desktop, true);
529 gchar c[64];
530 sp_svg_write_color (c, sizeof(c), color);
531 sp_repr_css_set_property (css, "fill", 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 fill"));
536 }
538 void SelectedStyle::on_stroke_lastused() {
539 SPCSSAttr *css = sp_repr_css_attr_new ();
540 guint32 color = sp_desktop_get_color(_desktop, false);
541 gchar c[64];
542 sp_svg_write_color (c, sizeof(c), color);
543 sp_repr_css_set_property (css, "stroke", 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 set color to stroke"));
548 }
550 void SelectedStyle::on_fill_lastselected() {
551 SPCSSAttr *css = sp_repr_css_attr_new ();
552 gchar c[64];
553 sp_svg_write_color (c, sizeof(c), _lastselected[SS_FILL]);
554 sp_repr_css_set_property (css, "fill", 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 fill"));
559 }
561 void SelectedStyle::on_stroke_lastselected() {
562 SPCSSAttr *css = sp_repr_css_attr_new ();
563 gchar c[64];
564 sp_svg_write_color (c, sizeof(c), _lastselected[SS_STROKE]);
565 sp_repr_css_set_property (css, "stroke", c);
566 sp_desktop_set_style (_desktop, css);
567 sp_repr_css_attr_unref (css);
568 sp_document_done (sp_desktop_document(_desktop), SP_VERB_DIALOG_FILL_STROKE,
569 _("Apply last selected color to stroke"));
570 }
572 void SelectedStyle::on_fill_invert() {
573 SPCSSAttr *css = sp_repr_css_attr_new ();
574 guint32 color = _thisselected[SS_FILL];
575 gchar c[64];
576 if (_mode[SS_FILL] != SS_COLOR) return;
577 sp_svg_write_color (c, sizeof(c),
578 SP_RGBA32_U_COMPOSE(
579 (255 - SP_RGBA32_R_U(color)),
580 (255 - SP_RGBA32_G_U(color)),
581 (255 - SP_RGBA32_B_U(color)),
582 SP_RGBA32_A_U(color)
583 )
584 );
585 sp_repr_css_set_property (css, "fill", c);
586 sp_desktop_set_style (_desktop, css);
587 sp_repr_css_attr_unref (css);
588 sp_document_done (sp_desktop_document(_desktop), SP_VERB_DIALOG_FILL_STROKE,
589 _("Invert fill"));
590 }
592 void SelectedStyle::on_stroke_invert() {
593 SPCSSAttr *css = sp_repr_css_attr_new ();
594 guint32 color = _thisselected[SS_STROKE];
595 gchar c[64];
596 if (_mode[SS_STROKE] != SS_COLOR) return;
597 sp_svg_write_color (c, sizeof(c),
598 SP_RGBA32_U_COMPOSE(
599 (255 - SP_RGBA32_R_U(color)),
600 (255 - SP_RGBA32_G_U(color)),
601 (255 - SP_RGBA32_B_U(color)),
602 SP_RGBA32_A_U(color)
603 )
604 );
605 sp_repr_css_set_property (css, "stroke", c);
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 _("Invert stroke"));
610 }
612 void SelectedStyle::on_fill_white() {
613 SPCSSAttr *css = sp_repr_css_attr_new ();
614 gchar c[64];
615 sp_svg_write_color (c, sizeof(c), 0xffffffff);
616 sp_repr_css_set_property (css, "fill", c);
617 sp_repr_css_set_property (css, "fill-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 fill"));
622 }
624 void SelectedStyle::on_stroke_white() {
625 SPCSSAttr *css = sp_repr_css_attr_new ();
626 gchar c[64];
627 sp_svg_write_color (c, sizeof(c), 0xffffffff);
628 sp_repr_css_set_property (css, "stroke", c);
629 sp_repr_css_set_property (css, "stroke-opacity", "1");
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 _("White stroke"));
634 }
636 void SelectedStyle::on_fill_black() {
637 SPCSSAttr *css = sp_repr_css_attr_new ();
638 gchar c[64];
639 sp_svg_write_color (c, sizeof(c), 0x000000ff);
640 sp_repr_css_set_property (css, "fill", c);
641 sp_repr_css_set_property (css, "fill-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 fill"));
646 }
648 void SelectedStyle::on_stroke_black() {
649 SPCSSAttr *css = sp_repr_css_attr_new ();
650 gchar c[64];
651 sp_svg_write_color (c, sizeof(c), 0x000000ff);
652 sp_repr_css_set_property (css, "stroke", c);
653 sp_repr_css_set_property (css, "stroke-opacity", "1.0");
654 sp_desktop_set_style (_desktop, css);
655 sp_repr_css_attr_unref (css);
656 sp_document_done (sp_desktop_document(_desktop), SP_VERB_DIALOG_FILL_STROKE,
657 _("Black stroke"));
658 }
660 void SelectedStyle::on_fill_copy() {
661 if (_mode[SS_FILL] == SS_COLOR) {
662 gchar c[64];
663 sp_svg_write_color (c, sizeof(c), _thisselected[SS_FILL]);
664 Glib::ustring text;
665 text += c;
666 if (!text.empty()) {
667 Glib::RefPtr<Gtk::Clipboard> refClipboard = Gtk::Clipboard::get();
668 refClipboard->set_text(text);
669 }
670 }
671 }
673 void SelectedStyle::on_stroke_copy() {
674 if (_mode[SS_STROKE] == SS_COLOR) {
675 gchar c[64];
676 sp_svg_write_color (c, sizeof(c), _thisselected[SS_STROKE]);
677 Glib::ustring text;
678 text += c;
679 if (!text.empty()) {
680 Glib::RefPtr<Gtk::Clipboard> refClipboard = Gtk::Clipboard::get();
681 refClipboard->set_text(text);
682 }
683 }
684 }
686 void SelectedStyle::on_fill_paste() {
687 Glib::RefPtr<Gtk::Clipboard> refClipboard = Gtk::Clipboard::get();
688 Glib::ustring const text = refClipboard->wait_for_text();
690 if (!text.empty()) {
691 guint32 color = sp_svg_read_color(text.c_str(), 0x000000ff); // impossible value, as SVG color cannot have opacity
692 if (color == 0x000000ff) // failed to parse color string
693 return;
695 SPCSSAttr *css = sp_repr_css_attr_new ();
696 sp_repr_css_set_property (css, "fill", text.c_str());
697 sp_desktop_set_style (_desktop, css);
698 sp_repr_css_attr_unref (css);
699 sp_document_done (sp_desktop_document(_desktop), SP_VERB_DIALOG_FILL_STROKE,
700 _("Paste fill"));
701 }
702 }
704 void SelectedStyle::on_stroke_paste() {
705 Glib::RefPtr<Gtk::Clipboard> refClipboard = Gtk::Clipboard::get();
706 Glib::ustring const text = refClipboard->wait_for_text();
708 if (!text.empty()) {
709 guint32 color = sp_svg_read_color(text.c_str(), 0x000000ff); // impossible value, as SVG color cannot have opacity
710 if (color == 0x000000ff) // failed to parse color string
711 return;
713 SPCSSAttr *css = sp_repr_css_attr_new ();
714 sp_repr_css_set_property (css, "stroke", text.c_str());
715 sp_desktop_set_style (_desktop, css);
716 sp_repr_css_attr_unref (css);
717 sp_document_done (sp_desktop_document(_desktop), SP_VERB_DIALOG_FILL_STROKE,
718 _("Paste stroke"));
719 }
720 }
722 void SelectedStyle::on_fillstroke_swap() {
723 SPCSSAttr *css = sp_repr_css_attr_new ();
725 switch (_mode[SS_FILL]) {
726 case SS_NA:
727 case SS_MANY:
728 break;
729 case SS_NONE:
730 sp_repr_css_set_property (css, "stroke", "none");
731 break;
732 case SS_UNSET:
733 sp_repr_css_unset_property (css, "stroke");
734 break;
735 case SS_COLOR:
736 gchar c[64];
737 sp_svg_write_color (c, sizeof(c), _thisselected[SS_FILL]);
738 sp_repr_css_set_property (css, "stroke", c);
739 break;
740 case SS_LGRADIENT:
741 case SS_RGRADIENT:
742 case SS_PATTERN:
743 sp_repr_css_set_property (css, "stroke", _paintserver_id[SS_FILL].c_str());
744 break;
745 }
747 switch (_mode[SS_STROKE]) {
748 case SS_NA:
749 case SS_MANY:
750 break;
751 case SS_NONE:
752 sp_repr_css_set_property (css, "fill", "none");
753 break;
754 case SS_UNSET:
755 sp_repr_css_unset_property (css, "fill");
756 break;
757 case SS_COLOR:
758 gchar c[64];
759 sp_svg_write_color (c, sizeof(c), _thisselected[SS_STROKE]);
760 sp_repr_css_set_property (css, "fill", c);
761 break;
762 case SS_LGRADIENT:
763 case SS_RGRADIENT:
764 case SS_PATTERN:
765 sp_repr_css_set_property (css, "fill", _paintserver_id[SS_STROKE].c_str());
766 break;
767 }
769 sp_desktop_set_style (_desktop, css);
770 sp_repr_css_attr_unref (css);
771 sp_document_done (sp_desktop_document(_desktop), SP_VERB_DIALOG_FILL_STROKE,
772 _("Swap fill and stroke"));
773 }
775 void SelectedStyle::on_fill_edit() {
776 if (Dialog::FillAndStroke *dialog = dynamic_cast<Dialog::FillAndStroke *>(
777 _desktop->_dlg_mgr->getDialog("FillAndStroke")))
778 dialog->showPageFill();
779 }
781 void SelectedStyle::on_stroke_edit() {
782 if (Dialog::FillAndStroke *dialog = dynamic_cast<Dialog::FillAndStroke *>(
783 _desktop->_dlg_mgr->getDialog("FillAndStroke")))
784 dialog->showPageStrokePaint();
785 }
787 bool
788 SelectedStyle::on_fill_click(GdkEventButton *event)
789 {
790 if (event->button == 1) { // click, open fill&stroke
792 if (Dialog::FillAndStroke *dialog = dynamic_cast<Dialog::FillAndStroke *>(
793 _desktop->_dlg_mgr->getDialog("FillAndStroke")))
794 dialog->showPageFill();
796 } else if (event->button == 3) { // right-click, popup menu
797 _popup[SS_FILL].popup(event->button, event->time);
798 } else if (event->button == 2) { // middle click, toggle none/lastcolor
799 if (_mode[SS_FILL] == SS_NONE) {
800 on_fill_lastused();
801 } else {
802 on_fill_remove();
803 }
804 }
805 return true;
806 }
808 bool
809 SelectedStyle::on_stroke_click(GdkEventButton *event)
810 {
811 if (event->button == 1) { // click, open fill&stroke
812 if (Dialog::FillAndStroke *dialog = dynamic_cast<Dialog::FillAndStroke *>(
813 _desktop->_dlg_mgr->getDialog("FillAndStroke")))
814 dialog->showPageStrokePaint();
815 } else if (event->button == 3) { // right-click, popup menu
816 _popup[SS_STROKE].popup(event->button, event->time);
817 } else if (event->button == 2) { // middle click, toggle none/lastcolor
818 if (_mode[SS_STROKE] == SS_NONE) {
819 on_stroke_lastused();
820 } else {
821 on_stroke_remove();
822 }
823 }
824 return true;
825 }
827 bool
828 SelectedStyle::on_sw_click(GdkEventButton *event)
829 {
830 if (event->button == 1) { // click, open fill&stroke
831 if (Dialog::FillAndStroke *dialog = dynamic_cast<Dialog::FillAndStroke *>(
832 _desktop->_dlg_mgr->getDialog("FillAndStroke")))
833 dialog->showPageStrokeStyle();
834 } else if (event->button == 3) { // right-click, popup menu
835 _popup_sw.popup(event->button, event->time);
836 } else if (event->button == 2) { // middle click, toggle none/lastwidth?
837 //
838 }
839 return true;
840 }
842 bool
843 SelectedStyle::on_opacity_click(GdkEventButton *event)
844 {
845 if (event->button == 2) { // middle click
846 const char* opacity = _opacity_sb.get_value() < 50? "0.5" : (_opacity_sb.get_value() == 100? "0" : "1");
847 SPCSSAttr *css = sp_repr_css_attr_new ();
848 sp_repr_css_set_property (css, "opacity", opacity);
849 sp_desktop_set_style (_desktop, css);
850 sp_repr_css_attr_unref (css);
851 sp_document_done (sp_desktop_document (_desktop), SP_VERB_DIALOG_FILL_STROKE,
852 _("Change opacity"));
853 return true;
854 }
856 return false;
857 }
859 void SelectedStyle::on_popup_px() {
860 _sw_unit = (SPUnit *) &(sp_unit_get_by_id(SP_UNIT_PX));
861 update();
862 }
863 void SelectedStyle::on_popup_pt() {
864 _sw_unit = (SPUnit *) &(sp_unit_get_by_id(SP_UNIT_PT));
865 update();
866 }
867 void SelectedStyle::on_popup_mm() {
868 _sw_unit = (SPUnit *) &(sp_unit_get_by_id(SP_UNIT_MM));
869 update();
870 }
872 void SelectedStyle::on_popup_preset(int i) {
873 SPCSSAttr *css = sp_repr_css_attr_new ();
874 gdouble w;
875 if (_sw_unit) {
876 w = sp_units_get_pixels (_sw_presets[i], *_sw_unit);
877 } else {
878 w = _sw_presets[i];
879 }
880 Inkscape::CSSOStringStream os;
881 os << w;
882 sp_repr_css_set_property (css, "stroke-width", os.str().c_str());
883 // FIXME: update dash patterns!
884 sp_desktop_set_style (_desktop, css, true);
885 sp_repr_css_attr_unref (css);
886 sp_document_done (sp_desktop_document(_desktop), SP_VERB_DIALOG_SWATCHES,
887 _("Change stroke width"));
888 }
890 void
891 SelectedStyle::update()
892 {
893 if (_desktop == NULL)
894 return;
896 // create temporary style
897 SPStyle *query = sp_style_new (sp_desktop_document(_desktop));
899 for (int i = SS_FILL; i <= SS_STROKE; i++) {
900 Gtk::EventBox *place = (i == SS_FILL)? &_fill_place : &_stroke_place;
901 Gtk::EventBox *flag_place = (i == SS_FILL)? &_fill_flag_place : &_stroke_flag_place;
903 place->remove();
904 flag_place->remove();
906 _tooltips.unset_tip(*place);
907 _tooltips.unset_tip(*flag_place);
909 _mode[i] = SS_NA;
910 _paintserver_id[i].clear();
912 _popup_copy[i].set_sensitive(false);
914 // query style from desktop. This returns a result flag and fills query with the style of subselection, if any, or selection
915 int result = sp_desktop_query_style (_desktop, query,
916 (i == SS_FILL)? QUERY_STYLE_PROPERTY_FILL : QUERY_STYLE_PROPERTY_STROKE);
917 switch (result) {
918 case QUERY_STYLE_NOTHING:
919 place->add(_na[i]);
920 _tooltips.set_tip(*place, __na[i]);
921 _mode[i] = SS_NA;
922 if ( _dropEnabled[i] ) {
923 gtk_drag_dest_unset( GTK_WIDGET((i==SS_FILL) ? _fill_place.gobj():_stroke_place.gobj()) );
924 _dropEnabled[i] = false;
925 }
926 break;
927 case QUERY_STYLE_SINGLE:
928 case QUERY_STYLE_MULTIPLE_AVERAGED:
929 case QUERY_STYLE_MULTIPLE_SAME:
930 if ( !_dropEnabled[i] ) {
931 gtk_drag_dest_set( GTK_WIDGET( (i==SS_FILL) ? _fill_place.gobj():_stroke_place.gobj()),
932 GTK_DEST_DEFAULT_ALL,
933 ui_drop_target_entries,
934 nui_drop_target_entries,
935 GdkDragAction(GDK_ACTION_COPY | GDK_ACTION_MOVE) );
936 _dropEnabled[i] = true;
937 }
938 SPIPaint *paint;
939 if (i == SS_FILL) {
940 paint = &(query->fill);
941 } else {
942 paint = &(query->stroke);
943 }
944 if (paint->set && paint->isPaintserver()) {
945 SPPaintServer *server = (i == SS_FILL)? SP_STYLE_FILL_SERVER (query) : SP_STYLE_STROKE_SERVER (query);
946 if ( server ) {
947 Inkscape::XML::Node *srepr = SP_OBJECT_REPR(server);
948 _paintserver_id[i] += "url(#";
949 _paintserver_id[i] += srepr->attribute("id");
950 _paintserver_id[i] += ")";
952 if (SP_IS_LINEARGRADIENT (server)) {
953 SPGradient *vector = sp_gradient_get_vector(SP_GRADIENT(server), false);
954 sp_gradient_image_set_gradient ((SPGradientImage *) _gradient_preview_l[i], vector);
955 place->add(_gradient_box_l[i]);
956 _tooltips.set_tip(*place, __lgradient[i]);
957 _mode[i] = SS_LGRADIENT;
958 } else if (SP_IS_RADIALGRADIENT (server)) {
959 SPGradient *vector = sp_gradient_get_vector(SP_GRADIENT(server), false);
960 sp_gradient_image_set_gradient ((SPGradientImage *) _gradient_preview_r[i], vector);
961 place->add(_gradient_box_r[i]);
962 _tooltips.set_tip(*place, __rgradient[i]);
963 _mode[i] = SS_RGRADIENT;
964 } else if (SP_IS_PATTERN (server)) {
965 place->add(_pattern[i]);
966 _tooltips.set_tip(*place, __pattern[i]);
967 _mode[i] = SS_PATTERN;
968 }
969 } else {
970 g_warning ("file %s: line %d: Unknown paint server", __FILE__, __LINE__);
971 }
972 } else if (paint->set && paint->isColor()) {
973 guint32 color = paint->value.color.toRGBA32(
974 SP_SCALE24_TO_FLOAT ((i == SS_FILL)? query->fill_opacity.value : query->stroke_opacity.value));
975 _lastselected[i] = _thisselected[i];
976 _thisselected[i] = color | 0xff; // only color, opacity === 1
977 ((Inkscape::UI::Widget::ColorPreview*)_color_preview[i])->setRgba32 (color);
978 _color_preview[i]->show_all();
979 place->add(*_color_preview[i]);
980 gchar c_string[64];
981 g_snprintf (c_string, 64, "%06x/%.3g", color >> 8, SP_RGBA32_A_F(color));
982 _tooltips.set_tip(*place, __color[i] + ": " + c_string + _(", drag to adjust"));
983 _mode[i] = SS_COLOR;
984 _popup_copy[i].set_sensitive(true);
986 } else if (paint->set && paint->isNone()) {
987 place->add(_none[i]);
988 _tooltips.set_tip(*place, __none[i]);
989 _mode[i] = SS_NONE;
990 } else if (!paint->set) {
991 place->add(_unset[i]);
992 _tooltips.set_tip(*place, __unset[i]);
993 _mode[i] = SS_UNSET;
994 }
995 if (result == QUERY_STYLE_MULTIPLE_AVERAGED) {
996 flag_place->add(_averaged[i]);
997 _tooltips.set_tip(*flag_place, __averaged[i]);
998 } else if (result == QUERY_STYLE_MULTIPLE_SAME) {
999 flag_place->add(_multiple[i]);
1000 _tooltips.set_tip(*flag_place, __multiple[i]);
1001 }
1002 break;
1003 case QUERY_STYLE_MULTIPLE_DIFFERENT:
1004 place->add(_many[i]);
1005 _tooltips.set_tip(*place, __many[i]);
1006 _mode[i] = SS_MANY;
1007 break;
1008 default:
1009 break;
1010 }
1011 }
1013 // Now query opacity
1014 _tooltips.unset_tip(_opacity_place);
1015 _tooltips.unset_tip(_opacity_sb);
1017 int result = sp_desktop_query_style (_desktop, query, QUERY_STYLE_PROPERTY_MASTEROPACITY);
1019 switch (result) {
1020 case QUERY_STYLE_NOTHING:
1021 _tooltips.set_tip(_opacity_place, _("Nothing selected"));
1022 _tooltips.set_tip(_opacity_sb, _("Nothing selected"));
1023 _opacity_sb.set_sensitive(false);
1024 break;
1025 case QUERY_STYLE_SINGLE:
1026 case QUERY_STYLE_MULTIPLE_AVERAGED:
1027 case QUERY_STYLE_MULTIPLE_SAME:
1028 _tooltips.set_tip(_opacity_place, _("Opacity, %"));
1029 _tooltips.set_tip(_opacity_sb, _("Opacity, %"));
1030 if (_opacity_blocked) break;
1031 _opacity_blocked = true;
1032 _opacity_sb.set_sensitive(true);
1033 _opacity_adjustment.set_value(SP_SCALE24_TO_FLOAT(query->opacity.value) * 100);
1034 _opacity_blocked = false;
1035 break;
1036 }
1038 // Now query stroke_width
1039 int result_sw = sp_desktop_query_style (_desktop, query, QUERY_STYLE_PROPERTY_STROKEWIDTH);
1040 switch (result_sw) {
1041 case QUERY_STYLE_NOTHING:
1042 _stroke_width.set_markup("");
1043 break;
1044 case QUERY_STYLE_SINGLE:
1045 case QUERY_STYLE_MULTIPLE_AVERAGED:
1046 case QUERY_STYLE_MULTIPLE_SAME:
1047 {
1048 double w;
1049 if (_sw_unit) {
1050 w = sp_pixels_get_units(query->stroke_width.computed, *_sw_unit);
1051 } else {
1052 w = query->stroke_width.computed;
1053 }
1054 {
1055 gchar *str = g_strdup_printf(" %.3g", w);
1056 _stroke_width.set_markup(str);
1057 g_free (str);
1058 }
1059 {
1060 gchar *str = g_strdup_printf(_("Stroke width: %.5g%s%s"),
1061 w,
1062 _sw_unit? sp_unit_get_abbreviation(_sw_unit) : "px",
1063 (result_sw == QUERY_STYLE_MULTIPLE_AVERAGED)?
1064 _(" (averaged)") : "");
1065 _tooltips.set_tip(_stroke_width_place, str);
1066 g_free (str);
1067 }
1068 break;
1069 }
1070 default:
1071 break;
1072 }
1074 sp_style_unref(query);
1075 }
1077 void SelectedStyle::opacity_0(void) {_opacity_sb.set_value(0);}
1078 void SelectedStyle::opacity_025(void) {_opacity_sb.set_value(25);}
1079 void SelectedStyle::opacity_05(void) {_opacity_sb.set_value(50);}
1080 void SelectedStyle::opacity_075(void) {_opacity_sb.set_value(75);}
1081 void SelectedStyle::opacity_1(void) {_opacity_sb.set_value(100);}
1083 void SelectedStyle::on_opacity_menu (Gtk::Menu *menu) {
1085 Glib::ListHandle<Gtk::Widget *> children = menu->get_children();
1086 for (Glib::ListHandle<Gtk::Widget *>::iterator iter = children.begin(); iter != children.end(); iter++) {
1087 menu->remove(*(*iter));
1088 }
1090 {
1091 Gtk::MenuItem *item = new Gtk::MenuItem;
1092 item->add(*(new Gtk::Label(_("0 (transparent)"), 0, 0)));
1093 item->signal_activate().connect(sigc::mem_fun(*this, &SelectedStyle::opacity_0 ));
1094 menu->add(*item);
1095 }
1096 {
1097 Gtk::MenuItem *item = new Gtk::MenuItem;
1098 item->add(*(new Gtk::Label("25%", 0, 0)));
1099 item->signal_activate().connect(sigc::mem_fun(*this, &SelectedStyle::opacity_025 ));
1100 menu->add(*item);
1101 }
1102 {
1103 Gtk::MenuItem *item = new Gtk::MenuItem;
1104 item->add(*(new Gtk::Label("50%", 0, 0)));
1105 item->signal_activate().connect(sigc::mem_fun(*this, &SelectedStyle::opacity_05 ));
1106 menu->add(*item);
1107 }
1108 {
1109 Gtk::MenuItem *item = new Gtk::MenuItem;
1110 item->add(*(new Gtk::Label("75%", 0, 0)));
1111 item->signal_activate().connect(sigc::mem_fun(*this, &SelectedStyle::opacity_075 ));
1112 menu->add(*item);
1113 }
1114 {
1115 Gtk::MenuItem *item = new Gtk::MenuItem;
1116 item->add(*(new Gtk::Label(_("100% (opaque)"), 0, 0)));
1117 item->signal_activate().connect(sigc::mem_fun(*this, &SelectedStyle::opacity_1 ));
1118 menu->add(*item);
1119 }
1121 menu->show_all();
1122 }
1124 void SelectedStyle::on_opacity_changed () {
1125 if (_opacity_blocked)
1126 return;
1127 _opacity_blocked = true;
1128 SPCSSAttr *css = sp_repr_css_attr_new ();
1129 Inkscape::CSSOStringStream os;
1130 os << CLAMP ((_opacity_adjustment.get_value() / 100), 0.0, 1.0);
1131 sp_repr_css_set_property (css, "opacity", os.str().c_str());
1132 // FIXME: workaround for GTK breakage: display interruptibility sometimes results in GTK
1133 // sending multiple value-changed events. As if when Inkscape interrupts redraw for main loop
1134 // iterations, GTK discovers that this callback hasn't finished yet, and for some weird reason
1135 // decides to add yet another value-changed event to the queue. Totally braindead if you ask
1136 // me. As a result, scrolling the spinbutton once results in runaway change until it hits 1.0
1137 // or 0.0. (And no, this is not a race with ::update, I checked that.)
1138 // Sigh. So we disable interruptibility while we're setting the new value.
1139 sp_canvas_force_full_redraw_after_interruptions(sp_desktop_canvas(_desktop), 0);
1140 sp_desktop_set_style (_desktop, css);
1141 sp_repr_css_attr_unref (css);
1142 sp_document_maybe_done (sp_desktop_document (_desktop), "fillstroke:opacity", SP_VERB_DIALOG_FILL_STROKE,
1143 _("Change opacity"));
1144 // resume interruptibility
1145 sp_canvas_end_forced_full_redraws(sp_desktop_canvas(_desktop));
1146 spinbutton_defocus(GTK_OBJECT(_opacity_sb.gobj()));
1147 _opacity_blocked = false;
1148 }
1150 RotateableSwatch::RotateableSwatch(guint mode) {
1151 fillstroke = mode;
1152 startcolor_set = false;
1153 undokey = "ssrot1";
1154 cr = NULL;
1155 cr_set = false;
1156 }
1158 RotateableSwatch::~RotateableSwatch() {
1159 }
1161 double
1162 RotateableSwatch::color_adjust(float *hsl, double by, guint32 cc, guint modifier)
1163 {
1164 sp_color_rgb_to_hsl_floatv (hsl, SP_RGBA32_R_F(cc), SP_RGBA32_G_F(cc), SP_RGBA32_B_F(cc));
1166 double diff = 0;
1167 if (modifier == 2) { // saturation
1168 double old = hsl[1];
1169 if (by > 0) {
1170 hsl[1] += by * (1 - hsl[1]);
1171 } else {
1172 hsl[1] += by * (hsl[1]);
1173 }
1174 diff = hsl[1] - old;
1175 } else if (modifier == 1) { // lightness
1176 double old = hsl[2];
1177 if (by > 0) {
1178 hsl[2] += by * (1 - hsl[2]);
1179 } else {
1180 hsl[2] += by * (hsl[2]);
1181 }
1182 diff = hsl[2] - old;
1183 } else { // hue
1184 double old = hsl[0];
1185 hsl[0] += by/2;
1186 while (hsl[0] < 0)
1187 hsl[0] += 1;
1188 while (hsl[0] > 1)
1189 hsl[0] -= 1;
1190 diff = hsl[0] - old;
1191 }
1193 float rgb[3];
1194 sp_color_hsl_to_rgb_floatv (rgb, hsl[0], hsl[1], hsl[2]);
1196 gchar c[64];
1197 sp_svg_write_color (c, sizeof(c),
1198 SP_RGBA32_U_COMPOSE(
1199 (SP_COLOR_F_TO_U(rgb[0])),
1200 (SP_COLOR_F_TO_U(rgb[1])),
1201 (SP_COLOR_F_TO_U(rgb[2])),
1202 0xff
1203 )
1204 );
1206 SPCSSAttr *css = sp_repr_css_attr_new ();
1207 if (fillstroke == SS_FILL)
1208 sp_repr_css_set_property (css, "fill", c);
1209 else
1210 sp_repr_css_set_property (css, "stroke", c);
1211 sp_desktop_set_style (parent->getDesktop(), css);
1212 sp_repr_css_attr_unref (css);
1213 return diff;
1214 }
1216 void
1217 RotateableSwatch::do_motion(double by, guint modifier) {
1218 if (parent->_mode[fillstroke] != SS_COLOR)
1219 return;
1221 if (!cr_set && modifier != 3) {
1222 GtkWidget *w = GTK_WIDGET(gobj());
1224 GdkBitmap *bitmap = NULL;
1225 GdkBitmap *mask = NULL;
1226 if (modifier == 2) { // saturation
1227 sp_cursor_bitmap_and_mask_from_xpm(&bitmap, &mask, cursor_adj_s_xpm);
1228 } else if (modifier == 1) { // lightness
1229 sp_cursor_bitmap_and_mask_from_xpm(&bitmap, &mask, cursor_adj_l_xpm);
1230 } else { // hue
1231 sp_cursor_bitmap_and_mask_from_xpm(&bitmap, &mask, cursor_adj_h_xpm);
1232 }
1233 if ((bitmap != NULL) && (mask != NULL)) {
1234 cr = gdk_cursor_new_from_pixmap(bitmap, mask,
1235 &w->style->black,
1236 &w->style->white,
1237 16, 16);
1238 g_object_unref (bitmap);
1239 g_object_unref (mask);
1240 gdk_window_set_cursor(w->window, cr);
1241 cr_set = true;
1242 }
1243 }
1245 guint32 cc;
1246 if (!startcolor_set) {
1247 cc = startcolor = parent->_thisselected[fillstroke];
1248 startcolor_set = true;
1249 } else {
1250 cc = startcolor;
1251 }
1253 float hsl[3];
1254 double diff = 0;
1255 if (modifier != 3) {
1256 diff = color_adjust(hsl, by, cc, modifier);
1257 }
1259 if (modifier == 3) { // do nothing
1261 } else if (modifier == 2) { // saturation
1262 sp_document_maybe_done (sp_desktop_document(parent->getDesktop()), undokey,
1263 SP_VERB_DIALOG_FILL_STROKE, (_("Adjust saturation")));
1264 double ch = hsl[1];
1265 parent->getDesktop()->event_context->_message_context->setF(Inkscape::IMMEDIATE_MESSAGE, _("Adusting <b>saturation</b>: was %.3g, now <b>%.3g</b> (diff %.3g); without modifiers to adjust hue, with <b>Ctrl</b> to adjust lightness"), ch - diff, ch, diff);
1267 } else if (modifier == 1) { // lightness
1268 sp_document_maybe_done (sp_desktop_document(parent->getDesktop()), undokey,
1269 SP_VERB_DIALOG_FILL_STROKE, (_("Adjust lightness")));
1270 double ch = hsl[2];
1271 parent->getDesktop()->event_context->_message_context->setF(Inkscape::IMMEDIATE_MESSAGE, _("Adusting <b>lightness</b>: was %.3g, now <b>%.3g</b> (diff %.3g); without modifiers to adjust hue, with <b>Shift</b> to adjust saturation"), ch - diff, ch, diff);
1273 } else { // hue
1274 sp_document_maybe_done (sp_desktop_document(parent->getDesktop()), undokey,
1275 SP_VERB_DIALOG_FILL_STROKE, (_("Adjust hue")));
1276 double ch = hsl[0];
1277 parent->getDesktop()->event_context->_message_context->setF(Inkscape::IMMEDIATE_MESSAGE, _("Adusting <b>hue</b>: was %.3g, now <b>%.3g</b> (diff %.3g); with <b>Shift</b> to adjust saturation, with <b>Ctrl</b> to adjust lightness"), ch - diff, ch, diff);
1278 }
1279 }
1281 void
1282 RotateableSwatch::do_release(double by, guint modifier) {
1283 if (parent->_mode[fillstroke] != SS_COLOR)
1284 return;
1286 float hsl[3];
1287 if (modifier != 3) {
1288 color_adjust(hsl, by, startcolor, modifier);
1289 }
1291 if (cr_set) {
1292 GtkWidget *w = GTK_WIDGET(gobj());
1293 gdk_window_set_cursor(w->window, NULL);
1294 if (cr) {
1295 gdk_cursor_unref (cr);
1296 cr = NULL;
1297 }
1298 cr_set = false;
1299 }
1301 if (modifier == 3) { // nothing
1302 } else if (modifier == 2) { // saturation
1303 sp_document_maybe_done (sp_desktop_document(parent->getDesktop()), undokey,
1304 SP_VERB_DIALOG_FILL_STROKE, ("Adjust saturation"));
1306 } else if (modifier == 1) { // lightness
1307 sp_document_maybe_done (sp_desktop_document(parent->getDesktop()), undokey,
1308 SP_VERB_DIALOG_FILL_STROKE, ("Adjust lightness"));
1310 } else { // hue
1311 sp_document_maybe_done (sp_desktop_document(parent->getDesktop()), undokey,
1312 SP_VERB_DIALOG_FILL_STROKE, ("Adjust hue"));
1313 }
1315 if (!strcmp(undokey, "ssrot1")) {
1316 undokey = "ssrot2";
1317 } else {
1318 undokey = "ssrot1";
1319 }
1321 parent->getDesktop()->event_context->_message_context->clear();
1322 startcolor_set = false;
1323 }
1326 } // namespace Widget
1327 } // namespace UI
1328 } // namespace Inkscape
1330 /*
1331 Local Variables:
1332 mode:c++
1333 c-file-style:"stroustrup"
1334 c-file-offsets:((innamespace . 0)(inline-open . 0))
1335 indent-tabs-mode:nil
1336 fill-column:99
1337 End:
1338 */
1339 // vim: filetype=c++:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 :