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