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