Code

8710162fd7f70d5249816bcd959efececf0464b7
[inkscape.git] / src / ui / widget / selected-style.cpp
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 "xml/repr.h"
33 #include "document.h"
34 #include "widgets/widget-sizes.h"
35 #include "widgets/spinbutton-events.h"
36 #include "widgets/gradient-image.h"
37 #include "sp-gradient.h"
38 #include "svg/svg-color.h"
39 #include "svg/css-ostringstream.h"
40 #include "helper/units.h"
41 #include "event-context.h"
42 #include "message-context.h"
43 #include "verbs.h"
44 #include "color.h"
45 #include <display/sp-canvas.h>
47 static gdouble const _sw_presets[]     = { 32 ,  16 ,  10 ,  8 ,  6 ,  4 ,  3 ,  2 ,  1.5 ,  1 ,  0.75 ,  0.5 ,  0.25 ,  0.1 };
48 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"};
50 static void 
51 ss_selection_changed (Inkscape::Selection *, gpointer data)
52 {
53     Inkscape::UI::Widget::SelectedStyle *ss = (Inkscape::UI::Widget::SelectedStyle *) data;
54     ss->update();
55 }
57 static void
58 ss_selection_modified (Inkscape::Selection *selection, guint flags, gpointer data)
59 {
60     ss_selection_changed (selection, data);
61 }
63 static void
64 ss_subselection_changed (gpointer dragger, gpointer data)
65 {
66     ss_selection_changed (NULL, data);
67 }
69 namespace Inkscape {
70 namespace UI {
71 namespace Widget {
74 typedef struct {
75     SelectedStyle* parent;
76     int item;
77 } DropTracker;
79 /* Drag and Drop */
80 typedef enum {
81     APP_X_COLOR
82 } ui_drop_target_info;
84 static GtkTargetEntry ui_drop_target_entries [] = {
85     {"application/x-color", 0, APP_X_COLOR}
86 };
88 #define ENTRIES_SIZE(n) sizeof(n)/sizeof(n[0])
89 static guint nui_drop_target_entries = ENTRIES_SIZE(ui_drop_target_entries);
92 SelectedStyle::SelectedStyle(bool layout)
93     : _desktop (NULL),
95       _table(2, 6),
96       _fill_label (_("Fill:")),
97       _stroke_label (_("Stroke:")),
98       _opacity_label (_("O:")),
100       _fill_place (SS_FILL),
101       _stroke_place (SS_STROKE),
103       _fill_flag_place (),
104       _stroke_flag_place (),
106       _opacity_place (),
107       _opacity_adjustment (100, 0.0, 100, 1.0, 10.0),
108       _opacity_sb (0.02, 0),
110       _stroke (),
111       _stroke_width (""),
113       _opacity_blocked (false),
115       _popup_px(_sw_group),
116       _popup_pt(_sw_group),
117       _popup_mm(_sw_group),
119       _sw_unit(NULL),
121       _tooltips ()
124     _drop[0] = _drop[1] = 0;
125     _dropEnabled[0] = _dropEnabled[1] = false;
127     _fill_label.set_alignment(0.0, 0.5);
128     _fill_label.set_padding(0, 0);
129     _stroke_label.set_alignment(0.0, 0.5);
130     _stroke_label.set_padding(0, 0);
131     _opacity_label.set_alignment(0.0, 0.5);
132     _opacity_label.set_padding(0, 0);
134     _table.set_col_spacings (2);
135     _table.set_row_spacings (0);
137     for (int i = SS_FILL; i <= SS_STROKE; i++) {
139         _na[i].set_markup (_("N/A"));
140         sp_set_font_size_smaller (GTK_WIDGET(_na[i].gobj()));
141         _na[i].show_all();
142         __na[i] = (_("Nothing selected"));
144         _none[i].set_markup (_("<i>None</i>"));
145         sp_set_font_size_smaller (GTK_WIDGET(_none[i].gobj()));
146         _none[i].show_all();
147         __none[i] = (i == SS_FILL)? (_("No fill")) : (_("No stroke"));
149         _pattern[i].set_markup (_("Pattern"));
150         sp_set_font_size_smaller (GTK_WIDGET(_pattern[i].gobj()));
151         _pattern[i].show_all();
152         __pattern[i] = (i == SS_FILL)? (_("Pattern fill")) : (_("Pattern stroke"));
154         _lgradient[i].set_markup (_("<b>L</b>"));
155         sp_set_font_size_smaller (GTK_WIDGET(_lgradient[i].gobj()));
156         _lgradient[i].show_all();
157         __lgradient[i] = (i == SS_FILL)? (_("Linear gradient fill")) : (_("Linear gradient stroke"));
159         _gradient_preview_l[i] =  GTK_WIDGET(sp_gradient_image_new (NULL));
160         _gradient_box_l[i].pack_start(_lgradient[i]);
161         _gradient_box_l[i].pack_start(*(Glib::wrap(_gradient_preview_l[i])));
162         _gradient_box_l[i].show_all();
164         _rgradient[i].set_markup (_("<b>R</b>"));
165         sp_set_font_size_smaller (GTK_WIDGET(_rgradient[i].gobj()));
166         _rgradient[i].show_all();
167         __rgradient[i] = (i == SS_FILL)? (_("Radial gradient fill")) : (_("Radial gradient stroke"));
169         _gradient_preview_r[i] = GTK_WIDGET(sp_gradient_image_new (NULL));
170         _gradient_box_r[i].pack_start(_rgradient[i]);
171         _gradient_box_r[i].pack_start(*(Glib::wrap(_gradient_preview_r[i])));
172         _gradient_box_r[i].show_all();
174         _many[i].set_markup (_("Different"));
175         sp_set_font_size_smaller (GTK_WIDGET(_many[i].gobj()));
176         _many[i].show_all();
177         __many[i] = (i == SS_FILL)? (_("Different fills")) : (_("Different strokes"));
179         _unset[i].set_markup (_("<b>Unset</b>"));
180         sp_set_font_size_smaller (GTK_WIDGET(_unset[i].gobj()));
181         _unset[i].show_all();
182         __unset[i] = (i == SS_FILL)? (_("Unset fill")) : (_("Unset stroke"));
184         _color_preview[i] = new Inkscape::UI::Widget::ColorPreview (0);
185         __color[i] = (i == SS_FILL)? (_("Flat color fill")) : (_("Flat color stroke"));
187         // TRANSLATOR COMMENT: A means "Averaged"
188         _averaged[i].set_markup (_("<b>a</b>"));
189         sp_set_font_size_smaller (GTK_WIDGET(_averaged[i].gobj()));
190         _averaged[i].show_all();
191         __averaged[i] = (i == SS_FILL)? (_("Fill is averaged over selected objects")) : (_("Stroke is averaged over selected objects"));
193         // TRANSLATOR COMMENT: M means "Multiple"
194         _multiple[i].set_markup (_("<b>m</b>"));
195         sp_set_font_size_smaller (GTK_WIDGET(_multiple[i].gobj()));
196         _multiple[i].show_all();
197         __multiple[i] = (i == SS_FILL)? (_("Multiple selected objects have the same fill")) : (_("Multiple selected objects have the same stroke"));
199         _popup_edit[i].add(*(new Gtk::Label((i == SS_FILL)? _("Edit fill...") : _("Edit stroke..."), 0.0, 0.5)));
200         _popup_edit[i].signal_activate().connect(sigc::mem_fun(*this, 
201                                (i == SS_FILL)? &SelectedStyle::on_fill_edit : &SelectedStyle::on_stroke_edit ));
203         _popup_lastused[i].add(*(new Gtk::Label(_("Last set color"), 0.0, 0.5)));
204         _popup_lastused[i].signal_activate().connect(sigc::mem_fun(*this, 
205                                (i == SS_FILL)? &SelectedStyle::on_fill_lastused : &SelectedStyle::on_stroke_lastused ));
207         _popup_lastselected[i].add(*(new Gtk::Label(_("Last selected color"), 0.0, 0.5)));
208         _popup_lastselected[i].signal_activate().connect(sigc::mem_fun(*this, 
209                                (i == SS_FILL)? &SelectedStyle::on_fill_lastselected : &SelectedStyle::on_stroke_lastselected ));
211         _popup_invert[i].add(*(new Gtk::Label(_("Invert"), 0.0, 0.5)));
212         _popup_invert[i].signal_activate().connect(sigc::mem_fun(*this, 
213                                (i == SS_FILL)? &SelectedStyle::on_fill_invert : &SelectedStyle::on_stroke_invert ));
215         _popup_white[i].add(*(new Gtk::Label(_("White"), 0.0, 0.5)));
216         _popup_white[i].signal_activate().connect(sigc::mem_fun(*this, 
217                                (i == SS_FILL)? &SelectedStyle::on_fill_white : &SelectedStyle::on_stroke_white ));
219         _popup_black[i].add(*(new Gtk::Label(_("Black"), 0.0, 0.5)));
220         _popup_black[i].signal_activate().connect(sigc::mem_fun(*this, 
221                                (i == SS_FILL)? &SelectedStyle::on_fill_black : &SelectedStyle::on_stroke_black ));
223         _popup_copy[i].add(*(new Gtk::Label(_("Copy color"), 0.0, 0.5)));
224         _popup_copy[i].signal_activate().connect(sigc::mem_fun(*this, 
225                                (i == SS_FILL)? &SelectedStyle::on_fill_copy : &SelectedStyle::on_stroke_copy ));
227         _popup_paste[i].add(*(new Gtk::Label(_("Paste color"), 0.0, 0.5)));
228         _popup_paste[i].signal_activate().connect(sigc::mem_fun(*this, 
229                                (i == SS_FILL)? &SelectedStyle::on_fill_paste : &SelectedStyle::on_stroke_paste ));
231         _popup_swap[i].add(*(new Gtk::Label(_("Swap fill and stroke"), 0.0, 0.5)));
232         _popup_swap[i].signal_activate().connect(sigc::mem_fun(*this, 
233                                &SelectedStyle::on_fillstroke_swap));
235         _popup_opaque[i].add(*(new Gtk::Label((i == SS_FILL)? _("Make fill opaque") : _("Make stroke opaque"), 0.0, 0.5)));
236         _popup_opaque[i].signal_activate().connect(sigc::mem_fun(*this, 
237                                (i == SS_FILL)? &SelectedStyle::on_fill_opaque : &SelectedStyle::on_stroke_opaque ));
239         //TRANSLATORS COMMENT: unset is a verb here
240         _popup_unset[i].add(*(new Gtk::Label((i == SS_FILL)? _("Unset fill") : _("Unset stroke"), 0.0, 0.5)));
241         _popup_unset[i].signal_activate().connect(sigc::mem_fun(*this, 
242                                (i == SS_FILL)? &SelectedStyle::on_fill_unset : &SelectedStyle::on_stroke_unset ));
244         _popup_remove[i].add(*(new Gtk::Label((i == SS_FILL)? _("Remove fill") : _("Remove stroke"), 0.0, 0.5)));
245         _popup_remove[i].signal_activate().connect(sigc::mem_fun(*this, 
246                                (i == SS_FILL)? &SelectedStyle::on_fill_remove : &SelectedStyle::on_stroke_remove ));
248         _popup[i].attach(_popup_edit[i], 0,1, 0,1);
249           _popup[i].attach(*(new Gtk::SeparatorMenuItem()), 0,1, 1,2);
250         _popup[i].attach(_popup_lastused[i], 0,1, 2,3);
251         _popup[i].attach(_popup_lastselected[i], 0,1, 3,4);
252           _popup[i].attach(*(new Gtk::SeparatorMenuItem()), 0,1, 4,5);
253         _popup[i].attach(_popup_invert[i], 0,1, 5,6);
254           _popup[i].attach(*(new Gtk::SeparatorMenuItem()), 0,1, 6,7);
255         _popup[i].attach(_popup_white[i], 0,1, 7,8);
256         _popup[i].attach(_popup_black[i], 0,1, 8,9);
257           _popup[i].attach(*(new Gtk::SeparatorMenuItem()), 0,1, 9,10);
258         _popup[i].attach(_popup_copy[i], 0,1, 10,11);
259         _popup_copy[i].set_sensitive(false);
260         _popup[i].attach(_popup_paste[i], 0,1, 11,12);
261         _popup[i].attach(_popup_swap[i], 0,1, 12,13);
262           _popup[i].attach(*(new Gtk::SeparatorMenuItem()), 0,1, 13,14); 
263         _popup[i].attach(_popup_opaque[i], 0,1, 14,15);
264         _popup[i].attach(_popup_unset[i], 0,1, 15,16);
265         _popup[i].attach(_popup_remove[i], 0,1, 16,17);
266         _popup[i].show_all();
268         _mode[i] = SS_NA;
269     }
271     {
272         _popup_px.add(*(new Gtk::Label(_("px"), 0.0, 0.5)));
273         _popup_px.signal_activate().connect(sigc::mem_fun(*this, &SelectedStyle::on_popup_px));
274         _popup_sw.attach(_popup_px, 0,1, 0,1);
276         _popup_pt.add(*(new Gtk::Label(_("pt"), 0.0, 0.5)));
277         _popup_pt.signal_activate().connect(sigc::mem_fun(*this, &SelectedStyle::on_popup_pt));
278         _popup_sw.attach(_popup_pt, 0,1, 1,2);
280         _popup_mm.add(*(new Gtk::Label(_("mm"), 0.0, 0.5)));
281         _popup_mm.signal_activate().connect(sigc::mem_fun(*this, &SelectedStyle::on_popup_mm));
282         _popup_sw.attach(_popup_mm, 0,1, 2,3);
284         _popup_sw.attach(*(new Gtk::SeparatorMenuItem()), 0,1, 3,4);
286         for (guint i = 0; i < G_N_ELEMENTS(_sw_presets_str); ++i) {
287             Gtk::MenuItem *mi = Gtk::manage(new Gtk::MenuItem());
288             mi->add(*(new Gtk::Label(_sw_presets_str[i], 0.0, 0.5)));
289             mi->signal_activate().connect(sigc::bind<int>(sigc::mem_fun(*this, &SelectedStyle::on_popup_preset), i));
290             _popup_sw.attach(*mi, 0,1, 4+i, 5+i);
291         }
293         guint i = G_N_ELEMENTS(_sw_presets_str) + 5;
295         _popup_sw.attach(*(new Gtk::SeparatorMenuItem()), 0,1, i,i+1);
297         _popup_sw_remove.add(*(new Gtk::Label(_("Remove"), 0.0, 0.5)));
298         _popup_sw_remove.signal_activate().connect(sigc::mem_fun(*this, &SelectedStyle::on_stroke_remove));
299         _popup_sw.attach(_popup_sw_remove, 0,1, i+1,i+2);
301         _popup_sw.show_all();
302     }
304     _fill_place.signal_button_release_event().connect(sigc::mem_fun(*this, &SelectedStyle::on_fill_click));
305     _stroke_place.signal_button_release_event().connect(sigc::mem_fun(*this, &SelectedStyle::on_stroke_click));
306     _opacity_place.signal_button_press_event().connect(sigc::mem_fun(*this, &SelectedStyle::on_opacity_click));
307     _stroke_width_place.signal_button_press_event().connect(sigc::mem_fun(*this, &SelectedStyle::on_sw_click));
309     _opacity_sb.signal_populate_popup().connect(sigc::mem_fun(*this, &SelectedStyle::on_opacity_menu));
310     _opacity_sb.signal_value_changed().connect(sigc::mem_fun(*this, &SelectedStyle::on_opacity_changed));
312     _fill_place.add(_na[SS_FILL]);
313     _tooltips.set_tip(_fill_place, __na[SS_FILL]);
315     _stroke_place.add(_na[SS_STROKE]);
316     _tooltips.set_tip(_stroke_place, __na[SS_STROKE]);
318     _stroke.pack_start(_stroke_place);
319     _stroke_width_place.add(_stroke_width);
320     _stroke.pack_start(_stroke_width_place, Gtk::PACK_SHRINK);
322     _opacity_sb.set_adjustment(_opacity_adjustment);
323     sp_set_font_size_smaller (GTK_WIDGET(_opacity_sb.gobj()));
324     _opacity_sb.set_size_request (SELECTED_STYLE_SB_WIDTH, -1);
325     _opacity_sb.set_sensitive (false);
327     _table.attach(_fill_label, 0,1, 0,1, Gtk::FILL, Gtk::SHRINK);
328     _table.attach(_stroke_label, 0,1, 1,2, Gtk::FILL, Gtk::SHRINK);
330     _table.attach(_fill_flag_place, 1,2, 0,1, Gtk::SHRINK, Gtk::SHRINK);
331     _table.attach(_stroke_flag_place, 1,2, 1,2, Gtk::SHRINK, Gtk::SHRINK);
333     _table.attach(_fill_place, 2,3, 0,1);
334     _table.attach(_stroke, 2,3, 1,2);
336     _opacity_place.add(_opacity_label);
337     _table.attach(_opacity_place, 4,5, 0,2, Gtk::SHRINK, Gtk::SHRINK);
338     _table.attach(_opacity_sb, 5,6, 0,2, Gtk::SHRINK, Gtk::SHRINK);
340     pack_start(_table, true, true, 2);
342     set_size_request (SELECTED_STYLE_WIDTH, -1);
344     sp_set_font_size_smaller (GTK_WIDGET(_opacity_label.gobj()));
345     sp_set_font_size_smaller (GTK_WIDGET(_opacity_sb.gobj()));
346     sp_set_font_size_smaller (GTK_WIDGET(_fill_place.gobj()));
347     sp_set_font_size_smaller (GTK_WIDGET(_fill_flag_place.gobj()));
348     sp_set_font_size_smaller (GTK_WIDGET(_stroke_place.gobj()));
349     sp_set_font_size_smaller (GTK_WIDGET(_stroke_flag_place.gobj()));
350     sp_set_font_size_smaller (GTK_WIDGET(_stroke_width.gobj()));
351     sp_set_font_size_smaller (GTK_WIDGET(_fill_label.gobj()));
352     sp_set_font_size_smaller (GTK_WIDGET(_stroke_label.gobj()));
354     _drop[SS_FILL] = new DropTracker();
355     ((DropTracker*)_drop[SS_FILL])->parent = this;
356     ((DropTracker*)_drop[SS_FILL])->item = SS_FILL;
358     _drop[SS_STROKE] = new DropTracker();
359     ((DropTracker*)_drop[SS_STROKE])->parent = this;
360     ((DropTracker*)_drop[SS_STROKE])->item = SS_STROKE;
362     g_signal_connect(_stroke_place.gobj(),
363                      "drag_data_received",
364                      G_CALLBACK(dragDataReceived),
365                      _drop[SS_STROKE]);
367     g_signal_connect(_fill_place.gobj(),
368                      "drag_data_received",
369                      G_CALLBACK(dragDataReceived),
370                      _drop[SS_FILL]);
372     _fill_place.parent = this;
373     _stroke_place.parent = this;
376 SelectedStyle::~SelectedStyle()
378     selection_changed_connection->disconnect();
379     delete selection_changed_connection;
380     selection_modified_connection->disconnect();
381     delete selection_modified_connection;
382     subselection_changed_connection->disconnect();
383     delete subselection_changed_connection;
385     for (int i = SS_FILL; i <= SS_STROKE; i++) {
386         delete _color_preview[i];
387         // FIXME: do we need this? the destroy methods are not exported 
388         //sp_gradient_image_destroy(GTK_OBJECT(_gradient_preview_l[i]));
389         //sp_gradient_image_destroy(GTK_OBJECT(_gradient_preview_r[i]));
390     }
392     delete (DropTracker*)_drop[SS_FILL];
393     delete (DropTracker*)_drop[SS_STROKE];
396 void
397 SelectedStyle::setDesktop(SPDesktop *desktop)
399     _desktop = desktop;
400     gtk_object_set_data (GTK_OBJECT(_opacity_sb.gobj()), "dtw", _desktop->canvas);
402     Inkscape::Selection *selection = sp_desktop_selection (desktop);
404     selection_changed_connection = new sigc::connection (selection->connectChanged(
405         sigc::bind (
406             sigc::ptr_fun(&ss_selection_changed),
407             this )
408     ));
409     selection_modified_connection = new sigc::connection (selection->connectModified(
410         sigc::bind (
411             sigc::ptr_fun(&ss_selection_modified),
412             this )
413     ));
414     subselection_changed_connection = new sigc::connection (desktop->connectToolSubselectionChanged(
415         sigc::bind (
416             sigc::ptr_fun(&ss_subselection_changed),
417             this )
418     ));
420     //_sw_unit = (SPUnit *) sp_desktop_namedview(desktop)->doc_units;
423 void SelectedStyle::dragDataReceived( GtkWidget *widget,
424                                       GdkDragContext *drag_context,
425                                       gint x, gint y,
426                                       GtkSelectionData *data,
427                                       guint info,
428                                       guint event_time,
429                                       gpointer user_data )
431     DropTracker* tracker = (DropTracker*)user_data;
433     switch ( (int)tracker->item ) {
434         case SS_FILL:
435         case SS_STROKE:
436         {
437             if ( data->length == 8 ) {
438                 gchar c[64];
439                 // Careful about endian issues.
440                 guint16* dataVals = (guint16*)data->data;
441                 sp_svg_write_color( c, sizeof(c),
442                                     SP_RGBA32_U_COMPOSE(
443                                         0x0ff & (dataVals[0] >> 8),
444                                         0x0ff & (dataVals[1] >> 8),
445                                         0x0ff & (dataVals[2] >> 8),
446                                         0xff // can't have transparency in the color itself
447                                         //0x0ff & (data->data[3] >> 8),
448                                         ));
449                 SPCSSAttr *css = sp_repr_css_attr_new();
450                 sp_repr_css_set_property( css, (tracker->item == SS_FILL) ? "fill":"stroke", c );
451                 sp_desktop_set_style( tracker->parent->_desktop, css );
452                 sp_repr_css_attr_unref( css );
453                 sp_document_done( sp_desktop_document(tracker->parent->_desktop) , SP_VERB_NONE, 
454                                   _("Drop color"));
455             }
456         }
457         break;
458     }
461 void SelectedStyle::on_fill_remove() {
462     SPCSSAttr *css = sp_repr_css_attr_new ();
463     sp_repr_css_set_property (css, "fill", "none");
464     sp_desktop_set_style (_desktop, css, true, true); 
465     sp_repr_css_attr_unref (css);
466     sp_document_done (sp_desktop_document(_desktop), SP_VERB_DIALOG_FILL_STROKE,
467                       _("Remove fill"));
470 void SelectedStyle::on_stroke_remove() {
471     SPCSSAttr *css = sp_repr_css_attr_new ();
472     sp_repr_css_set_property (css, "stroke", "none");
473     sp_desktop_set_style (_desktop, css, true, true); 
474     sp_repr_css_attr_unref (css);
475     sp_document_done (sp_desktop_document(_desktop), SP_VERB_DIALOG_FILL_STROKE,
476                       _("Remove stroke"));
479 void SelectedStyle::on_fill_unset() {
480     SPCSSAttr *css = sp_repr_css_attr_new ();
481     sp_repr_css_unset_property (css, "fill");
482     sp_desktop_set_style (_desktop, css, true, true); 
483     sp_repr_css_attr_unref (css);
484     sp_document_done (sp_desktop_document(_desktop), SP_VERB_DIALOG_FILL_STROKE,
485                       _("Unset fill"));
488 void SelectedStyle::on_stroke_unset() {
489     SPCSSAttr *css = sp_repr_css_attr_new ();
490     sp_repr_css_unset_property (css, "stroke");
491     sp_repr_css_unset_property (css, "stroke-opacity");
492     sp_repr_css_unset_property (css, "stroke-width");
493     sp_repr_css_unset_property (css, "stroke-miterlimit");
494     sp_repr_css_unset_property (css, "stroke-linejoin");
495     sp_repr_css_unset_property (css, "stroke-linecap");
496     sp_repr_css_unset_property (css, "stroke-dashoffset");
497     sp_repr_css_unset_property (css, "stroke-dasharray");
498     sp_desktop_set_style (_desktop, css, true, true);
499     sp_repr_css_attr_unref (css);
500     sp_document_done (sp_desktop_document(_desktop), SP_VERB_DIALOG_FILL_STROKE,
501                       _("Unset stroke"));
504 void SelectedStyle::on_fill_opaque() {
505     SPCSSAttr *css = sp_repr_css_attr_new ();
506     sp_repr_css_set_property (css, "fill-opacity", "1");
507     sp_desktop_set_style (_desktop, css, true);
508     sp_repr_css_attr_unref (css);
509     sp_document_done (sp_desktop_document(_desktop), SP_VERB_DIALOG_FILL_STROKE,
510                       _("Make fill opaque"));
513 void SelectedStyle::on_stroke_opaque() {
514     SPCSSAttr *css = sp_repr_css_attr_new ();
515     sp_repr_css_set_property (css, "stroke-opacity", "1");
516     sp_desktop_set_style (_desktop, css, true);
517     sp_repr_css_attr_unref (css);
518     sp_document_done (sp_desktop_document(_desktop), SP_VERB_DIALOG_FILL_STROKE,
519                       _("Make fill opaque"));
522 void SelectedStyle::on_fill_lastused() {
523     SPCSSAttr *css = sp_repr_css_attr_new ();
524     guint32 color = sp_desktop_get_color(_desktop, true);
525     gchar c[64];
526     sp_svg_write_color (c, sizeof(c), color);
527     sp_repr_css_set_property (css, "fill", c);
528     sp_desktop_set_style (_desktop, css);
529     sp_repr_css_attr_unref (css);
530     sp_document_done (sp_desktop_document(_desktop), SP_VERB_DIALOG_FILL_STROKE, 
531                       _("Apply last set color to fill"));
534 void SelectedStyle::on_stroke_lastused() {
535     SPCSSAttr *css = sp_repr_css_attr_new ();
536     guint32 color = sp_desktop_get_color(_desktop, false);
537     gchar c[64];
538     sp_svg_write_color (c, sizeof(c), color);
539     sp_repr_css_set_property (css, "stroke", 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 stroke"));
546 void SelectedStyle::on_fill_lastselected() {
547     SPCSSAttr *css = sp_repr_css_attr_new ();
548     gchar c[64];
549     sp_svg_write_color (c, sizeof(c), _lastselected[SS_FILL]);
550     sp_repr_css_set_property (css, "fill", c);
551     sp_desktop_set_style (_desktop, css);
552     sp_repr_css_attr_unref (css);
553     sp_document_done (sp_desktop_document(_desktop), SP_VERB_DIALOG_FILL_STROKE,
554                       _("Apply last selected color to fill"));
557 void SelectedStyle::on_stroke_lastselected() {
558     SPCSSAttr *css = sp_repr_css_attr_new ();
559     gchar c[64];
560     sp_svg_write_color (c, sizeof(c), _lastselected[SS_STROKE]);
561     sp_repr_css_set_property (css, "stroke", c);
562     sp_desktop_set_style (_desktop, css);
563     sp_repr_css_attr_unref (css);
564     sp_document_done (sp_desktop_document(_desktop), SP_VERB_DIALOG_FILL_STROKE, 
565                       _("Apply last selected color to stroke"));
568 void SelectedStyle::on_fill_invert() {
569     SPCSSAttr *css = sp_repr_css_attr_new ();
570     guint32 color = _thisselected[SS_FILL];
571     gchar c[64];
572     if (_mode[SS_FILL] != SS_COLOR) return;
573     sp_svg_write_color (c, sizeof(c),
574         SP_RGBA32_U_COMPOSE(
575                 (255 - SP_RGBA32_R_U(color)),
576                 (255 - SP_RGBA32_G_U(color)),
577                 (255 - SP_RGBA32_B_U(color)),
578                 SP_RGBA32_A_U(color)
579         )
580     );
581     sp_repr_css_set_property (css, "fill", c);
582     sp_desktop_set_style (_desktop, css);
583     sp_repr_css_attr_unref (css);
584     sp_document_done (sp_desktop_document(_desktop), SP_VERB_DIALOG_FILL_STROKE,
585                       _("Invert fill"));
588 void SelectedStyle::on_stroke_invert() {
589     SPCSSAttr *css = sp_repr_css_attr_new ();
590     guint32 color = _thisselected[SS_STROKE];
591     gchar c[64];
592     if (_mode[SS_STROKE] != SS_COLOR) return;
593     sp_svg_write_color (c, sizeof(c),
594         SP_RGBA32_U_COMPOSE(
595                 (255 - SP_RGBA32_R_U(color)),
596                 (255 - SP_RGBA32_G_U(color)),
597                 (255 - SP_RGBA32_B_U(color)),
598                 SP_RGBA32_A_U(color)
599         )
600     );
601     sp_repr_css_set_property (css, "stroke", c);
602     sp_desktop_set_style (_desktop, css);
603     sp_repr_css_attr_unref (css);
604     sp_document_done (sp_desktop_document(_desktop), SP_VERB_DIALOG_FILL_STROKE,
605                       _("Invert stroke"));
606
608 void SelectedStyle::on_fill_white() {
609     SPCSSAttr *css = sp_repr_css_attr_new ();
610     gchar c[64];
611     sp_svg_write_color (c, sizeof(c), 0xffffffff);
612     sp_repr_css_set_property (css, "fill", c);
613     sp_repr_css_set_property (css, "fill-opacity", "1");
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                       _("White fill"));
620 void SelectedStyle::on_stroke_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, "stroke", c);
625     sp_repr_css_set_property (css, "stroke-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 stroke"));
632 void SelectedStyle::on_fill_black() {
633     SPCSSAttr *css = sp_repr_css_attr_new ();
634     gchar c[64];
635     sp_svg_write_color (c, sizeof(c), 0x000000ff);
636     sp_repr_css_set_property (css, "fill", c);
637     sp_repr_css_set_property (css, "fill-opacity", "1.0");
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                       _("Black fill"));
644 void SelectedStyle::on_stroke_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, "stroke", c);
649     sp_repr_css_set_property (css, "stroke-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 stroke"));
656 void SelectedStyle::on_fill_copy() {
657     if (_mode[SS_FILL] == SS_COLOR) {
658         gchar c[64];
659         sp_svg_write_color (c, sizeof(c), _thisselected[SS_FILL]);
660         Glib::ustring text;
661         text += c;
662         if (!text.empty()) {
663             Glib::RefPtr<Gtk::Clipboard> refClipboard = Gtk::Clipboard::get();
664             refClipboard->set_text(text);
665         }
666     }
669 void SelectedStyle::on_stroke_copy() {
670     if (_mode[SS_STROKE] == SS_COLOR) {
671         gchar c[64];
672         sp_svg_write_color (c, sizeof(c), _thisselected[SS_STROKE]);
673         Glib::ustring text;
674         text += c;
675         if (!text.empty()) {
676             Glib::RefPtr<Gtk::Clipboard> refClipboard = Gtk::Clipboard::get();
677             refClipboard->set_text(text);
678         }
679     }
682 void SelectedStyle::on_fill_paste() {
683     Glib::RefPtr<Gtk::Clipboard> refClipboard = Gtk::Clipboard::get();
684     Glib::ustring const text = refClipboard->wait_for_text();
686     if (!text.empty()) {
687         guint32 color = sp_svg_read_color(text.c_str(), 0x000000ff); // impossible value, as SVG color cannot have opacity
688         if (color == 0x000000ff) // failed to parse color string
689             return;
691         SPCSSAttr *css = sp_repr_css_attr_new ();
692         sp_repr_css_set_property (css, "fill", text.c_str());
693         sp_desktop_set_style (_desktop, css);
694         sp_repr_css_attr_unref (css);
695         sp_document_done (sp_desktop_document(_desktop), SP_VERB_DIALOG_FILL_STROKE, 
696                       _("Paste fill"));
697     }
700 void SelectedStyle::on_stroke_paste() {
701     Glib::RefPtr<Gtk::Clipboard> refClipboard = Gtk::Clipboard::get();
702     Glib::ustring const text = refClipboard->wait_for_text();
704     if (!text.empty()) {
705         guint32 color = sp_svg_read_color(text.c_str(), 0x000000ff); // impossible value, as SVG color cannot have opacity
706         if (color == 0x000000ff) // failed to parse color string
707             return;
709         SPCSSAttr *css = sp_repr_css_attr_new ();
710         sp_repr_css_set_property (css, "stroke", text.c_str());
711         sp_desktop_set_style (_desktop, css);
712         sp_repr_css_attr_unref (css);
713         sp_document_done (sp_desktop_document(_desktop), SP_VERB_DIALOG_FILL_STROKE,
714                       _("Paste stroke"));
715     }
718 void SelectedStyle::on_fillstroke_swap() {
719     SPCSSAttr *css = sp_repr_css_attr_new ();
721     switch (_mode[SS_FILL]) {
722     case SS_NA:
723     case SS_MANY:
724         break;
725     case SS_NONE:
726         sp_repr_css_set_property (css, "stroke", "none");
727         break;
728     case SS_UNSET:
729         sp_repr_css_unset_property (css, "stroke");
730         break;
731     case SS_COLOR:
732         gchar c[64];
733         sp_svg_write_color (c, sizeof(c), _thisselected[SS_FILL]);
734         sp_repr_css_set_property (css, "stroke", c);
735         break;
736     case SS_LGRADIENT:
737     case SS_RGRADIENT:
738     case SS_PATTERN:
739         sp_repr_css_set_property (css, "stroke", _paintserver_id[SS_FILL].c_str());
740         break;
741     }
743     switch (_mode[SS_STROKE]) {
744     case SS_NA:
745     case SS_MANY:
746         break;
747     case SS_NONE:
748         sp_repr_css_set_property (css, "fill", "none");
749         break;
750     case SS_UNSET:
751         sp_repr_css_unset_property (css, "fill");
752         break;
753     case SS_COLOR:
754         gchar c[64];
755         sp_svg_write_color (c, sizeof(c), _thisselected[SS_STROKE]);
756         sp_repr_css_set_property (css, "fill", c);
757         break;
758     case SS_LGRADIENT:
759     case SS_RGRADIENT:
760     case SS_PATTERN:
761         sp_repr_css_set_property (css, "fill", _paintserver_id[SS_STROKE].c_str());
762         break;
763     }
765     sp_desktop_set_style (_desktop, css);
766     sp_repr_css_attr_unref (css);
767     sp_document_done (sp_desktop_document(_desktop), SP_VERB_DIALOG_FILL_STROKE, 
768                       _("Swap fill and stroke"));
771 void SelectedStyle::on_fill_edit() {
772     if (Dialog::FillAndStroke *dialog = dynamic_cast<Dialog::FillAndStroke *>(
773             _desktop->_dlg_mgr->getDialog("FillAndStroke")))
774         dialog->showPageFill();
777 void SelectedStyle::on_stroke_edit() {
778     if (Dialog::FillAndStroke *dialog = dynamic_cast<Dialog::FillAndStroke *>(
779             _desktop->_dlg_mgr->getDialog("FillAndStroke")))
780         dialog->showPageStrokePaint();
783 bool 
784 SelectedStyle::on_fill_click(GdkEventButton *event)
786     if (event->button == 1) { // click, open fill&stroke
788         if (Dialog::FillAndStroke *dialog = dynamic_cast<Dialog::FillAndStroke *>(
789                 _desktop->_dlg_mgr->getDialog("FillAndStroke")))
790             dialog->showPageFill();
792     } else if (event->button == 3) { // right-click, popup menu
793         _popup[SS_FILL].popup(event->button, event->time);
794     } else if (event->button == 2) { // middle click, toggle none/lastcolor
795         if (_mode[SS_FILL] == SS_NONE) {
796             on_fill_lastused();
797         } else {
798             on_fill_remove();
799         }
800     }
801     return true;
804 bool 
805 SelectedStyle::on_stroke_click(GdkEventButton *event)
807     if (event->button == 1) { // click, open fill&stroke
808         if (Dialog::FillAndStroke *dialog = dynamic_cast<Dialog::FillAndStroke *>(
809                 _desktop->_dlg_mgr->getDialog("FillAndStroke")))
810             dialog->showPageStrokePaint();
811     } else if (event->button == 3) { // right-click, popup menu
812         _popup[SS_STROKE].popup(event->button, event->time);
813     } else if (event->button == 2) { // middle click, toggle none/lastcolor
814         if (_mode[SS_STROKE] == SS_NONE) {
815             on_stroke_lastused();
816         } else {
817             on_stroke_remove();
818         }
819     }
820     return true;
823 bool 
824 SelectedStyle::on_sw_click(GdkEventButton *event)
826     if (event->button == 1) { // click, open fill&stroke
827         if (Dialog::FillAndStroke *dialog = dynamic_cast<Dialog::FillAndStroke *>(
828                 _desktop->_dlg_mgr->getDialog("FillAndStroke")))
829             dialog->showPageStrokeStyle();
830     } else if (event->button == 3) { // right-click, popup menu
831         _popup_sw.popup(event->button, event->time);
832     } else if (event->button == 2) { // middle click, toggle none/lastwidth?
833         //
834     }
835     return true;
838 bool 
839 SelectedStyle::on_opacity_click(GdkEventButton *event)
841     if (event->button == 2) { // middle click
842         const char* opacity = _opacity_sb.get_value() < 50? "0.5" : (_opacity_sb.get_value() == 100? "0" : "1");
843         SPCSSAttr *css = sp_repr_css_attr_new ();
844         sp_repr_css_set_property (css, "opacity", opacity);
845         sp_desktop_set_style (_desktop, css);
846         sp_repr_css_attr_unref (css);
847         sp_document_done (sp_desktop_document (_desktop), SP_VERB_DIALOG_FILL_STROKE,
848                       _("Change opacity"));
849         return true;
850     }
852     return false;
855 void SelectedStyle::on_popup_px() {
856     _sw_unit = (SPUnit *) &(sp_unit_get_by_id(SP_UNIT_PX));
857     update();
859 void SelectedStyle::on_popup_pt() {
860     _sw_unit = (SPUnit *) &(sp_unit_get_by_id(SP_UNIT_PT));
861     update();
863 void SelectedStyle::on_popup_mm() {
864     _sw_unit = (SPUnit *) &(sp_unit_get_by_id(SP_UNIT_MM));
865     update();
868 void SelectedStyle::on_popup_preset(int i) {
869     SPCSSAttr *css = sp_repr_css_attr_new ();
870     gdouble w;
871     if (_sw_unit) {
872         w = sp_units_get_pixels (_sw_presets[i], *_sw_unit);
873     } else {
874         w = _sw_presets[i];
875     }
876     Inkscape::CSSOStringStream os;
877     os << w;
878     sp_repr_css_set_property (css, "stroke-width", os.str().c_str());
879     // FIXME: update dash patterns!
880     sp_desktop_set_style (_desktop, css, true);
881     sp_repr_css_attr_unref (css);
882     sp_document_done (sp_desktop_document(_desktop), SP_VERB_DIALOG_SWATCHES,
883                       _("Change stroke width"));
886 void
887 SelectedStyle::update()
889     if (_desktop == NULL)
890         return;
892     // create temporary style
893     SPStyle *query = sp_style_new (sp_desktop_document(_desktop));
895     for (int i = SS_FILL; i <= SS_STROKE; i++) {
896         Gtk::EventBox *place = (i == SS_FILL)? &_fill_place : &_stroke_place;
897         Gtk::EventBox *flag_place = (i == SS_FILL)? &_fill_flag_place : &_stroke_flag_place;
899         place->remove();
900         flag_place->remove();
902         _tooltips.unset_tip(*place);
903         _tooltips.unset_tip(*flag_place);
905         _mode[i] = SS_NA;
906         _paintserver_id[i].clear();
908         _popup_copy[i].set_sensitive(false);
910         // query style from desktop. This returns a result flag and fills query with the style of subselection, if any, or selection
911         int result = sp_desktop_query_style (_desktop, query, 
912                                              (i == SS_FILL)? QUERY_STYLE_PROPERTY_FILL : QUERY_STYLE_PROPERTY_STROKE);
913         switch (result) {
914         case QUERY_STYLE_NOTHING:
915             place->add(_na[i]);
916             _tooltips.set_tip(*place, __na[i]);
917             _mode[i] = SS_NA;
918             if ( _dropEnabled[i] ) {
919                 gtk_drag_dest_unset( GTK_WIDGET((i==SS_FILL) ? _fill_place.gobj():_stroke_place.gobj()) );
920                 _dropEnabled[i] = false;
921             }
922             break;
923         case QUERY_STYLE_SINGLE:
924         case QUERY_STYLE_MULTIPLE_AVERAGED:
925         case QUERY_STYLE_MULTIPLE_SAME: 
926             if ( !_dropEnabled[i] ) {
927                 gtk_drag_dest_set( GTK_WIDGET( (i==SS_FILL) ? _fill_place.gobj():_stroke_place.gobj()),
928                                    GTK_DEST_DEFAULT_ALL,
929                                    ui_drop_target_entries,
930                                    nui_drop_target_entries,
931                                    GdkDragAction(GDK_ACTION_COPY | GDK_ACTION_MOVE) );
932                 _dropEnabled[i] = true;
933             }
934             SPIPaint *paint;
935             if (i == SS_FILL) {
936                 paint = &(query->fill);
937             } else {
938                 paint = &(query->stroke);
939             }
940             if (paint->set && paint->isPaintserver()) {
941                 SPPaintServer *server = (i == SS_FILL)? SP_STYLE_FILL_SERVER (query) : SP_STYLE_STROKE_SERVER (query);
942                 if ( server ) {
943                     Inkscape::XML::Node *srepr = SP_OBJECT_REPR(server);
944                     _paintserver_id[i] += "url(#";
945                     _paintserver_id[i] += srepr->attribute("id");
946                     _paintserver_id[i] += ")";
948                     if (SP_IS_LINEARGRADIENT (server)) {
949                         SPGradient *vector = sp_gradient_get_vector(SP_GRADIENT(server), false);
950                         sp_gradient_image_set_gradient ((SPGradientImage *) _gradient_preview_l[i], vector);
951                         place->add(_gradient_box_l[i]);
952                         _tooltips.set_tip(*place, __lgradient[i]);
953                         _mode[i] = SS_LGRADIENT;
954                     } else if (SP_IS_RADIALGRADIENT (server)) {
955                         SPGradient *vector = sp_gradient_get_vector(SP_GRADIENT(server), false);
956                         sp_gradient_image_set_gradient ((SPGradientImage *) _gradient_preview_r[i], vector);
957                         place->add(_gradient_box_r[i]);
958                         _tooltips.set_tip(*place, __rgradient[i]);
959                         _mode[i] = SS_RGRADIENT;
960                     } else if (SP_IS_PATTERN (server)) {
961                         place->add(_pattern[i]);
962                         _tooltips.set_tip(*place, __pattern[i]);
963                         _mode[i] = SS_PATTERN;
964                     }
965                 } else {
966                     g_warning ("file %s: line %d: Unknown paint server", __FILE__, __LINE__);
967                 }
968             } else if (paint->set && paint->isColor()) {
969                 guint32 color = paint->value.color.toRGBA32(
970                                      SP_SCALE24_TO_FLOAT ((i == SS_FILL)? query->fill_opacity.value : query->stroke_opacity.value));
971                 _lastselected[i] = _thisselected[i];
972                 _thisselected[i] = color | 0xff; // only color, opacity === 1
973                 ((Inkscape::UI::Widget::ColorPreview*)_color_preview[i])->setRgba32 (color);
974                 _color_preview[i]->show_all();
975                 place->add(*_color_preview[i]);
976                 gchar c_string[64];
977                 g_snprintf (c_string, 64, "%06x/%.3g", color >> 8, SP_RGBA32_A_F(color));
978                 _tooltips.set_tip(*place, __color[i] + ": " + c_string + _(", drag to adjust"));
979                 _mode[i] = SS_COLOR;
980                 _popup_copy[i].set_sensitive(true);
982             } else if (paint->set && paint->isNone()) {
983                 place->add(_none[i]);
984                 _tooltips.set_tip(*place, __none[i]);
985                 _mode[i] = SS_NONE;
986             } else if (!paint->set) {
987                 place->add(_unset[i]);
988                 _tooltips.set_tip(*place, __unset[i]);
989                 _mode[i] = SS_UNSET;
990             }
991             if (result == QUERY_STYLE_MULTIPLE_AVERAGED) {
992                 flag_place->add(_averaged[i]);
993                 _tooltips.set_tip(*flag_place, __averaged[i]);
994             } else if (result == QUERY_STYLE_MULTIPLE_SAME) {
995                 flag_place->add(_multiple[i]);
996                 _tooltips.set_tip(*flag_place, __multiple[i]);
997             }
998             break;
999         case QUERY_STYLE_MULTIPLE_DIFFERENT:
1000             place->add(_many[i]);
1001             _tooltips.set_tip(*place, __many[i]);
1002             _mode[i] = SS_MANY;
1003             break;
1004         default:
1005             break;
1006         }
1007     }
1009 // Now query opacity
1010     _tooltips.unset_tip(_opacity_place);
1011     _tooltips.unset_tip(_opacity_sb);
1013     int result = sp_desktop_query_style (_desktop, query, QUERY_STYLE_PROPERTY_MASTEROPACITY);
1015     switch (result) {
1016     case QUERY_STYLE_NOTHING:
1017         _tooltips.set_tip(_opacity_place, _("Nothing selected"));
1018         _tooltips.set_tip(_opacity_sb, _("Nothing selected"));
1019         _opacity_sb.set_sensitive(false);
1020         break;
1021     case QUERY_STYLE_SINGLE:
1022     case QUERY_STYLE_MULTIPLE_AVERAGED:
1023     case QUERY_STYLE_MULTIPLE_SAME:
1024         _tooltips.set_tip(_opacity_place, _("Opacity, %"));
1025         _tooltips.set_tip(_opacity_sb, _("Opacity, %"));
1026         if (_opacity_blocked) break;
1027         _opacity_blocked = true;
1028         _opacity_sb.set_sensitive(true);
1029         _opacity_adjustment.set_value(SP_SCALE24_TO_FLOAT(query->opacity.value) * 100);
1030         _opacity_blocked = false;
1031         break;
1032     }
1034 // Now query stroke_width
1035     int result_sw = sp_desktop_query_style (_desktop, query, QUERY_STYLE_PROPERTY_STROKEWIDTH);
1036     switch (result_sw) {
1037     case QUERY_STYLE_NOTHING:
1038         _stroke_width.set_markup("");
1039         break;
1040     case QUERY_STYLE_SINGLE:
1041     case QUERY_STYLE_MULTIPLE_AVERAGED:
1042     case QUERY_STYLE_MULTIPLE_SAME: 
1043     {
1044         double w;
1045         if (_sw_unit) {
1046             w = sp_pixels_get_units(query->stroke_width.computed, *_sw_unit);
1047         } else {
1048             w = query->stroke_width.computed;
1049         }
1050         {
1051             gchar *str = g_strdup_printf(" %.3g", w);
1052             _stroke_width.set_markup(str);
1053             g_free (str);
1054         }
1055         {
1056             gchar *str = g_strdup_printf(_("Stroke width: %.5g%s%s"), 
1057                                          w, 
1058                                          _sw_unit? sp_unit_get_abbreviation(_sw_unit) : "px", 
1059                                          (result_sw == QUERY_STYLE_MULTIPLE_AVERAGED)?
1060                                          _(" (averaged)") : "");
1061             _tooltips.set_tip(_stroke_width_place, str);
1062             g_free (str);
1063         }
1064         break;
1065     }
1066     default:
1067         break;
1068     }
1070     sp_style_unref(query);
1073 void SelectedStyle::opacity_0(void) {_opacity_sb.set_value(0);}
1074 void SelectedStyle::opacity_025(void) {_opacity_sb.set_value(25);}
1075 void SelectedStyle::opacity_05(void) {_opacity_sb.set_value(50);}
1076 void SelectedStyle::opacity_075(void) {_opacity_sb.set_value(75);}
1077 void SelectedStyle::opacity_1(void) {_opacity_sb.set_value(100);}
1079 void SelectedStyle::on_opacity_menu (Gtk::Menu *menu) {
1081     Glib::ListHandle<Gtk::Widget *> children = menu->get_children();
1082     for (Glib::ListHandle<Gtk::Widget *>::iterator iter = children.begin(); iter != children.end(); iter++) {
1083         menu->remove(*(*iter));
1084     }
1086     {
1087         Gtk::MenuItem *item = new Gtk::MenuItem;
1088         item->add(*(new Gtk::Label(_("0 (transparent)"), 0, 0)));
1089         item->signal_activate().connect(sigc::mem_fun(*this, &SelectedStyle::opacity_0 ));
1090         menu->add(*item);
1091     }
1092     {
1093         Gtk::MenuItem *item = new Gtk::MenuItem;
1094         item->add(*(new Gtk::Label("25%", 0, 0)));
1095         item->signal_activate().connect(sigc::mem_fun(*this, &SelectedStyle::opacity_025 ));
1096         menu->add(*item);
1097     }
1098     {
1099         Gtk::MenuItem *item = new Gtk::MenuItem;
1100         item->add(*(new Gtk::Label("50%", 0, 0)));
1101         item->signal_activate().connect(sigc::mem_fun(*this, &SelectedStyle::opacity_05 ));
1102         menu->add(*item);
1103     }
1104     {
1105         Gtk::MenuItem *item = new Gtk::MenuItem;
1106         item->add(*(new Gtk::Label("75%", 0, 0)));
1107         item->signal_activate().connect(sigc::mem_fun(*this, &SelectedStyle::opacity_075 ));
1108         menu->add(*item);
1109     }
1110     {
1111         Gtk::MenuItem *item = new Gtk::MenuItem;
1112         item->add(*(new Gtk::Label(_("100% (opaque)"), 0, 0)));
1113         item->signal_activate().connect(sigc::mem_fun(*this, &SelectedStyle::opacity_1 ));
1114         menu->add(*item);
1115     }
1117     menu->show_all();
1120 void SelectedStyle::on_opacity_changed () {
1121     if (_opacity_blocked)
1122         return;
1123     _opacity_blocked = true;
1124     SPCSSAttr *css = sp_repr_css_attr_new ();
1125     Inkscape::CSSOStringStream os;
1126     os << CLAMP ((_opacity_adjustment.get_value() / 100), 0.0, 1.0);
1127     sp_repr_css_set_property (css, "opacity", os.str().c_str());
1128     // FIXME: workaround for GTK breakage: display interruptibility sometimes results in GTK
1129     // sending multiple value-changed events. As if when Inkscape interrupts redraw for main loop
1130     // iterations, GTK discovers that this callback hasn't finished yet, and for some weird reason
1131     // decides to add yet another value-changed event to the queue. Totally braindead if you ask
1132     // me. As a result, scrolling the spinbutton once results in runaway change until it hits 1.0
1133     // or 0.0. (And no, this is not a race with ::update, I checked that.)
1134     // Sigh. So we disable interruptibility while we're setting the new value.
1135     sp_canvas_force_full_redraw_after_interruptions(sp_desktop_canvas(_desktop), 0);
1136     sp_desktop_set_style (_desktop, css);
1137     sp_repr_css_attr_unref (css);
1138     sp_document_maybe_done (sp_desktop_document (_desktop), "fillstroke:opacity", SP_VERB_DIALOG_FILL_STROKE,
1139                       _("Change opacity"));
1140     // resume interruptibility
1141     sp_canvas_end_forced_full_redraws(sp_desktop_canvas(_desktop));
1142     spinbutton_defocus(GTK_OBJECT(_opacity_sb.gobj()));
1143     _opacity_blocked = false;
1146 RotateableSwatch::RotateableSwatch(guint mode) {
1147     fillstroke = mode;
1148     startcolor_set = false;
1149     undokey = "ssrot1";
1152 RotateableSwatch::~RotateableSwatch() {
1155 double
1156 RotateableSwatch::color_adjust(float *hsl, double by, guint32 cc, guint modifier)
1158     sp_color_rgb_to_hsl_floatv (hsl, SP_RGBA32_R_F(cc), SP_RGBA32_G_F(cc), SP_RGBA32_B_F(cc));
1160     double diff = 0;
1161     if (modifier == 2) { // saturation
1162         double old = hsl[1];
1163         if (by > 0) {
1164             hsl[1] += by * (1 - hsl[1]);
1165         } else {
1166             hsl[1] += by * (hsl[1]);
1167         }
1168         diff = hsl[1] - old;
1169     } else if (modifier == 1) { // lightness
1170         double old = hsl[2];
1171         if (by > 0) {
1172             hsl[2] += by * (1 - hsl[2]);
1173         } else {
1174             hsl[2] += by * (hsl[2]);
1175         }
1176         diff = hsl[2] - old;
1177     } else { // hue
1178         double old = hsl[0];
1179         hsl[0] += by/2;
1180         while (hsl[0] < 0) 
1181             hsl[0] += 1;
1182         while (hsl[0] > 1) 
1183             hsl[0] -= 1;
1184         diff = hsl[0] - old;
1185     }
1187     float rgb[3];
1188     sp_color_hsl_to_rgb_floatv (rgb, hsl[0], hsl[1], hsl[2]);
1190     gchar c[64];
1191     sp_svg_write_color (c, sizeof(c),
1192         SP_RGBA32_U_COMPOSE(
1193                 (SP_COLOR_F_TO_U(rgb[0])),
1194                 (SP_COLOR_F_TO_U(rgb[1])),
1195                 (SP_COLOR_F_TO_U(rgb[2])),
1196                 0xff
1197         )
1198     );
1200     SPCSSAttr *css = sp_repr_css_attr_new ();
1201     if (fillstroke == SS_FILL)
1202         sp_repr_css_set_property (css, "fill", c);
1203     else 
1204         sp_repr_css_set_property (css, "stroke", c);
1205     sp_desktop_set_style (parent->getDesktop(), css);
1206     sp_repr_css_attr_unref (css);
1207     return diff;
1210 void
1211 RotateableSwatch::do_motion(double by, guint modifier) {
1212     if (parent->_mode[fillstroke] != SS_COLOR) 
1213         return;
1215     guint32 cc;
1216     if (!startcolor_set) {
1217         cc = startcolor = parent->_thisselected[fillstroke];
1218         startcolor_set = true;
1219     } else {
1220         cc = startcolor;
1221     }
1223     float hsl[3];
1224     double diff = color_adjust(hsl, by, cc, modifier);
1226     if (modifier == 2) { // saturation
1227         sp_document_maybe_done (sp_desktop_document(parent->getDesktop()), undokey, 
1228                                 SP_VERB_DIALOG_FILL_STROKE, ("Adjust saturation"));
1229         double ch = hsl[1];
1230         parent->getDesktop()->event_context->_message_context->setF(Inkscape::IMMEDIATE_MESSAGE, _("Adusting <b>saturation</b>: was %.3g, now <b>%.3g</b> (diff %.3g); without modifiers to adjust hue, with <b>Ctrl</b> to adjust lightness"), ch - diff, ch, diff);
1232     } else if (modifier == 1) { // lightness
1233         sp_document_maybe_done (sp_desktop_document(parent->getDesktop()), undokey, 
1234                                 SP_VERB_DIALOG_FILL_STROKE, ("Adjust lightness"));
1235         double ch = hsl[2];
1236         parent->getDesktop()->event_context->_message_context->setF(Inkscape::IMMEDIATE_MESSAGE, _("Adusting <b>lightness</b> was %.3g, now <b>%.3g</b> (diff %.3g); without modifiers to adjust hue, with <b>Shift</b> to adjust saturation"), ch - diff, ch, diff);
1238     } else { // hue
1239         sp_document_maybe_done (sp_desktop_document(parent->getDesktop()), undokey, 
1240                                 SP_VERB_DIALOG_FILL_STROKE, ("Adjust hue"));
1241         double ch = hsl[0];
1242         parent->getDesktop()->event_context->_message_context->setF(Inkscape::IMMEDIATE_MESSAGE, _("Adusting <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);
1243     }
1246 void
1247 RotateableSwatch::do_release(double by, guint modifier) {
1248     if (parent->_mode[fillstroke] != SS_COLOR) 
1249         return;
1251     float hsl[3];
1252     color_adjust(hsl, by, startcolor, modifier);
1254     if (modifier == 2) { // saturation
1255         sp_document_maybe_done (sp_desktop_document(parent->getDesktop()), undokey, 
1256                                 SP_VERB_DIALOG_FILL_STROKE, ("Adjust saturation"));
1258     } else if (modifier == 1) { // lightness
1259         sp_document_maybe_done (sp_desktop_document(parent->getDesktop()), undokey, 
1260                                 SP_VERB_DIALOG_FILL_STROKE, ("Adjust lightness"));
1262     } else { // hue
1263         sp_document_maybe_done (sp_desktop_document(parent->getDesktop()), undokey, 
1264                                 SP_VERB_DIALOG_FILL_STROKE, ("Adjust hue"));
1265     }
1267     if (!strcmp(undokey, "ssrot1")) {
1268         undokey = "ssrot2";
1269     } else {
1270         undokey = "ssrot1";
1271     }
1273     parent->getDesktop()->event_context->_message_context->clear();
1274     startcolor_set = false;
1278 } // namespace Widget
1279 } // namespace UI
1280 } // namespace Inkscape
1282 /* 
1283   Local Variables:
1284   mode:c++
1285   c-file-style:"stroustrup"
1286   c-file-offsets:((innamespace . 0)(inline-open . 0))
1287   indent-tabs-mode:nil
1288   fill-column:99
1289   End:
1290 */
1291 // vim: filetype=c++:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 :