Code

Pot and Dutch translation update
[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 //TODO: warning: deprecated conversion from string constant to ‘gchar*’
90 //
91 //Turn out to be warnings that we should probably leave in place. The
92 // pointers/types used need to be read-only. So until we correct the using
93 // code, those warnings are actually desired. They say "Hey! Fix this". We
94 // definitely don't want to hide/ignore them. --JonCruz
95 static GtkTargetEntry ui_drop_target_entries [] = {
96     {"application/x-color", 0, APP_X_COLOR}
97 };
99 #define ENTRIES_SIZE(n) sizeof(n)/sizeof(n[0])
100 static guint nui_drop_target_entries = ENTRIES_SIZE(ui_drop_target_entries);
102 /* convenience function */
103 static Dialog::FillAndStroke *get_fill_and_stroke_panel(SPDesktop *desktop);
105 SelectedStyle::SelectedStyle(bool /*layout*/)
106     :
107       current_stroke_width(0),
109       _desktop (NULL),
111       _table(2, 6),
112       _fill_label (_("Fill:")),
113       _stroke_label (_("Stroke:")),
114       _opacity_label (_("O:")),
116       _fill_place(this, SS_FILL),
117       _stroke_place(this, SS_STROKE),
119       _fill_flag_place (),
120       _stroke_flag_place (),
122       _opacity_place (),
123       _opacity_adjustment (100, 0.0, 100, 1.0, 10.0),
124       _opacity_sb (0.02, 0),
126       _stroke (),
127       _stroke_width_place(this),
128       _stroke_width (""),
130       _opacity_blocked (false),
132       _popup_px(_sw_group),
133       _popup_pt(_sw_group),
134       _popup_mm(_sw_group),
136       _sw_unit(NULL),
138       _tooltips ()
141     _drop[0] = _drop[1] = 0;
142     _dropEnabled[0] = _dropEnabled[1] = false;
144     _fill_label.set_alignment(0.0, 0.5);
145     _fill_label.set_padding(0, 0);
146     _stroke_label.set_alignment(0.0, 0.5);
147     _stroke_label.set_padding(0, 0);
148     _opacity_label.set_alignment(0.0, 0.5);
149     _opacity_label.set_padding(0, 0);
151     _table.set_col_spacings (2);
152     _table.set_row_spacings (0);
154     for (int i = SS_FILL; i <= SS_STROKE; i++) {
156         _na[i].set_markup (_("N/A"));
157         sp_set_font_size_smaller (GTK_WIDGET(_na[i].gobj()));
158         _na[i].show_all();
159         __na[i] = (_("Nothing selected"));
161         _none[i].set_markup (_("<i>None</i>"));
162         sp_set_font_size_smaller (GTK_WIDGET(_none[i].gobj()));
163         _none[i].show_all();
164         __none[i] = (i == SS_FILL)? (_("No fill")) : (_("No stroke"));
166         _pattern[i].set_markup (_("Pattern"));
167         sp_set_font_size_smaller (GTK_WIDGET(_pattern[i].gobj()));
168         _pattern[i].show_all();
169         __pattern[i] = (i == SS_FILL)? (_("Pattern fill")) : (_("Pattern stroke"));
171         _lgradient[i].set_markup (_("<b>L</b>"));
172         sp_set_font_size_smaller (GTK_WIDGET(_lgradient[i].gobj()));
173         _lgradient[i].show_all();
174         __lgradient[i] = (i == SS_FILL)? (_("Linear gradient fill")) : (_("Linear gradient stroke"));
176         _gradient_preview_l[i] =  GTK_WIDGET(sp_gradient_image_new (NULL));
177         _gradient_box_l[i].pack_start(_lgradient[i]);
178         _gradient_box_l[i].pack_start(*(Glib::wrap(_gradient_preview_l[i])));
179         _gradient_box_l[i].show_all();
181         _rgradient[i].set_markup (_("<b>R</b>"));
182         sp_set_font_size_smaller (GTK_WIDGET(_rgradient[i].gobj()));
183         _rgradient[i].show_all();
184         __rgradient[i] = (i == SS_FILL)? (_("Radial gradient fill")) : (_("Radial gradient stroke"));
186         _gradient_preview_r[i] = GTK_WIDGET(sp_gradient_image_new (NULL));
187         _gradient_box_r[i].pack_start(_rgradient[i]);
188         _gradient_box_r[i].pack_start(*(Glib::wrap(_gradient_preview_r[i])));
189         _gradient_box_r[i].show_all();
191         _many[i].set_markup (_("Different"));
192         sp_set_font_size_smaller (GTK_WIDGET(_many[i].gobj()));
193         _many[i].show_all();
194         __many[i] = (i == SS_FILL)? (_("Different fills")) : (_("Different strokes"));
196         _unset[i].set_markup (_("<b>Unset</b>"));
197         sp_set_font_size_smaller (GTK_WIDGET(_unset[i].gobj()));
198         _unset[i].show_all();
199         __unset[i] = (i == SS_FILL)? (_("Unset fill")) : (_("Unset stroke"));
201         _color_preview[i] = new Inkscape::UI::Widget::ColorPreview (0);
202         __color[i] = (i == SS_FILL)? (_("Flat color fill")) : (_("Flat color stroke"));
204         // TRANSLATOR COMMENT: A means "Averaged"
205         _averaged[i].set_markup (_("<b>a</b>"));
206         sp_set_font_size_smaller (GTK_WIDGET(_averaged[i].gobj()));
207         _averaged[i].show_all();
208         __averaged[i] = (i == SS_FILL)? (_("Fill is averaged over selected objects")) : (_("Stroke is averaged over selected objects"));
210         // TRANSLATOR COMMENT: M means "Multiple"
211         _multiple[i].set_markup (_("<b>m</b>"));
212         sp_set_font_size_smaller (GTK_WIDGET(_multiple[i].gobj()));
213         _multiple[i].show_all();
214         __multiple[i] = (i == SS_FILL)? (_("Multiple selected objects have the same fill")) : (_("Multiple selected objects have the same stroke"));
216         _popup_edit[i].add(*(new Gtk::Label((i == SS_FILL)? _("Edit fill...") : _("Edit stroke..."), 0.0, 0.5)));
217         _popup_edit[i].signal_activate().connect(sigc::mem_fun(*this,
218                                (i == SS_FILL)? &SelectedStyle::on_fill_edit : &SelectedStyle::on_stroke_edit ));
220         _popup_lastused[i].add(*(new Gtk::Label(_("Last set color"), 0.0, 0.5)));
221         _popup_lastused[i].signal_activate().connect(sigc::mem_fun(*this,
222                                (i == SS_FILL)? &SelectedStyle::on_fill_lastused : &SelectedStyle::on_stroke_lastused ));
224         _popup_lastselected[i].add(*(new Gtk::Label(_("Last selected color"), 0.0, 0.5)));
225         _popup_lastselected[i].signal_activate().connect(sigc::mem_fun(*this,
226                                (i == SS_FILL)? &SelectedStyle::on_fill_lastselected : &SelectedStyle::on_stroke_lastselected ));
228         _popup_invert[i].add(*(new Gtk::Label(_("Invert"), 0.0, 0.5)));
229         _popup_invert[i].signal_activate().connect(sigc::mem_fun(*this,
230                                (i == SS_FILL)? &SelectedStyle::on_fill_invert : &SelectedStyle::on_stroke_invert ));
232         _popup_white[i].add(*(new Gtk::Label(_("White"), 0.0, 0.5)));
233         _popup_white[i].signal_activate().connect(sigc::mem_fun(*this,
234                                (i == SS_FILL)? &SelectedStyle::on_fill_white : &SelectedStyle::on_stroke_white ));
236         _popup_black[i].add(*(new Gtk::Label(_("Black"), 0.0, 0.5)));
237         _popup_black[i].signal_activate().connect(sigc::mem_fun(*this,
238                                (i == SS_FILL)? &SelectedStyle::on_fill_black : &SelectedStyle::on_stroke_black ));
240         _popup_copy[i].add(*(new Gtk::Label(_("Copy color"), 0.0, 0.5)));
241         _popup_copy[i].signal_activate().connect(sigc::mem_fun(*this,
242                                (i == SS_FILL)? &SelectedStyle::on_fill_copy : &SelectedStyle::on_stroke_copy ));
244         _popup_paste[i].add(*(new Gtk::Label(_("Paste color"), 0.0, 0.5)));
245         _popup_paste[i].signal_activate().connect(sigc::mem_fun(*this,
246                                (i == SS_FILL)? &SelectedStyle::on_fill_paste : &SelectedStyle::on_stroke_paste ));
248         _popup_swap[i].add(*(new Gtk::Label(_("Swap fill and stroke"), 0.0, 0.5)));
249         _popup_swap[i].signal_activate().connect(sigc::mem_fun(*this,
250                                &SelectedStyle::on_fillstroke_swap));
252         _popup_opaque[i].add(*(new Gtk::Label((i == SS_FILL)? _("Make fill opaque") : _("Make stroke opaque"), 0.0, 0.5)));
253         _popup_opaque[i].signal_activate().connect(sigc::mem_fun(*this,
254                                (i == SS_FILL)? &SelectedStyle::on_fill_opaque : &SelectedStyle::on_stroke_opaque ));
256         //TRANSLATORS COMMENT: unset is a verb here
257         _popup_unset[i].add(*(new Gtk::Label((i == SS_FILL)? _("Unset fill") : _("Unset stroke"), 0.0, 0.5)));
258         _popup_unset[i].signal_activate().connect(sigc::mem_fun(*this,
259                                (i == SS_FILL)? &SelectedStyle::on_fill_unset : &SelectedStyle::on_stroke_unset ));
261         _popup_remove[i].add(*(new Gtk::Label((i == SS_FILL)? _("Remove fill") : _("Remove stroke"), 0.0, 0.5)));
262         _popup_remove[i].signal_activate().connect(sigc::mem_fun(*this,
263                                (i == SS_FILL)? &SelectedStyle::on_fill_remove : &SelectedStyle::on_stroke_remove ));
265         _popup[i].attach(_popup_edit[i], 0,1, 0,1);
266           _popup[i].attach(*(new Gtk::SeparatorMenuItem()), 0,1, 1,2);
267         _popup[i].attach(_popup_lastused[i], 0,1, 2,3);
268         _popup[i].attach(_popup_lastselected[i], 0,1, 3,4);
269           _popup[i].attach(*(new Gtk::SeparatorMenuItem()), 0,1, 4,5);
270         _popup[i].attach(_popup_invert[i], 0,1, 5,6);
271           _popup[i].attach(*(new Gtk::SeparatorMenuItem()), 0,1, 6,7);
272         _popup[i].attach(_popup_white[i], 0,1, 7,8);
273         _popup[i].attach(_popup_black[i], 0,1, 8,9);
274           _popup[i].attach(*(new Gtk::SeparatorMenuItem()), 0,1, 9,10);
275         _popup[i].attach(_popup_copy[i], 0,1, 10,11);
276         _popup_copy[i].set_sensitive(false);
277         _popup[i].attach(_popup_paste[i], 0,1, 11,12);
278         _popup[i].attach(_popup_swap[i], 0,1, 12,13);
279           _popup[i].attach(*(new Gtk::SeparatorMenuItem()), 0,1, 13,14);
280         _popup[i].attach(_popup_opaque[i], 0,1, 14,15);
281         _popup[i].attach(_popup_unset[i], 0,1, 15,16);
282         _popup[i].attach(_popup_remove[i], 0,1, 16,17);
283         _popup[i].show_all();
285         _mode[i] = SS_NA;
286     }
288     {
289         _popup_px.add(*(new Gtk::Label(_("px"), 0.0, 0.5)));
290         _popup_px.signal_activate().connect(sigc::mem_fun(*this, &SelectedStyle::on_popup_px));
291         _popup_sw.attach(_popup_px, 0,1, 0,1);
293         _popup_pt.add(*(new Gtk::Label(_("pt"), 0.0, 0.5)));
294         _popup_pt.signal_activate().connect(sigc::mem_fun(*this, &SelectedStyle::on_popup_pt));
295         _popup_sw.attach(_popup_pt, 0,1, 1,2);
297         _popup_mm.add(*(new Gtk::Label(_("mm"), 0.0, 0.5)));
298         _popup_mm.signal_activate().connect(sigc::mem_fun(*this, &SelectedStyle::on_popup_mm));
299         _popup_sw.attach(_popup_mm, 0,1, 2,3);
301         _popup_sw.attach(*(new Gtk::SeparatorMenuItem()), 0,1, 3,4);
303         for (guint i = 0; i < G_N_ELEMENTS(_sw_presets_str); ++i) {
304             Gtk::MenuItem *mi = Gtk::manage(new Gtk::MenuItem());
305             mi->add(*(new Gtk::Label(_sw_presets_str[i], 0.0, 0.5)));
306             mi->signal_activate().connect(sigc::bind<int>(sigc::mem_fun(*this, &SelectedStyle::on_popup_preset), i));
307             _popup_sw.attach(*mi, 0,1, 4+i, 5+i);
308         }
310         guint i = G_N_ELEMENTS(_sw_presets_str) + 5;
312         _popup_sw.attach(*(new Gtk::SeparatorMenuItem()), 0,1, i,i+1);
314         _popup_sw_remove.add(*(new Gtk::Label(_("Remove"), 0.0, 0.5)));
315         _popup_sw_remove.signal_activate().connect(sigc::mem_fun(*this, &SelectedStyle::on_stroke_remove));
316         _popup_sw.attach(_popup_sw_remove, 0,1, i+1,i+2);
318         _popup_sw.show_all();
319     }
321     _fill_place.signal_button_release_event().connect(sigc::mem_fun(*this, &SelectedStyle::on_fill_click));
322     _stroke_place.signal_button_release_event().connect(sigc::mem_fun(*this, &SelectedStyle::on_stroke_click));
323     _opacity_place.signal_button_press_event().connect(sigc::mem_fun(*this, &SelectedStyle::on_opacity_click));
324     _stroke_width_place.signal_button_press_event().connect(sigc::mem_fun(*this, &SelectedStyle::on_sw_click));
326     _opacity_sb.signal_populate_popup().connect(sigc::mem_fun(*this, &SelectedStyle::on_opacity_menu));
327     _opacity_sb.signal_value_changed().connect(sigc::mem_fun(*this, &SelectedStyle::on_opacity_changed));
329     _fill_place.add(_na[SS_FILL]);
330     _tooltips.set_tip(_fill_place, __na[SS_FILL]);
332     _stroke_place.add(_na[SS_STROKE]);
333     _tooltips.set_tip(_stroke_place, __na[SS_STROKE]);
335     _stroke.pack_start(_stroke_place);
336     _stroke_width_place.add(_stroke_width);
337     _stroke.pack_start(_stroke_width_place, Gtk::PACK_SHRINK);
339     _opacity_sb.set_adjustment(_opacity_adjustment);
340     sp_set_font_size_smaller (GTK_WIDGET(_opacity_sb.gobj()));
341     _opacity_sb.set_size_request (SELECTED_STYLE_SB_WIDTH, -1);
342     _opacity_sb.set_sensitive (false);
344     _table.attach(_fill_label, 0,1, 0,1, Gtk::FILL, Gtk::SHRINK);
345     _table.attach(_stroke_label, 0,1, 1,2, Gtk::FILL, Gtk::SHRINK);
347     _table.attach(_fill_flag_place, 1,2, 0,1, Gtk::SHRINK, Gtk::SHRINK);
348     _table.attach(_stroke_flag_place, 1,2, 1,2, Gtk::SHRINK, Gtk::SHRINK);
350     _table.attach(_fill_place, 2,3, 0,1);
351     _table.attach(_stroke, 2,3, 1,2);
353     _opacity_place.add(_opacity_label);
354     _table.attach(_opacity_place, 4,5, 0,2, Gtk::SHRINK, Gtk::SHRINK);
355     _table.attach(_opacity_sb, 5,6, 0,2, Gtk::SHRINK, Gtk::SHRINK);
357     pack_start(_table, true, true, 2);
359     set_size_request (SELECTED_STYLE_WIDTH, -1);
361     sp_set_font_size_smaller (GTK_WIDGET(_opacity_label.gobj()));
362     sp_set_font_size_smaller (GTK_WIDGET(_opacity_sb.gobj()));
363     sp_set_font_size_smaller (GTK_WIDGET(_fill_place.gobj()));
364     sp_set_font_size_smaller (GTK_WIDGET(_fill_flag_place.gobj()));
365     sp_set_font_size_smaller (GTK_WIDGET(_stroke_place.gobj()));
366     sp_set_font_size_smaller (GTK_WIDGET(_stroke_flag_place.gobj()));
367     sp_set_font_size_smaller (GTK_WIDGET(_stroke_width.gobj()));
368     sp_set_font_size_smaller (GTK_WIDGET(_fill_label.gobj()));
369     sp_set_font_size_smaller (GTK_WIDGET(_stroke_label.gobj()));
371     _drop[SS_FILL] = new DropTracker();
372     ((DropTracker*)_drop[SS_FILL])->parent = this;
373     ((DropTracker*)_drop[SS_FILL])->item = SS_FILL;
375     _drop[SS_STROKE] = new DropTracker();
376     ((DropTracker*)_drop[SS_STROKE])->parent = this;
377     ((DropTracker*)_drop[SS_STROKE])->item = SS_STROKE;
379     g_signal_connect(_stroke_place.gobj(),
380                      "drag_data_received",
381                      G_CALLBACK(dragDataReceived),
382                      _drop[SS_STROKE]);
384     g_signal_connect(_fill_place.gobj(),
385                      "drag_data_received",
386                      G_CALLBACK(dragDataReceived),
387                      _drop[SS_FILL]);
390 SelectedStyle::~SelectedStyle()
392     selection_changed_connection->disconnect();
393     delete selection_changed_connection;
394     selection_modified_connection->disconnect();
395     delete selection_modified_connection;
396     subselection_changed_connection->disconnect();
397     delete subselection_changed_connection;
399     for (int i = SS_FILL; i <= SS_STROKE; i++) {
400         delete _color_preview[i];
401         // FIXME: do we need this? the destroy methods are not exported
402         //sp_gradient_image_destroy(GTK_OBJECT(_gradient_preview_l[i]));
403         //sp_gradient_image_destroy(GTK_OBJECT(_gradient_preview_r[i]));
404     }
406     delete (DropTracker*)_drop[SS_FILL];
407     delete (DropTracker*)_drop[SS_STROKE];
410 void
411 SelectedStyle::setDesktop(SPDesktop *desktop)
413     _desktop = desktop;
414     gtk_object_set_data (GTK_OBJECT(_opacity_sb.gobj()), "dtw", _desktop->canvas);
416     Inkscape::Selection *selection = sp_desktop_selection (desktop);
418     selection_changed_connection = new sigc::connection (selection->connectChanged(
419         sigc::bind (
420             sigc::ptr_fun(&ss_selection_changed),
421             this )
422     ));
423     selection_modified_connection = new sigc::connection (selection->connectModified(
424         sigc::bind (
425             sigc::ptr_fun(&ss_selection_modified),
426             this )
427     ));
428     subselection_changed_connection = new sigc::connection (desktop->connectToolSubselectionChanged(
429         sigc::bind (
430             sigc::ptr_fun(&ss_subselection_changed),
431             this )
432     ));
434     //_sw_unit = (SPUnit *) sp_desktop_namedview(desktop)->doc_units;
437 void SelectedStyle::dragDataReceived( GtkWidget */*widget*/,
438                                       GdkDragContext */*drag_context*/,
439                                       gint /*x*/, gint /*y*/,
440                                       GtkSelectionData *data,
441                                       guint /*info*/,
442                                       guint /*event_time*/,
443                                       gpointer user_data )
445     DropTracker* tracker = (DropTracker*)user_data;
447     switch ( (int)tracker->item ) {
448         case SS_FILL:
449         case SS_STROKE:
450         {
451             if ( data->length == 8 ) {
452                 gchar c[64];
453                 // Careful about endian issues.
454                 guint16* dataVals = (guint16*)data->data;
455                 sp_svg_write_color( c, sizeof(c),
456                                     SP_RGBA32_U_COMPOSE(
457                                         0x0ff & (dataVals[0] >> 8),
458                                         0x0ff & (dataVals[1] >> 8),
459                                         0x0ff & (dataVals[2] >> 8),
460                                         0xff // can't have transparency in the color itself
461                                         //0x0ff & (data->data[3] >> 8),
462                                         ));
463                 SPCSSAttr *css = sp_repr_css_attr_new();
464                 sp_repr_css_set_property( css, (tracker->item == SS_FILL) ? "fill":"stroke", c );
465                 sp_desktop_set_style( tracker->parent->_desktop, css );
466                 sp_repr_css_attr_unref( css );
467                 sp_document_done( sp_desktop_document(tracker->parent->_desktop) , SP_VERB_NONE,
468                                   _("Drop color"));
469             }
470         }
471         break;
472     }
475 void SelectedStyle::on_fill_remove() {
476     SPCSSAttr *css = sp_repr_css_attr_new ();
477     sp_repr_css_set_property (css, "fill", "none");
478     sp_desktop_set_style (_desktop, css, true, true);
479     sp_repr_css_attr_unref (css);
480     sp_document_done (sp_desktop_document(_desktop), SP_VERB_DIALOG_FILL_STROKE,
481                       _("Remove fill"));
484 void SelectedStyle::on_stroke_remove() {
485     SPCSSAttr *css = sp_repr_css_attr_new ();
486     sp_repr_css_set_property (css, "stroke", "none");
487     sp_desktop_set_style (_desktop, css, true, true);
488     sp_repr_css_attr_unref (css);
489     sp_document_done (sp_desktop_document(_desktop), SP_VERB_DIALOG_FILL_STROKE,
490                       _("Remove stroke"));
493 void SelectedStyle::on_fill_unset() {
494     SPCSSAttr *css = sp_repr_css_attr_new ();
495     sp_repr_css_unset_property (css, "fill");
496     sp_desktop_set_style (_desktop, css, true, true);
497     sp_repr_css_attr_unref (css);
498     sp_document_done (sp_desktop_document(_desktop), SP_VERB_DIALOG_FILL_STROKE,
499                       _("Unset fill"));
502 void SelectedStyle::on_stroke_unset() {
503     SPCSSAttr *css = sp_repr_css_attr_new ();
504     sp_repr_css_unset_property (css, "stroke");
505     sp_repr_css_unset_property (css, "stroke-opacity");
506     sp_repr_css_unset_property (css, "stroke-width");
507     sp_repr_css_unset_property (css, "stroke-miterlimit");
508     sp_repr_css_unset_property (css, "stroke-linejoin");
509     sp_repr_css_unset_property (css, "stroke-linecap");
510     sp_repr_css_unset_property (css, "stroke-dashoffset");
511     sp_repr_css_unset_property (css, "stroke-dasharray");
512     sp_desktop_set_style (_desktop, css, true, true);
513     sp_repr_css_attr_unref (css);
514     sp_document_done (sp_desktop_document(_desktop), SP_VERB_DIALOG_FILL_STROKE,
515                       _("Unset stroke"));
518 void SelectedStyle::on_fill_opaque() {
519     SPCSSAttr *css = sp_repr_css_attr_new ();
520     sp_repr_css_set_property (css, "fill-opacity", "1");
521     sp_desktop_set_style (_desktop, css, true);
522     sp_repr_css_attr_unref (css);
523     sp_document_done (sp_desktop_document(_desktop), SP_VERB_DIALOG_FILL_STROKE,
524                       _("Make fill opaque"));
527 void SelectedStyle::on_stroke_opaque() {
528     SPCSSAttr *css = sp_repr_css_attr_new ();
529     sp_repr_css_set_property (css, "stroke-opacity", "1");
530     sp_desktop_set_style (_desktop, css, true);
531     sp_repr_css_attr_unref (css);
532     sp_document_done (sp_desktop_document(_desktop), SP_VERB_DIALOG_FILL_STROKE,
533                       _("Make fill opaque"));
536 void SelectedStyle::on_fill_lastused() {
537     SPCSSAttr *css = sp_repr_css_attr_new ();
538     guint32 color = sp_desktop_get_color(_desktop, true);
539     gchar c[64];
540     sp_svg_write_color (c, sizeof(c), color);
541     sp_repr_css_set_property (css, "fill", c);
542     sp_desktop_set_style (_desktop, css);
543     sp_repr_css_attr_unref (css);
544     sp_document_done (sp_desktop_document(_desktop), SP_VERB_DIALOG_FILL_STROKE,
545                       _("Apply last set color to fill"));
548 void SelectedStyle::on_stroke_lastused() {
549     SPCSSAttr *css = sp_repr_css_attr_new ();
550     guint32 color = sp_desktop_get_color(_desktop, false);
551     gchar c[64];
552     sp_svg_write_color (c, sizeof(c), color);
553     sp_repr_css_set_property (css, "stroke", c);
554     sp_desktop_set_style (_desktop, css);
555     sp_repr_css_attr_unref (css);
556     sp_document_done (sp_desktop_document(_desktop), SP_VERB_DIALOG_FILL_STROKE,
557                       _("Apply last set color to stroke"));
560 void SelectedStyle::on_fill_lastselected() {
561     SPCSSAttr *css = sp_repr_css_attr_new ();
562     gchar c[64];
563     sp_svg_write_color (c, sizeof(c), _lastselected[SS_FILL]);
564     sp_repr_css_set_property (css, "fill", c);
565     sp_desktop_set_style (_desktop, css);
566     sp_repr_css_attr_unref (css);
567     sp_document_done (sp_desktop_document(_desktop), SP_VERB_DIALOG_FILL_STROKE,
568                       _("Apply last selected color to fill"));
571 void SelectedStyle::on_stroke_lastselected() {
572     SPCSSAttr *css = sp_repr_css_attr_new ();
573     gchar c[64];
574     sp_svg_write_color (c, sizeof(c), _lastselected[SS_STROKE]);
575     sp_repr_css_set_property (css, "stroke", c);
576     sp_desktop_set_style (_desktop, css);
577     sp_repr_css_attr_unref (css);
578     sp_document_done (sp_desktop_document(_desktop), SP_VERB_DIALOG_FILL_STROKE,
579                       _("Apply last selected color to stroke"));
582 void SelectedStyle::on_fill_invert() {
583     SPCSSAttr *css = sp_repr_css_attr_new ();
584     guint32 color = _thisselected[SS_FILL];
585     gchar c[64];
586     if (_mode[SS_FILL] != SS_COLOR) return;
587     sp_svg_write_color (c, sizeof(c),
588         SP_RGBA32_U_COMPOSE(
589                 (255 - SP_RGBA32_R_U(color)),
590                 (255 - SP_RGBA32_G_U(color)),
591                 (255 - SP_RGBA32_B_U(color)),
592                 SP_RGBA32_A_U(color)
593         )
594     );
595     sp_repr_css_set_property (css, "fill", c);
596     sp_desktop_set_style (_desktop, css);
597     sp_repr_css_attr_unref (css);
598     sp_document_done (sp_desktop_document(_desktop), SP_VERB_DIALOG_FILL_STROKE,
599                       _("Invert fill"));
602 void SelectedStyle::on_stroke_invert() {
603     SPCSSAttr *css = sp_repr_css_attr_new ();
604     guint32 color = _thisselected[SS_STROKE];
605     gchar c[64];
606     if (_mode[SS_STROKE] != SS_COLOR) return;
607     sp_svg_write_color (c, sizeof(c),
608         SP_RGBA32_U_COMPOSE(
609                 (255 - SP_RGBA32_R_U(color)),
610                 (255 - SP_RGBA32_G_U(color)),
611                 (255 - SP_RGBA32_B_U(color)),
612                 SP_RGBA32_A_U(color)
613         )
614     );
615     sp_repr_css_set_property (css, "stroke", c);
616     sp_desktop_set_style (_desktop, css);
617     sp_repr_css_attr_unref (css);
618     sp_document_done (sp_desktop_document(_desktop), SP_VERB_DIALOG_FILL_STROKE,
619                       _("Invert stroke"));
622 void SelectedStyle::on_fill_white() {
623     SPCSSAttr *css = sp_repr_css_attr_new ();
624     gchar c[64];
625     sp_svg_write_color (c, sizeof(c), 0xffffffff);
626     sp_repr_css_set_property (css, "fill", c);
627     sp_repr_css_set_property (css, "fill-opacity", "1");
628     sp_desktop_set_style (_desktop, css);
629     sp_repr_css_attr_unref (css);
630     sp_document_done (sp_desktop_document(_desktop), SP_VERB_DIALOG_FILL_STROKE,
631                       _("White fill"));
634 void SelectedStyle::on_stroke_white() {
635     SPCSSAttr *css = sp_repr_css_attr_new ();
636     gchar c[64];
637     sp_svg_write_color (c, sizeof(c), 0xffffffff);
638     sp_repr_css_set_property (css, "stroke", c);
639     sp_repr_css_set_property (css, "stroke-opacity", "1");
640     sp_desktop_set_style (_desktop, css);
641     sp_repr_css_attr_unref (css);
642     sp_document_done (sp_desktop_document(_desktop), SP_VERB_DIALOG_FILL_STROKE,
643                       _("White stroke"));
646 void SelectedStyle::on_fill_black() {
647     SPCSSAttr *css = sp_repr_css_attr_new ();
648     gchar c[64];
649     sp_svg_write_color (c, sizeof(c), 0x000000ff);
650     sp_repr_css_set_property (css, "fill", c);
651     sp_repr_css_set_property (css, "fill-opacity", "1.0");
652     sp_desktop_set_style (_desktop, css);
653     sp_repr_css_attr_unref (css);
654     sp_document_done (sp_desktop_document(_desktop), SP_VERB_DIALOG_FILL_STROKE,
655                       _("Black fill"));
658 void SelectedStyle::on_stroke_black() {
659     SPCSSAttr *css = sp_repr_css_attr_new ();
660     gchar c[64];
661     sp_svg_write_color (c, sizeof(c), 0x000000ff);
662     sp_repr_css_set_property (css, "stroke", c);
663     sp_repr_css_set_property (css, "stroke-opacity", "1.0");
664     sp_desktop_set_style (_desktop, css);
665     sp_repr_css_attr_unref (css);
666     sp_document_done (sp_desktop_document(_desktop), SP_VERB_DIALOG_FILL_STROKE,
667                       _("Black stroke"));
670 void SelectedStyle::on_fill_copy() {
671     if (_mode[SS_FILL] == SS_COLOR) {
672         gchar c[64];
673         sp_svg_write_color (c, sizeof(c), _thisselected[SS_FILL]);
674         Glib::ustring text;
675         text += c;
676         if (!text.empty()) {
677             Glib::RefPtr<Gtk::Clipboard> refClipboard = Gtk::Clipboard::get();
678             refClipboard->set_text(text);
679         }
680     }
683 void SelectedStyle::on_stroke_copy() {
684     if (_mode[SS_STROKE] == SS_COLOR) {
685         gchar c[64];
686         sp_svg_write_color (c, sizeof(c), _thisselected[SS_STROKE]);
687         Glib::ustring text;
688         text += c;
689         if (!text.empty()) {
690             Glib::RefPtr<Gtk::Clipboard> refClipboard = Gtk::Clipboard::get();
691             refClipboard->set_text(text);
692         }
693     }
696 void SelectedStyle::on_fill_paste() {
697     Glib::RefPtr<Gtk::Clipboard> refClipboard = Gtk::Clipboard::get();
698     Glib::ustring const text = refClipboard->wait_for_text();
700     if (!text.empty()) {
701         guint32 color = sp_svg_read_color(text.c_str(), 0x000000ff); // impossible value, as SVG color cannot have opacity
702         if (color == 0x000000ff) // failed to parse color string
703             return;
705         SPCSSAttr *css = sp_repr_css_attr_new ();
706         sp_repr_css_set_property (css, "fill", text.c_str());
707         sp_desktop_set_style (_desktop, css);
708         sp_repr_css_attr_unref (css);
709         sp_document_done (sp_desktop_document(_desktop), SP_VERB_DIALOG_FILL_STROKE,
710                       _("Paste fill"));
711     }
714 void SelectedStyle::on_stroke_paste() {
715     Glib::RefPtr<Gtk::Clipboard> refClipboard = Gtk::Clipboard::get();
716     Glib::ustring const text = refClipboard->wait_for_text();
718     if (!text.empty()) {
719         guint32 color = sp_svg_read_color(text.c_str(), 0x000000ff); // impossible value, as SVG color cannot have opacity
720         if (color == 0x000000ff) // failed to parse color string
721             return;
723         SPCSSAttr *css = sp_repr_css_attr_new ();
724         sp_repr_css_set_property (css, "stroke", text.c_str());
725         sp_desktop_set_style (_desktop, css);
726         sp_repr_css_attr_unref (css);
727         sp_document_done (sp_desktop_document(_desktop), SP_VERB_DIALOG_FILL_STROKE,
728                       _("Paste stroke"));
729     }
732 void SelectedStyle::on_fillstroke_swap() {
733     SPCSSAttr *css = sp_repr_css_attr_new ();
735     switch (_mode[SS_FILL]) {
736     case SS_NA:
737     case SS_MANY:
738         break;
739     case SS_NONE:
740         sp_repr_css_set_property (css, "stroke", "none");
741         break;
742     case SS_UNSET:
743         sp_repr_css_unset_property (css, "stroke");
744         break;
745     case SS_COLOR:
746         gchar c[64];
747         sp_svg_write_color (c, sizeof(c), _thisselected[SS_FILL]);
748         sp_repr_css_set_property (css, "stroke", c);
749         break;
750     case SS_LGRADIENT:
751     case SS_RGRADIENT:
752     case SS_PATTERN:
753         sp_repr_css_set_property (css, "stroke", _paintserver_id[SS_FILL].c_str());
754         break;
755     }
757     switch (_mode[SS_STROKE]) {
758     case SS_NA:
759     case SS_MANY:
760         break;
761     case SS_NONE:
762         sp_repr_css_set_property (css, "fill", "none");
763         break;
764     case SS_UNSET:
765         sp_repr_css_unset_property (css, "fill");
766         break;
767     case SS_COLOR:
768         gchar c[64];
769         sp_svg_write_color (c, sizeof(c), _thisselected[SS_STROKE]);
770         sp_repr_css_set_property (css, "fill", c);
771         break;
772     case SS_LGRADIENT:
773     case SS_RGRADIENT:
774     case SS_PATTERN:
775         sp_repr_css_set_property (css, "fill", _paintserver_id[SS_STROKE].c_str());
776         break;
777     }
779     sp_desktop_set_style (_desktop, css);
780     sp_repr_css_attr_unref (css);
781     sp_document_done (sp_desktop_document(_desktop), SP_VERB_DIALOG_FILL_STROKE,
782                       _("Swap fill and stroke"));
785 void SelectedStyle::on_fill_edit() {
786     if (Dialog::FillAndStroke *fs = get_fill_and_stroke_panel(_desktop))
787         fs->showPageFill();
790 void SelectedStyle::on_stroke_edit() {
791     if (Dialog::FillAndStroke *fs = get_fill_and_stroke_panel(_desktop))
792         fs->showPageStrokePaint();
795 bool
796 SelectedStyle::on_fill_click(GdkEventButton *event)
798     if (event->button == 1) { // click, open fill&stroke
800         if (Dialog::FillAndStroke *fs = get_fill_and_stroke_panel(_desktop))
801             fs->showPageFill();
803     } else if (event->button == 3) { // right-click, popup menu
804         _popup[SS_FILL].popup(event->button, event->time);
805     } else if (event->button == 2) { // middle click, toggle none/lastcolor
806         if (_mode[SS_FILL] == SS_NONE) {
807             on_fill_lastused();
808         } else {
809             on_fill_remove();
810         }
811     }
812     return true;
815 bool
816 SelectedStyle::on_stroke_click(GdkEventButton *event)
818     if (event->button == 1) { // click, open fill&stroke
819         if (Dialog::FillAndStroke *fs = get_fill_and_stroke_panel(_desktop))
820             fs->showPageStrokePaint();
821     } else if (event->button == 3) { // right-click, popup menu
822         _popup[SS_STROKE].popup(event->button, event->time);
823     } else if (event->button == 2) { // middle click, toggle none/lastcolor
824         if (_mode[SS_STROKE] == SS_NONE) {
825             on_stroke_lastused();
826         } else {
827             on_stroke_remove();
828         }
829     }
830     return true;
833 bool
834 SelectedStyle::on_sw_click(GdkEventButton *event)
836     if (event->button == 1) { // click, open fill&stroke
837         if (Dialog::FillAndStroke *fs = get_fill_and_stroke_panel(_desktop))
838             fs->showPageStrokeStyle();
839     } else if (event->button == 3) { // right-click, popup menu
840         _popup_sw.popup(event->button, event->time);
841     } else if (event->button == 2) { // middle click, toggle none/lastwidth?
842         //
843     }
844     return true;
847 bool
848 SelectedStyle::on_opacity_click(GdkEventButton *event)
850     if (event->button == 2) { // middle click
851         const char* opacity = _opacity_sb.get_value() < 50? "0.5" : (_opacity_sb.get_value() == 100? "0" : "1");
852         SPCSSAttr *css = sp_repr_css_attr_new ();
853         sp_repr_css_set_property (css, "opacity", opacity);
854         sp_desktop_set_style (_desktop, css);
855         sp_repr_css_attr_unref (css);
856         sp_document_done (sp_desktop_document (_desktop), SP_VERB_DIALOG_FILL_STROKE,
857                       _("Change opacity"));
858         return true;
859     }
861     return false;
864 void SelectedStyle::on_popup_px() {
865     _sw_unit = (SPUnit *) &(sp_unit_get_by_id(SP_UNIT_PX));
866     update();
868 void SelectedStyle::on_popup_pt() {
869     _sw_unit = (SPUnit *) &(sp_unit_get_by_id(SP_UNIT_PT));
870     update();
872 void SelectedStyle::on_popup_mm() {
873     _sw_unit = (SPUnit *) &(sp_unit_get_by_id(SP_UNIT_MM));
874     update();
877 void SelectedStyle::on_popup_preset(int i) {
878     SPCSSAttr *css = sp_repr_css_attr_new ();
879     gdouble w;
880     if (_sw_unit) {
881         w = sp_units_get_pixels (_sw_presets[i], *_sw_unit);
882     } else {
883         w = _sw_presets[i];
884     }
885     Inkscape::CSSOStringStream os;
886     os << w;
887     sp_repr_css_set_property (css, "stroke-width", os.str().c_str());
888     // FIXME: update dash patterns!
889     sp_desktop_set_style (_desktop, css, true);
890     sp_repr_css_attr_unref (css);
891     sp_document_done (sp_desktop_document(_desktop), SP_VERB_DIALOG_SWATCHES,
892                       _("Change stroke width"));
895 void
896 SelectedStyle::update()
898     if (_desktop == NULL)
899         return;
901     // create temporary style
902     SPStyle *query = sp_style_new (sp_desktop_document(_desktop));
904     for (int i = SS_FILL; i <= SS_STROKE; i++) {
905         Gtk::EventBox *place = (i == SS_FILL)? &_fill_place : &_stroke_place;
906         Gtk::EventBox *flag_place = (i == SS_FILL)? &_fill_flag_place : &_stroke_flag_place;
908         place->remove();
909         flag_place->remove();
911         _tooltips.unset_tip(*place);
912         _tooltips.unset_tip(*flag_place);
914         _mode[i] = SS_NA;
915         _paintserver_id[i].clear();
917         _popup_copy[i].set_sensitive(false);
919         // query style from desktop. This returns a result flag and fills query with the style of subselection, if any, or selection
920         int result = sp_desktop_query_style (_desktop, query,
921                                              (i == SS_FILL)? QUERY_STYLE_PROPERTY_FILL : QUERY_STYLE_PROPERTY_STROKE);
922         switch (result) {
923         case QUERY_STYLE_NOTHING:
924             place->add(_na[i]);
925             _tooltips.set_tip(*place, __na[i]);
926             _mode[i] = SS_NA;
927             if ( _dropEnabled[i] ) {
928                 gtk_drag_dest_unset( GTK_WIDGET((i==SS_FILL) ? _fill_place.gobj():_stroke_place.gobj()) );
929                 _dropEnabled[i] = false;
930             }
931             break;
932         case QUERY_STYLE_SINGLE:
933         case QUERY_STYLE_MULTIPLE_AVERAGED:
934         case QUERY_STYLE_MULTIPLE_SAME:
935             if ( !_dropEnabled[i] ) {
936                 gtk_drag_dest_set( GTK_WIDGET( (i==SS_FILL) ? _fill_place.gobj():_stroke_place.gobj()),
937                                    GTK_DEST_DEFAULT_ALL,
938                                    ui_drop_target_entries,
939                                    nui_drop_target_entries,
940                                    GdkDragAction(GDK_ACTION_COPY | GDK_ACTION_MOVE) );
941                 _dropEnabled[i] = true;
942             }
943             SPIPaint *paint;
944             if (i == SS_FILL) {
945                 paint = &(query->fill);
946             } else {
947                 paint = &(query->stroke);
948             }
949             if (paint->set && paint->isPaintserver()) {
950                 SPPaintServer *server = (i == SS_FILL)? SP_STYLE_FILL_SERVER (query) : SP_STYLE_STROKE_SERVER (query);
951                 if ( server ) {
952                     Inkscape::XML::Node *srepr = SP_OBJECT_REPR(server);
953                     _paintserver_id[i] += "url(#";
954                     _paintserver_id[i] += srepr->attribute("id");
955                     _paintserver_id[i] += ")";
957                     if (SP_IS_LINEARGRADIENT (server)) {
958                         SPGradient *vector = SP_GRADIENT(server)->getVector();
959                         sp_gradient_image_set_gradient ((SPGradientImage *) _gradient_preview_l[i], vector);
960                         place->add(_gradient_box_l[i]);
961                         _tooltips.set_tip(*place, __lgradient[i]);
962                         _mode[i] = SS_LGRADIENT;
963                     } else if (SP_IS_RADIALGRADIENT (server)) {
964                         SPGradient *vector = SP_GRADIENT(server)->getVector();
965                         sp_gradient_image_set_gradient ((SPGradientImage *) _gradient_preview_r[i], vector);
966                         place->add(_gradient_box_r[i]);
967                         _tooltips.set_tip(*place, __rgradient[i]);
968                         _mode[i] = SS_RGRADIENT;
969                     } else if (SP_IS_PATTERN (server)) {
970                         place->add(_pattern[i]);
971                         _tooltips.set_tip(*place, __pattern[i]);
972                         _mode[i] = SS_PATTERN;
973                     }
974                 } else {
975                     g_warning ("file %s: line %d: Unknown paint server", __FILE__, __LINE__);
976                 }
977             } else if (paint->set && paint->isColor()) {
978                 guint32 color = paint->value.color.toRGBA32(
979                                      SP_SCALE24_TO_FLOAT ((i == SS_FILL)? query->fill_opacity.value : query->stroke_opacity.value));
980                 _lastselected[i] = _thisselected[i];
981                 _thisselected[i] = color | 0xff; // only color, opacity === 1
982                 ((Inkscape::UI::Widget::ColorPreview*)_color_preview[i])->setRgba32 (color);
983                 _color_preview[i]->show_all();
984                 place->add(*_color_preview[i]);
985                 gchar c_string[64];
986                 g_snprintf (c_string, 64, "%06x/%.3g", color >> 8, SP_RGBA32_A_F(color));
987                 _tooltips.set_tip(*place, __color[i] + ": " + c_string + _(", drag to adjust"));
988                 _mode[i] = SS_COLOR;
989                 _popup_copy[i].set_sensitive(true);
991             } else if (paint->set && paint->isNone()) {
992                 place->add(_none[i]);
993                 _tooltips.set_tip(*place, __none[i]);
994                 _mode[i] = SS_NONE;
995             } else if (!paint->set) {
996                 place->add(_unset[i]);
997                 _tooltips.set_tip(*place, __unset[i]);
998                 _mode[i] = SS_UNSET;
999             }
1000             if (result == QUERY_STYLE_MULTIPLE_AVERAGED) {
1001                 flag_place->add(_averaged[i]);
1002                 _tooltips.set_tip(*flag_place, __averaged[i]);
1003             } else if (result == QUERY_STYLE_MULTIPLE_SAME) {
1004                 flag_place->add(_multiple[i]);
1005                 _tooltips.set_tip(*flag_place, __multiple[i]);
1006             }
1007             break;
1008         case QUERY_STYLE_MULTIPLE_DIFFERENT:
1009             place->add(_many[i]);
1010             _tooltips.set_tip(*place, __many[i]);
1011             _mode[i] = SS_MANY;
1012             break;
1013         default:
1014             break;
1015         }
1016     }
1018 // Now query opacity
1019     _tooltips.unset_tip(_opacity_place);
1020     _tooltips.unset_tip(_opacity_sb);
1022     int result = sp_desktop_query_style (_desktop, query, QUERY_STYLE_PROPERTY_MASTEROPACITY);
1024     switch (result) {
1025     case QUERY_STYLE_NOTHING:
1026         _tooltips.set_tip(_opacity_place, _("Nothing selected"));
1027         _tooltips.set_tip(_opacity_sb, _("Nothing selected"));
1028         _opacity_sb.set_sensitive(false);
1029         break;
1030     case QUERY_STYLE_SINGLE:
1031     case QUERY_STYLE_MULTIPLE_AVERAGED:
1032     case QUERY_STYLE_MULTIPLE_SAME:
1033         _tooltips.set_tip(_opacity_place, _("Opacity (%)"));
1034         _tooltips.set_tip(_opacity_sb, _("Opacity (%)"));
1035         if (_opacity_blocked) break;
1036         _opacity_blocked = true;
1037         _opacity_sb.set_sensitive(true);
1038         _opacity_adjustment.set_value(SP_SCALE24_TO_FLOAT(query->opacity.value) * 100);
1039         _opacity_blocked = false;
1040         break;
1041     }
1043 // Now query stroke_width
1044     int result_sw = sp_desktop_query_style (_desktop, query, QUERY_STYLE_PROPERTY_STROKEWIDTH);
1045     switch (result_sw) {
1046     case QUERY_STYLE_NOTHING:
1047         _stroke_width.set_markup("");
1048         current_stroke_width = 0;
1049         break;
1050     case QUERY_STYLE_SINGLE:
1051     case QUERY_STYLE_MULTIPLE_AVERAGED:
1052     case QUERY_STYLE_MULTIPLE_SAME:
1053     {
1054         double w;
1055         if (_sw_unit) {
1056             w = sp_pixels_get_units(query->stroke_width.computed, *_sw_unit);
1057         } else {
1058             w = query->stroke_width.computed;
1059         }
1060         current_stroke_width = w;
1062         {
1063             gchar *str = g_strdup_printf(" %.3g", w);
1064             _stroke_width.set_markup(str);
1065             g_free (str);
1066         }
1067         {
1068             gchar *str = g_strdup_printf(_("Stroke width: %.5g%s%s"),
1069                                          w,
1070                                          _sw_unit? sp_unit_get_abbreviation(_sw_unit) : "px",
1071                                          (result_sw == QUERY_STYLE_MULTIPLE_AVERAGED)?
1072                                          _(" (averaged)") : "");
1073             _tooltips.set_tip(_stroke_width_place, str);
1074             g_free (str);
1075         }
1076         break;
1077     }
1078     default:
1079         break;
1080     }
1082     sp_style_unref(query);
1085 void SelectedStyle::opacity_0(void) {_opacity_sb.set_value(0);}
1086 void SelectedStyle::opacity_025(void) {_opacity_sb.set_value(25);}
1087 void SelectedStyle::opacity_05(void) {_opacity_sb.set_value(50);}
1088 void SelectedStyle::opacity_075(void) {_opacity_sb.set_value(75);}
1089 void SelectedStyle::opacity_1(void) {_opacity_sb.set_value(100);}
1091 void SelectedStyle::on_opacity_menu (Gtk::Menu *menu) {
1093     Glib::ListHandle<Gtk::Widget *> children = menu->get_children();
1094     for (Glib::ListHandle<Gtk::Widget *>::iterator iter = children.begin(); iter != children.end(); iter++) {
1095         menu->remove(*(*iter));
1096     }
1098     {
1099         Gtk::MenuItem *item = new Gtk::MenuItem;
1100         item->add(*(new Gtk::Label(_("0 (transparent)"), 0, 0)));
1101         item->signal_activate().connect(sigc::mem_fun(*this, &SelectedStyle::opacity_0 ));
1102         menu->add(*item);
1103     }
1104     {
1105         Gtk::MenuItem *item = new Gtk::MenuItem;
1106         item->add(*(new Gtk::Label("25%", 0, 0)));
1107         item->signal_activate().connect(sigc::mem_fun(*this, &SelectedStyle::opacity_025 ));
1108         menu->add(*item);
1109     }
1110     {
1111         Gtk::MenuItem *item = new Gtk::MenuItem;
1112         item->add(*(new Gtk::Label("50%", 0, 0)));
1113         item->signal_activate().connect(sigc::mem_fun(*this, &SelectedStyle::opacity_05 ));
1114         menu->add(*item);
1115     }
1116     {
1117         Gtk::MenuItem *item = new Gtk::MenuItem;
1118         item->add(*(new Gtk::Label("75%", 0, 0)));
1119         item->signal_activate().connect(sigc::mem_fun(*this, &SelectedStyle::opacity_075 ));
1120         menu->add(*item);
1121     }
1122     {
1123         Gtk::MenuItem *item = new Gtk::MenuItem;
1124         item->add(*(new Gtk::Label(_("100% (opaque)"), 0, 0)));
1125         item->signal_activate().connect(sigc::mem_fun(*this, &SelectedStyle::opacity_1 ));
1126         menu->add(*item);
1127     }
1129     menu->show_all();
1132 void SelectedStyle::on_opacity_changed () {
1133     if (_opacity_blocked)
1134         return;
1135     _opacity_blocked = true;
1136     SPCSSAttr *css = sp_repr_css_attr_new ();
1137     Inkscape::CSSOStringStream os;
1138     os << CLAMP ((_opacity_adjustment.get_value() / 100), 0.0, 1.0);
1139     sp_repr_css_set_property (css, "opacity", os.str().c_str());
1140     // FIXME: workaround for GTK breakage: display interruptibility sometimes results in GTK
1141     // sending multiple value-changed events. As if when Inkscape interrupts redraw for main loop
1142     // iterations, GTK discovers that this callback hasn't finished yet, and for some weird reason
1143     // decides to add yet another value-changed event to the queue. Totally braindead if you ask
1144     // me. As a result, scrolling the spinbutton once results in runaway change until it hits 1.0
1145     // or 0.0. (And no, this is not a race with ::update, I checked that.)
1146     // Sigh. So we disable interruptibility while we're setting the new value.
1147     sp_canvas_force_full_redraw_after_interruptions(sp_desktop_canvas(_desktop), 0);
1148     sp_desktop_set_style (_desktop, css);
1149     sp_repr_css_attr_unref (css);
1150     sp_document_maybe_done (sp_desktop_document (_desktop), "fillstroke:opacity", SP_VERB_DIALOG_FILL_STROKE,
1151                       _("Change opacity"));
1152     // resume interruptibility
1153     sp_canvas_end_forced_full_redraws(sp_desktop_canvas(_desktop));
1154     spinbutton_defocus(GTK_OBJECT(_opacity_sb.gobj()));
1155     _opacity_blocked = false;
1158 /* =============================================  RotateableSwatch  */
1160 RotateableSwatch::RotateableSwatch(SelectedStyle *parent, guint mode) :
1161     fillstroke(mode),
1162     parent(parent),
1163     startcolor(0),
1164     startcolor_set(false),
1165     undokey("ssrot1"),
1166     cr(0),
1167     cr_set(false)
1172 RotateableSwatch::~RotateableSwatch() {
1175 double
1176 RotateableSwatch::color_adjust(float *hsl, double by, guint32 cc, guint modifier)
1178     sp_color_rgb_to_hsl_floatv (hsl, SP_RGBA32_R_F(cc), SP_RGBA32_G_F(cc), SP_RGBA32_B_F(cc));
1180     double diff = 0;
1181     if (modifier == 2) { // saturation
1182         double old = hsl[1];
1183         if (by > 0) {
1184             hsl[1] += by * (1 - hsl[1]);
1185         } else {
1186             hsl[1] += by * (hsl[1]);
1187         }
1188         diff = hsl[1] - old;
1189     } else if (modifier == 1) { // lightness
1190         double old = hsl[2];
1191         if (by > 0) {
1192             hsl[2] += by * (1 - hsl[2]);
1193         } else {
1194             hsl[2] += by * (hsl[2]);
1195         }
1196         diff = hsl[2] - old;
1197     } else { // hue
1198         double old = hsl[0];
1199         hsl[0] += by/2;
1200         while (hsl[0] < 0)
1201             hsl[0] += 1;
1202         while (hsl[0] > 1)
1203             hsl[0] -= 1;
1204         diff = hsl[0] - old;
1205     }
1207     float rgb[3];
1208     sp_color_hsl_to_rgb_floatv (rgb, hsl[0], hsl[1], hsl[2]);
1210     gchar c[64];
1211     sp_svg_write_color (c, sizeof(c),
1212         SP_RGBA32_U_COMPOSE(
1213                 (SP_COLOR_F_TO_U(rgb[0])),
1214                 (SP_COLOR_F_TO_U(rgb[1])),
1215                 (SP_COLOR_F_TO_U(rgb[2])),
1216                 0xff
1217         )
1218     );
1220     SPCSSAttr *css = sp_repr_css_attr_new ();
1221     if (fillstroke == SS_FILL)
1222         sp_repr_css_set_property (css, "fill", c);
1223     else
1224         sp_repr_css_set_property (css, "stroke", c);
1225     sp_desktop_set_style (parent->getDesktop(), css);
1226     sp_repr_css_attr_unref (css);
1227     return diff;
1230 void
1231 RotateableSwatch::do_motion(double by, guint modifier) {
1232     if (parent->_mode[fillstroke] != SS_COLOR)
1233         return;
1235     if (!cr_set && modifier != 3) {
1236         GtkWidget *w = GTK_WIDGET(gobj());
1238         GdkBitmap *bitmap = NULL;
1239         GdkBitmap *mask = NULL;
1240         if (modifier == 2) { // saturation
1241             sp_cursor_bitmap_and_mask_from_xpm(&bitmap, &mask, cursor_adj_s_xpm);
1242         } else if (modifier == 1) { // lightness
1243             sp_cursor_bitmap_and_mask_from_xpm(&bitmap, &mask, cursor_adj_l_xpm);
1244         } else { // hue
1245             sp_cursor_bitmap_and_mask_from_xpm(&bitmap, &mask, cursor_adj_h_xpm);
1246         }
1247         if ((bitmap != NULL) && (mask != NULL)) {
1248             cr = gdk_cursor_new_from_pixmap(bitmap, mask,
1249                                             &w->style->black,
1250                                             &w->style->white,
1251                                             16, 16);
1252             g_object_unref (bitmap);
1253             g_object_unref (mask);
1254             gdk_window_set_cursor(w->window, cr);
1255             gdk_cursor_unref(cr);
1256             cr_set = true;
1257         }
1258     }
1260     guint32 cc;
1261     if (!startcolor_set) {
1262         cc = startcolor = parent->_thisselected[fillstroke];
1263         startcolor_set = true;
1264     } else {
1265         cc = startcolor;
1266     }
1268     float hsl[3];
1269     double diff = 0;
1270     if (modifier != 3) {
1271         diff = color_adjust(hsl, by, cc, modifier);
1272     }
1274     if (modifier == 3) { // Alt, do nothing
1276     } else if (modifier == 2) { // saturation
1277         sp_document_maybe_done (sp_desktop_document(parent->getDesktop()), undokey,
1278                                 SP_VERB_DIALOG_FILL_STROKE, (_("Adjust saturation")));
1279         double ch = hsl[1];
1280         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);
1282     } else if (modifier == 1) { // lightness
1283         sp_document_maybe_done (sp_desktop_document(parent->getDesktop()), undokey,
1284                                 SP_VERB_DIALOG_FILL_STROKE, (_("Adjust lightness")));
1285         double ch = hsl[2];
1286         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);
1288     } else { // hue
1289         sp_document_maybe_done (sp_desktop_document(parent->getDesktop()), undokey,
1290                                 SP_VERB_DIALOG_FILL_STROKE, (_("Adjust hue")));
1291         double ch = hsl[0];
1292         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);
1293     }
1296 void
1297 RotateableSwatch::do_release(double by, guint modifier) {
1298     if (parent->_mode[fillstroke] != SS_COLOR)
1299         return;
1301     float hsl[3];
1302     if (modifier != 3) {
1303         color_adjust(hsl, by, startcolor, modifier);
1304     }
1306     if (cr_set) {
1307         GtkWidget *w = GTK_WIDGET(gobj());
1308         gdk_window_set_cursor(w->window, NULL);
1309         if (cr) {
1310            gdk_cursor_unref (cr);
1311            cr = NULL;
1312         }
1313         cr_set = false;
1314     }
1316     if (modifier == 3) { // Alt, do nothing
1317     } else if (modifier == 2) { // saturation
1318         sp_document_maybe_done (sp_desktop_document(parent->getDesktop()), undokey,
1319                                 SP_VERB_DIALOG_FILL_STROKE, ("Adjust saturation"));
1321     } else if (modifier == 1) { // lightness
1322         sp_document_maybe_done (sp_desktop_document(parent->getDesktop()), undokey,
1323                                 SP_VERB_DIALOG_FILL_STROKE, ("Adjust lightness"));
1325     } else { // hue
1326         sp_document_maybe_done (sp_desktop_document(parent->getDesktop()), undokey,
1327                                 SP_VERB_DIALOG_FILL_STROKE, ("Adjust hue"));
1328     }
1330     if (!strcmp(undokey, "ssrot1")) {
1331         undokey = "ssrot2";
1332     } else {
1333         undokey = "ssrot1";
1334     }
1336     parent->getDesktop()->event_context->_message_context->clear();
1337     startcolor_set = false;
1340 /* =============================================  RotateableStrokeWidth  */
1342 RotateableStrokeWidth::RotateableStrokeWidth(SelectedStyle *parent) :
1343     parent(parent),
1344     startvalue(0),
1345     startvalue_set(false),
1346     undokey("swrot1"),
1347     cr(0),
1348     cr_set(false)
1352 RotateableStrokeWidth::~RotateableStrokeWidth() {
1355 double
1356 RotateableStrokeWidth::value_adjust(double current, double by, guint /*modifier*/, bool final)
1358     double newval;
1359     // by is -1..1
1360     if (by < 0) {
1361         // map negative 0..-1 to current..0
1362         newval = current * (1  +  by);
1363     } else {
1364         // map positive 0..1 to current..4*current
1365         newval = current * (1  +  by) * (1  +  by);
1366     }
1368     SPCSSAttr *css = sp_repr_css_attr_new ();
1369     if (final && newval < 1e-6) {
1370         // if dragged into zero and this is the final adjust on mouse release, delete stroke;
1371         // if it's not final, leave it a chance to increase again (which is not possible with "none")
1372         sp_repr_css_set_property (css, "stroke", "none");
1373     } else {
1374         Inkscape::CSSOStringStream os;
1375         os << newval;
1376         sp_repr_css_set_property (css, "stroke-width", os.str().c_str());
1377     }
1379     sp_desktop_set_style (parent->getDesktop(), css);
1380     sp_repr_css_attr_unref (css);
1381     return newval - current;
1384 void
1385 RotateableStrokeWidth::do_motion(double by, guint modifier) {
1387     // if this is the first motion after a mouse grab, remember the current width
1388     if (!startvalue_set) {
1389         startvalue = parent->current_stroke_width;
1390         // if it's 0, adjusting (which uses multiplication) will not be able to change it, so we
1391         // cheat and provide a non-zero value
1392         if (startvalue == 0)
1393             startvalue = 1;
1394         startvalue_set = true;
1395     }
1397     if (modifier == 3) { // Alt, do nothing
1398     } else {
1399         double diff = value_adjust(startvalue, by, modifier, false);
1400         sp_document_maybe_done (sp_desktop_document(parent->getDesktop()), undokey,
1401                                 SP_VERB_DIALOG_FILL_STROKE, (_("Adjust stroke width")));
1402         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);
1403     }
1406 void
1407 RotateableStrokeWidth::do_release(double by, guint modifier) {
1409     if (modifier == 3) { // do nothing
1411     } else {
1412         value_adjust(startvalue, by, modifier, true);
1413         startvalue_set = false;
1414         sp_document_maybe_done (sp_desktop_document(parent->getDesktop()), undokey,
1415                                 SP_VERB_DIALOG_FILL_STROKE, (_("Adjust stroke width")));
1416     }
1418     if (!strcmp(undokey, "swrot1")) {
1419         undokey = "swrot2";
1420     } else {
1421         undokey = "swrot1";
1422     }
1423     parent->getDesktop()->event_context->_message_context->clear();
1427 Dialog::FillAndStroke *get_fill_and_stroke_panel(SPDesktop *desktop)
1429     if (Dialog::PanelDialogBase *panel_dialog =
1430         dynamic_cast<Dialog::PanelDialogBase *>(desktop->_dlg_mgr->getDialog("FillAndStroke"))) {
1431         try {
1432             Dialog::FillAndStroke &fill_and_stroke =
1433                 dynamic_cast<Dialog::FillAndStroke &>(panel_dialog->getPanel());
1434             return &fill_and_stroke;
1435         } catch (std::exception e) { }
1436     }
1438     return 0;
1441 } // namespace Widget
1442 } // namespace UI
1443 } // namespace Inkscape
1445 /*
1446   Local Variables:
1447   mode:c++
1448   c-file-style:"stroustrup"
1449   c-file-offsets:((innamespace . 0)(inline-open . 0))
1450   indent-tabs-mode:nil
1451   fill-column:99
1452   End:
1453 */
1454 // vim: filetype=c++:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 :