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 "ui/dialog/panel-dialog.h"
33 #include "xml/repr.h"
34 #include "document.h"
35 #include "widgets/widget-sizes.h"
36 #include "widgets/spinbutton-events.h"
37 #include "widgets/gradient-image.h"
38 #include "sp-gradient.h"
39 #include "svg/svg-color.h"
40 #include "svg/css-ostringstream.h"
41 #include "helper/units.h"
42 #include "event-context.h"
43 #include "message-context.h"
44 #include "verbs.h"
45 #include "color.h"
46 #include <display/sp-canvas.h>
47 #include "pixmaps/cursor-adj-h.xpm"
48 #include "pixmaps/cursor-adj-s.xpm"
49 #include "pixmaps/cursor-adj-l.xpm"
50 #include "sp-cursor.h"
52 static gdouble const _sw_presets[] = { 32 , 16 , 10 , 8 , 6 , 4 , 3 , 2 , 1.5 , 1 , 0.75 , 0.5 , 0.25 , 0.1 };
53 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"};
55 static void
56 ss_selection_changed (Inkscape::Selection *, gpointer data)
57 {
58 Inkscape::UI::Widget::SelectedStyle *ss = (Inkscape::UI::Widget::SelectedStyle *) data;
59 ss->update();
60 }
62 static void
63 ss_selection_modified( Inkscape::Selection *selection, guint /*flags*/, gpointer data )
64 {
65 ss_selection_changed (selection, data);
66 }
68 static void
69 ss_subselection_changed( gpointer /*dragger*/, gpointer data )
70 {
71 ss_selection_changed (NULL, data);
72 }
74 namespace Inkscape {
75 namespace UI {
76 namespace Widget {
79 typedef struct {
80 SelectedStyle* parent;
81 int item;
82 } DropTracker;
84 /* Drag and Drop */
85 typedef enum {
86 APP_X_COLOR
87 } ui_drop_target_info;
89 //TODO: warning: deprecated conversion from string constant to ‘gchar*’
90 //
91 //Turn out to be warnings that we should probably leave in place. The
92 // pointers/types used need to be read-only. So until we correct the using
93 // code, those warnings are actually desired. They say "Hey! Fix this". We
94 // definitely don't want to hide/ignore them. --JonCruz
95 static GtkTargetEntry ui_drop_target_entries [] = {
96 {"application/x-color", 0, APP_X_COLOR}
97 };
99 #define ENTRIES_SIZE(n) sizeof(n)/sizeof(n[0])
100 static guint nui_drop_target_entries = ENTRIES_SIZE(ui_drop_target_entries);
102 /* convenience function */
103 static Dialog::FillAndStroke *get_fill_and_stroke_panel(SPDesktop *desktop);
105 SelectedStyle::SelectedStyle(bool /*layout*/)
106 :
107 current_stroke_width(0),
109 _desktop (NULL),
111 _table(2, 6),
112 _fill_label (_("Fill:")),
113 _stroke_label (_("Stroke:")),
114 _opacity_label (_("O:")),
116 _fill_place(this, SS_FILL),
117 _stroke_place(this, SS_STROKE),
119 _fill_flag_place (),
120 _stroke_flag_place (),
122 _opacity_place (),
123 _opacity_adjustment (100, 0.0, 100, 1.0, 10.0),
124 _opacity_sb (0.02, 0),
126 _stroke (),
127 _stroke_width_place(this),
128 _stroke_width (""),
130 _opacity_blocked (false),
132 _popup_px(_sw_group),
133 _popup_pt(_sw_group),
134 _popup_mm(_sw_group),
136 _sw_unit(NULL),
138 _tooltips ()
140 {
141 _drop[0] = _drop[1] = 0;
142 _dropEnabled[0] = _dropEnabled[1] = false;
144 _fill_label.set_alignment(0.0, 0.5);
145 _fill_label.set_padding(0, 0);
146 _stroke_label.set_alignment(0.0, 0.5);
147 _stroke_label.set_padding(0, 0);
148 _opacity_label.set_alignment(0.0, 0.5);
149 _opacity_label.set_padding(0, 0);
151 _table.set_col_spacings (2);
152 _table.set_row_spacings (0);
154 for (int i = SS_FILL; i <= SS_STROKE; i++) {
156 _na[i].set_markup (_("N/A"));
157 sp_set_font_size_smaller (GTK_WIDGET(_na[i].gobj()));
158 _na[i].show_all();
159 __na[i] = (_("Nothing selected"));
161 _none[i].set_markup (_("<i>None</i>"));
162 sp_set_font_size_smaller (GTK_WIDGET(_none[i].gobj()));
163 _none[i].show_all();
164 __none[i] = (i == SS_FILL)? (_("No fill")) : (_("No stroke"));
166 _pattern[i].set_markup (_("Pattern"));
167 sp_set_font_size_smaller (GTK_WIDGET(_pattern[i].gobj()));
168 _pattern[i].show_all();
169 __pattern[i] = (i == SS_FILL)? (_("Pattern fill")) : (_("Pattern stroke"));
171 _lgradient[i].set_markup (_("<b>L</b>"));
172 sp_set_font_size_smaller (GTK_WIDGET(_lgradient[i].gobj()));
173 _lgradient[i].show_all();
174 __lgradient[i] = (i == SS_FILL)? (_("Linear gradient fill")) : (_("Linear gradient stroke"));
176 _gradient_preview_l[i] = GTK_WIDGET(sp_gradient_image_new (NULL));
177 _gradient_box_l[i].pack_start(_lgradient[i]);
178 _gradient_box_l[i].pack_start(*(Glib::wrap(_gradient_preview_l[i])));
179 _gradient_box_l[i].show_all();
181 _rgradient[i].set_markup (_("<b>R</b>"));
182 sp_set_font_size_smaller (GTK_WIDGET(_rgradient[i].gobj()));
183 _rgradient[i].show_all();
184 __rgradient[i] = (i == SS_FILL)? (_("Radial gradient fill")) : (_("Radial gradient stroke"));
186 _gradient_preview_r[i] = GTK_WIDGET(sp_gradient_image_new (NULL));
187 _gradient_box_r[i].pack_start(_rgradient[i]);
188 _gradient_box_r[i].pack_start(*(Glib::wrap(_gradient_preview_r[i])));
189 _gradient_box_r[i].show_all();
191 _many[i].set_markup (_("Different"));
192 sp_set_font_size_smaller (GTK_WIDGET(_many[i].gobj()));
193 _many[i].show_all();
194 __many[i] = (i == SS_FILL)? (_("Different fills")) : (_("Different strokes"));
196 _unset[i].set_markup (_("<b>Unset</b>"));
197 sp_set_font_size_smaller (GTK_WIDGET(_unset[i].gobj()));
198 _unset[i].show_all();
199 __unset[i] = (i == SS_FILL)? (_("Unset fill")) : (_("Unset stroke"));
201 _color_preview[i] = new Inkscape::UI::Widget::ColorPreview (0);
202 __color[i] = (i == SS_FILL)? (_("Flat color fill")) : (_("Flat color stroke"));
204 // TRANSLATOR COMMENT: A means "Averaged"
205 _averaged[i].set_markup (_("<b>a</b>"));
206 sp_set_font_size_smaller (GTK_WIDGET(_averaged[i].gobj()));
207 _averaged[i].show_all();
208 __averaged[i] = (i == SS_FILL)? (_("Fill is averaged over selected objects")) : (_("Stroke is averaged over selected objects"));
210 // TRANSLATOR COMMENT: M means "Multiple"
211 _multiple[i].set_markup (_("<b>m</b>"));
212 sp_set_font_size_smaller (GTK_WIDGET(_multiple[i].gobj()));
213 _multiple[i].show_all();
214 __multiple[i] = (i == SS_FILL)? (_("Multiple selected objects have the same fill")) : (_("Multiple selected objects have the same stroke"));
216 _popup_edit[i].add(*(new Gtk::Label((i == SS_FILL)? _("Edit fill...") : _("Edit stroke..."), 0.0, 0.5)));
217 _popup_edit[i].signal_activate().connect(sigc::mem_fun(*this,
218 (i == SS_FILL)? &SelectedStyle::on_fill_edit : &SelectedStyle::on_stroke_edit ));
220 _popup_lastused[i].add(*(new Gtk::Label(_("Last set color"), 0.0, 0.5)));
221 _popup_lastused[i].signal_activate().connect(sigc::mem_fun(*this,
222 (i == SS_FILL)? &SelectedStyle::on_fill_lastused : &SelectedStyle::on_stroke_lastused ));
224 _popup_lastselected[i].add(*(new Gtk::Label(_("Last selected color"), 0.0, 0.5)));
225 _popup_lastselected[i].signal_activate().connect(sigc::mem_fun(*this,
226 (i == SS_FILL)? &SelectedStyle::on_fill_lastselected : &SelectedStyle::on_stroke_lastselected ));
228 _popup_invert[i].add(*(new Gtk::Label(_("Invert"), 0.0, 0.5)));
229 _popup_invert[i].signal_activate().connect(sigc::mem_fun(*this,
230 (i == SS_FILL)? &SelectedStyle::on_fill_invert : &SelectedStyle::on_stroke_invert ));
232 _popup_white[i].add(*(new Gtk::Label(_("White"), 0.0, 0.5)));
233 _popup_white[i].signal_activate().connect(sigc::mem_fun(*this,
234 (i == SS_FILL)? &SelectedStyle::on_fill_white : &SelectedStyle::on_stroke_white ));
236 _popup_black[i].add(*(new Gtk::Label(_("Black"), 0.0, 0.5)));
237 _popup_black[i].signal_activate().connect(sigc::mem_fun(*this,
238 (i == SS_FILL)? &SelectedStyle::on_fill_black : &SelectedStyle::on_stroke_black ));
240 _popup_copy[i].add(*(new Gtk::Label(_("Copy color"), 0.0, 0.5)));
241 _popup_copy[i].signal_activate().connect(sigc::mem_fun(*this,
242 (i == SS_FILL)? &SelectedStyle::on_fill_copy : &SelectedStyle::on_stroke_copy ));
244 _popup_paste[i].add(*(new Gtk::Label(_("Paste color"), 0.0, 0.5)));
245 _popup_paste[i].signal_activate().connect(sigc::mem_fun(*this,
246 (i == SS_FILL)? &SelectedStyle::on_fill_paste : &SelectedStyle::on_stroke_paste ));
248 _popup_swap[i].add(*(new Gtk::Label(_("Swap fill and stroke"), 0.0, 0.5)));
249 _popup_swap[i].signal_activate().connect(sigc::mem_fun(*this,
250 &SelectedStyle::on_fillstroke_swap));
252 _popup_opaque[i].add(*(new Gtk::Label((i == SS_FILL)? _("Make fill opaque") : _("Make stroke opaque"), 0.0, 0.5)));
253 _popup_opaque[i].signal_activate().connect(sigc::mem_fun(*this,
254 (i == SS_FILL)? &SelectedStyle::on_fill_opaque : &SelectedStyle::on_stroke_opaque ));
256 //TRANSLATORS COMMENT: unset is a verb here
257 _popup_unset[i].add(*(new Gtk::Label((i == SS_FILL)? _("Unset fill") : _("Unset stroke"), 0.0, 0.5)));
258 _popup_unset[i].signal_activate().connect(sigc::mem_fun(*this,
259 (i == SS_FILL)? &SelectedStyle::on_fill_unset : &SelectedStyle::on_stroke_unset ));
261 _popup_remove[i].add(*(new Gtk::Label((i == SS_FILL)? _("Remove fill") : _("Remove stroke"), 0.0, 0.5)));
262 _popup_remove[i].signal_activate().connect(sigc::mem_fun(*this,
263 (i == SS_FILL)? &SelectedStyle::on_fill_remove : &SelectedStyle::on_stroke_remove ));
265 _popup[i].attach(_popup_edit[i], 0,1, 0,1);
266 _popup[i].attach(*(new Gtk::SeparatorMenuItem()), 0,1, 1,2);
267 _popup[i].attach(_popup_lastused[i], 0,1, 2,3);
268 _popup[i].attach(_popup_lastselected[i], 0,1, 3,4);
269 _popup[i].attach(*(new Gtk::SeparatorMenuItem()), 0,1, 4,5);
270 _popup[i].attach(_popup_invert[i], 0,1, 5,6);
271 _popup[i].attach(*(new Gtk::SeparatorMenuItem()), 0,1, 6,7);
272 _popup[i].attach(_popup_white[i], 0,1, 7,8);
273 _popup[i].attach(_popup_black[i], 0,1, 8,9);
274 _popup[i].attach(*(new Gtk::SeparatorMenuItem()), 0,1, 9,10);
275 _popup[i].attach(_popup_copy[i], 0,1, 10,11);
276 _popup_copy[i].set_sensitive(false);
277 _popup[i].attach(_popup_paste[i], 0,1, 11,12);
278 _popup[i].attach(_popup_swap[i], 0,1, 12,13);
279 _popup[i].attach(*(new Gtk::SeparatorMenuItem()), 0,1, 13,14);
280 _popup[i].attach(_popup_opaque[i], 0,1, 14,15);
281 _popup[i].attach(_popup_unset[i], 0,1, 15,16);
282 _popup[i].attach(_popup_remove[i], 0,1, 16,17);
283 _popup[i].show_all();
285 _mode[i] = SS_NA;
286 }
288 {
289 _popup_px.add(*(new Gtk::Label(_("px"), 0.0, 0.5)));
290 _popup_px.signal_activate().connect(sigc::mem_fun(*this, &SelectedStyle::on_popup_px));
291 _popup_sw.attach(_popup_px, 0,1, 0,1);
293 _popup_pt.add(*(new Gtk::Label(_("pt"), 0.0, 0.5)));
294 _popup_pt.signal_activate().connect(sigc::mem_fun(*this, &SelectedStyle::on_popup_pt));
295 _popup_sw.attach(_popup_pt, 0,1, 1,2);
297 _popup_mm.add(*(new Gtk::Label(_("mm"), 0.0, 0.5)));
298 _popup_mm.signal_activate().connect(sigc::mem_fun(*this, &SelectedStyle::on_popup_mm));
299 _popup_sw.attach(_popup_mm, 0,1, 2,3);
301 _popup_sw.attach(*(new Gtk::SeparatorMenuItem()), 0,1, 3,4);
303 for (guint i = 0; i < G_N_ELEMENTS(_sw_presets_str); ++i) {
304 Gtk::MenuItem *mi = Gtk::manage(new Gtk::MenuItem());
305 mi->add(*(new Gtk::Label(_sw_presets_str[i], 0.0, 0.5)));
306 mi->signal_activate().connect(sigc::bind<int>(sigc::mem_fun(*this, &SelectedStyle::on_popup_preset), i));
307 _popup_sw.attach(*mi, 0,1, 4+i, 5+i);
308 }
310 guint i = G_N_ELEMENTS(_sw_presets_str) + 5;
312 _popup_sw.attach(*(new Gtk::SeparatorMenuItem()), 0,1, i,i+1);
314 _popup_sw_remove.add(*(new Gtk::Label(_("Remove"), 0.0, 0.5)));
315 _popup_sw_remove.signal_activate().connect(sigc::mem_fun(*this, &SelectedStyle::on_stroke_remove));
316 _popup_sw.attach(_popup_sw_remove, 0,1, i+1,i+2);
318 _popup_sw.show_all();
319 }
321 _fill_place.signal_button_release_event().connect(sigc::mem_fun(*this, &SelectedStyle::on_fill_click));
322 _stroke_place.signal_button_release_event().connect(sigc::mem_fun(*this, &SelectedStyle::on_stroke_click));
323 _opacity_place.signal_button_press_event().connect(sigc::mem_fun(*this, &SelectedStyle::on_opacity_click));
324 _stroke_width_place.signal_button_press_event().connect(sigc::mem_fun(*this, &SelectedStyle::on_sw_click));
326 _opacity_sb.signal_populate_popup().connect(sigc::mem_fun(*this, &SelectedStyle::on_opacity_menu));
327 _opacity_sb.signal_value_changed().connect(sigc::mem_fun(*this, &SelectedStyle::on_opacity_changed));
329 _fill_place.add(_na[SS_FILL]);
330 _tooltips.set_tip(_fill_place, __na[SS_FILL]);
332 _stroke_place.add(_na[SS_STROKE]);
333 _tooltips.set_tip(_stroke_place, __na[SS_STROKE]);
335 _stroke.pack_start(_stroke_place);
336 _stroke_width_place.add(_stroke_width);
337 _stroke.pack_start(_stroke_width_place, Gtk::PACK_SHRINK);
339 _opacity_sb.set_adjustment(_opacity_adjustment);
340 sp_set_font_size_smaller (GTK_WIDGET(_opacity_sb.gobj()));
341 _opacity_sb.set_size_request (SELECTED_STYLE_SB_WIDTH, -1);
342 _opacity_sb.set_sensitive (false);
344 _table.attach(_fill_label, 0,1, 0,1, Gtk::FILL, Gtk::SHRINK);
345 _table.attach(_stroke_label, 0,1, 1,2, Gtk::FILL, Gtk::SHRINK);
347 _table.attach(_fill_flag_place, 1,2, 0,1, Gtk::SHRINK, Gtk::SHRINK);
348 _table.attach(_stroke_flag_place, 1,2, 1,2, Gtk::SHRINK, Gtk::SHRINK);
350 _table.attach(_fill_place, 2,3, 0,1);
351 _table.attach(_stroke, 2,3, 1,2);
353 _opacity_place.add(_opacity_label);
354 _table.attach(_opacity_place, 4,5, 0,2, Gtk::SHRINK, Gtk::SHRINK);
355 _table.attach(_opacity_sb, 5,6, 0,2, Gtk::SHRINK, Gtk::SHRINK);
357 pack_start(_table, true, true, 2);
359 set_size_request (SELECTED_STYLE_WIDTH, -1);
361 sp_set_font_size_smaller (GTK_WIDGET(_opacity_label.gobj()));
362 sp_set_font_size_smaller (GTK_WIDGET(_opacity_sb.gobj()));
363 sp_set_font_size_smaller (GTK_WIDGET(_fill_place.gobj()));
364 sp_set_font_size_smaller (GTK_WIDGET(_fill_flag_place.gobj()));
365 sp_set_font_size_smaller (GTK_WIDGET(_stroke_place.gobj()));
366 sp_set_font_size_smaller (GTK_WIDGET(_stroke_flag_place.gobj()));
367 sp_set_font_size_smaller (GTK_WIDGET(_stroke_width.gobj()));
368 sp_set_font_size_smaller (GTK_WIDGET(_fill_label.gobj()));
369 sp_set_font_size_smaller (GTK_WIDGET(_stroke_label.gobj()));
371 _drop[SS_FILL] = new DropTracker();
372 ((DropTracker*)_drop[SS_FILL])->parent = this;
373 ((DropTracker*)_drop[SS_FILL])->item = SS_FILL;
375 _drop[SS_STROKE] = new DropTracker();
376 ((DropTracker*)_drop[SS_STROKE])->parent = this;
377 ((DropTracker*)_drop[SS_STROKE])->item = SS_STROKE;
379 g_signal_connect(_stroke_place.gobj(),
380 "drag_data_received",
381 G_CALLBACK(dragDataReceived),
382 _drop[SS_STROKE]);
384 g_signal_connect(_fill_place.gobj(),
385 "drag_data_received",
386 G_CALLBACK(dragDataReceived),
387 _drop[SS_FILL]);
388 }
390 SelectedStyle::~SelectedStyle()
391 {
392 selection_changed_connection->disconnect();
393 delete selection_changed_connection;
394 selection_modified_connection->disconnect();
395 delete selection_modified_connection;
396 subselection_changed_connection->disconnect();
397 delete subselection_changed_connection;
399 for (int i = SS_FILL; i <= SS_STROKE; i++) {
400 delete _color_preview[i];
401 // FIXME: do we need this? the destroy methods are not exported
402 //sp_gradient_image_destroy(GTK_OBJECT(_gradient_preview_l[i]));
403 //sp_gradient_image_destroy(GTK_OBJECT(_gradient_preview_r[i]));
404 }
406 delete (DropTracker*)_drop[SS_FILL];
407 delete (DropTracker*)_drop[SS_STROKE];
408 }
410 void
411 SelectedStyle::setDesktop(SPDesktop *desktop)
412 {
413 _desktop = desktop;
414 gtk_object_set_data (GTK_OBJECT(_opacity_sb.gobj()), "dtw", _desktop->canvas);
416 Inkscape::Selection *selection = sp_desktop_selection (desktop);
418 selection_changed_connection = new sigc::connection (selection->connectChanged(
419 sigc::bind (
420 sigc::ptr_fun(&ss_selection_changed),
421 this )
422 ));
423 selection_modified_connection = new sigc::connection (selection->connectModified(
424 sigc::bind (
425 sigc::ptr_fun(&ss_selection_modified),
426 this )
427 ));
428 subselection_changed_connection = new sigc::connection (desktop->connectToolSubselectionChanged(
429 sigc::bind (
430 sigc::ptr_fun(&ss_subselection_changed),
431 this )
432 ));
434 //_sw_unit = (SPUnit *) sp_desktop_namedview(desktop)->doc_units;
435 }
437 void SelectedStyle::dragDataReceived( GtkWidget */*widget*/,
438 GdkDragContext */*drag_context*/,
439 gint /*x*/, gint /*y*/,
440 GtkSelectionData *data,
441 guint /*info*/,
442 guint /*event_time*/,
443 gpointer user_data )
444 {
445 DropTracker* tracker = (DropTracker*)user_data;
447 switch ( (int)tracker->item ) {
448 case SS_FILL:
449 case SS_STROKE:
450 {
451 if ( data->length == 8 ) {
452 gchar c[64];
453 // Careful about endian issues.
454 guint16* dataVals = (guint16*)data->data;
455 sp_svg_write_color( c, sizeof(c),
456 SP_RGBA32_U_COMPOSE(
457 0x0ff & (dataVals[0] >> 8),
458 0x0ff & (dataVals[1] >> 8),
459 0x0ff & (dataVals[2] >> 8),
460 0xff // can't have transparency in the color itself
461 //0x0ff & (data->data[3] >> 8),
462 ));
463 SPCSSAttr *css = sp_repr_css_attr_new();
464 sp_repr_css_set_property( css, (tracker->item == SS_FILL) ? "fill":"stroke", c );
465 sp_desktop_set_style( tracker->parent->_desktop, css );
466 sp_repr_css_attr_unref( css );
467 sp_document_done( sp_desktop_document(tracker->parent->_desktop) , SP_VERB_NONE,
468 _("Drop color"));
469 }
470 }
471 break;
472 }
473 }
475 void SelectedStyle::on_fill_remove() {
476 SPCSSAttr *css = sp_repr_css_attr_new ();
477 sp_repr_css_set_property (css, "fill", "none");
478 sp_desktop_set_style (_desktop, css, true, true);
479 sp_repr_css_attr_unref (css);
480 sp_document_done (sp_desktop_document(_desktop), SP_VERB_DIALOG_FILL_STROKE,
481 _("Remove fill"));
482 }
484 void SelectedStyle::on_stroke_remove() {
485 SPCSSAttr *css = sp_repr_css_attr_new ();
486 sp_repr_css_set_property (css, "stroke", "none");
487 sp_desktop_set_style (_desktop, css, true, true);
488 sp_repr_css_attr_unref (css);
489 sp_document_done (sp_desktop_document(_desktop), SP_VERB_DIALOG_FILL_STROKE,
490 _("Remove stroke"));
491 }
493 void SelectedStyle::on_fill_unset() {
494 SPCSSAttr *css = sp_repr_css_attr_new ();
495 sp_repr_css_unset_property (css, "fill");
496 sp_desktop_set_style (_desktop, css, true, true);
497 sp_repr_css_attr_unref (css);
498 sp_document_done (sp_desktop_document(_desktop), SP_VERB_DIALOG_FILL_STROKE,
499 _("Unset fill"));
500 }
502 void SelectedStyle::on_stroke_unset() {
503 SPCSSAttr *css = sp_repr_css_attr_new ();
504 sp_repr_css_unset_property (css, "stroke");
505 sp_repr_css_unset_property (css, "stroke-opacity");
506 sp_repr_css_unset_property (css, "stroke-width");
507 sp_repr_css_unset_property (css, "stroke-miterlimit");
508 sp_repr_css_unset_property (css, "stroke-linejoin");
509 sp_repr_css_unset_property (css, "stroke-linecap");
510 sp_repr_css_unset_property (css, "stroke-dashoffset");
511 sp_repr_css_unset_property (css, "stroke-dasharray");
512 sp_desktop_set_style (_desktop, css, true, true);
513 sp_repr_css_attr_unref (css);
514 sp_document_done (sp_desktop_document(_desktop), SP_VERB_DIALOG_FILL_STROKE,
515 _("Unset stroke"));
516 }
518 void SelectedStyle::on_fill_opaque() {
519 SPCSSAttr *css = sp_repr_css_attr_new ();
520 sp_repr_css_set_property (css, "fill-opacity", "1");
521 sp_desktop_set_style (_desktop, css, true);
522 sp_repr_css_attr_unref (css);
523 sp_document_done (sp_desktop_document(_desktop), SP_VERB_DIALOG_FILL_STROKE,
524 _("Make fill opaque"));
525 }
527 void SelectedStyle::on_stroke_opaque() {
528 SPCSSAttr *css = sp_repr_css_attr_new ();
529 sp_repr_css_set_property (css, "stroke-opacity", "1");
530 sp_desktop_set_style (_desktop, css, true);
531 sp_repr_css_attr_unref (css);
532 sp_document_done (sp_desktop_document(_desktop), SP_VERB_DIALOG_FILL_STROKE,
533 _("Make fill opaque"));
534 }
536 void SelectedStyle::on_fill_lastused() {
537 SPCSSAttr *css = sp_repr_css_attr_new ();
538 guint32 color = sp_desktop_get_color(_desktop, true);
539 gchar c[64];
540 sp_svg_write_color (c, sizeof(c), color);
541 sp_repr_css_set_property (css, "fill", c);
542 sp_desktop_set_style (_desktop, css);
543 sp_repr_css_attr_unref (css);
544 sp_document_done (sp_desktop_document(_desktop), SP_VERB_DIALOG_FILL_STROKE,
545 _("Apply last set color to fill"));
546 }
548 void SelectedStyle::on_stroke_lastused() {
549 SPCSSAttr *css = sp_repr_css_attr_new ();
550 guint32 color = sp_desktop_get_color(_desktop, false);
551 gchar c[64];
552 sp_svg_write_color (c, sizeof(c), color);
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 set color to stroke"));
558 }
560 void SelectedStyle::on_fill_lastselected() {
561 SPCSSAttr *css = sp_repr_css_attr_new ();
562 gchar c[64];
563 sp_svg_write_color (c, sizeof(c), _lastselected[SS_FILL]);
564 sp_repr_css_set_property (css, "fill", c);
565 sp_desktop_set_style (_desktop, css);
566 sp_repr_css_attr_unref (css);
567 sp_document_done (sp_desktop_document(_desktop), SP_VERB_DIALOG_FILL_STROKE,
568 _("Apply last selected color to fill"));
569 }
571 void SelectedStyle::on_stroke_lastselected() {
572 SPCSSAttr *css = sp_repr_css_attr_new ();
573 gchar c[64];
574 sp_svg_write_color (c, sizeof(c), _lastselected[SS_STROKE]);
575 sp_repr_css_set_property (css, "stroke", c);
576 sp_desktop_set_style (_desktop, css);
577 sp_repr_css_attr_unref (css);
578 sp_document_done (sp_desktop_document(_desktop), SP_VERB_DIALOG_FILL_STROKE,
579 _("Apply last selected color to stroke"));
580 }
582 void SelectedStyle::on_fill_invert() {
583 SPCSSAttr *css = sp_repr_css_attr_new ();
584 guint32 color = _thisselected[SS_FILL];
585 gchar c[64];
586 if (_mode[SS_FILL] != SS_COLOR) return;
587 sp_svg_write_color (c, sizeof(c),
588 SP_RGBA32_U_COMPOSE(
589 (255 - SP_RGBA32_R_U(color)),
590 (255 - SP_RGBA32_G_U(color)),
591 (255 - SP_RGBA32_B_U(color)),
592 SP_RGBA32_A_U(color)
593 )
594 );
595 sp_repr_css_set_property (css, "fill", c);
596 sp_desktop_set_style (_desktop, css);
597 sp_repr_css_attr_unref (css);
598 sp_document_done (sp_desktop_document(_desktop), SP_VERB_DIALOG_FILL_STROKE,
599 _("Invert fill"));
600 }
602 void SelectedStyle::on_stroke_invert() {
603 SPCSSAttr *css = sp_repr_css_attr_new ();
604 guint32 color = _thisselected[SS_STROKE];
605 gchar c[64];
606 if (_mode[SS_STROKE] != SS_COLOR) return;
607 sp_svg_write_color (c, sizeof(c),
608 SP_RGBA32_U_COMPOSE(
609 (255 - SP_RGBA32_R_U(color)),
610 (255 - SP_RGBA32_G_U(color)),
611 (255 - SP_RGBA32_B_U(color)),
612 SP_RGBA32_A_U(color)
613 )
614 );
615 sp_repr_css_set_property (css, "stroke", c);
616 sp_desktop_set_style (_desktop, css);
617 sp_repr_css_attr_unref (css);
618 sp_document_done (sp_desktop_document(_desktop), SP_VERB_DIALOG_FILL_STROKE,
619 _("Invert stroke"));
620 }
622 void SelectedStyle::on_fill_white() {
623 SPCSSAttr *css = sp_repr_css_attr_new ();
624 gchar c[64];
625 sp_svg_write_color (c, sizeof(c), 0xffffffff);
626 sp_repr_css_set_property (css, "fill", c);
627 sp_repr_css_set_property (css, "fill-opacity", "1");
628 sp_desktop_set_style (_desktop, css);
629 sp_repr_css_attr_unref (css);
630 sp_document_done (sp_desktop_document(_desktop), SP_VERB_DIALOG_FILL_STROKE,
631 _("White fill"));
632 }
634 void SelectedStyle::on_stroke_white() {
635 SPCSSAttr *css = sp_repr_css_attr_new ();
636 gchar c[64];
637 sp_svg_write_color (c, sizeof(c), 0xffffffff);
638 sp_repr_css_set_property (css, "stroke", c);
639 sp_repr_css_set_property (css, "stroke-opacity", "1");
640 sp_desktop_set_style (_desktop, css);
641 sp_repr_css_attr_unref (css);
642 sp_document_done (sp_desktop_document(_desktop), SP_VERB_DIALOG_FILL_STROKE,
643 _("White stroke"));
644 }
646 void SelectedStyle::on_fill_black() {
647 SPCSSAttr *css = sp_repr_css_attr_new ();
648 gchar c[64];
649 sp_svg_write_color (c, sizeof(c), 0x000000ff);
650 sp_repr_css_set_property (css, "fill", c);
651 sp_repr_css_set_property (css, "fill-opacity", "1.0");
652 sp_desktop_set_style (_desktop, css);
653 sp_repr_css_attr_unref (css);
654 sp_document_done (sp_desktop_document(_desktop), SP_VERB_DIALOG_FILL_STROKE,
655 _("Black fill"));
656 }
658 void SelectedStyle::on_stroke_black() {
659 SPCSSAttr *css = sp_repr_css_attr_new ();
660 gchar c[64];
661 sp_svg_write_color (c, sizeof(c), 0x000000ff);
662 sp_repr_css_set_property (css, "stroke", c);
663 sp_repr_css_set_property (css, "stroke-opacity", "1.0");
664 sp_desktop_set_style (_desktop, css);
665 sp_repr_css_attr_unref (css);
666 sp_document_done (sp_desktop_document(_desktop), SP_VERB_DIALOG_FILL_STROKE,
667 _("Black stroke"));
668 }
670 void SelectedStyle::on_fill_copy() {
671 if (_mode[SS_FILL] == SS_COLOR) {
672 gchar c[64];
673 sp_svg_write_color (c, sizeof(c), _thisselected[SS_FILL]);
674 Glib::ustring text;
675 text += c;
676 if (!text.empty()) {
677 Glib::RefPtr<Gtk::Clipboard> refClipboard = Gtk::Clipboard::get();
678 refClipboard->set_text(text);
679 }
680 }
681 }
683 void SelectedStyle::on_stroke_copy() {
684 if (_mode[SS_STROKE] == SS_COLOR) {
685 gchar c[64];
686 sp_svg_write_color (c, sizeof(c), _thisselected[SS_STROKE]);
687 Glib::ustring text;
688 text += c;
689 if (!text.empty()) {
690 Glib::RefPtr<Gtk::Clipboard> refClipboard = Gtk::Clipboard::get();
691 refClipboard->set_text(text);
692 }
693 }
694 }
696 void SelectedStyle::on_fill_paste() {
697 Glib::RefPtr<Gtk::Clipboard> refClipboard = Gtk::Clipboard::get();
698 Glib::ustring const text = refClipboard->wait_for_text();
700 if (!text.empty()) {
701 guint32 color = sp_svg_read_color(text.c_str(), 0x000000ff); // impossible value, as SVG color cannot have opacity
702 if (color == 0x000000ff) // failed to parse color string
703 return;
705 SPCSSAttr *css = sp_repr_css_attr_new ();
706 sp_repr_css_set_property (css, "fill", text.c_str());
707 sp_desktop_set_style (_desktop, css);
708 sp_repr_css_attr_unref (css);
709 sp_document_done (sp_desktop_document(_desktop), SP_VERB_DIALOG_FILL_STROKE,
710 _("Paste fill"));
711 }
712 }
714 void SelectedStyle::on_stroke_paste() {
715 Glib::RefPtr<Gtk::Clipboard> refClipboard = Gtk::Clipboard::get();
716 Glib::ustring const text = refClipboard->wait_for_text();
718 if (!text.empty()) {
719 guint32 color = sp_svg_read_color(text.c_str(), 0x000000ff); // impossible value, as SVG color cannot have opacity
720 if (color == 0x000000ff) // failed to parse color string
721 return;
723 SPCSSAttr *css = sp_repr_css_attr_new ();
724 sp_repr_css_set_property (css, "stroke", text.c_str());
725 sp_desktop_set_style (_desktop, css);
726 sp_repr_css_attr_unref (css);
727 sp_document_done (sp_desktop_document(_desktop), SP_VERB_DIALOG_FILL_STROKE,
728 _("Paste stroke"));
729 }
730 }
732 void SelectedStyle::on_fillstroke_swap() {
733 SPCSSAttr *css = sp_repr_css_attr_new ();
735 switch (_mode[SS_FILL]) {
736 case SS_NA:
737 case SS_MANY:
738 break;
739 case SS_NONE:
740 sp_repr_css_set_property (css, "stroke", "none");
741 break;
742 case SS_UNSET:
743 sp_repr_css_unset_property (css, "stroke");
744 break;
745 case SS_COLOR:
746 gchar c[64];
747 sp_svg_write_color (c, sizeof(c), _thisselected[SS_FILL]);
748 sp_repr_css_set_property (css, "stroke", c);
749 break;
750 case SS_LGRADIENT:
751 case SS_RGRADIENT:
752 case SS_PATTERN:
753 sp_repr_css_set_property (css, "stroke", _paintserver_id[SS_FILL].c_str());
754 break;
755 }
757 switch (_mode[SS_STROKE]) {
758 case SS_NA:
759 case SS_MANY:
760 break;
761 case SS_NONE:
762 sp_repr_css_set_property (css, "fill", "none");
763 break;
764 case SS_UNSET:
765 sp_repr_css_unset_property (css, "fill");
766 break;
767 case SS_COLOR:
768 gchar c[64];
769 sp_svg_write_color (c, sizeof(c), _thisselected[SS_STROKE]);
770 sp_repr_css_set_property (css, "fill", c);
771 break;
772 case SS_LGRADIENT:
773 case SS_RGRADIENT:
774 case SS_PATTERN:
775 sp_repr_css_set_property (css, "fill", _paintserver_id[SS_STROKE].c_str());
776 break;
777 }
779 sp_desktop_set_style (_desktop, css);
780 sp_repr_css_attr_unref (css);
781 sp_document_done (sp_desktop_document(_desktop), SP_VERB_DIALOG_FILL_STROKE,
782 _("Swap fill and stroke"));
783 }
785 void SelectedStyle::on_fill_edit() {
786 if (Dialog::FillAndStroke *fs = get_fill_and_stroke_panel(_desktop))
787 fs->showPageFill();
788 }
790 void SelectedStyle::on_stroke_edit() {
791 if (Dialog::FillAndStroke *fs = get_fill_and_stroke_panel(_desktop))
792 fs->showPageStrokePaint();
793 }
795 bool
796 SelectedStyle::on_fill_click(GdkEventButton *event)
797 {
798 if (event->button == 1) { // click, open fill&stroke
800 if (Dialog::FillAndStroke *fs = get_fill_and_stroke_panel(_desktop))
801 fs->showPageFill();
803 } else if (event->button == 3) { // right-click, popup menu
804 _popup[SS_FILL].popup(event->button, event->time);
805 } else if (event->button == 2) { // middle click, toggle none/lastcolor
806 if (_mode[SS_FILL] == SS_NONE) {
807 on_fill_lastused();
808 } else {
809 on_fill_remove();
810 }
811 }
812 return true;
813 }
815 bool
816 SelectedStyle::on_stroke_click(GdkEventButton *event)
817 {
818 if (event->button == 1) { // click, open fill&stroke
819 if (Dialog::FillAndStroke *fs = get_fill_and_stroke_panel(_desktop))
820 fs->showPageStrokePaint();
821 } else if (event->button == 3) { // right-click, popup menu
822 _popup[SS_STROKE].popup(event->button, event->time);
823 } else if (event->button == 2) { // middle click, toggle none/lastcolor
824 if (_mode[SS_STROKE] == SS_NONE) {
825 on_stroke_lastused();
826 } else {
827 on_stroke_remove();
828 }
829 }
830 return true;
831 }
833 bool
834 SelectedStyle::on_sw_click(GdkEventButton *event)
835 {
836 if (event->button == 1) { // click, open fill&stroke
837 if (Dialog::FillAndStroke *fs = get_fill_and_stroke_panel(_desktop))
838 fs->showPageStrokeStyle();
839 } else if (event->button == 3) { // right-click, popup menu
840 _popup_sw.popup(event->button, event->time);
841 } else if (event->button == 2) { // middle click, toggle none/lastwidth?
842 //
843 }
844 return true;
845 }
847 bool
848 SelectedStyle::on_opacity_click(GdkEventButton *event)
849 {
850 if (event->button == 2) { // middle click
851 const char* opacity = _opacity_sb.get_value() < 50? "0.5" : (_opacity_sb.get_value() == 100? "0" : "1");
852 SPCSSAttr *css = sp_repr_css_attr_new ();
853 sp_repr_css_set_property (css, "opacity", opacity);
854 sp_desktop_set_style (_desktop, css);
855 sp_repr_css_attr_unref (css);
856 sp_document_done (sp_desktop_document (_desktop), SP_VERB_DIALOG_FILL_STROKE,
857 _("Change opacity"));
858 return true;
859 }
861 return false;
862 }
864 void SelectedStyle::on_popup_px() {
865 _sw_unit = (SPUnit *) &(sp_unit_get_by_id(SP_UNIT_PX));
866 update();
867 }
868 void SelectedStyle::on_popup_pt() {
869 _sw_unit = (SPUnit *) &(sp_unit_get_by_id(SP_UNIT_PT));
870 update();
871 }
872 void SelectedStyle::on_popup_mm() {
873 _sw_unit = (SPUnit *) &(sp_unit_get_by_id(SP_UNIT_MM));
874 update();
875 }
877 void SelectedStyle::on_popup_preset(int i) {
878 SPCSSAttr *css = sp_repr_css_attr_new ();
879 gdouble w;
880 if (_sw_unit) {
881 w = sp_units_get_pixels (_sw_presets[i], *_sw_unit);
882 } else {
883 w = _sw_presets[i];
884 }
885 Inkscape::CSSOStringStream os;
886 os << w;
887 sp_repr_css_set_property (css, "stroke-width", os.str().c_str());
888 // FIXME: update dash patterns!
889 sp_desktop_set_style (_desktop, css, true);
890 sp_repr_css_attr_unref (css);
891 sp_document_done (sp_desktop_document(_desktop), SP_VERB_DIALOG_SWATCHES,
892 _("Change stroke width"));
893 }
895 void
896 SelectedStyle::update()
897 {
898 if (_desktop == NULL)
899 return;
901 // create temporary style
902 SPStyle *query = sp_style_new (sp_desktop_document(_desktop));
904 for (int i = SS_FILL; i <= SS_STROKE; i++) {
905 Gtk::EventBox *place = (i == SS_FILL)? &_fill_place : &_stroke_place;
906 Gtk::EventBox *flag_place = (i == SS_FILL)? &_fill_flag_place : &_stroke_flag_place;
908 place->remove();
909 flag_place->remove();
911 _tooltips.unset_tip(*place);
912 _tooltips.unset_tip(*flag_place);
914 _mode[i] = SS_NA;
915 _paintserver_id[i].clear();
917 _popup_copy[i].set_sensitive(false);
919 // query style from desktop. This returns a result flag and fills query with the style of subselection, if any, or selection
920 int result = sp_desktop_query_style (_desktop, query,
921 (i == SS_FILL)? QUERY_STYLE_PROPERTY_FILL : QUERY_STYLE_PROPERTY_STROKE);
922 switch (result) {
923 case QUERY_STYLE_NOTHING:
924 place->add(_na[i]);
925 _tooltips.set_tip(*place, __na[i]);
926 _mode[i] = SS_NA;
927 if ( _dropEnabled[i] ) {
928 gtk_drag_dest_unset( GTK_WIDGET((i==SS_FILL) ? _fill_place.gobj():_stroke_place.gobj()) );
929 _dropEnabled[i] = false;
930 }
931 break;
932 case QUERY_STYLE_SINGLE:
933 case QUERY_STYLE_MULTIPLE_AVERAGED:
934 case QUERY_STYLE_MULTIPLE_SAME:
935 if ( !_dropEnabled[i] ) {
936 gtk_drag_dest_set( GTK_WIDGET( (i==SS_FILL) ? _fill_place.gobj():_stroke_place.gobj()),
937 GTK_DEST_DEFAULT_ALL,
938 ui_drop_target_entries,
939 nui_drop_target_entries,
940 GdkDragAction(GDK_ACTION_COPY | GDK_ACTION_MOVE) );
941 _dropEnabled[i] = true;
942 }
943 SPIPaint *paint;
944 if (i == SS_FILL) {
945 paint = &(query->fill);
946 } else {
947 paint = &(query->stroke);
948 }
949 if (paint->set && paint->isPaintserver()) {
950 SPPaintServer *server = (i == SS_FILL)? SP_STYLE_FILL_SERVER (query) : SP_STYLE_STROKE_SERVER (query);
951 if ( server ) {
952 Inkscape::XML::Node *srepr = SP_OBJECT_REPR(server);
953 _paintserver_id[i] += "url(#";
954 _paintserver_id[i] += srepr->attribute("id");
955 _paintserver_id[i] += ")";
957 if (SP_IS_LINEARGRADIENT (server)) {
958 SPGradient *vector = sp_gradient_get_vector(SP_GRADIENT(server), false);
959 sp_gradient_image_set_gradient ((SPGradientImage *) _gradient_preview_l[i], vector);
960 place->add(_gradient_box_l[i]);
961 _tooltips.set_tip(*place, __lgradient[i]);
962 _mode[i] = SS_LGRADIENT;
963 } else if (SP_IS_RADIALGRADIENT (server)) {
964 SPGradient *vector = sp_gradient_get_vector(SP_GRADIENT(server), false);
965 sp_gradient_image_set_gradient ((SPGradientImage *) _gradient_preview_r[i], vector);
966 place->add(_gradient_box_r[i]);
967 _tooltips.set_tip(*place, __rgradient[i]);
968 _mode[i] = SS_RGRADIENT;
969 } else if (SP_IS_PATTERN (server)) {
970 place->add(_pattern[i]);
971 _tooltips.set_tip(*place, __pattern[i]);
972 _mode[i] = SS_PATTERN;
973 }
974 } else {
975 g_warning ("file %s: line %d: Unknown paint server", __FILE__, __LINE__);
976 }
977 } else if (paint->set && paint->isColor()) {
978 guint32 color = paint->value.color.toRGBA32(
979 SP_SCALE24_TO_FLOAT ((i == SS_FILL)? query->fill_opacity.value : query->stroke_opacity.value));
980 _lastselected[i] = _thisselected[i];
981 _thisselected[i] = color | 0xff; // only color, opacity === 1
982 ((Inkscape::UI::Widget::ColorPreview*)_color_preview[i])->setRgba32 (color);
983 _color_preview[i]->show_all();
984 place->add(*_color_preview[i]);
985 gchar c_string[64];
986 g_snprintf (c_string, 64, "%06x/%.3g", color >> 8, SP_RGBA32_A_F(color));
987 _tooltips.set_tip(*place, __color[i] + ": " + c_string + _(", drag to adjust"));
988 _mode[i] = SS_COLOR;
989 _popup_copy[i].set_sensitive(true);
991 } else if (paint->set && paint->isNone()) {
992 place->add(_none[i]);
993 _tooltips.set_tip(*place, __none[i]);
994 _mode[i] = SS_NONE;
995 } else if (!paint->set) {
996 place->add(_unset[i]);
997 _tooltips.set_tip(*place, __unset[i]);
998 _mode[i] = SS_UNSET;
999 }
1000 if (result == QUERY_STYLE_MULTIPLE_AVERAGED) {
1001 flag_place->add(_averaged[i]);
1002 _tooltips.set_tip(*flag_place, __averaged[i]);
1003 } else if (result == QUERY_STYLE_MULTIPLE_SAME) {
1004 flag_place->add(_multiple[i]);
1005 _tooltips.set_tip(*flag_place, __multiple[i]);
1006 }
1007 break;
1008 case QUERY_STYLE_MULTIPLE_DIFFERENT:
1009 place->add(_many[i]);
1010 _tooltips.set_tip(*place, __many[i]);
1011 _mode[i] = SS_MANY;
1012 break;
1013 default:
1014 break;
1015 }
1016 }
1018 // Now query opacity
1019 _tooltips.unset_tip(_opacity_place);
1020 _tooltips.unset_tip(_opacity_sb);
1022 int result = sp_desktop_query_style (_desktop, query, QUERY_STYLE_PROPERTY_MASTEROPACITY);
1024 switch (result) {
1025 case QUERY_STYLE_NOTHING:
1026 _tooltips.set_tip(_opacity_place, _("Nothing selected"));
1027 _tooltips.set_tip(_opacity_sb, _("Nothing selected"));
1028 _opacity_sb.set_sensitive(false);
1029 break;
1030 case QUERY_STYLE_SINGLE:
1031 case QUERY_STYLE_MULTIPLE_AVERAGED:
1032 case QUERY_STYLE_MULTIPLE_SAME:
1033 _tooltips.set_tip(_opacity_place, _("Opacity, %"));
1034 _tooltips.set_tip(_opacity_sb, _("Opacity, %"));
1035 if (_opacity_blocked) break;
1036 _opacity_blocked = true;
1037 _opacity_sb.set_sensitive(true);
1038 _opacity_adjustment.set_value(SP_SCALE24_TO_FLOAT(query->opacity.value) * 100);
1039 _opacity_blocked = false;
1040 break;
1041 }
1043 // Now query stroke_width
1044 int result_sw = sp_desktop_query_style (_desktop, query, QUERY_STYLE_PROPERTY_STROKEWIDTH);
1045 switch (result_sw) {
1046 case QUERY_STYLE_NOTHING:
1047 _stroke_width.set_markup("");
1048 current_stroke_width = 0;
1049 break;
1050 case QUERY_STYLE_SINGLE:
1051 case QUERY_STYLE_MULTIPLE_AVERAGED:
1052 case QUERY_STYLE_MULTIPLE_SAME:
1053 {
1054 double w;
1055 if (_sw_unit) {
1056 w = sp_pixels_get_units(query->stroke_width.computed, *_sw_unit);
1057 } else {
1058 w = query->stroke_width.computed;
1059 }
1060 current_stroke_width = w;
1062 {
1063 gchar *str = g_strdup_printf(" %.3g", w);
1064 _stroke_width.set_markup(str);
1065 g_free (str);
1066 }
1067 {
1068 gchar *str = g_strdup_printf(_("Stroke width: %.5g%s%s"),
1069 w,
1070 _sw_unit? sp_unit_get_abbreviation(_sw_unit) : "px",
1071 (result_sw == QUERY_STYLE_MULTIPLE_AVERAGED)?
1072 _(" (averaged)") : "");
1073 _tooltips.set_tip(_stroke_width_place, str);
1074 g_free (str);
1075 }
1076 break;
1077 }
1078 default:
1079 break;
1080 }
1082 sp_style_unref(query);
1083 }
1085 void SelectedStyle::opacity_0(void) {_opacity_sb.set_value(0);}
1086 void SelectedStyle::opacity_025(void) {_opacity_sb.set_value(25);}
1087 void SelectedStyle::opacity_05(void) {_opacity_sb.set_value(50);}
1088 void SelectedStyle::opacity_075(void) {_opacity_sb.set_value(75);}
1089 void SelectedStyle::opacity_1(void) {_opacity_sb.set_value(100);}
1091 void SelectedStyle::on_opacity_menu (Gtk::Menu *menu) {
1093 Glib::ListHandle<Gtk::Widget *> children = menu->get_children();
1094 for (Glib::ListHandle<Gtk::Widget *>::iterator iter = children.begin(); iter != children.end(); iter++) {
1095 menu->remove(*(*iter));
1096 }
1098 {
1099 Gtk::MenuItem *item = new Gtk::MenuItem;
1100 item->add(*(new Gtk::Label(_("0 (transparent)"), 0, 0)));
1101 item->signal_activate().connect(sigc::mem_fun(*this, &SelectedStyle::opacity_0 ));
1102 menu->add(*item);
1103 }
1104 {
1105 Gtk::MenuItem *item = new Gtk::MenuItem;
1106 item->add(*(new Gtk::Label("25%", 0, 0)));
1107 item->signal_activate().connect(sigc::mem_fun(*this, &SelectedStyle::opacity_025 ));
1108 menu->add(*item);
1109 }
1110 {
1111 Gtk::MenuItem *item = new Gtk::MenuItem;
1112 item->add(*(new Gtk::Label("50%", 0, 0)));
1113 item->signal_activate().connect(sigc::mem_fun(*this, &SelectedStyle::opacity_05 ));
1114 menu->add(*item);
1115 }
1116 {
1117 Gtk::MenuItem *item = new Gtk::MenuItem;
1118 item->add(*(new Gtk::Label("75%", 0, 0)));
1119 item->signal_activate().connect(sigc::mem_fun(*this, &SelectedStyle::opacity_075 ));
1120 menu->add(*item);
1121 }
1122 {
1123 Gtk::MenuItem *item = new Gtk::MenuItem;
1124 item->add(*(new Gtk::Label(_("100% (opaque)"), 0, 0)));
1125 item->signal_activate().connect(sigc::mem_fun(*this, &SelectedStyle::opacity_1 ));
1126 menu->add(*item);
1127 }
1129 menu->show_all();
1130 }
1132 void SelectedStyle::on_opacity_changed () {
1133 if (_opacity_blocked)
1134 return;
1135 _opacity_blocked = true;
1136 SPCSSAttr *css = sp_repr_css_attr_new ();
1137 Inkscape::CSSOStringStream os;
1138 os << CLAMP ((_opacity_adjustment.get_value() / 100), 0.0, 1.0);
1139 sp_repr_css_set_property (css, "opacity", os.str().c_str());
1140 // FIXME: workaround for GTK breakage: display interruptibility sometimes results in GTK
1141 // sending multiple value-changed events. As if when Inkscape interrupts redraw for main loop
1142 // iterations, GTK discovers that this callback hasn't finished yet, and for some weird reason
1143 // decides to add yet another value-changed event to the queue. Totally braindead if you ask
1144 // me. As a result, scrolling the spinbutton once results in runaway change until it hits 1.0
1145 // or 0.0. (And no, this is not a race with ::update, I checked that.)
1146 // Sigh. So we disable interruptibility while we're setting the new value.
1147 sp_canvas_force_full_redraw_after_interruptions(sp_desktop_canvas(_desktop), 0);
1148 sp_desktop_set_style (_desktop, css);
1149 sp_repr_css_attr_unref (css);
1150 sp_document_maybe_done (sp_desktop_document (_desktop), "fillstroke:opacity", SP_VERB_DIALOG_FILL_STROKE,
1151 _("Change opacity"));
1152 // resume interruptibility
1153 sp_canvas_end_forced_full_redraws(sp_desktop_canvas(_desktop));
1154 spinbutton_defocus(GTK_OBJECT(_opacity_sb.gobj()));
1155 _opacity_blocked = false;
1156 }
1158 /* ============================================= RotateableSwatch */
1160 RotateableSwatch::RotateableSwatch(SelectedStyle *parent, guint mode) :
1161 fillstroke(mode),
1162 parent(parent),
1163 startcolor(0),
1164 startcolor_set(false),
1165 undokey("ssrot1"),
1166 cr(0),
1167 cr_set(false)
1169 {
1170 }
1172 RotateableSwatch::~RotateableSwatch() {
1173 }
1175 double
1176 RotateableSwatch::color_adjust(float *hsl, double by, guint32 cc, guint modifier)
1177 {
1178 sp_color_rgb_to_hsl_floatv (hsl, SP_RGBA32_R_F(cc), SP_RGBA32_G_F(cc), SP_RGBA32_B_F(cc));
1180 double diff = 0;
1181 if (modifier == 2) { // saturation
1182 double old = hsl[1];
1183 if (by > 0) {
1184 hsl[1] += by * (1 - hsl[1]);
1185 } else {
1186 hsl[1] += by * (hsl[1]);
1187 }
1188 diff = hsl[1] - old;
1189 } else if (modifier == 1) { // lightness
1190 double old = hsl[2];
1191 if (by > 0) {
1192 hsl[2] += by * (1 - hsl[2]);
1193 } else {
1194 hsl[2] += by * (hsl[2]);
1195 }
1196 diff = hsl[2] - old;
1197 } else { // hue
1198 double old = hsl[0];
1199 hsl[0] += by/2;
1200 while (hsl[0] < 0)
1201 hsl[0] += 1;
1202 while (hsl[0] > 1)
1203 hsl[0] -= 1;
1204 diff = hsl[0] - old;
1205 }
1207 float rgb[3];
1208 sp_color_hsl_to_rgb_floatv (rgb, hsl[0], hsl[1], hsl[2]);
1210 gchar c[64];
1211 sp_svg_write_color (c, sizeof(c),
1212 SP_RGBA32_U_COMPOSE(
1213 (SP_COLOR_F_TO_U(rgb[0])),
1214 (SP_COLOR_F_TO_U(rgb[1])),
1215 (SP_COLOR_F_TO_U(rgb[2])),
1216 0xff
1217 )
1218 );
1220 SPCSSAttr *css = sp_repr_css_attr_new ();
1221 if (fillstroke == SS_FILL)
1222 sp_repr_css_set_property (css, "fill", c);
1223 else
1224 sp_repr_css_set_property (css, "stroke", c);
1225 sp_desktop_set_style (parent->getDesktop(), css);
1226 sp_repr_css_attr_unref (css);
1227 return diff;
1228 }
1230 void
1231 RotateableSwatch::do_motion(double by, guint modifier) {
1232 if (parent->_mode[fillstroke] != SS_COLOR)
1233 return;
1235 if (!cr_set && modifier != 3) {
1236 GtkWidget *w = GTK_WIDGET(gobj());
1238 GdkBitmap *bitmap = NULL;
1239 GdkBitmap *mask = NULL;
1240 if (modifier == 2) { // saturation
1241 sp_cursor_bitmap_and_mask_from_xpm(&bitmap, &mask, cursor_adj_s_xpm);
1242 } else if (modifier == 1) { // lightness
1243 sp_cursor_bitmap_and_mask_from_xpm(&bitmap, &mask, cursor_adj_l_xpm);
1244 } else { // hue
1245 sp_cursor_bitmap_and_mask_from_xpm(&bitmap, &mask, cursor_adj_h_xpm);
1246 }
1247 if ((bitmap != NULL) && (mask != NULL)) {
1248 cr = gdk_cursor_new_from_pixmap(bitmap, mask,
1249 &w->style->black,
1250 &w->style->white,
1251 16, 16);
1252 g_object_unref (bitmap);
1253 g_object_unref (mask);
1254 gdk_window_set_cursor(w->window, cr);
1255 cr_set = true;
1256 }
1257 }
1259 guint32 cc;
1260 if (!startcolor_set) {
1261 cc = startcolor = parent->_thisselected[fillstroke];
1262 startcolor_set = true;
1263 } else {
1264 cc = startcolor;
1265 }
1267 float hsl[3];
1268 double diff = 0;
1269 if (modifier != 3) {
1270 diff = color_adjust(hsl, by, cc, modifier);
1271 }
1273 if (modifier == 3) { // Alt, do nothing
1275 } else if (modifier == 2) { // saturation
1276 sp_document_maybe_done (sp_desktop_document(parent->getDesktop()), undokey,
1277 SP_VERB_DIALOG_FILL_STROKE, (_("Adjust saturation")));
1278 double ch = hsl[1];
1279 parent->getDesktop()->event_context->_message_context->setF(Inkscape::IMMEDIATE_MESSAGE, _("Adjusting <b>saturation</b>: was %.3g, now <b>%.3g</b> (diff %.3g); with <b>Ctrl</b> to adjust lightness, without modifiers to adjust hue"), ch - diff, ch, diff);
1281 } else if (modifier == 1) { // lightness
1282 sp_document_maybe_done (sp_desktop_document(parent->getDesktop()), undokey,
1283 SP_VERB_DIALOG_FILL_STROKE, (_("Adjust lightness")));
1284 double ch = hsl[2];
1285 parent->getDesktop()->event_context->_message_context->setF(Inkscape::IMMEDIATE_MESSAGE, _("Adjusting <b>lightness</b>: was %.3g, now <b>%.3g</b> (diff %.3g); with <b>Shift</b> to adjust saturation, without modifiers to adjust hue"), ch - diff, ch, diff);
1287 } else { // hue
1288 sp_document_maybe_done (sp_desktop_document(parent->getDesktop()), undokey,
1289 SP_VERB_DIALOG_FILL_STROKE, (_("Adjust hue")));
1290 double ch = hsl[0];
1291 parent->getDesktop()->event_context->_message_context->setF(Inkscape::IMMEDIATE_MESSAGE, _("Adjusting <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);
1292 }
1293 }
1295 void
1296 RotateableSwatch::do_release(double by, guint modifier) {
1297 if (parent->_mode[fillstroke] != SS_COLOR)
1298 return;
1300 float hsl[3];
1301 if (modifier != 3) {
1302 color_adjust(hsl, by, startcolor, modifier);
1303 }
1305 if (cr_set) {
1306 GtkWidget *w = GTK_WIDGET(gobj());
1307 gdk_window_set_cursor(w->window, NULL);
1308 if (cr) {
1309 gdk_cursor_unref (cr);
1310 cr = NULL;
1311 }
1312 cr_set = false;
1313 }
1315 if (modifier == 3) { // Alt, do nothing
1316 } else if (modifier == 2) { // saturation
1317 sp_document_maybe_done (sp_desktop_document(parent->getDesktop()), undokey,
1318 SP_VERB_DIALOG_FILL_STROKE, ("Adjust saturation"));
1320 } else if (modifier == 1) { // lightness
1321 sp_document_maybe_done (sp_desktop_document(parent->getDesktop()), undokey,
1322 SP_VERB_DIALOG_FILL_STROKE, ("Adjust lightness"));
1324 } else { // hue
1325 sp_document_maybe_done (sp_desktop_document(parent->getDesktop()), undokey,
1326 SP_VERB_DIALOG_FILL_STROKE, ("Adjust hue"));
1327 }
1329 if (!strcmp(undokey, "ssrot1")) {
1330 undokey = "ssrot2";
1331 } else {
1332 undokey = "ssrot1";
1333 }
1335 parent->getDesktop()->event_context->_message_context->clear();
1336 startcolor_set = false;
1337 }
1339 /* ============================================= RotateableStrokeWidth */
1341 RotateableStrokeWidth::RotateableStrokeWidth(SelectedStyle *parent) :
1342 parent(parent),
1343 startvalue(0),
1344 startvalue_set(false),
1345 undokey("swrot1"),
1346 cr(0),
1347 cr_set(false)
1348 {
1349 }
1351 RotateableStrokeWidth::~RotateableStrokeWidth() {
1352 }
1354 double
1355 RotateableStrokeWidth::value_adjust(double current, double by, guint /*modifier*/, bool final)
1356 {
1357 double newval;
1358 // by is -1..1
1359 if (by < 0) {
1360 // map negative 0..-1 to current..0
1361 newval = current * (1 + by);
1362 } else {
1363 // map positive 0..1 to current..4*current
1364 newval = current * (1 + by) * (1 + by);
1365 }
1367 SPCSSAttr *css = sp_repr_css_attr_new ();
1368 if (final && newval < 1e-6) {
1369 // if dragged into zero and this is the final adjust on mouse release, delete stroke;
1370 // if it's not final, leave it a chance to increase again (which is not possible with "none")
1371 sp_repr_css_set_property (css, "stroke", "none");
1372 } else {
1373 Inkscape::CSSOStringStream os;
1374 os << newval;
1375 sp_repr_css_set_property (css, "stroke-width", os.str().c_str());
1376 }
1378 sp_desktop_set_style (parent->getDesktop(), css);
1379 sp_repr_css_attr_unref (css);
1380 return newval - current;
1381 }
1383 void
1384 RotateableStrokeWidth::do_motion(double by, guint modifier) {
1386 // if this is the first motion after a mouse grab, remember the current width
1387 if (!startvalue_set) {
1388 startvalue = parent->current_stroke_width;
1389 // if it's 0, adjusting (which uses multiplication) will not be able to change it, so we
1390 // cheat and provide a non-zero value
1391 if (startvalue == 0)
1392 startvalue = 1;
1393 startvalue_set = true;
1394 }
1396 if (modifier == 3) { // Alt, do nothing
1397 } else {
1398 double diff = value_adjust(startvalue, by, modifier, false);
1399 sp_document_maybe_done (sp_desktop_document(parent->getDesktop()), undokey,
1400 SP_VERB_DIALOG_FILL_STROKE, (_("Adjust stroke width")));
1401 parent->getDesktop()->event_context->_message_context->setF(Inkscape::IMMEDIATE_MESSAGE, _("Adjusting <b>stroke width</b>: was %.3g, now <b>%.3g</b> (diff %.3g)"), startvalue, startvalue + diff, diff);
1402 }
1403 }
1405 void
1406 RotateableStrokeWidth::do_release(double by, guint modifier) {
1408 if (modifier == 3) { // do nothing
1410 } else {
1411 value_adjust(startvalue, by, modifier, true);
1412 startvalue_set = false;
1413 sp_document_maybe_done (sp_desktop_document(parent->getDesktop()), undokey,
1414 SP_VERB_DIALOG_FILL_STROKE, (_("Adjust stroke width")));
1415 }
1417 if (!strcmp(undokey, "swrot1")) {
1418 undokey = "swrot2";
1419 } else {
1420 undokey = "swrot1";
1421 }
1422 parent->getDesktop()->event_context->_message_context->clear();
1423 }
1426 Dialog::FillAndStroke *get_fill_and_stroke_panel(SPDesktop *desktop)
1427 {
1428 if (Dialog::PanelDialogBase *panel_dialog =
1429 dynamic_cast<Dialog::PanelDialogBase *>(desktop->_dlg_mgr->getDialog("FillAndStroke"))) {
1430 try {
1431 Dialog::FillAndStroke &fill_and_stroke =
1432 dynamic_cast<Dialog::FillAndStroke &>(panel_dialog->getPanel());
1433 return &fill_and_stroke;
1434 } catch (std::exception e) { }
1435 }
1437 return 0;
1438 }
1440 } // namespace Widget
1441 } // namespace UI
1442 } // namespace Inkscape
1444 /*
1445 Local Variables:
1446 mode:c++
1447 c-file-style:"stroustrup"
1448 c-file-offsets:((innamespace . 0)(inline-open . 0))
1449 indent-tabs-mode:nil
1450 fill-column:99
1451 End:
1452 */
1453 // vim: filetype=c++:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 :