25
26
#include <gtk/gtk.h>
26
27
#include <glibmm/i18n.h>
29
#include "../dialogs/dialog-events.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>
31
38
#include "dash-selector.h"
33
enum {CHANGED, LAST_SIGNAL};
35
struct SPDashSelector {
42
struct SPDashSelectorClass {
43
GtkHBoxClass parent_class;
45
void (* changed) (SPDashSelector *dsel);
48
double dash_0[] = {-1.0};
49
double dash_1_1[] = {1.0, 1.0, -1.0};
50
double dash_2_1[] = {2.0, 1.0, -1.0};
51
double dash_4_1[] = {4.0, 1.0, -1.0};
52
double dash_1_2[] = {1.0, 2.0, -1.0};
53
double dash_1_4[] = {1.0, 4.0, -1.0};
55
double *builtin_dashes[] = {dash_0, dash_1_1, dash_2_1, dash_4_1, dash_1_2, dash_1_4, NULL};
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};
57
51
static double **dashes = NULL;
59
static void sp_dash_selector_class_init (SPDashSelectorClass *klass);
60
static void sp_dash_selector_init (SPDashSelector *dsel);
61
static GtkWidget *sp_dash_selector_menu_item_new (SPDashSelector *dsel, double *pattern);
62
static void sp_dash_selector_menu_item_image_realize (GtkWidget *mi, double *pattern);
63
static void sp_dash_selector_dash_activate (GtkObject *object, SPDashSelector *dsel);
64
static void sp_dash_selector_offset_value_changed (GtkAdjustment *adj, SPDashSelector *dsel);
66
static GtkHBoxClass *parent_class;
67
static guint signals[LAST_SIGNAL] = {0};
70
sp_dash_selector_get_type (void)
72
static GtkType type = 0;
76
sizeof (SPDashSelector),
77
sizeof (SPDashSelectorClass),
78
(GtkClassInitFunc) sp_dash_selector_class_init,
79
(GtkObjectInitFunc) sp_dash_selector_init,
82
type = gtk_type_unique (GTK_TYPE_HBOX, &info);
88
sp_dash_selector_class_init (SPDashSelectorClass *klass)
90
parent_class = (GtkHBoxClass*)gtk_type_class (GTK_TYPE_HBOX);
92
signals[CHANGED] = gtk_signal_new ("changed",
93
(GtkSignalRunType)(GTK_RUN_FIRST | GTK_RUN_NO_RECURSE),
94
G_TYPE_FROM_CLASS (klass),
95
GTK_SIGNAL_OFFSET (SPDashSelectorClass, changed),
96
gtk_marshal_NONE__NONE,
101
sp_dash_selector_init (SPDashSelector *dsel)
103
GtkTooltips *tt = gtk_tooltips_new();
105
dsel->dash = gtk_option_menu_new ();
106
gtk_tooltips_set_tip (tt, dsel->dash, _("Dash pattern"), NULL);
107
gtk_widget_show (dsel->dash);
108
gtk_box_pack_start (GTK_BOX (dsel), dsel->dash, FALSE, FALSE, 0);
110
GtkWidget *m = gtk_menu_new ();
112
for (int i = 0; dashes[i]; i++) {
113
GtkWidget *mi = sp_dash_selector_menu_item_new (dsel, dashes[i]);
114
gtk_widget_show (mi);
115
gtk_menu_append (GTK_MENU (m), mi);
117
gtk_option_menu_set_menu (GTK_OPTION_MENU (dsel->dash), m);
119
dsel->offset = gtk_adjustment_new (0.0, 0.0, 10.0, 0.1, 1.0, 1.0);
120
GtkWidget *sb = gtk_spin_button_new (GTK_ADJUSTMENT (dsel->offset), 0.1, 2);
121
gtk_tooltips_set_tip (tt, sb, _("Pattern offset"), NULL);
123
sp_dialog_defocus_on_enter (sb);
124
gtk_widget_show (sb);
125
gtk_box_pack_start (GTK_BOX (dsel), sb, FALSE, FALSE, 0);
126
gtk_signal_connect (dsel->offset, "value_changed", GTK_SIGNAL_FUNC (sp_dash_selector_offset_value_changed), dsel);
128
gtk_object_set_data (GTK_OBJECT (dsel), "pattern", dashes[0]);
132
sp_dash_selector_new (Inkscape::XML::Node *drepr)
137
for (Inkscape::XML::Node *dr = drepr->firstChild(); dr; dr = dr->next()) {
138
if (!strcmp (dr->name(), "dash"))
145
SPStyle *style = sp_style_new (NULL);
146
dashes = g_new (double *, ndashes + 1);
147
for (Inkscape::XML::Node *dr = drepr->firstChild(); dr; dr = dr->next()) {
148
if (!strcmp (dr->name(), "dash")) {
149
sp_style_read_from_repr (style, dr);
150
if (style->stroke_dash.n_dash > 0) {
151
dashes[pos] = g_new (double, style->stroke_dash.n_dash + 1);
152
double *d = dashes[pos];
154
for (; i < style->stroke_dash.n_dash; i++) {
155
d[i] = style->stroke_dash.dash[i];
159
dashes[pos] = dash_0;
164
sp_style_unref (style);
167
dashes = builtin_dashes;
171
GtkWidget *dsel = (GtkWidget*)gtk_type_new (SP_TYPE_DASH_SELECTOR);
177
sp_dash_selector_set_dash (SPDashSelector *dsel, int ndash, double *dash, double offset)
182
for (int i = 0; i < ndash; i++)
186
for (int i = 0; dashes[i]; i++) {
187
double *pattern = dashes[i];
189
while (pattern[np] >= 0.0)
193
for (j = 0; j < ndash; j++) {
194
if (!NR_DF_TEST_CLOSE (dash[j], pattern[j], delta))
205
gtk_object_set_data (GTK_OBJECT (dsel), "pattern", dashes[pos]);
206
gtk_option_menu_set_history (GTK_OPTION_MENU (dsel->dash), pos);
207
gtk_adjustment_set_value (GTK_ADJUSTMENT (dsel->offset), offset);
211
sp_dash_selector_get_dash (SPDashSelector *dsel, int *ndash, double **dash, double *offset)
213
double *pattern = (double*)gtk_object_get_data (GTK_OBJECT (dsel), "pattern");
216
while (pattern[nd] >= 0.0)
223
*dash = g_new (double, nd);
224
memcpy (*dash, pattern, nd * sizeof (double));
227
*offset = GTK_ADJUSTMENT (dsel->offset)->value;
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!!
59
Gtk::Tooltips *tt = new Gtk::Tooltips();
61
dash = new Gtk::OptionMenu();
62
tt->set_tip(*dash, _("Dash pattern"));
64
this->pack_start(*dash, false, false, 0);
66
Gtk::Menu *m = new Gtk::Menu();
68
for (int i = 0; dashes[i]; i++) {
69
Gtk::MenuItem *mi = menu_item_new(dashes[i]);
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);
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]);
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?
95
SPDashSelector::init_dashes() {
98
Inkscape::Preferences *prefs = Inkscape::Preferences::get();
99
std::vector<Glib::ustring> dash_prefs = prefs->getAllDirs(_prefs_path);
101
if (!dash_prefs.empty()) {
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];
113
for (; i < style->stroke_dash.n_dash; i++) {
114
d[i] = style->stroke_dash.dash[i];
118
dashes[pos] = dash_0;
124
dashes = builtin_dashes;
130
SPDashSelector::set_dash (int ndash, double *dash, double o)
135
for (int i = 0; i < ndash; i++)
139
for (int i = 0; dashes[i]; i++) {
140
double *pattern = dashes[i];
142
while (pattern[np] >= 0.0)
146
for (j = 0; j < ndash; j++) {
147
if (!NR_DF_TEST_CLOSE (dash[j], pattern[j], delta))
158
this->set_data("pattern", dashes[pos]);
159
this->dash->set_history(pos);
160
this->offset->set_value(o);
164
SPDashSelector::get_dash(int *ndash, double **dash, double *off)
166
double *pattern = (double*) this->get_data("pattern");
169
while (pattern[nd] >= 0.0)
176
*dash = g_new (double, nd);
177
memcpy (*dash, pattern, nd * sizeof (double));
180
*off = offset->get_value();
192
SPDashSelector::menu_item_new(double *pattern)
194
Gtk::MenuItem *mi = new Gtk::MenuItem();
195
Gtk::Image *px = new Gtk::Image();
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));
239
210
all_even_are_zero (double *pattern, int n)
241
212
for (int i = 0; i < n; i += 2) {
259
sp_dash_selector_menu_item_new (SPDashSelector *dsel, double *pattern)
261
GtkWidget *mi = gtk_menu_item_new ();
262
GtkWidget *px = gtk_image_new_from_pixmap (NULL, NULL);
264
gtk_widget_show (px);
265
gtk_container_add (GTK_CONTAINER (mi), px);
267
gtk_object_set_data (GTK_OBJECT (mi), "pattern", pattern);
268
gtk_object_set_data (GTK_OBJECT (mi), "px", px);
269
gtk_signal_connect (GTK_OBJECT (mi), "activate", G_CALLBACK (sp_dash_selector_dash_activate), dsel);
271
g_signal_connect_after(G_OBJECT(px), "realize", G_CALLBACK(sp_dash_selector_menu_item_image_realize), pattern);
276
static void sp_dash_selector_menu_item_image_realize (GtkWidget *px, double *pattern) {
277
GdkPixmap *pixmap = gdk_pixmap_new(px->window, DASH_PREVIEW_LENGTH + 4, 16, -1);
278
GdkGC *gc = gdk_gc_new (pixmap);
280
gdk_rgb_gc_set_foreground (gc, 0xffffffff);
281
gdk_draw_rectangle (pixmap, gc, TRUE, 0, 0, DASH_PREVIEW_LENGTH + 4, 16);
283
// FIXME: all of the below twibblering is due to the limitations of gdk_gc_set_dashes (only integers, no zeroes).
284
// Perhaps would make sense to rework this with manually drawn dashes.
286
// Fill in the integer array of pixel-lengths, for display
288
gdouble pixels_d[64];
289
int n_source_dashes = 0;
290
int n_pixel_dashes = 0;
293
for (i_s = 0, i_p = 0; pattern[i_s] >= 0.0; i_s ++, i_p ++) {
297
n_source_dashes = i_s;
299
for (i_s = 0, i_p = 0; i_s < n_source_dashes; i_s ++, i_p ++) {
301
// calculate the pixel length corresponding to the current dash
302
gdouble pixels = DASH_PREVIEW_WIDTH * pattern[i_s];
305
pixels_d [i_p] += pixels;
308
// dash is zero, skip this element in the array, and set pointer backwards so the next dash is added to the previous
311
// the first dash is zero; bad luck, gdk cannot start pattern with non-stroke, so we put a 1-pixel stub here
312
// (it may turn out not shown, though, see special cases below)
313
pixels_d [i_p] = 1.0;
318
n_pixel_dashes = i_p;
320
gdouble longest_dash = 0.0;
322
// after summation, convert double dash lengths to ints
323
for (i_p = 0; i_p < n_pixel_dashes; i_p ++) {
324
pixels_i [i_p] = (gint8) (pixels_d [i_p] + 0.5);
325
// zero-length dashes are already eliminated, so the <1 dash is short but not zero;
326
// we approximate it with a one-pixel mark
327
if (pixels_i [i_p] < 1)
329
if (i_p % 2 == 0) { // it's a dash
330
if (pixels_d [i_p] > longest_dash)
331
longest_dash = pixels_d [i_p];
335
if (longest_dash > 1e-18 && longest_dash < 0.5) {
336
// fake "shortening" of one-pixel marks by painting them lighter-than-black
337
gint rgb = 255 - (gint) (255 * longest_dash / 0.5);
338
gdk_rgb_gc_set_foreground (gc, SP_RGBA32_U_COMPOSE (rgb, rgb, rgb, rgb));
340
gdk_rgb_gc_set_foreground (gc, 0x00000000);
343
if (n_source_dashes > 0) {
345
if (all_even_are_zero (pattern, n_source_dashes)) {
346
; // do not draw anything, only gaps are non-zero
347
} else if (all_odd_are_zero (pattern, n_source_dashes)) {
348
// draw solid line, only dashes are non-zero
349
gdk_gc_set_line_attributes (gc, DASH_PREVIEW_WIDTH,
350
GDK_LINE_SOLID, GDK_CAP_BUTT,
352
gdk_draw_line (pixmap, gc, 4, 8, DASH_PREVIEW_LENGTH, 8);
354
// regular pattern with both gaps and dashes non-zero
355
gdk_gc_set_line_attributes (gc, DASH_PREVIEW_WIDTH,
356
GDK_LINE_ON_OFF_DASH, GDK_CAP_BUTT,
358
gdk_gc_set_dashes (gc, 0, pixels_i, n_pixel_dashes);
359
gdk_draw_line (pixmap, gc, 4, 8, DASH_PREVIEW_LENGTH, 8);
362
// no pattern, draw solid line
363
gdk_gc_set_line_attributes (gc, DASH_PREVIEW_WIDTH,
364
GDK_LINE_SOLID, GDK_CAP_BUTT,
366
gdk_draw_line (pixmap, gc, 4, 8, DASH_PREVIEW_LENGTH, 8);
371
gtk_image_set_from_pixmap(GTK_IMAGE(px), pixmap, NULL);
372
gdk_pixmap_unref(pixmap);
376
sp_dash_selector_dash_activate (GtkObject *object, SPDashSelector *dsel)
378
double *pattern = (double*)gtk_object_get_data (object, "pattern");
379
gtk_object_set_data (GTK_OBJECT (dsel), "pattern", pattern);
381
gtk_signal_emit (GTK_OBJECT (dsel), signals[CHANGED]);
385
sp_dash_selector_offset_value_changed (GtkAdjustment *adj, SPDashSelector *dsel)
387
gtk_signal_emit (GTK_OBJECT (dsel), signals[CHANGED]);
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
241
gdouble pixels_d[64];
242
int n_source_dashes = 0;
243
int n_pixel_dashes = 0;
246
for (i_s = 0, i_p = 0; pattern[i_s] >= 0.0; i_s ++, i_p ++) {
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];
257
pixels_d [i_p] += pixels;
260
// dash is zero, skip this element in the array, and set pointer backwards
261
// so the next dash is added to the previous
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;
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)
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];
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);
295
color.set_rgb(0, 0, 0);
296
gc->set_rgb_fg_color(color);
299
if (n_source_dashes > 0) {
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,
308
pixmap->draw_line(gc, 4, 8, DASH_PREVIEW_LENGTH, 8);
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);
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);
321
Glib::RefPtr<Gdk::Bitmap> null_ptr;
322
px->set(pixmap, null_ptr);
326
SPDashSelector::dash_activate (Gtk::MenuItem *mi)
328
double *pattern = (double*) mi->get_data("pattern");
329
this->set_data ("pattern", pattern);
331
changed_signal.emit();
336
SPDashSelector::offset_value_changed()
338
changed_signal.emit();
344
c-file-style:"stroustrup"
345
c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
350
// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :