Code

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