1 /**
2 * \brief Fill and Stroke dialog,
3 * based on sp_object_properties_dialog
4 *
5 * Authors:
6 * Bryce W. Harrington <bryce@bryceharrington.org>
7 * Gustav Broberg <broberg@kth.se>
8 *
9 * Copyright (C) 2004--2007 Authors
10 *
11 * Released under GNU GPL. Read the file 'COPYING' for more information.
12 */
14 #ifdef HAVE_CONFIG_H
15 # include <config.h>
16 #endif
18 #include "desktop-handles.h"
19 #include "desktop-style.h"
20 #include "document.h"
21 #include "fill-and-stroke.h"
22 #include "filter-chemistry.h"
23 #include "inkscape.h"
24 #include "inkscape-stock.h"
25 #include "selection.h"
26 #include "style.h"
27 #include "svg/css-ostringstream.h"
28 #include "verbs.h"
29 #include "xml/repr.h"
30 #include "widgets/icon.h"
32 #include "dialogs/fill-style.h"
33 #include "dialogs/stroke-style.h"
35 #include <widgets/paint-selector.h>
37 namespace Inkscape {
38 namespace UI {
39 namespace Dialog {
41 void on_selection_changed(Inkscape::Application *inkscape,
42 Inkscape::Selection *selection,
43 FillAndStroke *dlg)
44 {
45 dlg->selectionChanged(inkscape, selection);
46 }
48 void on_selection_modified(Inkscape::Application *inkscape,
49 Inkscape::Selection *selection,
50 guint flags,
51 FillAndStroke *dlg)
52 {
53 dlg->selectionChanged(inkscape, selection);
54 }
57 FillAndStroke::FillAndStroke()
58 : Dialog ("dialogs.fillstroke", SP_VERB_DIALOG_FILL_STROKE),
59 _page_fill(1, 1, true, true),
60 _page_stroke_paint(1, 1, true, true),
61 _page_stroke_style(1, 1, true, true),
62 _blur_vbox(false, 0),
63 _blur_label_box(false, 0),
64 _blur_label(_("_Blur, %"), 0.0, 1.0, true),
65 _blur_adjustment(0.0, 0.0, 100.0, 1.0, 1.0, 0.0),
66 _blur_hscale(_blur_adjustment),
67 _blur_spin_button(_blur_adjustment, 0.01, 1),
68 _opacity_vbox(false, 0),
69 _opacity_label_box(false, 0),
70 _opacity_label(_("Master _opacity, %"), 0.0, 1.0, true),
71 _opacity_adjustment(100.0, 0.0, 100.0, 1.0, 1.0, 0.0),
72 _opacity_hscale(_opacity_adjustment),
73 _opacity_spin_button(_opacity_adjustment, 0.01, 1),
74 _blocked(false)
75 {
76 Gtk::VBox *vbox = get_vbox();
77 vbox->set_spacing(0);
79 vbox->pack_start(_notebook, true, true);
81 _notebook.append_page(_page_fill, _createPageTabLabel(_("Fill"), INKSCAPE_STOCK_PROPERTIES_FILL_PAGE));
82 _notebook.append_page(_page_stroke_paint, _createPageTabLabel(_("Stroke _paint"), INKSCAPE_STOCK_PROPERTIES_STROKE_PAINT_PAGE));
83 _notebook.append_page(_page_stroke_style, _createPageTabLabel(_("Stroke st_yle"), INKSCAPE_STOCK_PROPERTIES_STROKE_PAGE));
85 _layoutPageFill();
86 _layoutPageStrokePaint();
87 _layoutPageStrokeStyle();
89 // Blur
90 vbox->pack_start(_blur_vbox, false, false, 2);
91 _blur_label_box.pack_start(_blur_label, false, false, 4);
92 _blur_vbox.pack_start(_blur_label_box, false, false, 0);
93 _blur_vbox.pack_start(_blur_hbox, false, false, 0);
94 _blur_hbox.pack_start(_blur_hscale, true, true, 4);
95 _blur_hbox.pack_start(_blur_spin_button, false, false, 0);
96 _blur_hscale.set_draw_value(false);
97 _blur_adjustment.signal_value_changed().connect(sigc::mem_fun(*this, &Inkscape::UI::Dialog::FillAndStroke::_blurValueChanged));
99 // Opacity
100 vbox->pack_start(_opacity_vbox, false, false, 2);
101 _opacity_label_box.pack_start(_opacity_label, false, false, 4);
102 _opacity_vbox.pack_start(_opacity_label_box, false, false, 0);
103 _opacity_vbox.pack_start(_opacity_hbox, false, false, 0);
104 _opacity_hbox.pack_start(_opacity_hscale, true, true, 4);
105 _opacity_hbox.pack_start(_opacity_spin_button, false, false, 0);
106 _opacity_hscale.set_draw_value(false);
107 _opacity_adjustment.signal_value_changed().connect(sigc::mem_fun(*this, &Inkscape::UI::Dialog::FillAndStroke::_opacityValueChanged));
109 // these callbacks are only for the blur and master opacity update; the tabs above take care of themselves
110 g_signal_connect ( G_OBJECT (INKSCAPE), "change_selection", G_CALLBACK (on_selection_changed), this );
111 g_signal_connect ( G_OBJECT (INKSCAPE), "change_subselection", G_CALLBACK (on_selection_changed), this );
112 g_signal_connect ( G_OBJECT (INKSCAPE), "modify_selection", G_CALLBACK (on_selection_modified), this );
113 g_signal_connect ( G_OBJECT (INKSCAPE), "activate_desktop", G_CALLBACK (on_selection_changed), this );
115 selectionChanged(INKSCAPE, sp_desktop_selection(SP_ACTIVE_DESKTOP));
117 show_all_children();
118 }
120 FillAndStroke::~FillAndStroke()
121 {
122 }
124 void
125 FillAndStroke::_layoutPageFill()
126 {
127 Gtk::Widget *fs = manage(Glib::wrap(sp_fill_style_widget_new()));
128 _page_fill.table().attach(*fs, 0, 1, 0, 1);
129 }
131 void
132 FillAndStroke::_layoutPageStrokePaint()
133 {
134 Gtk::Widget *ssp = manage(Glib::wrap(sp_stroke_style_paint_widget_new()));
135 _page_stroke_paint.table().attach(*ssp, 0, 1, 0, 1);
136 }
138 void
139 FillAndStroke::_layoutPageStrokeStyle()
140 {
141 Gtk::Widget *ssl = manage(Glib::wrap(sp_stroke_style_line_widget_new()));
142 _page_stroke_style.table().attach(*ssl, 0, 1, 0, 1);
143 }
145 void
146 FillAndStroke::_blurValueChanged()
147 {
148 if (_blocked)
149 return;
150 _blocked = true;
152 //get desktop
153 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
154 if (!desktop) {
155 return;
156 }
158 // FIXME: fix for GTK breakage, see comment in SelectedStyle::on_opacity_changed; here it results in crash 1580903
159 sp_canvas_force_full_redraw_after_interruptions(sp_desktop_canvas(desktop), 0);
161 //get current selection
162 Inkscape::Selection *selection = sp_desktop_selection (desktop);
164 NR::Maybe<NR::Rect> bbox = selection->bounds();
165 if (!bbox) {
166 return;
167 }
168 //get list of selected items
169 GSList const *items = selection->itemList();
170 //get current document
171 SPDocument *document = sp_desktop_document (desktop);
173 double perimeter = bbox->extent(NR::X) + bbox->extent(NR::Y);
174 double radius = _blur_adjustment.get_value() * perimeter / 400;
176 //apply created filter to every selected item
177 for (GSList const *i = items; i != NULL; i = i->next) {
179 SPItem * item = SP_ITEM(i->data);
180 SPStyle *style = SP_OBJECT_STYLE(item);
181 g_assert(style != NULL);
183 if (radius == 0.0) {
184 remove_filter (item, false);
185 } else {
186 //SPFilter *constructed = new_filter_gaussian_blur_from_item(document, item, radius);
187 //sp_style_set_property_url (SP_OBJECT(item), "filter", SP_OBJECT(constructed), false);
188 }
189 //request update
190 SP_OBJECT(item)->requestDisplayUpdate(( SP_OBJECT_MODIFIED_FLAG |
191 SP_OBJECT_STYLE_MODIFIED_FLAG ));
192 }
194 sp_document_maybe_done (sp_desktop_document (SP_ACTIVE_DESKTOP), "fillstroke:blur", SP_VERB_DIALOG_FILL_STROKE, _("Change blur"));
196 // resume interruptibility
197 sp_canvas_end_forced_full_redraws(sp_desktop_canvas(desktop));
199 _blocked = false;
200 }
202 void
203 FillAndStroke::_opacityValueChanged()
204 {
205 if (_blocked)
206 return;
207 _blocked = true;
209 // FIXME: fix for GTK breakage, see comment in SelectedStyle::on_opacity_changed; here it results in crash 1580903
210 // UPDATE: crash fixed in GTK+ 2.10.7 (bug 374378), remove this as soon as it's reasonably common
211 // (though this only fixes the crash, not the multiple change events)
212 sp_canvas_force_full_redraw_after_interruptions(sp_desktop_canvas(SP_ACTIVE_DESKTOP), 0);
214 SPCSSAttr *css = sp_repr_css_attr_new ();
216 Inkscape::CSSOStringStream os;
217 os << CLAMP (_opacity_adjustment.get_value() / 100, 0.0, 1.0);
218 sp_repr_css_set_property (css, "opacity", os.str().c_str());
220 sp_desktop_set_style (SP_ACTIVE_DESKTOP, css);
222 sp_repr_css_attr_unref (css);
224 sp_document_maybe_done (sp_desktop_document (SP_ACTIVE_DESKTOP), "fillstroke:opacity", SP_VERB_DIALOG_FILL_STROKE,
225 _("Change opacity"));
227 // resume interruptibility
228 sp_canvas_end_forced_full_redraws(sp_desktop_canvas(SP_ACTIVE_DESKTOP));
230 _blocked = false;
231 }
233 void
234 FillAndStroke::selectionChanged(Inkscape::Application *inkscape,
235 Inkscape::Selection *selection)
236 {
237 if (_blocked)
238 return;
239 _blocked = true;
241 // create temporary style
242 SPStyle *query = sp_style_new ();
243 // query style from desktop into it. This returns a result flag and fills query with the style of subselection, if any, or selection
244 int result = sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_MASTEROPACITY);
246 switch (result) {
247 case QUERY_STYLE_NOTHING:
248 _opacity_hbox.set_sensitive(false);
249 // gtk_widget_set_sensitive (opa, FALSE);
250 break;
251 case QUERY_STYLE_SINGLE:
252 case QUERY_STYLE_MULTIPLE_AVERAGED: // TODO: treat this slightly differently
253 case QUERY_STYLE_MULTIPLE_SAME:
254 _opacity_hbox.set_sensitive(true);
255 _opacity_adjustment.set_value(100 * SP_SCALE24_TO_FLOAT(query->opacity.value));
256 break;
257 }
259 //query now for current average blurring of selection
260 int blur_result = sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_BLUR);
261 switch (blur_result) {
262 case QUERY_STYLE_NOTHING: //no blurring
263 _blur_hbox.set_sensitive(false);
264 break;
265 case QUERY_STYLE_SINGLE:
266 case QUERY_STYLE_MULTIPLE_AVERAGED:
267 case QUERY_STYLE_MULTIPLE_SAME:
268 NR::Maybe<NR::Rect> bbox = sp_desktop_selection(SP_ACTIVE_DESKTOP)->bounds();
269 if (bbox) {
270 double perimeter = bbox->extent(NR::X) + bbox->extent(NR::Y);
271 _blur_hbox.set_sensitive(true);
272 //update blur widget value
273 float radius = query->filter_gaussianBlur_deviation.value;
274 float percent = radius * 400 / perimeter; // so that for a square, 100% == half side
275 _blur_adjustment.set_value(percent);
276 }
277 break;
278 }
280 sp_style_unref(query);
282 _blocked = false;
283 }
285 Gtk::HBox&
286 FillAndStroke::_createPageTabLabel(const Glib::ustring& label, const char *label_image)
287 {
288 Gtk::HBox *_tab_label_box = manage(new Gtk::HBox(false, 0));
289 _tab_label_box->pack_start(*Glib::wrap(sp_icon_new(Inkscape::ICON_SIZE_DECORATION,
290 label_image)));
292 Gtk::Label *_tab_label = manage(new Gtk::Label(label, true));
293 _tab_label_box->pack_start(*_tab_label);
294 _tab_label_box->show_all();
296 return *_tab_label_box;
297 }
299 } // namespace Dialog
300 } // namespace UI
301 } // namespace Inkscape
303 /*
304 Local Variables:
305 mode:c++
306 c-file-style:"stroustrup"
307 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
308 indent-tabs-mode:nil
309 fill-column:99
310 End:
311 */
312 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :