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_control.c for gretl -- gnuplot controller */
21 
22 #include "gretl.h"
23 #include "plotspec.h"
24 #include "libset.h"
25 #include "gpt_control.h"
26 #include "session.h"
27 #include "gpt_dialog.h"
28 #include "fileselect.h"
29 #include "calculator.h"
30 #include "guiprint.h"
31 #include "textbuf.h"
32 #include "graphics.h"
33 #include "boxplots.h"
34 #include "dlgutils.h"
35 #include "winstack.h"
36 #include "toolbar.h"
37 #include "clipboard.h"
38 
39 #ifdef G_OS_WIN32
40 # include <io.h>
41 # include "gretlwin32.h"
42 #endif
43 
44 #include <gdk-pixbuf/gdk-pixbuf.h>
45 #include <gdk/gdkkeysyms.h>
46 #include <errno.h>
47 
48 #define GPDEBUG 0
49 #define POINTS_DEBUG 0
50 #define COLLDEBUG 0
51 
52 /* the following needs more testing */
53 #define HANDLE_HEREDATA 1
54 
55 /* pager for plot collection: use spin button? */
56 #define SPIN_PAGER 0
57 
58 enum {
59     PLOT_SAVED          = 1 << 0,
60     PLOT_ZOOMED         = 1 << 1,
61     PLOT_ZOOMING        = 1 << 2,
62     PLOT_PNG_COORDS     = 1 << 3,
63     PLOT_HAS_XRANGE     = 1 << 4,
64     PLOT_HAS_YRANGE     = 1 << 5,
65     PLOT_DONT_ZOOM      = 1 << 6,
66     PLOT_DONT_EDIT      = 1 << 7,
67     PLOT_DONT_MOUSE     = 1 << 8,
68     PLOT_POSITIONING    = 1 << 9,
69     PLOT_CURSOR_LABEL   = 1 << 10,
70     PLOT_TERM_HIDDEN    = 1 << 11
71 } plot_status_flags;
72 
73 enum {
74     PLOT_TITLE          = 1 << 0,
75     PLOT_XLABEL         = 1 << 1,
76     PLOT_YLABEL         = 1 << 2,
77     PLOT_Y2AXIS         = 1 << 3,
78     PLOT_Y2LABEL        = 1 << 4,
79     PLOT_MARKERS_UP     = 1 << 5,
80     PLOT_POLAR          = 1 << 6
81 } plot_format_flags;
82 
83 #define MAX_MARKERS 250
84 
85 #define plot_is_zoomed(p)       (p->status & PLOT_ZOOMED)
86 #define plot_is_zooming(p)      (p->status & PLOT_ZOOMING)
87 #define plot_has_png_coords(p)  (p->status & PLOT_PNG_COORDS)
88 #define plot_has_xrange(p)      (p->status & PLOT_HAS_XRANGE)
89 #define plot_has_yrange(p)      (p->status & PLOT_HAS_YRANGE)
90 #define plot_not_editable(p)    (p->status & PLOT_DONT_EDIT)
91 #define plot_is_editable(p)     (!(p->status & PLOT_DONT_EDIT))
92 #define plot_doing_position(p)  (p->status & PLOT_POSITIONING)
93 
94 #define plot_has_title(p)        (p->format & PLOT_TITLE)
95 #define plot_has_xlabel(p)       (p->format & PLOT_XLABEL)
96 #define plot_has_ylabel(p)       (p->format & PLOT_YLABEL)
97 #define plot_has_y2axis(p)       (p->format & PLOT_Y2AXIS)
98 #define plot_has_y2label(p)      (p->format & PLOT_Y2LABEL)
99 #define plot_labels_shown(p)     (p->format & PLOT_MARKERS_UP)
100 #define plot_is_polar(p)         (p->format & PLOT_POLAR)
101 
102 #define plot_is_range_mean(p)   (p->spec->code == PLOT_RANGE_MEAN)
103 #define plot_is_hurst(p)        (p->spec->code == PLOT_HURST)
104 #define plot_is_roots(p)        (p->spec->code == PLOT_ROOTS)
105 #define plot_is_geomap(p)       (p->spec->code == PLOT_GEOMAP)
106 
107 #define plot_has_regression_list(p) (p->spec->reglist != NULL)
108 
109 #define labels_frozen(p)        (p->spec->flags & GPT_PRINT_MARKERS)
110 #define cant_do_labels(p)       (p->err || p->spec->markers == NULL)
111 
112 #define plot_show_cursor_label(p) (p->status & PLOT_CURSOR_LABEL)
113 
114 #define plot_has_controller(p) (p->editor != NULL)
115 
116 typedef enum {
117     PNG_START,
118     PNG_ZOOM,
119     PNG_UNZOOM,
120     PNG_REDISPLAY,
121     PNG_REPLACE
122 } viewcode;
123 
124 #if COLLDEBUG
125 static const char *viewstrs[] = {
126     "START", "ZOOM", "UNZOOM", "REDISPLAY", "REPLACE"
127 };
128 
viewstr(viewcode i)129 static const char *viewstr (viewcode i)
130 {
131     return viewstrs[i];
132 }
133 #endif
134 
135 struct multiplot_ {
136     gint64 mtime;
137     GList *list;
138     int current;
139     int id;
140 #if SPIN_PAGER
141     GtkWidget *sb;
142 #endif
143 };
144 
145 typedef struct multiplot_ multiplot;
146 
147 struct png_plot_t {
148     GtkWidget *shell;     /* top-level GTK window */
149     multiplot *mp;        /* collection-specific info */
150     GtkWidget *canvas;    /* area in which plot is drawn */
151     GtkWidget *popup;     /* transient popup menu */
152     GtkWidget *statusbar;
153     GtkWidget *cursor_label;
154     GtkWidget *editor;    /* state-dependent pointer */
155     GtkWidget *pos_entry; /* state-dependent pointer */
156     GtkWidget *toolbar;   /* toolbar at bottom right of window */
157     GtkWidget *up_icon;   /* resizing button */
158     GtkWidget *down_icon; /* resizing button */
159     GdkWindow *window;
160     GdkPixbuf *pbuf;
161     cairo_t *cr;
162 #if GTK_MAJOR_VERSION >= 3
163     cairo_surface_t *cs;
164 #else
165     GdkPixmap *pixmap;
166     GdkPixbuf *savebuf;
167 #endif
168     GPT_SPEC *spec;
169     double xmin, xmax;
170     double ymin, ymax;
171     int pixel_width, pixel_height;
172     int pixel_xmin, pixel_xmax;
173     int pixel_ymin, pixel_ymax;
174     int xint, yint;
175     int pd;
176     int err;
177     guint cid;
178     double zoom_xmin, zoom_xmax;
179     double zoom_ymin, zoom_ymax;
180     int screen_x0, screen_y0; /* to define selection */
181     guint32 status;
182     guint8 format;
183 };
184 
185 static int render_png (png_plot *plot, viewcode view);
186 static int repaint_png (png_plot *plot, int view);
187 static int zoom_replaces_plot (png_plot *plot);
188 static void prepare_for_zoom (png_plot *plot);
189 static int get_plot_ranges (png_plot *plot, PlotType ptype);
190 static void graph_display_pdf (png_plot *plot);
191 static void plot_do_rescale (png_plot *plot, int mod);
192 #ifdef G_OS_WIN32
193 static void win32_process_graph (png_plot *plot, int dest);
194 #else
195 static void set_plot_for_copy (png_plot *plot);
196 #endif
197 static void build_plot_menu (png_plot *plot);
198 static int plot_add_shell (png_plot *plot, const char *name);
199 static GdkPixbuf *pixbuf_from_file (png_plot *plot);
200 static int resize_png_plot (png_plot *plot, int width, int height,
201 			    int follower);
202 static png_plot *widget_get_plot (gpointer p);
203 static void destroy_png_plot (GtkWidget *w, png_plot *plot);
204 #if GTK_MAJOR_VERSION == 2
205 static void plot_nullify_surface (png_plot *plot);
206 #endif
207 
208 enum {
209     GRETL_PNG_OK,
210     GRETL_PNG_NO_OPEN,
211     GRETL_PNG_NOT_PNG,
212     GRETL_PNG_NO_COMMENTS,
213     GRETL_PNG_BAD_COMMENTS,
214     GRETL_PNG_NO_COORDS
215 };
216 
217 typedef struct png_bounds_t png_bounds;
218 
219 struct png_bounds_t {
220     int xleft;
221     int xright;
222     int ybot;
223     int ytop;
224     double xmin;
225     double xmax;
226     double ymin;
227     double ymax;
228     int width;
229     int height;
230 };
231 
232 typedef struct linestyle_ linestyle;
233 
234 struct linestyle_ {
235     char lc[8]; /* color */
236     float lw;   /* line width */
237     int dt;     /* dash type */
238     int pt;     /* point type */
239 };
240 
241 static int get_png_bounds_info (png_bounds *bounds);
242 
243 #define PLOTSPEC_DETAILS_IN_MEMORY(s) (s->lines != NULL)
244 
245 /* file-scope globals */
246 static png_plot *plot_collection;
247 static int collection_count;
248 
249 #include "gpt_collect.c"
250 
terminate_plot_positioning(png_plot * plot)251 static void terminate_plot_positioning (png_plot *plot)
252 {
253     if (plot->status & PLOT_POSITIONING) {
254 	plot->status ^= PLOT_POSITIONING;
255 	plot->pos_entry = NULL;
256 	gdk_window_set_cursor(plot->window, NULL);
257 	gtk_statusbar_pop(GTK_STATUSBAR(plot->statusbar), plot->cid);
258 	if (plot->editor != NULL) {
259 	    gtk_window_present(GTK_WINDOW(plot->editor));
260 	}
261     }
262 }
263 
plot_invalidate_pixbuf(png_plot * plot)264 static void plot_invalidate_pixbuf (png_plot *plot)
265 {
266     if (plot->pbuf != NULL) {
267 	g_object_unref(G_OBJECT(plot->pbuf));
268 	plot->pbuf = NULL;
269     }
270 }
271 
plot_get_current(png_plot * plot)272 static png_plot *plot_get_current (png_plot *plot)
273 {
274     if (in_collection(plot)) {
275 	return g_list_nth_data(plot->mp->list, plot->mp->current);
276     } else {
277 	return plot;
278     }
279 }
280 
widget_get_plot(gpointer p)281 static png_plot *widget_get_plot (gpointer p)
282 {
283     return g_object_get_data(G_OBJECT(p), "plot");
284 }
285 
is_shell_for_plotfile(GtkWidget * w,const char * fname)286 gboolean is_shell_for_plotfile (GtkWidget *w,
287 				const char *fname)
288 {
289     png_plot *plot = widget_get_plot(w);
290 
291     return plot != NULL && plot->spec != NULL &&
292 	!strcmp(fname, plot->spec->fname);
293 }
294 
plot_get_spec(png_plot * plot)295 GPT_SPEC *plot_get_spec (png_plot *plot)
296 {
297     return plot->spec;
298 }
299 
300 /* called from calculator.c */
301 
plot_get_shell(png_plot * plot)302 GtkWidget *plot_get_shell (png_plot *plot)
303 {
304     return plot->shell;
305 }
306 
plot_is_saved(const png_plot * plot)307 int plot_is_saved (const png_plot *plot)
308 {
309     return (plot->status & PLOT_SAVED);
310 }
311 
plot_is_mouseable(const png_plot * plot)312 int plot_is_mouseable (const png_plot *plot)
313 {
314     return !(plot->status & PLOT_DONT_MOUSE);
315 }
316 
plot_get_xmin(png_plot * plot)317 double plot_get_xmin (png_plot *plot)
318 {
319     return (plot != NULL)? plot->xmin : -1;
320 }
321 
plot_get_ymin(png_plot * plot)322 double plot_get_ymin (png_plot *plot)
323 {
324     return (plot != NULL)? plot->ymin : -1;
325 }
326 
plot_get_pixel_dims(png_plot * plot,double * pw,double * ph)327 void plot_get_pixel_dims (png_plot *plot,
328 			  double *pw,
329 			  double *ph)
330 {
331     *pw = plot->pixel_width;
332     *ph = plot->pixel_height;
333 }
334 
335 /* apparatus for graph toolbar */
336 
graph_rescale_callback(GtkWidget * w,png_plot * plot)337 static void graph_rescale_callback (GtkWidget *w, png_plot *plot)
338 {
339     int mod = (w == plot->up_icon)? 1 : -1;
340 
341     plot_do_rescale(plot, mod);
342 }
343 
graph_popup_callback(GtkWidget * w,png_plot * plot)344 static void graph_popup_callback (GtkWidget *w, png_plot *plot)
345 {
346     build_plot_menu(plot);
347     gtk_menu_popup(GTK_MENU(plot->popup), NULL, NULL, NULL, NULL,
348 		   1, gtk_get_current_event_time());
349 }
350 
plot_winlist_popup(GtkWidget * w,png_plot * plot)351 static void plot_winlist_popup (GtkWidget *w, png_plot *plot)
352 {
353     window_list_popup(w, NULL, plot->shell);
354 }
355 
356 static GretlToolItem plotbar_items[] = {
357     { N_("Menu"),        GRETL_STOCK_MENU,    G_CALLBACK(graph_popup_callback), 0 },
358     { N_("Bigger"),      GRETL_STOCK_BIGGER,  G_CALLBACK(graph_rescale_callback), 32 },
359     { N_("Smaller"),     GRETL_STOCK_SMALLER, G_CALLBACK(graph_rescale_callback), 0 },
360     { N_("Windows"),     GRETL_STOCK_WINLIST, G_CALLBACK(plot_winlist_popup), 0 }
361 };
362 
add_graph_toolbar(GtkWidget * hbox,png_plot * plot)363 static void add_graph_toolbar (GtkWidget *hbox, png_plot *plot)
364 {
365     GtkWidget *tbar, *button, **pb;
366     GretlToolItem *item;
367     int i, n = G_N_ELEMENTS(plotbar_items);
368 
369     plot->toolbar = tbar = gretl_toolbar_new(NULL);
370 
371     for (i=0; i<n; i++) {
372 	pb = NULL;
373 	item = &plotbar_items[i];
374 	if (item->func == G_CALLBACK(graph_rescale_callback)) {
375 	    if (plot_not_editable(plot)) {
376 		continue;
377 	    } else {
378 		pb = (item->flag == 32)? &plot->up_icon :
379 		    &plot->down_icon;
380 	    }
381 	}
382 	button = gretl_toolbar_insert(tbar, item, item->func, plot, -1);
383 	if (pb != NULL) {
384 	    *pb = button;
385 	}
386     }
387 
388     gtk_box_pack_start(GTK_BOX(hbox), tbar, FALSE, FALSE, 5);
389 }
390 
391 /* end apparatus for graph toolbar */
392 
393 /* Provide the data coordinates for a gretl/gnuplot
394    graph, if they are all positive, otherwise return
395    non-zero. Called by the plot editor.
396 */
397 
plot_get_coordinates(png_plot * plot,double * xmin,double * xmax,double * ymin,double * ymax)398 int plot_get_coordinates (png_plot *plot,
399 			  double *xmin,
400 			  double *xmax,
401 			  double *ymin,
402 			  double *ymax)
403 {
404     int err = 0;
405 
406     if (plot != NULL && plot->xmin > 0 &&
407 	plot->xmax > plot->xmin &&
408 	plot->ymax > plot->ymin) {
409 	*xmin = plot->xmin;
410 	*xmax = plot->xmax;
411 	*ymin = plot->ymin;
412 	*ymax = plot->ymax;
413     } else {
414 	err = E_DATA;
415 	gretl_errmsg_set("Couldn't get plot coordinates");
416     }
417 
418     return err;
419 }
420 
set_plot_has_y2_axis(png_plot * plot,gboolean s)421 void set_plot_has_y2_axis (png_plot *plot, gboolean s)
422 {
423     if (s == TRUE) {
424 	plot->format |= PLOT_Y2AXIS;
425     } else {
426 	plot->format &= ~PLOT_Y2AXIS;
427     }
428 }
429 
plot_position_click(GtkWidget * w,png_plot * plot)430 void plot_position_click (GtkWidget *w, png_plot *plot)
431 {
432     if (plot != NULL) {
433 	GtkWidget *entry;
434 	GdkCursor *cursor;
435 
436 	cursor = gdk_cursor_new(GDK_CROSSHAIR);
437 	if (cursor != NULL) {
438 	    gdk_window_set_cursor(plot->window, cursor);
439 	    gdk_cursor_unref(cursor);
440 	}
441 	entry = g_object_get_data(G_OBJECT(w), "pos_entry");
442 	plot->pos_entry = entry;
443 	plot->status |= PLOT_POSITIONING;
444 	gtk_statusbar_push(GTK_STATUSBAR(plot->statusbar), plot->cid,
445 			   _(" Click to set position"));
446     }
447 }
448 
open_gp_file(const char * fname,const char * mode)449 static FILE *open_gp_file (const char *fname, const char *mode)
450 {
451     FILE *fp = gretl_fopen(fname, mode);
452 
453     if (fp == NULL) {
454 	if (*mode == 'w') {
455 	    file_write_errbox(fname);
456 	} else {
457 	    file_read_errbox(fname);
458 	}
459     }
460 
461     return fp;
462 }
463 
commented_term_line(const char * s)464 static int commented_term_line (const char *s)
465 {
466     return !strncmp(s, "# set term png", 14);
467 }
468 
set_output_line(const char * s)469 static int set_output_line (const char *s)
470 {
471     return !strncmp(s, "set output", 10);
472 }
473 
set_encoding_line(const char * s)474 static int set_encoding_line (const char *s)
475 {
476     return !strncmp(s, "set encod", 9);
477 }
478 
set_print_line(const char * s)479 static int set_print_line (const char *s)
480 {
481     return (!strncmp(s, "set print ", 10) ||
482 	    !strncmp(s, "print \"pixe", 11) ||
483 	    !strncmp(s, "print \"data", 11) ||
484 	    !strncmp(s, "print \"term", 11));
485 }
486 
487 enum {
488     REMOVE_PNG,
489     ADD_PNG
490 };
491 
is_png_term_line(const char * s)492 static int is_png_term_line (const char *s)
493 {
494     return !strncmp(s, "set term png", 12);
495 }
496 
497 #ifdef G_OS_WIN32
498 
check_win32_png_spec(char * s)499 static void check_win32_png_spec (char *s)
500 {
501     if (!strncmp("set term png ", s, 13)) {
502 	/* should now be pngcairo */
503 	*s = '\0';
504     }
505 }
506 
507 #endif
508 
509 static int
add_or_remove_png_term(const char * fname,int action,GPT_SPEC * spec)510 add_or_remove_png_term (const char *fname, int action, GPT_SPEC *spec)
511 {
512     FILE *fsrc, *ftmp;
513     char temp[MAXLEN], fline[MAXLEN];
514     GptFlags flags = 0;
515     int ix = 0, iy = 0;
516     int err = 0;
517 
518     sprintf(temp, "%sgpttmp", gretl_dotdir());
519     ftmp = gretl_tempfile_open(temp);
520     if (ftmp == NULL) {
521 	return 1;
522     }
523 
524     fsrc = open_gp_file(fname, "r");
525     if (fsrc == NULL) {
526 	fclose(ftmp);
527 	return 1;
528     }
529 
530     if (action == ADD_PNG) {
531 	/* see if there's already a png term setting, possibly commented
532 	   out, that can be reused */
533 	char restore_line[MAXLEN];
534 	int add_line_styles = 1;
535 
536 	*restore_line = '\0';
537 
538 	while (fgets(fline, sizeof fline, fsrc)) {
539 	    if (is_png_term_line(fline) && *restore_line == '\0') {
540 		strcat(restore_line, fline);
541 	    } else if (commented_term_line(fline) && *restore_line == '\0') {
542 		strcat(restore_line, fline + 2);
543 	    } else if (strstr(fline, "letterbox")) {
544 		flags = GPT_LETTERBOX;
545 	    } else if (strstr(fline, "large")) {
546 		flags = GPT_XL;
547 	    } else if (strstr(fline, "extra-large")) {
548 		flags = GPT_XXL;
549 	    } else if (strstr(fline, "extra-wide")) {
550 		flags = GPT_XW;
551 	    } else if (sscanf(fline, "# geoplot %d %d", &ix, &iy) == 2) {
552 		; /* OK */
553 	    } else if (!strncmp(fline, "set style line", 14) ||
554 		       !strncmp(fline, "set linetype", 12)) {
555 		add_line_styles = 0;
556 	    } else if (!strncmp(fline, "plot", 4)) {
557 		break;
558 	    }
559 	}
560 
561 	rewind(fsrc);
562 
563 #ifdef G_OS_WIN32
564 	/* check for obsolete png term specification (as may be found
565 	   in an old session file) */
566 	check_win32_png_spec(restore_line);
567 #endif
568 
569 	if (*restore_line != '\0') {
570 	    fputs(restore_line, ftmp);
571 	    fputs("set encoding utf8\n", ftmp);
572 	} else {
573 	    const char *tline;
574 
575 	    if (ix > 0 && iy > 0) {
576 		set_special_plot_size(ix, iy);
577 	    }
578 	    if (spec != NULL) {
579 		tline = get_png_line_for_plotspec(spec);
580 	    } else {
581 		tline = gretl_gnuplot_term_line(GP_TERM_PNG,
582 						PLOT_REGULAR,
583 						flags, NULL);
584 	    }
585 	    fprintf(ftmp, "%s\n", tline);
586 	    if (strstr(tline, "encoding") == NULL) {
587 		fputs("set encoding utf8\n", ftmp);
588 	    }
589 	}
590 
591 	/* Note: we want "set encoding" to be done before we
592 	   write the line specifying the output destination
593 	   since the filename may be UTF-8.
594 	*/
595 	write_plot_output_line(NULL, ftmp);
596 
597 	if (spec != NULL && add_line_styles) {
598 	    write_plot_line_styles(spec->code, ftmp);
599 	}
600 
601 	/* now for the body of the plot file */
602 
603 	while (fgets(fline, sizeof fline, fsrc)) {
604 	    if (set_print_line(fline)) {
605 		; /* skip it (portability) */
606 	    } else if (is_png_term_line(fline)) {
607 		; /* handled above */
608 	    } else if (commented_term_line(fline)) {
609 		; /* handled above */
610 	    } else if (set_encoding_line(fline)) {
611 		; /* handled above */
612 	    } else if (set_output_line(fline)) {
613 		; /* handled above */
614 	    } else {
615 		fputs(fline, ftmp);
616 	    }
617 	}
618 	write_plot_bounding_box_request(ftmp);
619     } else {
620 	/* not ADD_PNG: we're removing the png term line */
621 	int printit, png_line_saved = 0;
622 
623 	while (fgets(fline, sizeof fline, fsrc)) {
624 	    printit = 1;
625 	    if (is_png_term_line(fline)) {
626 		if (!png_line_saved) {
627 		    /* comment it out, for future reference */
628 		    fprintf(ftmp, "# %s", fline);
629 		    png_line_saved = 1;
630 		}
631 		printit = 0;
632 	    } else if (commented_term_line(fline)) {
633 		if (png_line_saved) {
634 		    printit = 0;
635 		}
636 	    } else if (set_output_line(fline)) {
637 		printit = 0;
638 	    } else if (spec != NULL && (spec->flags & GPT_FIT_HIDDEN)
639 		       && is_auto_fit_string(fline)) {
640 		printit = 0;
641 	    } else if (set_print_line(fline)) {
642 		printit = 0;
643 	    }
644 	    if (printit) {
645 		fputs(fline, ftmp);
646 	    }
647 	}
648     }
649 
650     fclose(fsrc);
651     fclose(ftmp);
652 
653     /* delete the original */
654     gretl_remove(fname);
655 
656     /* and rename the new to the original name */
657     err = gretl_rename(temp, fname);
658     if (err) {
659 	fprintf(stderr, "warning: rename failed\n");
660     }
661 
662     return err;
663 }
664 
add_png_term_to_plot(const char * fname)665 static int add_png_term_to_plot (const char *fname)
666 {
667     return add_or_remove_png_term(fname, ADD_PNG, NULL);
668 }
669 
remove_png_term_from_plot(const char * fname,GPT_SPEC * spec)670 static int remove_png_term_from_plot (const char *fname, GPT_SPEC *spec)
671 {
672     return add_or_remove_png_term(fname, REMOVE_PNG, spec);
673 }
674 
675 /* public because called from session.c when editing plot commands */
676 
remove_png_term_from_plot_by_name(const char * fname)677 int remove_png_term_from_plot_by_name (const char *fname)
678 {
679     return add_or_remove_png_term(fname, REMOVE_PNG, NULL);
680 }
681 
mark_plot_as_saved(GPT_SPEC * spec)682 static void mark_plot_as_saved (GPT_SPEC *spec)
683 {
684     png_plot *plot = (png_plot *) spec->ptr;
685 
686     plot->status |= PLOT_SAVED;
687 }
688 
gnuplot_png_init(png_plot * plot,FILE ** fpp)689 static int gnuplot_png_init (png_plot *plot, FILE **fpp)
690 {
691     GPT_SPEC *spec = plot->spec;
692     char fname[FILENAME_MAX];
693 
694     if (plot_is_saved(plot)) {
695 	/* session graph: ensure we're writing to the correct
696 	   directory */
697 	session_graph_make_path(fname, spec->fname);
698     } else {
699 	strcpy(fname, spec->fname);
700     }
701 
702     *fpp = gretl_fopen(fname, "w");
703 
704     if (*fpp == NULL) {
705 	file_write_errbox(fname);
706 	return 1;
707     }
708 
709     fprintf(*fpp, "%s\n", get_png_line_for_plotspec(spec));
710     write_plot_output_line(NULL, *fpp);
711 
712     return 0;
713 }
714 
gp_term_code(gpointer p,int action)715 int gp_term_code (gpointer p, int action)
716 {
717     GPT_SPEC *spec;
718 
719     if (action == SAVE_GNUPLOT) {
720 	png_plot *plot = (png_plot *) p;
721 
722 	spec = plot->spec;
723     } else {
724 	/* EPS/PDF saver */
725 	spec = graph_saver_get_plotspec(p);
726     }
727 
728     return spec->termtype;
729 }
730 
emit_alt_datafile_line(const char * inpname,FILE * fp)731 static void emit_alt_datafile_line (const char *inpname,
732 				    FILE *fp)
733 {
734     gchar *tmp = g_strdup_printf("%s.dat", inpname);
735 
736 #ifdef G_OS_WIN32
737     gchar *s = tmp;
738 
739     while (*s) {
740 	if (*s == '\\') *s = '/';
741 	s++;
742     }
743 #endif
744 
745     fprintf(fp, "datafile = \"%s\"\n", tmp);
746     g_free(tmp);
747 }
748 
filter_gnuplot_file(int mono,const char * inpname,FILE * fpin,FILE * fpout)749 void filter_gnuplot_file (int mono, const char *inpname,
750 			  FILE *fpin, FILE *fpout)
751 {
752     char pline[512];
753 
754     while (fgets(pline, sizeof pline, fpin)) {
755 	if (set_print_line(pline)) {
756 	    break;
757 	}
758 
759 	if (!strncmp(pline, "set term", 8) ||
760 	    !strncmp(pline, "set enco", 8) ||
761 	    !strncmp(pline, "set outp", 8)) {
762 	    continue;
763 	}
764 
765 	if (!strncmp(pline, "datafile = sprintf", 17)) {
766 	    /* geomap special */
767 	    emit_alt_datafile_line(inpname, fpout);
768 	    continue;
769 	}
770 
771 	if (mono) {
772 	    if ((strstr(pline, "set style line") || strstr(pline, "set linetype"))
773 		&& strstr(pline, "rgb")) {
774 		continue;
775 	    } else if (strstr(pline, "set style fill solid")) {
776 		fputs("set style fill solid 0.3\n", fpout);
777 		continue;
778 	    }
779 	}
780 
781 	fputs(pline, fpout);
782     }
783 }
784 
785 /* note @termstr will be non-NULL only if we're being
786    called from the interactive PDF/EPS preview or save
787    context
788 */
789 
revise_plot_file(GPT_SPEC * spec,const char * inpname,const char * outname,const char * termstr)790 static int revise_plot_file (GPT_SPEC *spec,
791 			     const char *inpname,
792 			     const char *outname,
793 			     const char *termstr)
794 {
795     FILE *fpin = NULL;
796     FILE *fpout = NULL;
797     int mono = (spec->flags & GPT_MONO);
798     int err = 0;
799 
800     fpin = gretl_fopen(spec->fname, "rb");
801     if (fpin == NULL) {
802 	file_read_errbox(spec->fname);
803 	return 1;
804     }
805 
806     fpout = gretl_fopen(inpname, "wb");
807     if (fpout == NULL) {
808 	fclose(fpin);
809 	file_write_errbox(inpname);
810 	return 1;
811     }
812 
813     if (outname != NULL && *outname != '\0') {
814 	if (termstr == NULL) {
815 	    termstr = gretl_gnuplot_term_line(spec->termtype,
816 					      spec->code,
817 					      spec->flags,
818 					      spec->fontstr);
819 	}
820 	fprintf(fpout, "%s\n", termstr);
821 	if (mono) {
822 	    fputs("set mono\n", fpout);
823 	}
824 	if (strstr(termstr, "set encoding") == NULL) {
825 	    fputs("set encoding utf8\n", fpout);
826 	}
827 	write_plot_output_line(outname, fpout);
828     }
829 
830     filter_gnuplot_file(mono, spec->fname, fpin, fpout);
831 
832     fclose(fpin);
833     fclose(fpout);
834 
835     return err;
836 }
837 
save_graph_to_file(gpointer data,const char * fname)838 void save_graph_to_file (gpointer data, const char *fname)
839 {
840     png_plot *plot = (png_plot *) data;
841     GPT_SPEC *spec = plot->spec;
842     char pltname[FILENAME_MAX];
843     int err = 0;
844 
845     sprintf(pltname, "%sgptout.tmp", gretl_dotdir());
846 
847     if (plot_is_geomap(plot)) {
848 	set_special_plot_size(plot->pixel_width, plot->pixel_height);
849     }
850     err = revise_plot_file(spec, pltname, fname, NULL);
851 
852     if (!err) {
853 	gchar *plotcmd;
854 
855 	plotcmd = g_strdup_printf("\"%s\" \"%s\"",
856 				  gretl_gnuplot_path(),
857 				  pltname);
858 	err = gretl_spawn(plotcmd);
859 	gretl_remove(pltname);
860 	g_free(plotcmd);
861 	if (err) {
862 	    gui_errmsg(err);
863 	}
864     }
865 }
866 
map_pdf_termstr(png_plot * plot)867 static gchar *map_pdf_termstr (png_plot *plot)
868 {
869     double w = plot->pixel_width;
870     double h = plot->pixel_height;
871     double hr = h / w;
872 
873     if (hr > 8.5 / 5.5) {
874 	/* tall and skinny */
875 	h = 8.5;
876 	w = h / hr;
877     } else {
878 	w = 5.5;
879 	h = w * hr;
880     }
881 
882     return g_strdup_printf("set term pdfcairo font \"sans,12\" size %g,%g", w, h);
883 }
884 
885 #define GRETL_PDF_TMP "gretltmp.pdf"
886 #define GRETL_EPS_TMP "gretltmp.eps"
887 
graph_display_pdf(png_plot * plot)888 static void graph_display_pdf (png_plot *plot)
889 {
890     GPT_SPEC *spec = plot->spec;
891     char pdfname[FILENAME_MAX];
892     char plttmp[FILENAME_MAX];
893     gchar *plotcmd;
894     int err = 0;
895 
896     spec->termtype = GP_TERM_PDF;
897     gretl_build_path(plttmp, gretl_dotdir(), "gptout.tmp", NULL);
898     gretl_build_path(pdfname, gretl_dotdir(), GRETL_PDF_TMP, NULL);
899 
900     if (plot_is_geomap(plot)) {
901 	gchar *termstr = map_pdf_termstr(plot);
902 
903 	err = revise_plot_file(spec, plttmp, pdfname, termstr);
904 	g_free(termstr);
905     } else {
906 	err = revise_plot_file(spec, plttmp, pdfname, NULL);
907     }
908     if (err) {
909 	return;
910     }
911 
912     plotcmd = g_strdup_printf("\"%s\" \"%s\"",
913 			      gretl_gnuplot_path(),
914 			      plttmp);
915     err = gretl_spawn(plotcmd);
916     gretl_remove(plttmp);
917     g_free(plotcmd);
918 
919     if (err) {
920 	gui_errmsg(err);
921 	return;
922     }
923 
924 #if defined(G_OS_WIN32)
925     win32_open_file(pdfname);
926 #elif defined(OS_OSX)
927     osx_open_file(pdfname);
928 #else
929     gretl_fork("viewpdf", pdfname, NULL);
930 #endif
931 }
932 
saver_preview_graph(GPT_SPEC * spec,char * termstr)933 void saver_preview_graph (GPT_SPEC *spec, char *termstr)
934 {
935     char grfname[FILENAME_MAX];
936     char plttmp[FILENAME_MAX];
937     gchar *plotcmd;
938     int err = 0;
939 
940     gretl_build_path(plttmp, gretl_dotdir(), "gptout.tmp", NULL);
941 
942     if (spec->termtype == GP_TERM_EPS) {
943 	gretl_build_path(grfname, gretl_dotdir(), GRETL_EPS_TMP, NULL);
944     } else {
945 	gretl_build_path(grfname, gretl_dotdir(), GRETL_PDF_TMP, NULL);
946     }
947 
948     err = revise_plot_file(spec, plttmp, grfname, termstr);
949     if (err) {
950 	return;
951     }
952 
953     plotcmd = g_strdup_printf("\"%s\" \"%s\"", gretl_gnuplot_path(),
954 			      plttmp);
955     err = gretl_spawn(plotcmd);
956     gretl_remove(plttmp);
957     g_free(plotcmd);
958 
959     if (err) {
960 	gui_errmsg(err);
961 	return;
962     }
963 
964 #if defined(G_OS_WIN32)
965     win32_open_file(grfname);
966 #elif defined(OS_OSX)
967     osx_open_file(grfname);
968 #else
969     if (spec->termtype == GP_TERM_EPS) {
970 	gretl_fork("viewps", grfname, NULL);
971     } else {
972 	gretl_fork("viewpdf", grfname, NULL);
973     }
974 #endif
975 }
976 
saver_save_graph(GPT_SPEC * spec,char * termstr,const char * fname)977 int saver_save_graph (GPT_SPEC *spec, char *termstr, const char *fname)
978 {
979     char plttmp[FILENAME_MAX];
980     int err;
981 
982     gretl_build_path(plttmp, gretl_dotdir(), "gptout.tmp", NULL);
983     err = revise_plot_file(spec, plttmp, fname, termstr);
984 
985     if (!err) {
986 	gchar *plotcmd;
987 
988 	plotcmd = g_strdup_printf("\"%s\" \"%s\"", gretl_gnuplot_path(),
989 			      plttmp);
990 	err = gretl_spawn(plotcmd);
991 	gretl_remove(plttmp);
992 	g_free(plotcmd);
993 
994 	if (err) {
995 	    gui_errmsg(err);
996 	}
997     }
998 
999     return err;
1000 }
1001 
1002 /* we're looking for an uncommented "set term ..." */
1003 
is_term_line(const char * s,int * batch)1004 static int is_term_line (const char *s, int *batch)
1005 {
1006     int ret = 0;
1007 
1008     while (isspace(*s)) s++;
1009 
1010     if (*s != '#') {
1011 	s = strstr(s, "set term");
1012 	if (s != NULL) {
1013 	    *batch = ret = 1;
1014 	    s += 8;
1015 	    s += strcspn(s, " "); /* skip "inal"? */
1016 	    s += strspn(s, " ");  /* skip space */
1017 	    if (!strncmp(s, "win", 3) ||
1018 		!strncmp(s, "x11", 3) ||
1019 		!strncmp(s, "wxt", 3) ||
1020 		!strncmp(s, "qt", 2)) {
1021 		/* these are all interactive */
1022 		*batch = 0;
1023 	    }
1024 	}
1025     }
1026 
1027     return ret;
1028 }
1029 
1030 /* Check whether (a) we might want to prepend a "set
1031    term" statement (not if the user has already given
1032    one) and (b) whether we might want to append
1033    "pause mouse close" (not if a batch-type terminal
1034    has been specified).
1035 */
1036 
pre_test_plot_buffer(const char * buf,int * addpause,int * putterm,int * dim)1037 static void pre_test_plot_buffer (const char *buf,
1038 				  int *addpause,
1039 				  int *putterm,
1040 				  int *dim)
1041 {
1042     char bufline[512];
1043     int batch = 0;
1044 
1045     bufgets_init(buf);
1046 
1047     while (bufgets(bufline, sizeof bufline, buf)) {
1048 	if (is_term_line(bufline, &batch)) {
1049 	    *putterm = 0;
1050 	    if (*addpause && batch) {
1051 		*addpause = 0;
1052 	    }
1053 	} else if (!strncmp(bufline, "# geoplot", 9)) {
1054 	    int w, h;
1055 
1056 	    if (sscanf(bufline + 10, "%d %d", &w, &h) == 2) {
1057 		dim[0] = w; dim[1] = h;
1058 	    }
1059 	} else if (*addpause && strstr(bufline, "pause ")) {
1060 	    *addpause = 0;
1061 	}
1062     }
1063 
1064     bufgets_finalize(buf);
1065 }
1066 
geoplot_dump_revise(const char * buf,const char * src,FILE * fp)1067 static void geoplot_dump_revise (const char *buf,
1068 				 const char *src,
1069 				 FILE *fp)
1070 {
1071     char bufline[512];
1072 
1073     bufgets_init(buf);
1074     while (bufgets(bufline, sizeof bufline, buf)) {
1075 	if (!strncmp(bufline, "datafile = sp", 13)) {
1076 	    emit_alt_datafile_line(src, fp);
1077 	} else {
1078 	    fputs(bufline, fp);
1079 	}
1080     }
1081     bufgets_finalize(buf);
1082 }
1083 
1084 /* dump_plot_buffer: this is used when we're taking the material from
1085    an editor window containing gnuplot commands, and either (a)
1086    sending it to gnuplot for execution, or (b) saving it to a "user
1087    file".  In the saving-to-file case @addpause will be 0.
1088 
1089    This function handles the addition of "pause mouse close",
1090    if @addpause is non-zero.
1091 */
1092 
dump_plot_buffer(const char * buf,const char * fname,int addpause,const char * src)1093 int dump_plot_buffer (const char *buf, const char *fname,
1094 		      int addpause, const char *src)
1095 {
1096     FILE *fp = gretl_fopen(fname, "wb");
1097     int dim[2] = {0};
1098     int putterm = addpause;
1099     int wxt_ok = 0;
1100 
1101     if (fp == NULL) {
1102 	file_write_errbox(fname);
1103 	return E_FOPEN;
1104     }
1105 
1106     if (addpause) {
1107 	wxt_ok = gnuplot_has_wxt();
1108 	pre_test_plot_buffer(buf, &addpause, &putterm, dim);
1109 	if (putterm && wxt_ok) {
1110 	    if (dim[0] > 0 && dim[1] > 0) {
1111 		fprintf(fp, "set term wxt size %d,%d noenhanced\n",
1112 			dim[0], dim[1]);
1113 	    } else {
1114 		fputs("set term wxt size 640,420 noenhanced\n", fp);
1115 	    }
1116 	}
1117     }
1118 
1119     if (src != NULL && strstr(buf, "datafile = sp")) {
1120 	geoplot_dump_revise(buf, src, fp);
1121     } else {
1122 	fputs(buf, fp);
1123     }
1124 
1125     if (addpause) {
1126 	fputs("pause mouse close\n", fp);
1127     }
1128 
1129     fclose(fp);
1130 
1131     return 0;
1132 }
1133 
1134 #ifdef G_OS_WIN32
1135 
real_send_to_gp(const char * fname,int persist)1136 static int real_send_to_gp (const char *fname, int persist)
1137 {
1138     return win32_run_async(gretl_gnuplot_path(), fname);
1139 }
1140 
1141 #else
1142 
gnuplot_done(GPid pid,gint status,gpointer p)1143 static void gnuplot_done (GPid pid, gint status, gpointer p)
1144 {
1145     if (p != NULL) {
1146 	gint err_fd = GPOINTER_TO_INT(p);
1147 
1148 	if (err_fd > 0) {
1149 	    if (status != 0) {
1150 		char buf[128] = {0};
1151 
1152 		if (read(err_fd, buf, 127) > 0) {
1153 		    errbox(buf);
1154 		}
1155 	    }
1156 	    close(err_fd);
1157 	}
1158     }
1159 
1160     g_spawn_close_pid(pid);
1161 }
1162 
real_send_to_gp(const char * fname,int persist)1163 static int real_send_to_gp (const char *fname, int persist)
1164 {
1165     const char *gp = gretl_gnuplot_path();
1166     GError *error = NULL;
1167     gchar *argv[4];
1168     GPid pid = 0;
1169     gint fd = -1;
1170     gboolean run;
1171     int err = 0;
1172 
1173     argv[0] = g_strdup(gp);
1174     argv[1] = g_strdup(fname);
1175     argv[2] = persist ? g_strdup("-persist") : NULL;
1176     argv[3] = NULL;
1177 
1178     run = g_spawn_async_with_pipes(NULL, argv, NULL,
1179 				   G_SPAWN_SEARCH_PATH |
1180 				   G_SPAWN_DO_NOT_REAP_CHILD,
1181 				   NULL, NULL, &pid,
1182 				   NULL, NULL,
1183 				   &fd, &error);
1184 
1185     if (error != NULL) {
1186 	errbox(error->message);
1187 	g_error_free(error);
1188 	err = 1;
1189     } else if (!run) {
1190 	errbox(_("gnuplot command failed"));
1191 	err = 1;
1192     } else if (pid > 0) {
1193 	gpointer p = fd > 0 ? GINT_TO_POINTER(fd) : NULL;
1194 
1195 	g_child_watch_add(pid, gnuplot_done, p);
1196     }
1197 
1198     g_free(argv[0]);
1199     g_free(argv[1]);
1200     g_free(argv[2]);
1201 
1202     return err;
1203 }
1204 
1205 #endif /* Windows vs other */
1206 
1207 /* Callback for execute icon in window editing gnuplot
1208    commands: send script in @buf to gnuplot itself
1209 */
1210 
run_gnuplot_script(gchar * buf,windata_t * vwin)1211 void run_gnuplot_script (gchar *buf, windata_t *vwin)
1212 {
1213     gchar *src = NULL;
1214     gchar *tmpfile;
1215     int err;
1216 
1217     if (vwin != NULL && vwin->data != NULL) {
1218 	src = session_graph_get_filename(vwin->data);
1219     }
1220 
1221     tmpfile = gretl_make_dotpath("showtmp.gp");
1222     err = dump_plot_buffer(buf, tmpfile, 1, src);
1223 
1224     if (!err) {
1225 	err = real_send_to_gp(tmpfile, 1);
1226     }
1227 
1228     g_free(tmpfile);
1229     g_free(src);
1230 }
1231 
1232 #ifdef G_OS_WIN32
1233 
1234 /* common code for sending an EMF file to the clipboard,
1235    or printing an EMF, on MS Windows
1236 */
1237 
win32_process_graph(png_plot * plot,int dest)1238 static void win32_process_graph (png_plot *plot, int dest)
1239 {
1240     GPT_SPEC *spec = plot->spec;
1241     char emfname[FILENAME_MAX];
1242     char plttmp[FILENAME_MAX];
1243     gchar *plotcmd;
1244     int err = 0;
1245 
1246     spec->termtype = GP_TERM_EMF;
1247     gretl_build_path(plttmp, gretl_dotdir(), "gptout.tmp", NULL);
1248     gretl_build_path(emfname, gretl_dotdir(), "gpttmp.emf", NULL);
1249 
1250     if (plot_is_geomap(plot)) {
1251 	set_special_plot_size(plot->pixel_width, plot->pixel_height);
1252     }
1253 
1254     err = revise_plot_file(spec, plttmp, emfname, NULL);
1255     if (err) {
1256 	return;
1257     }
1258 
1259     plotcmd = g_strdup_printf("\"%s\" \"%s\"",
1260 			      gretl_gnuplot_path(),
1261 			      plttmp);
1262     err = gretl_spawn(plotcmd);
1263     g_free(plotcmd);
1264     gretl_remove(plttmp);
1265 
1266     if (err) {
1267         errbox(_("Gnuplot error creating graph"));
1268     } else if (dest == WIN32_TO_CLIPBOARD) {
1269 	err = emf_to_clipboard(emfname);
1270     } else if (dest == WIN32_TO_PRINTER) {
1271 	err = win32_print_graph(emfname);
1272     }
1273 
1274     gretl_remove(emfname);
1275 }
1276 
1277 #else /* ! MS Windows */
1278 
1279 static png_plot *copyplot;
1280 static gboolean cb_image_mono;
1281 
1282 /* Here we're just posting the information that an image
1283    should be available for pasting (and also recording
1284    if the user wanted it to be monochrome).
1285 */
1286 
set_plot_for_copy(png_plot * plot)1287 static void set_plot_for_copy (png_plot *plot)
1288 {
1289     copyplot = plot;
1290     cb_image_mono = plot->spec->flags & GPT_MONO ? 1 : 0;
1291     flag_image_available();
1292 }
1293 
1294 /* Here we're responding to a request to paste the plot
1295    advertised above; @target tells us which of the posted
1296    formats the application has selected.
1297 */
1298 
write_plot_for_copy(int target)1299 int write_plot_for_copy (int target)
1300 {
1301     GPT_SPEC *spec = copyplot->spec;
1302     char outname[FILENAME_MAX];
1303     char inpname[FILENAME_MAX];
1304     GptFlags saveflags;
1305     double savescale;
1306     int saveterm;
1307     int err = 0;
1308 
1309     if (spec == NULL) {
1310 	fprintf(stderr, "retrieve_plot_for_copy: no data\n");
1311 	return 1;
1312     }
1313 
1314     saveflags = spec->flags;
1315     saveterm = spec->termtype;
1316     savescale = spec->scale;
1317 
1318     /* FIXME dimensions for PLOT_GEOMAP */
1319 
1320     if (target == TARGET_SVG) {
1321 	spec->termtype = GP_TERM_SVG;
1322     } else if (target == TARGET_EMF) {
1323 	spec->termtype = GP_TERM_EMF;
1324     } else if (target == TARGET_PNG) {
1325 	spec->termtype = GP_TERM_PNG;
1326 	spec->scale = 0.8;
1327     } else if (target == TARGET_EPS) {
1328 	spec->termtype = GP_TERM_EPS;
1329     } else if (target == TARGET_PDF) {
1330 	spec->termtype = GP_TERM_PDF;
1331     } else {
1332 	fprintf(stderr, "write_plot_for_copy: unsupported type\n");
1333 	return 1;
1334     }
1335 
1336     if (cb_image_mono) {
1337 	spec->flags |= GPT_MONO;
1338     }
1339 
1340     gretl_build_path(inpname, gretl_dotdir(), "gptinp.tmp", NULL);
1341     gretl_build_path(outname, gretl_dotdir(), "gptout.tmp", NULL);
1342     err = revise_plot_file(spec, inpname, outname, NULL);
1343 
1344     spec->flags = saveflags;
1345     spec->termtype = saveterm;
1346     spec->scale = savescale;
1347 
1348     if (!err) {
1349 	gchar *plotcmd = g_strdup_printf("\"%s\" \"%s\"",
1350 					 gretl_gnuplot_path(),
1351 					 inpname);
1352 	err = gretl_spawn(plotcmd);
1353 	g_free(plotcmd);
1354 	gretl_remove(inpname);
1355     }
1356 
1357     if (err) {
1358         errbox(_("Gnuplot error creating graph"));
1359     } else {
1360 	err = image_file_to_clipboard(outname);
1361     }
1362 
1363     gretl_remove(outname);
1364 
1365     return err;
1366 }
1367 
1368 #endif /* Windows vs not */
1369 
1370 /* chop trailing comma, if present; return 1 if comma chopped,
1371    zero otherwise */
1372 
chop_comma(char * str)1373 static int chop_comma (char *str)
1374 {
1375     size_t i, n = strlen(str);
1376 
1377     for (i=n-1; i>0; i--) {
1378 	if (isspace((unsigned char) str[i])) {
1379 	    continue;
1380 	}
1381 	if (str[i] == ',') {
1382 	    str[i] = 0;
1383 	    return 1;
1384 	} else {
1385 	    break;
1386 	}
1387     }
1388 
1389     return 0;
1390 }
1391 
1392 /* returns non-zero on obtaining a marker */
1393 
get_gpt_marker(const char * line,char * label,const char * format)1394 static int get_gpt_marker (const char *line, char *label,
1395 			   const char *format)
1396 {
1397     const char *p = strchr(line, '#');
1398 
1399 #if GPDEBUG > 1
1400     fprintf(stderr, "get_gpt_marker, p='%s'\n", p);
1401 #endif
1402 
1403     *label = '\0';
1404 
1405     if (p != NULL) {
1406 	sscanf(p + 2, format, label);
1407 #if GPDEBUG > 1
1408 	fprintf(stderr, "read marker: '%s'\n", label);
1409 #endif
1410     }
1411 
1412     return *label != '\0';
1413 }
1414 
1415 /* special graphs for which editing via GUI is not supported */
1416 
1417 #define cant_edit(p) (p == PLOT_CORRELOGRAM || \
1418                       p == PLOT_LEVERAGE || \
1419                       p == PLOT_MULTI_IRF || \
1420                       p == PLOT_MULTI_SCATTER || \
1421                       p == PLOT_PANEL || \
1422                       p == PLOT_TRI_GRAPH || \
1423                       p == PLOT_BI_GRAPH || \
1424 		      p == PLOT_ELLIPSE || \
1425 		      p == PLOT_3D || \
1426 		      p == PLOT_HEATMAP || \
1427 		      p == PLOT_GEOMAP)
1428 
1429 /* graphs where we don't attempt to find data coordinates */
1430 
1431 #define no_readback(p) (p == PLOT_CORRELOGRAM || \
1432                         p == PLOT_LEVERAGE || \
1433                         p == PLOT_MULTI_IRF || \
1434                         p == PLOT_MULTI_SCATTER || \
1435                         p == PLOT_PANEL || \
1436                         p == PLOT_TRI_GRAPH || \
1437                         p == PLOT_BI_GRAPH || \
1438 			p == PLOT_STACKED_BAR || \
1439 			p == PLOT_BAR || \
1440 			p == PLOT_3D)
1441 
1442 #define gp_missing(s) (s[0] == '?' || !strcmp(s, "NaN"))
1443 
get_gpt_data(GPT_SPEC * spec,int * do_markers,const char * buf)1444 static int get_gpt_data (GPT_SPEC *spec,
1445 			 int *do_markers,
1446 			 const char *buf)
1447 {
1448     char s[MAXLEN];
1449     char *got = NULL;
1450     double *x[5] = { NULL };
1451     char test[5][32];
1452     char obsfmt[12] = {0};
1453     int started_data_lines = 0;
1454     int i, j, t, imin = 0;
1455     int err = 0;
1456 
1457     spec->okobs = spec->nobs;
1458 
1459     gretl_push_c_numeric_locale();
1460 
1461     /* first handle "shaded bars" info, if present */
1462 
1463     for (i=0; i<spec->nbars && !err; i++) {
1464 	double y1, y2, dx[2];
1465 
1466 	/* start date */
1467 	got = bufgets(s, sizeof s, buf);
1468 	if (got == NULL ||
1469 	    sscanf(s, "%lf %lf %lf", &dx[0], &y1, &y2) != 3) {
1470 	    err = 1;
1471 	    break;
1472 	}
1473 	/* stop date */
1474 	got = bufgets(s, sizeof s, buf);
1475 	if (got == NULL ||
1476 	    sscanf(s, "%lf %lf %lf", &dx[1], &y1, &y2) != 3) {
1477 	    err = 1;
1478 	    break;
1479 	}
1480 	/* trailing 'e' */
1481 	got = bufgets(s, sizeof s, buf);
1482 	if (got) {
1483 	    plotspec_set_bar_info(spec, i, dx[0], dx[1]);
1484 	} else {
1485 	    err = 1;
1486 	}
1487     }
1488 
1489     if (*do_markers) {
1490 	sprintf(obsfmt, "%%%d[^\r\n]", OBSLEN - 1);
1491     }
1492 
1493     /* then get the regular plot data */
1494 
1495     for (i=0; i<spec->n_lines && !err; i++) {
1496 	int ncols = gp_line_data_columns(spec, i);
1497 	int okobs = spec->nobs;
1498 	int offset = 1;
1499 
1500 	if (ncols == 0) {
1501 	    if (i == 0) {
1502 		imin = 1;
1503 	    }
1504 	    continue;
1505 	}
1506 
1507 #if GPDEBUG
1508 	fprintf(stderr, "reading data, line %d\n", i);
1509 #endif
1510 
1511 	if (!started_data_lines) {
1512 	    offset = 0;
1513 	    x[0] = spec->data->val;
1514 	    x[1] = x[0] + spec->nobs;
1515 	    started_data_lines = 1;
1516 	}
1517 
1518 	x[2] = x[1] + spec->nobs;
1519 	x[3] = x[2] + spec->nobs;
1520 	x[4] = x[3] + spec->nobs;
1521 
1522 	for (t=0; t<spec->nobs && !err; t++) {
1523 	    int missing = 0;
1524 	    int nf = 0;
1525 
1526 	    got = bufgets(s, sizeof s, buf);
1527 	    if (got == NULL) {
1528 		err = 1;
1529 		break;
1530 	    }
1531 
1532 	    nf = 0;
1533 	    if (ncols == 5) {
1534 		nf = sscanf(s, "%31s %31s %31s %31s %31s", test[0], test[1], test[2],
1535 			    test[3], test[4]);
1536 	    } else if (ncols == 4) {
1537 		nf = sscanf(s, "%31s %31s %31s %31s", test[0], test[1], test[2], test[3]);
1538 	    } else if (ncols == 3) {
1539 		nf = sscanf(s, "%31s %31s %31s", test[0], test[1], test[2]);
1540 	    } else if (ncols == 2) {
1541 		nf = sscanf(s, "%31s %31s", test[0], test[1]);
1542 	    }
1543 
1544 	    if (nf != ncols) {
1545 		err = 1;
1546 	    }
1547 
1548 	    for (j=offset; j<nf && !err; j++) {
1549 		if (gp_missing(test[j])) {
1550 		    x[j][t] = NADBL;
1551 		    missing++;
1552 		} else if (j == 0 && (spec->flags & GPT_TIMEFMT)) {
1553 		    /* allow for backward compatibility */
1554 		    x[j][t] = gnuplot_time_from_date(test[j], spec->timefmt);
1555 		    if (na(x[j][t])) {
1556 			err = E_DATA;
1557 		    }
1558 		} else {
1559 		    x[j][t] = atof(test[j]);
1560 		}
1561 	    }
1562 
1563 	    if (missing) {
1564 		okobs--;
1565 	    }
1566 
1567 	    if (i <= imin && *do_markers) {
1568 		*do_markers = get_gpt_marker(s, spec->markers[t], obsfmt);
1569 		if (spec->code == PLOT_FACTORIZED && imin == 0) {
1570 		    imin = 1;
1571 		}
1572 	    }
1573 	}
1574 
1575 	if (okobs < spec->okobs) {
1576 	    spec->okobs = okobs;
1577 	}
1578 
1579 	/* trailer line for data block */
1580 	bufgets(s, sizeof s, buf);
1581 
1582 	/* shift 'y' writing location */
1583 	x[1] += (ncols - 1) * spec->nobs;
1584     }
1585 
1586 #if GPDEBUG
1587     gretl_matrix_print(spec->data, "spec->data");
1588 #endif
1589 
1590     gretl_pop_c_numeric_locale();
1591 
1592     return err;
1593 }
1594 
get_gpt_heredata(GPT_SPEC * spec,int * do_markers,long barpos,long datapos,const char * buf)1595 static int get_gpt_heredata (GPT_SPEC *spec,
1596 			     int *do_markers,
1597 			     long barpos,
1598 			     long datapos,
1599 			     const char *buf)
1600 {
1601     gretl_matrix *m = spec->data;
1602     char line[MAXLEN], test[32];
1603     char obsfmt[12] = {0};
1604     char *s, *got = NULL;
1605     double xij;
1606     int i, j;
1607     int err = 0;
1608 
1609     spec->okobs = spec->nobs;
1610 
1611     gretl_push_c_numeric_locale();
1612 
1613     /* first handle "shaded bars" info, if present */
1614 
1615     if (barpos > 0) {
1616 	bufseek(buf, barpos);
1617 	for (i=0; i<spec->nbars && !err; i++) {
1618 	    double y1, y2, dx[2];
1619 
1620 	    /* start date */
1621 	    got = bufgets(line, sizeof line, buf);
1622 	    if (got == NULL ||
1623 		sscanf(line, "%lf %lf %lf", &dx[0], &y1, &y2) != 3) {
1624 		err = 1;
1625 		break;
1626 	    }
1627 	    /* stop date */
1628 	    got = bufgets(line, sizeof line, buf);
1629 	    if (got == NULL ||
1630 		sscanf(line, "%lf %lf %lf", &dx[1], &y1, &y2) != 3) {
1631 		err = 1;
1632 		break;
1633 	    }
1634 	    plotspec_set_bar_info(spec, i, dx[0], dx[1]);
1635 	}
1636     }
1637 
1638     if (*do_markers) {
1639 	sprintf(obsfmt, "%%%d[^\r\n]", OBSLEN - 1);
1640     }
1641 
1642     /* then get the regular plot data */
1643 
1644     bufseek(buf, datapos);
1645 
1646     for (i=0; i<m->rows && !err; i++) {
1647 	got = bufgets(line, sizeof line, buf);
1648 	if (got == NULL) {
1649 	    err = 1;
1650 	}
1651 	s = line;
1652 	for (j=0; j<m->cols && !err; j++) {
1653 	    s += strspn(s, " ");
1654 	    sscanf(s, "%31s", test);
1655 	    if (gp_missing(test)) {
1656 		xij = NADBL;
1657 	    } else if (j == 0 && (spec->flags & GPT_TIMEFMT)) {
1658 		xij = gnuplot_time_from_date(test, spec->timefmt);
1659 	    } else {
1660 		xij = atof(test);
1661 	    }
1662 	    gretl_matrix_set(m, i, j, xij);
1663 	    s += strlen(test);
1664 	    if (*do_markers) {
1665 		*do_markers = get_gpt_marker(s, spec->markers[i], obsfmt);
1666 	    }
1667 	}
1668     }
1669 
1670     gretl_pop_c_numeric_locale();
1671 
1672 #if GPDEBUG
1673     gretl_matrix_print(m, "gp heredata");
1674 #endif
1675 
1676     gretl_pop_c_numeric_locale();
1677 
1678     return err;
1679 }
1680 
line_starts_heredata(const char * line,char ** eod)1681 static int line_starts_heredata (const char *line,
1682 				 char **eod)
1683 {
1684     int ret = 0;
1685 
1686     if (*line == '$') {
1687 	const char *p = strstr(line, "<<");
1688 
1689 	if (p != NULL) {
1690 	    int len;
1691 
1692 	    p += 2;
1693 	    p += strspn(p, " ");
1694 	    len = gretl_namechar_spn(p);
1695 	    if (len > 0) {
1696 		*eod = gretl_strndup(p, len);
1697 		ret = 1;
1698 	    }
1699 	}
1700     }
1701 
1702     return ret;
1703 }
1704 
1705 /* read a gnuplot source line specifying a text label */
1706 
parse_label_line(GPT_SPEC * spec,const char * line)1707 static int parse_label_line (GPT_SPEC *spec, const char *line)
1708 {
1709     const char *p, *s;
1710     char *text = NULL;
1711     double x, y;
1712     int nc, just = GP_JUST_LEFT;
1713     int err = 0;
1714 
1715     /* Examples:
1716        set label "this is a label" at 1998.26,937.557 left front
1717        set label 'foobar' at 1500,350 left
1718     */
1719 
1720     /* find first double or single quote */
1721     p = strchr(line, '"');
1722     if (p == NULL) {
1723 	p = strchr(line, '\'');
1724     }
1725 
1726     if (p == NULL) {
1727 	/* no label text found */
1728 	return 1;
1729     }
1730 
1731     text = gretl_quoted_string_strdup(p, &s);
1732     if (text == NULL) {
1733 	return 1;
1734     }
1735 
1736     /* get the position */
1737     p = strstr(s, "at");
1738     if (p == NULL) {
1739 	err = E_DATA;
1740     } else {
1741 	p += 2;
1742 	gretl_push_c_numeric_locale();
1743 	nc = sscanf(p, "%lf,%lf", &x, &y);
1744 	gretl_pop_c_numeric_locale();
1745 	if (nc != 2) {
1746 	    err = E_DATA;
1747 	}
1748     }
1749 
1750     if (!err) {
1751 	/* justification */
1752 	if (strstr(p, "right")) {
1753 	    just = GP_JUST_RIGHT;
1754 	} else if (strstr(p, "center")) {
1755 	    just = GP_JUST_CENTER;
1756 	}
1757     }
1758 
1759     if (!err) {
1760 	err = plotspec_add_label(spec);
1761 	if (!err) {
1762 	    int i = spec->n_labels - 1;
1763 
1764 	    strncat(spec->labels[i].text, text, PLOT_LABEL_TEXT_LEN);
1765 	    spec->labels[i].pos[0] = x;
1766 	    spec->labels[i].pos[1] = y;
1767 	    spec->labels[i].just = just;
1768 	}
1769     }
1770 
1771     free(text);
1772 
1773     return err;
1774 }
1775 
1776 /* read a gnuplot source line specifying an arrow */
1777 
parse_arrow_line(GPT_SPEC * spec,const char * line)1778 static int parse_arrow_line (GPT_SPEC *spec, const char *line)
1779 {
1780     double x0, y0, x1, y1;
1781     char head[12] = {0};
1782     int n, err = 0;
1783 
1784     /* Example:
1785        set arrow from 2000,150 to 2000,500 nohead [ lt 0 ]
1786     */
1787 
1788     gretl_push_c_numeric_locale();
1789     n = sscanf(line, "set arrow from %lf,%lf to %lf,%lf %s",
1790 	       &x0, &y0, &x1, &y1, head);
1791     gretl_pop_c_numeric_locale();
1792 
1793     if (n < 5) {
1794 	err = E_DATA;
1795     } else {
1796 	err = plotspec_add_arrow(spec);
1797     }
1798 
1799     if (!err) {
1800 	int flags = 0;
1801 
1802 	if (strcmp(head, "nohead")) {
1803 	    flags |= GP_ARROW_HEAD;
1804 	}
1805 	if (strstr(line, "lt 0")) {
1806 	    flags |= GP_ARROW_DOTS;
1807 	}
1808 	n = spec->n_arrows - 1;
1809 	spec->arrows[n].x0 = x0;
1810 	spec->arrows[n].y0 = y0;
1811 	spec->arrows[n].x1 = x1;
1812 	spec->arrows[n].y1 = y1;
1813 	spec->arrows[n].flags = flags;
1814     }
1815 
1816     return err;
1817 }
1818 
1819 static int
read_plotspec_range(const char * obj,const char * s,GPT_SPEC * spec)1820 read_plotspec_range (const char *obj, const char *s, GPT_SPEC *spec)
1821 {
1822     double r0, r1;
1823     int i = 0, err = 0;
1824 
1825     if (!strcmp(obj, "xrange")) {
1826 	i = 0;
1827     } else if (!strcmp(obj, "yrange")) {
1828 	i = 1;
1829     } else if (!strcmp(obj, "y2range")) {
1830 	i = 2;
1831     } else if (!strcmp(obj, "trange")) {
1832 	i = 3;
1833     } else if (!strcmp(obj, "x2range")) {
1834 	i = 4;
1835     } else {
1836 	err = 1;
1837     }
1838 
1839     if (!strcmp(s, "[*:*]")) {
1840 	r0 = r1 = NADBL;
1841     } else {
1842 	gretl_push_c_numeric_locale();
1843 	if (!err && sscanf(s, "[%lf:%lf]", &r0, &r1) != 2) {
1844 	    err = 1;
1845 	}
1846 	gretl_pop_c_numeric_locale();
1847     }
1848 
1849     if (!err) {
1850 	spec->range[i][0] = r0;
1851 	spec->range[i][1] = r1;
1852     }
1853 
1854     return err;
1855 }
1856 
read_plot_logscale(const char * s,GPT_SPEC * spec)1857 static int read_plot_logscale (const char *s, GPT_SPEC *spec)
1858 {
1859     char axis[3] = {0};
1860     double base = 0;
1861     int i, n, err = 0;
1862 
1863     n = sscanf(s, "%2s %lf", axis, &base);
1864 
1865     if (n < 1 || (n == 2 && base < 1.1)) {
1866 	err = 1;
1867     } else {
1868 	if (n == 1) {
1869 	    base = 10.0;
1870 	}
1871 	if (!strcmp(axis, "x")) {
1872 	    i = 0;
1873 	} else if (!strcmp(axis, "y")) {
1874 	    i = 1;
1875 	} else if (!strcmp(axis, "y2")) {
1876 	    i = 2;
1877 	} else {
1878 	    err = 1;
1879 	}
1880     }
1881 
1882     if (!err) {
1883 	spec->logbase[i] = base;
1884     }
1885 
1886     return err;
1887 }
1888 
read_plot_format(const char * s,GPT_SPEC * spec,int timefmt)1889 static int read_plot_format (const char *s, GPT_SPEC *spec,
1890 			     int timefmt)
1891 {
1892     char fmt[16];
1893     char axis = '\0';
1894     int n, err = 0;
1895 
1896     if (timefmt) {
1897 	/* including 'x' axis here is actually wrong,
1898 	   but it may be in some old plot files */
1899 	s += strspn(s, " ");
1900 	if (*s == 'x') {
1901 	    n = sscanf(s, "%c \"%15[^\"]", &axis, fmt);
1902 	    err = n < 2;
1903 	} else {
1904 	    n = sscanf(s, "%15s", fmt);
1905 	    err = n < 1;
1906 	}
1907     } else {
1908 	n = sscanf(s, "%c \"%15[^\"]", &axis, fmt);
1909 	err = n < 2;
1910     }
1911 
1912     if (!err) {
1913 	if (timefmt) {
1914 	    *spec->timefmt = '\0';
1915 	    strncat(spec->timefmt, fmt, 15);
1916 	} else if (axis == 'x') {
1917 	    *spec->xfmt = '\0';
1918 	    strncat(spec->xfmt, fmt, 15);
1919 	} else if (axis == 'y') {
1920 	    *spec->yfmt = '\0';
1921 	    strncat(spec->yfmt, fmt, 15);
1922 	}
1923     }
1924 
1925     return err;
1926 }
1927 
catch_value(char * targ,const char * src,int maxlen)1928 static int catch_value (char *targ, const char *src, int maxlen)
1929 {
1930     int i, n, q = 0;
1931     int ret = 0;
1932 
1933     src += strspn(src, " \t\r\n");
1934     if (*src == '\'' || *src == '"') {
1935 	q = *src;
1936 	src++;
1937     }
1938 
1939     *targ = '\0';
1940 
1941     if (*src != '\0') {
1942 	strncat(targ, src, maxlen - 1);
1943 	n = strlen(targ);
1944 
1945 	for (i=n-1; i>=0; i--) {
1946 	    if (isspace((unsigned char) targ[i])) {
1947 		targ[i] = '\0';
1948 	    } else {
1949 		break;
1950 	    }
1951 	}
1952 	if (targ[i] == '\'' || targ[i] == '"') {
1953 	    if (i == 0 && targ[i] == q) {
1954 		ret = 1; /* accept empty string as "value" */
1955 	    }
1956 	    targ[i] = '\0';
1957 	}
1958     }
1959 
1960     return ret || (*targ != '\0');
1961 }
1962 
fix_old_roots_plot(GPT_SPEC * spec)1963 static void fix_old_roots_plot (GPT_SPEC *spec)
1964 {
1965     strings_array_free(spec->literal, spec->n_literal);
1966     spec->literal = NULL;
1967     spec->n_literal = 0;
1968 
1969     spec->flags |= (GPT_POLAR | GPT_XZEROAXIS | GPT_YZEROAXIS);
1970     spec->border = 0;
1971     spec->keyspec = GP_KEY_NONE;
1972     strcpy(spec->xtics, "none");
1973     strcpy(spec->ytics, "none");
1974 }
1975 
unhandled_gp_line_error(const char * s)1976 static int unhandled_gp_line_error (const char *s)
1977 {
1978     int n = strlen(s);
1979 
1980     if (s[n-1] == '\n') {
1981 	char *tmp = gretl_strndup(s, n-1);
1982 
1983 	fprintf(stderr, "unhandled gnuplot command: '%s'\n", tmp);
1984 	free(tmp);
1985     } else {
1986 	fprintf(stderr, "unhandled gnuplot command: '%s'\n", s);
1987     }
1988 
1989     return 1;
1990 }
1991 
parse_gp_unset_line(GPT_SPEC * spec,const char * s)1992 static int parse_gp_unset_line (GPT_SPEC *spec, const char *s)
1993 {
1994     char key[16] = {0};
1995     int err = 0;
1996 
1997     if (sscanf(s + 6, "%15s", key) != 1) {
1998 	err = 1;
1999     } else if (!strcmp(key, "border")) {
2000 	spec->border = 0;
2001     } else if (!strcmp(key, "key")) {
2002 	spec->keyspec = GP_KEY_NONE;
2003     } else if (!strcmp(key, "xtics")) {
2004 	strcpy(spec->xtics, "none");
2005     } else if (!strcmp(key, "ytics")) {
2006 	strcpy(spec->ytics, "none");
2007     } else {
2008 	/* unrecognized "unset" parameter */
2009 	err = 1;
2010     }
2011 
2012     if (err) {
2013 	unhandled_gp_line_error(s);
2014     }
2015 
2016     return err;
2017 }
2018 
read_xtics_setting(GPT_SPEC * spec,const char * key,const char * val)2019 static void read_xtics_setting (GPT_SPEC *spec,
2020 				const char *key,
2021 				const char *val)
2022 {
2023     if (!strcmp(key, "x2tics")) {
2024 	spec->x2ticstr = gretl_strdup(val);
2025     } else if (*val == '(') {
2026 	spec->xticstr = gretl_strdup(val);
2027     } else {
2028 	*spec->xtics = '\0';
2029 	strncat(spec->xtics, val, sizeof(spec->xtics) - 1);
2030     }
2031 }
2032 
2033 /* Try to accept variant RGB specifications besides the
2034    one that's standard in gretl plot files, namely
2035    "#RRGGBB" in hex.
2036 */
2037 
verify_rgb(char * rgb)2038 static int verify_rgb (char *rgb)
2039 {
2040     char *s = rgb;
2041     char delim = s[0];
2042     int err = 0;
2043 
2044     if (delim == '"' || delim == '\'') {
2045 	const char *p;
2046 
2047 	s++;
2048 	p = strchr(s, delim);
2049 	if (p != NULL) {
2050 	    char test[16] = {0};
2051 	    int len = p - s;
2052 
2053 	    if (len > 0 && len < 16) {
2054 		strncat(test, s, len);
2055 		err = parse_gnuplot_color(test, rgb);
2056 	    } else {
2057 		err = E_DATA;
2058 	    }
2059 	} else {
2060 	    err = E_DATA;
2061 	}
2062     }
2063 
2064     return err;
2065 }
2066 
parse_linetype(const char * s,linestyle * styles)2067 static int parse_linetype (const char *s, linestyle *styles)
2068 {
2069     const char *p;
2070     int n, i = 0;
2071     int err = 0;
2072 
2073     n = sscanf(s, " %d", &i);
2074 
2075     if (n != 1 || i <= 0 || i > N_GP_LINETYPES) {
2076 	/* get out on error */
2077 	return 1;
2078     } else {
2079 	/* convert index to zero-based */
2080 	i -= 1;
2081     }
2082 
2083     if (!err && (p = strstr(s, " lc ")) != NULL) {
2084 	char lc[20];
2085 
2086 	p += 4;
2087 	if (!strncmp(p, "rgb ", 4)) {
2088 	    p += 4;
2089 	}
2090 	p += strspn(p, " ");
2091 	if (sscanf(p, "%19s", lc)) {
2092 	    err = verify_rgb(lc);
2093 	    if (!err) {
2094 		strcpy(styles[i].lc, lc);
2095 	    }
2096 	} else {
2097 	    err = 1;
2098 	}
2099     }
2100     if (!err && (p = strstr(s, " lw ")) != NULL) {
2101 	float lw = 0.0;
2102 
2103 	if (sscanf(p + 4, "%f", &lw)) {
2104 	    styles[i].lw = lw;
2105 	} else {
2106 	    err = 1;
2107 	}
2108     }
2109     if (!err && (p = strstr(s, " dt ")) != NULL) {
2110 	int dt = 0;
2111 
2112 	if (sscanf(p + 4, "%d", &dt)) {
2113 	    styles[i].dt = dt;
2114 	} else {
2115 	    err = 1;
2116 	}
2117     }
2118     if (!err && (p = strstr(s, " pt ")) != NULL) {
2119 	int pt = 0;
2120 
2121 	if (sscanf(p + 4, "%d", &pt)) {
2122 	    styles[i].pt = pt;
2123 	} else {
2124 	    err = 1;
2125 	}
2126     }
2127 
2128     return err;
2129 }
2130 
parse_gp_set_line(GPT_SPEC * spec,const char * s,linestyle * styles,int * unhandled)2131 static int parse_gp_set_line (GPT_SPEC *spec,
2132 			      const char *s,
2133 			      linestyle *styles,
2134 			      int *unhandled)
2135 {
2136     char *p, key[16] = {0};
2137     char val[MAXLEN] = {0};
2138     char *extra = NULL;
2139     int lt_pos = 0;
2140     int err = 0;
2141 
2142     if (!strncmp(s, "set linetype", 12)) {
2143 	/* e.g. set linetype 1 lc rgb "#ff0000" */
2144 	lt_pos = 12;
2145     } else if (!strncmp(s, "set style line", 14)) {
2146 	/* legacy: e.g. set style line 1 lc rgb "#ff0000" */
2147 	lt_pos = 14;
2148     } else if (!strncmp(s, "set style fill solid", 20)) {
2149 	/* special with multi-word key */
2150 	strncat(val, s + 20, 8);
2151 	spec->fillfrac = (float) atof(val);
2152 	val[0] = '\0';
2153     }
2154 
2155     if (lt_pos > 0) {
2156 	err = parse_linetype(s + lt_pos, styles);
2157 	if (err && unhandled != NULL) {
2158 	    *unhandled = 1;
2159 	}
2160 	return err;
2161     }
2162 
2163     if (unhandled != NULL) {
2164 	/* we're peeking at "literal" lines */
2165 	if (strstr(s, "set style data histogram")) {
2166 	    spec->code = PLOT_BAR;
2167 	    *unhandled = 1;
2168 	    return 0;
2169 	} else if (strstr(s, "histogram rowstacked")) {
2170 	    spec->code = PLOT_STACKED_BAR;
2171 	    *unhandled = 1;
2172 	    return 0;
2173 	}
2174     }
2175 
2176     if (sscanf(s + 4, "%11s", key) != 1) {
2177 	return unhandled_gp_line_error(s);
2178     }
2179 
2180     if ((p = strchr(key, '[')) != NULL) {
2181 	/* try fixing this */
2182 	*p = '\0';
2183     }
2184 
2185 #if GPDEBUG
2186     fprintf(stderr, "parse_gp_set_line: key = '%s'\n", key);
2187 #endif
2188 
2189     if (!strcmp(key, "term") ||
2190 	!strcmp(key, "output") ||
2191 	!strcmp(key, "encoding") ||
2192 	!strcmp(key, "datafile")) {
2193 	/* we ignore these */
2194 	return 0;
2195     }
2196 
2197     /* first, settings that don't require a parameter */
2198 
2199     if (!strcmp(key, "y2tics")) {
2200 	spec->flags |= GPT_Y2AXIS;
2201 	return 0;
2202     } else if (!strcmp(key, "parametric")) {
2203 	spec->flags |= GPT_PARAMETRIC;
2204 	return 0;
2205     } else if (!strcmp(key, "polar")) {
2206 	spec->flags |= GPT_POLAR;
2207 	return 0;
2208     } else if (!strcmp(key, "mono") ||
2209 	       !strcmp(key, "monochrome")) {
2210 	spec->flags |= GPT_MONO;
2211 	return 0;
2212     } else if (!strcmp(key, "xzeroaxis")) {
2213 	spec->flags |= GPT_XZEROAXIS;
2214 	return 0;
2215     } else if (!strcmp(key, "yzeroaxis")) {
2216 	spec->flags |= GPT_YZEROAXIS;
2217 	return 0;
2218     } else if (!strcmp(key, "noxtics")) {
2219 	strcpy(spec->xtics, "none");
2220 	return 0;
2221     } else if (!strcmp(key, "noytics")) {
2222 	strcpy(spec->ytics, "none");
2223 	return 0;
2224     } else if (!strcmp(key, "nokey")) {
2225 	spec->keyspec = GP_KEY_NONE;
2226 	return 0;
2227     } else if (!strcmp(key, "label")) {
2228 	parse_label_line(spec, s);
2229 	return 0;
2230     } else if (!strcmp(key, "arrow")) {
2231 	parse_arrow_line(spec, s);
2232 	return 0;
2233     }
2234 
2235     /* grid lines: parameter is optional */
2236     if (!strcmp(key, "grid")) {
2237 	if (catch_value(val, s + 4 + strlen(key), MAXLEN)) {
2238 	    if (!strcmp(val, "ytics")) {
2239 		spec->flags |= GPT_GRID_Y;
2240 	    } else if (!strcmp(val, "xtics")) {
2241 		spec->flags |= GPT_GRID_X;
2242 	    }
2243 	} else {
2244 	    spec->flags |= GPT_GRID_Y;
2245 	    spec->flags |= GPT_GRID_X;
2246 	}
2247     }
2248 
2249     /* now catch value for settings that need a parameter */
2250 
2251     if (!catch_value(val, s + 4 + strlen(key), MAXLEN)) {
2252 	return 0;
2253     }
2254 
2255 #if GPDEBUG
2256     fprintf(stderr, " value = '%s'\n", val);
2257 #endif
2258 
2259     if (strstr(key, "range")) {
2260 	if (read_plotspec_range(key, val, spec)) {
2261 	    return unhandled_gp_line_error(s);
2262 	}
2263     } else if (!strcmp(key, "logscale")) {
2264 	if (read_plot_logscale(val, spec)) {
2265 	    return unhandled_gp_line_error(s);
2266 	}
2267     } else if (!strcmp(key, "format")) {
2268 	if (read_plot_format(val, spec, 0)) {
2269 	    return unhandled_gp_line_error(s);
2270 	}
2271     } else if (!strcmp(key, "timefmt")) {
2272 	if (read_plot_format(val, spec, 1)) {
2273 	    return unhandled_gp_line_error(s);
2274 	}
2275     } else if (!strcmp(key, "title")) {
2276 	spec->titles[0] = g_strdup(val);
2277     } else if (!strcmp(key, "xlabel")) {
2278 	spec->titles[1] = g_strdup(val);
2279 	*spec->xvarname = '\0';
2280 	strncat(spec->xvarname, val, MAXDISP-1);
2281     } else if (!strcmp(key, "ylabel")) {
2282 	spec->titles[2] = g_strdup(val);
2283 	*spec->yvarname = '\0';
2284 	strncat(spec->yvarname, val, MAXDISP-1);
2285     } else if (!strcmp(key, "y2label")) {
2286 	spec->titles[3] = g_strdup(val);
2287     } else if (!strcmp(key, "x2label")) {
2288 	spec->titles[4] = g_strdup(val);
2289     } else if (!strcmp(key, "key")) {
2290 	spec->keyspec = gp_keypos_from_name(val);
2291     } else if (!strcmp(key, "xtics") || !strcmp(key, "x2tics")) {
2292 	read_xtics_setting(spec, key, val);
2293     } else if (!strcmp(key, "mxtics")) {
2294 	*spec->mxtics = '\0';
2295 	strncat(spec->mxtics, val, sizeof(spec->mxtics) - 1);
2296     } else if (!strcmp(key, "ytics")) {
2297 	*spec->ytics = '\0';
2298 	strncat(spec->ytics, val, sizeof(spec->ytics) - 1);
2299     } else if (!strcmp(key, "border")) {
2300 	spec->border = atoi(val);
2301 	if ((extra = strstr(val, "lc rgb \"")) != NULL) {
2302 	    spec->border_lc[0] = '\0';
2303 	    strncat(spec->border_lc, extra + 8, 7);
2304 	}
2305     } else if (!strcmp(key, "bmargin")) {
2306 	spec->bmargin = atoi(val);
2307     } else if (!strcmp(key, "boxwidth")) {
2308 	spec->boxwidth = (float) atof(val);
2309 	if (strstr(s, "absolute")) {
2310 	    spec->boxwidth = -spec->boxwidth;
2311 	}
2312     } else if (!strcmp(key, "samples")) {
2313 	spec->samples = atoi(val);
2314     } else if (!strcmp(key, "xdata")) {
2315 	if (!strncmp(val, "time", 4)) {
2316 	    spec->flags |= GPT_TIMEFMT;
2317 	}
2318     } else if (unhandled != NULL) {
2319 	*unhandled = 1;
2320     }
2321 
2322     return 0;
2323 }
2324 
2325 /* allocate markers for identifying particular data points */
2326 
plotspec_allocate_markers(GPT_SPEC * spec)2327 static int plotspec_allocate_markers (GPT_SPEC *spec)
2328 {
2329     spec->markers = strings_array_new_with_length(spec->nobs,
2330 						  OBSLEN);
2331     if (spec->markers == NULL) {
2332 	spec->n_markers = 0;
2333 	return E_ALLOC;
2334     } else {
2335 	spec->n_markers = spec->nobs;
2336 	return 0;
2337     }
2338 }
2339 
plotspec_destroy_markers(GPT_SPEC * spec)2340 static void plotspec_destroy_markers (GPT_SPEC *spec)
2341 {
2342     strings_array_free(spec->markers, spec->n_markers);
2343     spec->markers = NULL;
2344     spec->n_markers = 0;
2345 }
2346 
2347 /* Determine the number of data points in a plot. While we're at it,
2348    determine the type of plot, check whether there are any
2349    data-point markers along with the data, and see if there are
2350    are definitions of time-series vertical bars.
2351 */
2352 
get_plot_nobs(png_plot * plot,const char * buf,int * do_markers,long * barpos,long * datapos)2353 static void get_plot_nobs (png_plot *plot,
2354 			   const char *buf,
2355 			   int *do_markers,
2356 			   long *barpos,
2357 			   long *datapos)
2358 {
2359     GPT_SPEC *spec = plot->spec;
2360     int n = 0, started = -1;
2361     int startmin = 1;
2362     long auxpos = 0;
2363     char line[MAXLEN], test[12];
2364     char *eod = NULL;
2365     char *p = NULL;
2366 
2367     spec->code = PLOT_REGULAR;
2368     *do_markers = 0;
2369 
2370     while (bufgets(line, MAXLEN - 1, buf)) {
2371 
2372 	if (*line == '#' && spec->code == PLOT_REGULAR) {
2373 	    tailstrip(line);
2374 	    spec->code = plot_type_from_string(line);
2375 	    if (spec->code == PLOT_GEOMAP) {
2376 		int w, h;
2377 
2378 		if (sscanf(line + 9, "%d %d", &w, &h) == 2) {
2379 		    plot->pixel_width = w;
2380 		    plot->pixel_height = h;
2381 		}
2382 		break;
2383 	    }
2384 	}
2385 
2386 	if (sscanf(line, "# n_bars = %d", &spec->nbars) == 1) {
2387 	    /* for old-style data representation */
2388 	    startmin += spec->nbars;
2389 	    continue;
2390 	}
2391 
2392 	if (spec->nbars > 0 && !strncmp(line, "$bars <", 7)) {
2393 	    *barpos = buftell(buf);
2394 	}
2395 
2396 	if (!strncmp(line, "# auxdata", 9)) {
2397 	    auxpos = buftell(buf);
2398 	}
2399 
2400 	if (started < 0 && line_starts_heredata(line, &eod)) {
2401 	    /* newer method of handling plot data */
2402 	    spec->heredata = 1;
2403 	    *datapos = buftell(buf);
2404 #if GPDEBUG
2405 	    fprintf(stderr, "*** got heredata, datapos %d\n", (int) *datapos);
2406 #endif
2407 	    continue;
2408 	}
2409 
2410 	if (eod != NULL) {
2411 	    if (!strncmp(line, eod, strlen(eod))) {
2412 #if GPDEBUG
2413 		fprintf(stderr, "*** reached end of heredata (%s)\n", eod);
2414 #endif
2415 		free(eod);
2416 		eod = NULL;
2417 		break;
2418 	    } else if (*do_markers == 0 && (p = strchr(line, '#')) != NULL) {
2419 		if (sscanf(p + 1, "%8s", test) == 1) {
2420 		    *do_markers = 1;
2421 		}
2422 	    }
2423 	    n++;
2424 	} else {
2425 	    if (!strncmp(line, "plot", 4) || !strncmp(line, "splot", 5)) {
2426 		started = 0;
2427 	    }
2428 
2429 	    if (started == 0 && strchr(line, '\\') == NULL) {
2430 		started = 1;
2431 		continue;
2432 	    }
2433 
2434 	    if (started > 0 && started < startmin) {
2435 		if (*line == 'e') {
2436 		    started++;
2437 		    continue;
2438 		}
2439 	    }
2440 
2441 	    if (started == startmin) {
2442 		if (*line == 'e' || !strncmp(line, "set ", 4)) {
2443 		    /* end of data, or onto "set print" for bounds */
2444 		    break;
2445 		} else if (*do_markers == 0 && (p = strchr(line, '#')) != NULL) {
2446 		    if (sscanf(p + 1, "%11s", test) == 1) {
2447 			*do_markers = 1;
2448 		    }
2449 		}
2450 		n++;
2451 	    }
2452 	}
2453     }
2454 
2455     spec->nobs = n;
2456 
2457     if (spec->code == PLOT_BOXPLOTS) {
2458 	/* In the case of boxplots the gnuplot file may contain extra
2459 	   (outlier) data, which have to be read into an auxiliary
2460 	   matrix.
2461 	*/
2462 	if (auxpos > 0) {
2463 	    /* we already went past it */
2464 	    bufseek(buf, auxpos);
2465 	    buf_back_lines(buf, 1);
2466 	    bufgets(line, MAXLEN - 1, buf);
2467 	} else if (!spec->heredata) {
2468 	    /* try reading further */
2469 	    while (bufgets(line, MAXLEN - 1, buf)) {
2470 		if (!strncmp(line, "# auxdata", 9)) {
2471 		    auxpos = buftell(buf);
2472 		    break;
2473 		}
2474 	    }
2475 	}
2476 
2477 	if (auxpos > 0) {
2478 	    double x, y;
2479 	    int i, n, r = 0, c = 0;
2480 	    int err = 0;
2481 
2482 	    n = sscanf(line + 9, "%d %d", &r, &c);
2483 	    if (n == 2 && r > 0 && c == 2) {
2484 		spec->auxdata = gretl_matrix_alloc(r, c);
2485 		if (spec->auxdata != NULL) {
2486 		    gretl_push_c_numeric_locale();
2487 		    if (spec->heredata) {
2488 			/* skip a line: "$aux << EOA" */
2489 			if (bufgets(line, MAXLEN - 1, buf) == NULL) {
2490 			    err = E_DATA;
2491 			}
2492 		    }
2493 		    for (i=0; i<r && !err; i++) {
2494 			if (bufgets(line, MAXLEN - 1, buf) == NULL) {
2495 			    err = E_DATA;
2496 			} else if (sscanf(line, "%lf %lf", &x, &y) != 2) {
2497 			    err = E_DATA;
2498 			} else {
2499 			    gretl_matrix_set(spec->auxdata, i, 0, x);
2500 			    gretl_matrix_set(spec->auxdata, i, 1, y);
2501 			}
2502 		    }
2503 		    gretl_pop_c_numeric_locale();
2504 		    if (err) {
2505 			gretl_matrix_free(spec->auxdata);
2506 			spec->auxdata = NULL;
2507 		    }
2508 		}
2509 	    }
2510 	}
2511     }
2512 }
2513 
grab_fit_coeffs(GPT_SPEC * spec,const char * s)2514 static int grab_fit_coeffs (GPT_SPEC *spec, const char *s)
2515 {
2516     gretl_matrix *b = NULL;
2517     int k, f = spec->fit;
2518     int n = 0, err = 0;
2519 
2520     k = (f == PLOT_FIT_CUBIC)? 4 : (f == PLOT_FIT_QUADRATIC)? 3 : 2;
2521 
2522     b = gretl_column_vector_alloc(k);
2523     if (b == NULL) {
2524 	return E_ALLOC;
2525     }
2526 
2527     if (!strncmp(s, "exp(", 4)) {
2528 	s += 4;
2529     }
2530 
2531     gretl_push_c_numeric_locale();
2532 
2533     if (k == 2) {
2534 	n = sscanf(s, "%lf + %lf", &b->val[0], &b->val[1]);
2535     } else if (k == 3) {
2536 	n = sscanf(s, "%lf + %lf*x + %lf", &b->val[0],
2537 		   &b->val[1], &b->val[2]);
2538     } else if (k == 4) {
2539 	n = sscanf(s, "%lf + %lf*x + %lf*x**2 + %lf", &b->val[0],
2540 		   &b->val[1], &b->val[2], &b->val[3]);
2541     }
2542 
2543     gretl_pop_c_numeric_locale();
2544 
2545     if (n != k) {
2546 	err = E_DATA;
2547 	gretl_matrix_free(b);
2548 	spec->flags &= ~GPT_AUTO_FIT;
2549     } else if (f == PLOT_FIT_OLS) {
2550 	spec->b_ols = b;
2551     } else if (f == PLOT_FIT_INVERSE) {
2552 	spec->b_inv = b;
2553     } else if (f == PLOT_FIT_LOGLIN) {
2554 	spec->b_log = b;
2555     } else if (f == PLOT_FIT_QUADRATIC) {
2556 	spec->b_quad = b;
2557     } else if (f == PLOT_FIT_CUBIC) {
2558 	spec->b_cub = b;
2559     } else if (f == PLOT_FIT_LINLOG) {
2560 	spec->b_linlog = b;
2561     }
2562 
2563     return err;
2564 }
2565 
2566 /* scan the stuff after "title '" or 'title "' */
2567 
grab_line_title(GPT_LINE * line,const char * src)2568 static void grab_line_title (GPT_LINE *line, const char *src)
2569 {
2570     char tmp[128];
2571 
2572     *tmp = '\0';
2573 
2574     if (*src == '\'') {
2575 	sscanf(src + 1, "%127[^']'", tmp);
2576     } else {
2577 	sscanf(src + 1, "%127[^\"]\"", tmp);
2578     }
2579 
2580     if (*tmp != '\0') {
2581 	line->title = g_strdup(tmp);
2582     }
2583 }
2584 
grab_line_rgb(char * targ,const char * src)2585 static void grab_line_rgb (char *targ, const char *src)
2586 {
2587     if (*src == '"') {
2588 	sscanf(src + 1, "%7s", targ);
2589     }
2590 }
2591 
2592 /* Examples:
2593 
2594    using 1:2
2595    using 1:n:m:p
2596    using 1:xtic(...)
2597    using 1:2:xtic(""):ytic("")
2598    using 1:($2+x)
2599 
2600    we need to extract the data column numbers, and if we
2601    find special stuff inlinerecord the 'using' string as
2602    a whole
2603 */
2604 
process_using_spec(const char ** ps,GPT_SPEC * spec,int i)2605 static int process_using_spec (const char **ps,
2606 			       GPT_SPEC *spec,
2607 			       int i)
2608 {
2609     GPT_LINE *line = &spec->lines[i];
2610     const char *f, *p, *s = *ps;
2611     int *cols = NULL;
2612     int inparen = 0;
2613     int anyparen = 0;
2614     int ticspecs = 0;
2615     int n_uniq = 0;
2616     int k, n = 0;
2617     int err = 0;
2618 
2619     if (isdigit(*s)) {
2620 	k = atoi(s);
2621 	gretl_list_append_term(&cols, k);
2622 	n_uniq++;
2623     }
2624 
2625     p = s;
2626 
2627     while (*s) {
2628 	if (*s == '(') {
2629 	    anyparen = 1;
2630 	    inparen++;
2631 	} else if (*s == ')') {
2632 	    inparen--;
2633 	} else if (!inparen) {
2634 	    if (*s == ':') {
2635 		f = s + 1;
2636 		if (*f == 'x' || *f == 'y') {
2637 		    ticspecs++;
2638 		} else {
2639 		    k = (*f == '$')? atoi(f + 1) : atoi(f);
2640 		    if (k > 0) {
2641 			if (!in_gretl_list(cols, k)) {
2642 			    n_uniq++;
2643 			}
2644 			gretl_list_append_term(&cols, k);
2645 		    }
2646 		}
2647 	    } else if (isspace(*s) || *s == ',') {
2648 		break;
2649 	    }
2650 	} else if (*s == '$' && isdigit(*(s+1))) {
2651 	    /* inparen: a tic specification may make reference
2652 	       to a data column */
2653 	    k = atoi(s + 1);
2654 	    if (k > 0) {
2655 		if (!in_gretl_list(cols, k)) {
2656 		    n_uniq++;
2657 		}
2658 		gretl_list_append_term(&cols, k);
2659 	    }
2660 	}
2661 	s++;
2662 	n++;
2663     }
2664 
2665     /* pass back pointer to remainder of line, if any */
2666     *ps = s;
2667 
2668     line->style = GP_STYLE_AUTO;
2669 
2670     if (n > 0 && (anyparen || ticspecs > 0)) {
2671 	/* record the 'using' string as is */
2672 	char *ustr = gretl_strndup(p, n);
2673 #if GPDEBUG
2674 	fprintf(stderr, "saving ustr = '%s'\n", ustr);
2675 #endif
2676 	line->ustr = ustr;
2677     }
2678 
2679     if (cols != NULL) {
2680 	line->ncols = n_uniq;
2681 	if (cols[0] == 5 && n_uniq == 2) {
2682 	    /* boxplot special */
2683 	    line->flags |= GP_LINE_BOXDATA;
2684 	}
2685 	/* cumulate total data columns */
2686 	if (spec->datacols > 0 && in_gretl_list(cols, 1)) {
2687 	    /* col 1 should already be counted? */
2688 	    spec->datacols += line->ncols - 1;
2689 	} else {
2690 	    spec->datacols += line->ncols;
2691 	}
2692 #if GPDEBUG
2693 	fprintf(stderr, "number of unique columns %d\n", n_uniq);
2694 	printlist(cols, "cols list");
2695 	fprintf(stderr, "spec->datacols now = %d\n", spec->datacols);
2696 #endif
2697 	if (line->ustr == NULL) {
2698 	    line->mcols = cols;
2699 	} else {
2700 	    free(cols);
2701 	}
2702     }
2703 
2704     return err;
2705 }
2706 
2707 /* parse the "using..." portion of plot specification for a
2708    given plot line: full form is like:
2709 
2710      using XX axes XX title XX w XX lt XX lw XX
2711 */
2712 
parse_gp_line_line(const char * s,GPT_SPEC * spec,int auto_linewidth)2713 static int parse_gp_line_line (const char *s, GPT_SPEC *spec,
2714 			       int auto_linewidth)
2715 {
2716     GPT_LINE *line;
2717     char tmp[16];
2718     const char *p;
2719     int i, err;
2720 
2721 #if GPDEBUG
2722     fprintf(stderr, "parse_gp_line_line, starting\n");
2723 #endif
2724 
2725     err = plotspec_add_line(spec);
2726     if (err) {
2727 	return err;
2728     }
2729 
2730     i = spec->n_lines - 1;
2731     line = &spec->lines[i];
2732 
2733     if (!strncmp(s, "plot ", 5)) {
2734 	s += 5;
2735     }
2736     s += strspn(s, " ");
2737 
2738     if ((p = strstr(s, " using "))) {
2739 	/* data column spec */
2740 	p += 7;
2741 	err = process_using_spec(&p, spec, i);
2742 	s = p; /* remainder of line */
2743     } else if (*s == '\'' || *s == '"') {
2744 	/* name of data file, without 'using': implicitly
2745 	   using cols 1 and 2
2746 	*/
2747 	if (*(s+1) != '-') {
2748 	    fprintf(stderr, "plotting datafile, not supported\n");
2749 	} else {
2750 	    line->ncols = 2;
2751 	    spec->datacols += 2;
2752 	}
2753     } else {
2754 	/* absence of "using" should mean that the line plots
2755 	   a formula, not a set of data columns
2756 	*/
2757 	/* get the formula: it runs up to "title" or "notitle" */
2758 	p = strstr(s, " title");
2759 	if (p == NULL) {
2760 	    p = strstr(s, " notitle");
2761 	}
2762 	if (p != NULL) {
2763 	    line->formula = g_strndup(s, p - s);
2764 	    if (i == 1 && spec->flags & GPT_AUTO_FIT) {
2765 		grab_fit_coeffs(spec, line->formula);
2766 	    }
2767 	} else {
2768 	    /* title must be implicit? */
2769 	    gchar *tmp = g_strstrip(g_strdup(s));
2770 
2771 	    line->formula = tmp;
2772 	    line->title = g_strdup(tmp);
2773 	}
2774     }
2775 
2776     if (strstr(s, "axes x1y2")) {
2777 	line->yaxis = 2;
2778     }
2779     if ((p = strstr(s, " title "))) {
2780 	grab_line_title(line, p + 7);
2781     }
2782     if ((p = strstr(s, " w "))) {
2783 	sscanf(p + 3, "%15[^, ]", tmp);
2784 	line->style = gp_style_index_from_name(tmp);
2785     }
2786     if ((p = strstr(s, " lt "))) {
2787 	sscanf(p + 4, "%d", &line->type);
2788     } else if ((p = strstr(s, " lc rgb "))) {
2789 	grab_line_rgb(line->rgb, p + 8);
2790     }
2791     if ((p = strstr(s, " ps "))) {
2792 	sscanf(p + 4, "%f", &line->pscale);
2793     }
2794     if ((p = strstr(s, " pt "))) {
2795 	sscanf(p + 4, "%d", &line->ptype);
2796     }
2797     if ((p = strstr(s, " dt "))) {
2798 	sscanf(p + 4, "%d", &line->dtype);
2799     }
2800     if (!auto_linewidth && (p = strstr(s, " lw "))) {
2801 	sscanf(p + 4, "%f", &line->width);
2802     }
2803     if ((p = strstr(s, " whiskerbars "))) {
2804 	double ww;
2805 
2806 	sscanf(p + 13, "%lf", &ww);
2807 	line->whiskwidth = (float) ww;
2808     }
2809 
2810     if (line->ncols == 0 && line->formula == NULL) {
2811 	/* got neither data column spec nor formula */
2812 	err = 1;
2813     }
2814 
2815 #if GPDEBUG
2816     fprintf(stderr, "parse_gp_line_line, returning %d\n", err);
2817 #endif
2818 
2819     return err;
2820 }
2821 
2822 /* We got a special comment supposedly indicating the name and ID
2823    number of the X or Y variable in an OLS fitted line. Here we check
2824    this info for validity: @vname should be the name of a bona fide
2825    series variable, and its ID number should match the given @v.  We
2826    return 1 if this works out OK, otherwise 0.
2827 */
2828 
plot_ols_var_ok(const char * vname,int v)2829 static int plot_ols_var_ok (const char *vname, int v)
2830 {
2831     int vcheck = current_series_index(dataset, vname);
2832 
2833     return vcheck == v;
2834 }
2835 
maybe_set_add_fit_ok(GPT_SPEC * spec)2836 static void maybe_set_add_fit_ok (GPT_SPEC *spec)
2837 {
2838     if (spec->n_lines == 2 && spec->fit != PLOT_FIT_NONE) {
2839 	; /* already OK */
2840     } else if (spec->data != NULL &&
2841 	       spec->code == PLOT_REGULAR &&
2842 	       spec->n_lines == 1 &&
2843 	       spec->lines[0].ncols == 2) {
2844 	if (spec->flags & GPT_TS) {
2845 	    spec->fit = dataset_is_time_series(dataset) ?
2846 		PLOT_FIT_NONE : PLOT_FIT_NA;
2847 	} else {
2848 	    spec->fit = PLOT_FIT_NONE;
2849 	}
2850     } else {
2851 	spec->fit = PLOT_FIT_NA;
2852     }
2853 }
2854 
plot_get_data_and_markers(GPT_SPEC * spec,const char * buf,int * do_markers,long barpos,long datapos)2855 static int plot_get_data_and_markers (GPT_SPEC *spec,
2856 				      const char *buf,
2857 				      int *do_markers,
2858 				      long barpos,
2859 				      long datapos)
2860 {
2861     int err = 0;
2862 
2863     if (spec->nobs == 0 || spec->datacols == 0) {
2864 	/* nothing to be done */
2865 	return 0;
2866     }
2867 
2868 #if GPDEBUG
2869     fprintf(stderr, "plot_get_data, allocating: nobs=%d, datacols=%d\n",
2870 	    spec->nobs, spec->datacols);
2871 #endif
2872 
2873     /* allocate for the plot data... */
2874     spec->data = gretl_matrix_alloc(spec->nobs, spec->datacols);
2875     if (spec->data == NULL) {
2876 	err = E_ALLOC;
2877     }
2878 
2879     /* and markers if any */
2880     if (!err && *do_markers) {
2881 	err = plotspec_allocate_markers(spec);
2882     }
2883 
2884     /* and time-series bars, if any */
2885     if (!err && spec->nbars > 0) {
2886 	err = plotspec_allocate_bars(spec);
2887     }
2888 
2889     /* read the data (and perhaps markers) from the plot file */
2890     if (!err) {
2891 	if (spec->heredata) {
2892 	    err = get_gpt_heredata(spec, do_markers,
2893 				   barpos, datapos, buf);
2894 	} else {
2895 	    err = get_gpt_data(spec, do_markers, buf);
2896 	}
2897     } else {
2898 	gretl_matrix_free(spec->data);
2899 	spec->data = NULL;
2900     }
2901 
2902     if (spec->markers != NULL && *do_markers == 0) {
2903 	/* something must have gone wrong: clean up */
2904 	plotspec_destroy_markers(spec);
2905     }
2906 
2907 #if GPDEBUG
2908     fprintf(stderr, "plot_get_data_and_markers:\n"
2909 	    " spec->data = %p, spec->markers = %p, spec->n_markers = %d, err = %d\n",
2910 	    spec->data, (void *) spec->markers, spec->n_markers, err);
2911 #endif
2912 
2913     return err;
2914 }
2915 
2916 /* Parse special comment to get the 0-based index numbers of
2917    any user-defined lines that have been added to the plot.
2918    The comment looks like:
2919 
2920    # %d user-defined lines: %d %d ...
2921 */
2922 
get_user_lines_list(const char * s)2923 static int *get_user_lines_list (const char *s)
2924 {
2925     int *list = NULL;
2926     char id[6];
2927     int i, n = 0;
2928 
2929     sscanf(s, "# %d", &n);
2930 
2931     if (n > 0) {
2932 	s = strchr(s, ':');
2933 	if (s != NULL) {
2934 	    list = gretl_list_new(n);
2935 	    if (list != NULL) {
2936 		s++;
2937 		for (i=0; i < n && *s != '\0'; i++) {
2938 		    s++;
2939 		    if (sscanf(s, "%5[^ ]", id)) {
2940 			list[i+1] = atoi(id);
2941 			s += strlen(id);
2942 		    }
2943 		}
2944 	    }
2945 	}
2946     }
2947 
2948     return list;
2949 }
2950 
recognize_fit_string(const char * s)2951 static FitType recognize_fit_string (const char *s)
2952 {
2953     if (strstr(s, "OLS")) {
2954 	return PLOT_FIT_OLS;
2955     } else if (strstr(s, "quadratic")) {
2956 	return PLOT_FIT_QUADRATIC;
2957     } else if (strstr(s, "cubic")) {
2958 	return PLOT_FIT_CUBIC;
2959     } else if (strstr(s, "inverse")) {
2960 	return PLOT_FIT_INVERSE;
2961     } else if (strstr(s, "loess")) {
2962 	return PLOT_FIT_LOESS;
2963     } else if (strstr(s, "semilog")) {
2964 	return PLOT_FIT_LOGLIN;
2965     } else if (strstr(s, "linlog")) {
2966 	return PLOT_FIT_LINLOG;
2967     } else {
2968 	return PLOT_FIT_NONE;
2969     }
2970 }
2971 
check_for_plot_size(GPT_SPEC * spec,gchar * buf)2972 static void check_for_plot_size (GPT_SPEC *spec, gchar *buf)
2973 {
2974     char line[128];
2975     int i = 0;
2976 
2977     while (bufgets(line, sizeof line, buf) && i < 6) {
2978 	if (!strncmp(line, "# multiple ", 11)) {
2979 	    if (strstr(line, "extra-large")) {
2980 		spec->flags |= GPT_XXL;
2981 		break;
2982 	    } else if (strstr(line, "large")) {
2983 		spec->flags |= GPT_XL;
2984 		break;
2985 	    }
2986 	} else if (strstr(line, "extra-wide")) {
2987 	    spec->flags |= GPT_XW;
2988 	    break;
2989 	}
2990 	i++;
2991     }
2992 }
2993 
check_for_plot_time_data(GPT_SPEC * spec,gchar * buf)2994 static void check_for_plot_time_data (GPT_SPEC *spec, gchar *buf)
2995 {
2996     char line[128];
2997 
2998     while (bufgets(line, sizeof line, buf)) {
2999 	if (!strncmp(line, "set timefmt \"%s\"", 16)) {
3000 	    spec->flags |= GPT_TIMEFMT;
3001 	} else if (*line == '#' && strstr(line, "letterbox")) {
3002 	    spec->flags |= GPT_LETTERBOX;
3003 	} else if (!strncmp(line, "plot", 4)) {
3004 	    break;
3005 	}
3006     }
3007 }
3008 
linestyle_init(linestyle * ls)3009 static void linestyle_init (linestyle *ls)
3010 {
3011     ls->lc[0] = '\0';
3012     ls->lw = 0;
3013     ls->dt = 0;
3014     ls->pt = 0;
3015 }
3016 
push_z_row(gretl_matrix * z,int i,int n,char * line)3017 static int push_z_row (gretl_matrix *z, int i, int n, char *line)
3018 {
3019     char *p = line;
3020     double x;
3021     int j, err = 0;
3022 
3023     errno = 0;
3024 
3025     for (j=0; j<n && *p; j++) {
3026 	if (*p == ' ') {
3027 	    p++;
3028 	}
3029 	if (*p == '?') {
3030 	    x = NADBL;
3031 	    p += 2;
3032 	} else if (!strncmp(p, "NaN", 3)) {
3033 	    x = NADBL;
3034 	    p += 4;
3035 	} else {
3036 	    x = strtod(p, &p);
3037 	    if (errno) {
3038 		err = 1;
3039 		break;
3040 	    }
3041 	}
3042 	gretl_matrix_set(z, i, j, x);
3043     }
3044 
3045     return err;
3046 }
3047 
get_heatmap_matrix(GPT_SPEC * spec,gchar * buf,char * line,size_t len,long datapos)3048 static void get_heatmap_matrix (GPT_SPEC *spec, gchar *buf,
3049 				char *line, size_t len,
3050 				long datapos)
3051 {
3052     gretl_matrix *z;
3053     int n = spec->nobs;
3054     int i, err = 0;
3055 
3056     z = gretl_matrix_alloc(n, n);
3057     if (z == NULL) {
3058 	return;
3059     }
3060 
3061     bufseek(buf, datapos);
3062 
3063     gretl_push_c_numeric_locale();
3064 
3065     for (i=0; i<n && !err; i++) {
3066 	bufgets(line, len, buf);
3067 	err = push_z_row(z, i, n, line);
3068     }
3069 
3070     gretl_pop_c_numeric_locale();
3071 
3072     if (err) {
3073 	gretl_matrix_free(z);
3074     } else {
3075 	spec->auxdata = z;
3076     }
3077 }
3078 
3079 /* Here we're seeing if we should "promote" literal plot
3080    lines to supplement or override "set" variables that
3081    are recognized by gretl and form part of @spec. If we
3082    don't do this the GUI plot editor may be broken.
3083 */
3084 
maybe_promote_literal_lines(GPT_SPEC * spec,linestyle * styles)3085 static void maybe_promote_literal_lines (GPT_SPEC *spec,
3086 					 linestyle *styles)
3087 {
3088     const char *line;
3089     int *rmlines = NULL;
3090     int unhandled;
3091     int i, err = 0;
3092 
3093     for (i=0; i<spec->n_literal; i++) {
3094 	line = spec->literal[i];
3095 	if (!strncmp(line, "set ", 4)) {
3096 	    unhandled = 0;
3097 	    gretl_push_c_numeric_locale();
3098 	    err = parse_gp_set_line(spec, line, styles, &unhandled);
3099 	    gretl_pop_c_numeric_locale();
3100 	    if (!err && !unhandled) {
3101 		gretl_list_append_term(&rmlines, i);
3102 	    }
3103 	} else if (!strncmp(line, "unset ", 6)) {
3104 	    err = parse_gp_unset_line(spec, line);
3105 	    if (!err) {
3106 		gretl_list_append_term(&rmlines, i);
3107 	    }
3108 	}
3109     }
3110 
3111     if (rmlines != NULL) {
3112 	/* at least one "literal" line has been promoted */
3113 	if (rmlines[0] == spec->n_literal) {
3114 	    strings_array_free(spec->literal, spec->n_literal);
3115 	    spec->literal = NULL;
3116 	    spec->n_literal = 0;
3117 	} else {
3118 	    char **S = spec->literal;
3119 	    int ns, orig_ns = spec->n_literal;
3120 
3121 	    ns = orig_ns - rmlines[0];
3122 	    spec->literal = strings_array_new(ns);
3123 
3124 	    if (spec->literal == NULL) {
3125 		/* unlikely, but... */
3126 		spec->literal = S;
3127 	    } else {
3128 		/* reduced version of the "literal" array */
3129 		int j = 0;
3130 
3131 		for (i=0; i<orig_ns; i++) {
3132 		    if (in_gretl_list(rmlines, i)) {
3133 			free(S[i]);
3134 		    } else {
3135 			spec->literal[j++] = S[i];
3136 		    }
3137 		    S[i] = NULL;
3138 		}
3139 		strings_array_free(S, orig_ns);
3140 		spec->n_literal = ns;
3141 	    }
3142 	}
3143     }
3144 }
3145 
plot_get_ols_info(const char * line,int * reglist)3146 static void plot_get_ols_info (const char *line,
3147 			       int *reglist)
3148 {
3149     char fmt[16], vname[VNAMELEN];
3150     int v;
3151 
3152     sprintf(fmt, "'%%%d[^\\']' (%%d)", VNAMELEN - 1);
3153 
3154     if (sscanf(line + 6, fmt, vname, &v) == 2) {
3155 	if (line[2] == 'X') {
3156 	    if (plot_ols_var_ok(vname, v)) {
3157 		reglist[3] = v;
3158 	    }
3159 	} else {
3160 	    /* 'Y' */
3161 	    if (reglist[3] > 0 && plot_ols_var_ok(vname, v)) {
3162 		reglist[0] = 3;
3163 		reglist[1] = v;
3164 	    }
3165 	}
3166     }
3167 }
3168 
3169 #define plot_needs_obs(c) (c != PLOT_ELLIPSE && \
3170                            c != PLOT_PROB_DIST && \
3171                            c != PLOT_CURVE && \
3172 			   c != PLOT_USER)
3173 
3174 /* Read plotspec struct from gnuplot command file.  This is not a
3175    general parser for gnuplot files; it is designed specifically for
3176    files auto-generated by gretl and even then there are some sorts
3177    of plot that are not fully handled.
3178 */
3179 
read_plotspec_from_file(png_plot * plot)3180 static int read_plotspec_from_file (png_plot *plot)
3181 {
3182     GPT_SPEC *spec = plot->spec;
3183     linestyle styles[N_GP_LINETYPES];
3184     int do_markers = 0;
3185     int auto_linewidth = 0;
3186     int reglist[4] = {0};
3187     int *uservec = NULL;
3188     long barpos = 0;
3189     long datapos = 0;
3190     char gpline[MAXLEN];
3191     gchar *buf = NULL;
3192     char *got = NULL;
3193 #if HANDLE_HEREDATA
3194     char *eod = NULL;
3195 #endif
3196     int ignore = 0;
3197     int i, done;
3198     int err = 0;
3199 
3200 #if GPDEBUG
3201     fprintf(stderr, "read_plotspec_from_file: spec=%p\n",
3202 	    (void *) spec);
3203 #endif
3204 
3205     /* check: are we already done? */
3206     if (PLOTSPEC_DETAILS_IN_MEMORY(spec)) {
3207 #if GPDEBUG
3208 	fprintf(stderr, " info already in memory, returning 0\n");
3209 #endif
3210 	return 0;
3211     }
3212 
3213     /* get the content of the plot file */
3214     err = gretl_file_get_contents(spec->fname, &buf, NULL);
3215     if (err) {
3216 	return err;
3217     }
3218 
3219     spec->datacols = 0;
3220     bufgets_init(buf);
3221 
3222     /* get the number of data-points, plot type, and check for
3223        observation markers and time-series bars */
3224     get_plot_nobs(plot, buf, &do_markers, &barpos, &datapos);
3225 
3226     if (spec->code == PLOT_GEOMAP) {
3227 	/* FIXME */
3228 	goto bailout;
3229     }
3230 
3231     if (spec->nobs == 0 && plot_needs_obs(spec->code)) {
3232 	/* failed reading plot data */
3233 #if GPDEBUG
3234 	fprintf(stderr, " got spec->nobs = 0\n");
3235 #endif
3236 	err = 1;
3237 	goto bailout;
3238     }
3239 
3240     if (do_markers && spec->nobs > MAX_MARKERS) {
3241 	do_markers = 0;
3242     }
3243 
3244     if (cant_edit(spec->code)) {
3245 	fprintf(stderr, "read_plotspec_from_file: plot is not editable\n");
3246 	if (maybe_big_multiplot(spec->code)) {
3247 	    buf_rewind(buf);
3248 	    check_for_plot_size(spec, buf);
3249 	} else if (spec->code == PLOT_BAND) {
3250 	    buf_rewind(buf);
3251 	    check_for_plot_time_data(spec, buf);
3252 	} else if (spec->code == PLOT_HEATMAP) {
3253 	    buf_rewind(buf);
3254 	    get_heatmap_matrix(spec, buf, gpline, sizeof gpline, datapos);
3255 	}
3256 	goto bailout;
3257     } else {
3258 	buf_rewind(buf);
3259 	check_for_plot_size(spec, buf);
3260     }
3261 
3262     for (i=0; i<N_GP_LINETYPES; i++) {
3263 	linestyle_init(&styles[i]);
3264     }
3265 
3266     buf_rewind(buf);
3267 
3268     /* get the preamble and "set" lines */
3269 
3270     while ((got = bufgets(gpline, sizeof gpline, buf)) && !err) {
3271 #if GPDEBUG
3272 	tailstrip(gpline);
3273 	fprintf(stderr, "gpline: '%s'\n", gpline);
3274 #endif
3275 	if (!strncmp(gpline, "plot ", 5)) {
3276 	    /* we're done with 'set' */
3277 	    break;
3278 	}
3279 	if (ignore) {
3280 	    if (!strncmp(gpline, "# end inline", 12)) {
3281 		ignore = 0;
3282 	    }
3283 	} else if (!strncmp(gpline, "# start inline", 14)) {
3284 	    ignore = 1;
3285 	} else if (!strncmp(gpline, "# timeseries", 12)) {
3286 	    if (sscanf(gpline, "# timeseries %d", &spec->pd)) {
3287 		plot->pd = spec->pd;
3288 	    }
3289 	    spec->flags |= GPT_TS;
3290 	    if (strstr(gpline, "letterbox")) {
3291 		spec->flags |= GPT_LETTERBOX;
3292 	    }
3293 	} else if (!strncmp(gpline, "# multiple timeseries", 21)) {
3294 	    if (sscanf(gpline, "# multiple timeseries %d", &spec->pd)) {
3295 		plot->pd = spec->pd;
3296 	    }
3297 	    spec->flags |= GPT_TS;
3298 	} else if (!strncmp(gpline, "# scale = ", 10)) {
3299 	    double x = dot_atof(gpline + 10);
3300 
3301 	    if (x >= 0.5 && x <= 2.0) {
3302 		spec->scale = x;
3303 	    }
3304 	} else if (!strncmp(gpline, "# auto linewidth", 16)) {
3305 	    auto_linewidth = 1;
3306 	} else if (!strncmp(gpline, "# fontspec: ", 12)) {
3307 	    free(spec->fontstr);
3308 	    spec->fontstr = gretl_strdup(gpline + 12);
3309 	    tailstrip(spec->fontstr);
3310 	} else if (!strncmp(gpline, "# boxplots", 10)) {
3311 	    ; /* OK */
3312 	} else if (!strncmp(gpline, "# X = ", 6) ||
3313 		   !strncmp(gpline, "# Y = ", 6)) {
3314 	    plot_get_ols_info(gpline, reglist);
3315 	} else if (sscanf(gpline, "# literal lines = %d", &spec->n_literal)) {
3316 	    spec->literal = strings_array_new(spec->n_literal);
3317 	    if (spec->literal == NULL) {
3318 		err = E_ALLOC;
3319 	    } else {
3320 		for (i=0; i<spec->n_literal; i++) {
3321 		    if (!bufgets(gpline, MAXLEN - 1, buf)) {
3322 			errbox(_("Plot file is corrupted"));
3323 		    } else {
3324 			top_n_tail(gpline, 0, NULL);
3325 			spec->literal[i] = g_strdup(gpline);
3326 		    }
3327 		}
3328 	    }
3329 	} else if (!strncmp(gpline, "# start literal lines", 21) &&
3330 		   spec->literal == NULL) {
3331 	    for (i=0; ; i++) {
3332 		if (!bufgets(gpline, MAXLEN - 1, buf)) {
3333 		    errbox(_("Plot file is corrupted"));
3334 		} else if (!strncmp(gpline, "# end literal lines", 19)) {
3335 		    break;
3336 		} else {
3337 		    top_n_tail(gpline, 0, NULL);
3338 		    strings_array_add(&spec->literal, &spec->n_literal,
3339 				      gpline);
3340 		}
3341 	    }
3342 	} else if (strstr(gpline, "automatic fit")) {
3343 	    spec->flags |= GPT_AUTO_FIT;
3344 	    spec->fit = recognize_fit_string(gpline);
3345 	} else 	if (strstr(gpline, "user-defined")) {
3346 	    uservec = get_user_lines_list(gpline);
3347 	} else if (strstr(gpline, "printing data labels")) {
3348 	    spec->flags |= GPT_PRINT_MARKERS;
3349 	} else if (!strncmp(gpline, "# ", 2)) {
3350 	    ; /* ignore unknown comment lines */
3351 	} else if (!strncmp(gpline, "set ", 4)) {
3352 	    gretl_push_c_numeric_locale();
3353 	    err = parse_gp_set_line(spec, gpline, styles, NULL);
3354 	    gretl_pop_c_numeric_locale();
3355 	} else if (!strncmp(gpline, "unset ", 6)) {
3356 	    err = parse_gp_unset_line(spec, gpline);
3357 	} else {
3358 	    /* done reading "set" lines? */
3359 	    break;
3360 	}
3361     }
3362 
3363     if (!err && got == NULL) {
3364 	err = E_DATA;
3365     }
3366 
3367     if (err) {
3368 	goto bailout;
3369     }
3370 
3371     if ((spec->flags & GPT_TIMEFMT) && *spec->timefmt == '\0') {
3372 	fprintf(stderr, "got 'xdata time' but no timefmt\n");
3373 	err = E_DATA;
3374 	goto bailout;
3375     }
3376 
3377     for (i=0; i<4; i++) {
3378 	if (!string_is_blank(spec->titles[i])) {
3379 	    gretl_delchar('"', spec->titles[i]);
3380 	}
3381     }
3382 
3383 #if HANDLE_HEREDATA
3384     /* Hmm, what's this doing here? (2020-07-13) */
3385     if (line_starts_heredata(gpline, &eod)) {
3386 	while (bufgets(gpline, MAXLEN - 1, buf) != NULL) {
3387 	    if (!strncmp(gpline, eod, strlen(eod))) {
3388 		bufgets(gpline, MAXLEN - 1, buf);
3389 		break;
3390 	    }
3391 	}
3392 	free(eod);
3393     }
3394 #endif
3395 
3396     /* then get the "plot" lines */
3397     if (strncmp(gpline, "plot ", 5) ||
3398 	(strlen(gpline) < 10 && bufgets(gpline, MAXLEN - 1, buf) == NULL)) {
3399 	err = unhandled_gp_line_error(gpline);
3400 	goto bailout;
3401     }
3402 
3403     /* if there are any time-series bars, skip past them */
3404     for (i=0; i<spec->nbars; i++) {
3405 	if ((got = bufgets(gpline, MAXLEN - 1, buf)) == NULL) {
3406 	    err = E_DATA;
3407 	}
3408     }
3409 
3410     done = 0;
3411 
3412     while (!err) {
3413 	top_n_tail(gpline, 0, NULL);
3414 	if (!chop_comma(gpline)) {
3415 	    /* line did not end with comma -> no continuation of
3416 	       the plot command */
3417 	    done = 1;
3418 	}
3419 	err = parse_gp_line_line(gpline, spec, auto_linewidth);
3420 	if (err || done || (got = bufgets(gpline, MAXLEN - 1, buf)) == NULL) {
3421 	    break;
3422 	}
3423     }
3424 
3425     if (!err && got == NULL) {
3426 	err = E_DATA;
3427     }
3428 
3429     if (!err && spec->literal != NULL) {
3430 	maybe_promote_literal_lines(spec, styles);
3431     }
3432 
3433     /* transcribe styles info into lines for use in the
3434        GUI plot-editor */
3435 
3436     for (i=0; i<spec->n_lines && !err; i++) {
3437 	GPT_LINE *line = &spec->lines[i];
3438 	int idx = line->type; /* this will be 1-based */
3439 
3440 	if (idx == LT_AUTO) {
3441 	    /* automatic sequential line-types */
3442 	    idx = i + 1;
3443 	}
3444 	if (uservec != NULL && in_gretl_list(uservec, i)) {
3445 	    line->flags |= GP_LINE_USER;
3446 	}
3447 	if (idx > 0 && idx < N_GP_LINETYPES) {
3448 	    int j = idx - 1;
3449 
3450 	    if (line->rgb[0] == '\0') {
3451 		strcpy(line->rgb, styles[j].lc);
3452 	    }
3453 	    if (line->width == 1 && styles[j].lw > 0) {
3454 		line->width = styles[j].lw;
3455 	    }
3456 	    if (line->dtype == 0 && styles[j].dt > 0) {
3457 		line->dtype = styles[j].dt;
3458 	    }
3459 	    if (line->ptype == 0 && styles[j].pt > 0) {
3460 		line->ptype = styles[j].pt;
3461 	    }
3462 	}
3463 	if (spec->auxdata != NULL && i == spec->n_lines - 1) {
3464 	    /* the last "line" doesn't use the regular
3465 	       data mechanism */
3466 	    line->flags = GP_LINE_AUXDATA;
3467 	    continue;
3468 	}
3469     }
3470 
3471 #if GPDEBUG
3472     fprintf(stderr, "plotspec, after line transcription, err=%d\n", err);
3473 #endif
3474 
3475     if (!err) {
3476 	err = plot_get_data_and_markers(spec, buf, &do_markers,
3477 					barpos, datapos);
3478     }
3479 
3480     if (!err && reglist[0] > 0) {
3481 	spec->reglist = gretl_list_copy(reglist);
3482     }
3483 
3484     if (!err && spec->fit == PLOT_FIT_NONE) {
3485 	maybe_set_add_fit_ok(spec);
3486     }
3487 
3488     if (!err && spec->code == PLOT_ROOTS && spec->n_literal == 8) {
3489 	/* backward compatibility */
3490 	fprintf(stderr, "old roots plot: fixing\n");
3491 	fix_old_roots_plot(spec);
3492     }
3493 
3494  bailout:
3495 
3496     if (err) {
3497 	fprintf(stderr, "read_plotspec_from_file: err = %d\n", err);
3498     }
3499 
3500     bufgets_finalize(buf);
3501     g_free(buf);
3502     free(uservec);
3503 
3504     return err;
3505 }
3506 
3507 #define has_log_axis(s) (s->logbase[0] != 0 || s->logbase[1] != 0)
3508 
get_data_xy(png_plot * plot,int x,int y,double * data_x,double * data_y)3509 static int get_data_xy (png_plot *plot, int x, int y,
3510 			double *data_x, double *data_y)
3511 {
3512     double xmin, xmax;
3513     double ymin, ymax;
3514     double dx = NADBL;
3515     double dy = NADBL;
3516     int ok = 1;
3517 
3518     if (plot_is_zoomed(plot)) {
3519 	xmin = plot->zoom_xmin;
3520 	xmax = plot->zoom_xmax;
3521 	ymin = plot->zoom_ymin;
3522 	ymax = plot->zoom_ymax;
3523     } else {
3524 	xmin = plot->xmin;
3525 	xmax = plot->xmax;
3526 	ymin = plot->ymin;
3527 	ymax = plot->ymax;
3528     }
3529 
3530 #if POINTS_DEBUG
3531     if (plot_doing_position(plot)) {
3532 	fprintf(stderr, "get_data_xy:\n"
3533 		" plot->xmin=%g, plot->xmax=%g, plot->ymin=%g, plot->ymax=%g\n",
3534 		plot->xmin, plot->xmax, plot->ymin, plot->ymax);
3535     }
3536 #endif
3537 
3538     if (xmin == 0.0 && xmax == 0.0) {
3539 	fprintf(stderr, "get_data_xy: unknown x range\n");
3540     } else {
3541 	dx = xmin + ((double) x - plot->pixel_xmin) /
3542 	    (plot->pixel_xmax - plot->pixel_xmin) * (xmax - xmin);
3543     }
3544 
3545     if (!na(dx)) {
3546 	if (ymin == 0.0 && ymax == 0.0) {
3547 	    fprintf(stderr, "get_data_xy: unknown y range\n");
3548 	} else {
3549 	    dy = ymax - ((double) y - plot->pixel_ymin) /
3550 		(plot->pixel_ymax - plot->pixel_ymin) * (ymax - ymin);
3551 	}
3552     }
3553 
3554     if (na(dx) || na(dy)) {
3555 	ok = 0;
3556     } else if (has_log_axis(plot->spec)) {
3557 	double base, dprop, lr;
3558 
3559 	base = plot->spec->logbase[0];
3560 	if (base != 0) {
3561 	    if (xmin > 0) {
3562 		dprop = (dx - xmin) / (xmax - xmin);
3563 		lr = log(xmax / xmin) / log(base);
3564 		dx = pow(base, dprop * lr);
3565 	    } else {
3566 		dx = NADBL;
3567 		ok = 0;
3568 	    }
3569 	}
3570 	base = plot->spec->logbase[1];
3571 	if (base != 0) {
3572 	    if (ymin > 0) {
3573 		dprop = (dy - ymin) / (ymax - ymin);
3574 		lr = log(ymax / ymin) / log(base);
3575 		dy = pow(base, dprop * lr);
3576 	    } else {
3577 		dy = NADBL;
3578 		ok = 0;
3579 	    }
3580 	}
3581     } else if (plot_is_polar(plot)) {
3582 	double px = atan2(dy, dx);
3583 	double py = sqrt(dx * dx + dy * dy);
3584 
3585 	dx = px;
3586 	dy = py;
3587     }
3588 
3589     *data_x = dx;
3590     *data_y = dy;
3591 
3592     return ok;
3593 }
3594 
x_to_date(double x,int pd,char * str)3595 static void x_to_date (double x, int pd, char *str)
3596 {
3597     int yr = (int) x;
3598     double t, frac = 1.0 / pd;
3599     int subper = (int) ((x - yr + frac) * pd);
3600     static int decpoint;
3601 
3602     if (decpoint == 0) {
3603 	decpoint = get_local_decpoint();
3604     }
3605 
3606     t = yr + subper / ((pd < 10)? 10.0 : 100.0);
3607     sprintf(str, "%.*f", (pd < 10)? 1 : 2, t);
3608     gretl_charsub(str, decpoint, ':');
3609 }
3610 
redraw_plot_rectangle(png_plot * plot,GdkRectangle * r)3611 static void redraw_plot_rectangle (png_plot *plot, GdkRectangle *r)
3612 {
3613     if (r != NULL) {
3614 	gdk_window_invalidate_rect(plot->window, r, FALSE);
3615     } else {
3616 	GdkRectangle rt = {
3617 	    0, 0, plot->pixel_width, plot->pixel_height
3618 	};
3619 
3620 	gdk_window_invalidate_rect(plot->window, &rt, FALSE);
3621     }
3622 }
3623 
make_cairo_rectangle(png_plot * plot,GdkRectangle * r)3624 static void make_cairo_rectangle (png_plot *plot,
3625 				  GdkRectangle *r)
3626 {
3627     cairo_move_to(plot->cr, r->x, r->y);
3628     cairo_line_to(plot->cr, r->x, r->y + r->height);
3629     cairo_line_to(plot->cr, r->x + r->width, r->y + r->height);
3630     cairo_line_to(plot->cr, r->x + r->width, r->y);
3631     cairo_close_path(plot->cr);
3632     cairo_stroke(plot->cr);
3633 }
3634 
3635 # if GTK_MAJOR_VERSION == 2
3636 
copy_state_to_pixbuf(png_plot * plot)3637 static void copy_state_to_pixbuf (png_plot *plot)
3638 {
3639     if (plot->savebuf != NULL) {
3640 	g_object_unref(plot->savebuf);
3641     }
3642 
3643     plot->savebuf =
3644 	gdk_pixbuf_get_from_drawable(NULL,
3645 				     plot->pixmap,
3646 				     NULL,
3647 				     0, 0, 0, 0,
3648 				     plot->pixel_width,
3649 				     plot->pixel_height);
3650 }
3651 
3652 # endif
3653 
3654 /* Note that the screen coordinates as of the last mouse
3655    button press are recorded in plot->screen_x0 and
3656    plot->screen_y0. These represent the constant corner of
3657    the box that should be drawn.
3658 */
3659 
draw_selection_box(png_plot * plot,int x,int y)3660 static void draw_selection_box (png_plot *plot, int x, int y)
3661 {
3662     GdkRectangle r;
3663 
3664     r.x = MIN(plot->screen_x0, x);
3665     r.y = MIN(plot->screen_y0, y);
3666     r.width = abs(x - plot->screen_x0);
3667     r.height = abs(y - plot->screen_y0);
3668 
3669 #if GTK_MAJOR_VERSION >= 3
3670     plot->cr = gdk_cairo_create(plot->window);
3671     cairo_set_source_rgba(plot->cr, 0.3, 0.3, 0.3, 0.3);
3672     make_cairo_rectangle(plot, &r);
3673     redraw_plot_rectangle(plot, &r);
3674     cairo_destroy(plot->cr);
3675 #else
3676     plot->cr = gdk_cairo_create(plot->pixmap);
3677     if (plot->savebuf != NULL) {
3678 	/* restore state prior to zoom start */
3679 	gdk_cairo_set_source_pixbuf(plot->cr, plot->savebuf, 0, 0);
3680 	cairo_paint(plot->cr);
3681     } else {
3682 	copy_state_to_pixbuf(plot);
3683     }
3684     cairo_set_source_rgba(plot->cr, 0.3, 0.3, 0.3, 0.3);
3685     make_cairo_rectangle(plot, &r);
3686     redraw_plot_rectangle(plot, NULL);
3687     cairo_destroy(plot->cr);
3688 #endif
3689 }
3690 
make_alt_label(gchar * alt,const gchar * label)3691 static int make_alt_label (gchar *alt, const gchar *label)
3692 {
3693     double x, y;
3694     int err = 0;
3695 
3696     gretl_push_c_numeric_locale();
3697 
3698     if (sscanf(label, "%lf,%lf", &x, &y) != 2) {
3699 	err = 1;
3700     }
3701 
3702     gretl_pop_c_numeric_locale();
3703 
3704     if (!err) {
3705 	if (get_local_decpoint() != '.') {
3706 	    sprintf(alt, "%.2f %.2f", x, y);
3707 	} else {
3708 	    sprintf(alt, "%.2f,%.2f", x, y);
3709 	}
3710     }
3711 
3712     return err;
3713 }
3714 
3715 #if GTK_MAJOR_VERSION >= 3
3716 
3717 /* given a GdkPixbuf read from file, create a corresponding cairo
3718    surface, attached to @plot as plot->cs. This will be used as
3719    the "backing store" for re-draws of the plot.
3720 */
3721 
copy_pixbuf_to_surface(png_plot * plot,GdkPixbuf * pixbuf)3722 static int copy_pixbuf_to_surface (png_plot *plot,
3723 				   GdkPixbuf *pixbuf)
3724 {
3725     cairo_format_t  format;
3726     guchar *p_data, *s_data;
3727     int nc, ps, ss;
3728     int width, height;
3729     int i, j;
3730 
3731     width = gdk_pixbuf_get_width(pixbuf);
3732     height = gdk_pixbuf_get_height(pixbuf);
3733     nc = gdk_pixbuf_get_n_channels(pixbuf);
3734     ps = gdk_pixbuf_get_rowstride(pixbuf);
3735     format = (nc == 3)? CAIRO_FORMAT_RGB24 : CAIRO_FORMAT_ARGB32;
3736 
3737     if (plot->cs != NULL) {
3738 	cairo_surface_destroy(plot->cs);
3739     }
3740 
3741     plot->cs = cairo_image_surface_create(format, width, height);
3742 
3743     if (plot->cs == NULL) {
3744 	fprintf(stderr, "copy_pixbuf_to_surface: failed\n");
3745 	return 1;
3746     } else if (cairo_surface_status(plot->cs) != CAIRO_STATUS_SUCCESS) {
3747 	fprintf(stderr, "copy_pixbuf_to_surface: failed\n");
3748 	cairo_surface_destroy(plot->cs);
3749 	plot->cs = NULL;
3750 	return 1;
3751     }
3752 
3753     ss = cairo_image_surface_get_stride(plot->cs);
3754     p_data = gdk_pixbuf_get_pixels(pixbuf);
3755     s_data = cairo_image_surface_get_data(plot->cs);
3756 
3757     for (j=0; j<height; j++) {
3758         guchar *p_iter = p_data + j * ps,
3759                *s_iter = s_data + j * ss;
3760 
3761         for (i=0; i<width; i++) {
3762 #if G_BYTE_ORDER == G_LITTLE_ENDIAN
3763             /* BGR(A) -> RGB(A) */
3764             s_iter[0] = p_iter[2];
3765             s_iter[1] = p_iter[1];
3766             s_iter[2] = p_iter[0];
3767             if (nc == 4) {
3768                 s_iter[3] = p_iter[3];
3769 	    }
3770 #else
3771             /* (A)RGB -> RGB(A) */
3772             if (nc == 4) {
3773                 s_iter[0] = p_iter[3];
3774 	    }
3775             s_iter[1] = p_iter[0];
3776             s_iter[2] = p_iter[1];
3777             s_iter[3] = p_iter[2];
3778 #endif
3779             p_iter += nc;
3780             s_iter += 4;
3781         }
3782     }
3783 
3784     return 0;
3785 }
3786 
3787 #endif
3788 
3789 /* implements "brushing-in" of data-point labels with the mouse */
3790 
3791 static void
write_label_to_plot(png_plot * plot,int i,gint x,gint y)3792 write_label_to_plot (png_plot *plot, int i, gint x, gint y)
3793 {
3794     GdkRectangle r = {x, y, 0, 0};
3795     const gchar *label = plot->spec->markers[i];
3796     PangoContext *context;
3797     PangoLayout *pl;
3798 
3799     if (plot_is_roots(plot)) {
3800 	gchar alt_label[12];
3801 
3802 	if (make_alt_label(alt_label, label)) {
3803 	    return;
3804 	}
3805 
3806 	label = alt_label;
3807     }
3808 
3809     context = gtk_widget_get_pango_context(plot->shell);
3810     pl = pango_layout_new(context);
3811     pango_layout_set_text(pl, label, -1);
3812 
3813     /* add the label, then show the modified image */
3814 
3815 #if GTK_MAJOR_VERSION >= 3
3816     plot->cr = cairo_create(plot->cs);
3817 #else
3818     plot->cr = gdk_cairo_create(plot->pixmap);
3819 #endif
3820     cairo_move_to(plot->cr, x, y);
3821     pango_cairo_show_layout(plot->cr, pl);
3822     cairo_fill(plot->cr);
3823     pango_layout_get_pixel_size(pl, &r.width, &r.height);
3824     redraw_plot_rectangle(plot, &r);
3825     cairo_destroy(plot->cr);
3826 
3827     /* trash the pango layout */
3828     g_object_unref(G_OBJECT(pl));
3829 
3830     /* record that a label is shown */
3831     plot->format |= PLOT_MARKERS_UP;
3832 }
3833 
3834 #define TOLDIST 0.01
3835 
identify_point(png_plot * plot,int pixel_x,int pixel_y,double x,double y)3836 static gint identify_point (png_plot *plot, int pixel_x, int pixel_y,
3837 			    double x, double y)
3838 {
3839     const double *data_x = NULL;
3840     const double *data_y = NULL;
3841     double xrange, yrange;
3842     double xdiff, ydiff;
3843     double dist, mindist = DBL_MAX;
3844     int best_match = -1;
3845     int done = 0, bank = 1;
3846     int t;
3847 
3848 #if GPDEBUG > 2
3849     fprintf(stderr, "identify_point: pixel_x = %d (x=%g), pixel_y = %d (y=%g)\n",
3850 	    pixel_x, x, pixel_y, y);
3851 #endif
3852 
3853     if (plot->err || plot->spec->markers == NULL) {
3854 	return TRUE;
3855     }
3856 
3857     /* need array to keep track of which points are labeled */
3858     if (plot->spec->labeled == NULL) {
3859 	plot->spec->labeled = calloc(plot->spec->nobs, 1);
3860 	if (plot->spec->labeled == NULL) {
3861 	    return TRUE;
3862 	}
3863     }
3864 
3865     if (plot_is_zoomed(plot)) {
3866 	xrange = plot->zoom_xmax - plot->zoom_xmin;
3867 	yrange = plot->zoom_ymax - plot->zoom_ymin;
3868     } else {
3869 	xrange = plot->xmax - plot->xmin;
3870 	yrange = plot->ymax - plot->ymin;
3871     }
3872 
3873     data_x = plot->spec->data->val;
3874     data_y = data_x + plot->spec->nobs;
3875 
3876     if (plot_has_y2axis(plot)) {
3877 	/* use first y-var that's on y1 axis, if any */
3878 	int i, got_y = 0;
3879 
3880 	for (i=0; i<plot->spec->n_lines; i++) {
3881 	    if (plot->spec->lines[i].yaxis == 1) {
3882 		got_y = 1;
3883 		break;
3884 	    }
3885 	    if (plot->spec->lines[i].ncols > 0) {
3886 		data_y += (plot->spec->lines[i].ncols - 1) * plot->spec->nobs;
3887 	    }
3888 	}
3889 	if (!got_y) {
3890 	    data_y = NULL;
3891 	    return TRUE;
3892 	}
3893     }
3894 
3895  repeat:
3896 
3897     /* find the best-matching data point */
3898     for (t=0; t<plot->spec->nobs; t++) {
3899 	if (na(data_x[t]) || na(data_y[t])) {
3900 	    continue;
3901 	}
3902 	xdiff = fabs(data_x[t] - x);
3903 	ydiff = fabs(data_y[t] - y);
3904 	dist = sqrt(xdiff * xdiff + ydiff * ydiff);
3905 #if GPDEBUG > 4
3906 	fprintf(stderr, " obs %d: x=%g, y=%g, dist=%g\n",
3907 		t, data_x[t], data_y[t], dist);
3908 #endif
3909 	if (dist < mindist) {
3910 	    mindist = dist;
3911 	    best_match = t;
3912 	    bank = done ? 2 : 1;
3913 	}
3914     }
3915 
3916     if (plot->spec->code == PLOT_FACTORIZED && !done) {
3917 	/* check the second "bank" of data also */
3918 	data_y += plot->spec->nobs;
3919 	done = 1;
3920 	goto repeat;
3921     }
3922 
3923     t = best_match;
3924     data_y = data_x + bank * plot->spec->nobs;
3925 
3926     /* if the closest point is already labeled, get out */
3927     if (t >= 0 && plot->spec->labeled[t]) {
3928 	return TRUE;
3929     }
3930 
3931 #if GPDEBUG > 2
3932     if (t >= 0) {
3933 	fprintf(stderr, " best_match=%d, with data_x[%d]=%g, data_y[%d]=%g\n",
3934 		t, t, data_x[t], t, data_y[t]);
3935     } else {
3936 	fprintf(stderr, " no 'best match' for %g, %g\n", x, y);
3937     }
3938 #endif
3939 
3940     /* if the match is good enough, show the label */
3941     if (best_match >= 0) {
3942 	xdiff = fabs(data_x[t] - x);
3943 	ydiff = fabs(data_y[t] - y);
3944 	if (xdiff < TOLDIST * xrange && ydiff < TOLDIST * yrange) {
3945 	    write_label_to_plot(plot, t, pixel_x, pixel_y);
3946 	    /* flag the point as labeled already */
3947 	    plot->spec->labeled[t] = 1;
3948 	}
3949     }
3950 
3951     return TRUE;
3952 }
3953 
use_integer_format(int xint,double xdata)3954 static inline int use_integer_format (int xint, double xdata)
3955 {
3956     return (xint && fabs(xdata) < 1.0e7);
3957 }
3958 
heatmap_show_z(png_plot * plot,double x,double y,gchar * label)3959 static void heatmap_show_z (png_plot *plot, double x, double y,
3960 			    gchar *label)
3961 {
3962     gretl_matrix *z = plot->spec->auxdata;
3963     double zij;
3964     int i = nearbyint(y);
3965     int j = nearbyint(x);
3966     int n = z->rows;
3967 
3968     if (i >= 0 && i < n && j >= 0 && j < n) {
3969 	zij = gretl_matrix_get(z, i, j);
3970 	if (isnan(zij)) {
3971 	    gtk_label_set_text(GTK_LABEL(plot->cursor_label), "");
3972 	} else {
3973 	    sprintf(label, "r = %.3f", zij);
3974 	    gtk_label_set_text(GTK_LABEL(plot->cursor_label), label);
3975 	}
3976     }
3977 }
3978 
3979 static gint
plot_motion_callback(GtkWidget * widget,GdkEventMotion * event,png_plot * plot)3980 plot_motion_callback (GtkWidget *widget, GdkEventMotion *event,
3981 		      png_plot *plot)
3982 {
3983     GdkModifierType state;
3984     gchar label[48], label_y[24];
3985     const char *xfmt = NULL;
3986     const char *yfmt = NULL;
3987     int do_label;
3988     int x, y;
3989 
3990     plot = plot_get_current(plot);
3991     if (plot->err) {
3992 	return TRUE;
3993     }
3994 
3995     if (event->is_hint) {
3996         gdk_window_get_pointer(event->window, &x, &y, &state);
3997     } else {
3998         x = event->x;
3999         y = event->y;
4000         state = event->state;
4001     }
4002 
4003     if (plot->spec->xfmt[0] != '\0') {
4004 	xfmt = plot->spec->xfmt;
4005     }
4006 
4007     if (plot->spec->yfmt[0] != '\0') {
4008 	yfmt = plot->spec->yfmt;
4009     }
4010 
4011     *label = '\0';
4012     do_label = plot_show_cursor_label(plot);
4013 
4014     if (x > plot->pixel_xmin && x < plot->pixel_xmax &&
4015 	y > plot->pixel_ymin && y < plot->pixel_ymax) {
4016 	double data_x, data_y;
4017 
4018 	get_data_xy(plot, x, y, &data_x, &data_y);
4019 	if (na(data_x)) {
4020 	    return TRUE;
4021 	}
4022 
4023 	if (plot->spec->code == PLOT_HEATMAP) {
4024 	    if (plot->spec->auxdata != NULL) {
4025 		heatmap_show_z(plot, data_x, data_y, label);
4026 	    }
4027 	    return TRUE;
4028 	}
4029 
4030 	if (!cant_do_labels(plot) && !labels_frozen(plot) &&
4031 	    !plot_is_zooming(plot) && !na(data_y)) {
4032 	    identify_point(plot, x, y, data_x, data_y);
4033 	}
4034 
4035 	if (do_label) {
4036 	    if (plot->pd == 4 || plot->pd == 12) {
4037 		x_to_date(data_x, plot->pd, label);
4038 	    } else if (plot->spec->flags & GPT_TIMEFMT) {
4039 		date_from_gnuplot_time(label, sizeof label,
4040 				       "%Y-%m-%d", data_x);
4041 	    } else if (xfmt != NULL) {
4042 		sprintf(label, xfmt, data_x);
4043 	    } else if (use_integer_format(plot->xint, data_x)) {
4044 		sprintf(label, "%7d", (int) data_x);
4045 	    } else {
4046 		sprintf(label, "%#7.4g", data_x);
4047 	    }
4048 	}
4049 
4050 	if (do_label && !na(data_y)) {
4051 	    if (plot_has_png_coords(plot)) {
4052 		if (yfmt != NULL) {
4053 		    sprintf(label_y, yfmt, data_y);
4054 		} else if (use_integer_format(plot->yint, data_y)) {
4055 		    sprintf(label_y, " %-7d", (int) data_y);
4056 		} else {
4057 		    sprintf(label_y, " %#-7.4g", data_y);
4058 		}
4059 	    } else if (use_integer_format(plot->yint, data_y)) {
4060 		sprintf(label_y, " %-7d", (int) data_y);
4061 	    } else {
4062 		sprintf(label_y, " %#-6.3g", data_y);
4063 	    }
4064 	    if (strlen(label) + strlen(label_y) < sizeof label) {
4065 		strcat(label, label_y);
4066 	    } else {
4067 		fprintf(stderr, "label='%s', label_y='%s'\n", label, label_y);
4068 		strcpy(label, label_y);
4069 	    }
4070 	}
4071 
4072 	if (plot_is_zooming(plot) && (state & GDK_BUTTON1_MASK)) {
4073 	    draw_selection_box(plot, x, y);
4074 	}
4075     }
4076 
4077     if (do_label) {
4078 	gtk_label_set_text(GTK_LABEL(plot->cursor_label), label);
4079     }
4080 
4081     return TRUE;
4082 }
4083 
set_plot_format_flags(png_plot * plot)4084 static void set_plot_format_flags (png_plot *plot)
4085 {
4086     plot->format = 0;
4087 
4088     if (!string_is_blank(plot->spec->titles[0])) {
4089 	plot->format |= PLOT_TITLE;
4090     }
4091     if (!string_is_blank(plot->spec->titles[1])) {
4092 	plot->format |= PLOT_XLABEL;
4093     }
4094     if (!string_is_blank(plot->spec->titles[2])) {
4095 	plot->format |= PLOT_YLABEL;
4096     }
4097     if (!string_is_blank(plot->spec->titles[3])) {
4098 	plot->format |= PLOT_Y2LABEL;
4099     }
4100     if (plot->spec->flags & GPT_Y2AXIS) {
4101 	plot->format |= PLOT_Y2AXIS;
4102     }
4103     if (plot->spec->flags & GPT_PRINT_MARKERS) {
4104 	plot->format |= PLOT_MARKERS_UP;
4105     }
4106     if (plot->spec->flags & GPT_POLAR) {
4107 	plot->format |= PLOT_POLAR;
4108     }
4109 }
4110 
4111 /* called from png plot popup menu, also toolbar item */
4112 
start_editing_png_plot(png_plot * plot)4113 void start_editing_png_plot (png_plot *plot)
4114 {
4115 #if GPDEBUG
4116     fprintf(stderr, "start_editing_png_plot: plot = %p\n", (void *) plot);
4117 #endif
4118 
4119     if (!PLOTSPEC_DETAILS_IN_MEMORY(plot->spec)) {
4120 	errbox(_("Couldn't access graph info"));
4121 	plot->err = 1;
4122 	return;
4123     }
4124 
4125     if (plot->editor != NULL) {
4126 	gtk_window_present(GTK_WINDOW(plot->editor));
4127     } else {
4128 	plot->editor = plot_add_editor(plot);
4129 	if (plot->editor != NULL) {
4130 	    g_signal_connect(G_OBJECT(plot->editor), "destroy",
4131 			     G_CALLBACK(gtk_widget_destroyed),
4132 			     &plot->editor);
4133 	    g_signal_connect_swapped(G_OBJECT(plot->editor), "destroy",
4134 				     G_CALLBACK(terminate_plot_positioning),
4135 				     plot);
4136 	}
4137     }
4138 }
4139 
substitute_graph_font(char * line,const gchar * fstr)4140 static int substitute_graph_font (char *line, const gchar *fstr)
4141 {
4142     char *p = strstr(line, " font ");
4143 
4144     if (p != NULL) {
4145 	char tmp[256];
4146 
4147 	*tmp = '\0';
4148 	strncat(tmp, line, p - line + 7);
4149 	strcat(tmp, fstr);      /* replacement font string */
4150 	p = strchr(p + 7, '"'); /* closing quote of original font string */
4151 	if (p != NULL) {
4152 	    strcat(tmp, p);
4153 	    strcpy(line, tmp);
4154 	}
4155     }
4156 
4157     return (p == NULL)? E_DATA : 0;
4158 }
4159 
4160 /* Filter the original font spec out of the plot file, substituting
4161    the user's choice from a font selection dialog, then recreate
4162    the on-screen PNG. We do this for plots for which we can't offer
4163    the full-blown graph editor.
4164 */
4165 
activate_plot_font_choice(png_plot * plot,const char * grfont)4166 void activate_plot_font_choice (png_plot *plot, const char *grfont)
4167 {
4168     FILE *fp = NULL;
4169     FILE *ftmp = NULL;
4170     char tmpname[FILENAME_MAX];
4171     char line[256], fontspec[128];
4172     int gotterm = 0;
4173     int err = 0;
4174 
4175     adjust_fontspec_string(fontspec, grfont, ADD_COMMA);
4176 
4177 #if GPDEBUG
4178     fprintf(stderr, "font choice: grfont='%s', fontspec='%s'\n",
4179 	    grfont, fontspec);
4180 #endif
4181 
4182     fp = gretl_fopen(plot->spec->fname, "r");
4183     if (fp == NULL) {
4184 	file_read_errbox(plot->spec->fname);
4185 	return;
4186     }
4187 
4188     sprintf(tmpname, "%sgpttmp", gretl_dotdir());
4189     ftmp = gretl_tempfile_open(tmpname);
4190     if (ftmp == NULL) {
4191 	fclose(fp);
4192 	gui_errmsg(E_FOPEN);
4193 	return;
4194     }
4195 
4196     while (fgets(line, sizeof line, fp) && !err) {
4197 	if (!gotterm && strncmp(line, "set term", 8) == 0) {
4198 	    err = substitute_graph_font(line, fontspec);
4199 	    fputs(line, ftmp);
4200 	    gotterm = 1;
4201 	} else {
4202 	    fputs(line, ftmp);
4203 	}
4204     }
4205 
4206     fclose(fp);
4207     fclose(ftmp);
4208 
4209     if (err) {
4210 	gretl_remove(tmpname);
4211     } else {
4212 	gchar *plotcmd;
4213 
4214 	err = gretl_rename(tmpname, plot->spec->fname);
4215 	if (!err) {
4216 	    plotcmd = g_strdup_printf("\"%s\" \"%s\"",
4217 				      gretl_gnuplot_path(),
4218 				      plot->spec->fname);
4219 	    err = gretl_spawn(plotcmd);
4220 	    g_free(plotcmd);
4221 	}
4222     }
4223 
4224     if (err) {
4225 	gui_errmsg(err);
4226     } else {
4227 	free(plot->spec->fontstr);
4228 	plot->spec->fontstr = gretl_strdup(fontspec);
4229 	render_png(plot, PNG_REDISPLAY);
4230     }
4231 }
4232 
menu_item_get_text(GtkMenuItem * item)4233 static const gchar *menu_item_get_text (GtkMenuItem *item)
4234 {
4235     GtkWidget *label = gtk_bin_get_child(GTK_BIN(item));
4236 
4237     return gtk_label_get_text(GTK_LABEL(label));
4238 }
4239 
color_popup_activated(GtkMenuItem * item,gpointer data)4240 static gint color_popup_activated (GtkMenuItem *item, gpointer data)
4241 {
4242     png_plot *plot = widget_get_plot(item);
4243     GtkWidget *parent = data;
4244     const gchar *item_string = NULL;
4245     const gchar *menu_string = NULL;
4246 
4247     item_string = menu_item_get_text(item);
4248     menu_string = menu_item_get_text(GTK_MENU_ITEM(parent));
4249 
4250     if (!strcmp(item_string, _("monochrome"))) {
4251 	plot->spec->flags |= GPT_MONO;
4252     }
4253 
4254     if (!strcmp(menu_string, _("Save as Windows metafile (EMF)..."))) {
4255 	plot->spec->termtype = GP_TERM_EMF;
4256 	file_selector_with_parent(SAVE_GNUPLOT, FSEL_DATA_MISC,
4257 				  plot, plot->shell);
4258     } else if (!strcmp(menu_string, _("Copy to clipboard"))) {
4259 #ifdef G_OS_WIN32
4260 	win32_process_graph(plot, WIN32_TO_CLIPBOARD);
4261 #else
4262 	set_plot_for_copy(plot);
4263 #endif
4264     }
4265 
4266 #ifdef G_OS_WIN32
4267     else if (!strcmp(menu_string, _("Print"))) {
4268 	win32_process_graph(plot, WIN32_TO_PRINTER);
4269     }
4270 #endif
4271 
4272     plot->spec->flags &= ~GPT_MONO;
4273 
4274     return TRUE;
4275 }
4276 
show_numbers_from_markers(GPT_SPEC * spec)4277 static void show_numbers_from_markers (GPT_SPEC *spec)
4278 {
4279     PRN *prn;
4280     double x, y, pi2;
4281     double mod, freq;
4282     int dcomma = 0;
4283     int i, err = 0;
4284 
4285     if (bufopen(&prn)) {
4286 	return;
4287     }
4288 
4289     pi2 = 2.0 * M_PI;
4290     dcomma = get_local_decpoint() != '.';
4291 
4292     pputs(prn, _("roots (real, imaginary, modulus, frequency)"));
4293     pputs(prn, "\n\n");
4294 
4295     if (dcomma) {
4296 	gretl_push_c_numeric_locale();
4297     }
4298 
4299     for (i=0; i<spec->n_markers; i++) {
4300 	if (sscanf(spec->markers[i], "%lf,%lf", &x, &y) == 2) {
4301 	    freq = gretl_matrix_get(spec->data, i, 0) / pi2;
4302 	    mod = gretl_matrix_get(spec->data, i, 1);
4303 	    if (dcomma) {
4304 		gretl_pop_c_numeric_locale();
4305 		pprintf(prn, "%2d: (%7.4f  %7.4f  %7.4f  %7.4f)\n", i+1,
4306 			x, y, mod, freq);
4307 		gretl_push_c_numeric_locale();
4308 	    } else {
4309 		pprintf(prn, "%2d: (%7.4f, %7.4f, %7.4f, %7.4f)\n", i+1,
4310 			x, y, mod, freq);
4311 	    }
4312 	} else {
4313 	    err = E_DATA;
4314 	    break;
4315 	}
4316     }
4317 
4318     if (dcomma) {
4319 	gretl_pop_c_numeric_locale();
4320     }
4321 
4322     if (err) {
4323 	gui_errmsg(err);
4324 	gretl_print_destroy(prn);
4325     } else {
4326 	gchar *title = g_strdup_printf("gretl: %s", _("roots"));
4327 
4328 	view_buffer(prn, 72, 340, title, PRINT, NULL);
4329 	g_free(title);
4330     }
4331 }
4332 
boxplot_show_summary(GPT_SPEC * spec)4333 static void boxplot_show_summary (GPT_SPEC *spec)
4334 {
4335     PRN *prn = NULL;
4336     int err;
4337 
4338     if (bufopen(&prn)) {
4339 	return;
4340     }
4341 
4342     err = boxplot_numerical_summary(spec->fname, prn);
4343 
4344     if (err) {
4345 	gui_errmsg(err);
4346 	gretl_print_destroy(prn);
4347     } else {
4348 	view_buffer(prn, 78, 240, _("gretl: boxplot data"), PRINT, NULL);
4349     }
4350 }
4351 
add_to_session_callback(png_plot * plot)4352 static void add_to_session_callback (png_plot *plot)
4353 {
4354     char fullname[MAXLEN] = {0};
4355     int err, type;
4356 
4357     type = (plot->spec->code == PLOT_BOXPLOTS)? GRETL_OBJ_PLOT :
4358 	GRETL_OBJ_GRAPH;
4359 
4360     err = gui_add_graph_to_session(plot->spec->fname, fullname, type);
4361 
4362     if (!err) {
4363 	remove_png_term_from_plot(fullname, plot->spec);
4364 	mark_plot_as_saved(plot->spec);
4365 	plot->status |= PLOT_TERM_HIDDEN;
4366     }
4367 }
4368 
plot_get_siblings(png_plot * plot)4369 static GList *plot_get_siblings (png_plot *plot)
4370 {
4371     if (in_collection(plot)) {
4372 	return plot->mp->list;
4373     } else {
4374 	return NULL;
4375     }
4376 }
4377 
4378 static double graph_scales[] = {
4379     0.8, 1.0, 1.1, 1.2, 1.4
4380 };
4381 
get_graph_scale(int i,double * s)4382 int get_graph_scale (int i, double *s)
4383 {
4384     int n = G_N_ELEMENTS(graph_scales);
4385 
4386     if (i >= 0 && i < n) {
4387 	*s = graph_scales[i];
4388 	return 1;
4389     } else {
4390 	return 0;
4391     }
4392 }
4393 
real_plot_rescale(png_plot * plot,double scale)4394 static int real_plot_rescale (png_plot *plot, double scale)
4395 {
4396     FILE *fp = NULL;
4397     int err = 0;
4398 
4399     plot->spec->scale = scale;
4400     gnuplot_png_init(plot, &fp);
4401 
4402     if (fp == NULL) {
4403 	err = E_FOPEN;
4404 	gui_errmsg(err);
4405     } else {
4406 	set_png_output(plot->spec);
4407 	plotspec_print(plot->spec, fp);
4408 	fclose(fp);
4409 	unset_png_output(plot->spec);
4410     }
4411 
4412     return err;
4413 }
4414 
regenerate_pixbuf(png_plot * plot)4415 static int regenerate_pixbuf (png_plot *plot)
4416 {
4417     gchar *cmd = g_strdup_printf("\"%s\" \"%s\"",
4418 				 gretl_gnuplot_path(),
4419 				 plot->spec->fname);
4420     int err = gretl_spawn(cmd);
4421 
4422     if (!err) {
4423 	plot_invalidate_pixbuf(plot);
4424 	plot->pbuf = pixbuf_from_file(plot);
4425 	if (plot->pbuf == NULL) {
4426 	    err = 1;
4427 	}
4428     }
4429 
4430     g_free(cmd);
4431 
4432     return err;
4433 }
4434 
rescale_siblings(png_plot * plot,double scale)4435 static int rescale_siblings (png_plot *plot, double scale)
4436 {
4437     GList *L = plot_get_siblings(plot);
4438     png_plot *sib;
4439     int w, h, err = 0;
4440 
4441     while (L != NULL && !err) {
4442 	sib = L->data;
4443 	if (sib != plot) {
4444 	    err = real_plot_rescale(sib, scale);
4445 	    if (!err) {
4446 		err = regenerate_pixbuf(sib);
4447 	    }
4448 	    if (!err) {
4449 		w = gdk_pixbuf_get_width(sib->pbuf);
4450 		h = gdk_pixbuf_get_height(sib->pbuf);
4451 		if (w != sib->pixel_width || h != sib->pixel_height) {
4452 		    resize_png_plot(sib, w, h, 1);
4453 		}
4454 	    }
4455 	}
4456 	L = L->next;
4457     }
4458 
4459     return err;
4460 }
4461 
plot_do_rescale(png_plot * plot,int mod)4462 static void plot_do_rescale (png_plot *plot, int mod)
4463 {
4464     int n = G_N_ELEMENTS(graph_scales);
4465     double scale = 1.0;
4466     int err = 0;
4467 
4468     plot = plot_get_current(plot);
4469 #if 0
4470     fprintf(stderr, "plot_do_rescale: %p\n", (void *) plot);
4471 #endif
4472 
4473     if (mod == 0) {
4474 	/* reset to default */
4475 	if (plot->spec->scale == 1.0) {
4476 	    /* no-op */
4477 	    return;
4478 	}
4479     } else {
4480 	/* enlarge or shrink */
4481 	int i;
4482 
4483 	for (i=0; i<n; i++) {
4484 	    if (plot->spec->scale == graph_scales[i]) {
4485 		break;
4486 	    }
4487 	}
4488 	if (mod == 1 && i < n - 1) {
4489 	    scale = graph_scales[i+1];
4490 	} else if (mod == -1 && i > 0) {
4491 	    scale = graph_scales[i-1];
4492 	} else {
4493 	    gdk_window_beep(plot->window);
4494 	    return;
4495 	}
4496     }
4497 
4498 #if 0
4499     fprintf(stderr, "call real_plot_scale on current\n");
4500 #endif
4501     err = real_plot_rescale(plot, scale);
4502 
4503     if (!err) {
4504 #if 0
4505 	fprintf(stderr, "repaint current\n");
4506 #endif
4507 	repaint_png(plot, PNG_REDISPLAY);
4508 	if (in_collection(plot)) {
4509 	    rescale_siblings(plot, scale);
4510 	}
4511     }
4512 
4513     gtk_widget_set_sensitive(plot->up_icon, scale != graph_scales[n-1]);
4514     gtk_widget_set_sensitive(plot->down_icon, scale != graph_scales[0]);
4515 }
4516 
show_all_labels(png_plot * plot)4517 static void show_all_labels (png_plot *plot)
4518 {
4519     FILE *fp = NULL;
4520 
4521     if (plot->spec->labeled != NULL) {
4522 	/* Destroy record of labeling of specific points via
4523 	   "brushing", if present.
4524 	*/
4525 	free(plot->spec->labeled);
4526 	plot->spec->labeled = NULL;
4527     }
4528 
4529     plot->spec->flags |= GPT_PRINT_MARKERS;
4530     gnuplot_png_init(plot, &fp);
4531 
4532     if (fp == NULL) {
4533 	gui_errmsg(E_FOPEN);
4534 	return;
4535     }
4536 
4537     plotspec_print(plot->spec, fp);
4538     fclose(fp);
4539 
4540     repaint_png(plot, PNG_REDISPLAY);
4541     plot->format |= PLOT_MARKERS_UP;
4542 }
4543 
clear_labels(png_plot * plot)4544 static void clear_labels (png_plot *plot)
4545 {
4546     FILE *fp = NULL;
4547 
4548     if (plot->spec->flags & GPT_PRINT_MARKERS) {
4549 	plot->spec->flags &= ~GPT_PRINT_MARKERS;
4550     }
4551 
4552     gnuplot_png_init(plot, &fp);
4553     if (fp == NULL) {
4554 	gui_errmsg(E_FOPEN);
4555 	return;
4556     }
4557     plotspec_print(plot->spec, fp);
4558     fclose(fp);
4559 
4560     repaint_png(plot, PNG_REDISPLAY);
4561     plot->format &= ~PLOT_MARKERS_UP;
4562 }
4563 
prepare_for_zoom(png_plot * plot)4564 static void prepare_for_zoom (png_plot *plot)
4565 {
4566     GdkCursor* cursor = gdk_cursor_new(GDK_CROSSHAIR);
4567 
4568     if (cursor != NULL) {
4569 	gdk_window_set_cursor(plot->window, cursor);
4570 	gdk_cursor_unref(cursor);
4571     }
4572     plot->status |= PLOT_ZOOMING;
4573     gtk_statusbar_push(GTK_STATUSBAR(plot->statusbar), plot->cid,
4574 		       _(" Drag to define zoom rectangle"));
4575 }
4576 
plot_popup_activated(GtkMenuItem * item,gpointer data)4577 static gint plot_popup_activated (GtkMenuItem *item, gpointer data)
4578 {
4579     png_plot *plot = (png_plot *) data;
4580     GtkWidget *shell = plot->shell;
4581     const gchar *item_string;
4582     int killplot = 0;
4583 
4584     plot = plot_get_current(plot);
4585     item_string = menu_item_get_text(item);
4586 
4587     if (!strcmp(item_string, _("Add another curve..."))) {
4588 	dist_graph_add(plot);
4589     } else if (!strcmp(item_string, _("Save as PNG..."))) {
4590 	plot->spec->termtype = GP_TERM_PNG;
4591         file_selector_with_parent(SAVE_GNUPLOT, FSEL_DATA_MISC,
4592 				  plot, shell);
4593     } else if (!strcmp(item_string, _("Save as PDF..."))) {
4594 	plot->spec->termtype = GP_TERM_PDF;
4595 	pdf_ps_dialog(plot->spec, shell);
4596     } else if (!strcmp(item_string, _("Save as postscript (EPS)..."))) {
4597 	plot->spec->termtype = GP_TERM_EPS;
4598 	pdf_ps_dialog(plot->spec, shell);
4599     } else if (!strcmp(item_string, _("Save to session as icon"))) {
4600 	add_to_session_callback(plot);
4601     } else if (plot_is_range_mean(plot) && !strcmp(item_string, _("Help"))) {
4602 	show_gui_help(RMPLOT);
4603     } else if (plot_is_hurst(plot) && !strcmp(item_string, _("Help"))) {
4604 	show_gui_help(HURST);
4605     } else if (!strcmp(item_string, _("Freeze data labels"))) {
4606 	plot->spec->flags |= GPT_PRINT_MARKERS;
4607 	redisplay_edited_plot(plot);
4608     } else if (!strcmp(item_string, _("Clear data labels"))) {
4609 	clear_labels(plot);
4610     } else if (!strcmp(item_string, _("All data labels"))) {
4611 	show_all_labels(plot);
4612     } else if (!strcmp(item_string, _("Zoom..."))) {
4613 	prepare_for_zoom(plot);
4614     } else if (!strcmp(item_string, _("Restore full view"))) {
4615 	repaint_png(plot, PNG_UNZOOM);
4616     } else if (!strcmp(item_string, _("Replace full view"))) {
4617 	zoom_replaces_plot(plot);
4618     } else if (!strcmp(item_string, _("Display PDF"))) {
4619 	graph_display_pdf(plot);
4620     } else if (!strcmp(item_string, _("OLS estimates"))) {
4621 	if (plot->spec != NULL) {
4622 	    do_graph_model(plot->spec->reglist, plot->spec->fit);
4623 	}
4624     } else if (!strcmp(item_string, _("Numerical values"))) {
4625 	show_numbers_from_markers(plot->spec);
4626     } else if (!strcmp(item_string, _("Numerical summary"))) {
4627 	boxplot_show_summary(plot->spec);
4628     } else if (!strcmp(item_string, _("Edit"))) {
4629 	start_editing_png_plot(plot);
4630     } else if (!strcmp(item_string, _("Font"))) {
4631 	plot_show_font_selector(plot, plot->spec->fontstr);
4632     } else if (!strcmp(item_string, _("Close"))) {
4633 	killplot = 1;
4634     } else if (!strcmp(item_string, _("Delete plot"))) {
4635         killplot = 2;
4636     } else if (!strcmp(item_string, _("Close collection"))) {
4637 	killplot = 1;
4638     } else if (!strcmp(item_string, _("Extract plot"))) {
4639 	killplot = 3;
4640     }
4641 
4642     if (killplot == 1) {
4643 	/* trash a singleton plot or entire collection */
4644 	gtk_widget_destroy(shell);
4645     } else if (killplot == 2) {
4646 	/* trash a specific plot in a collection */
4647 	plot_collection_remove_plot(plot, 1);
4648     } else if (killplot == 3) {
4649 	/* pull a plot out of a collection */
4650 	plot_collection_remove_plot(plot, 0);
4651     }
4652 
4653     return TRUE;
4654 }
4655 
attach_color_popup(GtkWidget * w,png_plot * plot)4656 static void attach_color_popup (GtkWidget *w, png_plot *plot)
4657 {
4658     GtkWidget *item, *cpopup;
4659     const char *color_items[] = {
4660 	N_("color"),
4661 	N_("monochrome")
4662     };
4663     int i;
4664 
4665     cpopup = gtk_menu_new();
4666 
4667     for (i=0; i<2; i++) {
4668 	item = gtk_menu_item_new_with_label(_(color_items[i]));
4669 	g_signal_connect(G_OBJECT(item), "activate",
4670 			 G_CALLBACK(color_popup_activated), w);
4671 	g_object_set_data(G_OBJECT(item), "plot", plot);
4672 	gtk_widget_show(item);
4673 	gtk_menu_shell_append(GTK_MENU_SHELL(cpopup), item);
4674     }
4675 
4676     gtk_menu_item_set_submenu(GTK_MENU_ITEM(w), cpopup);
4677 }
4678 
4679 #define showing_all_labels(p) (p->spec != NULL && \
4680 			       (p->spec->flags & GPT_PRINT_MARKERS) &&	\
4681 			       p->spec->labeled == NULL)
4682 
4683 #define graph_model_ok(f) (f == PLOT_FIT_OLS || \
4684                            f == PLOT_FIT_QUADRATIC || \
4685                            f == PLOT_FIT_INVERSE)
4686 
4687 #define plot_not_zoomable(p) ((p->status & PLOT_DONT_ZOOM) || \
4688 			      (p->spec != NULL && \
4689 			       p->spec->code == PLOT_ROOTS))
4690 
build_plot_menu(png_plot * plot)4691 static void build_plot_menu (png_plot *plot)
4692 {
4693     GtkWidget *item;
4694     const char *regular_items[] = {
4695 	N_("Add another curve..."),
4696 #ifdef G_OS_WIN32
4697 	N_("Save as Windows metafile (EMF)..."),
4698 #endif
4699 	N_("Save as PNG..."),
4700         N_("Save as postscript (EPS)..."),
4701 	N_("Save as PDF..."),
4702 #ifndef G_OS_WIN32
4703 	N_("Save as Windows metafile (EMF)..."),
4704 #endif
4705 	N_("Copy to clipboard"),
4706 	N_("Save to session as icon"),
4707 	N_("Freeze data labels"),
4708 	N_("All data labels"),
4709 	N_("Clear data labels"),
4710 	N_("Zoom..."),
4711 #ifdef G_OS_WIN32
4712 	N_("Print"),
4713 #endif
4714 	N_("Display PDF"),
4715 	N_("OLS estimates"),
4716 	N_("Numerical values"),
4717 	N_("Numerical summary"),
4718 	N_("Edit"),
4719 	N_("Font"),
4720 	N_("Help"),
4721         N_("Close"),
4722 	N_("Delete plot"),
4723 	N_("Close collection"),
4724 	N_("Extract plot"),
4725         NULL
4726     };
4727     const char *zoomed_items[] = {
4728 	N_("Restore full view"),
4729 	N_("Replace full view"),
4730 	N_("Close"),
4731 	NULL
4732     };
4733     const char **plot_items;
4734     int i, in_coll;
4735 
4736     plot->popup = gtk_menu_new();
4737 
4738     if (plot_is_zoomed(plot)) {
4739 	plot_items = zoomed_items;
4740     } else {
4741 	plot_items = regular_items;
4742     }
4743 
4744     /* geoplot FIXME: for several menu items below: either
4745        support them for geoplot or don't show them
4746     */
4747 
4748     in_coll = in_collection(plot);
4749 
4750     i = 0;
4751     while (plot_items[i]) {
4752 	int colorpop = 0;
4753 
4754 	if (plot->spec->code != PLOT_PROB_DIST &&
4755 	    !strcmp(plot_items[i], "Add another curve...")) {
4756 	    i++;
4757 	    continue;
4758 	}
4759 	if (plot_not_zoomable(plot) &&
4760 	    !strcmp(plot_items[i], "Zoom...")) {
4761 	    i++;
4762 	    continue;
4763 	}
4764 	if (!(plot_is_range_mean(plot) || plot_is_hurst(plot)) &&
4765 	    !strcmp(plot_items[i], "Help")) {
4766 	    i++;
4767 	    continue;
4768 	}
4769 	if (plot_is_saved(plot) &&
4770 	    !strcmp(plot_items[i], "Save to session as icon")) {
4771 	    i++;
4772 	    continue;
4773 	}
4774 	if ((plot_has_controller(plot) || plot_not_editable(plot)) &&
4775 	    !strcmp(plot_items[i], "Edit")) {
4776 	    i++;
4777 	    continue;
4778 	}
4779 	if ((plot_has_controller(plot) || plot_is_editable(plot)) &&
4780 	    !strcmp(plot_items[i], "Font")) {
4781 	    i++;
4782 	    continue;
4783 	}
4784 	if (!plot_labels_shown(plot) &&
4785 	    (!strcmp(plot_items[i], "Freeze data labels") ||
4786 	     !strcmp(plot_items[i], "Clear data labels"))) {
4787 	    /* no labels displayed, so these items are not relevant */
4788 	    i++;
4789 	    continue;
4790 	}
4791 	if (labels_frozen(plot) &&
4792 	    !strcmp(plot_items[i], "Freeze data labels")) {
4793 	    /* labels are frozen so this item inapplicable */
4794 	    i++;
4795 	    continue;
4796 	}
4797 	if ((cant_edit(plot->spec->code) ||
4798 	     cant_do_labels(plot) ||
4799 	     showing_all_labels(plot)) &&
4800 	    !strcmp(plot_items[i], "All data labels")) {
4801 	    i++;
4802 	    continue;
4803 	}
4804 	if ((!plot_has_regression_list(plot) ||
4805 	     !graph_model_ok(plot->spec->fit)) &&
4806 	    !strcmp(plot_items[i], "OLS estimates")) {
4807 	    i++;
4808 	    continue;
4809 	}
4810 	if (!plot_is_roots(plot) &&
4811 	    !strcmp(plot_items[i], "Numerical values")) {
4812 	    i++;
4813 	    continue;
4814 	}
4815 	if (plot->spec->code != PLOT_BOXPLOTS &&
4816 	    !strcmp(plot_items[i], "Numerical summary")) {
4817 	    i++;
4818 	    continue;
4819 	}
4820 	if (in_coll) {
4821 	    if (!strcmp(plot_items[i], "Close") ||
4822 		!strncmp(plot_items[i], "Save to", 7)) {
4823 		i++;
4824 		continue;
4825 	    }
4826 	} else if (!strncmp(plot_items[i], "Close ", 6) ||
4827 		   !strncmp(plot_items[i], "Delete ", 7) ||
4828 		   !strncmp(plot_items[i], "Extract", 7)) {
4829 	    i++;
4830 	    continue;
4831 	}
4832 
4833 	item = gtk_menu_item_new_with_label(_(plot_items[i]));
4834 
4835 #ifdef G_OS_WIN32
4836 	if (!strcmp(plot_items[i], "Copy to clipboard") ||
4837 	    !strcmp(plot_items[i], "Save as Windows metafile (EMF)...") ||
4838 	    !strcmp(plot_items[i], "Print")) {
4839 	    colorpop = 1;
4840 	}
4841 #else
4842 	if (!strcmp(plot_items[i], "Copy to clipboard") ||
4843 	    !strcmp(plot_items[i], "Save as Windows metafile (EMF)...")) {
4844 	    colorpop = 1;
4845 	}
4846 #endif
4847 	if (colorpop) {
4848 	    /* special: items with color sub-menu */
4849 	    attach_color_popup(item, plot);
4850 	} else {
4851 	    /* all other menu items */
4852 	    g_signal_connect(G_OBJECT(item), "activate",
4853 			     G_CALLBACK(plot_popup_activated),
4854 			     plot);
4855 	}
4856 
4857         gtk_widget_show(item);
4858         gtk_menu_shell_append(GTK_MENU_SHELL(plot->popup), item);
4859 
4860         i++;
4861     }
4862 
4863     g_signal_connect(G_OBJECT(plot->popup), "destroy",
4864 		     G_CALLBACK(gtk_widget_destroyed),
4865 		     &plot->popup);
4866 }
4867 
redisplay_edited_plot(png_plot * plot)4868 int redisplay_edited_plot (png_plot *plot)
4869 {
4870     gchar *plotcmd;
4871     FILE *fp;
4872     int err = 0;
4873 
4874 #if GPDEBUG
4875     fprintf(stderr, "redisplay_edited_plot: plot = %p\n", (void *) plot);
4876 #endif
4877 
4878     /* get the actual target of the edit */
4879     plot = plot_get_current(plot);
4880 
4881     /* open file in which to dump plot specification */
4882     gnuplot_png_init(plot, &fp);
4883     if (fp == NULL) {
4884 	return 1;
4885     }
4886 
4887     /* dump the edited plot details to file */
4888     set_png_output(plot->spec);
4889     plotspec_print(plot->spec, fp);
4890     fclose(fp);
4891 
4892     /* get gnuplot to create a new PNG graph */
4893     plotcmd = g_strdup_printf("\"%s\" \"%s\"",
4894 			      gretl_gnuplot_path(),
4895 			      plot->spec->fname);
4896     err = gretl_spawn(plotcmd);
4897     g_free(plotcmd);
4898 
4899     if (err) {
4900 	gui_errmsg(err);
4901 	return err;
4902     }
4903 
4904     /* reset format flags */
4905     set_plot_format_flags(plot);
4906 
4907     /* grab (possibly modified) data ranges */
4908     get_plot_ranges(plot, plot->spec->code);
4909 
4910     /* put the newly created PNG onto the plot canvas */
4911     return render_png(plot, PNG_REDISPLAY);
4912 }
4913 
recover_plot_header(char * line,FILE * fp)4914 static gchar *recover_plot_header (char *line, FILE *fp)
4915 {
4916     gchar *setterm = NULL;
4917 
4918     while (fgets(line, MAXLEN-1, fp)) {
4919 	if (commented_term_line(line) && setterm == NULL) {
4920 	    gchar *dpath = gretl_make_dotpath("gretltmp.png");
4921 	    GString *gs = g_string_new(line + 2);
4922 
4923 	    gs = g_string_append(gs, "set encoding utf8\n");
4924 	    g_string_append_printf(gs, "set output \"%s\"\n", dpath);
4925 	    g_free(dpath);
4926 	    setterm = g_string_free(gs, FALSE);
4927 	} else if (!strncmp(line, "plot", 4)) {
4928 	    break;
4929 	}
4930     }
4931 
4932     return setterm;
4933 }
4934 
4935 /* preparation for redisplaying graph: here we handle the case where
4936    we're switching to a zoomed view (by use of a temporary gnuplot
4937    source file); then we get gnuplot to create a new PNG.
4938  */
4939 
repaint_png(png_plot * plot,int view)4940 static int repaint_png (png_plot *plot, int view)
4941 {
4942     gchar *altname = NULL;
4943     gchar *plotcmd = NULL;
4944     int do_zoom = view == PNG_ZOOM;
4945     int err = 0;
4946 
4947     if (do_zoom || (plot->status & PLOT_TERM_HIDDEN)) {
4948 	/* we'll have to rewrite the plot file */
4949 	gchar *setterm = NULL;
4950 	char line[MAXLEN];
4951 	FILE *fpin, *fpout;
4952 	int gotterm = 0;
4953 
4954 	fpin = gretl_fopen(plot->spec->fname, "r");
4955 	if (fpin == NULL) {
4956 	    return E_FOPEN;
4957 	}
4958 	if (plot->status & PLOT_TERM_HIDDEN) {
4959 	    setterm = recover_plot_header(line, fpin);
4960 	    rewind(fpin);
4961 	}
4962 
4963 	altname = gretl_make_dotpath("altplot.gp");
4964 	fpout = gretl_fopen(altname, "w");
4965 	if (fpout == NULL) {
4966 	    fclose(fpin);
4967 	    g_free(altname);
4968 	    return E_FOPEN;
4969 	}
4970 
4971 	if (do_zoom) {
4972 	    /* write revised range into auxiliary gnuplot source file */
4973 	    gretl_push_c_numeric_locale();
4974 	    fprintf(fpout, "set xrange [%g:%g]\n", plot->zoom_xmin,
4975 		    plot->zoom_xmax);
4976 	    fprintf(fpout, "set yrange [%g:%g]\n", plot->zoom_ymin,
4977 		    plot->zoom_ymax);
4978 	    gretl_pop_c_numeric_locale();
4979 	}
4980 
4981 	while (fgets(line, MAXLEN-1, fpin)) {
4982 	    if (setterm != NULL && !gotterm && commented_term_line(line)) {
4983 		fputs(setterm, fpout);
4984 		gotterm = 1;
4985 	    } else if (!do_zoom) {
4986 		fputs(line, fpout);
4987 	    } else if (strncmp(line, "set xrange", 10) &&
4988 		       strncmp(line, "set yrange", 10)) {
4989 		fputs(line, fpout);
4990 	    }
4991 	}
4992 
4993 	fclose(fpin);
4994 	fclose(fpout);
4995 
4996 	plotcmd = g_strdup_printf("\"%s\" \"%s\"",
4997 				  gretl_gnuplot_path(),
4998 				  altname);
4999     } else {
5000 	plotcmd = g_strdup_printf("\"%s\" \"%s\"",
5001 				  gretl_gnuplot_path(),
5002 				  plot->spec->fname);
5003     }
5004 
5005     err = gretl_spawn(plotcmd);
5006     g_free(plotcmd);
5007 
5008     if (altname != NULL) {
5009 	gretl_remove(altname);
5010 	g_free(altname);
5011     }
5012 
5013     if (err) {
5014 	gui_errmsg(err);
5015 	return err;
5016     }
5017 
5018     return render_png(plot, view);
5019 }
5020 
5021 /* with a zoomed version of the current plot in place,
5022    replace the full version wiuth the zoom
5023 */
5024 
zoom_replaces_plot(png_plot * plot)5025 static int zoom_replaces_plot (png_plot *plot)
5026 {
5027     FILE *fpin, *fpout;
5028     char temp[MAXLEN], line[MAXLEN];
5029     int err = 0;
5030 
5031     fpin = gretl_fopen(plot->spec->fname, "r");
5032     if (fpin == NULL) {
5033 	return 1;
5034     }
5035 
5036     sprintf(temp, "%szoomtmp", gretl_dotdir());
5037     fpout = gretl_tempfile_open(temp);
5038     if (fpout == NULL) {
5039 	fclose(fpin);
5040 	return 1;
5041     }
5042 
5043     /* write zoomed range into temporary file */
5044 
5045     gretl_push_c_numeric_locale();
5046     fprintf(fpout, "set xrange [%g:%g]\n", plot->zoom_xmin,
5047 	    plot->zoom_xmax);
5048     fprintf(fpout, "set yrange [%g:%g]\n", plot->zoom_ymin,
5049 	    plot->zoom_ymax);
5050     gretl_pop_c_numeric_locale();
5051 
5052     while (fgets(line, MAXLEN-1, fpin)) {
5053 	if (strncmp(line, "set xrange", 10) &&
5054 	    strncmp(line, "set yrange", 10)) {
5055 	    fputs(line, fpout);
5056 	}
5057     }
5058 
5059     fclose(fpout);
5060     fclose(fpin);
5061 
5062     /* and copy over original graph source file */
5063 
5064     err = gretl_copy_file(temp, plot->spec->fname);
5065     if (err) {
5066 	gui_errmsg(err);
5067     } else {
5068 	plot->xmin = plot->zoom_xmin;
5069 	plot->xmax = plot->zoom_xmax;
5070 	plot->ymin = plot->zoom_ymin;
5071 	plot->ymax = plot->zoom_ymax;
5072 	plot->zoom_xmin = plot->zoom_xmax = 0.0;
5073 	plot->zoom_ymin = plot->zoom_ymax = 0.0;
5074 	plot->status ^= PLOT_ZOOMED;
5075     }
5076 
5077     gretl_remove(temp);
5078 
5079     return err;
5080 }
5081 
plot_button_release(GtkWidget * widget,GdkEventButton * event,png_plot * plot)5082 static gint plot_button_release (GtkWidget *widget, GdkEventButton *event,
5083 				 png_plot *plot)
5084 {
5085     plot = plot_get_current(plot);
5086 
5087     if (plot_is_zooming(plot)) {
5088 	double z;
5089 
5090 	if (!get_data_xy(plot, event->x, event->y,
5091 			 &plot->zoom_xmax, &plot->zoom_ymax)) {
5092 	    return TRUE;
5093 	}
5094 
5095 	/* flip the selected rectangle if required */
5096 	if (plot->zoom_xmin > plot->zoom_xmax) {
5097 	    z = plot->zoom_xmax;
5098 	    plot->zoom_xmax = plot->zoom_xmin;
5099 	    plot->zoom_xmin = z;
5100 	}
5101 
5102 	if (plot->zoom_ymin > plot->zoom_ymax) {
5103 	    z = plot->zoom_ymax;
5104 	    plot->zoom_ymax = plot->zoom_ymin;
5105 	    plot->zoom_ymin = z;
5106 	}
5107 
5108 	if (plot->zoom_xmin != plot->zoom_xmax &&
5109 	    plot->zoom_ymin != plot->zoom_ymax) {
5110 	    repaint_png(plot, PNG_ZOOM);
5111 	}
5112 
5113 	plot->status ^= PLOT_ZOOMING;
5114 	gdk_window_set_cursor(plot->window, NULL);
5115 	gtk_statusbar_pop(GTK_STATUSBAR(plot->statusbar), plot->cid);
5116     }
5117 
5118     return TRUE;
5119 }
5120 
plot_button_press(GtkWidget * widget,GdkEventButton * event,png_plot * plot)5121 static gint plot_button_press (GtkWidget *widget, GdkEventButton *event,
5122 			       png_plot *plot)
5123 {
5124     plot = plot_get_current(plot);
5125 
5126     if (plot_is_zooming(plot)) {
5127 	if (get_data_xy(plot, event->x, event->y,
5128 			&plot->zoom_xmin, &plot->zoom_ymin)) {
5129 	    plot->screen_x0 = event->x;
5130 	    plot->screen_y0 = event->y;
5131 	}
5132 	return TRUE;
5133     }
5134 
5135     if (plot_doing_position(plot)) {
5136 	if (plot->pos_entry != NULL) {
5137 	    double dx, dy;
5138 
5139 	    if (get_data_xy(plot, event->x, event->y, &dx, &dy)) {
5140 		gchar *posstr;
5141 
5142 		posstr = g_strdup_printf("%g %g", dx, dy);
5143 		gtk_entry_set_text(GTK_ENTRY(plot->pos_entry), posstr);
5144 		g_free(posstr);
5145 	    }
5146 	}
5147 	terminate_plot_positioning(plot);
5148 	return TRUE;
5149     }
5150 
5151     if (plot->popup != NULL) {
5152 	gtk_widget_destroy(plot->popup);
5153 	plot->popup = NULL;
5154     }
5155 
5156     if (right_click(event)) {
5157 	build_plot_menu(plot);
5158 	gtk_menu_popup(GTK_MENU(plot->popup), NULL, NULL, NULL, NULL,
5159 		       event->button, event->time);
5160     }
5161 
5162     return TRUE;
5163 }
5164 
5165 static gboolean
plot_key_handler(GtkWidget * w,GdkEventKey * event,png_plot * plot)5166 plot_key_handler (GtkWidget *w, GdkEventKey *event, png_plot *plot)
5167 {
5168     int Ctrl = (event->state & GDK_CONTROL_MASK);
5169     guint k = event->keyval;
5170 
5171 #ifdef OS_OSX
5172     if (!Ctrl && cmd_key(event)) {
5173 	/* treat Command as Ctrl */
5174 	Ctrl = 1;
5175     }
5176 #endif
5177 
5178     if (plot_is_editable(plot) &&
5179 	(k == GDK_plus || k == GDK_greater ||
5180 	 k == GDK_minus || k == GDK_less ||
5181 	 k == GDK_equal || k == GDK_0)) {
5182 	int rk = 1;
5183 
5184 	if (k == GDK_minus || k == GDK_less) {
5185 	    rk = -1;
5186 	} else if (k == GDK_0) {
5187 	    rk = 0;
5188 	}
5189 	plot_do_rescale(plot, rk);
5190 	return TRUE;
5191     }
5192 
5193     if (Ctrl && k == GDK_c) {
5194 #ifdef G_OS_WIN32
5195 	win32_process_graph(plot, WIN32_TO_CLIPBOARD);
5196 #else
5197 	set_plot_for_copy(plot);
5198 #endif
5199 	return TRUE;
5200     }
5201 
5202     if (in_collection(plot)) {
5203 	return TRUE;
5204     }
5205 
5206     switch (k) {
5207     case GDK_q:
5208     case GDK_Q:
5209 #ifdef OS_OSX
5210     case GDK_w:
5211     case GDK_W:
5212 #endif
5213 	gtk_widget_destroy(plot->shell);
5214 	break;
5215     case GDK_s:
5216     case GDK_S:
5217 	add_to_session_callback(plot);
5218 	break;
5219     case GDK_z:
5220     case GDK_Z:
5221 	if (!plot_not_zoomable(plot)) {
5222 	    prepare_for_zoom(plot);
5223 	}
5224 	break;
5225     default:
5226 	break;
5227     }
5228 
5229     return TRUE;
5230 }
5231 
5232 #if GTK_MAJOR_VERSION >= 3
5233 
5234 static
plot_draw(GtkWidget * canvas,cairo_t * cr,gpointer data)5235 void plot_draw (GtkWidget *canvas, cairo_t *cr, gpointer data)
5236 {
5237     png_plot *plot = plot_get_current(data);
5238 
5239     cairo_set_source_surface(cr, plot->cs, 0, 0);
5240     cairo_paint(cr);
5241 }
5242 
5243 #else /* transitional use of cairo */
5244 
5245 static
plot_expose(GtkWidget * canvas,GdkEventExpose * event,gpointer data)5246 void plot_expose (GtkWidget *canvas, GdkEventExpose *event,
5247 		  gpointer data)
5248 {
5249     png_plot *plot = data;
5250 
5251     plot->cr = gdk_cairo_create(plot->window);
5252     gdk_cairo_set_source_pixmap(plot->cr, plot->pixmap, 0, 0);
5253     gdk_cairo_rectangle(plot->cr, &event->area);
5254     cairo_fill(plot->cr);
5255     cairo_destroy(plot->cr);
5256 }
5257 
5258 #endif
5259 
record_coordinate_info(png_plot * plot,png_bounds * b)5260 static void record_coordinate_info (png_plot *plot, png_bounds *b)
5261 {
5262     if (b->height > 0) {
5263 	plot->pixel_height = b->height;
5264     }
5265     if (b->width > 0) {
5266 	plot->pixel_width = b->width;
5267     }
5268     plot->status |= PLOT_PNG_COORDS;
5269     plot->pixel_xmin = b->xleft;
5270     plot->pixel_xmax = b->xright;
5271     plot->pixel_ymin = plot->pixel_height - b->ytop;
5272     plot->pixel_ymax = plot->pixel_height - b->ybot;
5273     plot->xmin = b->xmin;
5274     plot->xmax = b->xmax;
5275     plot->ymin = b->ymin;
5276     plot->ymax = b->ymax;
5277 
5278 #if POINTS_DEBUG
5279     fprintf(stderr, "get_png_bounds_info():\n"
5280 	    " xmin=%d xmax=%d ymin=%d ymax=%d\n",
5281 	    plot->pixel_xmin, plot->pixel_xmax,
5282 	    plot->pixel_ymin, plot->pixel_ymax);
5283     fprintf(stderr, "using px_height %d, px_width %d\n",
5284 	    plot->pixel_height, plot->pixel_width);
5285 #endif
5286 }
5287 
5288 #if GTK_MAJOR_VERSION == 2
5289 
pixmap_sync(png_plot * plot)5290 static void pixmap_sync (png_plot *plot)
5291 {
5292     GList *L = plot->mp->list;
5293     png_plot *sibling;
5294 
5295     while (L != NULL) {
5296 	sibling = L->data;
5297 	if (sibling != plot) {
5298 	    sibling->pixmap = plot->pixmap;
5299 	}
5300 	L = L->next;
5301     }
5302 }
5303 
5304 #endif
5305 
resize_png_plot(png_plot * plot,int width,int height,int follower)5306 static int resize_png_plot (png_plot *plot, int width, int height,
5307 			    int follower)
5308 {
5309     png_bounds b;
5310 
5311     plot->pixel_width = width;
5312     plot->pixel_height = height;
5313 
5314     if (!follower) {
5315 	gtk_widget_set_size_request(GTK_WIDGET(plot->canvas),
5316 				    plot->pixel_width, plot->pixel_height);
5317 #if GTK_MAJOR_VERSION == 2
5318 	g_object_unref(plot->pixmap);
5319 	plot->pixmap = gdk_pixmap_new(plot->window,
5320 				      plot->pixel_width,
5321 				      plot->pixel_height,
5322 				      -1);
5323 	if (in_collection(plot)) {
5324 	    pixmap_sync(plot);
5325 	}
5326 #endif
5327     }
5328 
5329     if (plot->status & (PLOT_DONT_ZOOM | PLOT_DONT_MOUSE)) {
5330 	return 0;
5331     }
5332 
5333     b.width = width;
5334     b.height = height;
5335 
5336     /* try revising the gnuplot bounds info? */
5337     if (plot_has_png_coords(plot) &&
5338 	get_png_bounds_info(&b) == GRETL_PNG_OK) {
5339 	record_coordinate_info(plot, &b);
5340     } else {
5341 	plot->status |= (PLOT_DONT_ZOOM | PLOT_DONT_MOUSE);
5342     }
5343 
5344     return 0;
5345 }
5346 
pixbuf_from_file(png_plot * plot)5347 static GdkPixbuf *pixbuf_from_file (png_plot *plot)
5348 {
5349     char fname[MAXLEN];
5350     GError *gerr = NULL;
5351     GdkPixbuf *pbuf;
5352 
5353     gretl_build_path(fname, gretl_dotdir(), "gretltmp.png", NULL);
5354     pbuf = gdk_pixbuf_new_from_file(fname, &gerr);
5355 
5356     if (gerr != NULL) {
5357 	errbox(gerr->message);
5358 	g_error_free(gerr);
5359     } else if (pbuf == NULL) {
5360 	file_read_errbox(fname);
5361 	gretl_remove(fname);
5362     } else if (gdk_pixbuf_get_width(pbuf) == 0 ||
5363 	       gdk_pixbuf_get_height(pbuf) == 0) {
5364 	errbox(_("Malformed PNG file for graph"));
5365 	g_object_unref(pbuf);
5366 	pbuf = NULL;
5367     }
5368 
5369     return pbuf;
5370 }
5371 
5372 /* The last step in displaying a graph (or redisplaying after some
5373    change has been made): grab the gnuplot-generated PNG file, make a
5374    pixbuf out of it, and draw the pixbuf onto the canvas of the plot
5375    window.
5376 */
5377 
render_png(png_plot * plot,viewcode view)5378 static int render_png (png_plot *plot, viewcode view)
5379 {
5380     char pngname[MAXLEN];
5381     gint width, height;
5382     GdkPixbuf *pbuf;
5383 
5384     if (view == PNG_REDISPLAY || view == PNG_ZOOM || view == PNG_UNZOOM) {
5385 	/* we need to read a revised PNG file */
5386 	plot_invalidate_pixbuf(plot);
5387     }
5388 
5389 #if COLLDEBUG
5390     fprintf(stderr, "\nrender_png: plot %p, pixbuf %p, view = %s\n",
5391 	    (void *) plot, (void *) plot->pbuf, viewstr(view));
5392 #endif
5393 
5394     if (plot->pbuf != NULL) {
5395 	pbuf = plot->pbuf;
5396     } else {
5397 	pbuf = pixbuf_from_file(plot);
5398 	if (pbuf == NULL) {
5399 	    return 1;
5400 	}
5401     }
5402 
5403     width = gdk_pixbuf_get_width(pbuf);
5404     height = gdk_pixbuf_get_height(pbuf);
5405 
5406     if (width != plot->pixel_width || height != plot->pixel_height) {
5407 	resize_png_plot(plot, width, height, 0);
5408     }
5409 
5410     /* scrap any old record of which points are labeled */
5411     if (plot->spec->labeled != NULL) {
5412 	free(plot->spec->labeled);
5413 	plot->spec->labeled = NULL;
5414 	if (!(plot->spec->flags & GPT_PRINT_MARKERS)) {
5415 	    /* any markers will have disappeared on reprinting */
5416 	    plot->format &= ~PLOT_MARKERS_UP;
5417 	}
5418     }
5419 
5420 #if GTK_MAJOR_VERSION >= 3
5421     copy_pixbuf_to_surface(plot, pbuf);
5422 #else
5423     plot->cr = gdk_cairo_create(plot->pixmap);
5424     gdk_cairo_set_source_pixbuf(plot->cr, pbuf, 0, 0);
5425     cairo_paint(plot->cr);
5426     cairo_destroy(plot->cr);
5427     if (plot->savebuf != NULL) {
5428 	g_object_unref(plot->savebuf);
5429 	plot->savebuf = NULL;
5430     }
5431 #endif
5432 
5433     /* FIXME case of singleton plot? */
5434     plot->pbuf = pbuf;
5435 
5436     if (view != PNG_REPLACE) {
5437 	gretl_build_path(pngname, gretl_dotdir(), "gretltmp.png", NULL);
5438 	gretl_remove(pngname);
5439     }
5440 
5441     if (view != PNG_START) {
5442 	/* we're changing the view, so refresh the whole canvas */
5443 	redraw_plot_rectangle(plot, NULL);
5444 	if (view == PNG_ZOOM) {
5445 	    plot->status |= PLOT_ZOOMED;
5446 	} else if (view == PNG_UNZOOM) {
5447 	    plot->status ^= PLOT_ZOOMED;
5448 	}
5449     }
5450 
5451 #ifdef G_OS_WIN32
5452     /* somehow the plot can end up underneath */
5453     gtk_window_present(GTK_WINDOW(plot->shell));
5454 #endif
5455 
5456     return 0;
5457 }
5458 
5459 #if GTK_MAJOR_VERSION == 2
5460 
plot_nullify_surface(png_plot * plot)5461 static void plot_nullify_surface (png_plot *plot)
5462 {
5463     plot->pixmap = NULL;
5464     plot->savebuf = NULL;
5465 }
5466 
5467 #endif
5468 
plot_destroy_surface(png_plot * plot)5469 static void plot_destroy_surface (png_plot *plot)
5470 {
5471 #if GTK_MAJOR_VERSION >= 3
5472     if (plot->cs != NULL) {
5473 	cairo_surface_destroy(plot->cs);
5474     }
5475 #else
5476     if (plot->pixmap != NULL) {
5477 	g_object_unref(plot->pixmap);
5478     }
5479     if (plot->savebuf != NULL) {
5480 	g_object_unref(plot->savebuf);
5481     }
5482 #endif
5483 }
5484 
destroy_png_plot(GtkWidget * w,png_plot * plot)5485 static void destroy_png_plot (GtkWidget *w, png_plot *plot)
5486 {
5487     if (in_collection(plot)) {
5488 	GList *L = g_list_first(plot->mp->list);
5489 	png_plot *sibling;
5490 
5491 	while (L != NULL) {
5492 	    sibling = L->data;
5493 	    if (sibling != plot) {
5494 #if GTK_MAJOR_VERSION == 2
5495 		plot_nullify_surface(sibling);
5496 #endif
5497 		sibling->shell = NULL;
5498 		sibling->mp = NULL;
5499 		destroy_png_plot(NULL, sibling);
5500 	    }
5501 	    L = L->next;
5502 	}
5503     }
5504 
5505     if (!plot_is_saved(plot) && plot->spec != NULL) {
5506 	/* delete temporary plot source file? */
5507 	if (strstr(plot->spec->fname, gretl_dotdir())) {
5508 	    gretl_remove(plot->spec->fname);
5509 	}
5510     }
5511 
5512 #if GPDEBUG
5513     fprintf(stderr, "destroy_png_plot: plot = %p, spec = %p\n",
5514 	    (void *) plot, (void *) plot->spec);
5515 #endif
5516 
5517 #ifndef G_OS_WIN32
5518     if (copyplot == plot) {
5519 	copyplot = NULL;
5520     }
5521 #endif
5522 
5523     if (plot->spec != NULL) {
5524 	plotspec_destroy(plot->spec);
5525     }
5526 
5527     plot_destroy_surface(plot);
5528 
5529     if (plot->pbuf != NULL) {
5530 	g_object_unref(plot->pbuf);
5531     }
5532 
5533     if (plot->shell != NULL) {
5534 	g_object_unref(plot->shell);
5535     }
5536 
5537     if (plot == plot_collection) {
5538 	plot_collection = NULL;
5539     }
5540 
5541     if (plot->mp != NULL) {
5542 	g_list_free(plot->mp->list);
5543 	free(plot->mp);
5544     }
5545 
5546     free(plot);
5547 }
5548 
5549 /* Do a partial parse of the gnuplot source file: enough to determine
5550    the data ranges so we can read back the mouse pointer coordinates
5551    when the user moves the pointer over the graph.
5552 */
5553 
get_plot_ranges(png_plot * plot,PlotType ptype)5554 static int get_plot_ranges (png_plot *plot, PlotType ptype)
5555 {
5556     FILE *fp;
5557     char line[MAXLEN];
5558     int got_x = 0;
5559     int got_y = 0;
5560     int annual = 0;
5561     png_bounds b;
5562     int err = 0;
5563 
5564 #if GPDEBUG
5565     fprintf(stderr, "get_plot_ranges: plot=%p, plot->spec=%p\n",
5566 	    (void *) plot, (void *) plot->spec);
5567 #endif
5568 
5569     plot->xmin = plot->xmax = 0.0;
5570     plot->ymin = plot->ymax = 0.0;
5571     plot->xint = plot->yint = 0;
5572 
5573     if (no_readback(plot->spec->code)) {
5574 	plot->status |= (PLOT_DONT_ZOOM | PLOT_DONT_MOUSE);
5575 	return 1;
5576     }
5577 
5578     fp = gretl_fopen(plot->spec->fname, "r");
5579     if (fp == NULL) {
5580 	plot->status |= (PLOT_DONT_ZOOM | PLOT_DONT_MOUSE);
5581 	return 1;
5582     }
5583 
5584     gretl_push_c_numeric_locale();
5585 
5586     while (fgets(line, MAXLEN-1, fp) && strncmp(line, "plot ", 5)) {
5587 	if (sscanf(line, "set xrange [%lf:%lf]",
5588 		   &plot->xmin, &plot->xmax) == 2) {
5589 	    got_x = 1;
5590 	} else if (!strncmp(line, "# timeseries 1", 13)) {
5591 	    annual = 1;
5592 	}
5593     }
5594 
5595     gretl_pop_c_numeric_locale();
5596 
5597     fclose(fp);
5598 
5599     /* now try getting accurate coordinate info from
5600        auxiliary file (or maybe PNG file)
5601     */
5602     if (get_png_bounds_info(&b) == GRETL_PNG_OK) {
5603 	record_coordinate_info(plot, &b);
5604 	got_x = got_y = 1;
5605     }
5606 
5607     /* If got_x = 0 at this point, we didn't get an x-range out of
5608        gnuplot, so we might as well give up.
5609     */
5610     if (!got_x) {
5611 	plot->status |= (PLOT_DONT_ZOOM | PLOT_DONT_MOUSE);
5612 	return 1;
5613     }
5614 
5615     if (plot_has_png_coords(plot)) {
5616 	plot->status |= PLOT_HAS_XRANGE;
5617 	plot->status |= PLOT_HAS_YRANGE;
5618 	if (ptype != PLOT_ROOTS && ptype != PLOT_QQ) {
5619 	    plot->status |= PLOT_CURSOR_LABEL;
5620 	}
5621 	if (annual) {
5622 	    /* years on x-axis: show as integer */
5623 	    plot->xint = 1;
5624 	} else if ((plot->xmax - plot->xmin) /
5625 	    (plot->pixel_xmax - plot->pixel_xmin) >= 1.0) {
5626 	    /* show x-axis variable as integer */
5627 	    plot->xint = 1;
5628 	}
5629 	if ((plot->ymax - plot->ymin) /
5630 	    (plot->pixel_ymax - plot->pixel_ymin) >= 1.0) {
5631 	    /* show y-axis variable as integer */
5632 	    plot->yint = 1;
5633 	}
5634     } else {
5635 	plot->status |= (PLOT_DONT_ZOOM | PLOT_DONT_MOUSE);
5636 #if POINTS_DEBUG
5637 	fputs("get_plot_ranges: setting PLOT_DONT_ZOOM, PLOT_DONT_MOUSE\n",
5638 	      stderr);
5639 #endif
5640     }
5641 
5642     return err;
5643 }
5644 
png_plot_new(void)5645 static png_plot *png_plot_new (void)
5646 {
5647     png_plot *plot = mymalloc(sizeof *plot);
5648 
5649     if (plot == NULL) {
5650 	return NULL;
5651     }
5652 
5653     plot->shell = NULL;
5654     plot->mp = NULL;
5655     plot->canvas = NULL;
5656     plot->popup = NULL;
5657     plot->statusbar = NULL;
5658     plot->cursor_label = NULL;
5659     plot->pbuf = NULL;
5660     plot->cr = NULL;
5661 #if GTK_MAJOR_VERSION >= 3
5662     plot->cs = NULL;
5663 #else
5664     plot->pixmap = NULL;
5665     plot->savebuf = NULL;
5666 #endif
5667     plot->spec = NULL;
5668     plot->editor = NULL;
5669     plot->window = NULL;
5670     plot->toolbar = NULL;
5671     plot->up_icon = NULL;
5672     plot->down_icon = NULL;
5673 
5674     plot->pixel_width = GP_WIDTH;
5675     plot->pixel_height = GP_HEIGHT;
5676 
5677     plot->xmin = plot->xmax = 0.0;
5678     plot->ymin = plot->ymax = 0.0;
5679     plot->xint = plot->yint = 0;
5680 
5681     plot->zoom_xmin = plot->zoom_xmax = 0.0;
5682     plot->zoom_ymin = plot->zoom_ymax = 0.0;
5683     plot->screen_x0 = plot->screen_y0 = 0;
5684     plot->pd = 0;
5685     plot->err = 0;
5686     plot->cid = 0;
5687     plot->status = 0;
5688     plot->format = 0;
5689 
5690     return plot;
5691 }
5692 
plot_add_shell(png_plot * plot,const char * name)5693 static int plot_add_shell (png_plot *plot, const char *name)
5694 {
5695     GtkWidget *vbox;
5696     GtkWidget *canvas_hbox;
5697     GtkWidget *status_hbox;
5698     GtkWidget *status_area;
5699     gchar *title;
5700 
5701     plot->shell = gretl_gtk_window();
5702     g_object_ref(plot->shell);
5703     g_object_set_data(G_OBJECT(plot->shell), "plot", plot);
5704 #if 0 /* do we want this? */
5705     gtk_window_set_position(GTK_WINDOW(plot->shell), GTK_WIN_POS_MOUSE);
5706 #endif
5707 
5708     if (name != NULL) {
5709 	title = g_strdup_printf("gretl: %s", name);
5710     } else {
5711 	title = g_strdup(_("gretl: graph"));
5712     }
5713     gtk_window_set_title(GTK_WINDOW(plot->shell), title);
5714     gtk_window_set_resizable(GTK_WINDOW(plot->shell), FALSE);
5715     g_signal_connect(G_OBJECT(plot->shell), "destroy",
5716 		     G_CALLBACK(destroy_png_plot), plot);
5717     g_free(title);
5718 
5719     vbox = gtk_vbox_new(FALSE, 2);
5720     gtk_container_add(GTK_CONTAINER(plot->shell), vbox);
5721 
5722     /* box to hold canvas */
5723     canvas_hbox = gtk_hbox_new(FALSE, 1);
5724     gtk_box_pack_start(GTK_BOX(vbox), canvas_hbox, TRUE, TRUE, 0);
5725 
5726     /* eventbox and hbox for status area  */
5727     status_area = gtk_event_box_new();
5728     gtk_box_pack_start(GTK_BOX(vbox), status_area, FALSE, FALSE, 0);
5729     status_hbox = gtk_hbox_new(FALSE, 2);
5730     gtk_container_add(GTK_CONTAINER(status_area), status_hbox);
5731 
5732     /* Create drawing-area widget */
5733     plot->canvas = gtk_drawing_area_new();
5734     gtk_widget_set_size_request(GTK_WIDGET(plot->canvas),
5735 				plot->pixel_width, plot->pixel_height);
5736     gtk_widget_set_events (plot->canvas, GDK_EXPOSURE_MASK
5737                            | GDK_LEAVE_NOTIFY_MASK
5738                            | GDK_BUTTON_PRESS_MASK
5739                            | GDK_BUTTON_RELEASE_MASK
5740                            | GDK_POINTER_MOTION_MASK
5741                            | GDK_POINTER_MOTION_HINT_MASK);
5742     gtk_widget_set_can_focus(plot->canvas, TRUE);
5743     g_signal_connect(G_OBJECT(plot->canvas), "button-press-event",
5744 		     G_CALLBACK(plot_button_press), plot);
5745     g_signal_connect(G_OBJECT(plot->canvas), "button-release-event",
5746 		     G_CALLBACK(plot_button_release), plot);
5747     gtk_box_pack_start(GTK_BOX(canvas_hbox), plot->canvas, FALSE, FALSE, 0);
5748 
5749     if (plot_show_cursor_label(plot)) {
5750 	/* cursor label (graph position indicator) */
5751 	GtkWidget *frame = gtk_frame_new(NULL);
5752 
5753 	gtk_frame_set_shadow_type(GTK_FRAME(frame), GTK_SHADOW_IN);
5754 	plot->cursor_label = gtk_label_new(" ");
5755 	gtk_widget_set_size_request(plot->cursor_label, 160, -1);
5756 	gtk_container_add(GTK_CONTAINER(frame), plot->cursor_label);
5757 	gtk_box_pack_start(GTK_BOX(status_hbox), frame, FALSE, FALSE, 0);
5758     }
5759 
5760     /* the statusbar */
5761     plot->statusbar = gtk_statusbar_new();
5762     gtk_box_pack_start(GTK_BOX(status_hbox), plot->statusbar, TRUE, TRUE, 0);
5763     add_graph_toolbar(status_hbox, plot);
5764 #if GTK_MAJOR_VERSION < 3
5765     gtk_statusbar_set_has_resize_grip(GTK_STATUSBAR(plot->statusbar), FALSE);
5766 #endif
5767     plot->cid = gtk_statusbar_get_context_id(GTK_STATUSBAR(plot->statusbar),
5768 					     "plot_message");
5769 
5770     /* more on canvas and window */
5771     if (plot_has_xrange(plot)) {
5772 	g_signal_connect(G_OBJECT(plot->canvas), "motion-notify-event",
5773 			 G_CALLBACK(plot_motion_callback), plot);
5774     }
5775     gtk_widget_realize(plot->canvas);
5776     plot->window = gtk_widget_get_window(plot->canvas);
5777 #if GTK_MAJOR_VERSION < 3
5778     gdk_window_set_back_pixmap(plot->window, NULL, FALSE);
5779 #endif
5780 
5781     /* finish setup of plot->shell */
5782     g_object_set_data(G_OBJECT(plot->shell), "plot-filename",
5783 		      plot->spec->fname);
5784     window_list_add(plot->shell, GNUPLOT);
5785     g_signal_connect(G_OBJECT(plot->canvas), "key-press-event",
5786 		     G_CALLBACK(plot_key_handler), plot);
5787     gtk_widget_show_all(plot->shell);
5788 
5789 #if GTK_MAJOR_VERSION >= 3
5790     g_signal_connect(G_OBJECT(plot->canvas), "draw",
5791 		     G_CALLBACK(plot_draw), plot);
5792 #else
5793     plot->pixmap = gdk_pixmap_new(plot->window,
5794 				  plot->pixel_width,
5795 				  plot->pixel_height,
5796 				  -1);
5797     g_signal_connect(G_OBJECT(plot->canvas), "expose-event",
5798 		     G_CALLBACK(plot_expose), plot);
5799 #endif
5800 
5801     return 0;
5802 }
5803 
plot_handle_specials(png_plot * plot)5804 static void plot_handle_specials (png_plot *plot)
5805 {
5806     if (plot->spec->code == PLOT_PERIODOGRAM) {
5807 	/* the x2 axis gets broken, and also the x axis if it's
5808 	   in degrees or radians, on zooming
5809 	*/
5810 	plot->status |= PLOT_DONT_ZOOM;
5811     } else if (plot->spec->code == PLOT_ROOTS ||
5812 	plot->spec->code == PLOT_QQ) {
5813 	plot->pixel_width = plot->pixel_height = GP_SQ_SIZE;
5814     }
5815 
5816     if (plot->spec->flags & GPT_LETTERBOX) {
5817 	plot->pixel_width = GP_LB_WIDTH;
5818 	plot->pixel_height = GP_LB_HEIGHT;
5819     } else if (plot->spec->flags & GPT_XXL) {
5820 	plot->pixel_width = GP_XXL_WIDTH;
5821 	plot->pixel_height = GP_XXL_HEIGHT;
5822     } else if (plot->spec->flags & GPT_XL) {
5823 	plot->pixel_width = GP_XL_WIDTH;
5824 	plot->pixel_height = GP_XL_HEIGHT;
5825     } else if (plot->spec->flags & GPT_XW) {
5826 	plot->pixel_width = GP_XW_WIDTH;
5827 	plot->pixel_height = GP_HEIGHT;
5828     }
5829 
5830     if (plot->spec->scale != 1.0) {
5831 	plot_get_scaled_dimensions(&plot->pixel_width,
5832 				   &plot->pixel_height,
5833 				   plot->spec->scale);
5834     }
5835 }
5836 
5837 /* note: @fname is the name of the file containing the
5838    plot commands.
5839 */
5840 
gnuplot_show_png(const char * fname,const char * name,void * session_ptr,png_plot * coll)5841 static int gnuplot_show_png (const char *fname,
5842 			     const char *name,
5843 			     void *session_ptr,
5844 			     png_plot *coll)
5845 {
5846     png_plot *plot;
5847     int err = 0;
5848 
5849     if (*fname == '\0') {
5850 	return 0;
5851     }
5852 
5853     gretl_error_clear();
5854 
5855 #if GPDEBUG
5856     fprintf(stderr, "gnuplot_show_png:\n fname='%s', saved=%d\n",
5857 	    fname, (session_ptr != NULL));
5858 #endif
5859 
5860     plot = png_plot_new();
5861     if (plot == NULL) {
5862 	return E_ALLOC;
5863     }
5864 
5865     plot->spec = plotspec_new();
5866     if (plot->spec == NULL) {
5867 	free(plot);
5868 	return E_ALLOC;
5869     }
5870 
5871     strcpy(plot->spec->fname, fname);
5872 
5873     if (session_ptr != NULL) {
5874 	plot->status |= PLOT_SAVED;
5875     }
5876 
5877     /* make png plot struct accessible via spec */
5878     plot->spec->ptr = plot;
5879 
5880     /* Parse the gnuplot source file.  If we hit errors here,
5881        flag this, but it's not necessarily a show-stopper in
5882        terms of simply displaying the graph -- unless we get
5883        E_FOPEN.
5884     */
5885     plot->err = read_plotspec_from_file(plot);
5886 
5887     if (plot->err == E_FOPEN) {
5888 	plotspec_destroy(plot->spec);
5889 	free(plot);
5890 	return E_FOPEN;
5891     }
5892 
5893 #if GPDEBUG
5894     fprintf(stderr, "gnuplot_show_png: read_plotspec_from_file returned %d\n",
5895 	    plot->err);
5896 #endif
5897 
5898     if (plot->err || cant_edit(plot->spec->code)) {
5899 	plot->status |= (PLOT_DONT_EDIT | PLOT_DONT_ZOOM | PLOT_DONT_MOUSE);
5900     } else {
5901 	set_plot_format_flags(plot);
5902     }
5903 
5904     plot_handle_specials(plot);
5905 
5906     if (!plot->err) {
5907 	int range_err = get_plot_ranges(plot, plot->spec->code);
5908 
5909 #if GPDEBUG
5910 	fprintf(stderr, "range_err = %d\n", range_err);
5911 #endif
5912 	if (plot->spec->nbars > 0) {
5913 	    if (range_err) {
5914 		plot->spec->nbars = 0;
5915 	    } else {
5916 		plotspec_set_bars_limits(plot->spec,
5917 					 plot->xmin, plot->xmax,
5918 					 plot->ymin, plot->ymax);
5919 	    }
5920 	}
5921     }
5922 
5923     if (coll != NULL) {
5924 	/* add this plot to current collection? */
5925 	int attached = 0;
5926 
5927 	if (!err && plot_can_be_collected(coll, plot)) {
5928 	    err = plot_collection_add_plot(coll, plot);
5929 	    attached = (err == 0);
5930 	}
5931 	if (err || attached) {
5932 	    return err;
5933 	}
5934     }
5935 
5936     plot_add_shell(plot, name);
5937     err = render_png(plot, PNG_START);
5938     if (err) {
5939 	gtk_widget_destroy(plot->shell);
5940     } else {
5941 	g_object_set_data(G_OBJECT(plot->shell), "object", plot);
5942 	if (session_ptr != NULL) {
5943 	    g_object_set_data(G_OBJECT(plot->shell),
5944 			      "session-ptr", session_ptr);
5945 	} else if (coll == NULL && do_collect_plots()) {
5946 	    set_plot_collection(plot);
5947 	}
5948     }
5949 
5950     return err;
5951 }
5952 
gnuplot_show_map(gretl_bundle * mb)5953 int gnuplot_show_map (gretl_bundle *mb)
5954 {
5955     const char *fname = NULL;
5956     const char *mapname = NULL;
5957     const gretl_matrix *dims = NULL;
5958     png_plot *plot;
5959     png_bounds b;
5960     int err = 0;
5961 
5962     gretl_error_clear();
5963 
5964     fname = gretl_bundle_get_string(mb, "plotfile", &err);
5965     if (!err) {
5966 	dims = gretl_bundle_get_matrix(mb, "dims", &err);
5967     }
5968     if (err) {
5969 	gui_errmsg(err);
5970 	return err;
5971     }
5972 
5973     gretl_error_clear();
5974 
5975     plot = png_plot_new();
5976     if (plot == NULL) {
5977 	return E_ALLOC;
5978     }
5979 
5980     plot->spec = plotspec_new();
5981     if (plot->spec == NULL) {
5982 	free(plot);
5983 	return E_ALLOC;
5984     }
5985 
5986     strcpy(plot->spec->fname, fname);
5987     plot->spec->ptr = plot;
5988 
5989     err = gretl_test_fopen(plot->spec->fname, "rb");
5990     if (err) {
5991 	gretl_errmsg_sprintf(_("Couldn't read '%s'"), plot->spec->fname);
5992 	err = E_FOPEN;
5993     } else {
5994 	gchar *buf;
5995 
5996 	buf = g_strdup_printf("\"%s\" \"%s\"", gretl_gnuplot_path(),
5997 			      plot->spec->fname);
5998 	err = gretl_spawn(buf);
5999 	g_free(buf);
6000     }
6001 
6002     if (err) {
6003 	plotspec_destroy(plot->spec);
6004 	free(plot);
6005 	return err;
6006     }
6007 
6008     plot->spec->code = PLOT_GEOMAP;
6009     plot->status |= (PLOT_DONT_EDIT | PLOT_DONT_ZOOM);
6010     plot->pixel_width = dims->val[0];
6011     plot->pixel_height = dims->val[1];
6012 #if GPDEBUG
6013     fprintf(stderr, "map, via bundle: pixwidth %d, pixheight %d\n",
6014 	    plot->pixel_width, plot->pixel_height);
6015 #endif
6016 
6017     if (get_png_bounds_info(&b) == GRETL_PNG_OK) {
6018 	record_coordinate_info(plot, &b);
6019 	plot->status |= PLOT_CURSOR_LABEL;
6020 	plot->status |= PLOT_PNG_COORDS;
6021 	plot->status |= PLOT_HAS_XRANGE;
6022 	plot->status |= PLOT_HAS_YRANGE;
6023 #if GPDEBUG
6024 	fprintf(stderr, "map, via png bounds: %d, %d\n",
6025 		plot->pixel_width, plot->pixel_height);
6026 #endif
6027     } else {
6028 	plot->status |= PLOT_DONT_MOUSE;
6029     }
6030 
6031     mapname = gretl_bundle_get_string(mb, "mapname", NULL);
6032     plot_add_shell(plot, mapname != NULL ? mapname : "map");
6033     err = render_png(plot, PNG_START);
6034 
6035     if (err) {
6036 	gtk_widget_destroy(plot->shell);
6037     } else {
6038 	g_object_set_data(G_OBJECT(plot->shell), "object", plot);
6039     }
6040 
6041     return err;
6042 }
6043 
6044 /* Called on a newly created PNG graph; note that
6045    the filename returned by gretl_plotfile() is the
6046    input file (set of gnuplot commands).
6047 */
6048 
register_graph(void)6049 void register_graph (void)
6050 {
6051     int dcp = do_collect_plots();
6052     png_plot *pp = NULL;
6053 
6054     if (dcp && plot_collection != NULL) {
6055 	if (dcp > 1) {
6056 	    /* explicitly "on" */
6057 	    pp = plot_collection;
6058 	} else {
6059 	    /* "auto": time-limited */
6060 	    gint64 now = gretl_monotonic_time();
6061 	    gint64 ptm = plot_collection_get_mtime();
6062 
6063 	    if (now - ptm < 1.25e6) {
6064 		/* time limit 1.25 seconds */
6065 		pp = plot_collection;
6066 	    }
6067 	}
6068     }
6069 
6070     gnuplot_show_png(gretl_plotfile(), NULL, NULL, pp);
6071 }
6072 
6073 /* @fname is the name of a plot command file from the
6074    current session and @title is its display name;
6075    @session_ptr is a pointer to the session object.
6076 */
6077 
display_session_graph(const char * fname,const char * title,void * session_ptr)6078 void display_session_graph (const char *fname,
6079 			    const char *title,
6080 			    void *session_ptr)
6081 {
6082     char fullname[MAXLEN];
6083     gchar *plotcmd;
6084     int err = 0;
6085 
6086     if (g_path_is_absolute(fname)) {
6087 	strcpy(fullname, fname);
6088     } else {
6089 	sprintf(fullname, "%s%s", gretl_dotdir(), fname);
6090     }
6091 
6092     err = add_png_term_to_plot(fullname);
6093     if (err) {
6094 	return;
6095     }
6096 
6097     plotcmd = g_strdup_printf("\"%s\" \"%s\"",
6098 			      gretl_gnuplot_path(),
6099 			      fullname);
6100     err = gretl_spawn(plotcmd);
6101     g_free(plotcmd);
6102 
6103     if (err) {
6104 	/* display the bad plot file */
6105 	view_file(fullname, 0, 0, 78, 350, VIEW_FILE);
6106     } else {
6107 	err = gnuplot_show_png(fullname, title, session_ptr, NULL);
6108     }
6109 
6110     if (err) {
6111 	gui_errmsg(err);
6112     }
6113 }
6114 
get_png_plot_bounds(const char * str,png_bounds * bounds)6115 static int get_png_plot_bounds (const char *str, png_bounds *bounds)
6116 {
6117     int ret = GRETL_PNG_OK;
6118 
6119     bounds->xleft = bounds->xright = 0;
6120     bounds->ybot = bounds->ytop = 0;
6121 
6122     if (sscanf(str, "pixel_bounds: %d %d %d %d",
6123 	       &bounds->xleft, &bounds->xright,
6124 	       &bounds->ybot, &bounds->ytop) != 4) {
6125 	ret = GRETL_PNG_BAD_COMMENTS;
6126     }
6127 
6128     if (ret == GRETL_PNG_OK && bounds->xleft == 0 &&
6129 	bounds->xright == 0 && bounds->ybot == 0 &&
6130 	bounds->ytop == 0) {
6131 	ret = GRETL_PNG_NO_COORDS;
6132     }
6133 
6134 #if POINTS_DEBUG
6135     fprintf(stderr, "Got: xleft=%d, xright=%d, ybot=%d, ytop=%d\n",
6136 	    bounds->xleft, bounds->xright, bounds->ybot, bounds->ytop);
6137 #endif
6138 
6139     return ret;
6140 }
6141 
get_png_data_bounds(char * str,png_bounds * bounds)6142 static int get_png_data_bounds (char *str, png_bounds *bounds)
6143 {
6144     int ret = GRETL_PNG_OK;
6145     char *p = str;
6146 
6147     /* ensure decimal dot */
6148     while (*p) {
6149 	if (*p == ',') *p = '.';
6150 	p++;
6151     }
6152 
6153     bounds->xmin = bounds->xmax = 0.0;
6154     bounds->ymin = bounds->ymax = 0.0;
6155 
6156     gretl_push_c_numeric_locale();
6157 
6158     if (sscanf(str, "data_bounds: %lf %lf %lf %lf",
6159 	       &bounds->xmin, &bounds->xmax,
6160 	       &bounds->ymin, &bounds->ymax) != 4) {
6161 	ret = GRETL_PNG_BAD_COMMENTS;
6162     }
6163 
6164     if (ret == GRETL_PNG_OK && bounds->xmin == 0.0 &&
6165 	bounds->xmax == 0.0 && bounds->ymin == 0.0 &&
6166 	bounds->ymax == 0.0) {
6167 	ret = GRETL_PNG_NO_COORDS;
6168     }
6169 
6170 #if POINTS_DEBUG
6171     fprintf(stderr, "Got: xmin=%g, xmax=%g, ymin=%g, ymax=%g\n",
6172 	    bounds->xmin, bounds->xmax, bounds->ymin, bounds->ymax);
6173 #endif
6174 
6175     gretl_pop_c_numeric_locale();
6176 
6177     return ret;
6178 }
6179 
get_png_size(char * str,png_bounds * bounds)6180 static int get_png_size (char *str, png_bounds *bounds)
6181 {
6182     int ret = GRETL_PNG_OK;
6183     int pw, ph, sc;
6184 
6185     bounds->width = bounds->height = 0;
6186 
6187     if (sscanf(str, "term_size: %d %d %d", &pw, &ph, &sc) != 3 ||
6188 	pw <= 0 || ph <= 0 || sc <= 0) {
6189 	ret = GRETL_PNG_BAD_COMMENTS;
6190     } else {
6191 	pw /= sc; ph /= sc;
6192 	if (pw % 2) pw++;
6193 	if (ph % 2) ph++;
6194 #if 0
6195 	fprintf(stderr, "Got size: %d x %d\n", pw, ph);
6196 #endif
6197 	bounds->width = pw;
6198 	bounds->height = ph;
6199     }
6200 
6201     return ret;
6202 }
6203 
get_png_bounds_info(png_bounds * bounds)6204 static int get_png_bounds_info (png_bounds *bounds)
6205 {
6206     FILE *fp;
6207     char bbname[MAXLEN], line[128];
6208     int plot_ret = -1, data_ret = -1;
6209     int ret = GRETL_PNG_OK;
6210 
6211     gretl_build_path(bbname, gretl_dotdir(), "gretltmp.png.bounds", NULL);
6212     fp = gretl_fopen(bbname, "rb");
6213 
6214     if (fp == NULL) {
6215 	fprintf(stderr, "couldn't open %s\n", bbname);
6216 	return GRETL_PNG_NO_COMMENTS;
6217     }
6218 
6219     /* bounding box of plot */
6220     if (fgets(line, sizeof line, fp) == NULL) {
6221 	fprintf(stderr, "bounds file: couldn't get plot dims\n");
6222 	plot_ret = GRETL_PNG_NO_COMMENTS;
6223     } else {
6224 	plot_ret = get_png_plot_bounds(line, bounds);
6225     }
6226 
6227     /* data ranges */
6228     if (fgets(line, sizeof line, fp) == NULL) {
6229 	fprintf(stderr, "bounds file: couldn't get data dims\n");
6230 	data_ret = GRETL_PNG_NO_COMMENTS;
6231     } else {
6232 	data_ret = get_png_data_bounds(line, bounds);
6233     }
6234 
6235     /* overall size of plot */
6236     if (fgets(line, sizeof line, fp) == NULL) {
6237 	fprintf(stderr, "bounds file: couldn't get size info\n");
6238     } else {
6239 	get_png_size(line, bounds);
6240     }
6241 
6242     if (plot_ret == GRETL_PNG_NO_COORDS && data_ret == GRETL_PNG_NO_COORDS) {
6243 	/* comments were present and correct, but all zero */
6244 	fprintf(stderr, "bounds file: no actual coordinates\n");
6245 	ret = GRETL_PNG_NO_COORDS;
6246     } else if (plot_ret != GRETL_PNG_OK || data_ret != GRETL_PNG_OK) {
6247 	/* one or both set of coordinates bad or missing */
6248 	if (plot_ret >= 0 || data_ret >= 0) {
6249 	    fprintf(stderr, "bounds file: bad data\n");
6250 	    ret = GRETL_PNG_BAD_COMMENTS;
6251 	} else {
6252 	    ret = GRETL_PNG_NO_COMMENTS;
6253 	}
6254     }
6255 
6256     fclose(fp);
6257     gretl_remove(bbname);
6258 
6259     return ret;
6260 }
6261 
6262 #ifndef G_OS_WIN32
6263 
6264 #include <errno.h>
6265 
get_terminal(char * s)6266 static int get_terminal (char *s)
6267 {
6268     const gchar *terms[] = {
6269 	"xterm",
6270 	"rxvt",
6271 	"gnome-terminal",
6272 	"kterm",
6273 	"urxvt",
6274 	NULL
6275     };
6276     gchar *test;
6277     int i;
6278 
6279     for (i=0; terms[i] != NULL; i++) {
6280 	test = g_find_program_in_path(terms[i]);
6281 	if (test != NULL) {
6282 	    g_free(test);
6283 	    strcpy(s, terms[i]);
6284 	    return 0;
6285 	}
6286     }
6287 
6288 #ifdef OS_OSX
6289     /* fallback for XQuartz: may not be in PATH */
6290     strcpy(s, "/opt/X11/bin/xterm");
6291     if (gretl_file_exists(s)) {
6292 	return 0;
6293     } else {
6294 	*s = '\0';
6295     }
6296 #endif
6297 
6298     errbox(_("Couldn't find a usable terminal program"));
6299 
6300     return 1;
6301 }
6302 
6303 #endif /* !G_OS_WIN32 */
6304 
6305 #ifdef OS_OSX
6306 
mac_do_gp_script(const char * plotfile)6307 static void mac_do_gp_script (const char *plotfile)
6308 {
6309     gchar *buf = NULL;
6310     gsize sz = 0;
6311 
6312     if (g_file_get_contents(plotfile, &buf, &sz, NULL)) {
6313 	run_gnuplot_script(buf, NULL);
6314 	g_free(buf);
6315     }
6316 }
6317 
6318 #endif
6319 
6320 /* Callback for "Gnuplot" item in Tools menu: open a
6321    gnuplot session and let the user do whatever. In
6322    this case we need a controlling terminal window if
6323    we're not on MS Windows.
6324 */
6325 
launch_gnuplot_interactive(void)6326 void launch_gnuplot_interactive (void)
6327 {
6328 #if defined(G_OS_WIN32)
6329     win32_run_async(gretl_gnuplot_path(), NULL);
6330 #elif defined(OS_OSX)
6331     const char *gppath = gretl_gnuplot_path();
6332     gchar *gpline;
6333 
6334 # ifdef PKGBUILD
6335     /* call driver script to set environment correctly -- and
6336        in addition prepend a full path spec if necessary
6337     */
6338     if (g_path_is_absolute(gppath)) {
6339 	gpline = g_strdup_printf("open -a Terminal.app \"%s.sh\"",
6340 				 gppath);
6341     } else {
6342 	gpline = g_strdup_printf("open -a Terminal.app \"%s%s.sh\"",
6343 				 gretl_bindir(), gppath);
6344     }
6345 # else
6346     gpline = g_strdup_printf("open -a Terminal.app \"%s\"", gppath);
6347 # endif
6348     system(gpline);
6349     g_free(gpline);
6350 #else /* neither WIN32 nor MAC */
6351     char term[32];
6352     int err;
6353 
6354     err = get_terminal(term);
6355 
6356     if (!err) {
6357 	const char *gp = gretl_gnuplot_path();
6358 	GError *error = NULL;
6359 	gchar *argv[6];
6360 
6361 # ifdef OS_OSX
6362 	char *altgp = g_strdup_printf("%s.sh", gp);
6363 
6364 	if (gretl_file_exists(altgp)) {
6365 	    gp = altgp;
6366 	} else {
6367 	    g_free(altgp);
6368 	    altgp = NULL;
6369 	}
6370 # endif
6371 
6372 	if (strstr(term, "gnome")) {
6373 	    /* gnome-terminal */
6374 	    argv[0] = term;
6375 	    argv[1] = "--title=\"gnuplot: type q to quit\"";
6376 	    argv[2] = "-x";
6377 	    argv[3] = (char *) gp;
6378 	    argv[4] = NULL;
6379 	} else {
6380 	    /* xterm, rxvt, kterm */
6381 	    argv[0] = term;
6382 	    argv[1] = "-title";
6383 	    argv[2] = "gnuplot: type q to quit";
6384 	    argv[3] = "-e";
6385 	    argv[4] = (char *) gp;
6386 	    argv[5] = NULL;
6387 	}
6388 
6389 	g_spawn_async(NULL, /* working dir */
6390 		      argv,
6391 		      NULL, /* env */
6392 		      G_SPAWN_SEARCH_PATH,
6393 		      NULL, /* child_setup */
6394 		      NULL, /* user_data */
6395 		      NULL, /* child_pid ptr */
6396 		      &error);
6397 
6398 	if (error != NULL) {
6399 	    errbox(error->message);
6400 	    g_error_free(error);
6401 	}
6402 
6403 # ifdef OS_OSX
6404 	g_free(altgp);
6405 # endif
6406     }
6407 #endif /* !(G_OS_WIN32 or MAC) */
6408 }
6409 
gnuplot_view_3d(const char * plotfile)6410 void gnuplot_view_3d (const char *plotfile)
6411 {
6412 #if defined(G_OS_WIN32)
6413     win32_run_async(gretl_gnuplot_path(), plotfile);
6414 #elif defined(OS_OSX) && !defined(GNUPLOT3D)
6415     mac_do_gp_script(plotfile);
6416 #else
6417     real_send_to_gp(plotfile, 0);
6418 #endif /* !(G_OS_WIN32 or MAC) */
6419 }
6420