Code

3da613416a477c8143e9e67f9507cd2ce9bd8ced
[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 "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(this, SS_FILL),
111       _stroke_place(this, 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(this),
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 ()
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]);
384 SelectedStyle::~SelectedStyle()
386     selection_changed_connection->disconnect();
387     delete selection_changed_connection;
388     selection_modified_connection->disconnect();
389     delete selection_modified_connection;
390     subselection_changed_connection->disconnect();
391     delete subselection_changed_connection;
393     for (int i = SS_FILL; i <= SS_STROKE; i++) {
394         delete _color_preview[i];
395         // FIXME: do we need this? the destroy methods are not exported
396         //sp_gradient_image_destroy(GTK_OBJECT(_gradient_preview_l[i]));
397         //sp_gradient_image_destroy(GTK_OBJECT(_gradient_preview_r[i]));
398     }
400     delete (DropTracker*)_drop[SS_FILL];
401     delete (DropTracker*)_drop[SS_STROKE];
404 void
405 SelectedStyle::setDesktop(SPDesktop *desktop)
407     _desktop = desktop;
408     gtk_object_set_data (GTK_OBJECT(_opacity_sb.gobj()), "dtw", _desktop->canvas);
410     Inkscape::Selection *selection = sp_desktop_selection (desktop);
412     selection_changed_connection = new sigc::connection (selection->connectChanged(
413         sigc::bind (
414             sigc::ptr_fun(&ss_selection_changed),
415             this )
416     ));
417     selection_modified_connection = new sigc::connection (selection->connectModified(
418         sigc::bind (
419             sigc::ptr_fun(&ss_selection_modified),
420             this )
421     ));
422     subselection_changed_connection = new sigc::connection (desktop->connectToolSubselectionChanged(
423         sigc::bind (
424             sigc::ptr_fun(&ss_subselection_changed),
425             this )
426     ));
428     //_sw_unit = (SPUnit *) sp_desktop_namedview(desktop)->doc_units;
431 void SelectedStyle::dragDataReceived( GtkWidget */*widget*/,
432                                       GdkDragContext */*drag_context*/,
433                                       gint /*x*/, gint /*y*/,
434                                       GtkSelectionData *data,
435                                       guint /*info*/,
436                                       guint /*event_time*/,
437                                       gpointer user_data )
439     DropTracker* tracker = (DropTracker*)user_data;
441     switch ( (int)tracker->item ) {
442         case SS_FILL:
443         case SS_STROKE:
444         {
445             if ( data->length == 8 ) {
446                 gchar c[64];
447                 // Careful about endian issues.
448                 guint16* dataVals = (guint16*)data->data;
449                 sp_svg_write_color( c, sizeof(c),
450                                     SP_RGBA32_U_COMPOSE(
451                                         0x0ff & (dataVals[0] >> 8),
452                                         0x0ff & (dataVals[1] >> 8),
453                                         0x0ff & (dataVals[2] >> 8),
454                                         0xff // can't have transparency in the color itself
455                                         //0x0ff & (data->data[3] >> 8),
456                                         ));
457                 SPCSSAttr *css = sp_repr_css_attr_new();
458                 sp_repr_css_set_property( css, (tracker->item == SS_FILL) ? "fill":"stroke", c );
459                 sp_desktop_set_style( tracker->parent->_desktop, css );
460                 sp_repr_css_attr_unref( css );
461                 sp_document_done( sp_desktop_document(tracker->parent->_desktop) , SP_VERB_NONE,
462                                   _("Drop color"));
463             }
464         }
465         break;
466     }
469 void SelectedStyle::on_fill_remove() {
470     SPCSSAttr *css = sp_repr_css_attr_new ();
471     sp_repr_css_set_property (css, "fill", "none");
472     sp_desktop_set_style (_desktop, css, true, true);
473     sp_repr_css_attr_unref (css);
474     sp_document_done (sp_desktop_document(_desktop), SP_VERB_DIALOG_FILL_STROKE,
475                       _("Remove fill"));
478 void SelectedStyle::on_stroke_remove() {
479     SPCSSAttr *css = sp_repr_css_attr_new ();
480     sp_repr_css_set_property (css, "stroke", "none");
481     sp_desktop_set_style (_desktop, css, true, true);
482     sp_repr_css_attr_unref (css);
483     sp_document_done (sp_desktop_document(_desktop), SP_VERB_DIALOG_FILL_STROKE,
484                       _("Remove stroke"));
487 void SelectedStyle::on_fill_unset() {
488     SPCSSAttr *css = sp_repr_css_attr_new ();
489     sp_repr_css_unset_property (css, "fill");
490     sp_desktop_set_style (_desktop, css, true, true);
491     sp_repr_css_attr_unref (css);
492     sp_document_done (sp_desktop_document(_desktop), SP_VERB_DIALOG_FILL_STROKE,
493                       _("Unset fill"));
496 void SelectedStyle::on_stroke_unset() {
497     SPCSSAttr *css = sp_repr_css_attr_new ();
498     sp_repr_css_unset_property (css, "stroke");
499     sp_repr_css_unset_property (css, "stroke-opacity");
500     sp_repr_css_unset_property (css, "stroke-width");
501     sp_repr_css_unset_property (css, "stroke-miterlimit");
502     sp_repr_css_unset_property (css, "stroke-linejoin");
503     sp_repr_css_unset_property (css, "stroke-linecap");
504     sp_repr_css_unset_property (css, "stroke-dashoffset");
505     sp_repr_css_unset_property (css, "stroke-dasharray");
506     sp_desktop_set_style (_desktop, css, true, true);
507     sp_repr_css_attr_unref (css);
508     sp_document_done (sp_desktop_document(_desktop), SP_VERB_DIALOG_FILL_STROKE,
509                       _("Unset stroke"));
512 void SelectedStyle::on_fill_opaque() {
513     SPCSSAttr *css = sp_repr_css_attr_new ();
514     sp_repr_css_set_property (css, "fill-opacity", "1");
515     sp_desktop_set_style (_desktop, css, true);
516     sp_repr_css_attr_unref (css);
517     sp_document_done (sp_desktop_document(_desktop), SP_VERB_DIALOG_FILL_STROKE,
518                       _("Make fill opaque"));
521 void SelectedStyle::on_stroke_opaque() {
522     SPCSSAttr *css = sp_repr_css_attr_new ();
523     sp_repr_css_set_property (css, "stroke-opacity", "1");
524     sp_desktop_set_style (_desktop, css, true);
525     sp_repr_css_attr_unref (css);
526     sp_document_done (sp_desktop_document(_desktop), SP_VERB_DIALOG_FILL_STROKE,
527                       _("Make fill opaque"));
530 void SelectedStyle::on_fill_lastused() {
531     SPCSSAttr *css = sp_repr_css_attr_new ();
532     guint32 color = sp_desktop_get_color(_desktop, true);
533     gchar c[64];
534     sp_svg_write_color (c, sizeof(c), color);
535     sp_repr_css_set_property (css, "fill", c);
536     sp_desktop_set_style (_desktop, css);
537     sp_repr_css_attr_unref (css);
538     sp_document_done (sp_desktop_document(_desktop), SP_VERB_DIALOG_FILL_STROKE,
539                       _("Apply last set color to fill"));
542 void SelectedStyle::on_stroke_lastused() {
543     SPCSSAttr *css = sp_repr_css_attr_new ();
544     guint32 color = sp_desktop_get_color(_desktop, false);
545     gchar c[64];
546     sp_svg_write_color (c, sizeof(c), color);
547     sp_repr_css_set_property (css, "stroke", c);
548     sp_desktop_set_style (_desktop, css);
549     sp_repr_css_attr_unref (css);
550     sp_document_done (sp_desktop_document(_desktop), SP_VERB_DIALOG_FILL_STROKE,
551                       _("Apply last set color to stroke"));
554 void SelectedStyle::on_fill_lastselected() {
555     SPCSSAttr *css = sp_repr_css_attr_new ();
556     gchar c[64];
557     sp_svg_write_color (c, sizeof(c), _lastselected[SS_FILL]);
558     sp_repr_css_set_property (css, "fill", c);
559     sp_desktop_set_style (_desktop, css);
560     sp_repr_css_attr_unref (css);
561     sp_document_done (sp_desktop_document(_desktop), SP_VERB_DIALOG_FILL_STROKE,
562                       _("Apply last selected color to fill"));
565 void SelectedStyle::on_stroke_lastselected() {
566     SPCSSAttr *css = sp_repr_css_attr_new ();
567     gchar c[64];
568     sp_svg_write_color (c, sizeof(c), _lastselected[SS_STROKE]);
569     sp_repr_css_set_property (css, "stroke", c);
570     sp_desktop_set_style (_desktop, css);
571     sp_repr_css_attr_unref (css);
572     sp_document_done (sp_desktop_document(_desktop), SP_VERB_DIALOG_FILL_STROKE,
573                       _("Apply last selected color to stroke"));
576 void SelectedStyle::on_fill_invert() {
577     SPCSSAttr *css = sp_repr_css_attr_new ();
578     guint32 color = _thisselected[SS_FILL];
579     gchar c[64];
580     if (_mode[SS_FILL] != SS_COLOR) return;
581     sp_svg_write_color (c, sizeof(c),
582         SP_RGBA32_U_COMPOSE(
583                 (255 - SP_RGBA32_R_U(color)),
584                 (255 - SP_RGBA32_G_U(color)),
585                 (255 - SP_RGBA32_B_U(color)),
586                 SP_RGBA32_A_U(color)
587         )
588     );
589     sp_repr_css_set_property (css, "fill", c);
590     sp_desktop_set_style (_desktop, css);
591     sp_repr_css_attr_unref (css);
592     sp_document_done (sp_desktop_document(_desktop), SP_VERB_DIALOG_FILL_STROKE,
593                       _("Invert fill"));
596 void SelectedStyle::on_stroke_invert() {
597     SPCSSAttr *css = sp_repr_css_attr_new ();
598     guint32 color = _thisselected[SS_STROKE];
599     gchar c[64];
600     if (_mode[SS_STROKE] != SS_COLOR) return;
601     sp_svg_write_color (c, sizeof(c),
602         SP_RGBA32_U_COMPOSE(
603                 (255 - SP_RGBA32_R_U(color)),
604                 (255 - SP_RGBA32_G_U(color)),
605                 (255 - SP_RGBA32_B_U(color)),
606                 SP_RGBA32_A_U(color)
607         )
608     );
609     sp_repr_css_set_property (css, "stroke", c);
610     sp_desktop_set_style (_desktop, css);
611     sp_repr_css_attr_unref (css);
612     sp_document_done (sp_desktop_document(_desktop), SP_VERB_DIALOG_FILL_STROKE,
613                       _("Invert stroke"));
616 void SelectedStyle::on_fill_white() {
617     SPCSSAttr *css = sp_repr_css_attr_new ();
618     gchar c[64];
619     sp_svg_write_color (c, sizeof(c), 0xffffffff);
620     sp_repr_css_set_property (css, "fill", c);
621     sp_repr_css_set_property (css, "fill-opacity", "1");
622     sp_desktop_set_style (_desktop, css);
623     sp_repr_css_attr_unref (css);
624     sp_document_done (sp_desktop_document(_desktop), SP_VERB_DIALOG_FILL_STROKE,
625                       _("White fill"));
628 void SelectedStyle::on_stroke_white() {
629     SPCSSAttr *css = sp_repr_css_attr_new ();
630     gchar c[64];
631     sp_svg_write_color (c, sizeof(c), 0xffffffff);
632     sp_repr_css_set_property (css, "stroke", c);
633     sp_repr_css_set_property (css, "stroke-opacity", "1");
634     sp_desktop_set_style (_desktop, css);
635     sp_repr_css_attr_unref (css);
636     sp_document_done (sp_desktop_document(_desktop), SP_VERB_DIALOG_FILL_STROKE,
637                       _("White stroke"));
640 void SelectedStyle::on_fill_black() {
641     SPCSSAttr *css = sp_repr_css_attr_new ();
642     gchar c[64];
643     sp_svg_write_color (c, sizeof(c), 0x000000ff);
644     sp_repr_css_set_property (css, "fill", c);
645     sp_repr_css_set_property (css, "fill-opacity", "1.0");
646     sp_desktop_set_style (_desktop, css);
647     sp_repr_css_attr_unref (css);
648     sp_document_done (sp_desktop_document(_desktop), SP_VERB_DIALOG_FILL_STROKE,
649                       _("Black fill"));
652 void SelectedStyle::on_stroke_black() {
653     SPCSSAttr *css = sp_repr_css_attr_new ();
654     gchar c[64];
655     sp_svg_write_color (c, sizeof(c), 0x000000ff);
656     sp_repr_css_set_property (css, "stroke", c);
657     sp_repr_css_set_property (css, "stroke-opacity", "1.0");
658     sp_desktop_set_style (_desktop, css);
659     sp_repr_css_attr_unref (css);
660     sp_document_done (sp_desktop_document(_desktop), SP_VERB_DIALOG_FILL_STROKE,
661                       _("Black stroke"));
664 void SelectedStyle::on_fill_copy() {
665     if (_mode[SS_FILL] == SS_COLOR) {
666         gchar c[64];
667         sp_svg_write_color (c, sizeof(c), _thisselected[SS_FILL]);
668         Glib::ustring text;
669         text += c;
670         if (!text.empty()) {
671             Glib::RefPtr<Gtk::Clipboard> refClipboard = Gtk::Clipboard::get();
672             refClipboard->set_text(text);
673         }
674     }
677 void SelectedStyle::on_stroke_copy() {
678     if (_mode[SS_STROKE] == SS_COLOR) {
679         gchar c[64];
680         sp_svg_write_color (c, sizeof(c), _thisselected[SS_STROKE]);
681         Glib::ustring text;
682         text += c;
683         if (!text.empty()) {
684             Glib::RefPtr<Gtk::Clipboard> refClipboard = Gtk::Clipboard::get();
685             refClipboard->set_text(text);
686         }
687     }
690 void SelectedStyle::on_fill_paste() {
691     Glib::RefPtr<Gtk::Clipboard> refClipboard = Gtk::Clipboard::get();
692     Glib::ustring const text = refClipboard->wait_for_text();
694     if (!text.empty()) {
695         guint32 color = sp_svg_read_color(text.c_str(), 0x000000ff); // impossible value, as SVG color cannot have opacity
696         if (color == 0x000000ff) // failed to parse color string
697             return;
699         SPCSSAttr *css = sp_repr_css_attr_new ();
700         sp_repr_css_set_property (css, "fill", text.c_str());
701         sp_desktop_set_style (_desktop, css);
702         sp_repr_css_attr_unref (css);
703         sp_document_done (sp_desktop_document(_desktop), SP_VERB_DIALOG_FILL_STROKE,
704                       _("Paste fill"));
705     }
708 void SelectedStyle::on_stroke_paste() {
709     Glib::RefPtr<Gtk::Clipboard> refClipboard = Gtk::Clipboard::get();
710     Glib::ustring const text = refClipboard->wait_for_text();
712     if (!text.empty()) {
713         guint32 color = sp_svg_read_color(text.c_str(), 0x000000ff); // impossible value, as SVG color cannot have opacity
714         if (color == 0x000000ff) // failed to parse color string
715             return;
717         SPCSSAttr *css = sp_repr_css_attr_new ();
718         sp_repr_css_set_property (css, "stroke", text.c_str());
719         sp_desktop_set_style (_desktop, css);
720         sp_repr_css_attr_unref (css);
721         sp_document_done (sp_desktop_document(_desktop), SP_VERB_DIALOG_FILL_STROKE,
722                       _("Paste stroke"));
723     }
726 void SelectedStyle::on_fillstroke_swap() {
727     SPCSSAttr *css = sp_repr_css_attr_new ();
729     switch (_mode[SS_FILL]) {
730     case SS_NA:
731     case SS_MANY:
732         break;
733     case SS_NONE:
734         sp_repr_css_set_property (css, "stroke", "none");
735         break;
736     case SS_UNSET:
737         sp_repr_css_unset_property (css, "stroke");
738         break;
739     case SS_COLOR:
740         gchar c[64];
741         sp_svg_write_color (c, sizeof(c), _thisselected[SS_FILL]);
742         sp_repr_css_set_property (css, "stroke", c);
743         break;
744     case SS_LGRADIENT:
745     case SS_RGRADIENT:
746     case SS_PATTERN:
747         sp_repr_css_set_property (css, "stroke", _paintserver_id[SS_FILL].c_str());
748         break;
749     }
751     switch (_mode[SS_STROKE]) {
752     case SS_NA:
753     case SS_MANY:
754         break;
755     case SS_NONE:
756         sp_repr_css_set_property (css, "fill", "none");
757         break;
758     case SS_UNSET:
759         sp_repr_css_unset_property (css, "fill");
760         break;
761     case SS_COLOR:
762         gchar c[64];
763         sp_svg_write_color (c, sizeof(c), _thisselected[SS_STROKE]);
764         sp_repr_css_set_property (css, "fill", c);
765         break;
766     case SS_LGRADIENT:
767     case SS_RGRADIENT:
768     case SS_PATTERN:
769         sp_repr_css_set_property (css, "fill", _paintserver_id[SS_STROKE].c_str());
770         break;
771     }
773     sp_desktop_set_style (_desktop, css);
774     sp_repr_css_attr_unref (css);
775     sp_document_done (sp_desktop_document(_desktop), SP_VERB_DIALOG_FILL_STROKE,
776                       _("Swap fill and stroke"));
779 void SelectedStyle::on_fill_edit() {
780     if (Dialog::FillAndStroke *fs = get_fill_and_stroke_panel(_desktop))
781         fs->showPageFill();
784 void SelectedStyle::on_stroke_edit() {
785     if (Dialog::FillAndStroke *fs = get_fill_and_stroke_panel(_desktop))
786         fs->showPageStrokePaint();
789 bool
790 SelectedStyle::on_fill_click(GdkEventButton *event)
792     if (event->button == 1) { // click, open fill&stroke
794         if (Dialog::FillAndStroke *fs = get_fill_and_stroke_panel(_desktop))
795             fs->showPageFill();
797     } else if (event->button == 3) { // right-click, popup menu
798         _popup[SS_FILL].popup(event->button, event->time);
799     } else if (event->button == 2) { // middle click, toggle none/lastcolor
800         if (_mode[SS_FILL] == SS_NONE) {
801             on_fill_lastused();
802         } else {
803             on_fill_remove();
804         }
805     }
806     return true;
809 bool
810 SelectedStyle::on_stroke_click(GdkEventButton *event)
812     if (event->button == 1) { // click, open fill&stroke
813         if (Dialog::FillAndStroke *fs = get_fill_and_stroke_panel(_desktop))
814             fs->showPageStrokePaint();
815     } else if (event->button == 3) { // right-click, popup menu
816         _popup[SS_STROKE].popup(event->button, event->time);
817     } else if (event->button == 2) { // middle click, toggle none/lastcolor
818         if (_mode[SS_STROKE] == SS_NONE) {
819             on_stroke_lastused();
820         } else {
821             on_stroke_remove();
822         }
823     }
824     return true;
827 bool
828 SelectedStyle::on_sw_click(GdkEventButton *event)
830     if (event->button == 1) { // click, open fill&stroke
831         if (Dialog::FillAndStroke *fs = get_fill_and_stroke_panel(_desktop))
832             fs->showPageStrokeStyle();
833     } else if (event->button == 3) { // right-click, popup menu
834         _popup_sw.popup(event->button, event->time);
835     } else if (event->button == 2) { // middle click, toggle none/lastwidth?
836         //
837     }
838     return true;
841 bool
842 SelectedStyle::on_opacity_click(GdkEventButton *event)
844     if (event->button == 2) { // middle click
845         const char* opacity = _opacity_sb.get_value() < 50? "0.5" : (_opacity_sb.get_value() == 100? "0" : "1");
846         SPCSSAttr *css = sp_repr_css_attr_new ();
847         sp_repr_css_set_property (css, "opacity", opacity);
848         sp_desktop_set_style (_desktop, css);
849         sp_repr_css_attr_unref (css);
850         sp_document_done (sp_desktop_document (_desktop), SP_VERB_DIALOG_FILL_STROKE,
851                       _("Change opacity"));
852         return true;
853     }
855     return false;
858 void SelectedStyle::on_popup_px() {
859     _sw_unit = (SPUnit *) &(sp_unit_get_by_id(SP_UNIT_PX));
860     update();
862 void SelectedStyle::on_popup_pt() {
863     _sw_unit = (SPUnit *) &(sp_unit_get_by_id(SP_UNIT_PT));
864     update();
866 void SelectedStyle::on_popup_mm() {
867     _sw_unit = (SPUnit *) &(sp_unit_get_by_id(SP_UNIT_MM));
868     update();
871 void SelectedStyle::on_popup_preset(int i) {
872     SPCSSAttr *css = sp_repr_css_attr_new ();
873     gdouble w;
874     if (_sw_unit) {
875         w = sp_units_get_pixels (_sw_presets[i], *_sw_unit);
876     } else {
877         w = _sw_presets[i];
878     }
879     Inkscape::CSSOStringStream os;
880     os << w;
881     sp_repr_css_set_property (css, "stroke-width", os.str().c_str());
882     // FIXME: update dash patterns!
883     sp_desktop_set_style (_desktop, css, true);
884     sp_repr_css_attr_unref (css);
885     sp_document_done (sp_desktop_document(_desktop), SP_VERB_DIALOG_SWATCHES,
886                       _("Change stroke width"));
889 void
890 SelectedStyle::update()
892     if (_desktop == NULL)
893         return;
895     // create temporary style
896     SPStyle *query = sp_style_new (sp_desktop_document(_desktop));
898     for (int i = SS_FILL; i <= SS_STROKE; i++) {
899         Gtk::EventBox *place = (i == SS_FILL)? &_fill_place : &_stroke_place;
900         Gtk::EventBox *flag_place = (i == SS_FILL)? &_fill_flag_place : &_stroke_flag_place;
902         place->remove();
903         flag_place->remove();
905         _tooltips.unset_tip(*place);
906         _tooltips.unset_tip(*flag_place);
908         _mode[i] = SS_NA;
909         _paintserver_id[i].clear();
911         _popup_copy[i].set_sensitive(false);
913         // query style from desktop. This returns a result flag and fills query with the style of subselection, if any, or selection
914         int result = sp_desktop_query_style (_desktop, query,
915                                              (i == SS_FILL)? QUERY_STYLE_PROPERTY_FILL : QUERY_STYLE_PROPERTY_STROKE);
916         switch (result) {
917         case QUERY_STYLE_NOTHING:
918             place->add(_na[i]);
919             _tooltips.set_tip(*place, __na[i]);
920             _mode[i] = SS_NA;
921             if ( _dropEnabled[i] ) {
922                 gtk_drag_dest_unset( GTK_WIDGET((i==SS_FILL) ? _fill_place.gobj():_stroke_place.gobj()) );
923                 _dropEnabled[i] = false;
924             }
925             break;
926         case QUERY_STYLE_SINGLE:
927         case QUERY_STYLE_MULTIPLE_AVERAGED:
928         case QUERY_STYLE_MULTIPLE_SAME:
929             if ( !_dropEnabled[i] ) {
930                 gtk_drag_dest_set( GTK_WIDGET( (i==SS_FILL) ? _fill_place.gobj():_stroke_place.gobj()),
931                                    GTK_DEST_DEFAULT_ALL,
932                                    ui_drop_target_entries,
933                                    nui_drop_target_entries,
934                                    GdkDragAction(GDK_ACTION_COPY | GDK_ACTION_MOVE) );
935                 _dropEnabled[i] = true;
936             }
937             SPIPaint *paint;
938             if (i == SS_FILL) {
939                 paint = &(query->fill);
940             } else {
941                 paint = &(query->stroke);
942             }
943             if (paint->set && paint->isPaintserver()) {
944                 SPPaintServer *server = (i == SS_FILL)? SP_STYLE_FILL_SERVER (query) : SP_STYLE_STROKE_SERVER (query);
945                 if ( server ) {
946                     Inkscape::XML::Node *srepr = SP_OBJECT_REPR(server);
947                     _paintserver_id[i] += "url(#";
948                     _paintserver_id[i] += srepr->attribute("id");
949                     _paintserver_id[i] += ")";
951                     if (SP_IS_LINEARGRADIENT (server)) {
952                         SPGradient *vector = sp_gradient_get_vector(SP_GRADIENT(server), false);
953                         sp_gradient_image_set_gradient ((SPGradientImage *) _gradient_preview_l[i], vector);
954                         place->add(_gradient_box_l[i]);
955                         _tooltips.set_tip(*place, __lgradient[i]);
956                         _mode[i] = SS_LGRADIENT;
957                     } else if (SP_IS_RADIALGRADIENT (server)) {
958                         SPGradient *vector = sp_gradient_get_vector(SP_GRADIENT(server), false);
959                         sp_gradient_image_set_gradient ((SPGradientImage *) _gradient_preview_r[i], vector);
960                         place->add(_gradient_box_r[i]);
961                         _tooltips.set_tip(*place, __rgradient[i]);
962                         _mode[i] = SS_RGRADIENT;
963                     } else if (SP_IS_PATTERN (server)) {
964                         place->add(_pattern[i]);
965                         _tooltips.set_tip(*place, __pattern[i]);
966                         _mode[i] = SS_PATTERN;
967                     }
968                 } else {
969                     g_warning ("file %s: line %d: Unknown paint server", __FILE__, __LINE__);
970                 }
971             } else if (paint->set && paint->isColor()) {
972                 guint32 color = paint->value.color.toRGBA32(
973                                      SP_SCALE24_TO_FLOAT ((i == SS_FILL)? query->fill_opacity.value : query->stroke_opacity.value));
974                 _lastselected[i] = _thisselected[i];
975                 _thisselected[i] = color | 0xff; // only color, opacity === 1
976                 ((Inkscape::UI::Widget::ColorPreview*)_color_preview[i])->setRgba32 (color);
977                 _color_preview[i]->show_all();
978                 place->add(*_color_preview[i]);
979                 gchar c_string[64];
980                 g_snprintf (c_string, 64, "%06x/%.3g", color >> 8, SP_RGBA32_A_F(color));
981                 _tooltips.set_tip(*place, __color[i] + ": " + c_string + _(", drag to adjust"));
982                 _mode[i] = SS_COLOR;
983                 _popup_copy[i].set_sensitive(true);
985             } else if (paint->set && paint->isNone()) {
986                 place->add(_none[i]);
987                 _tooltips.set_tip(*place, __none[i]);
988                 _mode[i] = SS_NONE;
989             } else if (!paint->set) {
990                 place->add(_unset[i]);
991                 _tooltips.set_tip(*place, __unset[i]);
992                 _mode[i] = SS_UNSET;
993             }
994             if (result == QUERY_STYLE_MULTIPLE_AVERAGED) {
995                 flag_place->add(_averaged[i]);
996                 _tooltips.set_tip(*flag_place, __averaged[i]);
997             } else if (result == QUERY_STYLE_MULTIPLE_SAME) {
998                 flag_place->add(_multiple[i]);
999                 _tooltips.set_tip(*flag_place, __multiple[i]);
1000             }
1001             break;
1002         case QUERY_STYLE_MULTIPLE_DIFFERENT:
1003             place->add(_many[i]);
1004             _tooltips.set_tip(*place, __many[i]);
1005             _mode[i] = SS_MANY;
1006             break;
1007         default:
1008             break;
1009         }
1010     }
1012 // Now query opacity
1013     _tooltips.unset_tip(_opacity_place);
1014     _tooltips.unset_tip(_opacity_sb);
1016     int result = sp_desktop_query_style (_desktop, query, QUERY_STYLE_PROPERTY_MASTEROPACITY);
1018     switch (result) {
1019     case QUERY_STYLE_NOTHING:
1020         _tooltips.set_tip(_opacity_place, _("Nothing selected"));
1021         _tooltips.set_tip(_opacity_sb, _("Nothing selected"));
1022         _opacity_sb.set_sensitive(false);
1023         break;
1024     case QUERY_STYLE_SINGLE:
1025     case QUERY_STYLE_MULTIPLE_AVERAGED:
1026     case QUERY_STYLE_MULTIPLE_SAME:
1027         _tooltips.set_tip(_opacity_place, _("Opacity, %"));
1028         _tooltips.set_tip(_opacity_sb, _("Opacity, %"));
1029         if (_opacity_blocked) break;
1030         _opacity_blocked = true;
1031         _opacity_sb.set_sensitive(true);
1032         _opacity_adjustment.set_value(SP_SCALE24_TO_FLOAT(query->opacity.value) * 100);
1033         _opacity_blocked = false;
1034         break;
1035     }
1037 // Now query stroke_width
1038     int result_sw = sp_desktop_query_style (_desktop, query, QUERY_STYLE_PROPERTY_STROKEWIDTH);
1039     switch (result_sw) {
1040     case QUERY_STYLE_NOTHING:
1041         _stroke_width.set_markup("");
1042         current_stroke_width = 0;
1043         break;
1044     case QUERY_STYLE_SINGLE:
1045     case QUERY_STYLE_MULTIPLE_AVERAGED:
1046     case QUERY_STYLE_MULTIPLE_SAME:
1047     {
1048         double w;
1049         if (_sw_unit) {
1050             w = sp_pixels_get_units(query->stroke_width.computed, *_sw_unit);
1051         } else {
1052             w = query->stroke_width.computed;
1053         }
1054         current_stroke_width = w;
1056         {
1057             gchar *str = g_strdup_printf(" %.3g", w);
1058             _stroke_width.set_markup(str);
1059             g_free (str);
1060         }
1061         {
1062             gchar *str = g_strdup_printf(_("Stroke width: %.5g%s%s"),
1063                                          w,
1064                                          _sw_unit? sp_unit_get_abbreviation(_sw_unit) : "px",
1065                                          (result_sw == QUERY_STYLE_MULTIPLE_AVERAGED)?
1066                                          _(" (averaged)") : "");
1067             _tooltips.set_tip(_stroke_width_place, str);
1068             g_free (str);
1069         }
1070         break;
1071     }
1072     default:
1073         break;
1074     }
1076     sp_style_unref(query);
1079 void SelectedStyle::opacity_0(void) {_opacity_sb.set_value(0);}
1080 void SelectedStyle::opacity_025(void) {_opacity_sb.set_value(25);}
1081 void SelectedStyle::opacity_05(void) {_opacity_sb.set_value(50);}
1082 void SelectedStyle::opacity_075(void) {_opacity_sb.set_value(75);}
1083 void SelectedStyle::opacity_1(void) {_opacity_sb.set_value(100);}
1085 void SelectedStyle::on_opacity_menu (Gtk::Menu *menu) {
1087     Glib::ListHandle<Gtk::Widget *> children = menu->get_children();
1088     for (Glib::ListHandle<Gtk::Widget *>::iterator iter = children.begin(); iter != children.end(); iter++) {
1089         menu->remove(*(*iter));
1090     }
1092     {
1093         Gtk::MenuItem *item = new Gtk::MenuItem;
1094         item->add(*(new Gtk::Label(_("0 (transparent)"), 0, 0)));
1095         item->signal_activate().connect(sigc::mem_fun(*this, &SelectedStyle::opacity_0 ));
1096         menu->add(*item);
1097     }
1098     {
1099         Gtk::MenuItem *item = new Gtk::MenuItem;
1100         item->add(*(new Gtk::Label("25%", 0, 0)));
1101         item->signal_activate().connect(sigc::mem_fun(*this, &SelectedStyle::opacity_025 ));
1102         menu->add(*item);
1103     }
1104     {
1105         Gtk::MenuItem *item = new Gtk::MenuItem;
1106         item->add(*(new Gtk::Label("50%", 0, 0)));
1107         item->signal_activate().connect(sigc::mem_fun(*this, &SelectedStyle::opacity_05 ));
1108         menu->add(*item);
1109     }
1110     {
1111         Gtk::MenuItem *item = new Gtk::MenuItem;
1112         item->add(*(new Gtk::Label("75%", 0, 0)));
1113         item->signal_activate().connect(sigc::mem_fun(*this, &SelectedStyle::opacity_075 ));
1114         menu->add(*item);
1115     }
1116     {
1117         Gtk::MenuItem *item = new Gtk::MenuItem;
1118         item->add(*(new Gtk::Label(_("100% (opaque)"), 0, 0)));
1119         item->signal_activate().connect(sigc::mem_fun(*this, &SelectedStyle::opacity_1 ));
1120         menu->add(*item);
1121     }
1123     menu->show_all();
1126 void SelectedStyle::on_opacity_changed () {
1127     if (_opacity_blocked)
1128         return;
1129     _opacity_blocked = true;
1130     SPCSSAttr *css = sp_repr_css_attr_new ();
1131     Inkscape::CSSOStringStream os;
1132     os << CLAMP ((_opacity_adjustment.get_value() / 100), 0.0, 1.0);
1133     sp_repr_css_set_property (css, "opacity", os.str().c_str());
1134     // FIXME: workaround for GTK breakage: display interruptibility sometimes results in GTK
1135     // sending multiple value-changed events. As if when Inkscape interrupts redraw for main loop
1136     // iterations, GTK discovers that this callback hasn't finished yet, and for some weird reason
1137     // decides to add yet another value-changed event to the queue. Totally braindead if you ask
1138     // me. As a result, scrolling the spinbutton once results in runaway change until it hits 1.0
1139     // or 0.0. (And no, this is not a race with ::update, I checked that.)
1140     // Sigh. So we disable interruptibility while we're setting the new value.
1141     sp_canvas_force_full_redraw_after_interruptions(sp_desktop_canvas(_desktop), 0);
1142     sp_desktop_set_style (_desktop, css);
1143     sp_repr_css_attr_unref (css);
1144     sp_document_maybe_done (sp_desktop_document (_desktop), "fillstroke:opacity", SP_VERB_DIALOG_FILL_STROKE,
1145                       _("Change opacity"));
1146     // resume interruptibility
1147     sp_canvas_end_forced_full_redraws(sp_desktop_canvas(_desktop));
1148     spinbutton_defocus(GTK_OBJECT(_opacity_sb.gobj()));
1149     _opacity_blocked = false;
1152 /* =============================================  RotateableSwatch  */
1154 RotateableSwatch::RotateableSwatch(SelectedStyle *parent, guint mode) :
1155     fillstroke(mode),
1156     parent(parent),
1157     startcolor(0),
1158     startcolor_set(false),
1159     undokey("ssrot1"),
1160     cr(0),
1161     cr_set(false)
1162     
1166 RotateableSwatch::~RotateableSwatch() {
1169 double
1170 RotateableSwatch::color_adjust(float *hsl, double by, guint32 cc, guint modifier)
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;
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     }
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;
1333 /* =============================================  RotateableStrokeWidth  */
1335 RotateableStrokeWidth::RotateableStrokeWidth(SelectedStyle *parent) :
1336     parent(parent),
1337     startvalue(0),
1338     startvalue_set(false),
1339     undokey("swrot1"),
1340     cr(0),
1341     cr_set(false)
1345 RotateableStrokeWidth::~RotateableStrokeWidth() {
1348 double
1349 RotateableStrokeWidth::value_adjust(double current, double by, guint /*modifier*/, bool final)
1351     double newval;
1352     // by is -1..1
1353     if (by < 0) {
1354         // map negative 0..-1 to current..0
1355         newval = current * (1  +  by);
1356     } else {
1357         // map positive 0..1 to current..4*current
1358         newval = current * (1  +  by) * (1  +  by);
1359     }
1361     SPCSSAttr *css = sp_repr_css_attr_new ();
1362     if (final && newval < 1e-6) {
1363         // if dragged into zero and this is the final adjust on mouse release, delete stroke;
1364         // if it's not final, leave it a chance to increase again (which is not possible with "none")
1365         sp_repr_css_set_property (css, "stroke", "none");
1366     } else {
1367         Inkscape::CSSOStringStream os;
1368         os << newval;
1369         sp_repr_css_set_property (css, "stroke-width", os.str().c_str());
1370     }
1372     sp_desktop_set_style (parent->getDesktop(), css);
1373     sp_repr_css_attr_unref (css);
1374     return newval - current;
1377 void
1378 RotateableStrokeWidth::do_motion(double by, guint modifier) {
1380     // if this is the first motion after a mouse grab, remember the current width
1381     if (!startvalue_set) {
1382         startvalue = parent->current_stroke_width;
1383         // if it's 0, adjusting (which uses multiplication) will not be able to change it, so we
1384         // cheat and provide a non-zero value
1385         if (startvalue == 0)
1386             startvalue = 1;
1387         startvalue_set = true;
1388     }
1390     if (modifier == 3) { // Alt, do nothing
1391     } else {
1392         double diff = value_adjust(startvalue, by, modifier, false);
1393         sp_document_maybe_done (sp_desktop_document(parent->getDesktop()), undokey,
1394                                 SP_VERB_DIALOG_FILL_STROKE, (_("Adjust stroke width")));
1395         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);
1396     }
1399 void
1400 RotateableStrokeWidth::do_release(double by, guint modifier) {
1402     if (modifier == 3) { // do nothing
1404     } else {
1405         value_adjust(startvalue, by, modifier, true);
1406         startvalue_set = false;
1407         sp_document_maybe_done (sp_desktop_document(parent->getDesktop()), undokey,
1408                                 SP_VERB_DIALOG_FILL_STROKE, (_("Adjust stroke width")));
1409     }
1411     if (!strcmp(undokey, "swrot1")) {
1412         undokey = "swrot2";
1413     } else {
1414         undokey = "swrot1";
1415     }
1416     parent->getDesktop()->event_context->_message_context->clear();
1420 Dialog::FillAndStroke *get_fill_and_stroke_panel(SPDesktop *desktop)
1422     if (Dialog::PanelDialogBase *panel_dialog = 
1423         dynamic_cast<Dialog::PanelDialogBase *>(desktop->_dlg_mgr->getDialog("FillAndStroke"))) {
1424         try {
1425             Dialog::FillAndStroke &fill_and_stroke = 
1426                 dynamic_cast<Dialog::FillAndStroke &>(panel_dialog->getPanel());
1427             return &fill_and_stroke;
1428         } catch (std::exception e) { }
1429     }        
1431     return 0;
1434 } // namespace Widget
1435 } // namespace UI
1436 } // namespace Inkscape
1438 /*
1439   Local Variables:
1440   mode:c++
1441   c-file-style:"stroustrup"
1442   c-file-offsets:((innamespace . 0)(inline-open . 0))
1443   indent-tabs-mode:nil
1444   fill-column:99
1445   End:
1446 */
1447 // vim: filetype=c++:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 :