1 /*
2  *  gretl -- Gnu Regression, Econometrics and Time-series Library
3  *  Copyright (C) 2001 Allin Cottrell and Riccardo "Jack" Lucchetti
4  *
5  *  This program is free software: you can redistribute it and/or modify
6  *  it under the terms of the GNU General Public License as published by
7  *  the Free Software Foundation, either version 3 of the License, or
8  *  (at your option) any later version.
9  *
10  *  This program is distributed in the hope that it will be useful,
11  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
12  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  *  GNU General Public License for more details.
14  *
15  *  You should have received a copy of the GNU General Public License
16  *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
17  *
18  */
19 
20 /* gpt_dialog.c for gretl -- GUI gnuplot controller dialog */
21 
22 #include "gretl.h"
23 #include "plotspec.h"
24 #include "gpt_control.h"
25 #include "graphics.h"
26 #include "session.h"
27 #include "dlgutils.h"
28 #include "fileselect.h"
29 #include "calculator.h"
30 #include "gpt_dialog.h"
31 
32 #if GTK_MAJOR_VERSION > 2
33 # define USE_GTK_FONT_CHOOSER 1
34 #else
35 # define USE_GTK_FONT_CHOOSER 0
36 #endif
37 
38 #include "../pixmaps/mouse.xpm"
39 #include "gppoints.h"
40 
41 #ifdef G_OS_WIN32
42 # include "gretlwin32.h"
43 #endif
44 
45 struct gpt_titles_t {
46     char *desc;        /* How the field will show up in the options dialog */
47     short tab;         /* which tab (if any) does the item fall under? */
48     GtkWidget *widget;
49 };
50 
51 struct gpt_range_t {
52     gint ID;
53     GtkWidget *isauto;
54     GtkWidget *min;
55     GtkWidget *max;
56     GtkWidget *lbase;
57 };
58 
59 #define MAX_AXES 3
60 #define NTITLES  4
61 
62 #define BOXINT 99
63 
64 /* apparatus for handling the case where plot components are
65    added or removed via the gui dialog, but these changes are
66    discarded (the user doesn't click "OK" or "Apply")
67 */
68 
69 #define old_lines_init(e) (e->old_n_lines = -1, e->old_lines = NULL)
70 #define old_labels_init(e) (e->old_n_labels = -1, e->old_labels = NULL)
71 #define old_arrows_init(e) (e->old_n_arrows = -1, e->old_arrows = NULL)
72 
73 #define lines_not_synced(e) (e->old_n_lines < 0)
74 #define labels_not_synced(e) (e->old_n_labels < 0)
75 #define arrows_not_synced(e) (e->old_n_arrows < 0)
76 
77 #define restore_lines(e) (e->old_n_lines >= 0)
78 #define restore_labels(e) (e->old_n_labels >= 0)
79 #define restore_arrows(e) (e->old_n_arrows >= 0)
80 
81 typedef struct plot_editor_ plot_editor;
82 
83 struct plot_editor_ {
84     GtkWidget *dialog;
85     GtkWidget *notebook;
86     GPT_SPEC *spec;
87     gchar *user_barsfile;
88     gint active_bars;
89 
90     /* plot-line controls */
91     GtkWidget **lineformula;
92     GtkWidget **linetitle;
93     GtkWidget **stylecombo;
94     GtkWidget **dtcombo;
95     GtkWidget **linewidth;
96     GtkWidget **colorsel;
97     GtkWidget **pointsize;
98     GtkWidget **yaxiscombo;
99 
100     /* fitted-line controls */
101     GtkWidget *fitformula;
102     GtkWidget *fitlegend;
103 
104     /* label and arrow controls */
105     GtkWidget **labeltext;
106     GtkWidget **labeljust;
107     GtkWidget **labelpos;
108     GtkWidget **arrowpos;
109 
110     /* global controls */
111     GtkWidget *keycombo;
112     GtkWidget *fitcombo;
113     GtkWidget *border_check;
114     GtkWidget *grid_check;
115     GtkWidget *grid_combo;
116     GtkWidget *y2_check;
117     GtkWidget *bars_check;
118     GtkWidget *fontcheck;
119     GtkWidget *barscombo;
120 
121     int gui_nlines;
122     int gui_nlabels;
123     int gui_narrows;
124 
125     struct gpt_range_t axis_range[MAX_AXES];
126     struct gpt_titles_t gpt_titles[NTITLES];
127 
128     int old_n_lines;
129     int old_n_labels;
130     int old_n_arrows;
131 
132     GPT_LINE *old_lines;
133     GPT_LABEL *old_labels;
134     GPT_ARROW *old_arrows;
135 };
136 
137 #define PLOT_POSITION_LEN 32
138 
139 const gchar *fittype_strings[] = {
140     N_("none"),
141     N_("linear: y = a + b*x"),
142     N_("quadratic: y = a + b*x + c*x^2"),
143     N_("cubic: y = a + b*x + c*x^2 + d*x^3"),
144     N_("inverse: y = a + b*(1/x)"),
145     N_("loess (locally weighted fit)"),
146     N_("semilog: log y = a + b*x"),
147     N_("linear-log: y = a + b*log(x)"),
148     NULL
149 };
150 
151 enum {
152     GUI_LINE,
153     GUI_LABEL,
154     GUI_ARROW
155 };
156 
157 static void gpt_tab_lines (plot_editor *ed, GPT_SPEC *spec, int ins);
158 static void gpt_tab_labels (plot_editor *ed, GPT_SPEC *spec, int ins);
159 static void gpt_tab_arrows (plot_editor *ed, GPT_SPEC *spec, int ins);
160 static int add_line_widget (plot_editor *ed);
161 static int add_label_widget (plot_editor *ed);
162 static int add_arrow_widget (plot_editor *ed);
163 static void plot_editor_set_fontname (plot_editor *ed, const char *name);
164 static int line_get_point_type (GPT_LINE *line, int i);
165 
166 /* graph color selection apparatus */
167 
168 #define XPMROWS 19
169 #define XPMCOLS 17
170 
171 #define scale_round(v) ((v) * 255.0 / 65535.0)
172 
get_image_for_color(gretlRGB color)173 static GtkWidget *get_image_for_color (gretlRGB color)
174 {
175     static char **xpm = NULL;
176     GdkPixbuf *icon;
177     GtkWidget *image;
178     char colstr[8] = {0};
179     int i;
180 
181     if (xpm == NULL) {
182 	xpm = strings_array_new_with_length(XPMROWS, XPMCOLS);
183 	if (xpm == NULL) {
184 	    return NULL;
185 	}
186 
187 	/* common set-up */
188 	strcpy(xpm[0], "16 16 2 1");
189 	strcpy(xpm[1], "X      c #000000");
190 	strcpy(xpm[2], ".      c #000000");
191 	strcpy(xpm[3], "................");
192 
193 	for (i=4; i<XPMROWS-1; i++) {
194 	    strcpy(xpm[i], ".XXXXXXXXXXXXXX.");
195 	}
196 
197 	strcpy(xpm[XPMROWS-1], "................");
198     }
199 
200     /* write in the specific color we want */
201     print_rgb_hash(colstr, color);
202     for (i=0; i<6; i++) {
203 	xpm[1][10+i] = colstr[i+1];
204     }
205 
206     icon = gdk_pixbuf_new_from_xpm_data((const char **) xpm);
207     image = gtk_image_new_from_pixbuf(icon);
208     g_object_unref(icon);
209 
210     return image;
211 }
212 
213 /* ad hoc color selection for line in particular plot */
214 
color_select_callback(GtkWidget * button,GtkWidget * w)215 static void color_select_callback (GtkWidget *button, GtkWidget *w)
216 {
217     GtkWidget *csel;
218     GtkWidget *color_button, *image;
219     GdkColor gcolor;
220     guint8 r, g, b;
221     gretlRGB rgb;
222 
223     color_button = g_object_get_data(G_OBJECT(w), "color_button");
224     csel = gtk_color_selection_dialog_get_color_selection(GTK_COLOR_SELECTION_DIALOG(w));
225 
226     gtk_color_selection_get_current_color(GTK_COLOR_SELECTION(csel), &gcolor);
227     r = (unsigned char) (scale_round(gcolor.red));
228     g = (unsigned char) (scale_round(gcolor.green));
229     b = (unsigned char) (scale_round(gcolor.blue));
230     rgb = (r << 16) | (g << 8) | b;
231 
232     if (widget_get_int(w, "boxcolor")) {
233 	set_boxcolor(rgb);
234     } else {
235 	gretlRGB *prgb = malloc(sizeof *prgb);
236 
237 	if (prgb != NULL) {
238 	    *prgb = rgb;
239 	    g_object_set_data_full(G_OBJECT(color_button), "rgb",
240 				   prgb, free);
241 	}
242     }
243 
244     /* update the "image" widget */
245     image = g_object_get_data(G_OBJECT(color_button), "image");
246     gtk_widget_destroy(image);
247     image = get_image_for_color(rgb);
248     gtk_widget_show(image);
249     gtk_container_add(GTK_CONTAINER(color_button), image);
250     g_object_set_data(G_OBJECT(color_button), "image", image);
251 
252     gtk_widget_destroy(w);
253 }
254 
color_cancel(GtkWidget * button,GtkWidget * w)255 static void color_cancel (GtkWidget *button, GtkWidget *w)
256 {
257     gtk_widget_destroy(w);
258 }
259 
260 /* reset boxcolor patch button after selecting the option to
261    restore the default plot colors */
262 
boxcolor_patch_button_reset(GtkWidget * button)263 static void boxcolor_patch_button_reset (GtkWidget *button)
264 {
265     GtkWidget *image;
266 
267     image = g_object_get_data(G_OBJECT(button), "image");
268     gtk_widget_destroy(image);
269     image = get_image_for_color(get_boxcolor());
270     gtk_widget_show(image);
271     gtk_container_add(GTK_CONTAINER(button), image);
272     g_object_set_data(G_OBJECT(button), "image", image);
273 }
274 
graph_color_selector(GtkWidget * w,gpointer p)275 static void graph_color_selector (GtkWidget *w, gpointer p)
276 {
277     GPT_SPEC *spec;
278     GtkWidget *cdlg, *csel;
279     GtkWidget *button;
280     gint i = GPOINTER_TO_INT(p);
281     char colstr[8];
282     GdkColor gcolor;
283 
284     spec = g_object_get_data(G_OBJECT(w), "plotspec");
285 
286     if (spec != NULL && spec->lines[i].rgb[0] != '\0') {
287 	strcpy(colstr, spec->lines[i].rgb);
288     } else {
289 	gretlRGB rgb;
290 
291 	rgb = (i == BOXINT)? get_boxcolor() : get_graph_color(i);
292 	print_rgb_hash(colstr, rgb);
293     }
294 
295     gdk_color_parse(colstr, &gcolor);
296 
297     cdlg = gtk_color_selection_dialog_new(_("gretl: graph color selection"));
298     g_object_set_data(G_OBJECT(cdlg), "color_button", w);
299     if (i == BOXINT) {
300 	/* flag for special treatment in color_select_callback */
301 	widget_set_int(cdlg, "boxcolor", 1);
302     }
303 
304     csel = gtk_color_selection_dialog_get_color_selection(GTK_COLOR_SELECTION_DIALOG(cdlg));
305     gtk_color_selection_set_current_color(GTK_COLOR_SELECTION(csel), &gcolor);
306 
307     g_object_get(G_OBJECT(cdlg), "ok-button", &button, NULL);
308     g_signal_connect(G_OBJECT(button), "clicked",
309 		     G_CALLBACK(color_select_callback), cdlg);
310     g_object_get(G_OBJECT(cdlg), "cancel-button", &button, NULL);
311     g_signal_connect(G_OBJECT(button), "clicked",
312 		     G_CALLBACK(color_cancel), cdlg);
313 
314     gtk_widget_show(cdlg);
315     gtk_window_set_modal(GTK_WINDOW(cdlg), TRUE);
316 }
317 
boxcolor_patch_button(void)318 static GtkWidget *boxcolor_patch_button (void)
319 {
320     GtkWidget *image, *button;
321 
322     image = get_image_for_color(get_boxcolor());
323 
324     if (image == NULL) {
325 	button = gtk_button_new_with_label(_("Select color"));
326     } else {
327 	button = gtk_button_new();
328 	gtk_container_add(GTK_CONTAINER(button), image);
329 	g_object_set_data(G_OBJECT(button), "image", image);
330 	g_signal_connect(G_OBJECT(button), "clicked",
331 			 G_CALLBACK(graph_color_selector),
332 			 GINT_TO_POINTER(BOXINT));
333     }
334 
335     return button;
336 }
337 
style_from_line_number(GPT_SPEC * spec,int i)338 static int style_from_line_number (GPT_SPEC *spec, int i)
339 {
340     int j, lt = spec->lines[i].type;
341 
342     if (lt > 0) {
343 	j = lt - 1;
344     } else if (lt == LT_AUTO) {
345 	j = i;
346     } else if (lt == 0) {
347 	j = i;
348     } else {
349 	j = LT_AUTO;
350     }
351 
352     if (j >= spec->n_lines) {
353 	/* don't go out of bounds */
354 	j = -1;
355     }
356 
357     return j;
358 }
359 
line_color_button(GPT_SPEC * spec,int i)360 static GtkWidget *line_color_button (GPT_SPEC *spec, int i)
361 {
362     GtkWidget *image = NULL;
363     GtkWidget *button = NULL;
364     int j = i;
365 
366     if (spec->lines[i].type == 0) {
367 	return NULL;
368     }
369 
370     if (spec->lines[i].rgb[0] != '\0') {
371 	/* we have a specific color for this line */
372 	gretlRGB color;
373 
374 	color = gretl_rgb_get(spec->lines[i].rgb);
375 	image = get_image_for_color(color);
376     } else if (spec->lines[i].style == GP_STYLE_FILLEDCURVE) {
377 	image = get_image_for_color(get_shadecolor());
378     } else {
379 	j = style_from_line_number(spec, i);
380 	if (j < 0) {
381 	    return NULL;
382 	}
383 	image = get_image_for_color(get_graph_color(j));
384     }
385 
386     if (image != NULL) {
387 	button = gtk_button_new();
388 	gtk_container_add(GTK_CONTAINER(button), image);
389 	g_object_set_data(G_OBJECT(button), "image", image);
390 	g_object_set_data(G_OBJECT(button), "plotspec", spec);
391 	g_signal_connect(G_OBJECT(button), "clicked",
392 			 G_CALLBACK(graph_color_selector),
393 			 GINT_TO_POINTER(j));
394     }
395 
396     return button;
397 }
398 
apply_line_color(GtkWidget * cb,GPT_SPEC * spec,int i)399 static void apply_line_color (GtkWidget *cb,
400 			      GPT_SPEC *spec,
401 			      int i)
402 {
403     gretlRGB *prgb = NULL;
404 
405     if (cb != NULL && gtk_widget_is_sensitive(cb)) {
406 	prgb = g_object_get_data(G_OBJECT(cb), "rgb");
407     }
408 
409     if (prgb != NULL && i >= 0 && i < spec->n_lines) {
410 	print_rgb_hash(spec->lines[i].rgb, *prgb);
411     }
412 }
413 
414 /* end graph color selection apparatus */
415 
get_pixbuf_for_line(int dt)416 static GdkPixbuf *get_pixbuf_for_line (int dt)
417 {
418     const char *dstrs[] = {
419 	"............................................................",
420 	" .....        .....        .....        .....        .....  ",
421 	".    .    .    .    .    .    .    .    .    .    .    .    ",
422 	".    ..    ........    ..    ........    ..    ........    .",
423 	".......    .    .    .........    .    .    .........    .  "
424     };
425     static char **xpm = NULL;
426     int i;
427 
428     if (xpm == NULL) {
429 	xpm = strings_array_new_with_length(14, 61);
430 	if (xpm == NULL) {
431 	    return NULL;
432 	}
433 
434 	/* common set-up */
435 	strcpy(xpm[0], "60 11 2 1");
436 	strcpy(xpm[1], "  c None");
437 	strcpy(xpm[2], ". c #000000");
438 
439 	for (i=3; i<14; i++) {
440 	    memset(xpm[i], ' ', 60);
441 	    xpm[i][60] = '\0';
442 	}
443     }
444 
445     /* write in the specific pattern we want */
446     strcpy(xpm[8], dstrs[dt]);
447 
448     return gdk_pixbuf_new_from_xpm_data((const char **) xpm);
449 }
450 
flip_manual_range(GtkWidget * widget,plot_editor * ed)451 static void flip_manual_range (GtkWidget *widget, plot_editor *ed)
452 {
453     gint i = widget_get_int(widget, "axis");
454     gboolean s = button_is_active(ed->axis_range[i].isauto);
455 
456     gtk_widget_set_sensitive(ed->axis_range[i].min, !s);
457     gtk_widget_set_sensitive(ed->axis_range[i].max, !s);
458 }
459 
460 /* Take text from a gtkentry and write to gnuplot spec string */
461 
entry_to_gp_string(GtkWidget * w,char ** ptarg)462 static void entry_to_gp_string (GtkWidget *w, char **ptarg)
463 {
464     const gchar *s;
465 
466     g_return_if_fail(GTK_IS_ENTRY(w));
467 
468     if (*ptarg != NULL) {
469 	g_free(*ptarg);
470 	*ptarg = NULL;
471     }
472 
473     s = gtk_entry_get_text(GTK_ENTRY(w));
474     if (s != NULL && *s != '\0') {
475 	*ptarg = g_strdup(s);
476     }
477 }
478 
entry_to_gp_label(GtkWidget * w,char * targ,size_t n)479 static void entry_to_gp_label (GtkWidget *w, char *targ, size_t n)
480 {
481     const gchar *s;
482 
483     g_return_if_fail(GTK_IS_ENTRY(w));
484 
485     *targ = '\0';
486     s = gtk_entry_get_text(GTK_ENTRY(w));
487     if (s != NULL && *s != '\0') {
488 	strncat(targ, s, n - 1);
489     }
490 }
491 
combo_to_gp_style(GtkWidget * w,GpLineStyle * sty)492 static void combo_to_gp_style (GtkWidget *w, GpLineStyle *sty)
493 {
494     gchar *s;
495 
496     g_return_if_fail(GTK_IS_COMBO_BOX(w));
497 
498     s = combo_box_get_active_text(w);
499 
500     if (s != NULL && *s != '\0') {
501 	*sty = gp_style_index_from_display_name(s);
502     }
503 
504     g_free(s);
505 }
506 
ftype_from_selected(GtkWidget * w)507 static int ftype_from_selected (GtkWidget *w)
508 {
509     FitType f = PLOT_FIT_NONE;
510 
511     if (GTK_IS_COMBO_BOX(w)) {
512 	f = gtk_combo_box_get_active(GTK_COMBO_BOX(w));
513 	if (f == PLOT_FIT_LOGLIN && widget_get_int(w, "no-semilog")) {
514 	    /* the second-last fit option will have been
515 	       suppressed, so we're off by one */
516 	    f = PLOT_FIT_LINLOG;
517 	}
518     }
519 
520     return f;
521 }
522 
set_fit_type_from_combo(GtkWidget * box,GPT_SPEC * spec)523 static int set_fit_type_from_combo (GtkWidget *box,
524 				    GPT_SPEC *spec)
525 {
526     int oldfit = widget_get_int(box, "oldfit");
527     FitType f;
528     int err = 0;
529 
530     f = ftype_from_selected(box);
531 
532     if (f == oldfit) {
533 	/* no change */
534 	return 0;
535     }
536 
537     if (f == PLOT_FIT_OLS || f == PLOT_FIT_QUADRATIC ||
538 	f == PLOT_FIT_CUBIC || f == PLOT_FIT_INVERSE ||
539 	f == PLOT_FIT_LOESS || f == PLOT_FIT_LOGLIN ||
540 	f == PLOT_FIT_LINLOG) {
541 	err = plotspec_add_fit(spec, f);
542 	if (err) {
543 	    gui_errmsg(err);
544 	    f = PLOT_FIT_NONE;
545 	} else {
546 	    spec->flags &= ~GPT_FIT_HIDDEN;
547 	}
548     }
549 
550     if (f == PLOT_FIT_NONE) {
551 	if (spec->n_lines >= 2) {
552 	    spec->flags |= GPT_FIT_HIDDEN;
553 	}
554 	spec->fit = f;
555     }
556 
557     widget_set_int(box, "oldfit", f);
558 
559     return err;
560 }
561 
562 /* In this callback we're reacting to the user's selection of a
563    "fit type" for a plot (that is, to the "changed" signal from
564    the drop-down fit-type selector). We adjust other elements in
565    the plot dialog (the default title, the representations under
566    the "Lines" tab) to match this selection.
567 
568    Note, however, that no changes are being "committed" at this
569    point -- that will happen only if/when the user clicks "Apply"
570    or "OK" in the plot editor dialog action area.
571 */
572 
fit_type_changed(GtkComboBox * box,plot_editor * ed)573 static gboolean fit_type_changed (GtkComboBox *box, plot_editor *ed)
574 {
575     GPT_SPEC *spec = ed->spec;
576     const char *s1 = spec->yvarname;
577     const char *s2 = spec->xvarname;
578     FitType f;
579 
580     f = ftype_from_selected(GTK_WIDGET(box));
581 
582     if ((spec->flags & GPT_TS) && f != PLOT_FIT_NONE && ed->keycombo != NULL) {
583 	if (!gtk_widget_is_sensitive(ed->keycombo)) {
584 	    gtk_widget_set_sensitive(ed->keycombo, TRUE);
585 	    gtk_combo_box_set_active(GTK_COMBO_BOX(ed->keycombo), 0);
586 	}
587     }
588 
589     if (*s1 != '\0' && *s2 != '\0') {
590 	/* revise the default plot title */
591 	gchar *title = NULL;
592 
593 	if (f == PLOT_FIT_OLS) {
594 	    title = g_strdup_printf(_("%s versus %s (with least squares fit)"),
595 				    s1, s2);
596 	} else if (f == PLOT_FIT_QUADRATIC) {
597 	    title = g_strdup_printf(_("%s versus %s (with quadratic fit)"),
598 				    s1, s2);
599 	} else if (f == PLOT_FIT_CUBIC) {
600 	    title = g_strdup_printf(_("%s versus %s (with cubic fit)"),
601 				    s1, s2);
602 	} else if (f == PLOT_FIT_INVERSE) {
603 	    title = g_strdup_printf(_("%s versus %s (with inverse fit)"),
604 				    s1, s2);
605 	} else if (f == PLOT_FIT_LOESS) {
606 	    title = g_strdup_printf(_("%s versus %s (with loess fit)"),
607 				    s1, s2);
608 	} else if (f == PLOT_FIT_LOGLIN) {
609 	    title = g_strdup_printf(_("%s versus %s (with semilog fit)"),
610 				    s1, s2);
611 	} else if (f == PLOT_FIT_LINLOG) {
612 	    title = g_strdup_printf(_("%s versus %s (with linear-log fit)"),
613 				    s1, s2);
614 	} else {
615 	    title = g_strdup("");
616 	}
617 
618 	if (title != NULL) {
619 	    gtk_entry_set_text(GTK_ENTRY(ed->gpt_titles[0].widget), title);
620 	    g_free(title);
621 	}
622     }
623 
624     /* re-jig the "Lines" tab entries for the revised fitted line */
625 
626     if (ed->fitformula != NULL && ed->fitlegend != NULL) {
627 	set_fit_type_from_combo(GTK_WIDGET(box), spec);
628 	if (f == PLOT_FIT_LOESS || f == PLOT_FIT_NONE) {
629 	    gtk_entry_set_text(GTK_ENTRY(ed->fitformula), "");
630 	} else if (spec->lines[1].formula != NULL) {
631 	    gtk_entry_set_text(GTK_ENTRY(ed->fitformula), spec->lines[1].formula);
632 	}
633 	if (f == PLOT_FIT_NONE) {
634 	    gtk_entry_set_text(GTK_ENTRY(ed->fitlegend), "");
635 	} else if (spec->lines[1].title != NULL) {
636 	    gtk_entry_set_text(GTK_ENTRY(ed->fitlegend), spec->lines[1].title);
637 	}
638 	gtk_widget_set_sensitive(ed->fitlegend, (f != PLOT_FIT_NONE));
639     }
640 
641     return FALSE;
642 }
643 
spinner_get_float(GtkWidget * b)644 static float spinner_get_float (GtkWidget *b)
645 {
646     return (float) gtk_spin_button_get_value(GTK_SPIN_BUTTON(b));
647 }
648 
649 /* take a double (which might be NA) and format it for
650    a gtkentry widget */
651 
double_to_gp_entry(double x,GtkWidget * w)652 static void double_to_gp_entry (double x, GtkWidget *w)
653 {
654     if (w != NULL && GTK_IS_ENTRY(w)) {
655 	gchar *numstr;
656 
657 	if (na(x)) {
658 	    numstr = g_strdup("NA");
659 	} else if (x == floor(x)) {
660 	    numstr = g_strdup_printf("%.1f", x);
661 	} else {
662 	    numstr = g_strdup_printf("%.10g", x);
663 	}
664 	gtk_entry_set_text(GTK_ENTRY(w), numstr);
665 	g_free(numstr);
666     }
667 }
668 
669 /* read a double from a gtkentry, with error checking */
670 
entry_to_gp_double(GtkWidget * w,double * val)671 static void entry_to_gp_double (GtkWidget *w, double *val)
672 {
673     double x = NADBL;
674 
675     if (w != NULL && GTK_IS_ENTRY(w)) {
676 	const gchar *s = gtk_entry_get_text(GTK_ENTRY(w));
677 
678 	if (s != NULL) {
679 	    if (!strcmp(s, "NA")) {
680 		*val = NADBL;
681 		return;
682 	    } else if (*s != '\0') {
683 		gchar *tmp = g_strdup(s);
684 
685 		gretl_charsub(tmp, ',', '.');
686 		gretl_push_c_numeric_locale();
687 		if (check_atof(tmp)) {
688 		    errbox(gretl_errmsg_get());
689 		} else {
690 		    x = atof(tmp);
691 		}
692 		gretl_pop_c_numeric_locale();
693 		g_free(tmp);
694 	    }
695 	}
696     }
697 
698     if (!na(x)) {
699 	*val = x;
700     }
701 }
702 
gp_string_to_entry(GtkWidget * w,const char * s)703 static void gp_string_to_entry (GtkWidget *w, const char *s)
704 {
705     if (s != NULL && *s != '\0') {
706 	gtk_entry_set_text(GTK_ENTRY(w), s);
707     }
708 }
709 
get_label_pos_from_entry(GtkWidget * w,double * pos)710 static int get_label_pos_from_entry (GtkWidget *w, double *pos)
711 {
712     int err = 0;
713 
714     if (GTK_IS_ENTRY(w)) {
715 	const gchar *s = gtk_entry_get_text(GTK_ENTRY(w));
716 	int chk;
717 
718 	chk = sscanf(s, "%lf %lf", &pos[0], &pos[1]);
719 	if (chk != 2) {
720 	    errbox(_("Invalid position, must be X Y"));
721 	    gtk_editable_select_region(GTK_EDITABLE(w), 0, strlen(s));
722 	    pos[0] = pos[1] = NADBL;
723 	    err = 1;
724 	}
725     } else {
726 	err = 1;
727     }
728 
729     return err;
730 }
731 
get_arrow_spec_from_entries(plot_editor * ed,int i,GPT_ARROW * arrow)732 static int get_arrow_spec_from_entries (plot_editor *ed, int i,
733 					GPT_ARROW *arrow)
734 {
735     int j, err = 0;
736 
737     for (j=0; j<2 && !err; j++) {
738 	GtkWidget *entry, *b1 = NULL, *b2 = NULL;
739 	double *x, *y;
740 
741 	if (j == 0) {
742 	    entry = ed->arrowpos[i];
743 	    b1 = g_object_get_data(G_OBJECT(entry), "arrow_check");
744 	    b2 = g_object_get_data(G_OBJECT(entry), "dots_check");
745 	    x = &arrow->x0;
746 	    y = &arrow->y0;
747 	} else {
748 	    entry = g_object_get_data(G_OBJECT(ed->arrowpos[i]), "pos_2");
749 	    x = &arrow->x1;
750 	    y = &arrow->y1;
751 	}
752 
753 	if (GTK_IS_ENTRY(entry)) {
754 	    const gchar *s = gtk_entry_get_text(GTK_ENTRY(entry));
755 	    int n;
756 
757 	    n = sscanf(s, "%lf %lf", x, y);
758 	    if (n != 2) {
759 		errbox(_("Invalid position, must be X Y"));
760 		gtk_editable_select_region(GTK_EDITABLE(entry), 0, strlen(s));
761 		*x = *y = NADBL;
762 		err = 1;
763 	    }
764 	} else {
765 	    err = 1;
766 	}
767 
768 	if (b1 != NULL) {
769 	    if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(b1))) {
770 		arrow->flags |= GP_ARROW_HEAD;
771 	    } else {
772 		arrow->flags &= ~GP_ARROW_HEAD;
773 	    }
774 	}
775 	if (b2 != NULL) {
776 	    if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(b2))) {
777 		arrow->flags |= GP_ARROW_DOTS;
778 	    } else {
779 		arrow->flags &= ~GP_ARROW_DOTS;
780 	    }
781 	}
782     }
783 
784     return err;
785 }
786 
validate_range(double * r)787 static int validate_range (double *r)
788 {
789     int err = 0;
790 
791     if (na(r[0]) || na(r[1])) {
792 	r[0] = r[1] = NADBL;
793 	err = 1;
794     } else if (r[1] <= r[0]) {
795 	r[0] = r[1] = NADBL;
796 	err = 1;
797     }
798 
799     return err;
800 }
801 
802 static int
set_logscale_from_entry(GPT_SPEC * spec,int i,GtkWidget * entry)803 set_logscale_from_entry (GPT_SPEC *spec, int i, GtkWidget *entry)
804 {
805     double base;
806     int err = 0;
807 
808     base = entry_get_numeric_value(entry, C_POS_DBL);
809     if (na(base)) {
810 	err = 1;
811     } else if (!na(base) && base < 1.1) {
812 	err = 1;
813 	errbox("bad base");
814     } else {
815 	spec->logbase[i] = base;
816 	if (i == 0) {
817 	    *spec->xfmt = '\0';
818 	    *spec->xtics = '\0';
819 	}
820     }
821 
822     return err;
823 }
824 
maybe_set_point_type(GPT_LINE * line,GtkWidget * w,int i)825 static void maybe_set_point_type (GPT_LINE *line, GtkWidget *w, int i)
826 {
827     GtkWidget *ptsel = g_object_get_data(G_OBJECT(w), "pointsel");
828 
829     if (ptsel != NULL && gtk_widget_is_sensitive(ptsel)) {
830 	int pt = gtk_combo_box_get_active(GTK_COMBO_BOX(ptsel));
831 	int pt0 = line_get_point_type(line, i);
832 
833 	if (pt != pt0) {
834 	    int ptdef = (line->type == LT_AUTO)? i : line->type - 1;
835 
836 	    if (pt == ptdef) {
837 		line->ptype = 0;
838 	    } else {
839 		line->ptype = pt + 1;
840 	    }
841 	}
842     }
843 }
844 
plot_editor_sync(plot_editor * ed)845 static void plot_editor_sync (plot_editor *ed)
846 {
847     free(ed->old_lines);
848     old_lines_init(ed);
849 
850     free(ed->old_labels);
851     old_labels_init(ed);
852 
853     free(ed->old_arrows);
854     old_arrows_init(ed);
855 }
856 
default_bars_filename(void)857 static char *default_bars_filename (void)
858 {
859     static char barsname[FILENAME_MAX];
860 
861     if (*barsname == '\0') {
862 	sprintf(barsname, "%sdata%cplotbars%cnber.txt",
863 		gretl_home(), SLASH, SLASH);
864     }
865 
866     return barsname;
867 }
868 
869 static char user_barsfile[FILENAME_MAX];
870 
871 /* called on destruction of a plot dialog, if the user has
872    selected a user-defined "plotbars" file, so that is will
873    be remembered for use with the next plot
874 */
875 
remember_user_bars_file(const char * fname)876 static void remember_user_bars_file (const char *fname)
877 {
878     *user_barsfile = '\0';
879     strncat(user_barsfile, fname, FILENAME_MAX-1);
880 }
881 
882 /* if there's a saved filename for a user-defined
883    "plotbars" file, append it to the list of options
884 */
885 
maybe_append_user_bars_file(GtkComboBox * combo,plot_editor * ed)886 static gboolean maybe_append_user_bars_file (GtkComboBox *combo,
887 					     plot_editor *ed)
888 {
889     if (ed->user_barsfile == NULL && *user_barsfile != '\0') {
890 	ed->user_barsfile = g_strdup(user_barsfile);
891     }
892 
893     if (ed->user_barsfile != NULL) {
894 	const char *p = path_last_slash_const(ed->user_barsfile);
895 	const char *show = (p != NULL)? (p+1) : ed->user_barsfile;
896 
897 	combo_box_append_text(combo, show);
898 	return TRUE;
899     } else {
900 	return FALSE;
901     }
902 }
903 
try_adding_plotbars(GPT_SPEC * spec,plot_editor * ed)904 static void try_adding_plotbars (GPT_SPEC *spec, plot_editor *ed)
905 {
906     png_plot *plot = (png_plot *) spec->ptr;
907     double xmin = -1, xmax = -1;
908     double ymin = -1, ymax = -1;
909     int err;
910 
911     err = plot_get_coordinates(plot, &xmin, &xmax, &ymin, &ymax);
912 
913     if (!err) {
914 	int active = gtk_combo_box_get_active(GTK_COMBO_BOX(ed->barscombo));
915 	const char *fname = NULL;
916 
917 	if (active == 1 && ed->user_barsfile != NULL) {
918 	    fname = ed->user_barsfile;
919 	} else if (active == 0) {
920 	    fname = default_bars_filename();
921 	}
922 
923 	if (fname != NULL) {
924 	    if (ed->axis_range[1].isauto != NULL &&
925 		!button_is_active(ed->axis_range[1].isauto)) {
926 		/* we have a manual y-axis setting that may override the
927 		   current (ymin, ymax) as read from the plot */
928 		ymin = spec->range[1][0];
929 		ymax = spec->range[1][1];
930 	    } else {
931 		/* freeze the current (ymin, ymax) so that the addition
932 		   of bars doesn't disturb the range */
933 		spec->range[1][0] = ymin;
934 		spec->range[1][1] = ymax;
935 	    }
936 
937 	    err = plotspec_add_bars_info(spec, xmin, xmax,
938 					 ymin, ymax, fname);
939 	}
940     }
941 
942     if (err) {
943 	gui_errmsg(err);
944 	gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(ed->bars_check),
945 				     FALSE);
946     }
947 }
948 
should_apply_changes(GtkWidget * w)949 static gboolean should_apply_changes (GtkWidget *w)
950 {
951     return w != NULL && gtk_widget_is_sensitive(w);
952 }
953 
954 /* Respond to "OK" or "Apply", or to hitting the Enter key in
955    some parts of the plot editor dialog */
956 
apply_gpt_changes(GtkWidget * w,plot_editor * ed)957 static void apply_gpt_changes (GtkWidget *w, plot_editor *ed)
958 {
959     GPT_SPEC *spec = ed->spec;
960     GPT_LINE *line;
961     int suppress_y2 = 0;
962     gchar *s;
963     int i, k, err = 0;
964 
965     for (i=0; i<NTITLES; i++) {
966 	if (ed->gpt_titles[i].widget != NULL) {
967 	    entry_to_gp_string(ed->gpt_titles[i].widget, &spec->titles[i]);
968 	}
969     }
970 
971     if (should_apply_changes(ed->keycombo)) {
972         s = combo_box_get_active_text(ed->keycombo);
973 	spec->keyspec = gp_keypos_from_display_name(s);
974 	g_free(s);
975     }
976 
977     spec->flags &= ~GPT_Y2AXIS;
978 
979     if (ed->y2_check != NULL && button_is_active(ed->y2_check)) {
980 	suppress_y2 = 1;
981     }
982 
983     for (i=0; i<ed->gui_nlines; i++) {
984 	line = &spec->lines[i];
985 	line->yaxis = 1;
986 	if (!suppress_y2 && ed->yaxiscombo[i] != NULL) {
987 	    s = combo_box_get_active_text(ed->yaxiscombo[i]);
988 	    if (!strcmp(s, _("right"))) {
989 		line->yaxis = 2;
990 	    }
991 	    if (line->yaxis == 2) {
992 		spec->flags |= GPT_Y2AXIS;
993 	    }
994 	    g_free(s);
995 	}
996     }
997 
998     if (spec->code == PLOT_REGULAR ||
999 	spec->code == PLOT_CURVE ||
1000 	spec->code == PLOT_MANY_TS) {
1001 	k = (spec->flags & GPT_Y2AXIS)? 3 : 2;
1002 	for (i=0; i<k; i++) {
1003 	    if (ed->axis_range[i].isauto != NULL) {
1004 		if (button_is_active(ed->axis_range[i].isauto)) {
1005 		    spec->range[i][0] = NADBL;
1006 		    spec->range[i][1] = NADBL;
1007 		} else {
1008 		    entry_to_gp_double(ed->axis_range[i].min, &spec->range[i][0]);
1009 		    entry_to_gp_double(ed->axis_range[i].max, &spec->range[i][1]);
1010 		    err = validate_range(spec->range[i]);
1011 		}
1012 	    }
1013 	    if (spec->code == PLOT_REGULAR && ed->axis_range[i].lbase != NULL) {
1014 		if (gtk_widget_is_sensitive(ed->axis_range[i].lbase)) {
1015 		    err = set_logscale_from_entry(spec, i, ed->axis_range[i].lbase);
1016 		} else {
1017 		    spec->logbase[i] = 0.0;
1018 		}
1019 	    }
1020 	}
1021     }
1022 
1023     for (i=0; i<ed->gui_nlines && !err; i++) {
1024 	GtkWidget *combo = ed->stylecombo[i];
1025 
1026 	line = &spec->lines[i];
1027 	if (should_apply_changes(combo)) {
1028 	    combo_to_gp_style(combo, &line->style);
1029 	    maybe_set_point_type(line, combo, i);
1030 	}
1031 	if (should_apply_changes(ed->linetitle[i])) {
1032 	    entry_to_gp_string(ed->linetitle[i], &line->title);
1033 	}
1034 	if (should_apply_changes(ed->lineformula[i])) {
1035 	    entry_to_gp_string(ed->lineformula[i], &line->formula);
1036 	}
1037 	if (should_apply_changes(ed->linewidth[i])) {
1038 	    line->width = spinner_get_float(ed->linewidth[i]);
1039 	}
1040 	if (should_apply_changes(ed->colorsel[i])) {
1041 	    apply_line_color(ed->colorsel[i], spec, i);
1042 	}
1043 	if (should_apply_changes(ed->dtcombo[i])) {
1044 	    line->dtype =
1045 		gtk_combo_box_get_active(GTK_COMBO_BOX(ed->dtcombo[i])) + 1;
1046 	}
1047 	if (should_apply_changes(ed->pointsize[i])) {
1048 	    line->pscale = spinner_get_float(ed->pointsize[i]);
1049 	}
1050     }
1051 
1052     for (i=0; i<ed->gui_nlabels && !err; i++) {
1053 	entry_to_gp_label(ed->labeltext[i], spec->labels[i].text,
1054 			  sizeof spec->labels[0].text);
1055 	if (string_is_blank(spec->labels[i].text)) {
1056 	    continue;
1057 	}
1058 	err = get_label_pos_from_entry(ed->labelpos[i], spec->labels[i].pos);
1059 	if (!err) {
1060 	    spec->labels[i].just =
1061 		gtk_combo_box_get_active(GTK_COMBO_BOX(ed->labeljust[i]));
1062 	}
1063     }
1064 
1065     for (i=0; i<ed->gui_narrows && !err; i++) {
1066 	err = get_arrow_spec_from_entries(ed, i, &spec->arrows[i]);
1067     }
1068 
1069     if (!err && ed->border_check != NULL) {
1070 	if (button_is_active(ed->border_check)) {
1071 	    /* full border */
1072 	    spec->border = GP_BORDER_DEFAULT;
1073 	} else {
1074 	    /* left and bottom only */
1075 	    spec->border = 3;
1076 	}
1077     }
1078 
1079     if (!err && ed->grid_check != NULL) {
1080 	spec->flags &= ~(GPT_GRID_Y | GPT_GRID_X);
1081 	if (button_is_active(ed->grid_check)) {
1082 	    int i = gtk_combo_box_get_active(GTK_COMBO_BOX(ed->grid_combo));
1083 
1084 	    if (i == 0) {
1085 		spec->flags |= GPT_GRID_Y;
1086 	    } else if (i == 1) {
1087 		spec->flags |= GPT_GRID_X;
1088 	    } else {
1089 		spec->flags |= (GPT_GRID_Y | GPT_GRID_X);
1090 	    }
1091 	}
1092     }
1093 
1094     if (!err) {
1095 	if (ed->fontcheck != NULL && button_is_active(ed->fontcheck)) {
1096 	    if (ed->spec->fontstr != NULL && *ed->spec->fontstr != '\0') {
1097 		set_gretl_png_font(ed->spec->fontstr);
1098 		update_persistent_graph_font();
1099 	    }
1100 	}
1101     }
1102 
1103     if (!err && should_apply_changes(ed->fitcombo)) {
1104 	err = set_fit_type_from_combo(ed->fitcombo, spec);
1105 	if ((spec->fit == PLOT_FIT_NONE || spec->fit == PLOT_FIT_LOESS) &&
1106 	    ed->fitformula != NULL && spec->n_lines > 1) {
1107 	    /* scrub irrelevant formula, if any */
1108 	    if (spec->lines[1].formula != NULL) {
1109 		g_free(spec->lines[1].formula);
1110 		spec->lines[1].formula = NULL;
1111 	    }
1112 	}
1113     }
1114 
1115     if (!err && should_apply_changes(ed->bars_check)) {
1116 	if (button_is_active(ed->bars_check)) {
1117 	    try_adding_plotbars(spec, ed);
1118 	} else {
1119 	    plotspec_remove_bars(spec);
1120 	}
1121     }
1122 
1123     if (!err) {
1124 	png_plot *plot = (png_plot *) spec->ptr;
1125 
1126 	set_plot_has_y2_axis(plot, spec->flags & GPT_Y2AXIS);
1127 	redisplay_edited_plot(plot);
1128 	if (plot_is_saved(plot)) {
1129 	    mark_session_changed();
1130 	}
1131     }
1132 
1133     plot_editor_sync(ed);
1134 }
1135 
set_keyspec_sensitivity(plot_editor * ed)1136 static void set_keyspec_sensitivity (plot_editor *ed)
1137 {
1138     gboolean state = FALSE;
1139 
1140     /* key doesn't make sense for boxplots? */
1141 
1142     if (ed->spec->code != PLOT_BOXPLOTS) {
1143 	const char *p;
1144 	int i;
1145 
1146 	for (i=0; i<ed->gui_nlines; i++) {
1147 	    p = gtk_entry_get_text(GTK_ENTRY(ed->linetitle[i]));
1148 	    if (p != NULL && *p != 0) {
1149 		state = TRUE;
1150 		break;
1151 	    }
1152 	}
1153     }
1154 
1155     gtk_widget_set_sensitive(ed->keycombo, state);
1156 }
1157 
1158 #define TAB_MAIN_COLS 3
1159 
plot_editor_set_fontname(plot_editor * ed,const char * name)1160 static void plot_editor_set_fontname (plot_editor *ed, const char *name)
1161 {
1162     free(ed->spec->fontstr);
1163     ed->spec->fontstr = gretl_strdup(name);
1164 }
1165 
1166 #if USE_GTK_FONT_CHOOSER
1167 
graph_font_selection_ok(GtkWidget * w,GtkFontChooser * fc)1168 static void graph_font_selection_ok (GtkWidget *w, GtkFontChooser *fc)
1169 {
1170     gchar *fontname = gtk_font_chooser_get_font(fc);
1171 
1172     if (fontname != NULL && *fontname != '\0') {
1173 	gpointer p = g_object_get_data(G_OBJECT(fc), "parent");
1174 	gint type = widget_get_int(fc, "parent-type");
1175 
1176 	if (type < 2) {
1177 	    GtkWidget *b = g_object_get_data(G_OBJECT(fc), "launcher");
1178 	    gchar *title = g_strdup_printf(_("font: %s"), fontname);
1179 
1180 	    gtk_button_set_label(GTK_BUTTON(b), title);
1181 	    g_free(title);
1182 	}
1183 	if (type == 0) {
1184 	    plot_editor_set_fontname(p, fontname);
1185 	} else if (type == 1) {
1186 	    pdf_ps_saver_set_fontname(p, fontname);
1187 	} else if (type == 2) {
1188 	    activate_plot_font_choice(p, fontname);
1189 	}
1190     }
1191 
1192     g_free(fontname);
1193     gtk_widget_destroy(GTK_WIDGET(fc));
1194 }
1195 
real_graph_font_selector(GtkButton * button,gpointer p,int type,const char * default_font)1196 static void real_graph_font_selector (GtkButton *button, gpointer p, int type,
1197 				      const char *default_font)
1198 {
1199     static GtkWidget *fontsel = NULL;
1200     char fontname[128];
1201     GtkWidget *b;
1202 
1203     if (fontsel != NULL) {
1204 	gtk_window_present(GTK_WINDOW(fontsel));
1205         return;
1206     }
1207 
1208     if (default_font == NULL) {
1209 	if (type == 1) {
1210 	    default_font = pdf_ps_saver_current_font(p);
1211 	} else {
1212 	    default_font = gretl_png_font();
1213 	}
1214     }
1215 
1216     fontsel = gtk_font_chooser_dialog_new(_("Font for graphs"), NULL);
1217     adjust_fontspec_string(fontname, default_font, DROP_COMMA);
1218     gtk_font_chooser_set_font(GTK_FONT_CHOOSER(fontsel), fontname);
1219 
1220     /* attach data items */
1221     if (button != NULL) {
1222 	g_object_set_data(G_OBJECT(fontsel), "launcher", button);
1223     }
1224     g_object_set_data(G_OBJECT(fontsel), "parent", p);
1225     g_object_set_data(G_OBJECT(fontsel), "parent-type", GINT_TO_POINTER(type));
1226 
1227     gtk_window_set_position(GTK_WINDOW(fontsel), GTK_WIN_POS_MOUSE);
1228 
1229     /* destruction signals */
1230     g_signal_connect(G_OBJECT(fontsel), "destroy",
1231 		     G_CALLBACK(gtk_widget_destroyed),
1232 		     &fontsel);
1233     g_signal_connect(G_OBJECT(fontsel), "destroy",
1234 		     G_CALLBACK(gtk_main_quit), NULL);
1235 
1236     /* button signals */
1237     b = gtk_dialog_get_widget_for_response(GTK_DIALOG(fontsel),
1238 					   GTK_RESPONSE_OK);
1239     if (b != NULL) {
1240 	g_signal_connect(G_OBJECT(b), "clicked",
1241 			 G_CALLBACK(graph_font_selection_ok),
1242 			 fontsel);
1243     }
1244 
1245     b = gtk_dialog_get_widget_for_response(GTK_DIALOG(fontsel),
1246 					   GTK_RESPONSE_CANCEL);
1247     if (b != NULL) {
1248 	g_signal_connect(G_OBJECT(b), "clicked",
1249 			 G_CALLBACK(delete_widget),
1250 			 fontsel);
1251     }
1252 
1253     gtk_widget_show(fontsel);
1254 
1255     gtk_main();
1256 }
1257 
1258 #else /* using GtkFontSelectionDialog */
1259 
1260 /* callback from OK button in graph font selector */
1261 
graph_font_selection_ok(GtkWidget * w,GtkFontSelectionDialog * fs)1262 static void graph_font_selection_ok (GtkWidget *w, GtkFontSelectionDialog *fs)
1263 {
1264     gchar *fontname;
1265 
1266     fontname = gtk_font_selection_dialog_get_font_name(fs);
1267 
1268     if (fontname != NULL && *fontname != '\0') {
1269 	gpointer p = g_object_get_data(G_OBJECT(fs), "parent");
1270 	gint type = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(fs), "parent-type"));
1271 
1272 	if (type < 2) {
1273 	    GtkWidget *b = g_object_get_data(G_OBJECT(fs), "launcher");
1274 	    gchar *title = g_strdup_printf(_("font: %s"), fontname);
1275 
1276 	    gtk_button_set_label(GTK_BUTTON(b), title);
1277 	    g_free(title);
1278 	}
1279 	if (type == 0) {
1280 	    plot_editor_set_fontname(p, fontname);
1281 	} else if (type == 1) {
1282 	    pdf_ps_saver_set_fontname(p, fontname);
1283 	} else if (type == 2) {
1284 	    activate_plot_font_choice(p, fontname);
1285 	}
1286     }
1287 
1288     g_free(fontname);
1289     gtk_widget_destroy(GTK_WIDGET(fs));
1290 }
1291 
real_graph_font_selector(GtkButton * button,gpointer p,int type,const char * default_font)1292 static void real_graph_font_selector (GtkButton *button, gpointer p, int type,
1293 				      const char *default_font)
1294 {
1295     static GtkWidget *fontsel = NULL;
1296     char fontname[128];
1297     GtkWidget *b;
1298 
1299     if (fontsel != NULL) {
1300 	gtk_window_present(GTK_WINDOW(fontsel));
1301         return;
1302     }
1303 
1304     if (default_font == NULL) {
1305 	if (type == 1) {
1306 	    default_font = pdf_ps_saver_current_font(p);
1307 	} else {
1308 	    default_font = gretl_png_font();
1309 	}
1310     }
1311 
1312     adjust_fontspec_string(fontname, default_font, DROP_COMMA);
1313     fontsel = gtk_font_selection_dialog_new(_("Font for graphs"));
1314     gtk_font_selection_dialog_set_font_name(GTK_FONT_SELECTION_DIALOG(fontsel),
1315 					    fontname);
1316     if (button != NULL) {
1317 	g_object_set_data(G_OBJECT(fontsel), "launcher", button);
1318     }
1319     g_object_set_data(G_OBJECT(fontsel), "parent", p);
1320     g_object_set_data(G_OBJECT(fontsel), "parent-type", GINT_TO_POINTER(type));
1321 
1322     gtk_window_set_position(GTK_WINDOW(fontsel), GTK_WIN_POS_MOUSE);
1323 
1324     g_signal_connect(G_OBJECT(fontsel), "destroy",
1325 		     G_CALLBACK(gtk_widget_destroyed),
1326 		     &fontsel);
1327     g_signal_connect(G_OBJECT(fontsel), "destroy",
1328 		     G_CALLBACK(gtk_main_quit), NULL);
1329 
1330     b = gtk_font_selection_dialog_get_ok_button(GTK_FONT_SELECTION_DIALOG(fontsel));
1331     g_signal_connect(G_OBJECT(b), "clicked", G_CALLBACK(graph_font_selection_ok),
1332 		     fontsel);
1333     b = gtk_font_selection_dialog_get_cancel_button(GTK_FONT_SELECTION_DIALOG(fontsel));
1334     g_signal_connect(G_OBJECT(b), "clicked", G_CALLBACK(delete_widget),
1335 		     fontsel);
1336 
1337     gtk_widget_show(fontsel);
1338 
1339     gtk_main();
1340 }
1341 
1342 #endif /* end aternative font selectors */
1343 
graph_font_selector(GtkButton * button,gpointer p)1344 static void graph_font_selector (GtkButton *button, gpointer p)
1345 {
1346     plot_editor *ed = p;
1347 
1348     real_graph_font_selector(button, p, 0, ed->spec->fontstr);
1349 }
1350 
pdf_font_selector(GtkButton * button,gpointer p)1351 void pdf_font_selector (GtkButton *button, gpointer p)
1352 {
1353     real_graph_font_selector(button, p, 1, NULL);
1354 }
1355 
plot_show_font_selector(png_plot * plot,const char * currfont)1356 void plot_show_font_selector (png_plot *plot, const char *currfont)
1357 {
1358     real_graph_font_selector(NULL, plot, 2, currfont);
1359 }
1360 
strip_lr(gchar * txt)1361 static void strip_lr (gchar *txt)
1362 {
1363     gchar *test;
1364     gchar *p;
1365 
1366     test = g_strdup_printf("(%s)", _("left"));
1367     p = strstr(txt, test);
1368     g_free(test);
1369     if (p != NULL) {
1370 	*p = '\0';
1371     } else {
1372 	test = g_strdup_printf("(%s)", _("right"));
1373 	p = strstr(txt, test);
1374 	g_free(test);
1375 	if (p != NULL) {
1376 	   *p = '\0';
1377 	}
1378     }
1379 }
1380 
toggle_axis_selection(GtkWidget * w,plot_editor * ed)1381 static void toggle_axis_selection (GtkWidget *w, plot_editor *ed)
1382 {
1383     int no_y2 = button_is_active(w);
1384     int i;
1385 
1386     for (i=0; i<ed->gui_nlines; i++) {
1387 	if (ed->yaxiscombo[i] != NULL) {
1388 	    gtk_widget_set_sensitive(ed->yaxiscombo[i], !no_y2);
1389 	}
1390     }
1391 }
1392 
1393 /* specific to BOXCOLOR selection: return this particular
1394    value to its default */
1395 
boxcolor_default_callback(GtkWidget * w,GtkWidget * button)1396 static void boxcolor_default_callback (GtkWidget *w,
1397 				       GtkWidget *button)
1398 {
1399     graph_palette_reset(0);
1400     boxcolor_patch_button_reset(button);
1401     update_persistent_graph_colors();
1402 }
1403 
table_add_row(GtkWidget * tbl,int * rows,int cols)1404 static void table_add_row (GtkWidget *tbl, int *rows, int cols)
1405 {
1406     *rows += 1;
1407     gtk_table_resize(GTK_TABLE(tbl), *rows, cols);
1408 }
1409 
color_reset_button(GtkWidget * tbl,int row)1410 static GtkWidget *color_reset_button (GtkWidget *tbl, int row)
1411 {
1412     GtkWidget *button, *hbox;
1413 
1414     hbox = gtk_hbox_new(FALSE, 2);
1415     button = gtk_button_new_with_label(_("Reset to default"));
1416     gtk_box_pack_start(GTK_BOX(hbox), button, FALSE, FALSE, 10);
1417     gtk_table_attach(GTK_TABLE(tbl), hbox, 0, 2, row, row+1,
1418 		     GTK_FILL, 0, 0, 5);
1419     gtk_widget_show_all(hbox);
1420 
1421     return button;
1422 }
1423 
add_boxcolor_selector(GtkWidget * tbl,int cols,int * rows,GtkWidget * notebook)1424 static void add_boxcolor_selector (GtkWidget *tbl, int cols,
1425 				   int *rows, GtkWidget *notebook)
1426 {
1427     GtkWidget *hbox, *label;
1428     GtkWidget *button, *reset;
1429     int row = *rows;
1430 
1431     /* add rows for single color selector and reset */
1432     *rows += 2;
1433     gtk_table_resize(GTK_TABLE(tbl), *rows, cols);
1434 
1435     /* hbox containing label and color button into hbox */
1436     hbox = gtk_hbox_new(FALSE, 2);
1437     label = gtk_label_new(_("Fill color"));
1438     gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 5);
1439     button = boxcolor_patch_button();
1440     gtk_box_pack_start(GTK_BOX(hbox), button, FALSE, FALSE, 5);
1441     gtk_table_attach_defaults(GTK_TABLE(tbl), hbox, 0, 1,
1442 			      row, row + 1);
1443     gtk_widget_show_all(hbox);
1444 
1445     reset = color_reset_button(tbl, row + 2);
1446     g_signal_connect(G_OBJECT(reset), "clicked",
1447 		     G_CALLBACK(boxcolor_default_callback),
1448 		     button);
1449 }
1450 
combo_set_ignore_changed(GtkComboBox * box,gboolean s)1451 static void combo_set_ignore_changed (GtkComboBox *box, gboolean s)
1452 {
1453     if (s) {
1454 	g_object_set_data(G_OBJECT(box), "ignore-changed",
1455 			  GINT_TO_POINTER(1));
1456     } else {
1457 	g_object_set_data(G_OBJECT(box), "ignore-changed",
1458 			  NULL);
1459     }
1460 }
1461 
1462 /* callback from the file-open selector: if the user
1463    cancelled then @fname will be NULL
1464 */
1465 
set_plotbars_filename(const char * fname,gpointer data)1466 void set_plotbars_filename (const char *fname, gpointer data)
1467 {
1468     plot_editor *ed = (plot_editor *) data;
1469     GtkComboBox *box = GTK_COMBO_BOX(ed->barscombo);
1470 
1471     /* we should block response to the "changed" signal
1472        for the duration of this adjustment */
1473     combo_set_ignore_changed(box, TRUE);
1474 
1475     if (fname == NULL) {
1476 	/* canceled or error */
1477 	if (ed->user_barsfile == NULL) {
1478 	    gtk_combo_box_set_active(box, 0);
1479 	}
1480     } else {
1481 	/* we got a completed file selection */
1482 	const char *p = path_last_slash_const(fname);
1483 	const char *show = (p != NULL)? (p+1) : fname;
1484 	int had_userfile = ed->user_barsfile != NULL;
1485 
1486 	if (had_userfile) {
1487 	    g_free(ed->user_barsfile);
1488 	    combo_box_remove(box, 2);
1489 	    combo_box_remove(box, 1);
1490 	} else {
1491 	    combo_box_remove(box, 1);
1492 	}
1493 	ed->user_barsfile = g_strdup(fname);
1494 	combo_box_append_text(box, show);
1495 	combo_box_append_text(box, _("other..."));
1496 	gtk_combo_box_set_active(box, 1);
1497     }
1498 
1499     /* unblock the "changed" signal */
1500     combo_set_ignore_changed(box, FALSE);
1501 }
1502 
1503 /* "changed" callback from the combo box listing the default
1504    plotbars file and possibly a user-specified file: the point
1505    of this callback is to allow the user to add a new
1506    plotbars file.
1507 */
1508 
plot_bars_changed(GtkComboBox * box,plot_editor * ed)1509 static void plot_bars_changed (GtkComboBox *box, plot_editor *ed)
1510 {
1511     int i = gtk_combo_box_get_active(box);
1512 
1513     if (g_object_get_data(G_OBJECT(box), "ignore-changed")) {
1514 	return;
1515     }
1516 
1517     if (i == 0) {
1518 	ed->active_bars = 0; /* selected the default */
1519     } else if (i == 1 && ed->user_barsfile != NULL) {
1520 	ed->active_bars = 1; /* selected a previously opened own file */
1521     } else if (i == 2 || (i == 1 && ed->user_barsfile == NULL)) {
1522 	/* selected "other..." */
1523 	const char *msg = N_("To add your own \"bars\" to a plot, you must supply the\n"
1524 			     "name of a plain text file containing pairs of dates.");
1525 	GtkWidget *dlg;
1526 	gint ret;
1527 
1528 	dlg = gtk_dialog_new_with_buttons("gretl",
1529 					  GTK_WINDOW(ed->dialog),
1530 					  GTK_DIALOG_MODAL |
1531 					  GTK_DIALOG_DESTROY_WITH_PARENT,
1532 					  GTK_STOCK_OPEN, 1,
1533 					  _("See example"), 2,
1534 					  GTK_STOCK_CANCEL, 3,
1535 					  NULL);
1536 
1537 	gretl_dialog_add_message(dlg, _(msg));
1538 #if GTK_MAJOR_VERSION < 3
1539 	gtk_dialog_set_has_separator(GTK_DIALOG(dlg), FALSE);
1540 #endif
1541 	gtk_window_set_keep_above(GTK_WINDOW(dlg), TRUE);
1542 	ret = gtk_dialog_run(GTK_DIALOG(dlg));
1543 	gtk_widget_destroy(dlg);
1544 
1545 	if (ret != 1 && ed->active_bars >= 0) {
1546 	    /* not opening a file: revert to default selection */
1547 	    gtk_combo_box_set_active(box, ed->active_bars);
1548 	}
1549 
1550 	if (ret == 1) {
1551 	    /* open */
1552 	    file_selector_with_parent(OPEN_BARS, FSEL_DATA_MISC,
1553 				      ed, ed->dialog);
1554 	} else if (ret == 2) {
1555 	    /* look at the example file */
1556 	    const char *fname = default_bars_filename();
1557 
1558 	    view_file(fname, 0, 0, 60, 420, VIEW_FILE);
1559 	}
1560     }
1561 }
1562 
gp_dialog_table(int rows,int cols,GtkWidget * vbox)1563 static GtkWidget *gp_dialog_table (int rows, int cols,
1564 				   GtkWidget *vbox)
1565 {
1566     GtkWidget *tbl;
1567 
1568     tbl = gtk_table_new(rows, cols, FALSE);
1569     gtk_table_set_row_spacings(GTK_TABLE(tbl), 5);
1570     gtk_table_set_col_spacings(GTK_TABLE(tbl), 5);
1571     gtk_box_pack_start(GTK_BOX(vbox), tbl, FALSE, FALSE, 0);
1572 
1573     return tbl;
1574 }
1575 
gp_dialog_vbox(void)1576 static GtkWidget *gp_dialog_vbox (void)
1577 {
1578     GtkWidget *vbox;
1579 
1580     vbox = gtk_vbox_new(FALSE, 0);
1581     gtk_container_set_border_width(GTK_CONTAINER(vbox), 10);
1582 
1583     return vbox;
1584 }
1585 
gp_page_vbox(GtkWidget * notebook,char * str)1586 static GtkWidget *gp_page_vbox (GtkWidget *notebook, char *str)
1587 {
1588     GtkWidget *vbox;
1589     GtkWidget *label;
1590 
1591     vbox = gtk_vbox_new(FALSE, 0);
1592     gtk_container_set_border_width(GTK_CONTAINER(vbox), 10);
1593     gtk_widget_show(vbox);
1594     label = gtk_label_new(str);
1595     gtk_widget_show(label);
1596     gtk_notebook_append_page(GTK_NOTEBOOK(notebook), vbox, label);
1597 
1598     return vbox;
1599 }
1600 
semilog_is_ok(GPT_SPEC * spec)1601 static int semilog_is_ok (GPT_SPEC *spec)
1602 {
1603     const double *x = spec->data->val;
1604 
1605     if (x == NULL) {
1606 	return 0;
1607     } else {
1608 	const double *y = x + spec->nobs;
1609 
1610 	return gretl_ispositive(0, spec->nobs - 1, y, 1);
1611     }
1612 }
1613 
log_x_ok(GPT_SPEC * spec)1614 static int log_x_ok (GPT_SPEC *spec)
1615 {
1616     const double *x = spec->data->val;
1617 
1618     if (x == NULL) {
1619 	return 0;
1620     } else {
1621 	return gretl_ispositive(0, spec->nobs - 1, x, 1);
1622     }
1623 }
1624 
plotspec_gridval(GPT_SPEC * spec)1625 static int plotspec_gridval (GPT_SPEC *spec)
1626 {
1627     int val = 0;
1628 
1629     if (spec->flags & (GPT_GRID_Y | GPT_GRID_X)) {
1630 	if (!(spec->flags & GPT_GRID_X)) {
1631 	    val = 1;
1632 	} else if (!(spec->flags & GPT_GRID_Y)) {
1633 	    val = 2;
1634 	} else {
1635 	    val = 3;
1636 	}
1637     }
1638 
1639     return val;
1640 }
1641 
show_bars_check(GPT_SPEC * spec)1642 static int show_bars_check (GPT_SPEC *spec)
1643 {
1644     if (!(spec->flags & GPT_TS)) {
1645 	return 0;
1646     }
1647 
1648     return (spec->pd == 1 ||
1649 	    spec->pd == 4 ||
1650 	    spec->pd == 12);
1651 }
1652 
1653 #define plot_has_tics(s) (strcmp(s->xtics, "none") || strcmp(s->ytics, "none"))
1654 
1655 #define bar_plot(c) (c == PLOT_BAR || c == PLOT_STACKED_BAR)
1656 
gpt_tab_main(plot_editor * ed,GPT_SPEC * spec)1657 static void gpt_tab_main (plot_editor *ed, GPT_SPEC *spec)
1658 {
1659     GtkWidget *label, *vbox, *tbl;
1660     GtkWidget *hsep, *button;
1661     gchar *title;
1662     int i, rows = 1;
1663     int kactive = 0;
1664 
1665     vbox = gp_page_vbox(ed->notebook, _("Main"));
1666 
1667     tbl = gp_dialog_table(rows, TAB_MAIN_COLS, vbox);
1668     gtk_widget_show(tbl);
1669 
1670     for (i=0; i<NTITLES; i++) {
1671 	if (ed->gpt_titles[i].tab == 0) {
1672 	    GtkWidget *entry;
1673 
1674 	    if (i > 0) {
1675 		table_add_row(tbl, &rows, TAB_MAIN_COLS);
1676 	    }
1677 
1678 	    label = gtk_label_new(_(ed->gpt_titles[i].desc));
1679 	    gtk_table_attach_defaults(GTK_TABLE (tbl),
1680 				      label, 0, 1, rows-1, rows);
1681 	    gtk_widget_show(label);
1682 
1683 	    entry = gtk_entry_new();
1684 	    gtk_table_attach_defaults(GTK_TABLE(tbl),
1685 				      entry, 1, TAB_MAIN_COLS,
1686 				      rows-1, rows);
1687 	    gp_string_to_entry(entry, spec->titles[i]);
1688 	    g_signal_connect(G_OBJECT(entry), "activate",
1689 			     G_CALLBACK(apply_gpt_changes),
1690 			     ed);
1691 	    gtk_widget_show(entry);
1692 	    ed->gpt_titles[i].widget = entry;
1693 	}
1694     }
1695 
1696     /* specify position of plot key or legend */
1697     table_add_row(tbl, &rows, TAB_MAIN_COLS);
1698     label = gtk_label_new(_("key position"));
1699     gtk_table_attach_defaults(GTK_TABLE(tbl),
1700 			      label, 0, 1, rows-1, rows);
1701     gtk_widget_show(label);
1702 
1703     ed->keycombo = gtk_combo_box_text_new();
1704     gtk_table_attach_defaults(GTK_TABLE(tbl),
1705 			      ed->keycombo, 1, TAB_MAIN_COLS, rows-1, rows);
1706     for (i=0; ; i++) {
1707 	gp_key_spec *kp = get_keypos_spec(i);
1708 
1709 	if (kp == NULL) {
1710 	    break;
1711 	}
1712 	combo_box_append_text(ed->keycombo, _(kp->str));
1713 	if (kp->id == spec->keyspec) {
1714 	    kactive = i;
1715 	}
1716     }
1717     gtk_combo_box_set_active(GTK_COMBO_BOX(ed->keycombo), kactive);
1718     gtk_widget_show(ed->keycombo);
1719 
1720     if (spec->fit != PLOT_FIT_NA) {
1721 	/* give choice of fitted line type, if applicable */
1722 	int semilog_ok = semilog_is_ok(spec);
1723 	int linlog_ok = log_x_ok(spec);
1724 
1725 	table_add_row(tbl, &rows, TAB_MAIN_COLS);
1726 
1727 	label = gtk_label_new(_("fitted line"));
1728 	gtk_table_attach_defaults(GTK_TABLE(tbl),
1729 				  label, 0, 1, rows-1, rows);
1730 	gtk_widget_show(label);
1731 
1732 	ed->fitcombo = gtk_combo_box_text_new();
1733 	gtk_table_attach_defaults(GTK_TABLE(tbl), ed->fitcombo,
1734 				  1, TAB_MAIN_COLS, rows-1, rows);
1735 
1736 	if (spec->flags & GPT_TS) {
1737 	    char *p, tmp[128];
1738 
1739 	    for (i=0; fittype_strings[i] != NULL; i++) {
1740 		if (i == PLOT_FIT_LOGLIN && !semilog_ok) {
1741 		    widget_set_int(ed->fitcombo, "no-semilog", 1);
1742 		    continue;
1743 		} else {
1744 		    strcpy(tmp, _(fittype_strings[i]));
1745 		    p = strchr(tmp, ':');
1746 		    if (p != NULL) {
1747 			gretl_charsub(tmp, 'x', 't');
1748 		    }
1749 		    combo_box_append_text(ed->fitcombo, tmp);
1750 		}
1751 	    }
1752 	} else {
1753 	    for (i=0; fittype_strings[i] != NULL; i++) {
1754 		if (i == PLOT_FIT_LOGLIN && !semilog_ok) {
1755 		    widget_set_int(ed->fitcombo, "no-semilog", 1);
1756 		    continue;
1757 		} else if (i == PLOT_FIT_LINLOG && !linlog_ok) {
1758 		    continue;
1759 		} else {
1760 		    combo_box_append_text(ed->fitcombo, _(fittype_strings[i]));
1761 		}
1762 	    }
1763 	}
1764 
1765 	gtk_combo_box_set_active(GTK_COMBO_BOX(ed->fitcombo), spec->fit);
1766 	widget_set_int(ed->fitcombo, "oldfit", spec->fit);
1767 	g_signal_connect(G_OBJECT(ed->fitcombo), "changed",
1768 			 G_CALLBACK(fit_type_changed), ed);
1769 	gtk_widget_show(ed->fitcombo);
1770     }
1771 
1772     if (spec->flags & GPT_Y2AXIS) {
1773 	/* give option of forcing a single y-axis */
1774 	table_add_row(tbl, &rows, TAB_MAIN_COLS);
1775 	ed->y2_check = gtk_check_button_new_with_label(_("Use only one y axis"));
1776 	gtk_table_attach_defaults(GTK_TABLE(tbl),
1777 				  ed->y2_check, 0, TAB_MAIN_COLS,
1778 				  rows-1, rows);
1779 	gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(ed->y2_check),
1780 				     FALSE);
1781 	g_signal_connect(G_OBJECT(ed->y2_check), "clicked",
1782 			 G_CALLBACK(toggle_axis_selection), ed);
1783 	gtk_widget_show(ed->y2_check);
1784     } else {
1785 	/* give option of removing/adding top & right border */
1786 	if (spec->border == GP_BORDER_DEFAULT || spec->border == 3) {
1787 	    table_add_row(tbl, &rows, TAB_MAIN_COLS);
1788 	    ed->border_check = gtk_check_button_new_with_label(_("Show full border"));
1789 	    gtk_table_attach_defaults(GTK_TABLE(tbl),
1790 				      ed->border_check, 0, TAB_MAIN_COLS,
1791 				      rows-1, rows);
1792 	    gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(ed->border_check),
1793 					 spec->border == GP_BORDER_DEFAULT);
1794 	    gtk_widget_show(ed->border_check);
1795 	}
1796     }
1797 
1798     if (plot_has_tics(spec) && !bar_plot(spec->code)) {
1799 	/* add show grid options */
1800 	const char *grid_opts[] = {
1801 	    N_("horizontal"),
1802 	    N_("vertical"),
1803 	    N_("both")
1804 	};
1805 	int gridval = plotspec_gridval(spec);
1806 	GtkWidget *combo;
1807 
1808 	/* check button */
1809 	table_add_row(tbl, &rows, TAB_MAIN_COLS);
1810 	ed->grid_check = gtk_check_button_new_with_label(_("Show grid"));
1811 	gtk_table_attach_defaults(GTK_TABLE(tbl), ed->grid_check,
1812 				  0, 1, rows-1, rows);
1813 	gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(ed->grid_check),
1814 				     gridval > 0);
1815 	gtk_widget_show(ed->grid_check);
1816 
1817 	/* plus combo selector */
1818 	ed->grid_combo = combo = gtk_combo_box_text_new();
1819 	for (i=0; i<3; i++) {
1820 	    combo_box_append_text(combo, _(grid_opts[i]));
1821 	}
1822 	gtk_widget_set_sensitive(combo, gridval > 0);
1823 	if (gridval > 0) {
1824 	    gtk_combo_box_set_active(GTK_COMBO_BOX(combo), gridval - 1);
1825 	} else {
1826 	    gtk_combo_box_set_active(GTK_COMBO_BOX(combo), 0);
1827 	}
1828 	gtk_table_attach(GTK_TABLE(tbl), combo,
1829 			 1, 2, rows-1, rows,
1830 			 GTK_FILL, 0, 0, 0);
1831 	gtk_widget_show(combo);
1832 	sensitize_conditional_on(combo, ed->grid_check);
1833     }
1834 
1835     if (show_bars_check(spec)) {
1836 	/* option to display NBER "recession bars" or similar */
1837 	gboolean userbars = FALSE;
1838 	GtkWidget *combo;
1839 
1840 	/* check button */
1841 	table_add_row(tbl, &rows, TAB_MAIN_COLS);
1842 	ed->bars_check = gtk_check_button_new_with_label(_("Show bars"));
1843 	gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(ed->bars_check),
1844 				     spec->nbars > 0);
1845 	gtk_table_attach_defaults(GTK_TABLE(tbl), ed->bars_check,
1846 				  0, 1, rows-1, rows);
1847 	gtk_widget_show(ed->bars_check);
1848 
1849 	/* plus combo selector */
1850 	ed->barscombo = combo = gtk_combo_box_text_new();
1851 	combo_box_append_text(combo, _("NBER recessions"));
1852 	userbars = maybe_append_user_bars_file(GTK_COMBO_BOX(combo), ed);
1853 	combo_box_append_text(combo, _("other..."));
1854 	/* if we got a user-specific plotbars filename, make it
1855 	   the selected value, otherwise use the NBER file */
1856 	ed->active_bars = userbars ? 1 : 0;
1857 	gtk_combo_box_set_active(GTK_COMBO_BOX(combo), ed->active_bars);
1858 	gtk_widget_set_sensitive(combo, spec->nbars > 0);
1859 	g_signal_connect(G_OBJECT(combo), "changed",
1860 			 G_CALLBACK(plot_bars_changed),
1861 			 ed);
1862 	gtk_table_attach(GTK_TABLE(tbl), combo,
1863 			 1, 2, rows-1, rows,
1864 			 GTK_FILL, 0, 0, 0);
1865 	gtk_widget_show(combo);
1866 	sensitize_conditional_on(combo, ed->bars_check);
1867     }
1868 
1869     /* setting of graph font */
1870     table_add_row(tbl, &rows, TAB_MAIN_COLS);
1871     hsep = gtk_hseparator_new();
1872     gtk_table_attach_defaults(GTK_TABLE(tbl), hsep, 0, TAB_MAIN_COLS,
1873 			      rows-1, rows);
1874     gtk_widget_show(hsep);
1875 
1876     if (spec->fontstr != NULL) {
1877 	title = g_strdup_printf(_("font: %s"), spec->fontstr);
1878     } else {
1879 	title = g_strdup_printf(_("font: %s"), gretl_png_font());
1880     }
1881     button = gtk_button_new_with_label(title);
1882     table_add_row(tbl, &rows, TAB_MAIN_COLS);
1883     gtk_table_attach_defaults(GTK_TABLE(tbl), button, 0, TAB_MAIN_COLS,
1884 			      rows - 1, rows);
1885     g_signal_connect(G_OBJECT(button), "clicked",
1886 		     G_CALLBACK(graph_font_selector),
1887 		     ed);
1888     gtk_widget_show(button);
1889     g_free(title);
1890 
1891     /* set font as default button */
1892     table_add_row(tbl, &rows, TAB_MAIN_COLS);
1893     button = gtk_check_button_new_with_label(_("Set as default"));
1894     gtk_table_attach_defaults(GTK_TABLE(tbl), button, 0, TAB_MAIN_COLS,
1895 			      rows - 1, rows);
1896     gtk_widget_show(button);
1897     ed->fontcheck = button;
1898 
1899     if (frequency_plot_code(spec->code)) {
1900 	/* give option of setting fill color */
1901 	GtkWidget *hsep = gtk_hseparator_new();
1902 
1903 	table_add_row(tbl, &rows, TAB_MAIN_COLS);
1904 	gtk_table_attach_defaults(GTK_TABLE(tbl), hsep, 0, TAB_MAIN_COLS,
1905 				  rows - 1, rows);
1906 	gtk_widget_show(hsep);
1907 	add_boxcolor_selector(tbl, TAB_MAIN_COLS, &rows, ed->notebook);
1908     }
1909 }
1910 
linetitle_callback(GtkWidget * w,plot_editor * ed)1911 static void linetitle_callback (GtkWidget *w, plot_editor *ed)
1912 {
1913     set_keyspec_sensitivity(ed);
1914 }
1915 
1916 struct new_line_info_ {
1917     plot_editor *editor;
1918     GtkWidget *formula_entry;
1919     GtkWidget *dlg;
1920 };
1921 
1922 typedef struct new_line_info_ new_line_info;
1923 
gpt_tab_new_line(plot_editor * ed,new_line_info * nlinfo)1924 static void gpt_tab_new_line (plot_editor *ed, new_line_info *nlinfo)
1925 {
1926     GtkWidget *label, *tbl;
1927     GtkWidget *vbox, *hbox;
1928     gchar *text;
1929     int nrows = 1;
1930 
1931     vbox = gtk_dialog_get_content_area(GTK_DIALOG(nlinfo->dlg));
1932     hbox = gtk_hbox_new(FALSE, 5);
1933 
1934     tbl = gtk_table_new(nrows, 3, FALSE);
1935     gtk_table_set_row_spacings(GTK_TABLE(tbl), 5);
1936     gtk_table_set_col_spacings(GTK_TABLE(tbl), 5);
1937     gtk_box_pack_start(GTK_BOX(hbox), tbl, FALSE, FALSE, 5);
1938     gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0);
1939     gtk_widget_show(hbox);
1940     gtk_widget_show(tbl);
1941 
1942     /* identifier and formula text */
1943     gtk_table_resize(GTK_TABLE(tbl), ++nrows, 3);
1944     text = g_strdup_printf(_("line %d: "), ed->gui_nlines + 1);
1945     label = gtk_label_new(text);
1946     g_free(text);
1947     gtk_table_attach(GTK_TABLE(tbl), label, 0, 1, nrows-1, nrows,
1948 		     0, 0, 0, 0);
1949     gtk_widget_show(label);
1950 
1951     label = gtk_label_new(_("formula"));
1952     gtk_misc_set_alignment(GTK_MISC(label), 1, 0.5);
1953     gtk_table_attach_defaults(GTK_TABLE(tbl), label,
1954 			      1, 2, nrows-1, nrows);
1955     gtk_widget_show(label);
1956 
1957     nlinfo->formula_entry = gtk_entry_new();
1958     gtk_entry_set_text(GTK_ENTRY(nlinfo->formula_entry), "");
1959     gtk_entry_set_width_chars(GTK_ENTRY(nlinfo->formula_entry), 32);
1960     gtk_table_attach_defaults(GTK_TABLE(tbl),
1961 			      nlinfo->formula_entry, 2, 3, nrows-1, nrows);
1962     gtk_entry_set_activates_default(GTK_ENTRY(nlinfo->formula_entry), TRUE);
1963     gtk_widget_show(nlinfo->formula_entry);
1964 }
1965 
1966 /* back up existing spec components in case changes are not
1967    applied and we need to restore the prior state */
1968 
make_labels_backup(plot_editor * ed)1969 static int make_labels_backup (plot_editor *ed)
1970 {
1971     int err = 0;
1972 
1973     if (labels_not_synced(ed)) {
1974 	ed->old_labels = plotspec_clone_labels(ed->spec, &err);
1975 	if (!err) {
1976 	    ed->old_n_labels = ed->spec->n_labels;
1977 	}
1978     }
1979 
1980     return err;
1981 }
1982 
make_arrows_backup(plot_editor * ed)1983 static int make_arrows_backup (plot_editor *ed)
1984 {
1985     int err = 0;
1986 
1987     if (arrows_not_synced(ed)) {
1988 	ed->old_arrows = plotspec_clone_arrows(ed->spec, &err);
1989 	if (!err) {
1990 	    ed->old_n_arrows = ed->spec->n_arrows;
1991 	}
1992     }
1993 
1994     return err;
1995 }
1996 
make_lines_backup(plot_editor * ed)1997 static int make_lines_backup (plot_editor *ed)
1998 {
1999     int err = 0;
2000 
2001     if (lines_not_synced(ed)) {
2002 	ed->old_lines = plotspec_clone_lines(ed->spec, &err);
2003 	if (!err) {
2004 	    ed->old_n_lines = ed->spec->n_lines;
2005 	}
2006     }
2007 
2008     return err;
2009 }
2010 
allocate_label(plot_editor * ed)2011 static int allocate_label (plot_editor *ed)
2012 {
2013     int err;
2014 
2015     err = make_labels_backup(ed);
2016 
2017     if (!err) {
2018 	err = plotspec_add_label(ed->spec);
2019     }
2020 
2021     if (!err) {
2022 	err = add_label_widget(ed);
2023 	if (err) {
2024 	    ed->spec->n_labels -= 1;
2025 	}
2026     }
2027 
2028     if (err) {
2029 	nomem();
2030     }
2031 
2032     return err;
2033 }
2034 
allocate_arrow(plot_editor * ed)2035 static int allocate_arrow (plot_editor *ed)
2036 {
2037     int err = 0;
2038 
2039     err = make_arrows_backup(ed);
2040 
2041     if (!err) {
2042 	err = plotspec_add_arrow(ed->spec);
2043     }
2044 
2045     if (!err) {
2046 	err = add_arrow_widget(ed);
2047 	if (err) {
2048 	    ed->spec->n_arrows -= 1;
2049 	}
2050     }
2051 
2052     if (err) {
2053 	nomem();
2054     }
2055 
2056     return err;
2057 }
2058 
allocate_line(plot_editor * ed)2059 static int allocate_line (plot_editor *ed)
2060 {
2061     int err = make_lines_backup(ed);
2062 
2063     if (!err) {
2064 	err = plotspec_add_line(ed->spec);
2065     }
2066 
2067     if (!err) {
2068 	err = add_line_widget(ed);
2069 	if (err) {
2070 	    ed->spec->n_lines -= 1;
2071 	}
2072     }
2073 
2074     if (err) {
2075 	nomem();
2076     }
2077 
2078     return err;
2079 }
2080 
add_label_callback(GtkWidget * w,plot_editor * ed)2081 static void add_label_callback (GtkWidget *w, plot_editor *ed)
2082 {
2083     int err = allocate_label(ed);
2084 
2085     if (!err) {
2086 	/* re-fill the "labels" notebook page */
2087 	gint pgnum = widget_get_int(ed->notebook, "labels_page");
2088 
2089 	gtk_notebook_remove_page(GTK_NOTEBOOK(ed->notebook), pgnum);
2090 	gpt_tab_labels(ed, ed->spec, pgnum);
2091 	gtk_notebook_set_current_page(GTK_NOTEBOOK(ed->notebook), pgnum);
2092     }
2093 }
2094 
add_arrow_callback(GtkWidget * w,plot_editor * ed)2095 static void add_arrow_callback (GtkWidget *w, plot_editor *ed)
2096 {
2097     int err = allocate_arrow(ed);
2098 
2099     if (!err) {
2100 	/* re-fill the "arrows" notebook page */
2101 	gint pgnum = widget_get_int(ed->notebook, "arrows_page");
2102 
2103 	gtk_notebook_remove_page(GTK_NOTEBOOK(ed->notebook), pgnum);
2104 	gpt_tab_arrows(ed, ed->spec, pgnum);
2105 	gtk_notebook_set_current_page(GTK_NOTEBOOK(ed->notebook), pgnum);
2106     }
2107 }
2108 
2109 /* Set formula for a given line, taking input from a
2110    text entry box.  We'll convert from decimal comma
2111    to decimal dot, if necessary, and from '^' to '**'
2112    for exponentiation, to create a formula which is
2113    acceptable by gnuplot.
2114 */
2115 
set_gp_formula(GPT_LINE * line,const char * src)2116 static void set_gp_formula (GPT_LINE *line, const char *src)
2117 {
2118     int decom = (get_local_decpoint() == ',');
2119     const char *p;
2120     gchar *s;
2121     int i, n = 0;
2122 
2123     p = src;
2124     while (*p) {
2125 	if (*p == '^') n++;
2126 	n++;
2127 	p++;
2128     }
2129 
2130     if (n == 0) {
2131 	return;
2132     }
2133 
2134     s = g_malloc0(n+1);
2135     p = src;
2136     i = 0;
2137 
2138     while (*p) {
2139 	if (decom && *p == ',') {
2140 	    s[i++] = '.';
2141 	} else if (*p == '^') {
2142 	    s[i++] = '*';
2143 	    s[i++] = '*';
2144 	} else {
2145 	    s[i++] = *p;
2146 	}
2147 	p++;
2148     }
2149 
2150     g_free(line->formula);
2151     line->formula = g_strstrip(s);
2152 }
2153 
real_add_line(GtkWidget * w,new_line_info * nlinfo)2154 static void real_add_line (GtkWidget *w, new_line_info *nlinfo)
2155 {
2156     plot_editor *ed = nlinfo->editor;
2157     GPT_SPEC *spec = ed->spec;
2158     GPT_LINE *line;
2159     const gchar *s;
2160     gint pgnum;
2161     int err = 0;
2162 
2163     s = gtk_entry_get_text(GTK_ENTRY(nlinfo->formula_entry));
2164     if (s == NULL || *s == '\0') {
2165 	errbox(_("No formula was given"));
2166 	return;
2167     }
2168 
2169     /* add GUI apparatus for new line */
2170     err = allocate_line(ed);
2171     if (err) {
2172 	gtk_widget_destroy(nlinfo->dlg);
2173 	return;
2174     }
2175 
2176     line = &spec->lines[spec->n_lines - 1];
2177     set_gp_formula(line, s);
2178 
2179     line->style = GP_STYLE_LINES;
2180     line->type = spec->n_lines; /* assign next line style */
2181     line->flags = GP_LINE_USER;
2182 
2183     /* re-fill the "lines" notebook page */
2184     pgnum = widget_get_int(ed->notebook, "lines_page");
2185     gtk_notebook_remove_page(GTK_NOTEBOOK(ed->notebook), pgnum);
2186     gpt_tab_lines(ed, spec, pgnum);
2187     gtk_notebook_set_current_page(GTK_NOTEBOOK(ed->notebook), pgnum);
2188     if (spec->n_lines == 2) {
2189 	/* user-defined line has taken the place of a potential fitted line */
2190 	spec->fit = PLOT_FIT_NA;
2191 	if (ed->fitcombo != NULL) {
2192 	    gtk_widget_set_sensitive(ed->fitcombo, FALSE);
2193 	}
2194     }
2195 
2196     gtk_widget_destroy(nlinfo->dlg);
2197 }
2198 
add_line_callback(GtkWidget * w,plot_editor * ed)2199 static void add_line_callback (GtkWidget *w, plot_editor *ed)
2200 {
2201     new_line_info *nlinfo;
2202     GtkWidget *hbox;
2203     GtkWidget *button;
2204 
2205     nlinfo = mymalloc(sizeof *nlinfo);
2206     if (nlinfo == NULL) {
2207 	return;
2208     }
2209 
2210     nlinfo->editor = ed;
2211     nlinfo->dlg = gretl_dialog_new(NULL, ed->dialog, GRETL_DLG_BLOCK);
2212     gpt_tab_new_line(ed, nlinfo);
2213 
2214     hbox = gtk_dialog_get_action_area(GTK_DIALOG(nlinfo->dlg));
2215 
2216     button = cancel_button(hbox);
2217     g_signal_connect(G_OBJECT(button), "clicked",
2218 		     G_CALLBACK(delete_widget), nlinfo->dlg);
2219 
2220     button = ok_button(hbox);
2221     g_signal_connect(G_OBJECT(button), "clicked",
2222 		     G_CALLBACK(real_add_line), nlinfo);
2223     gtk_widget_grab_default(button);
2224 
2225     context_help_button(hbox, GPT_ADDLINE);
2226 
2227     gtk_widget_show_all(nlinfo->dlg);
2228 
2229     free(nlinfo);
2230 }
2231 
remove_line(GtkWidget * w,plot_editor * ed)2232 static void remove_line (GtkWidget *w, plot_editor *ed)
2233 {
2234     if (make_lines_backup(ed) != 0) {
2235 	nomem();
2236     } else {
2237 	int pgnum = widget_get_int(ed->notebook, "lines_page");
2238 	int lnum = widget_get_int(w, "linenum");
2239 
2240 	plotspec_delete_line(ed->spec, lnum);
2241 	ed->gui_nlines -= 1;
2242 
2243 	/* refresh the associated notebook page */
2244 	gtk_notebook_remove_page(GTK_NOTEBOOK(ed->notebook), pgnum);
2245 	gpt_tab_lines(ed, ed->spec, pgnum);
2246 	gtk_notebook_set_current_page(GTK_NOTEBOOK(ed->notebook), pgnum);
2247     }
2248 }
2249 
remove_label(GtkWidget * w,plot_editor * ed)2250 static void remove_label (GtkWidget *w, plot_editor *ed)
2251 {
2252     if (make_labels_backup(ed) != 0) {
2253 	nomem();
2254     } else {
2255 	int pgnum = widget_get_int(ed->notebook, "labels_page");
2256 	int labnum = widget_get_int(w, "labelnum");
2257 
2258 	plotspec_delete_label(ed->spec, labnum);
2259 	ed->gui_nlabels -= 1;
2260 
2261 	/* refresh the associated notebook page */
2262 	gtk_notebook_remove_page(GTK_NOTEBOOK(ed->notebook), pgnum);
2263 	gpt_tab_labels(ed, ed->spec, pgnum);
2264 	gtk_notebook_set_current_page(GTK_NOTEBOOK(ed->notebook), pgnum);
2265     }
2266 }
2267 
remove_arrow(GtkWidget * w,plot_editor * ed)2268 static void remove_arrow (GtkWidget *w, plot_editor *ed)
2269 {
2270     if (make_arrows_backup(ed) != 0) {
2271 	nomem();
2272     } else {
2273 	int pgnum = widget_get_int(ed->notebook, "arrows_page");
2274 	int anum = widget_get_int(w, "arrownum");
2275 
2276 	plotspec_delete_arrow(ed->spec, anum);
2277 	ed->gui_narrows -= 1;
2278 
2279 	/* refresh the associated notebook page */
2280 	gtk_notebook_remove_page(GTK_NOTEBOOK(ed->notebook), pgnum);
2281 	gpt_tab_arrows(ed, ed->spec, pgnum);
2282 	gtk_notebook_set_current_page(GTK_NOTEBOOK(ed->notebook), pgnum);
2283     }
2284 }
2285 
item_remove_button(GtkWidget * tbl,int row,plot_editor * ed,int i,int j)2286 static void item_remove_button (GtkWidget *tbl, int row,
2287 				plot_editor *ed, int i,
2288 				int j)
2289 {
2290     GtkWidget *button;
2291     const gchar *key[] = {
2292 	"linenum",
2293 	"labelnum",
2294 	"arrownum"
2295     };
2296     GCallback cb[] = {
2297 	G_CALLBACK(remove_line),
2298 	G_CALLBACK(remove_label),
2299 	G_CALLBACK(remove_arrow)
2300     };
2301 
2302     button = gtk_button_new_with_label(_("Remove"));
2303     widget_set_int(button, key[j], i);
2304     g_signal_connect(G_OBJECT(button), "clicked",
2305 		     G_CALLBACK(cb[j]), ed);
2306     gtk_table_attach_defaults(GTK_TABLE(tbl),
2307 			      button, 0, 1, row-1, row);
2308     gtk_widget_show(button);
2309 }
2310 
boxplot_has_ci(GPT_SPEC * spec)2311 static int boxplot_has_ci (GPT_SPEC *spec)
2312 {
2313     int i, nc = 0;
2314 
2315     for (i=0; i<spec->n_lines; i++) {
2316 	if (spec->lines[i].style == GP_STYLE_CANDLESTICKS) {
2317 	    nc++;
2318 	}
2319     }
2320 
2321     return nc > 2;
2322 }
2323 
print_line_label(GtkWidget * tbl,int row,GPT_SPEC * spec,int i)2324 static void print_line_label (GtkWidget *tbl, int row,
2325 			      GPT_SPEC *spec, int i)
2326 {
2327     gchar *text = NULL;
2328     GtkWidget *label;
2329 
2330     if (spec->code == PLOT_BOXPLOTS) {
2331 	if (i == 0) {
2332 	    text = g_strdup_printf("%s: ", _("box"));
2333 	} else if (i == 1) {
2334 	    text = g_strdup_printf("%s: ", _("median"));
2335 	} else if (boxplot_has_ci(spec)) {
2336 	    if (i == 2 || i == 3) {
2337 		text = g_strdup_printf("%s: ", _("c.i. bound"));
2338 	    } else {
2339 		text = g_strdup_printf("%s: ", _("outliers"));
2340 	    }
2341 	} else {
2342 	    if (i == 2) {
2343 		text = g_strdup_printf("%s: ", _("mean"));
2344 	    } else {
2345 		text = g_strdup_printf("%s: ", _("outliers"));
2346 	    }
2347 	}
2348     } else {
2349 	text = g_strdup_printf(_("line %d: "), i+1);
2350     }
2351 
2352     if (text == NULL) {
2353 	label = gtk_label_new("");
2354     } else {
2355 	label = gtk_label_new(text);
2356 	g_free(text);
2357     }
2358 
2359     gtk_misc_set_alignment(GTK_MISC(label), 1, 0.5);
2360     gtk_table_attach_defaults(GTK_TABLE(tbl),
2361 			      label, 0, 1, row - 1, row);
2362     gtk_widget_show(label);
2363 }
2364 
print_label_label(GtkWidget * tbl,int row,GPT_SPEC * spec,int i)2365 static void print_label_label (GtkWidget *tbl, int row, GPT_SPEC *spec,
2366 			       int i)
2367 {
2368     gchar *label_text;
2369     GtkWidget *label;
2370 
2371     label_text = g_strdup_printf(_("label %d: "), i + 1);
2372     label = gtk_label_new(label_text);
2373     g_free(label_text);
2374     gtk_misc_set_alignment(GTK_MISC(label), 1, 0.5);
2375     gtk_table_attach_defaults(GTK_TABLE(tbl),
2376 			      label, 0, 1, row - 1, row);
2377     gtk_widget_show(label);
2378 }
2379 
print_field_label(GtkWidget * tbl,int row,const gchar * text)2380 static GtkWidget *print_field_label (GtkWidget *tbl, int row,
2381 				     const gchar *text)
2382 {
2383     GtkWidget *label = gtk_label_new(text);
2384 
2385     gtk_misc_set_alignment(GTK_MISC(label), 1, 0.5);
2386     gtk_table_attach_defaults(GTK_TABLE(tbl),
2387 			      label, 1, 2, row - 1, row);
2388     gtk_widget_show(label);
2389 
2390     return label;
2391 }
2392 
dash_types_combo(void)2393 static GtkWidget *dash_types_combo (void)
2394 {
2395     GtkWidget *dtsel;
2396     GtkCellRenderer *cell;
2397     GtkListStore *store;
2398     GtkTreeIter iter;
2399     GdkPixbuf *pbuf;
2400     int i;
2401 
2402     store = gtk_list_store_new(1, GDK_TYPE_PIXBUF);
2403 
2404     for (i=0; i<5; i++) {
2405 	gtk_list_store_append(store, &iter);
2406 	pbuf = get_pixbuf_for_line(i);
2407 	gtk_list_store_set(store, &iter, 0, pbuf, -1);
2408 	g_object_unref(pbuf);
2409     }
2410 
2411     dtsel = gtk_combo_box_new_with_model(GTK_TREE_MODEL(store));
2412     cell = gtk_cell_renderer_pixbuf_new();
2413     gtk_cell_layout_pack_start(GTK_CELL_LAYOUT(dtsel), cell, FALSE);
2414     gtk_cell_layout_set_attributes(GTK_CELL_LAYOUT(dtsel), cell,
2415 				   "pixbuf", 0, NULL);
2416 
2417     return dtsel;
2418 }
2419 
point_types_combo(void)2420 static GtkWidget *point_types_combo (void)
2421 {
2422     GtkWidget *ptsel;
2423     GtkCellRenderer *cell;
2424     GtkListStore *store;
2425     GtkTreeIter iter;
2426     GdkPixbuf *pbuf;
2427     int i;
2428 
2429     store = gtk_list_store_new(1, GDK_TYPE_PIXBUF);
2430 
2431     for (i=0; i<13; i++) {
2432 	gtk_list_store_append(store, &iter);
2433 	pbuf = gdk_pixbuf_from_pixdata(gppoints[i], FALSE, NULL);
2434 	gtk_list_store_set(store, &iter, 0, pbuf, -1);
2435 	g_object_unref(pbuf);
2436     }
2437 
2438     ptsel = gtk_combo_box_new_with_model(GTK_TREE_MODEL(store));
2439     cell = gtk_cell_renderer_pixbuf_new();
2440     gtk_cell_layout_pack_start(GTK_CELL_LAYOUT(ptsel), cell, FALSE);
2441     gtk_cell_layout_set_attributes(GTK_CELL_LAYOUT(ptsel), cell,
2442 				   "pixbuf", 0, NULL);
2443 
2444     return ptsel;
2445 }
2446 
line_get_point_type(GPT_LINE * line,int i)2447 static int line_get_point_type (GPT_LINE *line, int i)
2448 {
2449     if (line->ptype > 0) {
2450 	/* a specific point-style has been selected: convert
2451 	   to zero-based */
2452 	return line->ptype - 1;
2453     } else if (line->type == LT_AUTO) {
2454 	/* line type is set by placement of line in plot */
2455 	return i;
2456     } else {
2457 	/* a specific line-type has been selected: give the
2458 	   associated point-style */
2459 	return line->type - 1;
2460     }
2461 }
2462 
2463 #define has_point(s) (s == GP_STYLE_POINTS || s == GP_STYLE_LINESPOINTS)
2464 #define has_line(s)  (s == GP_STYLE_LINES || \
2465 		      s == GP_STYLE_LINESPOINTS || \
2466 		      s == GP_STYLE_IMPULSES)
2467 
adjust_line_controls(GtkWidget * src,gpointer p)2468 static void adjust_line_controls (GtkWidget *src, gpointer p)
2469 {
2470     GtkWidget *ptc = g_object_get_data(G_OBJECT(src), "point-controls");
2471     GtkWidget *lnc = g_object_get_data(G_OBJECT(src), "line-controls");
2472     gchar *s = combo_box_get_active_text(src);
2473     int idx = gp_style_index_from_display_name(s);
2474 
2475     if (ptc != NULL) {
2476 	gtk_widget_set_sensitive(ptc, has_point(idx));
2477     }
2478     if (lnc != NULL) {
2479 	gtk_widget_set_sensitive(lnc, has_line(idx));
2480     }
2481 }
2482 
scroller_page(GtkWidget * vbox)2483 static GtkWidget *scroller_page (GtkWidget *vbox)
2484 {
2485     GtkWidget *scroller;
2486 
2487     scroller = gtk_scrolled_window_new(NULL, NULL);
2488     gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scroller),
2489 				   GTK_POLICY_NEVER,
2490 				   GTK_POLICY_AUTOMATIC);
2491     gtk_widget_show(scroller);
2492     gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(scroller),
2493 					  vbox);
2494     return scroller;
2495 }
2496 
show_axis_chooser(GPT_SPEC * spec)2497 static int show_axis_chooser (GPT_SPEC *spec)
2498 {
2499     int i, s = 0;
2500 
2501     if (spec->code == PLOT_REGULAR && spec->n_lines > 1) {
2502 	s = 1;
2503     } else {
2504 	for (i=0; i<spec->n_lines; i++) {
2505 	    if (spec->lines[i].yaxis == 2) {
2506 		s = 1;
2507 		break;
2508 	    }
2509 	}
2510     }
2511 
2512     return s;
2513 }
2514 
gp_style_index(int sty,GList * list)2515 static int gp_style_index (int sty, GList *list)
2516 {
2517     gp_style_spec *spec;
2518     GList *mylist = list;
2519     int i = 0;
2520 
2521     while (mylist != NULL) {
2522 	spec = (gp_style_spec *) mylist->data;
2523 	if (sty == spec->id) {
2524 	    return i;
2525 	}
2526 	i++;
2527 	mylist = mylist->next;
2528     }
2529 
2530     return 0;
2531 }
2532 
gpt_hboxit(GtkWidget * w)2533 static GtkWidget *gpt_hboxit (GtkWidget *w)
2534 {
2535     GtkWidget *hbox = gtk_hbox_new(FALSE, 0);
2536 
2537     gtk_box_pack_start(GTK_BOX(hbox), w, 0, 0, 0);
2538     return hbox;
2539 }
2540 
set_combo_box_strings_from_stylist(GtkWidget * box,GList * list)2541 static void set_combo_box_strings_from_stylist (GtkWidget *box,
2542 						GList *list)
2543 {
2544     gp_style_spec *spec;
2545     GList *mylist = list;
2546 
2547     while (mylist != NULL) {
2548 	spec = (gp_style_spec *) mylist->data;
2549 	combo_box_append_text(box, _(spec->trname));
2550 	mylist = mylist->next;
2551     }
2552 }
2553 
add_style_spec(GList * list,int t)2554 static GList *add_style_spec (GList *list, int t)
2555 {
2556     return g_list_append(list, get_style_spec(t));
2557 }
2558 
line_controls_init(plot_editor * ed,int i)2559 static void line_controls_init (plot_editor *ed, int i)
2560 {
2561     ed->lineformula[i] = NULL;
2562     ed->linetitle[i] = NULL;
2563     ed->stylecombo[i] = NULL;
2564     ed->dtcombo[i] = NULL;
2565     ed->linewidth[i] = NULL;
2566     ed->colorsel[i] = NULL;
2567     ed->pointsize[i] = NULL;
2568     ed->yaxiscombo[i] = NULL;
2569 }
2570 
2571 /* For the "lines" tab: we don't want this to take up
2572    too much vertical space, so we need to provide for
2573    vertical scrolling if the number of lines to be
2574    handled exceeds some maximum. This max used to be
2575    4, but it seems a smaller value is called for.
2576 */
2577 
want_vertical_scroller(plot_editor * ed)2578 static int want_vertical_scroller (plot_editor *ed)
2579 {
2580     return ed->gui_nlines > 2;
2581 }
2582 
gpt_tab_lines(plot_editor * ed,GPT_SPEC * spec,int ins)2583 static void gpt_tab_lines (plot_editor *ed, GPT_SPEC *spec, int ins)
2584 {
2585     GtkWidget *notebook = ed->notebook;
2586     GtkWidget *label, *tbl;
2587     GtkWidget *vbox, *hbox, *sep;
2588     GtkWidget *page;
2589     GList *stylist = NULL;
2590     int axis_chooser;
2591     int i, nrows, ncols = 4;
2592     int pgnum = -1;
2593 
2594     axis_chooser = show_axis_chooser(spec);
2595 
2596     if (frequency_plot_code(spec->code)) {
2597 	stylist = add_style_spec(stylist, GP_STYLE_BOXES);
2598     }
2599 
2600     if (spec->flags & GPT_TS) {
2601 	stylist = add_style_spec(stylist, GP_STYLE_LINES);
2602 	stylist = add_style_spec(stylist, GP_STYLE_POINTS);
2603     } else {
2604 	stylist = add_style_spec(stylist, GP_STYLE_POINTS);
2605 	stylist = add_style_spec(stylist, GP_STYLE_LINES);
2606     }
2607 
2608     stylist = add_style_spec(stylist, GP_STYLE_LINESPOINTS);
2609     stylist = add_style_spec(stylist, GP_STYLE_IMPULSES);
2610     stylist = add_style_spec(stylist, GP_STYLE_DOTS);
2611     stylist = add_style_spec(stylist, GP_STYLE_STEPS);
2612 
2613     if (!frequency_plot_code(spec->code)) {
2614 	stylist = add_style_spec(stylist, GP_STYLE_BOXES);
2615     }
2616 
2617     vbox = gp_dialog_vbox();
2618     gtk_widget_show(vbox);
2619 
2620     label = gtk_label_new(_("Lines"));
2621     gtk_widget_show(label);
2622 
2623     if (want_vertical_scroller(ed)) {
2624 	page = scroller_page(vbox);
2625     } else {
2626 	page = vbox;
2627     }
2628 
2629     if (ins > 0) {
2630 	pgnum = gtk_notebook_insert_page(GTK_NOTEBOOK(notebook), page, label, ins);
2631     } else {
2632 	pgnum = gtk_notebook_append_page(GTK_NOTEBOOK(notebook), page, label);
2633     }
2634 
2635     widget_set_int(notebook, "lines_page", pgnum);
2636 
2637     nrows = 1;
2638     tbl = gp_dialog_table(nrows, ncols, vbox);
2639     gtk_widget_show(tbl);
2640 
2641     for (i=0; i<ed->gui_nlines; i++) {
2642 	GPT_LINE *line = &spec->lines[i];
2643 	GtkWidget *ptsel = NULL;
2644 	int line_width_ok = 1;
2645 	int dash_type_ok = 1;
2646 	int color_sel_ok = 1;
2647 	int is_autofit = 0;
2648 	int is_formula = 0;
2649 	int is_hidden = 0;
2650 	int label_done = 0;
2651 
2652 	hbox = NULL;
2653 	line_controls_init(ed, i);
2654 
2655 	if (i >= 8 || frequency_plot_code(spec->code)) {
2656 	    dash_type_ok = 0; /* ? */
2657 	    color_sel_ok = 0;
2658 	}
2659 	if (i == 1 && spec->flags & GPT_AUTO_FIT) {
2660 	    is_autofit = 1;
2661 	    if (spec->flags & GPT_FIT_HIDDEN) {
2662 		is_hidden = 1;
2663 	    }
2664 	}
2665 	if (line->type <= 0 && line->type != LT_AUTO) {
2666 	    color_sel_ok = 0;
2667 	}
2668 	is_formula = plotspec_line_is_formula(spec, i);
2669 
2670 	if (is_formula) {
2671 	    gtk_table_resize(GTK_TABLE(tbl), ++nrows, ncols);
2672 	    print_line_label(tbl, nrows, spec, i);
2673 	    label_done = 1;
2674 	    print_field_label(tbl, nrows, _("formula"));
2675 	    ed->lineformula[i] = gtk_entry_new();
2676 	    gtk_table_attach_defaults(GTK_TABLE(tbl), ed->lineformula[i],
2677 				      2, ncols, nrows-1, nrows);
2678 	    if (line->formula != NULL) {
2679 		strip_lr(line->formula);
2680 	    }
2681 	    gp_string_to_entry(ed->lineformula[i], line->formula);
2682 	    if (is_autofit) {
2683 		/* fitted formula: not GUI-editable */
2684 		ed->fitformula = ed->lineformula[i];
2685 		gtk_widget_set_sensitive(ed->lineformula[i], FALSE);
2686 	    } else {
2687 		g_signal_connect(G_OBJECT(ed->lineformula[i]), "activate",
2688 				 G_CALLBACK(apply_gpt_changes),
2689 				 ed);
2690 	    }
2691 	    gtk_widget_show(ed->lineformula[i]);
2692 	}
2693 
2694 	/* identifier */
2695 	gtk_table_resize(GTK_TABLE(tbl), ++nrows, ncols);
2696 	if (!label_done) {
2697 	    print_line_label(tbl, nrows, spec, i);
2698 	}
2699 	if (line->flags & GP_LINE_USER) {
2700 	    item_remove_button(tbl, nrows, ed, i, GUI_LINE);
2701 	}
2702 
2703 	if (spec->code != PLOT_BOXPLOTS) {
2704 	    /* key or legend text */
2705 	    print_field_label(tbl, nrows, _("legend"));
2706 	    ed->linetitle[i] = gtk_entry_new();
2707 	    gtk_table_attach_defaults(GTK_TABLE(tbl), ed->linetitle[i],
2708 				      2, ncols, nrows-1, nrows);
2709 	    if (is_hidden) {
2710 		gtk_entry_set_text(GTK_ENTRY(ed->linetitle[i]), "");
2711 	    } else {
2712 		if (line->title != NULL) {
2713 		    strip_lr(line->title);
2714 		}
2715 		gp_string_to_entry(ed->linetitle[i], line->title);
2716 		g_signal_connect(G_OBJECT(ed->linetitle[i]), "changed",
2717 				 G_CALLBACK(linetitle_callback), ed);
2718 		g_signal_connect(G_OBJECT(ed->linetitle[i]), "activate",
2719 				 G_CALLBACK(apply_gpt_changes), ed);
2720 	    }
2721 	    gtk_widget_show(ed->linetitle[i]);
2722 	    if (is_autofit) {
2723 		ed->fitlegend = ed->linetitle[i];
2724 		gtk_widget_set_sensitive(ed->fitlegend, !is_hidden);
2725 	    }
2726 	}
2727 
2728 	if (is_formula) {
2729 	    goto line_width_adj;
2730 	}
2731 
2732 	/* data representation style (lines, points, etc.):
2733 	   in some cases this is user-selectable while in others
2734 	   it is an immutable consequence of the type of plot
2735 	   that has been produced
2736 	*/
2737 	if (spec->code != PLOT_BOXPLOTS) {
2738 	    gtk_table_resize(GTK_TABLE(tbl), ++nrows, ncols);
2739 	}
2740 	label = gtk_label_new(_("type"));
2741 	gtk_misc_set_alignment(GTK_MISC(label), 1, 0.5);
2742 	gtk_table_attach_defaults(GTK_TABLE(tbl), label, 1, 2,
2743 				  nrows-1, nrows);
2744 	gtk_widget_show(label);
2745 	ed->stylecombo[i] = gtk_combo_box_text_new();
2746 	hbox = gpt_hboxit(ed->stylecombo[i]);
2747 
2748 	if (line->style == GP_STYLE_ERRORBARS ||
2749 	    line->style == GP_STYLE_FILLEDCURVE ||
2750 	    line->style == GP_STYLE_CANDLESTICKS ||
2751 	    (spec->code == PLOT_BOXPLOTS &&
2752 	     line->style == GP_STYLE_POINTS)) {
2753 	    /* cases where style is immutable */
2754 	    GList *altsty = NULL;
2755 
2756 	    altsty = add_style_spec(altsty, line->style);
2757 	    set_combo_box_strings_from_stylist(ed->stylecombo[i], altsty);
2758 	    gtk_combo_box_set_active(GTK_COMBO_BOX(ed->stylecombo[i]), 0);
2759 	    /* no messing with these! */
2760 	    gtk_widget_set_sensitive(ed->stylecombo[i], FALSE);
2761 	    g_list_free(altsty);
2762 	    dash_type_ok = 0;
2763 	    if (line->style == GP_STYLE_POINTS ||
2764 		line->style == GP_STYLE_FILLEDCURVE) {
2765 		line_width_ok = 0;
2766 	    } else if (line->style == GP_STYLE_CANDLESTICKS && i > 0) {
2767 		/* boxplot median: same color as box */
2768 		color_sel_ok = 0;
2769 	    }
2770 	} else {
2771 	    /* otherwise offer choice of styles of representation */
2772 	    int lt = gp_style_index(line->style, stylist);
2773 
2774 	    set_combo_box_strings_from_stylist(ed->stylecombo[i], stylist);
2775 	    gtk_combo_box_set_active(GTK_COMBO_BOX(ed->stylecombo[i]), lt);
2776 	    ptsel = point_types_combo();
2777 	    g_object_set_data(G_OBJECT(ed->stylecombo[i]), "pointsel", ptsel);
2778 	}
2779 
2780 	if (color_sel_ok) {
2781 	    ed->colorsel[i] = line_color_button(spec, i);
2782 	    gtk_box_pack_start(GTK_BOX(hbox), ed->colorsel[i], FALSE, FALSE, 20);
2783 	    color_sel_ok = 0;
2784 	}
2785 
2786 	if (axis_chooser) {
2787 	    GtkWidget *ycombo;
2788 
2789 	    label = gtk_label_new(_("y axis"));
2790 	    gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0);
2791 	    ycombo = gtk_combo_box_text_new();
2792 	    combo_box_append_text(ycombo, _("left"));
2793 	    combo_box_append_text(ycombo, _("right"));
2794 	    gtk_combo_box_set_active(GTK_COMBO_BOX(ycombo),
2795 				     (line->yaxis == 1)? 0 : 1);
2796 	    gtk_box_pack_start(GTK_BOX(hbox), ycombo, FALSE, FALSE, 5);
2797 	    gtk_widget_set_sensitive(ycombo, !is_hidden);
2798 	    ed->yaxiscombo[i] = ycombo;
2799 	}
2800 
2801 	gtk_table_attach_defaults(GTK_TABLE(tbl), hbox, 2, ncols,
2802 				  nrows-1, nrows);
2803 	gtk_widget_show_all(hbox);
2804 
2805     line_width_adj:
2806 
2807 	if (line_width_ok) {
2808 	    /* characteristics of the lines (or linespoints)
2809 	       representation of the data, if applicable; plus
2810 	       linewidth for boxplot "candlesticks"
2811 	    */
2812 	    int hl = has_line(line->style);
2813 
2814 	    gtk_table_resize(GTK_TABLE(tbl), ++nrows, ncols);
2815 	    print_field_label(tbl, nrows, dash_type_ok ? _("line") :
2816 			      _("line width"));
2817 	    hbox = gtk_hbox_new(FALSE, 5);
2818 
2819 	    if (dash_type_ok) {
2820 		/* not relevant for "candlesticks" */
2821 		int active = line->dtype > 1 ? line->dtype - 1 : 0;
2822 
2823 		ed->dtcombo[i] = dash_types_combo();
2824 		gtk_combo_box_set_active(GTK_COMBO_BOX(ed->dtcombo[i]), active);
2825 		gtk_box_pack_start(GTK_BOX(hbox), ed->dtcombo[i], FALSE, FALSE, 0);
2826 		label = gtk_label_new( _("width"));
2827 		gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0);
2828 	    }
2829 
2830 	    ed->linewidth[i] = gtk_spin_button_new_with_range(0.5, 6.0, 0.5);
2831 	    gtk_spin_button_set_value(GTK_SPIN_BUTTON(ed->linewidth[i]),
2832 				      line->width);
2833 	    gtk_box_pack_start(GTK_BOX(hbox), ed->linewidth[i], FALSE, FALSE, 0);
2834 	    gtk_table_attach_defaults(GTK_TABLE(tbl), hbox, 2, 3,
2835 				      nrows-1, nrows);
2836 	    g_signal_connect(G_OBJECT(ed->linewidth[i]), "activate",
2837 			     G_CALLBACK(apply_gpt_changes), ed);
2838 	    gtk_widget_show_all(hbox);
2839 	    gtk_widget_set_sensitive(hbox, !is_hidden);
2840 	    if (should_apply_changes(ed->stylecombo[i])) {
2841 		g_object_set_data(G_OBJECT(ed->stylecombo[i]), "line-controls",
2842 				  hbox);
2843 		g_signal_connect(G_OBJECT(ed->stylecombo[i]), "changed",
2844 				 G_CALLBACK(adjust_line_controls), NULL);
2845 	    }
2846 	    if (line->style == GP_STYLE_CANDLESTICKS) {
2847 		gtk_widget_set_sensitive(hbox, TRUE);
2848 	    } else {
2849 		gtk_widget_set_sensitive(hbox, hl && !is_hidden);
2850 	    }
2851 	}
2852 
2853 	if (color_sel_ok) {
2854 	    /* color selection ok and not already added */
2855 	    ed->colorsel[i] = line_color_button(spec, i);
2856 	    hbox = gpt_hboxit(ed->colorsel[i]);
2857 	    gtk_table_attach_defaults(GTK_TABLE(tbl), hbox, 3, ncols,
2858 				      nrows-1, nrows);
2859 	    gtk_widget_set_sensitive(hbox, !is_hidden);
2860 	    gtk_widget_show_all(hbox);
2861 	}
2862 
2863 	if (ptsel != NULL) {
2864 	    /* point type and size adjustment: we show this only
2865 	       if representation of the data as points is acceptable
2866 	       in context; and we make it sensitive iff the point
2867 	       (or linespoints) style is actually selected
2868 	    */
2869 	    int pt = line_get_point_type(line, i);
2870 	    int hp = has_point(line->style);
2871 
2872 	    gtk_table_resize(GTK_TABLE(tbl), ++nrows, ncols);
2873 	    label = print_field_label(tbl, nrows, _("point"));
2874 	    gtk_combo_box_set_active(GTK_COMBO_BOX(ptsel), pt);
2875 
2876 	    hbox = gtk_hbox_new(FALSE, 5);
2877 	    gtk_box_pack_start(GTK_BOX(hbox), ptsel, 0, 0, 0);
2878 
2879 	    label = gtk_label_new( _("size"));
2880 	    gtk_box_pack_start(GTK_BOX(hbox), label, 0, 0, 0);
2881 	    ed->pointsize[i] = gtk_spin_button_new_with_range(0.5, 6.0, 0.5);
2882 	    gtk_spin_button_set_value(GTK_SPIN_BUTTON(ed->pointsize[i]),
2883 				      line->pscale);
2884 	    g_object_set_data(G_OBJECT(ptsel), "psize", ed->pointsize[i]);
2885 	    gtk_box_pack_start(GTK_BOX(hbox), ed->pointsize[i], 0, 0, 0);
2886 	    gtk_table_attach_defaults(GTK_TABLE(tbl), hbox, 2, 3,
2887 				      nrows-1, nrows);
2888 	    gtk_widget_show_all(hbox);
2889 	    if (should_apply_changes(ed->stylecombo[i])) {
2890 		g_object_set_data(G_OBJECT(ed->stylecombo[i]), "point-controls",
2891 				  hbox);
2892 		g_signal_connect(G_OBJECT(ed->stylecombo[i]), "changed",
2893 				 G_CALLBACK(adjust_line_controls), NULL);
2894 	    }
2895 	    gtk_widget_set_sensitive(hbox, hp);
2896 	}
2897 
2898 	if (axis_chooser && ed->yaxiscombo[i] == NULL) {
2899 	    /* fallback, probably not needed */
2900 	    gtk_table_resize(GTK_TABLE(tbl), ++nrows, ncols);
2901 	    print_field_label(tbl, nrows, _("y axis"));
2902 	    ed->yaxiscombo[i] = gtk_combo_box_text_new();
2903 	    combo_box_append_text(ed->yaxiscombo[i], _("left"));
2904 	    combo_box_append_text(ed->yaxiscombo[i], _("right"));
2905 	    gtk_combo_box_set_active(GTK_COMBO_BOX(ed->yaxiscombo[i]),
2906 				     (line->yaxis == 1)? 0 : 1);
2907 	    hbox = gpt_hboxit(ed->yaxiscombo[i]);
2908 	    gtk_table_attach_defaults(GTK_TABLE(tbl), hbox, 2, ncols,
2909 				      nrows-1, nrows);
2910 	    gtk_widget_show_all(hbox);
2911 	    gtk_widget_set_sensitive(hbox, !is_hidden);
2912 	}
2913 
2914 	/* separator */
2915 	gtk_table_resize(GTK_TABLE(tbl), ++nrows, ncols);
2916 	sep = gtk_hseparator_new();
2917 	gtk_table_attach_defaults(GTK_TABLE(tbl), sep, 0, ncols,
2918 				  nrows-1, nrows);
2919 	gtk_widget_show(sep);
2920     } /* end iteration over lines in plot */
2921 
2922     if ((spec->code == PLOT_REGULAR || spec->code == PLOT_CURVE)
2923 	&& spec->n_lines < 8) {
2924 	/* button for adding a line (formula) */
2925 	GtkWidget *add_button;
2926 
2927 	gtk_table_resize(GTK_TABLE(tbl), ++nrows, ncols);
2928 	add_button = gtk_button_new_with_label(_("Add line..."));
2929 	g_signal_connect(G_OBJECT(add_button), "clicked",
2930 			 G_CALLBACK(add_line_callback),
2931 			 ed);
2932 	gtk_widget_show(add_button);
2933 	gtk_table_attach_defaults(GTK_TABLE(tbl), add_button,
2934 				  0, 1, nrows-1, nrows);
2935     }
2936 
2937     g_list_free(stylist);
2938 }
2939 
label_pos_to_entry(double * pos,GtkWidget * w)2940 static void label_pos_to_entry (double *pos, GtkWidget *w)
2941 {
2942     if (!na(pos[0]) && !na(pos[1])) {
2943 	gchar *s = g_strdup_printf("%.10g %.10g", pos[0], pos[1]);
2944 
2945 	gtk_entry_set_text(GTK_ENTRY(w), s);
2946 	g_free(s);
2947     } else {
2948 	gtk_entry_set_text(GTK_ENTRY(w), "");
2949     }
2950 }
2951 
gpt_tab_labels(plot_editor * ed,GPT_SPEC * spec,int ins)2952 static void gpt_tab_labels (plot_editor *ed, GPT_SPEC *spec, int ins)
2953 {
2954     GtkWidget *notebook = ed->notebook;
2955     GtkWidget *label, *tbl;
2956     GtkWidget *vbox, *page, *sep;
2957     int i, j, nrows;
2958     png_plot *plot = (png_plot *) spec->ptr;
2959     int pgnum = -1;
2960 
2961     vbox = gp_dialog_vbox();
2962     gtk_widget_show(vbox);
2963 
2964     label = gtk_label_new(_("Labels"));
2965     gtk_widget_show(label);
2966 
2967     if (ed->gui_nlabels > 4) {
2968 	page = scroller_page(vbox);
2969     } else {
2970 	page = vbox;
2971     }
2972 
2973     if (ins > 0) {
2974 	pgnum = gtk_notebook_insert_page(GTK_NOTEBOOK(notebook), page, label, ins);
2975     } else {
2976 	pgnum = gtk_notebook_append_page(GTK_NOTEBOOK(notebook), page, label);
2977     }
2978 
2979     widget_set_int(notebook, "labels_page", pgnum);
2980 
2981     nrows = 1;
2982     tbl = gp_dialog_table(nrows, 3, vbox);
2983     gtk_widget_show(tbl);
2984 
2985     for (i=0; i<ed->gui_nlabels; i++) {
2986 	GtkWidget *hbox, *button, *image;
2987 	GdkPixbuf *icon;
2988 
2989 	/* label text */
2990 	gtk_table_resize(GTK_TABLE(tbl), ++nrows, 3);
2991 	print_label_label(tbl, nrows, spec, i);
2992 	print_field_label(tbl, nrows, _("text"));
2993 	ed->labeltext[i] = gtk_entry_new();
2994 	gtk_entry_set_max_length(GTK_ENTRY(ed->labeltext[i]), PLOT_LABEL_TEXT_LEN);
2995 	gp_string_to_entry(ed->labeltext[i], spec->labels[i].text);
2996 	g_signal_connect(G_OBJECT(ed->labeltext[i]), "activate",
2997 			 G_CALLBACK(apply_gpt_changes),
2998 			 ed);
2999 	gtk_table_attach_defaults(GTK_TABLE(tbl),
3000 				  ed->labeltext[i], 2, 3, nrows-1, nrows);
3001 	gtk_widget_show(ed->labeltext[i]);
3002 
3003 	/* label placement */
3004 	gtk_table_resize(GTK_TABLE(tbl), ++nrows, 3);
3005 
3006 	item_remove_button(tbl, nrows, ed, i, GUI_LABEL);
3007 
3008 	print_field_label(tbl, nrows, _("position (X Y)"));
3009 
3010 	/* holder for entry and button */
3011 	hbox = gtk_hbox_new(FALSE, 5);
3012 
3013 	/* entry for coordinates */
3014 	ed->labelpos[i] = gtk_entry_new();
3015 	gtk_entry_set_max_length(GTK_ENTRY(ed->labelpos[i]), PLOT_POSITION_LEN);
3016 	label_pos_to_entry(spec->labels[i].pos, ed->labelpos[i]);
3017 	g_signal_connect(G_OBJECT(ed->labelpos[i]), "activate",
3018 			 G_CALLBACK(apply_gpt_changes),
3019 			 ed);
3020 	gtk_container_add(GTK_CONTAINER(hbox), ed->labelpos[i]);
3021 	gtk_widget_show(ed->labelpos[i]);
3022 
3023 	if (plot_is_mouseable(plot)) {
3024 	    /* button to invoke mouse-assisted placement */
3025 	    button = gtk_button_new();
3026 	    g_object_set_data(G_OBJECT(button), "pos_entry", ed->labelpos[i]);
3027 	    g_signal_connect(G_OBJECT(button), "clicked",
3028 			     G_CALLBACK(plot_position_click), spec->ptr);
3029 	    icon = gdk_pixbuf_new_from_xpm_data((const char **) mini_mouse_xpm);
3030 	    image = gtk_image_new_from_pixbuf(icon);
3031 	    g_object_unref(icon);
3032 	    gtk_widget_set_size_request(button, 32, 26);
3033 	    gtk_container_add(GTK_CONTAINER(button), image);
3034 	    gtk_container_add(GTK_CONTAINER(hbox), button);
3035 	    gtk_widget_show_all(button);
3036 	}
3037 
3038 	gtk_table_attach_defaults(GTK_TABLE(tbl),
3039 				  hbox, 2, 3, nrows-1, nrows);
3040 	gtk_widget_show(hbox);
3041 
3042 	/* label justification */
3043 	gtk_table_resize(GTK_TABLE(tbl), ++nrows, 3);
3044 
3045 	print_field_label(tbl, nrows, _("justification"));
3046 
3047 	ed->labeljust[i] = gtk_combo_box_text_new();
3048 	for (j=0; j<3; j++) {
3049 	    combo_box_append_text(ed->labeljust[i],
3050 				  _(gp_justification_string(j)));
3051 	}
3052 	gtk_combo_box_set_active(GTK_COMBO_BOX(ed->labeljust[i]),
3053 				 spec->labels[i].just);
3054 	gtk_table_attach_defaults(GTK_TABLE(tbl),
3055 				  ed->labeljust[i], 2, 3, nrows-1, nrows);
3056 	gtk_widget_show_all(ed->labeljust[i]);
3057 
3058 	if (i < ed->gui_nlabels - 1 || spec->n_labels < 8) {
3059 	    /* separator */
3060 	    gtk_table_resize(GTK_TABLE(tbl), ++nrows, 3);
3061 	    sep = gtk_hseparator_new();
3062 	    gtk_table_attach_defaults(GTK_TABLE(tbl), sep, 0, 3,
3063 				      nrows-1, nrows);
3064 	    gtk_widget_show(sep);
3065 	}
3066     }
3067 
3068     if (spec->n_labels < 8) {
3069 	/* button for adding a label */
3070 	GtkWidget *button;
3071 
3072 	gtk_table_resize(GTK_TABLE(tbl), ++nrows, 3);
3073 	button = gtk_button_new_with_label(_("Add..."));
3074 	g_signal_connect(G_OBJECT(button), "clicked",
3075 			 G_CALLBACK(add_label_callback),
3076 			 ed);
3077 	gtk_widget_show(button);
3078 	gtk_table_attach_defaults(GTK_TABLE(tbl), button, 0, 1,
3079 				  nrows-1, nrows);
3080     }
3081 }
3082 
arrow_pos_to_entry(GPT_ARROW * arrow,GtkWidget * w,int j)3083 static void arrow_pos_to_entry (GPT_ARROW *arrow, GtkWidget *w, int j)
3084 {
3085     double x = (j == 0)? arrow->x0 : arrow->x1;
3086     double y = (j == 0)? arrow->y0 : arrow->y1;
3087 
3088     if (!na(x) && !na(y)) {
3089 	gchar *s = g_strdup_printf("%.10g %.10g", x, y);
3090 
3091 	gtk_entry_set_text(GTK_ENTRY(w), s);
3092 	g_free(s);
3093     } else {
3094 	gtk_entry_set_text(GTK_ENTRY(w), "");
3095     }
3096 }
3097 
3098 #define ARROW_MAX 6
3099 
gpt_tab_arrows(plot_editor * ed,GPT_SPEC * spec,int ins)3100 static void gpt_tab_arrows (plot_editor *ed, GPT_SPEC *spec, int ins)
3101 {
3102     GtkWidget *notebook = ed->notebook;
3103     GtkWidget *label, *tbl;
3104     GtkWidget *vbox, *page, *sep;
3105     int i, j, r, nrows;
3106     png_plot *plot = (png_plot *) spec->ptr;
3107     int nsep = 0, pgnum = -1;
3108 
3109     vbox = gp_dialog_vbox();
3110     gtk_widget_show(vbox);
3111 
3112     label = gtk_label_new(_("Arrows"));
3113     gtk_widget_show(label);
3114     page = vbox;
3115 
3116     if (ins > 0) {
3117 	pgnum = gtk_notebook_insert_page(GTK_NOTEBOOK(notebook), page, label, ins);
3118     } else {
3119 	pgnum = gtk_notebook_append_page(GTK_NOTEBOOK(notebook), page, label);
3120     }
3121 
3122     widget_set_int(notebook, "arrows_page", pgnum);
3123 
3124     if (ed->gui_narrows > 1) {
3125 	nsep = ed->gui_narrows - 1;
3126     }
3127     nrows = 1 + 4 * ed->gui_narrows + nsep;
3128     tbl = gp_dialog_table(nrows, 3, vbox);
3129     r = 1;
3130 
3131     for (i=0; i<ed->gui_narrows; i++) {
3132 	GtkWidget *button, *image, *chk;
3133 	GdkPixbuf *icon;
3134 
3135 	item_remove_button(tbl, r, ed, i, GUI_ARROW);
3136 
3137 	/* entries and buttons for (start, stop) coordinates */
3138 	for (j=0; j<2; j++) {
3139 	    GtkWidget *hbox = gtk_hbox_new(FALSE, 5);
3140 	    GtkWidget *apos;
3141 
3142 	    if (j == 0) {
3143 		label = gtk_label_new(_("from (X Y)"));
3144 	    } else {
3145 		label = gtk_label_new(_("to (X Y)"));
3146 	    }
3147 	    gtk_table_attach_defaults(GTK_TABLE(tbl), label, 1, 2, r-1, r);
3148 
3149 	    apos = gtk_entry_new();
3150 	    gtk_entry_set_max_length(GTK_ENTRY(apos), PLOT_POSITION_LEN);
3151 	    arrow_pos_to_entry(&spec->arrows[i], apos, j);
3152 	    g_signal_connect(G_OBJECT(apos), "activate",
3153 			     G_CALLBACK(apply_gpt_changes),
3154 			     ed);
3155 	    gtk_container_add(GTK_CONTAINER(hbox), apos);
3156 
3157 	    if (j == 0) {
3158 		ed->arrowpos[i] = apos;
3159 	    } else {
3160 		g_object_set_data(G_OBJECT(ed->arrowpos[i]), "pos_2", apos);
3161 	    }
3162 
3163 	    if (plot_is_mouseable(plot)) {
3164 		/* button to invoke mouse-assisted placement */
3165 		button = gtk_button_new();
3166 		g_object_set_data(G_OBJECT(button), "pos_entry", apos);
3167 		g_signal_connect(G_OBJECT(button), "clicked",
3168 				 G_CALLBACK(plot_position_click), spec->ptr);
3169 		icon = gdk_pixbuf_new_from_xpm_data((const char **) mini_mouse_xpm);
3170 		image = gtk_image_new_from_pixbuf(icon);
3171 		g_object_unref(icon);
3172 		gtk_widget_set_size_request(button, 32, 26);
3173 		gtk_container_add(GTK_CONTAINER(button), image);
3174 		gtk_container_add(GTK_CONTAINER(hbox), button);
3175 		gtk_table_attach_defaults(GTK_TABLE(tbl), hbox, 2, 3, r-1, r);
3176 		r++;
3177 	    }
3178 	}
3179 
3180 	/* arrow head selector */
3181 	chk = gtk_check_button_new_with_label(_("arrow has head"));
3182 	g_object_set_data(G_OBJECT(ed->arrowpos[i]), "arrow_check", chk);
3183 	gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(chk),
3184 				     (spec->arrows[i].flags & GP_ARROW_HEAD)?
3185 				     TRUE : FALSE);
3186 	gtk_table_attach_defaults(GTK_TABLE(tbl), chk, 2, 3, r-1, r);
3187 	r++;
3188 
3189 	/* dotted line selector */
3190 	chk = gtk_check_button_new_with_label(_("line is dotted"));
3191 	g_object_set_data(G_OBJECT(ed->arrowpos[i]), "dots_check", chk);
3192 	gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(chk),
3193 				     (spec->arrows[i].flags & GP_ARROW_DOTS)?
3194 				     TRUE : FALSE);
3195 	gtk_table_attach_defaults(GTK_TABLE(tbl), chk, 2, 3, r-1, r);
3196 	r++;
3197 
3198 	if (i < ed->gui_narrows - 1) {
3199 	    /* separator */
3200 	    sep = gtk_hseparator_new();
3201 	    gtk_table_attach_defaults(GTK_TABLE(tbl), sep, 0, 3, r-1, r);
3202 	    r++;
3203 	}
3204     }
3205 
3206     if (spec->n_arrows < ARROW_MAX) {
3207 	/* button for adding an arrow */
3208 	GtkWidget *button = gtk_button_new_with_label(_("Add..."));
3209 
3210 	g_signal_connect(G_OBJECT(button), "clicked",
3211 			 G_CALLBACK(add_arrow_callback), ed);
3212 	gtk_table_attach_defaults(GTK_TABLE(tbl), button, 0, 1, r-1, r);
3213     }
3214 
3215     gtk_widget_show_all(tbl);
3216 }
3217 
gpt_tab_XY(plot_editor * ed,GPT_SPEC * spec,gint axis)3218 static void gpt_tab_XY (plot_editor *ed, GPT_SPEC *spec, gint axis)
3219 {
3220     png_plot *plot = (png_plot *) spec->ptr;
3221     GtkWidget *notebook = ed->notebook;
3222     GtkWidget *b1, *b2, *entry, *vbox, *tbl;
3223     GtkWidget *label = NULL;
3224     char *labelstr = NULL;
3225     int i, nrows;
3226 
3227     if (axis == 0) {
3228 	labelstr = _("X-axis");
3229     } else if (axis == 1) {
3230 	labelstr = _("Y-axis");
3231     } else if (axis == 2) {
3232 	labelstr = _("Y2-axis");
3233     } else {
3234 	return;
3235     }
3236 
3237     vbox = gp_page_vbox(notebook, labelstr);
3238 
3239     nrows = 1;
3240     tbl = gp_dialog_table(nrows, 2, vbox);
3241     gtk_widget_show(tbl);
3242 
3243     for (i=0; i<NTITLES; i++) {
3244 	if (ed->gpt_titles[i].tab == 1 + axis) {
3245 	    gtk_table_resize(GTK_TABLE(tbl), ++nrows, 2);
3246 
3247 	    label = gtk_label_new(_(ed->gpt_titles[i].desc));
3248 	    gtk_misc_set_alignment(GTK_MISC(label), 1, 0.5);
3249 	    gtk_table_attach_defaults(GTK_TABLE(tbl), label, 0, 1,
3250 				      nrows-1, nrows);
3251 	    gtk_widget_show(label);
3252 
3253 	    entry = gtk_entry_new();
3254 	    gtk_table_attach_defaults(GTK_TABLE(tbl), entry, 1, 2,
3255 				      nrows-1, nrows);
3256 	    gp_string_to_entry(entry, spec->titles[i]);
3257 	    g_signal_connect(G_OBJECT(entry), "activate",
3258 			     G_CALLBACK(apply_gpt_changes),
3259 			     ed);
3260 	    gtk_widget_show(entry);
3261 	    ed->gpt_titles[i].widget = entry;
3262 	}
3263     }
3264 
3265     if (spec->code != PLOT_REGULAR &&
3266 	spec->code != PLOT_CURVE &&
3267 	spec->code != PLOT_MANY_TS) {
3268 	return;
3269     }
3270 
3271     if (axis == 0 && (spec->flags & GPT_TIMEFMT)) {
3272 	return;
3273     }
3274 
3275     ed->axis_range[axis].ID = axis;
3276 
3277     /* axis range: "auto" button */
3278     nrows += 3;
3279     gtk_table_resize(GTK_TABLE(tbl), nrows, 2);
3280 
3281     label = gtk_label_new("");
3282     gtk_table_attach_defaults(GTK_TABLE(tbl), label, 0, 1,
3283 			      nrows-3, nrows-2);
3284     gtk_widget_show(label);
3285     b1 = gtk_radio_button_new_with_label(NULL, _("auto axis range"));
3286     widget_set_int(b1, "axis", axis);
3287     g_signal_connect(G_OBJECT(b1), "clicked",
3288 		     G_CALLBACK(flip_manual_range), ed);
3289     gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(b1), TRUE);
3290     gtk_table_attach_defaults(GTK_TABLE(tbl), b1, 0, 1,
3291 			      nrows-2, nrows-1);
3292     gtk_widget_show(b1);
3293     ed->axis_range[axis].isauto = b1;
3294 
3295     /* axis range: manual range button */
3296     b2 = gtk_radio_button_new_with_label(gtk_radio_button_get_group
3297 					 (GTK_RADIO_BUTTON(b1)),
3298 					 _("manual range:"));
3299     widget_set_int(b2, "axis", axis);
3300     g_signal_connect(G_OBJECT(b2), "clicked",
3301 		     G_CALLBACK(flip_manual_range), ed);
3302     gtk_table_attach_defaults(GTK_TABLE(tbl), b2, 0, 1,
3303 			      nrows-1, nrows);
3304     gtk_widget_show(b2);
3305 
3306     /* axis range min. entry */
3307     nrows++;
3308     label = gtk_label_new(_("minimum"));
3309     gtk_table_attach_defaults(GTK_TABLE(tbl),
3310 			      label, 0, 1, nrows-1, nrows);
3311     gtk_widget_show(label);
3312     gtk_table_resize(GTK_TABLE(tbl), nrows, 2);
3313     entry = gtk_entry_new();
3314     gtk_table_attach_defaults(GTK_TABLE(tbl), entry, 1, 2,
3315 			      nrows-1, nrows);
3316     gtk_entry_set_text(GTK_ENTRY(entry), "");
3317     g_signal_connect(G_OBJECT(entry), "activate",
3318 		     G_CALLBACK(apply_gpt_changes),
3319 		     ed);
3320     gtk_widget_show(entry);
3321     ed->axis_range[axis].min = entry;
3322 
3323     /* axis range max. entry */
3324     nrows++;
3325     label = gtk_label_new(_("maximum"));
3326     gtk_table_attach_defaults(GTK_TABLE(tbl), label, 0, 1,
3327 			      nrows-1, nrows);
3328     gtk_widget_show(label);
3329     gtk_table_resize(GTK_TABLE(tbl), nrows, 2);
3330     entry = gtk_entry_new();
3331     gtk_table_attach_defaults(GTK_TABLE(tbl), entry, 1, 2,
3332 			      nrows-1, nrows);
3333     gtk_entry_set_text(GTK_ENTRY(entry), "");
3334     g_signal_connect(G_OBJECT(entry), "activate",
3335 		     G_CALLBACK(apply_gpt_changes),
3336 		     ed);
3337     gtk_widget_show(entry);
3338     ed->axis_range[axis].max = entry;
3339 
3340     if (na(spec->range[axis][0])) {
3341 	gtk_widget_set_sensitive(ed->axis_range[axis].min, FALSE);
3342 	gtk_widget_set_sensitive(ed->axis_range[axis].max, FALSE);
3343     } else {
3344 	double_to_gp_entry(spec->range[axis][0], ed->axis_range[axis].min);
3345 	double_to_gp_entry(spec->range[axis][1], ed->axis_range[axis].max);
3346 	gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(ed->axis_range[axis].isauto),
3347 				     FALSE);
3348 	gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(b2), TRUE);
3349     }
3350 
3351     if (spec->code != PLOT_REGULAR) {
3352 	return;
3353     }
3354 
3355     /* axis scale: linear vs log? */
3356 
3357     if ((axis == 0 && plot_get_xmin(plot) >= 0.0) ||
3358 	(axis == 1 && plot_get_ymin(plot) >= 0.0)) {
3359 	GtkWidget *combo;
3360 	GList *strs = NULL;
3361 
3362 	strs = g_list_append(strs, "e");
3363 	strs = g_list_append(strs, "2");
3364 	strs = g_list_append(strs, "10");
3365 
3366 	nrows += 3;
3367 	gtk_table_resize(GTK_TABLE(tbl), nrows, 2);
3368 
3369 	label = gtk_label_new("");
3370 	gtk_table_attach_defaults(GTK_TABLE(tbl), label, 0, 1,
3371 				  nrows-3, nrows-2);
3372 	gtk_widget_show(label);
3373 	b1 = gtk_radio_button_new_with_label(NULL, _("linear scale"));
3374 	gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(b1), TRUE);
3375 	gtk_table_attach_defaults(GTK_TABLE(tbl), b1, 0, 1,
3376 				  nrows-2, nrows-1);
3377 	gtk_widget_show(b1);
3378 
3379 	b2 = gtk_radio_button_new_with_label(gtk_radio_button_get_group
3380 					     (GTK_RADIO_BUTTON(b1)),
3381 					     _("logarithmic scale, base:"));
3382 	gtk_table_attach_defaults(GTK_TABLE(tbl), b2, 0, 1,
3383 				  nrows-1, nrows);
3384 	gtk_widget_show(b2);
3385 
3386 	combo = combo_box_text_new_with_entry();
3387 	set_combo_box_strings_from_list(combo, strs);
3388 	g_list_free(strs);
3389 	gtk_table_attach_defaults(GTK_TABLE(tbl), combo, 1, 2,
3390 				  nrows-1, nrows);
3391 	entry = gtk_bin_get_child(GTK_BIN(combo));
3392 	g_signal_connect(G_OBJECT(entry), "activate",
3393 			 G_CALLBACK(apply_gpt_changes),
3394 			 ed);
3395 	gtk_widget_show(combo);
3396 	ed->axis_range[axis].lbase = entry;
3397 
3398 	if (spec->logbase[axis] > 1.1) {
3399 	    gchar *txt = g_strdup_printf("%g", spec->logbase[axis]);
3400 
3401 	    gtk_entry_set_text(GTK_ENTRY(entry), txt);
3402 	    g_free(txt);
3403 	    gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(b2), TRUE);
3404 	} else {
3405 	    gtk_combo_box_set_active(GTK_COMBO_BOX(combo), 0);
3406 	    gtk_widget_set_sensitive(entry, FALSE);
3407 	}
3408 
3409 	sensitize_conditional_on(entry, b2);
3410     }
3411 }
3412 
plot_editor_destroy(plot_editor * ed)3413 static void plot_editor_destroy (plot_editor *ed)
3414 {
3415     if (restore_lines(ed)) {
3416 	/* undo any unapplied changes to lines */
3417 	free(ed->spec->lines);
3418 	ed->spec->lines = ed->old_lines;
3419 	ed->spec->n_lines = ed->old_n_lines;
3420     }
3421 
3422     if (restore_labels(ed)) {
3423 	/* undo any unapplied changes to labels */
3424 	free(ed->spec->labels);
3425 	ed->spec->labels = ed->old_labels;
3426 	ed->spec->n_labels = ed->old_n_labels;
3427     }
3428 
3429     if (restore_arrows(ed)) {
3430 	/* undo any unapplied changes to arrows */
3431 	free(ed->spec->arrows);
3432 	ed->spec->arrows = ed->old_arrows;
3433 	ed->spec->n_arrows = ed->old_n_arrows;
3434     }
3435 
3436     if (ed->user_barsfile != NULL) {
3437 	remember_user_bars_file(ed->user_barsfile);
3438 	g_free(ed->user_barsfile);
3439     }
3440 
3441     free(ed->linetitle);
3442     free(ed->lineformula);
3443     free(ed->stylecombo);
3444     free(ed->yaxiscombo);
3445     free(ed->linewidth);
3446     free(ed->colorsel);
3447     free(ed->dtcombo);
3448     free(ed->pointsize);
3449 
3450     free(ed->labeltext);
3451     free(ed->labelpos);
3452     free(ed->labeljust);
3453 
3454     free(ed->arrowpos);
3455 
3456     free(ed);
3457 }
3458 
widget_array_new(int n,int * err)3459 static GtkWidget **widget_array_new (int n, int *err)
3460 {
3461     GtkWidget **pw = malloc(n * sizeof *pw);
3462     int i;
3463 
3464     if (pw != NULL) {
3465 	for (i=0; i<n; i++) {
3466 	    pw[i] = NULL;
3467 	}
3468     } else {
3469 	*err = E_ALLOC;
3470     }
3471 
3472     return pw;
3473 }
3474 
widget_array_expand(GtkWidget *** ppw,int n,int * err)3475 static GtkWidget **widget_array_expand (GtkWidget ***ppw, int n, int *err)
3476 {
3477     GtkWidget **tmp;
3478     int i;
3479 
3480     tmp = realloc(*ppw, n * sizeof *tmp);
3481 
3482     if (tmp != NULL) {
3483 	for (i=0; i<n; i++) {
3484 	    tmp[i] = NULL;
3485 	}
3486 	*ppw = tmp;
3487     } else {
3488 	*err = E_ALLOC;
3489     }
3490 
3491     return *ppw;
3492 }
3493 
add_line_widget(plot_editor * ed)3494 static int add_line_widget (plot_editor *ed)
3495 {
3496     int n = ed->gui_nlines + 1;
3497     int err = 0;
3498 
3499     ed->linetitle   = widget_array_expand(&ed->linetitle, n, &err);
3500     ed->lineformula = widget_array_expand(&ed->lineformula, n, &err);
3501     ed->stylecombo  = widget_array_expand(&ed->stylecombo, n, &err);
3502     ed->yaxiscombo  = widget_array_expand(&ed->yaxiscombo, n, &err);
3503     ed->linewidth   = widget_array_expand(&ed->linewidth, n, &err);
3504     ed->colorsel    = widget_array_expand(&ed->colorsel, n, &err);
3505     ed->dtcombo     = widget_array_expand(&ed->dtcombo, n, &err);
3506     ed->pointsize   = widget_array_expand(&ed->pointsize, n, &err);
3507 
3508     if (!err) {
3509 	ed->gui_nlines = n;
3510     }
3511 
3512     return err;
3513 }
3514 
allocate_line_widgets(plot_editor * ed,int n)3515 static int allocate_line_widgets (plot_editor *ed, int n)
3516 {
3517     int err = 0;
3518 
3519     if (n > 0) {
3520 	ed->linetitle   = widget_array_new(n, &err);
3521 	ed->lineformula = widget_array_new(n, &err);
3522 	ed->stylecombo  = widget_array_new(n, &err);
3523 	ed->yaxiscombo  = widget_array_new(n, &err);
3524 	ed->linewidth   = widget_array_new(n, &err);
3525 	ed->colorsel    = widget_array_new(n, &err);
3526 	ed->dtcombo     = widget_array_new(n, &err);
3527 	ed->pointsize   = widget_array_new(n, &err);
3528 	if (!err) {
3529 	    ed->gui_nlines = n;
3530 	}
3531     }
3532 
3533     return err;
3534 }
3535 
add_label_widget(plot_editor * ed)3536 static int add_label_widget (plot_editor *ed)
3537 {
3538     int n = ed->gui_nlabels + 1;
3539     int err = 0;
3540 
3541     ed->labeltext = widget_array_expand(&ed->labeltext, n, &err);
3542     ed->labelpos  = widget_array_expand(&ed->labelpos, n, &err);
3543     ed->labeljust = widget_array_expand(&ed->labeljust, n, &err);
3544 
3545     if (!err) {
3546 	ed->gui_nlabels = n;
3547     }
3548 
3549     return err;
3550 }
3551 
allocate_label_widgets(plot_editor * ed,int n)3552 static int allocate_label_widgets (plot_editor *ed, int n)
3553 {
3554     int err = 0;
3555 
3556     if (n > 0) {
3557 	ed->labeltext = widget_array_new(n, &err);
3558 	ed->labelpos  = widget_array_new(n, &err);
3559 	ed->labeljust = widget_array_new(n, &err);
3560 	if (!err) {
3561 	    ed->gui_nlabels = n;
3562 	}
3563     }
3564 
3565     return err;
3566 }
3567 
add_arrow_widget(plot_editor * ed)3568 static int add_arrow_widget (plot_editor *ed)
3569 {
3570     int n = ed->gui_narrows + 1;
3571     int err = 0;
3572 
3573     ed->arrowpos = widget_array_expand(&ed->arrowpos, n, &err);
3574 
3575     if (!err) {
3576 	ed->gui_narrows = n;
3577     }
3578 
3579     return err;
3580 }
3581 
allocate_arrow_widgets(plot_editor * ed,int n)3582 static int allocate_arrow_widgets (plot_editor *ed, int n)
3583 {
3584     int err = 0;
3585 
3586     if (n > 0) {
3587 	ed->arrowpos = widget_array_new(n, &err);
3588 	if (!err) {
3589 	    ed->gui_narrows = n;
3590 	}
3591     }
3592 
3593     return err;
3594 }
3595 
plot_editor_new(GPT_SPEC * spec)3596 static plot_editor *plot_editor_new (GPT_SPEC *spec)
3597 {
3598     plot_editor *ed;
3599     int i;
3600 
3601     ed = malloc(sizeof *ed);
3602     if (ed == NULL) {
3603 	return NULL;
3604     }
3605 
3606     ed->spec = spec;
3607     ed->user_barsfile = NULL;
3608     ed->active_bars = -1;
3609 
3610     ed->dialog = NULL;
3611 
3612     ed->linetitle = NULL;
3613     ed->lineformula = NULL;
3614     ed->stylecombo = NULL;
3615     ed->yaxiscombo = NULL;
3616     ed->linewidth = NULL;
3617     ed->colorsel = NULL;
3618     ed->dtcombo = NULL;
3619     ed->pointsize = NULL;
3620 
3621     ed->labeltext = NULL;
3622     ed->labelpos = NULL;
3623     ed->labeljust = NULL;
3624 
3625     ed->arrowpos = NULL;
3626 
3627     /* also set to NULL the widgets which may or may not
3628        actually be used in constructing the plot editor
3629        dialog
3630     */
3631     ed->fitformula = NULL;
3632     ed->fitlegend = NULL;
3633     ed->keycombo = NULL;
3634     ed->fitcombo = NULL;
3635     ed->border_check = NULL;
3636     ed->grid_check = NULL;
3637     ed->grid_combo = NULL;
3638     ed->y2_check = NULL;
3639     ed->bars_check = NULL;
3640     ed->fontcheck = NULL;
3641     ed->barscombo = NULL;
3642 
3643     ed->gui_nlines = ed->gui_nlabels = ed->gui_narrows = 0;
3644 
3645     if (spec->code != PLOT_STACKED_BAR) {
3646 	if (allocate_line_widgets(ed, spec->n_lines)) {
3647 	    plot_editor_destroy(ed);
3648 	    return NULL;
3649 	}
3650     }
3651 
3652     if (allocate_label_widgets(ed, spec->n_labels)) {
3653 	plot_editor_destroy(ed);
3654 	return NULL;
3655     }
3656 
3657     if (allocate_arrow_widgets(ed, spec->n_arrows)) {
3658 	plot_editor_destroy(ed);
3659 	return NULL;
3660     }
3661 
3662     for (i=0; i<MAX_AXES; i++) {
3663 	ed->axis_range[i].isauto = NULL;
3664 	ed->axis_range[i].lbase = NULL;
3665     }
3666 
3667     for (i=0; i<NTITLES; i++) {
3668 	ed->gpt_titles[i].desc = (i == 0)? N_("Title of plot"):
3669 	    N_("Title for axis");
3670 	ed->gpt_titles[i].tab = i;
3671 	ed->gpt_titles[i].widget = NULL;
3672     }
3673 
3674     old_lines_init(ed);
3675     old_labels_init(ed);
3676     old_arrows_init(ed);
3677 
3678     return ed;
3679 }
3680 
plot_add_editor(png_plot * plot)3681 GtkWidget *plot_add_editor (png_plot *plot)
3682 {
3683     GPT_SPEC *spec = plot_get_spec(plot);
3684     GtkWidget *plotshell = plot_get_shell(plot);
3685     plot_editor *editor;
3686     GtkWidget *dialog, *vbox, *hbox;
3687     GtkWidget *button, *notebook;
3688 
3689     editor = plot_editor_new(spec);
3690     if (editor == NULL) {
3691 	nomem();
3692 	return NULL;
3693     }
3694 
3695     dialog = gretl_dialog_new(_("gretl plot controls"), plotshell,
3696 			      GRETL_DLG_RESIZE);
3697     vbox = gtk_dialog_get_content_area(GTK_DIALOG(dialog));
3698     gtk_box_set_spacing(GTK_BOX(vbox), 2);
3699 #if GTK_MAJOR_VERSION < 3
3700     gtk_dialog_set_has_separator(GTK_DIALOG(dialog), FALSE);
3701 #endif
3702 
3703     if (plot != NULL) {
3704 	gtk_window_set_transient_for(GTK_WINDOW(dialog),
3705 				     GTK_WINDOW(plotshell));
3706     }
3707 
3708     editor->dialog = dialog;
3709 
3710     g_signal_connect_swapped(G_OBJECT(dialog), "destroy",
3711 			     G_CALLBACK(plot_editor_destroy),
3712 			     editor);
3713 
3714     editor->notebook = notebook = gtk_notebook_new();
3715     gtk_box_pack_start(GTK_BOX(vbox), notebook, TRUE, TRUE, 0);
3716     gtk_widget_show(notebook);
3717 
3718     gpt_tab_main(editor, spec);
3719     gpt_tab_XY(editor, spec, 0);
3720     gpt_tab_XY(editor, spec, 1);
3721 
3722     if (spec->flags & GPT_Y2AXIS) {
3723 	gpt_tab_XY(editor, spec, 2);
3724     }
3725 
3726     if (spec->lines != NULL && !bar_plot(spec->code)) {
3727 	/* FIXME exclusion of bar plots? */
3728 	gpt_tab_lines(editor, spec, 0);
3729     }
3730 
3731     if (plot_is_mouseable(plot)) {
3732 	gpt_tab_labels(editor, spec, 0);
3733 	gpt_tab_arrows(editor, spec, 0);
3734     }
3735 
3736     hbox = gtk_dialog_get_action_area(GTK_DIALOG(dialog));
3737 
3738     /* "Apply" button */
3739     button = apply_button(hbox);
3740     g_signal_connect(G_OBJECT(button), "clicked",
3741 		     G_CALLBACK(apply_gpt_changes), editor);
3742     gtk_widget_grab_default(button);
3743 
3744     /* "OK" button (apply and close) */
3745     button = ok_button(hbox);
3746     g_signal_connect(G_OBJECT(button), "clicked",
3747 		     G_CALLBACK(apply_gpt_changes), editor);
3748     g_signal_connect_swapped(G_OBJECT(button), "clicked",
3749 			     G_CALLBACK(gtk_widget_destroy),
3750 			     dialog);
3751 
3752     /* Close button (do not apply changes) */
3753     button = gtk_button_new_from_stock(GTK_STOCK_CLOSE);
3754     gtk_widget_set_can_default(button, TRUE);
3755     gtk_container_add(GTK_CONTAINER(hbox), button);
3756     g_signal_connect_swapped(G_OBJECT(button), "clicked",
3757 			     G_CALLBACK(gtk_widget_destroy),
3758 			     dialog);
3759 
3760     /* Help button */
3761     context_help_button(hbox, GR_PLOT);
3762 
3763     set_keyspec_sensitivity(editor);
3764 
3765     gtk_widget_show_all(hbox);
3766     gtk_widget_show(dialog);
3767 
3768     return dialog;
3769 }
3770