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 "dialogs/object-properties.h"
31 #include "xml/repr.h"
32 #include "document.h"
33 #include "widgets/widget-sizes.h"
34 #include "widgets/spinbutton-events.h"
35 #include "svg/svg-color.h"
36 #include "svg/css-ostringstream.h"
37 #include "helper/units.h"
39 static gdouble const _sw_presets[] = { 32 , 16 , 10 , 8 , 6 , 4 , 3 , 2 , 1.5 , 1 , 0.75 , 0.5 , 0.25 , 0.1 };
40 static gchar* const _sw_presets_str[] = {"32", "16", "10", "8", "6", "4", "3", "2", "1.5", "1", "0.75", "0.5", "0.25", "0.1"};
42 static void
43 ss_selection_changed (Inkscape::Selection *, gpointer data)
44 {
45 Inkscape::UI::Widget::SelectedStyle *ss = (Inkscape::UI::Widget::SelectedStyle *) data;
46 ss->update();
47 }
49 static void
50 ss_selection_modified (Inkscape::Selection *selection, guint flags, gpointer data)
51 {
52 ss_selection_changed (selection, data);
53 }
55 static void
56 ss_subselection_changed (gpointer dragger, gpointer data)
57 {
58 ss_selection_changed (NULL, data);
59 }
61 namespace Inkscape {
62 namespace UI {
63 namespace Widget {
66 typedef struct {
67 SelectedStyle* parent;
68 int item;
69 } DropTracker;
71 /* Drag and Drop */
72 typedef enum {
73 APP_X_COLOR
74 } ui_drop_target_info;
76 static GtkTargetEntry ui_drop_target_entries [] = {
77 {"application/x-color", 0, APP_X_COLOR}
78 };
80 #define ENTRIES_SIZE(n) sizeof(n)/sizeof(n[0])
81 static guint nui_drop_target_entries = ENTRIES_SIZE(ui_drop_target_entries);
84 SelectedStyle::SelectedStyle(bool layout)
85 : _desktop (NULL),
87 _table(2, 6),
88 _fill_label (_("F:")),
89 _stroke_label (_("S:")),
90 _opacity_label (_("O:")),
91 _fill_place (),
92 _stroke_place (),
94 _fill_flag_place (),
95 _stroke_flag_place (),
97 _opacity_place (),
98 _opacity_adjustment (1.0, 0.0, 1.0, 0.01, 0.1),
99 _opacity_sb (0.02, 2),
101 _stroke (),
102 _stroke_width (""),
104 _opacity_blocked (false),
106 _popup_px(_sw_group),
107 _popup_pt(_sw_group),
108 _popup_mm(_sw_group),
110 _sw_unit(NULL),
112 _tooltips (),
114 _drop((void*[]){0,0}),
115 _dropEnabled((bool[]){false, false})
116 {
117 _fill_label.set_alignment(0.0, 0.5);
118 _fill_label.set_padding(0, 0);
119 _stroke_label.set_alignment(0.0, 0.5);
120 _stroke_label.set_padding(0, 0);
121 _opacity_label.set_alignment(0.0, 0.5);
122 _opacity_label.set_padding(0, 0);
124 _table.set_col_spacings (2);
125 _table.set_row_spacings (0);
127 for (int i = SS_FILL; i <= SS_STROKE; i++) {
129 _na[i].set_markup (_("N/A"));
130 sp_set_font_size_smaller (GTK_WIDGET(_na[i].gobj()));
131 _na[i].show_all();
132 __na[i] = (_("Nothing selected"));
134 _none[i].set_markup (_("None"));
135 sp_set_font_size_smaller (GTK_WIDGET(_none[i].gobj()));
136 _none[i].show_all();
137 __none[i] = (i == SS_FILL)? (_("No fill")) : (_("No stroke"));
139 _pattern[i].set_markup (_("Pattern"));
140 sp_set_font_size_smaller (GTK_WIDGET(_pattern[i].gobj()));
141 _pattern[i].show_all();
142 __pattern[i] = (i == SS_FILL)? (_("Pattern fill")) : (_("Pattern stroke"));
144 _lgradient[i].set_markup (_("L Gradient"));
145 sp_set_font_size_smaller (GTK_WIDGET(_lgradient[i].gobj()));
146 _lgradient[i].show_all();
147 __lgradient[i] = (i == SS_FILL)? (_("Linear gradient fill")) : (_("Linear gradient stroke"));
149 _rgradient[i].set_markup (_("R Gradient"));
150 sp_set_font_size_smaller (GTK_WIDGET(_rgradient[i].gobj()));
151 _rgradient[i].show_all();
152 __rgradient[i] = (i == SS_FILL)? (_("Radial gradient fill")) : (_("Radial gradient stroke"));
154 _many[i].set_markup (_("Different"));
155 sp_set_font_size_smaller (GTK_WIDGET(_many[i].gobj()));
156 _many[i].show_all();
157 __many[i] = (i == SS_FILL)? (_("Different fills")) : (_("Different strokes"));
159 _unset[i].set_markup (_("Unset"));
160 sp_set_font_size_smaller (GTK_WIDGET(_unset[i].gobj()));
161 _unset[i].show_all();
162 __unset[i] = (i == SS_FILL)? (_("Unset fill")) : (_("Unset stroke"));
164 _color_preview[i] = new Inkscape::UI::Widget::ColorPreview (0);
165 __color[i] = (i == SS_FILL)? (_("Flat color fill")) : (_("Flat color stroke"));
167 // TRANSLATOR COMMENT: A means "Averaged"
168 _averaged[i].set_markup (_("<b>a</b>"));
169 sp_set_font_size_smaller (GTK_WIDGET(_averaged[i].gobj()));
170 _averaged[i].show_all();
171 __averaged[i] = (i == SS_FILL)? (_("Fill is averaged over selected objects")) : (_("Stroke is averaged over selected objects"));
173 // TRANSLATOR COMMENT: M means "Multiple"
174 _multiple[i].set_markup (_("<b>m</b>"));
175 sp_set_font_size_smaller (GTK_WIDGET(_multiple[i].gobj()));
176 _multiple[i].show_all();
177 __multiple[i] = (i == SS_FILL)? (_("Multiple selected objects have the same fill")) : (_("Multiple selected objects have the same stroke"));
179 _popup_edit[i].add(*(new Gtk::Label((i == SS_FILL)? _("Edit fill...") : _("Edit stroke..."), 0.0, 0.5)));
180 _popup_edit[i].signal_activate().connect(sigc::mem_fun(*this,
181 (i == SS_FILL)? &SelectedStyle::on_fill_edit : &SelectedStyle::on_stroke_edit ));
183 _popup_lastused[i].add(*(new Gtk::Label(_("Last set color"), 0.0, 0.5)));
184 _popup_lastused[i].signal_activate().connect(sigc::mem_fun(*this,
185 (i == SS_FILL)? &SelectedStyle::on_fill_lastused : &SelectedStyle::on_stroke_lastused ));
187 _popup_lastselected[i].add(*(new Gtk::Label(_("Last selected color"), 0.0, 0.5)));
188 _popup_lastselected[i].signal_activate().connect(sigc::mem_fun(*this,
189 (i == SS_FILL)? &SelectedStyle::on_fill_lastselected : &SelectedStyle::on_stroke_lastselected ));
191 _popup_invert[i].add(*(new Gtk::Label(_("Invert"), 0.0, 0.5)));
192 _popup_invert[i].signal_activate().connect(sigc::mem_fun(*this,
193 (i == SS_FILL)? &SelectedStyle::on_fill_invert : &SelectedStyle::on_stroke_invert ));
195 _popup_white[i].add(*(new Gtk::Label(_("White"), 0.0, 0.5)));
196 _popup_white[i].signal_activate().connect(sigc::mem_fun(*this,
197 (i == SS_FILL)? &SelectedStyle::on_fill_white : &SelectedStyle::on_stroke_white ));
199 _popup_black[i].add(*(new Gtk::Label(_("Black"), 0.0, 0.5)));
200 _popup_black[i].signal_activate().connect(sigc::mem_fun(*this,
201 (i == SS_FILL)? &SelectedStyle::on_fill_black : &SelectedStyle::on_stroke_black ));
203 _popup_copy[i].add(*(new Gtk::Label(_("Copy color"), 0.0, 0.5)));
204 _popup_copy[i].signal_activate().connect(sigc::mem_fun(*this,
205 (i == SS_FILL)? &SelectedStyle::on_fill_copy : &SelectedStyle::on_stroke_copy ));
207 _popup_paste[i].add(*(new Gtk::Label(_("Paste color"), 0.0, 0.5)));
208 _popup_paste[i].signal_activate().connect(sigc::mem_fun(*this,
209 (i == SS_FILL)? &SelectedStyle::on_fill_paste : &SelectedStyle::on_stroke_paste ));
211 _popup_swap[i].add(*(new Gtk::Label(_("Swap fill and stroke"), 0.0, 0.5)));
212 _popup_swap[i].signal_activate().connect(sigc::mem_fun(*this,
213 &SelectedStyle::on_fillstroke_swap));
215 _popup_opaque[i].add(*(new Gtk::Label((i == SS_FILL)? _("Make fill opaque") : _("Make stroke opaque"), 0.0, 0.5)));
216 _popup_opaque[i].signal_activate().connect(sigc::mem_fun(*this,
217 (i == SS_FILL)? &SelectedStyle::on_fill_opaque : &SelectedStyle::on_stroke_opaque ));
219 //TRANSLATORS COMMENT: unset is a verb here
220 _popup_unset[i].add(*(new Gtk::Label((i == SS_FILL)? _("Unset fill") : _("Unset stroke"), 0.0, 0.5)));
221 _popup_unset[i].signal_activate().connect(sigc::mem_fun(*this,
222 (i == SS_FILL)? &SelectedStyle::on_fill_unset : &SelectedStyle::on_stroke_unset ));
224 _popup_remove[i].add(*(new Gtk::Label((i == SS_FILL)? _("Remove fill") : _("Remove stroke"), 0.0, 0.5)));
225 _popup_remove[i].signal_activate().connect(sigc::mem_fun(*this,
226 (i == SS_FILL)? &SelectedStyle::on_fill_remove : &SelectedStyle::on_stroke_remove ));
228 _popup[i].attach(_popup_edit[i], 0,1, 0,1);
229 _popup[i].attach(*(new Gtk::SeparatorMenuItem()), 0,1, 1,2);
230 _popup[i].attach(_popup_lastused[i], 0,1, 2,3);
231 _popup[i].attach(_popup_lastselected[i], 0,1, 3,4);
232 _popup[i].attach(*(new Gtk::SeparatorMenuItem()), 0,1, 4,5);
233 _popup[i].attach(_popup_invert[i], 0,1, 5,6);
234 _popup[i].attach(*(new Gtk::SeparatorMenuItem()), 0,1, 6,7);
235 _popup[i].attach(_popup_white[i], 0,1, 7,8);
236 _popup[i].attach(_popup_black[i], 0,1, 8,9);
237 _popup[i].attach(*(new Gtk::SeparatorMenuItem()), 0,1, 9,10);
238 _popup[i].attach(_popup_copy[i], 0,1, 10,11);
239 _popup_copy[i].set_sensitive(false);
240 _popup[i].attach(_popup_paste[i], 0,1, 11,12);
241 _popup[i].attach(_popup_swap[i], 0,1, 12,13);
242 _popup[i].attach(*(new Gtk::SeparatorMenuItem()), 0,1, 13,14);
243 _popup[i].attach(_popup_opaque[i], 0,1, 14,15);
244 _popup[i].attach(_popup_unset[i], 0,1, 15,16);
245 _popup[i].attach(_popup_remove[i], 0,1, 16,17);
246 _popup[i].show_all();
248 _mode[i] = SS_NA;
249 }
251 {
252 _popup_px.add(*(new Gtk::Label(_("px"), 0.0, 0.5)));
253 _popup_px.signal_activate().connect(sigc::mem_fun(*this, &SelectedStyle::on_popup_px));
254 _popup_sw.attach(_popup_px, 0,1, 0,1);
256 _popup_pt.add(*(new Gtk::Label(_("pt"), 0.0, 0.5)));
257 _popup_pt.signal_activate().connect(sigc::mem_fun(*this, &SelectedStyle::on_popup_pt));
258 _popup_sw.attach(_popup_pt, 0,1, 1,2);
260 _popup_mm.add(*(new Gtk::Label(_("mm"), 0.0, 0.5)));
261 _popup_mm.signal_activate().connect(sigc::mem_fun(*this, &SelectedStyle::on_popup_mm));
262 _popup_sw.attach(_popup_mm, 0,1, 2,3);
264 _popup_sw.attach(*(new Gtk::SeparatorMenuItem()), 0,1, 3,4);
266 for (guint i = 0; i < G_N_ELEMENTS(_sw_presets_str); ++i) {
267 Gtk::MenuItem *mi = Gtk::manage(new Gtk::MenuItem());
268 mi->add(*(new Gtk::Label(_sw_presets_str[i], 0.0, 0.5)));
269 mi->signal_activate().connect(sigc::bind<int>(sigc::mem_fun(*this, &SelectedStyle::on_popup_preset), i));
270 _popup_sw.attach(*mi, 0,1, 4+i, 5+i);
271 }
273 guint i = G_N_ELEMENTS(_sw_presets_str) + 5;
275 _popup_sw.attach(*(new Gtk::SeparatorMenuItem()), 0,1, i,i+1);
277 _popup_sw_remove.add(*(new Gtk::Label(_("Remove"), 0.0, 0.5)));
278 _popup_sw_remove.signal_activate().connect(sigc::mem_fun(*this, &SelectedStyle::on_stroke_remove));
279 _popup_sw.attach(_popup_sw_remove, 0,1, i+1,i+2);
281 _popup_sw.show_all();
282 }
284 _fill_place.signal_button_press_event().connect(sigc::mem_fun(*this, &SelectedStyle::on_fill_click));
285 _stroke_place.signal_button_press_event().connect(sigc::mem_fun(*this, &SelectedStyle::on_stroke_click));
286 _opacity_place.signal_button_press_event().connect(sigc::mem_fun(*this, &SelectedStyle::on_opacity_click));
287 _stroke_width_place.signal_button_press_event().connect(sigc::mem_fun(*this, &SelectedStyle::on_sw_click));
289 _opacity_sb.signal_populate_popup().connect(sigc::mem_fun(*this, &SelectedStyle::on_opacity_menu));
290 _opacity_sb.signal_value_changed().connect(sigc::mem_fun(*this, &SelectedStyle::on_opacity_changed));
292 _fill_place.add(_na[SS_FILL]);
293 _tooltips.set_tip(_fill_place, __na[SS_FILL]);
295 _stroke_place.add(_na[SS_STROKE]);
296 _tooltips.set_tip(_stroke_place, __na[SS_STROKE]);
298 _stroke.pack_start(_stroke_place);
299 _stroke_width_place.add(_stroke_width);
300 _stroke.pack_start(_stroke_width_place, Gtk::PACK_SHRINK);
302 _opacity_sb.set_adjustment(_opacity_adjustment);
303 sp_set_font_size_smaller (GTK_WIDGET(_opacity_sb.gobj()));
304 _opacity_sb.set_size_request (SELECTED_STYLE_SB_WIDTH, -1);
305 _opacity_sb.set_sensitive (false);
307 _table.attach(_fill_label, 0,1, 0,1, Gtk::SHRINK, Gtk::SHRINK);
308 _table.attach(_stroke_label, 0,1, 1,2, Gtk::SHRINK, Gtk::SHRINK);
310 _table.attach(_fill_flag_place, 1,2, 0,1, Gtk::SHRINK, Gtk::SHRINK);
311 _table.attach(_stroke_flag_place, 1,2, 1,2, Gtk::SHRINK, Gtk::SHRINK);
313 _table.attach(_fill_place, 2,3, 0,1);
314 _table.attach(_stroke, 2,3, 1,2);
316 _opacity_place.add(_opacity_label);
317 _table.attach(_opacity_place, 4,5, 0,2, Gtk::SHRINK, Gtk::SHRINK);
318 _table.attach(_opacity_sb, 5,6, 0,2, Gtk::SHRINK, Gtk::SHRINK);
320 pack_start(_table, true, true, 2);
322 set_size_request (SELECTED_STYLE_WIDTH, -1);
324 sp_set_font_size_smaller (GTK_WIDGET(_opacity_label.gobj()));
325 sp_set_font_size_smaller (GTK_WIDGET(_opacity_sb.gobj()));
326 sp_set_font_size_smaller (GTK_WIDGET(_fill_place.gobj()));
327 sp_set_font_size_smaller (GTK_WIDGET(_fill_flag_place.gobj()));
328 sp_set_font_size_smaller (GTK_WIDGET(_stroke_place.gobj()));
329 sp_set_font_size_smaller (GTK_WIDGET(_stroke_flag_place.gobj()));
330 sp_set_font_size_smaller (GTK_WIDGET(_stroke_width.gobj()));
331 sp_set_font_size_smaller (GTK_WIDGET(_fill_label.gobj()));
332 sp_set_font_size_smaller (GTK_WIDGET(_stroke_label.gobj()));
334 _drop[SS_FILL] = new DropTracker();
335 ((DropTracker*)_drop[SS_FILL])->parent = this;
336 ((DropTracker*)_drop[SS_FILL])->item = SS_FILL;
338 _drop[SS_STROKE] = new DropTracker();
339 ((DropTracker*)_drop[SS_STROKE])->parent = this;
340 ((DropTracker*)_drop[SS_STROKE])->item = SS_STROKE;
342 g_signal_connect(_stroke_place.gobj(),
343 "drag_data_received",
344 G_CALLBACK(dragDataReceived),
345 _drop[SS_STROKE]);
347 g_signal_connect(_fill_place.gobj(),
348 "drag_data_received",
349 G_CALLBACK(dragDataReceived),
350 _drop[SS_FILL]);
351 }
353 SelectedStyle::~SelectedStyle()
354 {
355 selection_changed_connection->disconnect();
356 delete selection_changed_connection;
357 selection_modified_connection->disconnect();
358 delete selection_modified_connection;
359 subselection_changed_connection->disconnect();
360 delete subselection_changed_connection;
362 for (int i = SS_FILL; i <= SS_STROKE; i++) {
363 delete _color_preview[i];
364 }
366 delete (DropTracker*)_drop[SS_FILL];
367 delete (DropTracker*)_drop[SS_STROKE];
368 }
370 void
371 SelectedStyle::setDesktop(SPDesktop *desktop)
372 {
373 _desktop = desktop;
374 gtk_object_set_data (GTK_OBJECT(_opacity_sb.gobj()), "dtw", _desktop->canvas);
376 Inkscape::Selection *selection = SP_DT_SELECTION (desktop);
378 selection_changed_connection = new sigc::connection (selection->connectChanged(
379 sigc::bind (
380 sigc::ptr_fun(&ss_selection_changed),
381 this )
382 ));
383 selection_modified_connection = new sigc::connection (selection->connectModified(
384 sigc::bind (
385 sigc::ptr_fun(&ss_selection_modified),
386 this )
387 ));
388 subselection_changed_connection = new sigc::connection (desktop->connectToolSubselectionChanged(
389 sigc::bind (
390 sigc::ptr_fun(&ss_subselection_changed),
391 this )
392 ));
394 //_sw_unit = (SPUnit *) SP_DT_NAMEDVIEW(desktop)->doc_units;
395 }
397 void SelectedStyle::dragDataReceived( GtkWidget *widget,
398 GdkDragContext *drag_context,
399 gint x, gint y,
400 GtkSelectionData *data,
401 guint info,
402 guint event_time,
403 gpointer user_data )
404 {
405 DropTracker* tracker = (DropTracker*)user_data;
407 switch ( (int)tracker->item ) {
408 case SS_FILL:
409 case SS_STROKE:
410 {
411 if ( data->length == 8 ) {
412 gchar c[64];
413 // Careful about endian issues.
414 guint16* dataVals = (guint16*)data->data;
415 sp_svg_write_color( c, 64,
416 SP_RGBA32_U_COMPOSE(
417 0x0ff & (dataVals[0] >> 8),
418 0x0ff & (dataVals[1] >> 8),
419 0x0ff & (dataVals[2] >> 8),
420 0xff // can't have transparency in the color itself
421 //0x0ff & (data->data[3] >> 8),
422 ));
423 SPCSSAttr *css = sp_repr_css_attr_new();
424 sp_repr_css_set_property( css, (tracker->item == SS_FILL) ? "fill":"stroke", c );
425 sp_desktop_set_style( tracker->parent->_desktop, css );
426 sp_repr_css_attr_unref( css );
427 sp_document_done( SP_DT_DOCUMENT(tracker->parent->_desktop) );
428 }
429 }
430 break;
431 }
432 }
434 void SelectedStyle::on_fill_remove() {
435 SPCSSAttr *css = sp_repr_css_attr_new ();
436 sp_repr_css_set_property (css, "fill", "none");
437 sp_desktop_set_style (_desktop, css, true, false); // do not write to current, to preserve current color
438 sp_repr_css_attr_unref (css);
439 sp_document_done (SP_DT_DOCUMENT(_desktop));
440 }
442 void SelectedStyle::on_stroke_remove() {
443 SPCSSAttr *css = sp_repr_css_attr_new ();
444 sp_repr_css_set_property (css, "stroke", "none");
445 sp_desktop_set_style (_desktop, css, true, false); // do not write to current, to preserve current color
446 sp_repr_css_attr_unref (css);
447 sp_document_done (SP_DT_DOCUMENT(_desktop));
448 }
450 void SelectedStyle::on_fill_unset() {
451 SPCSSAttr *css = sp_repr_css_attr_new ();
452 sp_repr_css_unset_property (css, "fill");
453 sp_desktop_set_style (_desktop, css, true, false); // do not write to current, to preserve current color
454 sp_repr_css_attr_unref (css);
455 sp_document_done (SP_DT_DOCUMENT(_desktop));
456 }
458 void SelectedStyle::on_stroke_unset() {
459 SPCSSAttr *css = sp_repr_css_attr_new ();
460 sp_repr_css_unset_property (css, "stroke");
461 sp_desktop_set_style (_desktop, css, true, false); // do not write to current, to preserve current color
462 sp_repr_css_attr_unref (css);
463 sp_document_done (SP_DT_DOCUMENT(_desktop));
464 }
466 void SelectedStyle::on_fill_opaque() {
467 SPCSSAttr *css = sp_repr_css_attr_new ();
468 sp_repr_css_set_property (css, "fill-opacity", "1");
469 sp_desktop_set_style (_desktop, css, true);
470 sp_repr_css_attr_unref (css);
471 sp_document_done (SP_DT_DOCUMENT(_desktop));
472 }
474 void SelectedStyle::on_stroke_opaque() {
475 SPCSSAttr *css = sp_repr_css_attr_new ();
476 sp_repr_css_set_property (css, "stroke-opacity", "1");
477 sp_desktop_set_style (_desktop, css, true);
478 sp_repr_css_attr_unref (css);
479 sp_document_done (SP_DT_DOCUMENT(_desktop));
480 }
482 void SelectedStyle::on_fill_lastused() {
483 SPCSSAttr *css = sp_repr_css_attr_new ();
484 guint32 color = sp_desktop_get_color(_desktop, true);
485 gchar c[64];
486 sp_svg_write_color (c, 64, color);
487 sp_repr_css_set_property (css, "fill", c);
488 sp_desktop_set_style (_desktop, css);
489 sp_repr_css_attr_unref (css);
490 sp_document_done (SP_DT_DOCUMENT(_desktop));
491 }
493 void SelectedStyle::on_stroke_lastused() {
494 SPCSSAttr *css = sp_repr_css_attr_new ();
495 guint32 color = sp_desktop_get_color(_desktop, false);
496 gchar c[64];
497 sp_svg_write_color (c, 64, color);
498 sp_repr_css_set_property (css, "stroke", c);
499 sp_desktop_set_style (_desktop, css);
500 sp_repr_css_attr_unref (css);
501 sp_document_done (SP_DT_DOCUMENT(_desktop));
502 }
504 void SelectedStyle::on_fill_lastselected() {
505 SPCSSAttr *css = sp_repr_css_attr_new ();
506 gchar c[64];
507 sp_svg_write_color (c, 64, _lastselected[SS_FILL]);
508 sp_repr_css_set_property (css, "fill", c);
509 sp_desktop_set_style (_desktop, css);
510 sp_repr_css_attr_unref (css);
511 sp_document_done (SP_DT_DOCUMENT(_desktop));
512 }
514 void SelectedStyle::on_stroke_lastselected() {
515 SPCSSAttr *css = sp_repr_css_attr_new ();
516 gchar c[64];
517 sp_svg_write_color (c, 64, _lastselected[SS_STROKE]);
518 sp_repr_css_set_property (css, "stroke", c);
519 sp_desktop_set_style (_desktop, css);
520 sp_repr_css_attr_unref (css);
521 sp_document_done (SP_DT_DOCUMENT(_desktop));
522 }
524 void SelectedStyle::on_fill_invert() {
525 SPCSSAttr *css = sp_repr_css_attr_new ();
526 guint32 color = _thisselected[SS_FILL];
527 gchar c[64];
528 if (_mode[SS_FILL] != SS_COLOR) return;
529 sp_svg_write_color (c, 64,
530 SP_RGBA32_U_COMPOSE(
531 (255 - SP_RGBA32_R_U(color)),
532 (255 - SP_RGBA32_G_U(color)),
533 (255 - SP_RGBA32_B_U(color)),
534 SP_RGBA32_A_U(color)
535 )
536 );
537 sp_repr_css_set_property (css, "fill", c);
538 sp_desktop_set_style (_desktop, css);
539 sp_repr_css_attr_unref (css);
540 sp_document_done (SP_DT_DOCUMENT(_desktop));
541 }
543 void SelectedStyle::on_stroke_invert() {
544 SPCSSAttr *css = sp_repr_css_attr_new ();
545 guint32 color = _thisselected[SS_STROKE];
546 gchar c[64];
547 if (_mode[SS_STROKE] != SS_COLOR) return;
548 sp_svg_write_color (c, 64,
549 SP_RGBA32_U_COMPOSE(
550 (255 - SP_RGBA32_R_U(color)),
551 (255 - SP_RGBA32_G_U(color)),
552 (255 - SP_RGBA32_B_U(color)),
553 SP_RGBA32_A_U(color)
554 )
555 );
556 sp_repr_css_set_property (css, "stroke", c);
557 sp_desktop_set_style (_desktop, css);
558 sp_repr_css_attr_unref (css);
559 sp_document_done (SP_DT_DOCUMENT(_desktop));
560 }
562 void SelectedStyle::on_fill_white() {
563 SPCSSAttr *css = sp_repr_css_attr_new ();
564 gchar c[64];
565 sp_svg_write_color (c, 64, 0xffffffff);
566 sp_repr_css_set_property (css, "fill", c);
567 sp_repr_css_set_property (css, "fill-opacity", "1");
568 sp_desktop_set_style (_desktop, css);
569 sp_repr_css_attr_unref (css);
570 sp_document_done (SP_DT_DOCUMENT(_desktop));
571 }
573 void SelectedStyle::on_stroke_white() {
574 SPCSSAttr *css = sp_repr_css_attr_new ();
575 gchar c[64];
576 sp_svg_write_color (c, 64, 0xffffffff);
577 sp_repr_css_set_property (css, "stroke", c);
578 sp_repr_css_set_property (css, "stroke-opacity", "1");
579 sp_desktop_set_style (_desktop, css);
580 sp_repr_css_attr_unref (css);
581 sp_document_done (SP_DT_DOCUMENT(_desktop));
582 }
584 void SelectedStyle::on_fill_black() {
585 SPCSSAttr *css = sp_repr_css_attr_new ();
586 gchar c[64];
587 sp_svg_write_color (c, 64, 0x000000ff);
588 sp_repr_css_set_property (css, "fill", c);
589 sp_repr_css_set_property (css, "fill-opacity", "1.0");
590 sp_desktop_set_style (_desktop, css);
591 sp_repr_css_attr_unref (css);
592 sp_document_done (SP_DT_DOCUMENT(_desktop));
593 }
595 void SelectedStyle::on_stroke_black() {
596 SPCSSAttr *css = sp_repr_css_attr_new ();
597 gchar c[64];
598 sp_svg_write_color (c, 64, 0x000000ff);
599 sp_repr_css_set_property (css, "stroke", c);
600 sp_repr_css_set_property (css, "stroke-opacity", "1.0");
601 sp_desktop_set_style (_desktop, css);
602 sp_repr_css_attr_unref (css);
603 sp_document_done (SP_DT_DOCUMENT(_desktop));
604 }
606 void SelectedStyle::on_fill_copy() {
607 if (_mode[SS_FILL] == SS_COLOR) {
608 gchar c[64];
609 sp_svg_write_color (c, 64, _thisselected[SS_FILL]);
610 Glib::ustring text;
611 text += c;
612 if (!text.empty()) {
613 Glib::RefPtr<Gtk::Clipboard> refClipboard = Gtk::Clipboard::get();
614 refClipboard->set_text(text);
615 }
616 }
617 }
619 void SelectedStyle::on_stroke_copy() {
620 if (_mode[SS_STROKE] == SS_COLOR) {
621 gchar c[64];
622 sp_svg_write_color (c, 64, _thisselected[SS_STROKE]);
623 Glib::ustring text;
624 text += c;
625 if (!text.empty()) {
626 Glib::RefPtr<Gtk::Clipboard> refClipboard = Gtk::Clipboard::get();
627 refClipboard->set_text(text);
628 }
629 }
630 }
632 void SelectedStyle::on_fill_paste() {
633 Glib::RefPtr<Gtk::Clipboard> refClipboard = Gtk::Clipboard::get();
634 Glib::ustring const text = refClipboard->wait_for_text();
636 if (!text.empty()) {
637 guint32 color = sp_svg_read_color(text.c_str(), 0x000000ff); // impossible value, as SVG color cannot have opacity
638 if (color == 0x000000ff) // failed to parse color string
639 return;
641 SPCSSAttr *css = sp_repr_css_attr_new ();
642 sp_repr_css_set_property (css, "fill", text.c_str());
643 sp_desktop_set_style (_desktop, css);
644 sp_repr_css_attr_unref (css);
645 sp_document_done (SP_DT_DOCUMENT(_desktop));
646 }
647 }
649 void SelectedStyle::on_stroke_paste() {
650 Glib::RefPtr<Gtk::Clipboard> refClipboard = Gtk::Clipboard::get();
651 Glib::ustring const text = refClipboard->wait_for_text();
653 if (!text.empty()) {
654 guint32 color = sp_svg_read_color(text.c_str(), 0x000000ff); // impossible value, as SVG color cannot have opacity
655 if (color == 0x000000ff) // failed to parse color string
656 return;
658 SPCSSAttr *css = sp_repr_css_attr_new ();
659 sp_repr_css_set_property (css, "stroke", text.c_str());
660 sp_desktop_set_style (_desktop, css);
661 sp_repr_css_attr_unref (css);
662 sp_document_done (SP_DT_DOCUMENT(_desktop));
663 }
664 }
666 void SelectedStyle::on_fillstroke_swap() {
667 SPCSSAttr *css = sp_repr_css_attr_new ();
669 g_message("on_fillstroke_swap()");
671 switch (_mode[SS_FILL]) {
672 case SS_NA:
673 case SS_MANY:
674 break;
675 case SS_NONE:
676 sp_repr_css_set_property (css, "stroke", "none");
677 break;
678 case SS_UNSET:
679 sp_repr_css_unset_property (css, "stroke");
680 break;
681 case SS_COLOR:
682 gchar c[64];
683 sp_svg_write_color (c, 64, _thisselected[SS_FILL]);
684 sp_repr_css_set_property (css, "stroke", c);
685 break;
686 case SS_LGRADIENT:
687 case SS_RGRADIENT:
688 case SS_PATTERN:
689 sp_repr_css_set_property (css, "stroke", _paintserver_id[SS_FILL].c_str());
690 break;
691 }
693 switch (_mode[SS_STROKE]) {
694 case SS_NA:
695 case SS_MANY:
696 break;
697 case SS_NONE:
698 sp_repr_css_set_property (css, "fill", "none");
699 break;
700 case SS_UNSET:
701 sp_repr_css_unset_property (css, "fill");
702 break;
703 case SS_COLOR:
704 gchar c[64];
705 sp_svg_write_color (c, 64, _thisselected[SS_STROKE]);
706 sp_repr_css_set_property (css, "fill", c);
707 break;
708 case SS_LGRADIENT:
709 case SS_RGRADIENT:
710 case SS_PATTERN:
711 sp_repr_css_set_property (css, "fill", _paintserver_id[SS_STROKE].c_str());
712 break;
713 }
715 sp_desktop_set_style (_desktop, css);
716 sp_repr_css_attr_unref (css);
717 sp_document_done (SP_DT_DOCUMENT(_desktop));
718 }
720 void SelectedStyle::on_fill_edit() {
721 sp_object_properties_fill();
722 }
724 void SelectedStyle::on_stroke_edit() {
725 sp_object_properties_stroke();
726 }
728 bool
729 SelectedStyle::on_fill_click(GdkEventButton *event)
730 {
731 if (event->button == 1) { // click, open fill&stroke
732 sp_object_properties_fill();
733 } else if (event->button == 3) { // right-click, popup menu
734 _popup[SS_FILL].popup(event->button, event->time);
735 } else if (event->button == 2) { // middle click, toggle none/lastcolor
736 if (_mode[SS_FILL] == SS_NONE) {
737 on_fill_lastused();
738 } else {
739 on_fill_remove();
740 }
741 }
742 return true;
743 }
745 bool
746 SelectedStyle::on_stroke_click(GdkEventButton *event)
747 {
748 if (event->button == 1) { // click, open fill&stroke
749 sp_object_properties_stroke();
750 } else if (event->button == 3) { // right-click, popup menu
751 _popup[SS_STROKE].popup(event->button, event->time);
752 } else if (event->button == 2) { // middle click, toggle none/lastcolor
753 if (_mode[SS_STROKE] == SS_NONE) {
754 on_stroke_lastused();
755 } else {
756 on_stroke_remove();
757 }
758 }
759 return true;
760 }
762 bool
763 SelectedStyle::on_sw_click(GdkEventButton *event)
764 {
765 if (event->button == 1) { // click, open fill&stroke
766 sp_object_properties_stroke_style ();
767 } else if (event->button == 3) { // right-click, popup menu
768 _popup_sw.popup(event->button, event->time);
769 } else if (event->button == 2) { // middle click, toggle none/lastwidth?
770 //
771 }
772 return true;
773 }
775 bool
776 SelectedStyle::on_opacity_click(GdkEventButton *event)
777 {
778 if (event->button == 2) { // middle click
779 const char* opacity = _opacity_sb.get_value() < 0.5? "0.5" : (_opacity_sb.get_value() == 1? "0" : "1");
780 SPCSSAttr *css = sp_repr_css_attr_new ();
781 sp_repr_css_set_property (css, "opacity", opacity);
782 sp_desktop_set_style (_desktop, css);
783 sp_repr_css_attr_unref (css);
784 sp_document_done (SP_DT_DOCUMENT (_desktop));
785 return true;
786 }
788 return false;
789 }
791 void SelectedStyle::on_popup_px() {
792 _sw_unit = (SPUnit *) &(sp_unit_get_by_id(SP_UNIT_PX));
793 update();
794 }
795 void SelectedStyle::on_popup_pt() {
796 _sw_unit = (SPUnit *) &(sp_unit_get_by_id(SP_UNIT_PT));
797 update();
798 }
799 void SelectedStyle::on_popup_mm() {
800 _sw_unit = (SPUnit *) &(sp_unit_get_by_id(SP_UNIT_MM));
801 update();
802 }
804 void SelectedStyle::on_popup_preset(int i) {
805 SPCSSAttr *css = sp_repr_css_attr_new ();
806 gdouble w;
807 if (_sw_unit) {
808 w = sp_units_get_pixels (_sw_presets[i], *_sw_unit);
809 } else {
810 w = _sw_presets[i];
811 }
812 Inkscape::CSSOStringStream os;
813 os << w;
814 sp_repr_css_set_property (css, "stroke-width", os.str().c_str());
815 sp_desktop_set_style (_desktop, css, true);
816 sp_repr_css_attr_unref (css);
817 sp_document_done (SP_DT_DOCUMENT(_desktop));
818 }
820 void
821 SelectedStyle::update()
822 {
823 if (_desktop == NULL)
824 return;
826 // create temporary style
827 SPStyle *query = sp_style_new ();
829 for (int i = SS_FILL; i <= SS_STROKE; i++) {
830 Gtk::EventBox *place = (i == SS_FILL)? &_fill_place : &_stroke_place;
831 Gtk::EventBox *flag_place = (i == SS_FILL)? &_fill_flag_place : &_stroke_flag_place;
833 place->remove();
834 flag_place->remove();
836 _tooltips.unset_tip(*place);
837 _tooltips.unset_tip(*flag_place);
839 _mode[i] = SS_NA;
840 _paintserver_id[i].clear();
842 _popup_copy[i].set_sensitive(false);
844 // query style from desktop. This returns a result flag and fills query with the style of subselection, if any, or selection
845 int result = sp_desktop_query_style (_desktop, query,
846 (i == SS_FILL)? QUERY_STYLE_PROPERTY_FILL : QUERY_STYLE_PROPERTY_STROKE);
847 switch (result) {
848 case QUERY_STYLE_NOTHING:
849 place->add(_na[i]);
850 _tooltips.set_tip(*place, __na[i]);
851 _mode[i] = SS_NA;
852 if ( _dropEnabled[i] ) {
853 gtk_drag_dest_unset( GTK_WIDGET((i==SS_FILL) ? _fill_place.gobj():_stroke_place.gobj()) );
854 _dropEnabled[i] = false;
855 }
856 break;
857 case QUERY_STYLE_SINGLE:
858 case QUERY_STYLE_MULTIPLE_AVERAGED:
859 case QUERY_STYLE_MULTIPLE_SAME:
860 if ( !_dropEnabled[i] ) {
861 gtk_drag_dest_set( GTK_WIDGET( (i==SS_FILL) ? _fill_place.gobj():_stroke_place.gobj()),
862 GTK_DEST_DEFAULT_ALL,
863 ui_drop_target_entries,
864 nui_drop_target_entries,
865 GdkDragAction(GDK_ACTION_COPY | GDK_ACTION_MOVE) );
866 _dropEnabled[i] = true;
867 }
868 SPIPaint *paint;
869 if (i == SS_FILL) {
870 paint = &(query->fill);
871 } else {
872 paint = &(query->stroke);
873 }
874 if (paint->set && paint->type == SP_PAINT_TYPE_COLOR) {
875 guint32 color = sp_color_get_rgba32_falpha (&(paint->value.color),
876 SP_SCALE24_TO_FLOAT ((i == SS_FILL)? query->fill_opacity.value : query->stroke_opacity.value));
877 _lastselected[i] = _thisselected[i];
878 _thisselected[i] = color | 0xff; // only color, opacity === 1
879 ((Inkscape::UI::Widget::ColorPreview*)_color_preview[i])->setRgba32 (color);
880 _color_preview[i]->show_all();
881 place->add(*_color_preview[i]);
882 gchar c_string[64];
883 g_snprintf (c_string, 64, "%06x/%.3g", color >> 8, SP_RGBA32_A_F(color));
884 _tooltips.set_tip(*place, __color[i] + ": " + c_string);
885 _mode[i] = SS_COLOR;
886 _popup_copy[i].set_sensitive(true);
888 } else if (paint->set && paint->type == SP_PAINT_TYPE_PAINTSERVER) {
889 SPPaintServer *server = (i == SS_FILL)? SP_STYLE_FILL_SERVER (query) : SP_STYLE_STROKE_SERVER (query);
891 Inkscape::XML::Node *srepr = SP_OBJECT_REPR(server);
892 _paintserver_id[i] += "url(#";
893 _paintserver_id[i] += srepr->attribute("id");
894 _paintserver_id[i] += ")";
896 if (SP_IS_LINEARGRADIENT (server)) {
897 place->add(_lgradient[i]);
898 _tooltips.set_tip(*place, __lgradient[i]);
899 _mode[i] = SS_LGRADIENT;
900 } else if (SP_IS_RADIALGRADIENT (server)) {
901 place->add(_rgradient[i]);
902 _tooltips.set_tip(*place, __rgradient[i]);
903 _mode[i] = SS_RGRADIENT;
904 } else if (SP_IS_PATTERN (server)) {
905 place->add(_pattern[i]);
906 _tooltips.set_tip(*place, __pattern[i]);
907 _mode[i] = SS_PATTERN;
908 }
910 } else if (paint->set && paint->type == SP_PAINT_TYPE_NONE) {
911 place->add(_none[i]);
912 _tooltips.set_tip(*place, __none[i]);
913 _mode[i] = SS_NONE;
914 } else if (!paint->set) {
915 place->add(_unset[i]);
916 _tooltips.set_tip(*place, __unset[i]);
917 _mode[i] = SS_UNSET;
918 }
919 if (result == QUERY_STYLE_MULTIPLE_AVERAGED) {
920 flag_place->add(_averaged[i]);
921 _tooltips.set_tip(*flag_place, __averaged[i]);
922 } else if (result == QUERY_STYLE_MULTIPLE_SAME) {
923 flag_place->add(_multiple[i]);
924 _tooltips.set_tip(*flag_place, __multiple[i]);
925 }
926 break;
927 case QUERY_STYLE_MULTIPLE_DIFFERENT:
928 place->add(_many[i]);
929 _tooltips.set_tip(*place, __many[i]);
930 _mode[i] = SS_MANY;
931 break;
932 default:
933 break;
934 }
935 }
937 // Now query opacity
938 _tooltips.unset_tip(_opacity_place);
940 int result = sp_desktop_query_style (_desktop, query, QUERY_STYLE_PROPERTY_MASTEROPACITY);
942 switch (result) {
943 case QUERY_STYLE_NOTHING:
944 _tooltips.set_tip(_opacity_place, _("Nothing selected"));
945 _opacity_sb.set_sensitive(false);
946 break;
947 case QUERY_STYLE_SINGLE:
948 case QUERY_STYLE_MULTIPLE_AVERAGED:
949 case QUERY_STYLE_MULTIPLE_SAME:
950 _tooltips.set_tip(_opacity_place, _("Master opacity"));
951 _opacity_blocked = true;
952 _opacity_sb.set_sensitive(true);
953 _opacity_adjustment.set_value(SP_SCALE24_TO_FLOAT(query->opacity.value));
954 _opacity_blocked = false;
955 break;
956 }
958 // Now query stroke_width
959 int result_sw = sp_desktop_query_style (_desktop, query, QUERY_STYLE_PROPERTY_STROKEWIDTH);
960 switch (result_sw) {
961 case QUERY_STYLE_NOTHING:
962 _stroke_width.set_markup("");
963 break;
964 case QUERY_STYLE_SINGLE:
965 case QUERY_STYLE_MULTIPLE_AVERAGED:
966 case QUERY_STYLE_MULTIPLE_SAME:
967 {
968 double w;
969 if (_sw_unit) {
970 w = sp_pixels_get_units(query->stroke_width.computed, *_sw_unit);
971 } else {
972 w = query->stroke_width.computed;
973 }
974 {
975 gchar *str = g_strdup_printf(" %.3g", w);
976 _stroke_width.set_markup(str);
977 g_free (str);
978 }
979 {
980 gchar *str = g_strdup_printf(_("Stroke width: %.5g%s%s"),
981 w,
982 _sw_unit? sp_unit_get_abbreviation(_sw_unit) : "px",
983 (result_sw == QUERY_STYLE_MULTIPLE_AVERAGED)?
984 _(" (averaged)") : "");
985 _tooltips.set_tip(_stroke_width_place, str);
986 g_free (str);
987 }
988 break;
989 }
990 default:
991 break;
992 }
994 g_free (query);
995 }
997 void SelectedStyle::opacity_0(void) {_opacity_sb.set_value(0);}
998 void SelectedStyle::opacity_025(void) {_opacity_sb.set_value(0.25);}
999 void SelectedStyle::opacity_05(void) {_opacity_sb.set_value(0.5);}
1000 void SelectedStyle::opacity_075(void) {_opacity_sb.set_value(0.75);}
1001 void SelectedStyle::opacity_1(void) {_opacity_sb.set_value(1.0);}
1003 void SelectedStyle::on_opacity_menu (Gtk::Menu *menu) {
1005 Glib::ListHandle<Gtk::Widget *> children = menu->get_children();
1006 for (Glib::ListHandle<Gtk::Widget *>::iterator iter = children.begin(); iter != children.end(); iter++) {
1007 menu->remove(*(*iter));
1008 }
1010 {
1011 Gtk::MenuItem *item = new Gtk::MenuItem;
1012 item->add(*(new Gtk::Label(_("0 (transparent)"), 0, 0)));
1013 item->signal_activate().connect(sigc::mem_fun(*this, &SelectedStyle::opacity_0 ));
1014 menu->add(*item);
1015 }
1016 {
1017 Gtk::MenuItem *item = new Gtk::MenuItem;
1018 item->add(*(new Gtk::Label("0.25", 0, 0)));
1019 item->signal_activate().connect(sigc::mem_fun(*this, &SelectedStyle::opacity_025 ));
1020 menu->add(*item);
1021 }
1022 {
1023 Gtk::MenuItem *item = new Gtk::MenuItem;
1024 item->add(*(new Gtk::Label("0.5", 0, 0)));
1025 item->signal_activate().connect(sigc::mem_fun(*this, &SelectedStyle::opacity_05 ));
1026 menu->add(*item);
1027 }
1028 {
1029 Gtk::MenuItem *item = new Gtk::MenuItem;
1030 item->add(*(new Gtk::Label("0.75", 0, 0)));
1031 item->signal_activate().connect(sigc::mem_fun(*this, &SelectedStyle::opacity_075 ));
1032 menu->add(*item);
1033 }
1034 {
1035 Gtk::MenuItem *item = new Gtk::MenuItem;
1036 item->add(*(new Gtk::Label(_("1.0 (opaque)"), 0, 0)));
1037 item->signal_activate().connect(sigc::mem_fun(*this, &SelectedStyle::opacity_1 ));
1038 menu->add(*item);
1039 }
1041 menu->show_all();
1042 }
1044 void SelectedStyle::on_opacity_changed () {
1045 if (_opacity_blocked)
1046 return;
1047 _opacity_blocked = true;
1048 SPCSSAttr *css = sp_repr_css_attr_new ();
1049 Inkscape::CSSOStringStream os;
1050 os << CLAMP (_opacity_adjustment.get_value(), 0.0, 1.0);
1051 sp_repr_css_set_property (css, "opacity", os.str().c_str());
1052 sp_desktop_set_style (_desktop, css);
1053 sp_repr_css_attr_unref (css);
1054 sp_document_maybe_done (SP_DT_DOCUMENT (_desktop), "fillstroke:opacity");
1055 spinbutton_defocus(GTK_OBJECT(_opacity_sb.gobj()));
1056 _opacity_blocked = false;
1057 }
1059 } // namespace Widget
1060 } // namespace UI
1061 } // namespace Inkscape
1063 /*
1064 Local Variables:
1065 mode:c++
1066 c-file-style:"stroustrup"
1067 c-file-offsets:((innamespace . 0)(inline-open . 0))
1068 indent-tabs-mode:nil
1069 fill-column:99
1070 End:
1071 */
1072 // vim: filetype=c++:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 :