1 /** @file
2 * @brief Static style swatch (fill, stroke, opacity)
3 */
4 /* Authors:
5 * buliabyak@gmail.com
6 * Krzysztof KosiĆski <tweenk.pl@gmail.com>
7 *
8 * Copyright (C) 2005-2008 Authors
9 *
10 * Released under GNU GPL. Read the file 'COPYING' for more information.
11 */
13 #ifdef HAVE_CONFIG_H
14 # include <config.h>
15 #endif
17 #include <cstring>
18 #include <string>
20 #include "style-swatch.h"
22 #include "widgets/spw-utilities.h"
23 #include "ui/widget/color-preview.h"
25 #include "style.h"
26 #include "sp-linear-gradient-fns.h"
27 #include "sp-radial-gradient-fns.h"
28 #include "sp-pattern.h"
29 #include "xml/repr.h"
30 #include "widgets/widget-sizes.h"
31 #include "helper/units.h"
32 #include "helper/action.h"
33 #include "preferences.h"
34 #include "inkscape.h"
36 enum {
37 SS_FILL,
38 SS_STROKE
39 };
41 namespace Inkscape {
42 namespace UI {
43 namespace Widget {
45 /**
46 * @brief Watches whether the tool uses the current style
47 */
48 class StyleSwatch::ToolObserver : public Inkscape::Preferences::Observer {
49 public:
50 ToolObserver(Glib::ustring const &path, StyleSwatch &ss) :
51 Observer(path),
52 _style_swatch(ss)
53 {}
54 virtual void notify(Inkscape::Preferences::Entry const &val);
55 private:
56 StyleSwatch &_style_swatch;
57 };
59 /**
60 * @brief Watches for changes in the observed style pref
61 */
62 class StyleSwatch::StyleObserver : public Inkscape::Preferences::Observer {
63 public:
64 StyleObserver(Glib::ustring const &path, StyleSwatch &ss) :
65 Observer(path),
66 _style_swatch(ss)
67 {
68 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
69 this->notify(prefs->getEntry(path));
70 }
71 virtual void notify(Inkscape::Preferences::Entry const &val) {
72 SPCSSAttr *css = val.getInheritedStyle();
73 _style_swatch.setStyle(css);
74 sp_repr_css_attr_unref(css);
75 }
76 private:
77 StyleSwatch &_style_swatch;
78 };
80 void StyleSwatch::ToolObserver::notify(Inkscape::Preferences::Entry const &val)
81 {
82 bool usecurrent = val.getBool();
84 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
85 if (_style_swatch._style_obs) delete _style_swatch._style_obs;
87 if (usecurrent) {
88 _style_swatch._style_obs = new StyleObserver("/desktop/style", _style_swatch);
90 // If desktop's last-set style is empty, a tool uses its own fixed style even if set to use
91 // last-set (so long as it's empty). To correctly show this, we get the tool's style
92 // if the desktop's style is empty.
93 SPCSSAttr *css = prefs->getStyle("/desktop/style");
94 if (!css->attributeList()) {
95 SPCSSAttr *css2 = prefs->getInheritedStyle(_style_swatch._tool_path + "/style");
96 _style_swatch.setStyle(css2);
97 sp_repr_css_attr_unref(css2);
98 }
99 sp_repr_css_attr_unref(css);
100 } else {
101 _style_swatch._style_obs = new StyleObserver(_style_swatch._tool_path + "/style", _style_swatch);
102 }
103 prefs->addObserver(*_style_swatch._style_obs);
104 }
106 StyleSwatch::StyleSwatch(SPCSSAttr *css, gchar const *main_tip)
107 :
108 _desktop(NULL),
109 _verb_t(NULL),
110 _css(NULL),
111 _tool_obs(NULL),
112 _style_obs(NULL),
113 _table(2, 6),
114 _sw_unit(NULL),
115 _tooltips ()
116 {
117 _label[SS_FILL].set_markup(_("Fill:"));
118 _label[SS_STROKE].set_markup(_("Stroke:"));
120 for (int i = SS_FILL; i <= SS_STROKE; i++) {
121 _label[i].set_alignment(0.0, 0.5);
122 _label[i].set_padding(0, 0);
124 _color_preview[i] = new Inkscape::UI::Widget::ColorPreview (0);
125 }
127 _opacity_value.set_alignment(0.0, 0.5);
128 _opacity_value.set_padding(0, 0);
130 _table.set_col_spacings (2);
131 _table.set_row_spacings (0);
133 _stroke.pack_start(_place[SS_STROKE]);
134 _stroke_width_place.add(_stroke_width);
135 _stroke.pack_start(_stroke_width_place, Gtk::PACK_SHRINK);
137 _table.attach(_label[SS_FILL], 0,1, 0,1, Gtk::FILL, Gtk::SHRINK);
138 _table.attach(_label[SS_STROKE], 0,1, 1,2, Gtk::FILL, Gtk::SHRINK);
140 _table.attach(_place[SS_FILL], 1,2, 0,1);
141 _table.attach(_stroke, 1,2, 1,2);
143 _opacity_place.add(_opacity_value);
144 _table.attach(_opacity_place, 2,3, 0,2, Gtk::SHRINK, Gtk::SHRINK);
146 _swatch.add(_table);
147 pack_start(_swatch, true, true, 0);
149 set_size_request (STYLE_SWATCH_WIDTH, -1);
151 sp_set_font_size_smaller (GTK_WIDGET(_opacity_value.gobj()));
152 sp_set_font_size_smaller (GTK_WIDGET(_stroke_width.gobj()));
153 for (int i = SS_FILL; i <= SS_STROKE; i++) {
154 sp_set_font_size_smaller (GTK_WIDGET(_value[i].gobj()));
155 sp_set_font_size_smaller (GTK_WIDGET(_place[i].gobj()));
156 sp_set_font_size_smaller (GTK_WIDGET(_label[i].gobj()));
157 }
159 setStyle (css);
161 _swatch.signal_button_press_event().connect(sigc::mem_fun(*this, &StyleSwatch::on_click));
163 _tooltips.set_tip(_swatch, main_tip);
164 }
166 void StyleSwatch::setClickVerb(sp_verb_t verb_t) {
167 _verb_t = verb_t;
168 }
170 void StyleSwatch::setDesktop(SPDesktop *desktop) {
171 _desktop = desktop;
172 }
174 bool
175 StyleSwatch::on_click(GdkEventButton */*event*/)
176 {
177 if (this->_desktop && this->_verb_t != SP_VERB_NONE) {
178 Inkscape::Verb *verb = Inkscape::Verb::get(this->_verb_t);
179 SPAction *action = verb->get_action((Inkscape::UI::View::View *) this->_desktop);
180 sp_action_perform (action, NULL);
181 return true;
182 }
183 return false;
184 }
186 StyleSwatch::~StyleSwatch()
187 {
188 if (_css)
189 sp_repr_css_attr_unref (_css);
191 for (int i = SS_FILL; i <= SS_STROKE; i++) {
192 delete _color_preview[i];
193 }
195 if (_style_obs) delete _style_obs;
196 if (_tool_obs) delete _tool_obs;
197 }
199 void
200 StyleSwatch::setWatchedTool(const char *path, bool synthesize)
201 {
202 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
204 if (_tool_obs) {
205 delete _tool_obs;
206 _tool_obs = NULL;
207 }
209 if (path) {
210 _tool_path = path;
211 _tool_obs = new ToolObserver(_tool_path + "/usecurrent", *this);
212 prefs->addObserver(*_tool_obs);
213 } else {
214 _tool_path = "";
215 }
217 // hack until there is a real synthesize events function for prefs,
218 // which shouldn't be hard to write once there is sufficient need for it
219 if (synthesize && _tool_obs) {
220 _tool_obs->notify(prefs->getEntry(_tool_path + "/usecurrent"));
221 }
222 }
225 void
226 StyleSwatch::setStyle(SPCSSAttr *css)
227 {
228 if (_css)
229 sp_repr_css_attr_unref (_css);
231 if (!css)
232 return;
234 _css = sp_repr_css_attr_new();
235 sp_repr_css_merge(_css, css);
237 gchar const *css_string = sp_repr_css_write_string (_css);
238 SPStyle *temp_spstyle = sp_style_new(SP_ACTIVE_DOCUMENT);
239 if (css_string)
240 sp_style_merge_from_style_string (temp_spstyle, css_string);
242 setStyle (temp_spstyle);
244 sp_style_unref (temp_spstyle);
245 }
247 void
248 StyleSwatch::setStyle(SPStyle *query)
249 {
250 _place[SS_FILL].remove();
251 _place[SS_STROKE].remove();
253 bool has_stroke = true;
255 for (int i = SS_FILL; i <= SS_STROKE; i++) {
256 Gtk::EventBox *place = &(_place[i]);
258 SPIPaint *paint;
259 if (i == SS_FILL) {
260 paint = &(query->fill);
261 } else {
262 paint = &(query->stroke);
263 }
265 if (paint->set && paint->isPaintserver()) {
266 SPPaintServer *server = (i == SS_FILL)? SP_STYLE_FILL_SERVER (query) : SP_STYLE_STROKE_SERVER (query);
268 if (SP_IS_LINEARGRADIENT (server)) {
269 _value[i].set_markup(_("L Gradient"));
270 place->add(_value[i]);
271 _tooltips.set_tip(*place, (i == SS_FILL)? (_("Linear gradient fill")) : (_("Linear gradient stroke")));
272 } else if (SP_IS_RADIALGRADIENT (server)) {
273 _value[i].set_markup(_("R Gradient"));
274 place->add(_value[i]);
275 _tooltips.set_tip(*place, (i == SS_FILL)? (_("Radial gradient fill")) : (_("Radial gradient stroke")));
276 } else if (SP_IS_PATTERN (server)) {
277 _value[i].set_markup(_("Pattern"));
278 place->add(_value[i]);
279 _tooltips.set_tip(*place, (i == SS_FILL)? (_("Pattern fill")) : (_("Pattern stroke")));
280 }
282 } else if (paint->set && paint->isColor()) {
283 guint32 color = paint->value.color.toRGBA32( SP_SCALE24_TO_FLOAT ((i == SS_FILL)? query->fill_opacity.value : query->stroke_opacity.value) );
284 ((Inkscape::UI::Widget::ColorPreview*)_color_preview[i])->setRgba32 (color);
285 _color_preview[i]->show_all();
286 place->add(*_color_preview[i]);
287 gchar *tip;
288 if (i == SS_FILL) {
289 tip = g_strdup_printf (_("Fill: %06x/%.3g"), color >> 8, SP_RGBA32_A_F(color));
290 } else {
291 tip = g_strdup_printf (_("Stroke: %06x/%.3g"), color >> 8, SP_RGBA32_A_F(color));
292 }
293 _tooltips.set_tip(*place, tip);
294 g_free (tip);
295 } else if (paint->set && paint->isNone()) {
296 _value[i].set_markup(_("<i>None</i>"));
297 place->add(_value[i]);
298 _tooltips.set_tip(*place, (i == SS_FILL)? (_("No fill")) : (_("No stroke")));
299 if (i == SS_STROKE) has_stroke = false;
300 } else if (!paint->set) {
301 _value[i].set_markup(_("<b>Unset</b>"));
302 place->add(_value[i]);
303 _tooltips.set_tip(*place, (i == SS_FILL)? (_("Unset fill")) : (_("Unset stroke")));
304 if (i == SS_STROKE) has_stroke = false;
305 }
306 }
308 // Now query stroke_width
309 if (has_stroke) {
310 double w;
311 if (_sw_unit) {
312 w = sp_pixels_get_units(query->stroke_width.computed, *_sw_unit);
313 } else {
314 w = query->stroke_width.computed;
315 }
317 {
318 gchar *str = g_strdup_printf(" %.3g", w);
319 _stroke_width.set_markup(str);
320 g_free (str);
321 }
322 {
323 gchar *str = g_strdup_printf(_("Stroke width: %.5g%s"),
324 w,
325 _sw_unit? sp_unit_get_abbreviation(_sw_unit) : "px");
326 _tooltips.set_tip(_stroke_width_place, str);
327 g_free (str);
328 }
329 } else {
330 _tooltips.unset_tip(_stroke_width_place);
331 _stroke_width.set_markup ("");
332 }
334 gdouble op = SP_SCALE24_TO_FLOAT(query->opacity.value);
335 if (op != 1) {
336 {
337 gchar *str;
338 if (op == 0)
339 str = g_strdup_printf(_("O:%.3g"), op);
340 else
341 str = g_strdup_printf(_("O:.%d"), (int) (op*10));
342 _opacity_value.set_markup (str);
343 g_free (str);
344 }
345 {
346 gchar *str = g_strdup_printf(_("Opacity: %.3g"), op);
347 _tooltips.set_tip(_opacity_place, str);
348 g_free (str);
349 }
350 } else {
351 _tooltips.unset_tip(_opacity_place);
352 _opacity_value.set_markup ("");
353 }
355 show_all();
356 }
358 } // namespace Widget
359 } // namespace UI
360 } // namespace Inkscape
362 /*
363 Local Variables:
364 mode:c++
365 c-file-style:"stroustrup"
366 c-file-offsets:((innamespace . 0)(inline-open . 0))
367 indent-tabs-mode:nil
368 fill-column:99
369 End:
370 */
371 // vim: filetype=c++:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 :