3ac2bd4afefaa9d930c9d146188aa2daaf6e2af0
1 #define __SP_DASH_SELECTOR_NEW_C__
3 /** @file
4 * @brief Option menu for selecting dash patterns - implementation
5 */
6 /* Author:
7 * Lauris Kaplinski <lauris@kaplinski.com>
8 * bulia byak <buliabyak@users.sf.net>
9 * Maximilian Albert <maximilian.albert@gmail.com>
10 *
11 * Copyright (C) 2002 Lauris Kaplinski
12 *
13 * Released under GNU GPL, read the file 'COPYING' for more information
14 */
16 #define DASH_PREVIEW_WIDTH 2
17 #define DASH_PREVIEW_LENGTH 80
19 #ifdef HAVE_CONFIG_H
20 # include "config.h"
21 #endif
23 #include <cstring>
24 #include <string>
25 #include <libnr/nr-macros.h>
26 #include <gtk/gtk.h>
27 #include <glibmm/i18n.h>
29 #include "style.h"
30 #include "dialogs/dialog-events.h"
31 #include "preferences.h"
33 #include <gtkmm/optionmenu.h>
34 #include <gtkmm/adjustment.h>
35 #include <gtkmm/spinbutton.h>
38 #include "dash-selector.h"
40 gchar const *const SPDashSelector::_prefs_path = "/palette/dashes";
42 static double dash_0[] = {-1.0};
43 static double dash_1_1[] = {1.0, 1.0, -1.0};
44 static double dash_2_1[] = {2.0, 1.0, -1.0};
45 static double dash_4_1[] = {4.0, 1.0, -1.0};
46 static double dash_1_2[] = {1.0, 2.0, -1.0};
47 static double dash_1_4[] = {1.0, 4.0, -1.0};
49 static double *builtin_dashes[] = {dash_0, dash_1_1, dash_2_1, dash_4_1, dash_1_2, dash_1_4, NULL};
51 static double **dashes = NULL;
53 static void sp_dash_selector_menu_item_image_realize(Gtk::Image *px, double *pattern);
55 SPDashSelector::SPDashSelector() {
56 // TODO: find something more sensible here!!
57 init_dashes();
59 Gtk::Tooltips *tt = new Gtk::Tooltips();
61 dash = new Gtk::OptionMenu();
62 tt->set_tip(*dash, _("Dash pattern"));
63 dash->show();
64 this->pack_start(*dash, false, false, 0);
66 Gtk::Menu *m = new Gtk::Menu();
67 m->show();
68 for (int i = 0; dashes[i]; i++) {
69 Gtk::MenuItem *mi = menu_item_new(dashes[i]);
70 mi->show();
71 m->append(*mi);
72 }
73 dash->set_menu(*m);
75 offset = new Gtk::Adjustment(0.0, 0.0, 10.0, 0.1, 1.0, 0.0);
76 Gtk::SpinButton *sb = new Gtk::SpinButton(*offset, 0.1, 2);
77 tt->set_tip(*sb, _("Pattern offset"));
79 sp_dialog_defocus_on_enter_cpp(sb);
80 sb->show();
81 this->pack_start(*sb, false, false, 0);
82 offset->signal_value_changed().connect(sigc::mem_fun(*this, &SPDashSelector::offset_value_changed));
84 this->set_data("pattern", dashes[0]);
85 }
87 SPDashSelector::~SPDashSelector() {
88 // FIXME: for some reason this doesn't get called; does the call to manage() in
89 // sp_stroke_style_line_widget_new() not processed correctly?
90 delete dash;
91 delete offset;
92 }
94 void
95 SPDashSelector::init_dashes() {
96 if (!dashes) {
98 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
99 std::vector<Glib::ustring> dash_prefs = prefs->getAllDirs(_prefs_path);
101 if (!dash_prefs.empty()) {
102 int pos = 0;
103 SPStyle *style = sp_style_new (NULL);
104 dashes = g_new (double *, dash_prefs.size() + 1);
106 for (std::vector<Glib::ustring>::iterator i = dash_prefs.begin(); i != dash_prefs.end(); ++i) {
107 sp_style_read_from_prefs(style, *i);
109 if (style->stroke_dash.n_dash > 0) {
110 dashes[pos] = g_new (double, style->stroke_dash.n_dash + 1);
111 double *d = dashes[pos];
112 int i = 0;
113 for (; i < style->stroke_dash.n_dash; i++) {
114 d[i] = style->stroke_dash.dash[i];
115 }
116 d[i] = -1;
117 } else {
118 dashes[pos] = dash_0;
119 }
120 pos += 1;
121 }
122 dashes[pos] = NULL;
123 } else {
124 dashes = builtin_dashes;
125 }
126 }
127 }
129 void
130 SPDashSelector::set_dash (int ndash, double *dash, double o)
131 {
132 int pos = 0;
133 if (ndash > 0) {
134 double delta = 0.0;
135 for (int i = 0; i < ndash; i++)
136 delta += dash[i];
137 delta /= 1000.0;
139 for (int i = 0; dashes[i]; i++) {
140 double *pattern = dashes[i];
141 int np = 0;
142 while (pattern[np] >= 0.0)
143 np += 1;
144 if (np == ndash) {
145 int j;
146 for (j = 0; j < ndash; j++) {
147 if (!NR_DF_TEST_CLOSE (dash[j], pattern[j], delta))
148 break;
149 }
150 if (j == ndash) {
151 pos = i;
152 break;
153 }
154 }
155 }
156 }
158 this->set_data("pattern", dashes[pos]);
159 this->dash->set_history(pos);
160 this->offset->set_value(o);
161 }
163 void
164 SPDashSelector::get_dash(int *ndash, double **dash, double *off)
165 {
166 double *pattern = (double*) this->get_data("pattern");
168 int nd = 0;
169 while (pattern[nd] >= 0.0)
170 nd += 1;
172 if (nd > 0) {
173 if (ndash)
174 *ndash = nd;
175 if (dash) {
176 *dash = g_new (double, nd);
177 memcpy (*dash, pattern, nd * sizeof (double));
178 }
179 if (off)
180 *off = offset->get_value();
181 } else {
182 if (ndash)
183 *ndash = 0;
184 if (dash)
185 *dash = NULL;
186 if (off)
187 *off = 0.0;
188 }
189 }
191 Gtk::MenuItem *
192 SPDashSelector::menu_item_new(double *pattern)
193 {
194 Gtk::MenuItem *mi = new Gtk::MenuItem();
195 Gtk::Image *px = new Gtk::Image();
197 px->show();
198 mi->add(*px);
200 mi->set_data("pattern", pattern);
201 mi->set_data("px", px);
202 mi->signal_activate().connect(sigc::bind(sigc::mem_fun(*this, &SPDashSelector::dash_activate), mi));
204 px->signal_realize().connect(sigc::bind(sigc::ptr_fun(&sp_dash_selector_menu_item_image_realize), px, pattern));
206 return mi;
207 }
209 static bool
210 all_even_are_zero (double *pattern, int n)
211 {
212 for (int i = 0; i < n; i += 2) {
213 if (pattern[i] != 0)
214 return false;
215 }
216 return true;
217 }
219 static bool
220 all_odd_are_zero (double *pattern, int n)
221 {
222 for (int i = 1; i < n; i += 2) {
223 if (pattern[i] != 0)
224 return false;
225 }
226 return true;
227 }
229 static void sp_dash_selector_menu_item_image_realize(Gtk::Image *px, double *pattern) {
230 Glib::RefPtr<Gdk::Pixmap> pixmap = Gdk::Pixmap::create(px->get_window(), DASH_PREVIEW_LENGTH + 4, 16, -1);
231 Glib::RefPtr<Gdk::GC> gc = Gdk::GC::create(pixmap);
233 gc->set_rgb_fg_color(Gdk::Color("#ffffff"));
234 pixmap->draw_rectangle(gc, true, 0, 0, DASH_PREVIEW_LENGTH + 4, 16);
236 // FIXME: all of the below twibblering is due to the limitations of gdk_gc_set_dashes (only integers, no zeroes).
237 // Perhaps would make sense to rework this with manually drawn dashes.
239 // Fill in the integer array of pixel-lengths, for display
240 gint8 pixels_i[64];
241 gdouble pixels_d[64];
242 int n_source_dashes = 0;
243 int n_pixel_dashes = 0;
245 signed int i_s, i_p;
246 for (i_s = 0, i_p = 0; pattern[i_s] >= 0.0; i_s ++, i_p ++) {
247 pixels_d[i_p] = 0.0;
248 }
250 n_source_dashes = i_s;
252 for (i_s = 0, i_p = 0; i_s < n_source_dashes; i_s ++, i_p ++) {
253 // calculate the pixel length corresponding to the current dash
254 gdouble pixels = DASH_PREVIEW_WIDTH * pattern[i_s];
256 if (pixels > 0.0)
257 pixels_d [i_p] += pixels;
258 else {
259 if (i_p >= 1) {
260 // dash is zero, skip this element in the array, and set pointer backwards
261 // so the next dash is added to the previous
262 i_p -= 2;
263 } else {
264 // the first dash is zero; bad luck, gdk cannot start pattern with non-stroke, so we
265 // put a 1-pixel stub here (it may turn out not shown, though, see special cases below)
266 pixels_d [i_p] = 1.0;
267 }
268 }
269 }
271 n_pixel_dashes = i_p;
273 gdouble longest_dash = 0.0;
275 // after summation, convert double dash lengths to ints
276 for (i_p = 0; i_p < n_pixel_dashes; i_p ++) {
277 pixels_i [i_p] = (gint8) (pixels_d [i_p] + 0.5);
278 // zero-length dashes are already eliminated, so the <1 dash is short but not zero;
279 // we approximate it with a one-pixel mark
280 if (pixels_i [i_p] < 1)
281 pixels_i [i_p] = 1;
282 if (i_p % 2 == 0) { // it's a dash
283 if (pixels_d [i_p] > longest_dash)
284 longest_dash = pixels_d [i_p];
285 }
286 }
288 Gdk::Color color;
289 if (longest_dash > 1e-18 && longest_dash < 0.5) {
290 // fake "shortening" of one-pixel marks by painting them lighter-than-black
291 gint rgb = 0xffff - (gint) (0xffff * longest_dash / 0.5);
292 color.set_rgb(rgb, rgb, rgb);
293 gc->set_rgb_fg_color(color);
294 } else {
295 color.set_rgb(0, 0, 0);
296 gc->set_rgb_fg_color(color);
297 }
299 if (n_source_dashes > 0) {
300 // special cases:
301 if (all_even_are_zero (pattern, n_source_dashes)) {
302 ; // do not draw anything, only gaps are non-zero
303 } else if (all_odd_are_zero (pattern, n_source_dashes)) {
304 // draw solid line, only dashes are non-zero
305 gc->set_line_attributes(DASH_PREVIEW_WIDTH,
306 Gdk::LINE_SOLID, Gdk::CAP_BUTT,
307 Gdk::JOIN_MITER);
308 pixmap->draw_line(gc, 4, 8, DASH_PREVIEW_LENGTH, 8);
309 } else {
310 // regular pattern with both gaps and dashes non-zero
311 gc->set_line_attributes(DASH_PREVIEW_WIDTH, Gdk::LINE_ON_OFF_DASH, Gdk::CAP_BUTT, Gdk::JOIN_MITER);
312 gc->set_dashes(0, pixels_i, n_pixel_dashes);
313 pixmap->draw_line(gc, 4, 8, DASH_PREVIEW_LENGTH, 8);
314 }
315 } else {
316 // no pattern, draw solid line
317 gc->set_line_attributes(DASH_PREVIEW_WIDTH, Gdk::LINE_SOLID, Gdk::CAP_BUTT, Gdk::JOIN_MITER);
318 pixmap->draw_line(gc, 4, 8, DASH_PREVIEW_LENGTH, 8);
319 }
321 Glib::RefPtr<Gdk::Bitmap> null_ptr;
322 px->set(pixmap, null_ptr);
323 }
325 void
326 SPDashSelector::dash_activate (Gtk::MenuItem *mi)
327 {
328 double *pattern = (double*) mi->get_data("pattern");
329 this->set_data ("pattern", pattern);
331 changed_signal.emit();
332 }
335 void
336 SPDashSelector::offset_value_changed()
337 {
338 changed_signal.emit();
339 }
341 /*
342 Local Variables:
343 mode:c++
344 c-file-style:"stroustrup"
345 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
346 indent-tabs-mode:nil
347 fill-column:99
348 End:
349 */
350 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :