1 /**
2 * \brief Static style swatch (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 "style-swatch.h"
18 #include "widgets/spw-utilities.h"
19 #include "ui/widget/color-preview.h"
21 #include "style.h"
22 #include "sp-linear-gradient-fns.h"
23 #include "sp-radial-gradient-fns.h"
24 #include "sp-pattern.h"
25 #include "xml/repr.h"
26 #include "xml/node-event-vector.h"
27 #include "widgets/widget-sizes.h"
28 #include "helper/units.h"
29 #include "helper/action.h"
30 #include "inkscape.h"
32 enum {
33 SS_FILL,
34 SS_STROKE
35 };
37 static void style_swatch_attr_changed( Inkscape::XML::Node *repr, gchar const *name,
38 gchar const */*old_value*/, gchar const */*new_value*/,
39 bool /*is_interactive*/, gpointer data)
40 {
41 Inkscape::UI::Widget::StyleSwatch *ss = (Inkscape::UI::Widget::StyleSwatch *) data;
43 if (!strcmp (name, "style")) { // FIXME: watching only for the style attr, no CSS attrs
44 SPCSSAttr *css = sp_repr_css_attr_inherited(repr, "style");
45 ss->setStyle (css);
46 }
47 }
50 static Inkscape::XML::NodeEventVector style_swatch_repr_events =
51 {
52 NULL, /* child_added */
53 NULL, /* child_removed */
54 style_swatch_attr_changed,
55 NULL, /* content_changed */
56 NULL /* order_changed */
57 };
60 static void style_swatch_tool_attr_changed( Inkscape::XML::Node */*repr*/, gchar const *name,
61 gchar const */*old_value*/, gchar const *new_value,
62 bool /*is_interactive*/, gpointer data)
63 {
64 Inkscape::UI::Widget::StyleSwatch *ss = (Inkscape::UI::Widget::StyleSwatch *) data;
66 if (!strcmp (name, "usecurrent")) { // FIXME: watching only for the style attr, no CSS attrs
67 if (!strcmp (new_value, "1")) {
68 ss->setWatched (inkscape_get_repr(INKSCAPE, "desktop"), inkscape_get_repr(INKSCAPE, ss->_tool_path));
69 } else {
70 ss->setWatched (inkscape_get_repr(INKSCAPE, ss->_tool_path), NULL);
71 }
72 // UGLY HACK: we have to reconnect to the watched tool repr again, retrieving it from the stored
73 // tool_path, because the actual repr keeps shifting with each change, no idea why
74 ss->setWatchedTool(ss->_tool_path, false);
75 }
76 }
78 static Inkscape::XML::NodeEventVector style_swatch_tool_repr_events =
79 {
80 NULL, /* child_added */
81 NULL, /* child_removed */
82 style_swatch_tool_attr_changed,
83 NULL, /* content_changed */
84 NULL /* order_changed */
85 };
87 namespace Inkscape {
88 namespace UI {
89 namespace Widget {
91 StyleSwatch::StyleSwatch(SPCSSAttr *css, gchar const *main_tip)
92 :
93 _tool_path(NULL),
94 _css (NULL),
96 _watched(NULL),
97 _watched_tool(NULL),
99 _table(2, 6),
101 _sw_unit(NULL),
103 _tooltips ()
104 {
105 _label[SS_FILL].set_markup(_("Fill:"));
106 _label[SS_STROKE].set_markup(_("Stroke:"));
108 for (int i = SS_FILL; i <= SS_STROKE; i++) {
109 _label[i].set_alignment(0.0, 0.5);
110 _label[i].set_padding(0, 0);
112 _color_preview[i] = new Inkscape::UI::Widget::ColorPreview (0);
113 }
115 _opacity_value.set_alignment(0.0, 0.5);
116 _opacity_value.set_padding(0, 0);
118 _table.set_col_spacings (2);
119 _table.set_row_spacings (0);
121 _stroke.pack_start(_place[SS_STROKE]);
122 _stroke_width_place.add(_stroke_width);
123 _stroke.pack_start(_stroke_width_place, Gtk::PACK_SHRINK);
125 _table.attach(_label[SS_FILL], 0,1, 0,1, Gtk::FILL, Gtk::SHRINK);
126 _table.attach(_label[SS_STROKE], 0,1, 1,2, Gtk::FILL, Gtk::SHRINK);
128 _table.attach(_place[SS_FILL], 1,2, 0,1);
129 _table.attach(_stroke, 1,2, 1,2);
131 _opacity_place.add(_opacity_value);
132 _table.attach(_opacity_place, 2,3, 0,2, Gtk::SHRINK, Gtk::SHRINK);
134 _swatch.add(_table);
135 pack_start(_swatch, true, true, 0);
137 set_size_request (STYLE_SWATCH_WIDTH, -1);
139 sp_set_font_size_smaller (GTK_WIDGET(_opacity_value.gobj()));
140 sp_set_font_size_smaller (GTK_WIDGET(_stroke_width.gobj()));
141 for (int i = SS_FILL; i <= SS_STROKE; i++) {
142 sp_set_font_size_smaller (GTK_WIDGET(_value[i].gobj()));
143 sp_set_font_size_smaller (GTK_WIDGET(_place[i].gobj()));
144 sp_set_font_size_smaller (GTK_WIDGET(_label[i].gobj()));
145 }
147 setStyle (css);
149 _swatch.signal_button_press_event().connect(sigc::mem_fun(*this, &StyleSwatch::on_click));
151 _tooltips.set_tip(_swatch, main_tip);
152 }
154 void StyleSwatch::setClickVerb(sp_verb_t verb_t) {
155 _verb_t = verb_t;
156 }
158 void StyleSwatch::setDesktop(SPDesktop *desktop) {
159 _desktop = desktop;
160 }
162 bool
163 StyleSwatch::on_click(GdkEventButton */*event*/)
164 {
165 if (this->_desktop && this->_verb_t != SP_VERB_NONE) {
166 Inkscape::Verb *verb = Inkscape::Verb::get(this->_verb_t);
167 SPAction *action = verb->get_action((Inkscape::UI::View::View *) this->_desktop);
168 sp_action_perform (action, NULL);
169 return true;
170 }
171 return false;
172 }
174 StyleSwatch::~StyleSwatch()
175 {
176 if (_css)
177 sp_repr_css_attr_unref (_css);
179 for (int i = SS_FILL; i <= SS_STROKE; i++) {
180 delete _color_preview[i];
181 }
183 if (_watched) {
184 sp_repr_remove_listener_by_data(_watched, this);
185 Inkscape::GC::release(_watched);
186 _watched = NULL;
187 }
189 if (_watched_tool) {
190 sp_repr_remove_listener_by_data(_watched_tool, this);
191 Inkscape::GC::release(_watched_tool);
192 _watched_tool = NULL;
193 _tool_path = NULL;
194 }
195 }
197 void
198 StyleSwatch::setWatched(Inkscape::XML::Node *watched, Inkscape::XML::Node *secondary)
199 {
200 if (_watched) {
201 sp_repr_remove_listener_by_data(_watched, this);
202 Inkscape::GC::release(_watched);
203 _watched = NULL;
204 }
206 if (watched) {
207 _watched = watched;
208 Inkscape::GC::anchor(_watched);
209 sp_repr_add_listener(_watched, &style_swatch_repr_events, this);
210 sp_repr_synthesize_events(_watched, &style_swatch_repr_events, this);
212 // If desktop's last-set style is empty, a tool uses its own fixed style even if set to use
213 // last-set (so long as it's empty). To correctly show this, we're passed the second repr,
214 // that of the tool prefs node, from which we now setStyle if the watched repr's style is
215 // empty.
216 if (secondary) {
217 SPCSSAttr *css = sp_repr_css_attr_inherited(watched, "style");
218 if (!css->attributeList()) { // is css empty?
219 SPCSSAttr *css_secondary = sp_repr_css_attr_inherited(secondary, "style");
220 this->setStyle (css_secondary);
221 }
222 }
223 }
224 }
226 void
227 StyleSwatch::setWatchedTool(const char *path, bool synthesize)
228 {
229 if (_watched_tool) {
230 sp_repr_remove_listener_by_data(_watched_tool, this);
231 Inkscape::GC::release(_watched_tool);
232 _watched_tool = NULL;
233 _tool_path = NULL;
234 }
236 if (path) {
237 _tool_path = (char *) path;
238 Inkscape::XML::Node *watched_tool = inkscape_get_repr(INKSCAPE, path);
239 if (watched_tool) {
240 _watched_tool = watched_tool;
241 Inkscape::GC::anchor(_watched_tool);
242 sp_repr_add_listener(_watched_tool, &style_swatch_tool_repr_events, this);
243 if (synthesize) {
244 sp_repr_synthesize_events(_watched_tool, &style_swatch_tool_repr_events, this);
245 }
246 }
247 }
249 }
252 void
253 StyleSwatch::setStyle(SPCSSAttr *css)
254 {
255 if (_css)
256 sp_repr_css_attr_unref (_css);
258 if (!css)
259 return;
261 _css = sp_repr_css_attr_new();
262 sp_repr_css_merge(_css, css);
264 gchar const *css_string = sp_repr_css_write_string (_css);
265 SPStyle *temp_spstyle = sp_style_new(SP_ACTIVE_DOCUMENT);
266 if (css_string)
267 sp_style_merge_from_style_string (temp_spstyle, css_string);
269 setStyle (temp_spstyle);
271 sp_style_unref (temp_spstyle);
272 }
274 void
275 StyleSwatch::setStyle(SPStyle *query)
276 {
277 _place[SS_FILL].remove();
278 _place[SS_STROKE].remove();
280 bool has_stroke = true;
282 for (int i = SS_FILL; i <= SS_STROKE; i++) {
283 Gtk::EventBox *place = &(_place[i]);
285 SPIPaint *paint;
286 if (i == SS_FILL) {
287 paint = &(query->fill);
288 } else {
289 paint = &(query->stroke);
290 }
292 if (paint->set && paint->isPaintserver()) {
293 SPPaintServer *server = (i == SS_FILL)? SP_STYLE_FILL_SERVER (query) : SP_STYLE_STROKE_SERVER (query);
295 if (SP_IS_LINEARGRADIENT (server)) {
296 _value[i].set_markup(_("L Gradient"));
297 place->add(_value[i]);
298 _tooltips.set_tip(*place, (i == SS_FILL)? (_("Linear gradient fill")) : (_("Linear gradient stroke")));
299 } else if (SP_IS_RADIALGRADIENT (server)) {
300 _value[i].set_markup(_("R Gradient"));
301 place->add(_value[i]);
302 _tooltips.set_tip(*place, (i == SS_FILL)? (_("Radial gradient fill")) : (_("Radial gradient stroke")));
303 } else if (SP_IS_PATTERN (server)) {
304 _value[i].set_markup(_("Pattern"));
305 place->add(_value[i]);
306 _tooltips.set_tip(*place, (i == SS_FILL)? (_("Pattern fill")) : (_("Pattern stroke")));
307 }
309 } else if (paint->set && paint->isColor()) {
310 guint32 color = paint->value.color.toRGBA32( SP_SCALE24_TO_FLOAT ((i == SS_FILL)? query->fill_opacity.value : query->stroke_opacity.value) );
311 ((Inkscape::UI::Widget::ColorPreview*)_color_preview[i])->setRgba32 (color);
312 _color_preview[i]->show_all();
313 place->add(*_color_preview[i]);
314 gchar *tip;
315 if (i == SS_FILL) {
316 tip = g_strdup_printf (_("Fill: %06x/%.3g"), color >> 8, SP_RGBA32_A_F(color));
317 } else {
318 tip = g_strdup_printf (_("Stroke: %06x/%.3g"), color >> 8, SP_RGBA32_A_F(color));
319 }
320 _tooltips.set_tip(*place, tip);
321 g_free (tip);
322 } else if (paint->set && paint->isNone()) {
323 _value[i].set_markup(_("<i>None</i>"));
324 place->add(_value[i]);
325 _tooltips.set_tip(*place, (i == SS_FILL)? (_("No fill")) : (_("No stroke")));
326 if (i == SS_STROKE) has_stroke = false;
327 } else if (!paint->set) {
328 _value[i].set_markup(_("<b>Unset</b>"));
329 place->add(_value[i]);
330 _tooltips.set_tip(*place, (i == SS_FILL)? (_("Unset fill")) : (_("Unset stroke")));
331 if (i == SS_STROKE) has_stroke = false;
332 }
333 }
335 // Now query stroke_width
336 if (has_stroke) {
337 double w;
338 if (_sw_unit) {
339 w = sp_pixels_get_units(query->stroke_width.computed, *_sw_unit);
340 } else {
341 w = query->stroke_width.computed;
342 }
344 {
345 gchar *str = g_strdup_printf(" %.3g", w);
346 _stroke_width.set_markup(str);
347 g_free (str);
348 }
349 {
350 gchar *str = g_strdup_printf(_("Stroke width: %.5g%s"),
351 w,
352 _sw_unit? sp_unit_get_abbreviation(_sw_unit) : "px");
353 _tooltips.set_tip(_stroke_width_place, str);
354 g_free (str);
355 }
356 } else {
357 _tooltips.unset_tip(_stroke_width_place);
358 _stroke_width.set_markup ("");
359 }
361 gdouble op = SP_SCALE24_TO_FLOAT(query->opacity.value);
362 if (op != 1) {
363 {
364 gchar *str;
365 if (op == 0)
366 str = g_strdup_printf(_("O:%.3g"), op);
367 else
368 str = g_strdup_printf(_("O:.%d"), (int) (op*10));
369 _opacity_value.set_markup (str);
370 g_free (str);
371 }
372 {
373 gchar *str = g_strdup_printf(_("Opacity: %.3g"), op);
374 _tooltips.set_tip(_opacity_place, str);
375 g_free (str);
376 }
377 } else {
378 _tooltips.unset_tip(_opacity_place);
379 _opacity_value.set_markup ("");
380 }
382 show_all();
383 }
385 } // namespace Widget
386 } // namespace UI
387 } // namespace Inkscape
389 /*
390 Local Variables:
391 mode:c++
392 c-file-style:"stroustrup"
393 c-file-offsets:((innamespace . 0)(inline-open . 0))
394 indent-tabs-mode:nil
395 fill-column:99
396 End:
397 */
398 // vim: filetype=c++:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 :