/* 
 *  gretl -- Gnu Regression, Econometrics and Time-series Library
 *  Copyright (C) 2001 Allin Cottrell and Riccardo "Jack" Lucchetti
 * 
 *  This program is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation, either version 3 of the License, or
 *  (at your option) any later version.
 * 
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 * 
 *  You should have received a copy of the GNU General Public License
 *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
 * 
 */

/* gpt_dialog.c for gretl -- GUI gnuplot controller dialog */

#include "gretl.h"
#include "plotspec.h"
#include "gpt_control.h"
#include "session.h"
#include "dlgutils.h"
#include "fileselect.h"
#include "calculator.h"

#include "../pixmaps/mouse.xpm"

struct gpt_titles_t {
    char *description; /* How the field will show up in the options dialog */
    short tab;         /* which tab (if any) does the item fall under? */
    GtkWidget *widget;
};

struct gpt_range_t {
    gint ID;
    GtkWidget *isauto;
    GtkWidget *min;
    GtkWidget *max;
    GtkWidget *lbase;
};

static GtkWidget **linetitle;
static GtkWidget **stylecombo;
static GtkWidget **yaxiscombo;
static GtkWidget **linescale;
static GtkWidget **linewidth;

static GtkWidget *labeltext[MAX_PLOT_LABELS];
static GtkWidget *labeljust[MAX_PLOT_LABELS];
static GtkWidget *labelpos[MAX_PLOT_LABELS];

static GtkWidget *gpt_control;
static GtkWidget *keycombo;
static GtkWidget *fitcombo;
static GtkWidget *border_check;
static GtkWidget *markers_check;
static GtkWidget *y2_check;
static GtkWidget *ttfcombo;
static GtkWidget *ttfspin;

static int gui_nlines;

#define MAX_AXES 3

struct gpt_range_t axis_range[MAX_AXES];

#define NTITLES 4
#define PLOT_LABEL_POS_LEN 32

struct gpt_titles_t gpt_titles[] = {
    { N_("Title of plot"),  0, NULL },
    { N_("Title for axis"), 1, NULL },
    { N_("Title for axis"), 2, NULL },
    { N_("Title for axis"), 3, NULL },
};

const gchar *fittype_strings[] = {
    N_("none"),
    N_("linear: y = a + b*x"),
    N_("quadratic: y = a + b*x + c*x^2"),
    N_("inverse: y = a + b*(1/x)"),
    N_("loess (locally weighted fit)"),
    NULL
};

static const char *get_font_filename (const char *showname);

static void close_plot_controller (GtkWidget *widget, gpointer data) 
{
    GPT_SPEC *spec = (GPT_SPEC *) data;
    png_plot *plot = (png_plot *) spec->ptr;

    gpt_control = NULL;

    if (plot != NULL) { 
	/* PNG plot window is open */
#ifdef G_OS_WIN32 /* z-order gets messed up */
	GtkWidget *shell = plot_get_shell(plot);

	gdk_window_raise(shell->window);
#endif
	plot_remove_controller(plot);
    } else {
	plotspec_destroy(spec); 
    }
} 

static void flip_manual_range (GtkWidget *widget, gpointer data)
{
    gint i = GPOINTER_TO_INT(data);
    gboolean s = GTK_TOGGLE_BUTTON(axis_range[i].isauto)->active;

    gtk_widget_set_sensitive(GTK_WIDGET(axis_range[i].min), !s);
    gtk_widget_set_sensitive(GTK_WIDGET(axis_range[i].max), !s);
}

static void disable_lbase (GtkWidget *b, GtkWidget *entry)
{
    gboolean s = GTK_TOGGLE_BUTTON(b)->active;

    gtk_widget_set_sensitive(entry, !s);
}

static void enable_lbase (GtkWidget *b, GtkWidget *entry)
{
    gboolean s = GTK_TOGGLE_BUTTON(b)->active;

    gtk_widget_set_sensitive(entry, s);
}

/* Take text from a gtkentry and write to gnuplot spec string */

static void entry_to_gp_string (GtkWidget *w, char *targ, size_t n)
{
    const gchar *str;

    *targ = '\0';

    g_return_if_fail(GTK_IS_ENTRY(w));

    str = gtk_entry_get_text(GTK_ENTRY(w));
    if (str != NULL || *str != '\0') {
	strncat(targ, str, n-1);
    }
}

static void combo_to_gp_string (GtkWidget *w, char *targ, size_t n)
{
    gchar *str;

    *targ = '\0';

    g_return_if_fail(GTK_IS_COMBO_BOX(w));

    str = gtk_combo_box_get_active_text(GTK_COMBO_BOX(w));

    if (str != NULL || *str != '\0') {
	strncat(targ, str, n-1);
    }

    g_free(str);
}

static FitType fit_type_from_string (const char *s)
{
    FitType f = PLOT_FIT_NONE;
    int i;

    if (s != NULL && *s != '\0') {
	for (i=0; fittype_strings[i] != NULL; i++) {
	    if (!strcmp(s, _(fittype_strings[i]))) {
		f = i;
		break;
	    }
	}
    }

    return f;
}

static void fittype_from_combo (GtkComboBox *box, GPT_SPEC *spec)
{
    gchar *s = gtk_combo_box_get_active_text(box);
    FitType f;

    f = fit_type_from_string(s);
    g_free(s);

    if (f == PLOT_FIT_OLS || f == PLOT_FIT_QUADRATIC || 
	f == PLOT_FIT_INVERSE || f == PLOT_FIT_LOESS) {
	plotspec_add_fit(spec, f);
	spec->flags &= ~GPT_FIT_HIDDEN;
    } else if (f == PLOT_FIT_NONE) {
	if (spec->n_lines == 2) {
	    spec->flags |= GPT_FIT_HIDDEN;
	}
	spec->fit = f;
    }
}

static gboolean fit_type_changed (GtkComboBox *box, GPT_SPEC *spec)
{
    const char *s1 = spec->yvarname;
    const char *s2 = spec->xvarname;
    gchar *s, *title = NULL;
    FitType f = PLOT_FIT_NONE;

    if (*s1 == '\0' || *s2 == '\0') {
	return FALSE;
    }

    s = gtk_combo_box_get_active_text(box);
    f = fit_type_from_string(s);
    g_free(s);

    if (f == PLOT_FIT_OLS) {
	title = g_strdup_printf(_("%s versus %s (with least squares fit)"),
		s1, s2);
    } else if (f == PLOT_FIT_QUADRATIC) {
	title = g_strdup_printf(_("%s versus %s (with quadratic fit)"),
		s1, s2);
    } else if (f == PLOT_FIT_INVERSE) {
	title = g_strdup_printf(_("%s versus %s (with inverse fit)"),
		s1, s2);
    } else if (f == PLOT_FIT_LOESS) {
	title = g_strdup_printf(_("%s versus %s (with loess fit)"),
		s1, s2);
    }

    if (title != NULL) {
	gtk_entry_set_text(GTK_ENTRY(gpt_titles[0].widget), title);
	g_free(title);
    }
    
    return FALSE;
}

/* take a double (which might be NA) and format it for
   a gtkentry widget */

static void double_to_gp_entry (double x, GtkWidget *w)
{
    if (w != NULL && GTK_IS_ENTRY(w)) {
	gchar *numstr;

	if (na(x)) {
	    numstr = g_strdup("*");
	} else {
	    numstr = g_strdup_printf("%g", x);
	}
	gtk_entry_set_text(GTK_ENTRY(w), numstr);
	g_free(numstr);
    }
}

/* read a double from a gtkentry, with error checking */

static double entry_to_gp_double (GtkWidget *w)
{
    double ret = NADBL;

    if (w != NULL && GTK_IS_ENTRY(w)) {
	const gchar *s = gtk_entry_get_text(GTK_ENTRY(w));

#ifdef ENABLE_NLS
	if (s != NULL && *s != '\0') {
	    gchar *tmp = g_strdup(s);

	    charsub(tmp, ',', '.');
	    gretl_push_c_numeric_locale();	    
	    if (check_atof(tmp)) {
		errbox(gretl_errmsg_get());
	    } else {
		ret = atof(tmp);
	    }
	    gretl_pop_c_numeric_locale();
	    g_free(tmp);
	}
#else
	if (s != NULL && *s != '\0') {
	    if (check_atof(s)) {
		errbox(gretl_errmsg_get());
	    } else {
		ret = atof(s);
	    }
	}
#endif
    }

    return ret;
}

#define gp_string_to_entry(w,s) do { \
	gtk_entry_set_text(GTK_ENTRY(w),s); } while (0)

static int
get_label_pos_from_entry (GtkWidget *w, double *pos)
{
    int err = 0;

    if (GTK_IS_ENTRY(w)) {
	const gchar *s = gtk_entry_get_text(GTK_ENTRY(w));
	int chk;
    
	chk = sscanf(s, "%lf %lf", &pos[0], &pos[1]);
	if (chk != 2) {
	    errbox(_("Invalid label position, must be X Y"));
	    gtk_editable_select_region(GTK_EDITABLE(w), 0, strlen(s));
	    pos[0] = pos[1] = NADBL;
	    err = 1;
	} 
    } else {
	err = 1;
    }

    return err;
}

static int validate_range (double *r)
{
    int err = 0;

    if (na(r[0]) || na(r[1])) {
	r[0] = r[1] = NADBL;
	err = 1;
    } else if (r[1] <= r[0]) {
	r[0] = r[1] = NADBL;
	err = 1;
    }

    return err;
}

static int 
set_logscale_from_entry (GPT_SPEC *spec, int i, GtkWidget *entry)
{
    double base;
    int err = 0;

    base = entry_get_numeric_value(entry, C_POS_DBL);
    if (na(base)) {
	err = 1;
    } else if (!na(base) && base < 1.1) {
	err = 1;
	errbox("bad base");
    } else {
	spec->logbase[i] = base;
    }

    return err;
}

enum {
    ERRORBARS = 1,
    FILLEDCURVE
};

static void apply_gpt_changes (GtkWidget *w, GPT_SPEC *spec) 
{
    int supress_y2 = 0;
    int i, k, err = 0;

    for (i=0; i<NTITLES; i++) {
	if (gpt_titles[i].widget != NULL) {
	    entry_to_gp_string(gpt_titles[i].widget, spec->titles[i], 
			       sizeof spec->titles[0]);
	}
    }

    if (keycombo != NULL) {
	gchar *s = gtk_combo_box_get_active_text(GTK_COMBO_BOX(keycombo));

	strcpy(spec->keyspec, s);
	g_free(s);
    }

    spec->flags &= ~GPT_Y2AXIS;

    if (y2_check != NULL && GTK_TOGGLE_BUTTON(y2_check)->active) {
	supress_y2 = 1;
    } 

    for (i=0; i<gui_nlines; i++) {
	spec->lines[i].yaxis = 1;
	if (!supress_y2 && yaxiscombo[i] != NULL) {
	    gchar *s = 
		gtk_combo_box_get_active_text(GTK_COMBO_BOX(yaxiscombo[i]));

	    if (!strcmp(s, "right")) {
		spec->lines[i].yaxis = 2;	
	    }
	    if (spec->lines[i].yaxis == 2) {
		spec->flags |= GPT_Y2AXIS;
	    }
	    g_free(s);
	}
    }

    if (spec->code == PLOT_REGULAR) {
	k = (spec->flags & GPT_Y2AXIS)? 3 : 2;
	for (i=0; i<k; i++) {
	    if (axis_range[i].isauto != NULL) {
		if (GTK_TOGGLE_BUTTON(axis_range[i].isauto)->active) {
		    spec->range[i][0] = NADBL;
		    spec->range[i][1] = NADBL;
		} else {
		    spec->range[i][0] = entry_to_gp_double(axis_range[i].min);
		    spec->range[i][1] = entry_to_gp_double(axis_range[i].max);
		    err = validate_range(spec->range[i]);
		}
	    }
	    if (axis_range[i].lbase != NULL) {
		if (GTK_WIDGET_SENSITIVE(axis_range[i].lbase)) {
		    err = set_logscale_from_entry(spec, i, axis_range[i].lbase);
		} else {
		    spec->logbase[i] = 0.0;
		}
	    }
	}
    }

    if (!err) {   
	for (i=0; i<gui_nlines; i++) {
	    if (stylecombo[i] != NULL) {
		int oldalt = 0;

		if (!strncmp(spec->lines[i].style, "filled", 6)) {
		    oldalt = FILLEDCURVE;
		} else if (!strncmp(spec->lines[i].style, "error", 5)) {
		    oldalt = ERRORBARS;
		}
		combo_to_gp_string(stylecombo[i], spec->lines[i].style, 
				   sizeof spec->lines[0].style);
		if (oldalt == FILLEDCURVE &&
		    !strncmp(spec->lines[i].style, "error", 5)) {
		    spec->flags |= GPT_ERR_SWITCH;
		    spec->flags &= ~GPT_FILL_SWITCH;
		} else if (oldalt == ERRORBARS &&
			   !strncmp(spec->lines[i].style, "filled", 6)) {
		    spec->flags |= GPT_FILL_SWITCH;
		    spec->flags &= ~GPT_ERR_SWITCH;
		}
	    }
	    if (linetitle[i] != NULL) {
		entry_to_gp_string(linetitle[i], 
				   spec->lines[i].title, 
				   sizeof spec->lines[0].title);
	    }
	    if (linescale[i] != NULL) {
		entry_to_gp_string(linescale[i], 
				   spec->lines[i].scale, 
				   sizeof spec->lines[0].scale);
	    }
	    if (linewidth[i] != NULL) {
		spec->lines[i].width = 
		    gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(linewidth[i]));
	    }
	}
    }

    for (i=0; i<MAX_PLOT_LABELS && !err; i++) {
	entry_to_gp_string(labeltext[i], spec->labels[i].text, 
			   sizeof spec->labels[0].text);
	if (string_is_blank(spec->labels[i].text)) {
	    continue;
	}
	err = get_label_pos_from_entry(labelpos[i], spec->labels[i].pos);
	if (err) {
	    break;
	}
	spec->labels[i].just = 
	    gtk_combo_box_get_active(GTK_COMBO_BOX(labeljust[i]));
    } 

    if (!err && border_check != NULL) {
	if (GTK_TOGGLE_BUTTON(border_check)->active) {
	    spec->flags &= ~GPT_MINIMAL_BORDER;
	} else {
	    spec->flags |= GPT_MINIMAL_BORDER;
	}
    } 

    if (!err && markers_check != NULL) {
	if (GTK_TOGGLE_BUTTON(markers_check)->active) {
	    free(spec->labeled);
	    spec->labeled = NULL;
	    spec->flags |= GPT_ALL_MARKERS;
	} else {
	    spec->flags &= ~GPT_ALL_MARKERS;
	}
    }

    if (!err && ttfcombo != NULL && ttfspin != NULL) {
	gchar *tmp = gtk_combo_box_get_active_text(GTK_COMBO_BOX(ttfcombo));
	int ptsize = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(ttfspin));

	if (tmp != NULL && *tmp != '\0') {
	    const char *fname = get_font_filename(tmp);
	    char pngfont[128];

	    if (fname != NULL && ptsize > 5 && ptsize < 25) {
		sprintf(pngfont, "%s %d", fname, ptsize);
	    } else {
		*pngfont = '\0';
	    }
	    set_gretl_png_font(pngfont, &paths);
	}
	g_free(tmp);
    }

    if (!err && fitcombo != NULL) {
	fittype_from_combo(GTK_COMBO_BOX(fitcombo), spec);
    }

    if (!err) {
	png_plot *plot = (png_plot *) spec->ptr;

	set_plot_has_y2_axis(plot, spec->flags & GPT_Y2AXIS);
	redisplay_edited_plot(plot);
	mark_session_changed();
    }

    spec->flags &= ~(GPT_FILL_SWITCH | GPT_ERR_SWITCH);
}

static void set_keyspec_sensitivity (GPT_SPEC *spec)
{
    gboolean state = FALSE;
    const char *p;
    int i;

    for (i=0; i<gui_nlines; i++) {
	p = gtk_entry_get_text(GTK_ENTRY(linetitle[i]));
	if (p != NULL && *p != 0) {
	    state = TRUE;
	    break;
	}
    }

    gtk_widget_set_sensitive(keycombo, state);
}

#define TAB_MAIN_COLS 3

struct font_info {
    const char *fname;
    const char *showname;
};

static struct font_info ttf_fonts[] = {
    { "arial", "Arial", },
    { "georgia", "Georgia", },
#ifndef G_OS_WIN32
    { "luxirr", "Luxi Serif" },
    { "luxisr", "Luxi Sans" },
    { "Vera", "Vera" },
#endif
    { "tahoma", "Tahoma" },
    { "trebuc", "Trebuchet" },
    { "verdana", "Verdana" }
};

static const char *get_font_filename (const char *showname)
{
    int i, nfonts = sizeof ttf_fonts / sizeof ttf_fonts[0];

    for (i=0; i<nfonts; i++) {
	if (!strcmp(ttf_fonts[i].showname, showname)) {
	    return ttf_fonts[i].fname;
	}
    }
    return NULL;
}

#ifndef G_OS_WIN32

static int font_is_ok (const char *fname)
{
    static int pngterm;
    gchar *cmd;
    int err;

    if (pngterm == 0) {
	pngterm = gnuplot_png_terminal();
    }

    if (pngterm == GP_PNG_CAIRO) {
	cmd = g_strdup_printf("set term pngcairo font \"%s,10\"", fname);
    } else {
	cmd = g_strdup_printf("set term png font %s 10", fname);
    }

    err = gnuplot_test_command(cmd);
    g_free(cmd);

    return err == 0;
}

#endif

static struct font_info *get_gnuplot_ttf_list (int *nf)
{
    static struct font_info *retlist = NULL;
    static int goodfonts = -1;

    if (goodfonts >= 0) {
	*nf = goodfonts;
    } else {
	int i, j, nfonts = sizeof ttf_fonts / sizeof ttf_fonts[0];

	retlist = malloc(nfonts * sizeof *retlist);
	if (retlist == NULL) return NULL;

	j = 0;
	for (i=0; i<nfonts; i++) {
#ifdef G_OS_WIN32
	    retlist[j++] = ttf_fonts[i];
#else
	    if (font_is_ok(ttf_fonts[i].fname)) {
		retlist[j++] = ttf_fonts[i];
	    }
#endif
	}
	goodfonts = j;
	*nf = goodfonts;
    }

    return retlist;
}

static int font_match (const char *ttfname, const char *pngfont)
{
    return !strncmp(ttfname, pngfont, strlen(ttfname));
}

static int get_point_size (const char *font)
{
    int pts;

    if (sscanf(font, "%*s %d\n", &pts) == 1) {
	return pts;
    } else {
	return 10;
    }
}

static void strip_lr (gchar *txt)
{
    gchar test[16];
    gchar *p;

    sprintf(test, "(%s)", I_("left"));
    p = strstr(txt, test);
    if (p != NULL) {
	*p = '\0';
    } else {
	sprintf(test, "(%s)", I_("right"));
	p = strstr(txt, test);
	if (p != NULL) {
	   *p = '\0';
	}
    } 
}

static void toggle_axis_selection (GtkWidget *w, GPT_SPEC *spec)
{
    int no_y2 = GTK_TOGGLE_BUTTON(w)->active;
    int i;

    for (i=0; i<gui_nlines; i++) {
	if (yaxiscombo[i] != NULL) {
	    gtk_widget_set_sensitive(yaxiscombo[i], !no_y2);
	}
    }
}

/* re-establish the default plot colors and reset the
   color selection buttons accordingly */

static void color_default_callback (GtkWidget *w, GtkWidget *book)
{
    GtkWidget *button;
    char id[32];
    int i;

    sprintf(id, "color-button%d", BOXCOLOR);
    button = g_object_get_data(G_OBJECT(book), id);

    if (button != NULL) {
	graph_palette_reset(BOXCOLOR);
	color_patch_button_reset(button, BOXCOLOR);
    } else {
	for (i=0; i<BOXCOLOR; i++) {
	    sprintf(id, "color-button%d", i);
	    button = g_object_get_data(G_OBJECT(book), id);
	    if (button != NULL) {
		if (i == 0) {
		    graph_palette_reset(i);
		}
		color_patch_button_reset(button, i);
	    }
	}
    }
}

static void table_add_row (GtkWidget *tbl, int *rows, int cols)
{
    *rows += 1;
    gtk_table_resize(GTK_TABLE(tbl), *rows, cols);    
}

static void add_color_selector (int i, GtkWidget *tbl, int *rows,
				GtkWidget *notebook)
{
    GtkWidget *button, *hbox;
    GtkWidget *label;
    char str[32];

    table_add_row(tbl, rows, TAB_MAIN_COLS);

    hbox = gtk_hbox_new(FALSE, 2);

    if (i == BOXCOLOR) {
	strcpy(str, _("Fill color"));
    } else {
	sprintf(str, _("Color %d"), i + 1);
    }

    label = gtk_label_new(str);
    gtk_container_add(GTK_CONTAINER(hbox), label);
    gtk_table_attach_defaults(GTK_TABLE(tbl), hbox, 0, 1, 
			      *rows - 1, *rows);
    gtk_widget_show(label);
    gtk_widget_show(hbox);

    hbox = gtk_hbox_new(FALSE, 2);
    button = color_patch_button(i);
    gtk_box_pack_start(GTK_BOX(hbox), button, FALSE, FALSE, 0);
    gtk_table_attach_defaults(GTK_TABLE(tbl), hbox, 1, 2, 
			      *rows - 1, *rows);
    g_signal_connect(G_OBJECT(button), "clicked", 
		     G_CALLBACK(graph_color_selector), 
		     GINT_TO_POINTER(i));

    gtk_widget_show_all(button);
    gtk_widget_show(hbox);

    sprintf(str, "color-button%d", i);
    g_object_set_data(G_OBJECT(notebook), str, button);

    if (i == BOXCOLOR || i == BOXCOLOR - 1) {
	table_add_row(tbl, rows, TAB_MAIN_COLS);
	hbox = gtk_hbox_new(FALSE, 2);
	button = gtk_button_new_with_label(_("Reset to default"));
	gtk_box_pack_start(GTK_BOX(hbox), button, FALSE, FALSE, 10);
	g_signal_connect(G_OBJECT(button), "clicked", 
			 G_CALLBACK(color_default_callback), 
			 notebook);
	gtk_table_attach(GTK_TABLE(tbl), hbox, 0, 2, *rows - 1, *rows,
			 GTK_FILL, 0, 0, 5);
	gtk_widget_show(button);
	gtk_widget_show(hbox);
    }
}

/* PNG anti-aliasing switch */

static void set_aa_status (GtkWidget *w, int *ok)
{
    *ok = GTK_TOGGLE_BUTTON(w)->active;

    gnuplot_png_set_use_aa(*ok);
}

static GtkWidget *gp_dialog_table (int rows, int cols, 
				   GtkWidget *vbox)
{
    GtkWidget *tbl;

    tbl = gtk_table_new(rows, cols, FALSE);
    gtk_table_set_row_spacings(GTK_TABLE(tbl), 5);
    gtk_table_set_col_spacings(GTK_TABLE(tbl), 5);
    gtk_box_pack_start(GTK_BOX(vbox), tbl, FALSE, FALSE, 0);

    return tbl;
}

static GtkWidget *gp_dialog_vbox (void)
{
    GtkWidget *vbox;

    vbox = gtk_vbox_new(FALSE, 0);
    gtk_container_set_border_width(GTK_CONTAINER(vbox), 10);

    return vbox;
}

static GtkWidget *gp_page_vbox (GtkWidget *notebook, char *str)
{
    GtkWidget *vbox;
    GtkWidget *label;

    vbox = gtk_vbox_new(FALSE, 0);
    gtk_container_set_border_width(GTK_CONTAINER(vbox), 10);
    gtk_widget_show(vbox);
    label = gtk_label_new(str);
    gtk_widget_show(label);
    gtk_notebook_append_page(GTK_NOTEBOOK(notebook), vbox, label); 

    return vbox;
}

static void gpt_tab_main (GtkWidget *notebook, GPT_SPEC *spec) 
{
    static int aa_ok = 1;
    static int show_aa_check = -1;
    GtkWidget *label, *vbox, *tbl;
    int i, rows = 1;
    int kactive = 0;
    gchar *keypos[] = {
	"left top",
	"right top",
	"left bottom",
	"right bottom",
	"outside",
	"none",
	NULL
    };

    vbox = gp_page_vbox(notebook, _("Main"));

    tbl = gp_dialog_table(rows, TAB_MAIN_COLS, vbox);
    gtk_widget_show(tbl);
   
    for (i=0; i<NTITLES; i++) {
	if (gpt_titles[i].tab == 0) {
	    GtkWidget *entry;

	    if (i > 0) {
		table_add_row(tbl, &rows, TAB_MAIN_COLS);
	    }

	    label = gtk_label_new(_(gpt_titles[i].description));
	    gtk_table_attach_defaults(GTK_TABLE (tbl), 
				      label, 0, 1, rows-1, rows);
	    gtk_widget_show(label);

	    entry = gtk_entry_new();
	    gtk_table_attach_defaults(GTK_TABLE(tbl), 
				      entry, 1, TAB_MAIN_COLS, 
				      rows-1, rows);
				      
            if (spec->titles[i] != NULL && *spec->titles[i] != '\0') {
		gp_string_to_entry(entry, spec->titles[i]);
            }		

	    g_signal_connect(G_OBJECT(entry), "activate", 
			     G_CALLBACK(apply_gpt_changes), 
			     spec);

	    gtk_widget_show(entry);
	    gpt_titles[i].widget = entry;
	}
    }

    /* specify position of plot key or legend */
    table_add_row(tbl, &rows, TAB_MAIN_COLS);
    label = gtk_label_new(_("key position"));
    gtk_table_attach_defaults(GTK_TABLE(tbl), 
			      label, 0, 1, rows-1, rows);
    gtk_widget_show(label);

    keycombo = gtk_combo_box_new_text();
    gtk_table_attach_defaults(GTK_TABLE(tbl), 
			      keycombo, 1, TAB_MAIN_COLS, rows-1, rows);
    for (i=0; keypos[i] != NULL; i++) {
	gtk_combo_box_append_text(GTK_COMBO_BOX(keycombo), keypos[i]);
	if (!strcmp(keypos[i], spec->keyspec)) {
	    kactive = i;
	}
    }
    gtk_combo_box_set_active(GTK_COMBO_BOX(keycombo), kactive);
    gtk_widget_show(keycombo);

    /* choice of fitted line type, if appropriate */
    if (spec->fit != PLOT_FIT_NA) {
	table_add_row(tbl, &rows, TAB_MAIN_COLS);

	label = gtk_label_new(_("fitted line"));
	gtk_table_attach_defaults(GTK_TABLE(tbl), 
				  label, 0, 1, rows-1, rows);
	gtk_widget_show(label);

	fitcombo = gtk_combo_box_new_text();
	gtk_table_attach_defaults(GTK_TABLE(tbl), 
				  fitcombo, 1, TAB_MAIN_COLS, rows-1, rows);
	for (i=0; fittype_strings[i] != NULL; i++) {
	    gtk_combo_box_append_text(GTK_COMBO_BOX(fitcombo), _(fittype_strings[i]));
	}
	gtk_combo_box_set_active(GTK_COMBO_BOX(fitcombo), spec->fit);
	g_signal_connect(G_OBJECT(fitcombo), "changed",
			 G_CALLBACK(fit_type_changed), spec);
	gtk_widget_show(fitcombo);
    } else {
	fitcombo = NULL;
    }

    /* give option of removing top & right border */
    if (!(spec->flags & GPT_Y2AXIS)) { 
	y2_check = NULL;
	table_add_row(tbl, &rows, TAB_MAIN_COLS);
	border_check = gtk_check_button_new_with_label(_("Show full border"));
	gtk_table_attach_defaults(GTK_TABLE(tbl), 
				  border_check, 0, TAB_MAIN_COLS, 
				  rows-1, rows);
	if (!(spec->flags & GPT_MINIMAL_BORDER)) {
	    gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(border_check),
					 TRUE);
	}	
	gtk_widget_show(border_check);
    } else {
	border_check = NULL;
	table_add_row(tbl, &rows, TAB_MAIN_COLS);
	y2_check = gtk_check_button_new_with_label(_("Use only one y axis"));
	gtk_table_attach_defaults(GTK_TABLE(tbl), 
				  y2_check, 0, TAB_MAIN_COLS, 
				  rows-1, rows);
	gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(y2_check),
				     FALSE);
	g_signal_connect(G_OBJECT(y2_check), "clicked", 
			 G_CALLBACK(toggle_axis_selection), spec);
	gtk_widget_show(y2_check);
    }

    /* give option of showing all case markers */
    if (spec->flags & GPT_ALL_MARKERS_OK) { 
	table_add_row(tbl, &rows, TAB_MAIN_COLS);
	markers_check = gtk_check_button_new_with_label(_("Show all data labels"));
	gtk_table_attach_defaults(GTK_TABLE(tbl), 
				  markers_check, 0, TAB_MAIN_COLS, 
				  rows-1, rows);
	if (spec->flags & GPT_ALL_MARKERS) {
	    gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(markers_check),
					 TRUE);
	}	
	gtk_widget_show(markers_check);
    } else {
	markers_check = NULL;
    }

    if (show_aa_check < 0) {
	show_aa_check = (gnuplot_png_terminal() == GP_PNG_GD2);
    }

    /* give option of suppressing anti-aliasing for PNGs */
    if (show_aa_check) {
	GtkWidget *aa_check;

	table_add_row(tbl, &rows, TAB_MAIN_COLS);
	aa_check = gtk_check_button_new_with_label(_("Allow anti-aliasing of lines"));
	gtk_table_attach_defaults(GTK_TABLE(tbl), 
				  aa_check, 0, TAB_MAIN_COLS, 
				  rows-1, rows);
	gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(aa_check),
				     aa_ok);
	g_signal_connect(G_OBJECT(aa_check), "clicked", 
			 G_CALLBACK(set_aa_status), &aa_ok);
	gtk_widget_show(aa_check);
    }	

    /* set TT font, if gnuplot uses freetype */
    if (gnuplot_has_ttf(0)) {
	GtkWidget *ebox, *hsep, *entry;
	GList *fontnames = NULL;
	struct font_info *ttflist;
	const char *default_font = NULL;
	int nfonts = 0;

	ttflist = get_gnuplot_ttf_list(&nfonts);

	for (i=0; i<nfonts; i++) {
	    fontnames = g_list_append(fontnames, (gpointer) ttflist[i].showname);
	    if (font_match(ttflist[i].fname, gretl_png_font())) {
		default_font = ttflist[i].showname;
	    }
	}

	fontnames = g_list_append(fontnames, _("None"));
	if (default_font == NULL) {
	    default_font = _("None");
	}

	/* first a separator */
	table_add_row(tbl, &rows, TAB_MAIN_COLS);	
	hsep = gtk_hseparator_new();
	gtk_table_attach_defaults(GTK_TABLE(tbl), hsep, 0, TAB_MAIN_COLS, 
				  rows-1, rows);  
	gtk_widget_show(hsep);

	table_add_row(tbl, &rows, TAB_MAIN_COLS);
	ebox = gtk_event_box_new();
	label = gtk_label_new(_("TrueType font"));
	gtk_container_add(GTK_CONTAINER(ebox), label);
	gtk_table_attach_defaults(GTK_TABLE (tbl), ebox, 0, 1, 
				  rows-1, rows);
	gtk_widget_show(label);
	gtk_widget_show(ebox);

	/* FIXME max length of font name? */

	ttfcombo = gtk_combo_box_entry_new_text();
	entry = gtk_bin_get_child(GTK_BIN(ttfcombo));
	gtk_entry_set_max_length(GTK_ENTRY(entry), 15);
	gtk_table_attach_defaults(GTK_TABLE(tbl), ttfcombo, 1, 2, 
				  rows-1, rows);
	set_combo_box_strings_from_list(GTK_COMBO_BOX(ttfcombo), fontnames); 
	gtk_entry_set_text(GTK_ENTRY(entry), default_font);
	gtk_entry_set_width_chars(GTK_ENTRY(entry), 15);
	g_signal_connect(G_OBJECT(entry), "activate", 
			 G_CALLBACK(apply_gpt_changes), 
			 spec);
	gtk_widget_show(ttfcombo);
	g_list_free(fontnames);

	ttfspin = gtk_spin_button_new_with_range(6, 24, 1);
	gtk_spin_button_set_value(GTK_SPIN_BUTTON(ttfspin), 
				  get_point_size(gretl_png_font()));
	gtk_table_attach_defaults(GTK_TABLE(tbl), ttfspin, 2, 3, 
				  rows - 1, rows);
	gtk_widget_show(ttfspin);
    } else {
	ttfcombo = NULL;
	ttfspin = NULL;
    }

    if (gnuplot_png_terminal() != GP_PNG_OLD) {
	GtkWidget *hsep = gtk_hseparator_new();

	table_add_row(tbl, &rows, TAB_MAIN_COLS);
	gtk_table_attach_defaults(GTK_TABLE(tbl), hsep, 0, TAB_MAIN_COLS, 
				  rows - 1, rows);  
	gtk_widget_show(hsep);

	if (frequency_plot_code(spec->code)) {
	    add_color_selector(BOXCOLOR, tbl, &rows, notebook);
	} else {
	    for (i=0; i<BOXCOLOR; i++) {
		add_color_selector(i, tbl, &rows, notebook);
	    }
	}
    }
}

static void linetitle_callback (GtkWidget *w, GPT_SPEC *spec)
{
    set_keyspec_sensitivity(spec);
}

static void gpt_tab_lines (GtkWidget *notebook, GPT_SPEC *spec) 
{
    GtkWidget *label, *tbl;
    GtkWidget *vbox, *hbox;
    int i, tbl_len, tbl_num, tbl_col;
    char label_text[32];
    GList *stylist = NULL;
    int do_scale_axis = 0;

    if (spec->code == PLOT_REGULAR && (spec->flags & GPT_TS)) {
	do_scale_axis = 1;
    }

    if (frequency_plot_code(spec->code)) {
	stylist = g_list_append(stylist, "boxes");
    }

    if (spec->flags & GPT_TS) {
	stylist = g_list_append(stylist, "lines");
	stylist = g_list_append(stylist, "points");
    } else {
	stylist = g_list_append(stylist, "points");
	stylist = g_list_append(stylist, "lines");
    }

    stylist = g_list_append(stylist, "linespoints"); 
    stylist = g_list_append(stylist, "impulses");
    stylist = g_list_append(stylist, "dots");
    stylist = g_list_append(stylist, "steps");

    vbox = gp_dialog_vbox();
    gtk_widget_show(vbox);

    label = gtk_label_new(_("Lines"));
    gtk_widget_show(label);

    if (gui_nlines > 4) {
	GtkWidget *scroller;
	
	scroller = gtk_scrolled_window_new(NULL, NULL);
	gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scroller),
				       GTK_POLICY_AUTOMATIC, 
				       GTK_POLICY_AUTOMATIC);
	gtk_widget_show(scroller);
	gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(scroller), 
					      vbox);
	gtk_notebook_append_page(GTK_NOTEBOOK(notebook), scroller, label); 
    } else {
	gtk_notebook_append_page(GTK_NOTEBOOK(notebook), vbox, label); 
    }  

    tbl_len = 1;
    tbl = gp_dialog_table(tbl_len, 3, vbox);
    gtk_widget_show(tbl);
   
    tbl_num = tbl_col = 0;

    for (i=0; i<gui_nlines; i++) {
	/* identifier and key or legend text */
	tbl_len++;
	gtk_table_resize(GTK_TABLE(tbl), tbl_len, 3);
	sprintf(label_text, _("line %d: "), i + 1);
	label = gtk_label_new(label_text);
	gtk_misc_set_alignment(GTK_MISC(label), 1, 0.5);
	gtk_table_attach_defaults(GTK_TABLE(tbl), 
				  label, 0, 1, tbl_len-1, tbl_len);
	gtk_widget_show(label);

	label = gtk_label_new(_("legend"));
	gtk_misc_set_alignment(GTK_MISC(label), 1, 0.5);
	gtk_table_attach_defaults(GTK_TABLE(tbl), 
				  label, 1, 2, tbl_len-1, tbl_len);
	gtk_widget_show(label);

	linetitle[i] = gtk_entry_new();
	gtk_table_attach_defaults(GTK_TABLE(tbl), 
				  linetitle[i], 2, 3, tbl_len-1, tbl_len);

	strip_lr(spec->lines[i].title);
	gp_string_to_entry(linetitle[i], spec->lines[i].title);

	g_signal_connect(G_OBJECT(linetitle[i]), "changed", 
			 G_CALLBACK(linetitle_callback), 
			 spec);
	g_signal_connect(G_OBJECT(linetitle[i]), "activate", 
			 G_CALLBACK(apply_gpt_changes), 
			 spec);
	gtk_widget_show(linetitle[i]);

	/* line type or style */
	tbl_len++;
	gtk_table_resize(GTK_TABLE(tbl), tbl_len, 3);
	label = gtk_label_new(_("type"));
	gtk_misc_set_alignment(GTK_MISC(label), 1, 0.5);
	gtk_table_attach_defaults(GTK_TABLE(tbl), 
				  label, 1, 2, tbl_len-1, tbl_len);
	gtk_widget_show(label);

	stylecombo[i] = gtk_combo_box_entry_new_text();
	gtk_table_attach_defaults(GTK_TABLE(tbl), 
				  stylecombo[i], 2, 3, tbl_len-1, tbl_len);

	/* the errorbars and filledcurves styles are not exchangeable
	   with the others */
	if (!strcmp(spec->lines[i].style, "errorbars") &&
	    !gnuplot_has_style_fill()) {
	    set_combo_box_default_text(GTK_COMBO_BOX(stylecombo[i]), 
				       spec->lines[i].style); 
	    gtk_widget_set_sensitive(stylecombo[i], FALSE);
	} else if (!strcmp(spec->lines[i].style, "errorbars") ||
		   !strcmp(spec->lines[i].style, "filledcurve")) {
	    GList *altsty = NULL;

	    altsty = g_list_append(altsty, "errorbars"); 
	    altsty = g_list_append(altsty, "filledcurve");
	    set_combo_box_strings_from_list(GTK_COMBO_BOX(stylecombo[i]), altsty);
	    set_combo_box_default_text(GTK_COMBO_BOX(stylecombo[i]), 
				       spec->lines[i].style); 
	    g_list_free(altsty);
	} else {
	    set_combo_box_strings_from_list(GTK_COMBO_BOX(stylecombo[i]), stylist);
	    set_combo_box_default_text(GTK_COMBO_BOX(stylecombo[i]), 
				       spec->lines[i].style); 
	} 

	gtk_widget_show(stylecombo[i]);	

	if (!do_scale_axis) {
	    linescale[i] = NULL;
	    yaxiscombo[i] = NULL;
	} else {
	    /* scale factor for data? */
	    tbl_len++;
	    gtk_table_resize(GTK_TABLE(tbl), tbl_len, 3);
	    label = gtk_label_new(_("scale"));
	    gtk_misc_set_alignment(GTK_MISC(label), 1, 0.5);
	    gtk_table_attach_defaults(GTK_TABLE(tbl), 
				      label, 1, 2, tbl_len-1, tbl_len);
	    gtk_widget_show(label);

	    linescale[i] = gtk_entry_new();
	    gtk_entry_set_max_length(GTK_ENTRY(linescale[i]), 6);
	    gtk_entry_set_text(GTK_ENTRY(linescale[i]), spec->lines[i].scale);
	    gtk_entry_set_width_chars(GTK_ENTRY(linescale[i]), 6);
	    g_signal_connect(G_OBJECT(linescale[i]), "activate", 
			     G_CALLBACK(apply_gpt_changes), 
			     spec);
	    gtk_table_attach_defaults(GTK_TABLE(tbl), 
				      linescale[i], 2, 3, tbl_len-1, tbl_len);
	    gtk_widget_show(linescale[i]);

	    /* use left or right y axis? */
	    tbl_len++;
	    gtk_table_resize(GTK_TABLE(tbl), tbl_len, 3);
	    label = gtk_label_new(_("y axis"));
	    gtk_misc_set_alignment(GTK_MISC(label), 1, 0.5);
	    gtk_table_attach_defaults(GTK_TABLE(tbl), 
				      label, 1, 2, tbl_len-1, tbl_len);
	    gtk_widget_show(label);

	    yaxiscombo[i] = gtk_combo_box_new_text();
	    gtk_table_attach_defaults(GTK_TABLE(tbl), 
				      yaxiscombo[i], 2, 3, tbl_len-1, tbl_len);
	    gtk_combo_box_append_text(GTK_COMBO_BOX(yaxiscombo[i]), "left");
	    gtk_combo_box_append_text(GTK_COMBO_BOX(yaxiscombo[i]), "right");
	    gtk_combo_box_set_active(GTK_COMBO_BOX(yaxiscombo[i]), 
				     (spec->lines[i].yaxis == 1)? 0 : 1);
	    gtk_widget_show(yaxiscombo[i]);
	}

	/* line-width adjustment */
	tbl_len++;
	gtk_table_resize(GTK_TABLE(tbl), tbl_len, 3);
	label = gtk_label_new(_("line width"));
	gtk_misc_set_alignment(GTK_MISC(label), 1, 0.5);
	gtk_table_attach_defaults(GTK_TABLE(tbl), 
				  label, 1, 2, tbl_len-1, tbl_len);
	gtk_widget_show(label);

	hbox = gtk_hbox_new(FALSE, 5);
	linewidth[i] = gtk_spin_button_new_with_range(1, 6, 1);
	if (spec->lines[i].width > 1) {
	    gtk_spin_button_set_value(GTK_SPIN_BUTTON(linewidth[i]),
				      spec->lines[i].width);
	}
	g_signal_connect(G_OBJECT(linewidth[i]), "activate", 
			 G_CALLBACK(apply_gpt_changes), 
			 spec);
	gtk_box_pack_start(GTK_BOX(hbox), linewidth[i], FALSE, FALSE, 0);
	gtk_widget_show(linewidth[i]);
	gtk_table_attach_defaults(GTK_TABLE(tbl), hbox, 2, 3, 
				  tbl_len-1, tbl_len);
	gtk_widget_show(hbox);
    }

    g_list_free(stylist);
}

static void label_pos_to_entry (double *pos, GtkWidget *w)
{
    if (!na(pos[0]) && !na(pos[1])) {
	gchar *s = g_strdup_printf("%g %g", pos[0], pos[1]);

	gtk_entry_set_text(GTK_ENTRY(w), s);
	g_free(s);
    } else {
	gtk_entry_set_text(GTK_ENTRY(w), "");
    }
}

static void gpt_tab_labels (GtkWidget *notebook, GPT_SPEC *spec) 
{
    GtkWidget *label, *vbox, *tbl;
    int i, j, tbl_len, tbl_num, tbl_col;
    char label_text[32];
    png_plot *plot = (png_plot *) spec->ptr;

    vbox = gp_page_vbox(notebook, _("Labels"));
 
    tbl_len = 1;
    tbl = gp_dialog_table(tbl_len, 3, vbox);
    gtk_widget_show(tbl);
   
    tbl_num = tbl_col = 0;

    for (i=0; i<MAX_PLOT_LABELS; i++) {
	GtkWidget *hbox, *button, *image;
	GdkPixbuf *icon;

	/* label text */
	tbl_len++;
	gtk_table_resize(GTK_TABLE(tbl), tbl_len, 3);
	sprintf(label_text, _("label %d: "), i + 1);
	label = gtk_label_new(label_text);
	gtk_misc_set_alignment(GTK_MISC(label), 1, 0.5);
	gtk_table_attach_defaults(GTK_TABLE(tbl), 
				  label, 0, 1, tbl_len-1, tbl_len);
	gtk_widget_show(label);

	label = gtk_label_new(_("text"));
	gtk_misc_set_alignment(GTK_MISC(label), 1, 0.5);
	gtk_table_attach_defaults(GTK_TABLE(tbl), 
				  label, 1, 2, tbl_len-1, tbl_len);
	gtk_widget_show(label);

	labeltext[i] = gtk_entry_new();
	gtk_entry_set_max_length(GTK_ENTRY(labeltext[i]), PLOT_LABEL_TEXT_LEN);
	gp_string_to_entry(labeltext[i], spec->labels[i].text);
	gtk_entry_set_width_chars(GTK_ENTRY(labeltext[i]), PLOT_LABEL_TEXT_LEN);
	g_signal_connect (G_OBJECT(labeltext[i]), "activate", 
			  G_CALLBACK(apply_gpt_changes), 
			  spec);
	gtk_table_attach_defaults(GTK_TABLE(tbl), 
				  labeltext[i], 2, 3, tbl_len-1, tbl_len);
	gtk_widget_show(labeltext[i]);

	/* label placement */
	tbl_len++;

	gtk_table_resize(GTK_TABLE(tbl), tbl_len, 3);
	label = gtk_label_new(_("position (X Y)"));
	gtk_misc_set_alignment(GTK_MISC(label), 1, 0.5);
	gtk_table_attach_defaults(GTK_TABLE(tbl), 
				  label, 1, 2, tbl_len-1, tbl_len);
	gtk_widget_show(label);

	/* holder for entry and button */
	hbox = gtk_hbox_new(FALSE, 5);

	/* entry for coordinates */
	labelpos[i] = gtk_entry_new();
	gtk_entry_set_max_length(GTK_ENTRY(labelpos[i]), PLOT_LABEL_POS_LEN);
	label_pos_to_entry(spec->labels[i].pos, labelpos[i]);
	gtk_entry_set_width_chars(GTK_ENTRY(labelpos[i]), PLOT_LABEL_POS_LEN);
	g_signal_connect(G_OBJECT(labelpos[i]), "activate", 
			 G_CALLBACK(apply_gpt_changes), 
			 spec);
	gtk_container_add(GTK_CONTAINER(hbox), labelpos[i]);
	gtk_widget_show(labelpos[i]);

	if (plot_is_mouseable(plot)) {
	    /* button to invoke mouse-assisted placement */
	    button = gtk_button_new();
	    g_object_set_data(G_OBJECT(button), "labelpos_entry", labelpos[i]);
	    g_signal_connect(G_OBJECT(button), "clicked",
			     G_CALLBACK(plot_label_position_click), spec->ptr);
	    icon = gdk_pixbuf_new_from_xpm_data((const char **) mini_mouse_xpm);
	    image = gtk_image_new_from_pixbuf(icon);
	    gtk_widget_set_size_request(button, 32, 26);
	    gtk_container_add (GTK_CONTAINER(button), image);
	    gtk_container_add(GTK_CONTAINER(hbox), button);
	    gtk_widget_show_all(button);
	}

	gtk_table_attach_defaults(GTK_TABLE(tbl), 
				  hbox, 2, 3, tbl_len-1, tbl_len);
	gtk_widget_show(hbox);

	/* label justification */
	tbl_len++;
	gtk_table_resize(GTK_TABLE(tbl), tbl_len, 3);
	label = gtk_label_new(_("justification"));
	gtk_misc_set_alignment(GTK_MISC(label), 1, 0.5);
	gtk_table_attach_defaults(GTK_TABLE(tbl), 
				  label, 1, 2, tbl_len-1, tbl_len);
	gtk_widget_show(label);

	labeljust[i] = gtk_combo_box_new_text();
	for (j=0; j<3; j++) {
	    gtk_combo_box_append_text(GTK_COMBO_BOX(labeljust[i]),
				      gp_justification_string(j));
	}
	gtk_combo_box_set_active(GTK_COMBO_BOX(labeljust[i]), 
				 spec->labels[i].just);
	gtk_table_attach_defaults(GTK_TABLE(tbl), 
				  labeljust[i], 2, 3, tbl_len-1, tbl_len);
	gtk_widget_show_all(labeljust[i]);	
    }
}

static void gpt_tab_XY (GtkWidget *notebook, GPT_SPEC *spec, gint axis) 
{
    png_plot *plot = (png_plot *) spec->ptr;
    GtkWidget *b1, *b2, *entry, *vbox, *tbl;
    GtkWidget *label = NULL;
    char *labelstr = NULL;
    int i, tbl_len;
   
    if (axis == 0) {
	labelstr = _("X-axis");
    } else if (axis == 1) {
	labelstr = _("Y-axis");
    } else if (axis == 2) {
	labelstr = _("Y2-axis");
    } else {
	return;
    }

    vbox = gp_page_vbox(notebook, labelstr);

    tbl_len = 1;
    tbl = gp_dialog_table(tbl_len, 2, vbox);
    gtk_widget_show(tbl);
   
    for (i=0; i<NTITLES; i++) {
	if (gpt_titles[i].tab == 1 + axis) {
	    GtkWidget *title_entry;

	    tbl_len++;
	    gtk_table_resize(GTK_TABLE(tbl), tbl_len, 2);
            
	    label = gtk_label_new(_(gpt_titles[i].description));
	    gtk_misc_set_alignment(GTK_MISC(label), 1, 0.5);
	    gtk_table_attach_defaults(GTK_TABLE(tbl), 
				      label, 0, 1, tbl_len-1, tbl_len);
	    gtk_widget_show(label);

	    title_entry = gtk_entry_new();
	    gtk_table_attach_defaults(GTK_TABLE(tbl), 
				      title_entry, 1, 2, 
				      tbl_len-1, tbl_len);
	    gp_string_to_entry(title_entry, spec->titles[i]);

	    g_signal_connect(G_OBJECT(title_entry), "activate", 
			     G_CALLBACK(apply_gpt_changes), 
			     spec);

	    gtk_widget_show(title_entry);
	    gpt_titles[i].widget = title_entry;
	}
    } 

    if (spec->code != PLOT_REGULAR) return;

    axis_range[axis].ID = axis;

    /* axis range: "auto" button */
    tbl_len += 3;
    gtk_table_resize(GTK_TABLE(tbl), tbl_len, 2);

    label = gtk_label_new("");
    gtk_table_attach_defaults(GTK_TABLE(tbl), label, 0, 1, 
			      tbl_len-3, tbl_len-2);
    gtk_widget_show(label);
    b1 = gtk_radio_button_new_with_label(NULL, _("auto axis range"));
    g_signal_connect(G_OBJECT(b1), "clicked",
		     G_CALLBACK(flip_manual_range), 
		     GINT_TO_POINTER(axis));
    gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(b1), TRUE);
    gtk_table_attach_defaults(GTK_TABLE(tbl), b1, 0, 1, 
			      tbl_len-2, tbl_len-1);
    gtk_widget_show(b1);
    axis_range[axis].isauto = b1;

    /* axis range: manual range button */
    b2 = gtk_radio_button_new_with_label(gtk_radio_button_get_group 
					 (GTK_RADIO_BUTTON(b1)),
					 _("manual range:")); 
    g_signal_connect(G_OBJECT(b2), "clicked",
		     G_CALLBACK(flip_manual_range), 
		     GINT_TO_POINTER(axis));
    gtk_table_attach_defaults(GTK_TABLE(tbl), b2, 0, 1, 
			      tbl_len-1, tbl_len);
    gtk_widget_show(b2);

    /* axis range min. entry */
    tbl_len++;
    label = gtk_label_new(_("minimum"));
    gtk_table_attach_defaults(GTK_TABLE(tbl), 
			      label, 0, 1, tbl_len-1, tbl_len);
    gtk_widget_show(label);
    gtk_table_resize(GTK_TABLE(tbl), tbl_len, 2);
    entry = gtk_entry_new();
    gtk_table_attach_defaults(GTK_TABLE(tbl), entry, 1, 2, 
			      tbl_len-1, tbl_len);
    gtk_entry_set_text(GTK_ENTRY(entry), "");
    g_signal_connect(G_OBJECT(entry), "activate", 
		     G_CALLBACK(apply_gpt_changes), 
		     spec);
    gtk_widget_show(entry);
    axis_range[axis].min = entry;

    /* axis range max. entry */
    tbl_len++;
    label = gtk_label_new(_("maximum"));
    gtk_table_attach_defaults(GTK_TABLE(tbl), label, 0, 1, 
			      tbl_len-1, tbl_len);
    gtk_widget_show(label);
    gtk_table_resize(GTK_TABLE(tbl), tbl_len, 2);
    entry = gtk_entry_new();
    gtk_table_attach_defaults(GTK_TABLE(tbl), entry, 1, 2, 
			      tbl_len-1, tbl_len);
    gtk_entry_set_text(GTK_ENTRY(entry), "");
    g_signal_connect(G_OBJECT(entry), "activate", 
		     G_CALLBACK(apply_gpt_changes), 
		     spec);
    gtk_widget_show(entry);
    axis_range[axis].max = entry;

    if (na(spec->range[axis][0])) {
	flip_manual_range(NULL, GINT_TO_POINTER(axis));
    } else {
	double_to_gp_entry(spec->range[axis][0], axis_range[axis].min);
	double_to_gp_entry(spec->range[axis][1], axis_range[axis].max);
	gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(axis_range[axis].isauto), 
				     FALSE);
	gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(b2), TRUE);
    }

    /* axis scale: linear vs log? */

    if ((axis == 0 && plot_get_xmin(plot) >= 0.0) ||
	(axis == 1 && plot_get_ymin(plot) >= 0.0)) {
	GtkWidget *combo;
	GList *strs = NULL;

	strs = g_list_append(strs, "e");
	strs = g_list_append(strs, "2");
	strs = g_list_append(strs, "10");

	tbl_len += 3;
	gtk_table_resize(GTK_TABLE(tbl), tbl_len, 2);

	label = gtk_label_new("");
	gtk_table_attach_defaults(GTK_TABLE(tbl), label, 0, 1, 
				  tbl_len-3, tbl_len-2);
	gtk_widget_show(label);
	b1 = gtk_radio_button_new_with_label(NULL, _("linear scale"));
	gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(b1), TRUE);
	gtk_table_attach_defaults(GTK_TABLE(tbl), b1, 0, 1, 
				  tbl_len-2, tbl_len-1);
	gtk_widget_show(b1);

	b2 = gtk_radio_button_new_with_label(gtk_radio_button_get_group 
					     (GTK_RADIO_BUTTON(b1)),
					     _("logarithmic scale, base:")); 
	gtk_table_attach_defaults(GTK_TABLE(tbl), b2, 0, 1, 
				  tbl_len-1, tbl_len);
	gtk_widget_show(b2);    

	combo = gtk_combo_box_entry_new_text();
	set_combo_box_strings_from_list(GTK_COMBO_BOX(combo), strs);
	g_list_free(strs);
	gtk_table_attach_defaults(GTK_TABLE(tbl), combo, 1, 2, 
				  tbl_len-1, tbl_len);
	entry = gtk_bin_get_child(GTK_BIN(combo));
	g_signal_connect(G_OBJECT(entry), "activate", 
			 G_CALLBACK(apply_gpt_changes), 
			 spec);
	gtk_widget_show(combo);
	axis_range[axis].lbase = entry;

	if (spec->logbase[axis] > 1.1) {
	    gchar *txt = g_strdup_printf("%g", spec->logbase[axis]);

	    gtk_entry_set_text(GTK_ENTRY(entry), txt);
	    g_free(txt);
	    gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(b2), TRUE);
	} else {
	    gtk_widget_set_sensitive(entry, FALSE);
	}

	g_signal_connect(G_OBJECT(b1), "clicked", G_CALLBACK(disable_lbase), 
			 entry);
	g_signal_connect(G_OBJECT(b2), "clicked", G_CALLBACK(enable_lbase), 
			 entry);
    }
}

void close_gnuplot_dialog (GtkWidget *w, gpointer p)
{
    free(linetitle);
    free(stylecombo);
    free(yaxiscombo);
    free(linescale);
    free(linewidth);

    linetitle = NULL;
    stylecombo = NULL;
    yaxiscombo = NULL;
    linescale = NULL;
    linewidth = NULL;

    gtk_widget_destroy(GTK_WIDGET(p));
}

static int gpt_allocate_widgets (int n)
{
    int i;

    if (n == 0) {
	return 0;
    }

    linetitle = malloc(n * sizeof *linetitle);
    stylecombo = malloc(n * sizeof *stylecombo);
    yaxiscombo = malloc(n * sizeof *yaxiscombo);
    linescale = malloc(n * sizeof *linescale);
    linewidth = malloc(n * sizeof *linewidth);
    
    if (linetitle == NULL || stylecombo == NULL ||
	yaxiscombo == NULL || linescale == NULL ||
	linewidth == NULL) {
	return E_ALLOC;
    }

    for (i=0; i<n; i++) {
	linetitle[i] = NULL;
	stylecombo[i] = NULL;
	yaxiscombo[i] = NULL;
	linescale[i] = NULL;
	linewidth[i] = NULL;
    }

    gui_nlines = n;

    return 0;
}

int show_gnuplot_dialog (GPT_SPEC *spec) 
{
    png_plot *plot = (png_plot *) spec->ptr;
    GtkWidget *button, *notebook;
    GtkWidget *hbox;
    int i;

    if (gpt_control != NULL) {
	errbox(_("You can only have one plot controller open\n"
		 "at any given time"));
	return 1;
    }

    if (gpt_allocate_widgets(spec->n_lines)) {
	nomem();
	return 1;
    }

    for (i=0; i<MAX_AXES; i++) {
	axis_range[i].isauto = NULL;
	axis_range[i].lbase = NULL;
    }

    for (i=0; i<NTITLES; i++) {
	gpt_titles[i].widget = NULL;
    }

    gpt_control = gretl_dialog_new(_("gretl plot controls"), NULL, 0);
    gtk_box_set_spacing(GTK_BOX(GTK_DIALOG(gpt_control)->vbox), 2);
    gtk_dialog_set_has_separator(GTK_DIALOG(gpt_control), FALSE);

    if (plot != NULL) {
	gtk_window_set_transient_for(GTK_WINDOW(gpt_control), 
				     GTK_WINDOW(plot_get_shell(plot)));
    }

    g_signal_connect(G_OBJECT(gpt_control), "destroy",
		     G_CALLBACK(close_plot_controller), 
		     (gpointer *) spec);
   
    notebook = gtk_notebook_new();
    gtk_box_pack_start(GTK_BOX(GTK_DIALOG(gpt_control)->vbox), 
		       notebook, TRUE, TRUE, 0);
    gtk_widget_show(notebook);

    gpt_tab_main(notebook, spec);
    gpt_tab_XY(notebook, spec, 0);
    gpt_tab_XY(notebook, spec, 1);

    if (spec->flags & GPT_Y2AXIS) {
	gpt_tab_XY(notebook, spec, 2);
    }

    if (spec->lines != NULL) {
	gpt_tab_lines(notebook, spec);
    }

    gpt_tab_labels(notebook, spec); 

    hbox = GTK_DIALOG(gpt_control)->action_area;

    /* "Apply" button */
    button = apply_button(hbox);
    g_signal_connect(G_OBJECT(button), "clicked", 
		     G_CALLBACK(apply_gpt_changes), spec);
    gtk_widget_grab_default(button);
    gtk_widget_show(button);

    /* "OK" button (apply and close) */
    button = ok_button(hbox);
    g_signal_connect(G_OBJECT(button), "clicked", 
		     G_CALLBACK(apply_gpt_changes), spec);
    g_signal_connect(G_OBJECT(button), "clicked",
		     G_CALLBACK(close_gnuplot_dialog), gpt_control);
    gtk_widget_show(button);

    /* Close button (do not apply changes) */
    button = gtk_button_new_from_stock(GTK_STOCK_CLOSE);
    GTK_WIDGET_SET_FLAGS(button, GTK_CAN_DEFAULT);
    gtk_container_add(GTK_CONTAINER(hbox), button);
    g_signal_connect(G_OBJECT(button), "clicked",
		     G_CALLBACK(close_gnuplot_dialog), gpt_control);
    gtk_widget_show(button);

    /* Help button */
    context_help_button(hbox, GR_PLOT);

    set_keyspec_sensitivity(spec);

    gtk_widget_show(gpt_control);

    return 0;
}

void raise_gpt_control_window (void)
{
    if (gpt_control != NULL) {
	gdk_window_raise(gpt_control->window);
    }
}

void destroy_gpt_control_window (void)
{
    gtk_widget_destroy(gpt_control);
}

