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