20838ee22ba8d0d12d2ba9ece042db14363b35cc
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 static GtkTargetEntry ui_drop_target_entries [] = {
90 {"application/x-color", 0, APP_X_COLOR}
91 };
93 #define ENTRIES_SIZE(n) sizeof(n)/sizeof(n[0])
94 static guint nui_drop_target_entries = ENTRIES_SIZE(ui_drop_target_entries);
96 /* convenience function */
97 static Dialog::FillAndStroke *get_fill_and_stroke_panel(SPDesktop *desktop);
99 SelectedStyle::SelectedStyle(bool /*layout*/)
100 : _desktop (NULL),
102 _table(2, 6),
103 _fill_label (_("Fill:")),
104 _stroke_label (_("Stroke:")),
105 _opacity_label (_("O:")),
107 _fill_place (SS_FILL),
108 _stroke_place (SS_STROKE),
110 _fill_flag_place (),
111 _stroke_flag_place (),
113 _opacity_place (),
114 _opacity_adjustment (100, 0.0, 100, 1.0, 10.0),
115 _opacity_sb (0.02, 0),
117 _stroke (),
118 _stroke_width (""),
120 _opacity_blocked (false),
122 _popup_px(_sw_group),
123 _popup_pt(_sw_group),
124 _popup_mm(_sw_group),
126 _sw_unit(NULL),
128 _tooltips ()
130 {
131 _drop[0] = _drop[1] = 0;
132 _dropEnabled[0] = _dropEnabled[1] = false;
134 _fill_label.set_alignment(0.0, 0.5);
135 _fill_label.set_padding(0, 0);
136 _stroke_label.set_alignment(0.0, 0.5);
137 _stroke_label.set_padding(0, 0);
138 _opacity_label.set_alignment(0.0, 0.5);
139 _opacity_label.set_padding(0, 0);
141 _table.set_col_spacings (2);
142 _table.set_row_spacings (0);
144 for (int i = SS_FILL; i <= SS_STROKE; i++) {
146 _na[i].set_markup (_("N/A"));
147 sp_set_font_size_smaller (GTK_WIDGET(_na[i].gobj()));
148 _na[i].show_all();
149 __na[i] = (_("Nothing selected"));
151 _none[i].set_markup (_("<i>None</i>"));
152 sp_set_font_size_smaller (GTK_WIDGET(_none[i].gobj()));
153 _none[i].show_all();
154 __none[i] = (i == SS_FILL)? (_("No fill")) : (_("No stroke"));
156 _pattern[i].set_markup (_("Pattern"));
157 sp_set_font_size_smaller (GTK_WIDGET(_pattern[i].gobj()));
158 _pattern[i].show_all();
159 __pattern[i] = (i == SS_FILL)? (_("Pattern fill")) : (_("Pattern stroke"));
161 _lgradient[i].set_markup (_("<b>L</b>"));
162 sp_set_font_size_smaller (GTK_WIDGET(_lgradient[i].gobj()));
163 _lgradient[i].show_all();
164 __lgradient[i] = (i == SS_FILL)? (_("Linear gradient fill")) : (_("Linear gradient stroke"));
166 _gradient_preview_l[i] = GTK_WIDGET(sp_gradient_image_new (NULL));
167 _gradient_box_l[i].pack_start(_lgradient[i]);
168 _gradient_box_l[i].pack_start(*(Glib::wrap(_gradient_preview_l[i])));
169 _gradient_box_l[i].show_all();
171 _rgradient[i].set_markup (_("<b>R</b>"));
172 sp_set_font_size_smaller (GTK_WIDGET(_rgradient[i].gobj()));
173 _rgradient[i].show_all();
174 __rgradient[i] = (i == SS_FILL)? (_("Radial gradient fill")) : (_("Radial gradient stroke"));
176 _gradient_preview_r[i] = GTK_WIDGET(sp_gradient_image_new (NULL));
177 _gradient_box_r[i].pack_start(_rgradient[i]);
178 _gradient_box_r[i].pack_start(*(Glib::wrap(_gradient_preview_r[i])));
179 _gradient_box_r[i].show_all();
181 _many[i].set_markup (_("Different"));
182 sp_set_font_size_smaller (GTK_WIDGET(_many[i].gobj()));
183 _many[i].show_all();
184 __many[i] = (i == SS_FILL)? (_("Different fills")) : (_("Different strokes"));
186 _unset[i].set_markup (_("<b>Unset</b>"));
187 sp_set_font_size_smaller (GTK_WIDGET(_unset[i].gobj()));
188 _unset[i].show_all();
189 __unset[i] = (i == SS_FILL)? (_("Unset fill")) : (_("Unset stroke"));
191 _color_preview[i] = new Inkscape::UI::Widget::ColorPreview (0);
192 __color[i] = (i == SS_FILL)? (_("Flat color fill")) : (_("Flat color stroke"));
194 // TRANSLATOR COMMENT: A means "Averaged"
195 _averaged[i].set_markup (_("<b>a</b>"));
196 sp_set_font_size_smaller (GTK_WIDGET(_averaged[i].gobj()));
197 _averaged[i].show_all();
198 __averaged[i] = (i == SS_FILL)? (_("Fill is averaged over selected objects")) : (_("Stroke is averaged over selected objects"));
200 // TRANSLATOR COMMENT: M means "Multiple"
201 _multiple[i].set_markup (_("<b>m</b>"));
202 sp_set_font_size_smaller (GTK_WIDGET(_multiple[i].gobj()));
203 _multiple[i].show_all();
204 __multiple[i] = (i == SS_FILL)? (_("Multiple selected objects have the same fill")) : (_("Multiple selected objects have the same stroke"));
206 _popup_edit[i].add(*(new Gtk::Label((i == SS_FILL)? _("Edit fill...") : _("Edit stroke..."), 0.0, 0.5)));
207 _popup_edit[i].signal_activate().connect(sigc::mem_fun(*this,
208 (i == SS_FILL)? &SelectedStyle::on_fill_edit : &SelectedStyle::on_stroke_edit ));
210 _popup_lastused[i].add(*(new Gtk::Label(_("Last set color"), 0.0, 0.5)));
211 _popup_lastused[i].signal_activate().connect(sigc::mem_fun(*this,
212 (i == SS_FILL)? &SelectedStyle::on_fill_lastused : &SelectedStyle::on_stroke_lastused ));
214 _popup_lastselected[i].add(*(new Gtk::Label(_("Last selected color"), 0.0, 0.5)));
215 _popup_lastselected[i].signal_activate().connect(sigc::mem_fun(*this,
216 (i == SS_FILL)? &SelectedStyle::on_fill_lastselected : &SelectedStyle::on_stroke_lastselected ));
218 _popup_invert[i].add(*(new Gtk::Label(_("Invert"), 0.0, 0.5)));
219 _popup_invert[i].signal_activate().connect(sigc::mem_fun(*this,
220 (i == SS_FILL)? &SelectedStyle::on_fill_invert : &SelectedStyle::on_stroke_invert ));
222 _popup_white[i].add(*(new Gtk::Label(_("White"), 0.0, 0.5)));
223 _popup_white[i].signal_activate().connect(sigc::mem_fun(*this,
224 (i == SS_FILL)? &SelectedStyle::on_fill_white : &SelectedStyle::on_stroke_white ));
226 _popup_black[i].add(*(new Gtk::Label(_("Black"), 0.0, 0.5)));
227 _popup_black[i].signal_activate().connect(sigc::mem_fun(*this,
228 (i == SS_FILL)? &SelectedStyle::on_fill_black : &SelectedStyle::on_stroke_black ));
230 _popup_copy[i].add(*(new Gtk::Label(_("Copy color"), 0.0, 0.5)));
231 _popup_copy[i].signal_activate().connect(sigc::mem_fun(*this,
232 (i == SS_FILL)? &SelectedStyle::on_fill_copy : &SelectedStyle::on_stroke_copy ));
234 _popup_paste[i].add(*(new Gtk::Label(_("Paste color"), 0.0, 0.5)));
235 _popup_paste[i].signal_activate().connect(sigc::mem_fun(*this,
236 (i == SS_FILL)? &SelectedStyle::on_fill_paste : &SelectedStyle::on_stroke_paste ));
238 _popup_swap[i].add(*(new Gtk::Label(_("Swap fill and stroke"), 0.0, 0.5)));
239 _popup_swap[i].signal_activate().connect(sigc::mem_fun(*this,
240 &SelectedStyle::on_fillstroke_swap));
242 _popup_opaque[i].add(*(new Gtk::Label((i == SS_FILL)? _("Make fill opaque") : _("Make stroke opaque"), 0.0, 0.5)));
243 _popup_opaque[i].signal_activate().connect(sigc::mem_fun(*this,
244 (i == SS_FILL)? &SelectedStyle::on_fill_opaque : &SelectedStyle::on_stroke_opaque ));
246 //TRANSLATORS COMMENT: unset is a verb here
247 _popup_unset[i].add(*(new Gtk::Label((i == SS_FILL)? _("Unset fill") : _("Unset stroke"), 0.0, 0.5)));
248 _popup_unset[i].signal_activate().connect(sigc::mem_fun(*this,
249 (i == SS_FILL)? &SelectedStyle::on_fill_unset : &SelectedStyle::on_stroke_unset ));
251 _popup_remove[i].add(*(new Gtk::Label((i == SS_FILL)? _("Remove fill") : _("Remove stroke"), 0.0, 0.5)));
252 _popup_remove[i].signal_activate().connect(sigc::mem_fun(*this,
253 (i == SS_FILL)? &SelectedStyle::on_fill_remove : &SelectedStyle::on_stroke_remove ));
255 _popup[i].attach(_popup_edit[i], 0,1, 0,1);
256 _popup[i].attach(*(new Gtk::SeparatorMenuItem()), 0,1, 1,2);
257 _popup[i].attach(_popup_lastused[i], 0,1, 2,3);
258 _popup[i].attach(_popup_lastselected[i], 0,1, 3,4);
259 _popup[i].attach(*(new Gtk::SeparatorMenuItem()), 0,1, 4,5);
260 _popup[i].attach(_popup_invert[i], 0,1, 5,6);
261 _popup[i].attach(*(new Gtk::SeparatorMenuItem()), 0,1, 6,7);
262 _popup[i].attach(_popup_white[i], 0,1, 7,8);
263 _popup[i].attach(_popup_black[i], 0,1, 8,9);
264 _popup[i].attach(*(new Gtk::SeparatorMenuItem()), 0,1, 9,10);
265 _popup[i].attach(_popup_copy[i], 0,1, 10,11);
266 _popup_copy[i].set_sensitive(false);
267 _popup[i].attach(_popup_paste[i], 0,1, 11,12);
268 _popup[i].attach(_popup_swap[i], 0,1, 12,13);
269 _popup[i].attach(*(new Gtk::SeparatorMenuItem()), 0,1, 13,14);
270 _popup[i].attach(_popup_opaque[i], 0,1, 14,15);
271 _popup[i].attach(_popup_unset[i], 0,1, 15,16);
272 _popup[i].attach(_popup_remove[i], 0,1, 16,17);
273 _popup[i].show_all();
275 _mode[i] = SS_NA;
276 }
278 {
279 _popup_px.add(*(new Gtk::Label(_("px"), 0.0, 0.5)));
280 _popup_px.signal_activate().connect(sigc::mem_fun(*this, &SelectedStyle::on_popup_px));
281 _popup_sw.attach(_popup_px, 0,1, 0,1);
283 _popup_pt.add(*(new Gtk::Label(_("pt"), 0.0, 0.5)));
284 _popup_pt.signal_activate().connect(sigc::mem_fun(*this, &SelectedStyle::on_popup_pt));
285 _popup_sw.attach(_popup_pt, 0,1, 1,2);
287 _popup_mm.add(*(new Gtk::Label(_("mm"), 0.0, 0.5)));
288 _popup_mm.signal_activate().connect(sigc::mem_fun(*this, &SelectedStyle::on_popup_mm));
289 _popup_sw.attach(_popup_mm, 0,1, 2,3);
291 _popup_sw.attach(*(new Gtk::SeparatorMenuItem()), 0,1, 3,4);
293 for (guint i = 0; i < G_N_ELEMENTS(_sw_presets_str); ++i) {
294 Gtk::MenuItem *mi = Gtk::manage(new Gtk::MenuItem());
295 mi->add(*(new Gtk::Label(_sw_presets_str[i], 0.0, 0.5)));
296 mi->signal_activate().connect(sigc::bind<int>(sigc::mem_fun(*this, &SelectedStyle::on_popup_preset), i));
297 _popup_sw.attach(*mi, 0,1, 4+i, 5+i);
298 }
300 guint i = G_N_ELEMENTS(_sw_presets_str) + 5;
302 _popup_sw.attach(*(new Gtk::SeparatorMenuItem()), 0,1, i,i+1);
304 _popup_sw_remove.add(*(new Gtk::Label(_("Remove"), 0.0, 0.5)));
305 _popup_sw_remove.signal_activate().connect(sigc::mem_fun(*this, &SelectedStyle::on_stroke_remove));
306 _popup_sw.attach(_popup_sw_remove, 0,1, i+1,i+2);
308 _popup_sw.show_all();
309 }
311 _fill_place.signal_button_release_event().connect(sigc::mem_fun(*this, &SelectedStyle::on_fill_click));
312 _stroke_place.signal_button_release_event().connect(sigc::mem_fun(*this, &SelectedStyle::on_stroke_click));
313 _opacity_place.signal_button_press_event().connect(sigc::mem_fun(*this, &SelectedStyle::on_opacity_click));
314 _stroke_width_place.signal_button_press_event().connect(sigc::mem_fun(*this, &SelectedStyle::on_sw_click));
316 _opacity_sb.signal_populate_popup().connect(sigc::mem_fun(*this, &SelectedStyle::on_opacity_menu));
317 _opacity_sb.signal_value_changed().connect(sigc::mem_fun(*this, &SelectedStyle::on_opacity_changed));
319 _fill_place.add(_na[SS_FILL]);
320 _tooltips.set_tip(_fill_place, __na[SS_FILL]);
322 _stroke_place.add(_na[SS_STROKE]);
323 _tooltips.set_tip(_stroke_place, __na[SS_STROKE]);
325 _stroke.pack_start(_stroke_place);
326 _stroke_width_place.add(_stroke_width);
327 _stroke.pack_start(_stroke_width_place, Gtk::PACK_SHRINK);
329 _opacity_sb.set_adjustment(_opacity_adjustment);
330 sp_set_font_size_smaller (GTK_WIDGET(_opacity_sb.gobj()));
331 _opacity_sb.set_size_request (SELECTED_STYLE_SB_WIDTH, -1);
332 _opacity_sb.set_sensitive (false);
334 _table.attach(_fill_label, 0,1, 0,1, Gtk::FILL, Gtk::SHRINK);
335 _table.attach(_stroke_label, 0,1, 1,2, Gtk::FILL, Gtk::SHRINK);
337 _table.attach(_fill_flag_place, 1,2, 0,1, Gtk::SHRINK, Gtk::SHRINK);
338 _table.attach(_stroke_flag_place, 1,2, 1,2, Gtk::SHRINK, Gtk::SHRINK);
340 _table.attach(_fill_place, 2,3, 0,1);
341 _table.attach(_stroke, 2,3, 1,2);
343 _opacity_place.add(_opacity_label);
344 _table.attach(_opacity_place, 4,5, 0,2, Gtk::SHRINK, Gtk::SHRINK);
345 _table.attach(_opacity_sb, 5,6, 0,2, Gtk::SHRINK, Gtk::SHRINK);
347 pack_start(_table, true, true, 2);
349 set_size_request (SELECTED_STYLE_WIDTH, -1);
351 sp_set_font_size_smaller (GTK_WIDGET(_opacity_label.gobj()));
352 sp_set_font_size_smaller (GTK_WIDGET(_opacity_sb.gobj()));
353 sp_set_font_size_smaller (GTK_WIDGET(_fill_place.gobj()));
354 sp_set_font_size_smaller (GTK_WIDGET(_fill_flag_place.gobj()));
355 sp_set_font_size_smaller (GTK_WIDGET(_stroke_place.gobj()));
356 sp_set_font_size_smaller (GTK_WIDGET(_stroke_flag_place.gobj()));
357 sp_set_font_size_smaller (GTK_WIDGET(_stroke_width.gobj()));
358 sp_set_font_size_smaller (GTK_WIDGET(_fill_label.gobj()));
359 sp_set_font_size_smaller (GTK_WIDGET(_stroke_label.gobj()));
361 _drop[SS_FILL] = new DropTracker();
362 ((DropTracker*)_drop[SS_FILL])->parent = this;
363 ((DropTracker*)_drop[SS_FILL])->item = SS_FILL;
365 _drop[SS_STROKE] = new DropTracker();
366 ((DropTracker*)_drop[SS_STROKE])->parent = this;
367 ((DropTracker*)_drop[SS_STROKE])->item = SS_STROKE;
369 g_signal_connect(_stroke_place.gobj(),
370 "drag_data_received",
371 G_CALLBACK(dragDataReceived),
372 _drop[SS_STROKE]);
374 g_signal_connect(_fill_place.gobj(),
375 "drag_data_received",
376 G_CALLBACK(dragDataReceived),
377 _drop[SS_FILL]);
379 _fill_place.parent = this;
380 _stroke_place.parent = this;
381 }
383 SelectedStyle::~SelectedStyle()
384 {
385 selection_changed_connection->disconnect();
386 delete selection_changed_connection;
387 selection_modified_connection->disconnect();
388 delete selection_modified_connection;
389 subselection_changed_connection->disconnect();
390 delete subselection_changed_connection;
392 for (int i = SS_FILL; i <= SS_STROKE; i++) {
393 delete _color_preview[i];
394 // FIXME: do we need this? the destroy methods are not exported
395 //sp_gradient_image_destroy(GTK_OBJECT(_gradient_preview_l[i]));
396 //sp_gradient_image_destroy(GTK_OBJECT(_gradient_preview_r[i]));
397 }
399 delete (DropTracker*)_drop[SS_FILL];
400 delete (DropTracker*)_drop[SS_STROKE];
401 }
403 void
404 SelectedStyle::setDesktop(SPDesktop *desktop)
405 {
406 _desktop = desktop;
407 gtk_object_set_data (GTK_OBJECT(_opacity_sb.gobj()), "dtw", _desktop->canvas);
409 Inkscape::Selection *selection = sp_desktop_selection (desktop);
411 selection_changed_connection = new sigc::connection (selection->connectChanged(
412 sigc::bind (
413 sigc::ptr_fun(&ss_selection_changed),
414 this )
415 ));
416 selection_modified_connection = new sigc::connection (selection->connectModified(
417 sigc::bind (
418 sigc::ptr_fun(&ss_selection_modified),
419 this )
420 ));
421 subselection_changed_connection = new sigc::connection (desktop->connectToolSubselectionChanged(
422 sigc::bind (
423 sigc::ptr_fun(&ss_subselection_changed),
424 this )
425 ));
427 //_sw_unit = (SPUnit *) sp_desktop_namedview(desktop)->doc_units;
428 }
430 void SelectedStyle::dragDataReceived( GtkWidget */*widget*/,
431 GdkDragContext */*drag_context*/,
432 gint /*x*/, gint /*y*/,
433 GtkSelectionData *data,
434 guint /*info*/,
435 guint /*event_time*/,
436 gpointer user_data )
437 {
438 DropTracker* tracker = (DropTracker*)user_data;
440 switch ( (int)tracker->item ) {
441 case SS_FILL:
442 case SS_STROKE:
443 {
444 if ( data->length == 8 ) {
445 gchar c[64];
446 // Careful about endian issues.
447 guint16* dataVals = (guint16*)data->data;
448 sp_svg_write_color( c, sizeof(c),
449 SP_RGBA32_U_COMPOSE(
450 0x0ff & (dataVals[0] >> 8),
451 0x0ff & (dataVals[1] >> 8),
452 0x0ff & (dataVals[2] >> 8),
453 0xff // can't have transparency in the color itself
454 //0x0ff & (data->data[3] >> 8),
455 ));
456 SPCSSAttr *css = sp_repr_css_attr_new();
457 sp_repr_css_set_property( css, (tracker->item == SS_FILL) ? "fill":"stroke", c );
458 sp_desktop_set_style( tracker->parent->_desktop, css );
459 sp_repr_css_attr_unref( css );
460 sp_document_done( sp_desktop_document(tracker->parent->_desktop) , SP_VERB_NONE,
461 _("Drop color"));
462 }
463 }
464 break;
465 }
466 }
468 void SelectedStyle::on_fill_remove() {
469 SPCSSAttr *css = sp_repr_css_attr_new ();
470 sp_repr_css_set_property (css, "fill", "none");
471 sp_desktop_set_style (_desktop, css, true, true);
472 sp_repr_css_attr_unref (css);
473 sp_document_done (sp_desktop_document(_desktop), SP_VERB_DIALOG_FILL_STROKE,
474 _("Remove fill"));
475 }
477 void SelectedStyle::on_stroke_remove() {
478 SPCSSAttr *css = sp_repr_css_attr_new ();
479 sp_repr_css_set_property (css, "stroke", "none");
480 sp_desktop_set_style (_desktop, css, true, true);
481 sp_repr_css_attr_unref (css);
482 sp_document_done (sp_desktop_document(_desktop), SP_VERB_DIALOG_FILL_STROKE,
483 _("Remove stroke"));
484 }
486 void SelectedStyle::on_fill_unset() {
487 SPCSSAttr *css = sp_repr_css_attr_new ();
488 sp_repr_css_unset_property (css, "fill");
489 sp_desktop_set_style (_desktop, css, true, true);
490 sp_repr_css_attr_unref (css);
491 sp_document_done (sp_desktop_document(_desktop), SP_VERB_DIALOG_FILL_STROKE,
492 _("Unset fill"));
493 }
495 void SelectedStyle::on_stroke_unset() {
496 SPCSSAttr *css = sp_repr_css_attr_new ();
497 sp_repr_css_unset_property (css, "stroke");
498 sp_repr_css_unset_property (css, "stroke-opacity");
499 sp_repr_css_unset_property (css, "stroke-width");
500 sp_repr_css_unset_property (css, "stroke-miterlimit");
501 sp_repr_css_unset_property (css, "stroke-linejoin");
502 sp_repr_css_unset_property (css, "stroke-linecap");
503 sp_repr_css_unset_property (css, "stroke-dashoffset");
504 sp_repr_css_unset_property (css, "stroke-dasharray");
505 sp_desktop_set_style (_desktop, css, true, true);
506 sp_repr_css_attr_unref (css);
507 sp_document_done (sp_desktop_document(_desktop), SP_VERB_DIALOG_FILL_STROKE,
508 _("Unset stroke"));
509 }
511 void SelectedStyle::on_fill_opaque() {
512 SPCSSAttr *css = sp_repr_css_attr_new ();
513 sp_repr_css_set_property (css, "fill-opacity", "1");
514 sp_desktop_set_style (_desktop, css, true);
515 sp_repr_css_attr_unref (css);
516 sp_document_done (sp_desktop_document(_desktop), SP_VERB_DIALOG_FILL_STROKE,
517 _("Make fill opaque"));
518 }
520 void SelectedStyle::on_stroke_opaque() {
521 SPCSSAttr *css = sp_repr_css_attr_new ();
522 sp_repr_css_set_property (css, "stroke-opacity", "1");
523 sp_desktop_set_style (_desktop, css, true);
524 sp_repr_css_attr_unref (css);
525 sp_document_done (sp_desktop_document(_desktop), SP_VERB_DIALOG_FILL_STROKE,
526 _("Make fill opaque"));
527 }
529 void SelectedStyle::on_fill_lastused() {
530 SPCSSAttr *css = sp_repr_css_attr_new ();
531 guint32 color = sp_desktop_get_color(_desktop, true);
532 gchar c[64];
533 sp_svg_write_color (c, sizeof(c), color);
534 sp_repr_css_set_property (css, "fill", c);
535 sp_desktop_set_style (_desktop, css);
536 sp_repr_css_attr_unref (css);
537 sp_document_done (sp_desktop_document(_desktop), SP_VERB_DIALOG_FILL_STROKE,
538 _("Apply last set color to fill"));
539 }
541 void SelectedStyle::on_stroke_lastused() {
542 SPCSSAttr *css = sp_repr_css_attr_new ();
543 guint32 color = sp_desktop_get_color(_desktop, false);
544 gchar c[64];
545 sp_svg_write_color (c, sizeof(c), color);
546 sp_repr_css_set_property (css, "stroke", c);
547 sp_desktop_set_style (_desktop, css);
548 sp_repr_css_attr_unref (css);
549 sp_document_done (sp_desktop_document(_desktop), SP_VERB_DIALOG_FILL_STROKE,
550 _("Apply last set color to stroke"));
551 }
553 void SelectedStyle::on_fill_lastselected() {
554 SPCSSAttr *css = sp_repr_css_attr_new ();
555 gchar c[64];
556 sp_svg_write_color (c, sizeof(c), _lastselected[SS_FILL]);
557 sp_repr_css_set_property (css, "fill", c);
558 sp_desktop_set_style (_desktop, css);
559 sp_repr_css_attr_unref (css);
560 sp_document_done (sp_desktop_document(_desktop), SP_VERB_DIALOG_FILL_STROKE,
561 _("Apply last selected color to fill"));
562 }
564 void SelectedStyle::on_stroke_lastselected() {
565 SPCSSAttr *css = sp_repr_css_attr_new ();
566 gchar c[64];
567 sp_svg_write_color (c, sizeof(c), _lastselected[SS_STROKE]);
568 sp_repr_css_set_property (css, "stroke", c);
569 sp_desktop_set_style (_desktop, css);
570 sp_repr_css_attr_unref (css);
571 sp_document_done (sp_desktop_document(_desktop), SP_VERB_DIALOG_FILL_STROKE,
572 _("Apply last selected color to stroke"));
573 }
575 void SelectedStyle::on_fill_invert() {
576 SPCSSAttr *css = sp_repr_css_attr_new ();
577 guint32 color = _thisselected[SS_FILL];
578 gchar c[64];
579 if (_mode[SS_FILL] != SS_COLOR) return;
580 sp_svg_write_color (c, sizeof(c),
581 SP_RGBA32_U_COMPOSE(
582 (255 - SP_RGBA32_R_U(color)),
583 (255 - SP_RGBA32_G_U(color)),
584 (255 - SP_RGBA32_B_U(color)),
585 SP_RGBA32_A_U(color)
586 )
587 );
588 sp_repr_css_set_property (css, "fill", c);
589 sp_desktop_set_style (_desktop, css);
590 sp_repr_css_attr_unref (css);
591 sp_document_done (sp_desktop_document(_desktop), SP_VERB_DIALOG_FILL_STROKE,
592 _("Invert fill"));
593 }
595 void SelectedStyle::on_stroke_invert() {
596 SPCSSAttr *css = sp_repr_css_attr_new ();
597 guint32 color = _thisselected[SS_STROKE];
598 gchar c[64];
599 if (_mode[SS_STROKE] != SS_COLOR) return;
600 sp_svg_write_color (c, sizeof(c),
601 SP_RGBA32_U_COMPOSE(
602 (255 - SP_RGBA32_R_U(color)),
603 (255 - SP_RGBA32_G_U(color)),
604 (255 - SP_RGBA32_B_U(color)),
605 SP_RGBA32_A_U(color)
606 )
607 );
608 sp_repr_css_set_property (css, "stroke", c);
609 sp_desktop_set_style (_desktop, css);
610 sp_repr_css_attr_unref (css);
611 sp_document_done (sp_desktop_document(_desktop), SP_VERB_DIALOG_FILL_STROKE,
612 _("Invert stroke"));
613 }
615 void SelectedStyle::on_fill_white() {
616 SPCSSAttr *css = sp_repr_css_attr_new ();
617 gchar c[64];
618 sp_svg_write_color (c, sizeof(c), 0xffffffff);
619 sp_repr_css_set_property (css, "fill", c);
620 sp_repr_css_set_property (css, "fill-opacity", "1");
621 sp_desktop_set_style (_desktop, css);
622 sp_repr_css_attr_unref (css);
623 sp_document_done (sp_desktop_document(_desktop), SP_VERB_DIALOG_FILL_STROKE,
624 _("White fill"));
625 }
627 void SelectedStyle::on_stroke_white() {
628 SPCSSAttr *css = sp_repr_css_attr_new ();
629 gchar c[64];
630 sp_svg_write_color (c, sizeof(c), 0xffffffff);
631 sp_repr_css_set_property (css, "stroke", c);
632 sp_repr_css_set_property (css, "stroke-opacity", "1");
633 sp_desktop_set_style (_desktop, css);
634 sp_repr_css_attr_unref (css);
635 sp_document_done (sp_desktop_document(_desktop), SP_VERB_DIALOG_FILL_STROKE,
636 _("White stroke"));
637 }
639 void SelectedStyle::on_fill_black() {
640 SPCSSAttr *css = sp_repr_css_attr_new ();
641 gchar c[64];
642 sp_svg_write_color (c, sizeof(c), 0x000000ff);
643 sp_repr_css_set_property (css, "fill", c);
644 sp_repr_css_set_property (css, "fill-opacity", "1.0");
645 sp_desktop_set_style (_desktop, css);
646 sp_repr_css_attr_unref (css);
647 sp_document_done (sp_desktop_document(_desktop), SP_VERB_DIALOG_FILL_STROKE,
648 _("Black fill"));
649 }
651 void SelectedStyle::on_stroke_black() {
652 SPCSSAttr *css = sp_repr_css_attr_new ();
653 gchar c[64];
654 sp_svg_write_color (c, sizeof(c), 0x000000ff);
655 sp_repr_css_set_property (css, "stroke", c);
656 sp_repr_css_set_property (css, "stroke-opacity", "1.0");
657 sp_desktop_set_style (_desktop, css);
658 sp_repr_css_attr_unref (css);
659 sp_document_done (sp_desktop_document(_desktop), SP_VERB_DIALOG_FILL_STROKE,
660 _("Black stroke"));
661 }
663 void SelectedStyle::on_fill_copy() {
664 if (_mode[SS_FILL] == SS_COLOR) {
665 gchar c[64];
666 sp_svg_write_color (c, sizeof(c), _thisselected[SS_FILL]);
667 Glib::ustring text;
668 text += c;
669 if (!text.empty()) {
670 Glib::RefPtr<Gtk::Clipboard> refClipboard = Gtk::Clipboard::get();
671 refClipboard->set_text(text);
672 }
673 }
674 }
676 void SelectedStyle::on_stroke_copy() {
677 if (_mode[SS_STROKE] == SS_COLOR) {
678 gchar c[64];
679 sp_svg_write_color (c, sizeof(c), _thisselected[SS_STROKE]);
680 Glib::ustring text;
681 text += c;
682 if (!text.empty()) {
683 Glib::RefPtr<Gtk::Clipboard> refClipboard = Gtk::Clipboard::get();
684 refClipboard->set_text(text);
685 }
686 }
687 }
689 void SelectedStyle::on_fill_paste() {
690 Glib::RefPtr<Gtk::Clipboard> refClipboard = Gtk::Clipboard::get();
691 Glib::ustring const text = refClipboard->wait_for_text();
693 if (!text.empty()) {
694 guint32 color = sp_svg_read_color(text.c_str(), 0x000000ff); // impossible value, as SVG color cannot have opacity
695 if (color == 0x000000ff) // failed to parse color string
696 return;
698 SPCSSAttr *css = sp_repr_css_attr_new ();
699 sp_repr_css_set_property (css, "fill", text.c_str());
700 sp_desktop_set_style (_desktop, css);
701 sp_repr_css_attr_unref (css);
702 sp_document_done (sp_desktop_document(_desktop), SP_VERB_DIALOG_FILL_STROKE,
703 _("Paste fill"));
704 }
705 }
707 void SelectedStyle::on_stroke_paste() {
708 Glib::RefPtr<Gtk::Clipboard> refClipboard = Gtk::Clipboard::get();
709 Glib::ustring const text = refClipboard->wait_for_text();
711 if (!text.empty()) {
712 guint32 color = sp_svg_read_color(text.c_str(), 0x000000ff); // impossible value, as SVG color cannot have opacity
713 if (color == 0x000000ff) // failed to parse color string
714 return;
716 SPCSSAttr *css = sp_repr_css_attr_new ();
717 sp_repr_css_set_property (css, "stroke", text.c_str());
718 sp_desktop_set_style (_desktop, css);
719 sp_repr_css_attr_unref (css);
720 sp_document_done (sp_desktop_document(_desktop), SP_VERB_DIALOG_FILL_STROKE,
721 _("Paste stroke"));
722 }
723 }
725 void SelectedStyle::on_fillstroke_swap() {
726 SPCSSAttr *css = sp_repr_css_attr_new ();
728 switch (_mode[SS_FILL]) {
729 case SS_NA:
730 case SS_MANY:
731 break;
732 case SS_NONE:
733 sp_repr_css_set_property (css, "stroke", "none");
734 break;
735 case SS_UNSET:
736 sp_repr_css_unset_property (css, "stroke");
737 break;
738 case SS_COLOR:
739 gchar c[64];
740 sp_svg_write_color (c, sizeof(c), _thisselected[SS_FILL]);
741 sp_repr_css_set_property (css, "stroke", c);
742 break;
743 case SS_LGRADIENT:
744 case SS_RGRADIENT:
745 case SS_PATTERN:
746 sp_repr_css_set_property (css, "stroke", _paintserver_id[SS_FILL].c_str());
747 break;
748 }
750 switch (_mode[SS_STROKE]) {
751 case SS_NA:
752 case SS_MANY:
753 break;
754 case SS_NONE:
755 sp_repr_css_set_property (css, "fill", "none");
756 break;
757 case SS_UNSET:
758 sp_repr_css_unset_property (css, "fill");
759 break;
760 case SS_COLOR:
761 gchar c[64];
762 sp_svg_write_color (c, sizeof(c), _thisselected[SS_STROKE]);
763 sp_repr_css_set_property (css, "fill", c);
764 break;
765 case SS_LGRADIENT:
766 case SS_RGRADIENT:
767 case SS_PATTERN:
768 sp_repr_css_set_property (css, "fill", _paintserver_id[SS_STROKE].c_str());
769 break;
770 }
772 sp_desktop_set_style (_desktop, css);
773 sp_repr_css_attr_unref (css);
774 sp_document_done (sp_desktop_document(_desktop), SP_VERB_DIALOG_FILL_STROKE,
775 _("Swap fill and stroke"));
776 }
778 void SelectedStyle::on_fill_edit() {
779 if (Dialog::FillAndStroke *fs = get_fill_and_stroke_panel(_desktop))
780 fs->showPageFill();
781 }
783 void SelectedStyle::on_stroke_edit() {
784 if (Dialog::FillAndStroke *fs = get_fill_and_stroke_panel(_desktop))
785 fs->showPageStrokePaint();
786 }
788 bool
789 SelectedStyle::on_fill_click(GdkEventButton *event)
790 {
791 if (event->button == 1) { // click, open fill&stroke
793 if (Dialog::FillAndStroke *fs = get_fill_and_stroke_panel(_desktop))
794 fs->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 *fs = get_fill_and_stroke_panel(_desktop))
813 fs->showPageStrokePaint();
814 } else if (event->button == 3) { // right-click, popup menu
815 _popup[SS_STROKE].popup(event->button, event->time);
816 } else if (event->button == 2) { // middle click, toggle none/lastcolor
817 if (_mode[SS_STROKE] == SS_NONE) {
818 on_stroke_lastused();
819 } else {
820 on_stroke_remove();
821 }
822 }
823 return true;
824 }
826 bool
827 SelectedStyle::on_sw_click(GdkEventButton *event)
828 {
829 if (event->button == 1) { // click, open fill&stroke
830 if (Dialog::FillAndStroke *fs = get_fill_and_stroke_panel(_desktop))
831 fs->showPageStrokeStyle();
832 } else if (event->button == 3) { // right-click, popup menu
833 _popup_sw.popup(event->button, event->time);
834 } else if (event->button == 2) { // middle click, toggle none/lastwidth?
835 //
836 }
837 return true;
838 }
840 bool
841 SelectedStyle::on_opacity_click(GdkEventButton *event)
842 {
843 if (event->button == 2) { // middle click
844 const char* opacity = _opacity_sb.get_value() < 50? "0.5" : (_opacity_sb.get_value() == 100? "0" : "1");
845 SPCSSAttr *css = sp_repr_css_attr_new ();
846 sp_repr_css_set_property (css, "opacity", opacity);
847 sp_desktop_set_style (_desktop, css);
848 sp_repr_css_attr_unref (css);
849 sp_document_done (sp_desktop_document (_desktop), SP_VERB_DIALOG_FILL_STROKE,
850 _("Change opacity"));
851 return true;
852 }
854 return false;
855 }
857 void SelectedStyle::on_popup_px() {
858 _sw_unit = (SPUnit *) &(sp_unit_get_by_id(SP_UNIT_PX));
859 update();
860 }
861 void SelectedStyle::on_popup_pt() {
862 _sw_unit = (SPUnit *) &(sp_unit_get_by_id(SP_UNIT_PT));
863 update();
864 }
865 void SelectedStyle::on_popup_mm() {
866 _sw_unit = (SPUnit *) &(sp_unit_get_by_id(SP_UNIT_MM));
867 update();
868 }
870 void SelectedStyle::on_popup_preset(int i) {
871 SPCSSAttr *css = sp_repr_css_attr_new ();
872 gdouble w;
873 if (_sw_unit) {
874 w = sp_units_get_pixels (_sw_presets[i], *_sw_unit);
875 } else {
876 w = _sw_presets[i];
877 }
878 Inkscape::CSSOStringStream os;
879 os << w;
880 sp_repr_css_set_property (css, "stroke-width", os.str().c_str());
881 // FIXME: update dash patterns!
882 sp_desktop_set_style (_desktop, css, true);
883 sp_repr_css_attr_unref (css);
884 sp_document_done (sp_desktop_document(_desktop), SP_VERB_DIALOG_SWATCHES,
885 _("Change stroke width"));
886 }
888 void
889 SelectedStyle::update()
890 {
891 if (_desktop == NULL)
892 return;
894 // create temporary style
895 SPStyle *query = sp_style_new (sp_desktop_document(_desktop));
897 for (int i = SS_FILL; i <= SS_STROKE; i++) {
898 Gtk::EventBox *place = (i == SS_FILL)? &_fill_place : &_stroke_place;
899 Gtk::EventBox *flag_place = (i == SS_FILL)? &_fill_flag_place : &_stroke_flag_place;
901 place->remove();
902 flag_place->remove();
904 _tooltips.unset_tip(*place);
905 _tooltips.unset_tip(*flag_place);
907 _mode[i] = SS_NA;
908 _paintserver_id[i].clear();
910 _popup_copy[i].set_sensitive(false);
912 // query style from desktop. This returns a result flag and fills query with the style of subselection, if any, or selection
913 int result = sp_desktop_query_style (_desktop, query,
914 (i == SS_FILL)? QUERY_STYLE_PROPERTY_FILL : QUERY_STYLE_PROPERTY_STROKE);
915 switch (result) {
916 case QUERY_STYLE_NOTHING:
917 place->add(_na[i]);
918 _tooltips.set_tip(*place, __na[i]);
919 _mode[i] = SS_NA;
920 if ( _dropEnabled[i] ) {
921 gtk_drag_dest_unset( GTK_WIDGET((i==SS_FILL) ? _fill_place.gobj():_stroke_place.gobj()) );
922 _dropEnabled[i] = false;
923 }
924 break;
925 case QUERY_STYLE_SINGLE:
926 case QUERY_STYLE_MULTIPLE_AVERAGED:
927 case QUERY_STYLE_MULTIPLE_SAME:
928 if ( !_dropEnabled[i] ) {
929 gtk_drag_dest_set( GTK_WIDGET( (i==SS_FILL) ? _fill_place.gobj():_stroke_place.gobj()),
930 GTK_DEST_DEFAULT_ALL,
931 ui_drop_target_entries,
932 nui_drop_target_entries,
933 GdkDragAction(GDK_ACTION_COPY | GDK_ACTION_MOVE) );
934 _dropEnabled[i] = true;
935 }
936 SPIPaint *paint;
937 if (i == SS_FILL) {
938 paint = &(query->fill);
939 } else {
940 paint = &(query->stroke);
941 }
942 if (paint->set && paint->isPaintserver()) {
943 SPPaintServer *server = (i == SS_FILL)? SP_STYLE_FILL_SERVER (query) : SP_STYLE_STROKE_SERVER (query);
944 if ( server ) {
945 Inkscape::XML::Node *srepr = SP_OBJECT_REPR(server);
946 _paintserver_id[i] += "url(#";
947 _paintserver_id[i] += srepr->attribute("id");
948 _paintserver_id[i] += ")";
950 if (SP_IS_LINEARGRADIENT (server)) {
951 SPGradient *vector = sp_gradient_get_vector(SP_GRADIENT(server), false);
952 sp_gradient_image_set_gradient ((SPGradientImage *) _gradient_preview_l[i], vector);
953 place->add(_gradient_box_l[i]);
954 _tooltips.set_tip(*place, __lgradient[i]);
955 _mode[i] = SS_LGRADIENT;
956 } else if (SP_IS_RADIALGRADIENT (server)) {
957 SPGradient *vector = sp_gradient_get_vector(SP_GRADIENT(server), false);
958 sp_gradient_image_set_gradient ((SPGradientImage *) _gradient_preview_r[i], vector);
959 place->add(_gradient_box_r[i]);
960 _tooltips.set_tip(*place, __rgradient[i]);
961 _mode[i] = SS_RGRADIENT;
962 } else if (SP_IS_PATTERN (server)) {
963 place->add(_pattern[i]);
964 _tooltips.set_tip(*place, __pattern[i]);
965 _mode[i] = SS_PATTERN;
966 }
967 } else {
968 g_warning ("file %s: line %d: Unknown paint server", __FILE__, __LINE__);
969 }
970 } else if (paint->set && paint->isColor()) {
971 guint32 color = paint->value.color.toRGBA32(
972 SP_SCALE24_TO_FLOAT ((i == SS_FILL)? query->fill_opacity.value : query->stroke_opacity.value));
973 _lastselected[i] = _thisselected[i];
974 _thisselected[i] = color | 0xff; // only color, opacity === 1
975 ((Inkscape::UI::Widget::ColorPreview*)_color_preview[i])->setRgba32 (color);
976 _color_preview[i]->show_all();
977 place->add(*_color_preview[i]);
978 gchar c_string[64];
979 g_snprintf (c_string, 64, "%06x/%.3g", color >> 8, SP_RGBA32_A_F(color));
980 _tooltips.set_tip(*place, __color[i] + ": " + c_string + _(", drag to adjust"));
981 _mode[i] = SS_COLOR;
982 _popup_copy[i].set_sensitive(true);
984 } else if (paint->set && paint->isNone()) {
985 place->add(_none[i]);
986 _tooltips.set_tip(*place, __none[i]);
987 _mode[i] = SS_NONE;
988 } else if (!paint->set) {
989 place->add(_unset[i]);
990 _tooltips.set_tip(*place, __unset[i]);
991 _mode[i] = SS_UNSET;
992 }
993 if (result == QUERY_STYLE_MULTIPLE_AVERAGED) {
994 flag_place->add(_averaged[i]);
995 _tooltips.set_tip(*flag_place, __averaged[i]);
996 } else if (result == QUERY_STYLE_MULTIPLE_SAME) {
997 flag_place->add(_multiple[i]);
998 _tooltips.set_tip(*flag_place, __multiple[i]);
999 }
1000 break;
1001 case QUERY_STYLE_MULTIPLE_DIFFERENT:
1002 place->add(_many[i]);
1003 _tooltips.set_tip(*place, __many[i]);
1004 _mode[i] = SS_MANY;
1005 break;
1006 default:
1007 break;
1008 }
1009 }
1011 // Now query opacity
1012 _tooltips.unset_tip(_opacity_place);
1013 _tooltips.unset_tip(_opacity_sb);
1015 int result = sp_desktop_query_style (_desktop, query, QUERY_STYLE_PROPERTY_MASTEROPACITY);
1017 switch (result) {
1018 case QUERY_STYLE_NOTHING:
1019 _tooltips.set_tip(_opacity_place, _("Nothing selected"));
1020 _tooltips.set_tip(_opacity_sb, _("Nothing selected"));
1021 _opacity_sb.set_sensitive(false);
1022 break;
1023 case QUERY_STYLE_SINGLE:
1024 case QUERY_STYLE_MULTIPLE_AVERAGED:
1025 case QUERY_STYLE_MULTIPLE_SAME:
1026 _tooltips.set_tip(_opacity_place, _("Opacity, %"));
1027 _tooltips.set_tip(_opacity_sb, _("Opacity, %"));
1028 if (_opacity_blocked) break;
1029 _opacity_blocked = true;
1030 _opacity_sb.set_sensitive(true);
1031 _opacity_adjustment.set_value(SP_SCALE24_TO_FLOAT(query->opacity.value) * 100);
1032 _opacity_blocked = false;
1033 break;
1034 }
1036 // Now query stroke_width
1037 int result_sw = sp_desktop_query_style (_desktop, query, QUERY_STYLE_PROPERTY_STROKEWIDTH);
1038 switch (result_sw) {
1039 case QUERY_STYLE_NOTHING:
1040 _stroke_width.set_markup("");
1041 break;
1042 case QUERY_STYLE_SINGLE:
1043 case QUERY_STYLE_MULTIPLE_AVERAGED:
1044 case QUERY_STYLE_MULTIPLE_SAME:
1045 {
1046 double w;
1047 if (_sw_unit) {
1048 w = sp_pixels_get_units(query->stroke_width.computed, *_sw_unit);
1049 } else {
1050 w = query->stroke_width.computed;
1051 }
1052 {
1053 gchar *str = g_strdup_printf(" %.3g", w);
1054 _stroke_width.set_markup(str);
1055 g_free (str);
1056 }
1057 {
1058 gchar *str = g_strdup_printf(_("Stroke width: %.5g%s%s"),
1059 w,
1060 _sw_unit? sp_unit_get_abbreviation(_sw_unit) : "px",
1061 (result_sw == QUERY_STYLE_MULTIPLE_AVERAGED)?
1062 _(" (averaged)") : "");
1063 _tooltips.set_tip(_stroke_width_place, str);
1064 g_free (str);
1065 }
1066 break;
1067 }
1068 default:
1069 break;
1070 }
1072 sp_style_unref(query);
1073 }
1075 void SelectedStyle::opacity_0(void) {_opacity_sb.set_value(0);}
1076 void SelectedStyle::opacity_025(void) {_opacity_sb.set_value(25);}
1077 void SelectedStyle::opacity_05(void) {_opacity_sb.set_value(50);}
1078 void SelectedStyle::opacity_075(void) {_opacity_sb.set_value(75);}
1079 void SelectedStyle::opacity_1(void) {_opacity_sb.set_value(100);}
1081 void SelectedStyle::on_opacity_menu (Gtk::Menu *menu) {
1083 Glib::ListHandle<Gtk::Widget *> children = menu->get_children();
1084 for (Glib::ListHandle<Gtk::Widget *>::iterator iter = children.begin(); iter != children.end(); iter++) {
1085 menu->remove(*(*iter));
1086 }
1088 {
1089 Gtk::MenuItem *item = new Gtk::MenuItem;
1090 item->add(*(new Gtk::Label(_("0 (transparent)"), 0, 0)));
1091 item->signal_activate().connect(sigc::mem_fun(*this, &SelectedStyle::opacity_0 ));
1092 menu->add(*item);
1093 }
1094 {
1095 Gtk::MenuItem *item = new Gtk::MenuItem;
1096 item->add(*(new Gtk::Label("25%", 0, 0)));
1097 item->signal_activate().connect(sigc::mem_fun(*this, &SelectedStyle::opacity_025 ));
1098 menu->add(*item);
1099 }
1100 {
1101 Gtk::MenuItem *item = new Gtk::MenuItem;
1102 item->add(*(new Gtk::Label("50%", 0, 0)));
1103 item->signal_activate().connect(sigc::mem_fun(*this, &SelectedStyle::opacity_05 ));
1104 menu->add(*item);
1105 }
1106 {
1107 Gtk::MenuItem *item = new Gtk::MenuItem;
1108 item->add(*(new Gtk::Label("75%", 0, 0)));
1109 item->signal_activate().connect(sigc::mem_fun(*this, &SelectedStyle::opacity_075 ));
1110 menu->add(*item);
1111 }
1112 {
1113 Gtk::MenuItem *item = new Gtk::MenuItem;
1114 item->add(*(new Gtk::Label(_("100% (opaque)"), 0, 0)));
1115 item->signal_activate().connect(sigc::mem_fun(*this, &SelectedStyle::opacity_1 ));
1116 menu->add(*item);
1117 }
1119 menu->show_all();
1120 }
1122 void SelectedStyle::on_opacity_changed () {
1123 if (_opacity_blocked)
1124 return;
1125 _opacity_blocked = true;
1126 SPCSSAttr *css = sp_repr_css_attr_new ();
1127 Inkscape::CSSOStringStream os;
1128 os << CLAMP ((_opacity_adjustment.get_value() / 100), 0.0, 1.0);
1129 sp_repr_css_set_property (css, "opacity", os.str().c_str());
1130 // FIXME: workaround for GTK breakage: display interruptibility sometimes results in GTK
1131 // sending multiple value-changed events. As if when Inkscape interrupts redraw for main loop
1132 // iterations, GTK discovers that this callback hasn't finished yet, and for some weird reason
1133 // decides to add yet another value-changed event to the queue. Totally braindead if you ask
1134 // me. As a result, scrolling the spinbutton once results in runaway change until it hits 1.0
1135 // or 0.0. (And no, this is not a race with ::update, I checked that.)
1136 // Sigh. So we disable interruptibility while we're setting the new value.
1137 sp_canvas_force_full_redraw_after_interruptions(sp_desktop_canvas(_desktop), 0);
1138 sp_desktop_set_style (_desktop, css);
1139 sp_repr_css_attr_unref (css);
1140 sp_document_maybe_done (sp_desktop_document (_desktop), "fillstroke:opacity", SP_VERB_DIALOG_FILL_STROKE,
1141 _("Change opacity"));
1142 // resume interruptibility
1143 sp_canvas_end_forced_full_redraws(sp_desktop_canvas(_desktop));
1144 spinbutton_defocus(GTK_OBJECT(_opacity_sb.gobj()));
1145 _opacity_blocked = false;
1146 }
1148 RotateableSwatch::RotateableSwatch(guint mode) {
1149 fillstroke = mode;
1150 startcolor_set = false;
1151 undokey = "ssrot1";
1152 cr = NULL;
1153 cr_set = false;
1154 }
1156 RotateableSwatch::~RotateableSwatch() {
1157 }
1159 double
1160 RotateableSwatch::color_adjust(float *hsl, double by, guint32 cc, guint modifier)
1161 {
1162 sp_color_rgb_to_hsl_floatv (hsl, SP_RGBA32_R_F(cc), SP_RGBA32_G_F(cc), SP_RGBA32_B_F(cc));
1164 double diff = 0;
1165 if (modifier == 2) { // saturation
1166 double old = hsl[1];
1167 if (by > 0) {
1168 hsl[1] += by * (1 - hsl[1]);
1169 } else {
1170 hsl[1] += by * (hsl[1]);
1171 }
1172 diff = hsl[1] - old;
1173 } else if (modifier == 1) { // lightness
1174 double old = hsl[2];
1175 if (by > 0) {
1176 hsl[2] += by * (1 - hsl[2]);
1177 } else {
1178 hsl[2] += by * (hsl[2]);
1179 }
1180 diff = hsl[2] - old;
1181 } else { // hue
1182 double old = hsl[0];
1183 hsl[0] += by/2;
1184 while (hsl[0] < 0)
1185 hsl[0] += 1;
1186 while (hsl[0] > 1)
1187 hsl[0] -= 1;
1188 diff = hsl[0] - old;
1189 }
1191 float rgb[3];
1192 sp_color_hsl_to_rgb_floatv (rgb, hsl[0], hsl[1], hsl[2]);
1194 gchar c[64];
1195 sp_svg_write_color (c, sizeof(c),
1196 SP_RGBA32_U_COMPOSE(
1197 (SP_COLOR_F_TO_U(rgb[0])),
1198 (SP_COLOR_F_TO_U(rgb[1])),
1199 (SP_COLOR_F_TO_U(rgb[2])),
1200 0xff
1201 )
1202 );
1204 SPCSSAttr *css = sp_repr_css_attr_new ();
1205 if (fillstroke == SS_FILL)
1206 sp_repr_css_set_property (css, "fill", c);
1207 else
1208 sp_repr_css_set_property (css, "stroke", c);
1209 sp_desktop_set_style (parent->getDesktop(), css);
1210 sp_repr_css_attr_unref (css);
1211 return diff;
1212 }
1214 void
1215 RotateableSwatch::do_motion(double by, guint modifier) {
1216 if (parent->_mode[fillstroke] != SS_COLOR)
1217 return;
1219 if (!cr_set && modifier != 3) {
1220 GtkWidget *w = GTK_WIDGET(gobj());
1222 GdkBitmap *bitmap = NULL;
1223 GdkBitmap *mask = NULL;
1224 if (modifier == 2) { // saturation
1225 sp_cursor_bitmap_and_mask_from_xpm(&bitmap, &mask, cursor_adj_s_xpm);
1226 } else if (modifier == 1) { // lightness
1227 sp_cursor_bitmap_and_mask_from_xpm(&bitmap, &mask, cursor_adj_l_xpm);
1228 } else { // hue
1229 sp_cursor_bitmap_and_mask_from_xpm(&bitmap, &mask, cursor_adj_h_xpm);
1230 }
1231 if ((bitmap != NULL) && (mask != NULL)) {
1232 cr = gdk_cursor_new_from_pixmap(bitmap, mask,
1233 &w->style->black,
1234 &w->style->white,
1235 16, 16);
1236 g_object_unref (bitmap);
1237 g_object_unref (mask);
1238 gdk_window_set_cursor(w->window, cr);
1239 cr_set = true;
1240 }
1241 }
1243 guint32 cc;
1244 if (!startcolor_set) {
1245 cc = startcolor = parent->_thisselected[fillstroke];
1246 startcolor_set = true;
1247 } else {
1248 cc = startcolor;
1249 }
1251 float hsl[3];
1252 double diff = 0;
1253 if (modifier != 3) {
1254 diff = color_adjust(hsl, by, cc, modifier);
1255 }
1257 if (modifier == 3) { // do nothing
1259 } else if (modifier == 2) { // saturation
1260 sp_document_maybe_done (sp_desktop_document(parent->getDesktop()), undokey,
1261 SP_VERB_DIALOG_FILL_STROKE, (_("Adjust saturation")));
1262 double ch = hsl[1];
1263 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);
1265 } else if (modifier == 1) { // lightness
1266 sp_document_maybe_done (sp_desktop_document(parent->getDesktop()), undokey,
1267 SP_VERB_DIALOG_FILL_STROKE, (_("Adjust lightness")));
1268 double ch = hsl[2];
1269 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);
1271 } else { // hue
1272 sp_document_maybe_done (sp_desktop_document(parent->getDesktop()), undokey,
1273 SP_VERB_DIALOG_FILL_STROKE, (_("Adjust hue")));
1274 double ch = hsl[0];
1275 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);
1276 }
1277 }
1279 void
1280 RotateableSwatch::do_release(double by, guint modifier) {
1281 if (parent->_mode[fillstroke] != SS_COLOR)
1282 return;
1284 float hsl[3];
1285 if (modifier != 3) {
1286 color_adjust(hsl, by, startcolor, modifier);
1287 }
1289 if (cr_set) {
1290 GtkWidget *w = GTK_WIDGET(gobj());
1291 gdk_window_set_cursor(w->window, NULL);
1292 if (cr) {
1293 gdk_cursor_unref (cr);
1294 cr = NULL;
1295 }
1296 cr_set = false;
1297 }
1299 if (modifier == 3) { // nothing
1300 } else if (modifier == 2) { // saturation
1301 sp_document_maybe_done (sp_desktop_document(parent->getDesktop()), undokey,
1302 SP_VERB_DIALOG_FILL_STROKE, ("Adjust saturation"));
1304 } else if (modifier == 1) { // lightness
1305 sp_document_maybe_done (sp_desktop_document(parent->getDesktop()), undokey,
1306 SP_VERB_DIALOG_FILL_STROKE, ("Adjust lightness"));
1308 } else { // hue
1309 sp_document_maybe_done (sp_desktop_document(parent->getDesktop()), undokey,
1310 SP_VERB_DIALOG_FILL_STROKE, ("Adjust hue"));
1311 }
1313 if (!strcmp(undokey, "ssrot1")) {
1314 undokey = "ssrot2";
1315 } else {
1316 undokey = "ssrot1";
1317 }
1319 parent->getDesktop()->event_context->_message_context->clear();
1320 startcolor_set = false;
1321 }
1323 Dialog::FillAndStroke *get_fill_and_stroke_panel(SPDesktop *desktop)
1324 {
1325 if (Dialog::PanelDialogBase *panel_dialog =
1326 dynamic_cast<Dialog::PanelDialogBase *>(desktop->_dlg_mgr->getDialog("FillAndStroke"))) {
1327 try {
1328 Dialog::FillAndStroke &fill_and_stroke =
1329 dynamic_cast<Dialog::FillAndStroke &>(panel_dialog->getPanel());
1330 return &fill_and_stroke;
1331 } catch (std::exception e) { }
1332 }
1334 return 0;
1335 }
1337 } // namespace Widget
1338 } // namespace UI
1339 } // namespace Inkscape
1341 /*
1342 Local Variables:
1343 mode:c++
1344 c-file-style:"stroustrup"
1345 c-file-offsets:((innamespace . 0)(inline-open . 0))
1346 indent-tabs-mode:nil
1347 fill-column:99
1348 End:
1349 */
1350 // vim: filetype=c++:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 :