/*
 * FIG : Facility for Interactive Generation of figures
 * Copyright (c) 1985-1988 by Supoj Sutanthavibul
 * Parts Copyright (c) 1989-2015 by Brian V. Smith
 * Parts Copyright (c) 1991 by Paul King
 * Parts Copyright (c) 2016-2024 by Thomas Loimer
 *
 * Any party obtaining a copy of these files is granted, free of charge, a
 * full and unrestricted irrevocable, world-wide, paid up, royalty-free,
 * nonexclusive right and license to deal in this software and documentation
 * files (the "Software"), including without limitation the rights to use,
 * copy, modify, merge, publish, distribute, sublicense and/or sell copies of
 * the Software, and to permit persons who receive copies from any such
 * party to do so, with the only requirement being that the above copyright
 * and this permission notice remain intact.
 *
 */

#ifdef HAVE_CONFIG_H
#include "config.h"		/* intptr_t */
#endif
#include "w_file.h"

#include <errno.h>
#include <limits.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <X11/Intrinsic.h>     /* includes X11/Xlib.h, which includes X11/X.h */
#include <X11/Shell.h>
#include <X11/StringDefs.h>
#include <X11/Xatom.h>		/* XA_STRING */
#include <X11/Xft/Xft.h>

#include "figx.h"
#include "resources.h"
#include "object.h"
#include "mode.h"
#include "paintop.h"
#include "e_compound.h"
#include "f_load.h"
#include "f_read.h"
#include "f_save.h"
#include "f_util.h"
#include "u_colors.h"
#include "u_create.h"
#include "u_free.h"
#include "u_list.h"
#include "u_redraw.h"
#include "w_canvas.h"
#include "w_cmdpanel.h"
#include "w_color.h"
#include "w_cursor.h"
#include "w_drawprim.h"		/* for max_char_height */
#include "w_dir.h"
#include "w_export.h"
#include "w_layers.h"
#include "w_msgpanel.h"
#include "w_setup.h"
#include "w_util.h"
#include "w_zoom.h"
#include "xfig_math.h"

#define	FILE_WID	157	/* width of Current file etc to show preview to the right */
#define FILE_ALT_WID	260	/* width of file alternatives panel */
#define PREVIEW_CANVAS_W 232	/* width of landscape preview canvas (swap W, H for portrait) */
#define PREVIEW_CANVAS_H 180	/* height of landscape preview */

/* file modes for popup panel */

enum file_modes {
	F_SAVEAS,
	F_OPEN,
	F_MERGE
};

/* EXPORTS */

Boolean		file_up = False;	/* whether the file panel is up (or the export panel) */
Widget		file_panel;		/* the file panel itself */
Widget		cfile_text;		/* widget for the current filename */
Widget		file_selfile,		/* selected file widget */
		file_mask,		/* mask widget */
		file_dir,		/* current directory widget */
		file_flist,		/* file list widget */
		file_dlist;		/* dir list widget */
Widget		file_popup;
Boolean		colors_are_swapped = False;
Widget		preview_size, preview_widget, preview_widget_form, preview_name;
Widget		preview_stop, preview_label, dummy_label;
Widget		comments_widget;
Boolean		cancel_preview = False;
Boolean		preview_in_progress = False;
void		load_request(Widget w, XButtonEvent *ev); /* needed by main() */

/* LOCALS */

static Boolean	file_cancel_request = False;
static Boolean	file_load_request = False;
static Boolean	file_save_request = False;
static Boolean	file_merge_request = False;
static char	save_file_dir[PATH_MAX];

static Boolean	user_colors_saved = False;
static XftColor	saved_user_colors[MAX_USR_COLS];
static Boolean	saved_userFree[MAX_USR_COLS];
static int	saved_user_num;

static Boolean	nuser_colors_saved = False;
static XftColor	saved_nuser_colors[MAX_USR_COLS];
static Boolean	saved_nuserFree[MAX_USR_COLS];
static int	saved_nuser_num;
static Pixmap	preview_land_pixmap, preview_port_pixmap;


static void	file_preview_stop(Widget w, XButtonEvent *ev);

/* these are in fig units */
static float	offset_unit_conv[] = {
	(float)PIX_PER_INCH,
	(float)PIX_PER_CM,
	1.0
};

static char	*load_msg = "The current figure is modified.\nDo you want to discard it and load the new file?";
static char	buf[40];

/* to save image colors when doing a figure preview */

static XftColor	save_image_cells[MAX_COLORMAP_SIZE];
static int	save_avail_image_cols;
static Boolean	image_colors_are_saved = False;

static void	popup_file_panel(int mode);
static void	file_panel_cancel(Widget w, XButtonEvent *ev);
static void	do_load(Widget w, XButtonEvent *ev), do_merge(Widget w, XButtonEvent *ev);
static void	merge_request(Widget w, XButtonEvent *ev), cancel_request(Widget w, XButtonEvent *ev), save_request(Widget w, XButtonEvent *ev);
static void	clear_preview(void);

DeclareStaticArgs(15);
static Widget	file_stat_label, file_status, num_obj_label, num_objects;
static Widget	figure_off, cfile_lab;
static Widget	cancel_button, save_button, merge_button;
static Widget	load_button, new_xfig_button;
static Widget	fig_offset_lbl_x, fig_offset_lbl_y;
static Widget	fig_offset_x, fig_offset_y;
static Widget	file_xoff_unit_panel;
static Widget	file_yoff_unit_panel;
static intptr_t	xoff_unit_setting, yoff_unit_setting;

static String	file_list_load_translations =
			"<Btn1Down>,<Btn1Up>: Set()Notify()\n\
			<Btn1Up>(2): DoLoad()\n\
			<Key>Return: DoLoad()";
static String	file_name_load_translations =
			"<Key>Return: DoLoad()\n\
			<Key>Escape: CancelFile()";
static String	file_list_saveas_translations =
			"<Btn1Down>,<Btn1Up>: Set()Notify()\n\
			<Btn1Up>(2): SaveRequest()\n\
			<Key>Return: DoSave()\n\
			<Key>Escape: CancelFile()";
static String	file_name_saveas_translations =
			"<Key>Return: DoSave()\n\
			<Key>Escape: CancelFile()";
static String	file_list_merge_translations =
			"<Btn1Down>,<Btn1Up>: Set()Notify()\n\
			<Btn1Up>(2): DoMerge()\n\
			<Key>Return: DoMerge()";
static String	file_name_merge_translations =
			"<Key>Return: DoMerge()\n\
			<Key>Escape: CancelFile()";

static XtActionsRec	file_name_actions[] =
{
    {"DoLoad", (XtActionProc) load_request},
    {"SaveRequest", (XtActionProc) save_request},
    {"DoSave", (XtActionProc) do_save},
    {"DoMerge", (XtActionProc) merge_request},
    {"Load", (XtActionProc) popup_open_panel},
    {"Merge", (XtActionProc) popup_merge_panel},
};
static String	file_translations =
	"<Message>WM_PROTOCOLS: DismissFile()";
static XtActionsRec	file_actions[] =
{
    {"DismissFile", (XtActionProc) cancel_request},
    {"CancelFile", (XtActionProc) cancel_request},
    {"DoLoad", (XtActionProc) load_request},
    {"DoMerge", (XtActionProc) merge_request},
};


int renamefile (char *file);
static void create_file_panel (void);

void
file_panel_dismiss(void)
{
    int i;

    /* this function is only called from the Open, Save as and Merge panels */
    if (save_file_dir[0] != '\0') {
	update_file_export_dir(save_file_dir);
	change_directory(save_file_dir);
    }

    if (image_colors_are_saved) {
	/* restore image colors on canvas */
	avail_image_cols = save_avail_image_cols;
	for (i=0; i<avail_image_cols; i++) {
	    image_cells[i].color.red   = save_image_cells[i].color.red;
	    image_cells[i].color.green = save_image_cells[i].color.green;
	    image_cells[i].color.blue  = save_image_cells[i].color.blue;
        }
	alloc_or_store_colors(image_cells, avail_image_cols);
	image_colors_are_saved = False;
    }
    FirstArg(XtNstring, "\0");
    SetValues(file_selfile);	/* clear Filename string */
    XtPopdown(file_popup);
    file_up = popup_up = False;
    /* in case the colormap was switched while previewing */
    redisplay_canvas();
}

/* get x/y offsets from panel */

void file_getxyoff(int *ixoff, int *iyoff)
{
    float xoff, yoff;
    *ixoff = *iyoff = 0;
    /* if no file panel yet, use 0, 0 for offsets */
    if (fig_offset_x == (Widget) 0 ||
	fig_offset_y == (Widget) 0)
	    return;

    sscanf(panel_get_value(fig_offset_x),"%f",&xoff);
    *ixoff = round(xoff*offset_unit_conv[xoff_unit_setting]);
    sscanf(panel_get_value(fig_offset_y),"%f",&yoff);
    *iyoff = round(yoff*offset_unit_conv[yoff_unit_setting]);
}

static void
merge_request(Widget w, XButtonEvent *ev)
{
    if (preview_in_progress) {
	file_merge_request = True;
	/* request to cancel the preview */
	cancel_preview = True;
    } else {
	/* make sure this is false from any previous previewing */
	cancel_preview = False;
	do_merge(w, ev);
    }
}

static void
do_merge(Widget w, XButtonEvent *ev)
{
	(void)w;
	(void)ev;
    char	    path[PATH_MAX], fname[PATH_MAX];
    char	   *fval, *dval;
    int		    xoff, yoff;

    FirstArg(XtNstring, &fval);
    GetValues(file_selfile);	/* check the ascii widget for a filename */
    strcpy(fname, fval);
    if (emptyname(fname))		/* "Filename" widget empty, */
	strcpy(fname, cur_filename);	/*  use current filename */

    if (!strchr(fname,'.'))	/* if no suffix, add .fig */
	strcat(fname,".fig");

    if (emptyname_msg(fname, "MERGE"))
	return;

    FirstArg(XtNstring, &dval);
    GetValues(file_dir);

    strcpy(path, dval);
    strcat(path, "/");
    strcat(path, fname);
    file_getxyoff(&xoff,&yoff);	/* get x/y offsets from panel */
    /* if the user colors are saved we must swap current colors to save them for the preview */
    if (user_colors_saved) {
	swap_colors();
	/* get back the colors from the figure on the canvas */
	restore_user_colors();
	merge_file(path, xoff, yoff);
    } else {
	merge_file(path, xoff, yoff);
    }
    /* we have merged any image colors from the file with the canvas */
    image_colors_are_saved = False;
    /* dismiss the panel */
    file_panel_dismiss();
}

/*
   Set a flag requesting a load.  This is called by double clicking on a
   filename or by pressing the Open button.

   If we aren't in the middle of previewing a file, do the actual load.

   Otherwise just set a flag here because we are in the middle of a preview,
   since the first click on the filename started up preview_figure().

   We can't do the load yet because the canvas_win variable is occupied by
   the pixmap for the preview, and we need to change it back to the real canvas
   before we draw the figure we will load.
*/

void
load_request(Widget w, XButtonEvent *ev)
{
    if (preview_in_progress) {
	file_load_request = True;
	/* request to cancel the preview */
	cancel_preview = True;
    } else {
	/* make sure this is false from any previous previewing */
	cancel_preview = False;
	do_load(w, ev);
    }
}

static void
do_load(Widget w, XButtonEvent *ev)
{
	(void)w;
	(void)ev;
    char	    fname[PATH_MAX];
    char	   *fval, *dval;
    int		    xoff, yoff;

    /* don't load if in the middle of drawing/editing */
    if (check_action_on())
	return;

    /* restore any colors that were saved during a preview */
    restore_user_colors();
    restore_nuser_colors();

    /* first check if the figure was modified before reloading it */
    if (!emptyfigure() && figure_modified) {
	if (file_popup)
	    XtSetSensitive(load_button, False);
	if (popup_query(QUERY_YESCAN, load_msg) == RESULT_CANCEL) {
	    if (file_popup)
		XtSetSensitive(load_button, True);
	    return;
	}
    }
    /* if the user used a keyboard accelerator but the filename
       is empty, popup the file panel to force him/her to enter a name */
    if (emptyname(cur_filename) && !file_up) {
	put_msg("No filename, please enter name");
	beep();
	popup_open_panel();
	return;
    }
    if (file_popup) {
	app_flush();			/* make sure widget is updated (race condition) */
	FirstArg(XtNstring, &dval);
	GetValues(file_dir);
	FirstArg(XtNstring, &fval);
	GetValues(file_selfile);	/* check the ascii widget for a filename */
	if (emptyname(fval))
	    strcpy(fname, cur_filename); /* Filename widget empty, use current filename */
	else
	    strcpy(fname, fval);	/* get name from widget */

	if (!strchr(fname,'.'))		/* if no suffix, add .fig */
	    strcat(fname,".fig");

	if (emptyname_msg(fname, "LOAD"))
	    return;
	if (change_directory(dval) == 0) {
	    strcpy(save_file_dir, dval);
	    file_getxyoff(&xoff,&yoff);	/* get x/y offsets from panel */
	    if (load_file(fname, xoff, yoff) == 0) {
		FirstArg(XtNlabel, fname);
		SetValues(cfile_text);		/* set the current filename */
		update_def_filename();		/* update default export filename */
		XtSetSensitive(load_button, True);
		/* we have just loaded the image colors from the file */
		image_colors_are_saved = False;
		if (file_up)
		    file_panel_dismiss();
	    }
	}
    } else {
	file_getxyoff(&xoff,&yoff);
	(void) load_file(cur_filename, xoff, yoff);
    }
}

void
new_xfig_request(Widget w, XButtonEvent *ev)
{
	(void)w;
	(void)ev;
    pid_t pid;
    char *fval, fname[PATH_MAX];
    char **xxargv;
    int i, j;
    extern int xargc;
    extern char **xargv;
    extern char *arg_filename;

    FirstArg(XtNstring, &fval);
    GetValues(file_selfile);	/* check the ascii widget for a filename */
    strcpy(fname, fval);

    /* restore any colors that were saved during a preview */
    restore_user_colors();
    restore_nuser_colors();

    if (emptyname(fname))
	put_msg("Launching new xfig...");
    else {
	if (!strchr(fname,'.'))		/* if no suffix, add .fig */
	    strcat(fname,".fig");
	put_msg("Launching new xfig for file %s...", fname);
    }

    pid = fork();
    if (pid == -1) {
	file_msg("Couldn't fork the process: %s", strerror(errno));
    } else if (pid == 0) {
	xxargv = (char **)XtMalloc((xargc+2) * sizeof (char *));
	for (i = j = 0; i < xargc; i++) {
	    if (emptyname(arg_filename) || strcmp(xargv[i], arg_filename) != 0)
		xxargv[j++] = xargv[i];
	}
	if (!emptyname(fname))
	    xxargv[j++] = fname;
	xxargv[j] = NULL;
	execvp(xxargv[0], xxargv);

	fprintf(stderr, "xfig: couldn't exec %s: %s\n", xxargv[0], strerror(errno));
	exit(1);
    }

    file_panel_dismiss();
}

/* set a request to save.  See notes above for load_request() */

void
save_request(Widget w, XButtonEvent *ev)
{
    /* turn off Compose key LED */
    setCompLED(0);

    if (preview_in_progress) {
	file_save_request = True;
	/* request to cancel the preview */
	cancel_preview = True;
    } else {
	/* make sure this is false from any previous previewing */
	cancel_preview = False;
	do_save(w, ev);
    }
}

/*
 * The user might enter a file path containing directory components relative to
 * cur_file_dir, an absolute path, or a path relative to the home dir.
 * Set cur_file_dir to the directory containing the basename of the file.
 * If something goes wrong, continue with a probably wrong cur_file_dir.
 */
static void
set_curfiledir_from_savepath(const char *restrict fname)
{
	char	*b;	/* point to slash before basename component in fname */

	b = strrchr(fname, '/');
	if (!b && !(fname[0] == '~' && fname[1] == '/'))
		/* fname does not contain a directory component */
		return;

	if (fname[0] == '~' && fname[1] == '/') {
		char	*home = getenv("HOME");
		size_t	home_len = strlen(home);

		if (b - fname + home_len < PATH_MAX) {
			memcpy(cur_file_dir, home, home_len);
			memcpy(cur_file_dir + home_len, fname + 1,
					b - fname - 1);/* omit the last slash */
			cur_file_dir[b - fname + home_len - 1] = '\0';
		} else {
			goto warning;
		}
		return;
	} else if (fname[0] == '/') {
		if (b - fname + 1 < PATH_MAX) {
			memcpy(cur_file_dir, fname, b - fname);
			cur_file_dir[b - fname] = '\0';
		} else {
			goto warning;
		}
	} else { /*  to/rel/path/f.fig */
		size_t	cur_len = strlen(cur_file_dir);
		if (cur_len + b - fname + 2 < PATH_MAX) {
			cur_file_dir[cur_len] = '/';
			memcpy(cur_file_dir + cur_len + 1, fname, b - fname);
			cur_file_dir[cur_len + b - fname + 1] = '\0';
		} else {
			goto warning;
		}
	}
	return;

warning:
	file_msg("Could not change to directory of saved file. "
			"If %s contains pictures, incorrect picture file paths "
			"might be written to the saved file.");
}

void
do_save(Widget w, XButtonEvent *ev)
{
	(void)w;
	(void)ev;
    char	   *fval, *dval, fname[PATH_MAX];
    int		    qresult;

    /* don't save if in the middle of drawing/editing */
    if (check_action_on())
	return;

    if (emptyfigure_msg("Save"))
	return;

    /* restore any colors that were saved during a preview */
    restore_user_colors();
    restore_nuser_colors();

    /* if the user is inside any compound objects, ask whether to save all or just this part */
    if ((F_compound *)objects.parent != NULL) {
	qresult = popup_query(QUERY_ALLPARTCAN,
		"You have opened a compound. You may save just\nthe visible part or all of the figure.");
	if (qresult == RESULT_CANCEL)
		return;
	if (qresult == RESULT_ALL)
	    close_all_compounds();
    }

    /*
     * if the user used a keyboard accelerator or right button on File
     * but the filename is empty, popup the file panel to force him/her
     * to enter a name
     */
    if (emptyname(cur_filename) && !file_up) {
	put_msg("No filename, please enter name");
	beep();
	popup_saveas_panel();
	return;
    }
    if (file_popup) {
	FirstArg(XtNstring, &fval);
	GetValues(file_selfile);    /* check the ascii widget for a filename */
	strcpy(fname, fval);
	if (emptyname(fname)) {
		/* "Filename" widget empty, use current filename */
	    strcpy(fname, cur_filename);
	    warnexist = False;		/* don't warn if this file exists */
	/* copy the name from the file_name widget to the current filename */
	} else {
		char	*b = strrchr(fname, '/');
		/* if no suffix in the basename part, add .fig */
	    if ((b && !strchr(b, '.')) || !strchr(fname, '.'))
		strcat(fname,".fig");
	    if (strcmp(cur_filename, fname) != 0)
		warnexist = True;	/* warn if this file exists */
	}

	if (emptyname_msg(fname, "Save"))
	    return;

	if (file_up) {
	    /* get the directory from the ascii widget */
	    FirstArg(XtNstring, &dval);
	    GetValues(file_dir);
	    strcpy(save_file_dir, dval);
	}

	/*
	 * Image paths are written to the .fig file as relative to cur_file_dir.
	 * The user might enter a file path containing directory components into
	 * the file_selfile widget. Update cur_file_dir accordingly.
	 */

	/* go to the file directory */
	if (change_directory(save_file_dir) == 0) {
		char	tmp_save_dir[PATH_MAX];
		char	abs_path_buf[1024] = "";
		char	*abs_path = abs_path_buf;

	    if (!ok_to_write(fname, "SAVE"))
		return;
	    XtSetSensitive(save_button, False);
	    if (appres.write_bak == True)
		(void) renamefile(fname);
	    strcpy(tmp_save_dir, cur_file_dir);
	    set_curfiledir_from_savepath(fname);
	    if (fname[0] == '~' && fname[1] == '/') {
		    char	*home = getenv("HOME");
		    size_t	home_len = strlen(home);
		    size_t	fname_len = strlen(fname) - 1;
		    if (home_len + fname_len + 1 > sizeof abs_path_buf &&
			    !(abs_path = new_string(home_len + fname_len)))
			    return;
		    memcpy(abs_path, home, home_len);
		    memcpy(abs_path + home_len, fname + 1, fname_len + 1);
	    }
	    if (write_file(*abs_path ? abs_path : fname, True) == 0) {
		FirstArg(XtNlabel, fname);
		SetValues(cfile_text);
		if (strcmp(fname, cur_filename) != 0) {
		    update_cur_filename(fname);	/* update cur_filename */
		}
		reset_modifiedflag();
		if (file_up)
		    file_panel_dismiss();
	    }
	    if (abs_path != abs_path_buf)
		    free(abs_path);
	    XtSetSensitive(save_button, True);
	    strcpy(cur_file_dir, tmp_save_dir);
	}
    } else { /* !file_popup */
	    char	tmp_save_dir[PATH_MAX];
	/*
	 * !file_popup, so the file panel was never created and probably
	 * save_file_dir not set. Also, the user did not have opportunity to
	 * enter "~/some.fig" as file path, no need to parse for "~".
	 */
	if (*save_file_dir == '\0') {
		strcpy(save_file_dir, cur_file_dir);
		change_directory(save_file_dir);
	}
	/* see if writable */
	if (!ok_to_write(cur_filename, "SAVE"))
	    return;
	/* not using popup => filename not changed so ok to write existing file */
	warnexist = False;
	(void) renamefile(cur_filename);
	strcpy(tmp_save_dir, cur_file_dir);
	set_curfiledir_from_savepath(fname);
	if (write_file(cur_filename, True) == 0)
	    reset_modifiedflag();
	strcpy(cur_file_dir, tmp_save_dir);
    }
}

/* try to rename current to current.bak */

int renamefile(char *file)
{
    char	    bak_name[PATH_MAX];

    strcpy(bak_name,file);
    strcat(bak_name,".bak");
    if (rename(file,bak_name) < 0)
	return (-1);
    return 0;
}

Boolean
query_save(char *msg)
{
    int		    qresult;
    if (!emptyfigure() && figure_modified && !aborting) {
	if ((qresult = popup_query(QUERY_YESNOCAN, msg)) == RESULT_CANCEL)
	    return False;
	else if (qresult == RESULT_YES) {
	    save_request((Widget) 0, (XButtonEvent *) 0);
	    /*
	     * if saving was not successful, figure_modified is still true:
	     * do not quit!
	     */
	    if (figure_modified)
		return False;
	}
    }
    /* ok */
    return True;
}

static void
cancel_request(Widget w, XButtonEvent *ev)
{
    if (preview_in_progress) {
	file_cancel_request = True;
	/* request to cancel the preview */
	cancel_preview = True;
    } else {
	/* make sure this is false from any previous previewing */
	cancel_preview = False;
	file_panel_cancel(w, ev);
    }
}

static void
file_panel_cancel(Widget w, XButtonEvent *ev)
{
	(void)w;
	(void)ev;
    if (user_colors_saved) {
	restore_user_colors();
	restore_nuser_colors();
	colors_are_swapped = 0;
    }
    if (colors_are_swapped)
	swap_colors();
    file_panel_dismiss();
}

void
popup_saveas_panel(void)
{
	popup_file_panel(F_SAVEAS);
}

void
popup_open_panel(void)
{
	popup_file_panel(F_OPEN);
}

void
popup_merge_panel(void)
{
	popup_file_panel(F_MERGE);
}

static void
popup_file_panel(int mode)
{
	DeclareArgs(5);

	/* turn off Compose key LED */
	setCompLED(0);

	/* already up? */
	if (file_up) {
	    /* yes, just raise to top */
	    XRaiseWindow(tool_d, XtWindow(file_popup));
	    return;
	}

	/* clear flags */
	colors_are_swapped = False;
	image_colors_are_saved = False;

	set_temp_cursor(wait_cursor);
	/* if the export panel is up get rid of it */
	if (export_up) {
	    export_up = False;
	    XtPopdown(export_popup);
	}
	file_up = popup_up = True;

	/* create it if it isn't already created */
	if (!file_popup) {
	    create_file_panel();
	} else {
	    FirstArg(XtNstring, cur_file_dir);
	    SetValues(file_dir);
	    Rescan(0, 0, 0, 0);
	}
	XtSetSensitive(figure_off, mode != F_SAVEAS);
	XtSetSensitive(fig_offset_lbl_x, mode != F_SAVEAS);
	XtSetSensitive(fig_offset_lbl_y, mode != F_SAVEAS);
	XtSetSensitive(fig_offset_x, mode != F_SAVEAS);
	XtSetSensitive(fig_offset_y, mode != F_SAVEAS);
	XtSetSensitive(file_xoff_unit_panel, mode != F_SAVEAS);
	XtSetSensitive(file_yoff_unit_panel, mode != F_SAVEAS);

	/* now add/remove appropriate buttons and set the title in the titlebar */
	if (mode == F_SAVEAS) {
	    XtUnmanageChild(load_button);
	    XtUnmanageChild(merge_button);
	    XtManageChild(save_button);
	    FirstArg(XtNtitle, "Xfig: SaveAs");
	    SetValues(file_popup);
	    /* make <return> in the filename window save the file */
	    XtOverrideTranslations(file_selfile,
			   XtParseTranslationTable(file_name_saveas_translations));
	    /* make <return> and a double click in the file list window save the file */
	    XtOverrideTranslations(file_flist,
			   XtParseTranslationTable(file_list_saveas_translations));

	} else if (mode == F_OPEN) {
	    XtUnmanageChild(save_button);
	    XtUnmanageChild(merge_button);
	    XtManageChild(load_button);
	    XtManageChild(new_xfig_button);
	    FirstArg(XtNtitle, "Xfig: Open Figure");
	    SetValues(file_popup);
	    /* make <return> in the filename window load the file */
	    XtOverrideTranslations(file_selfile,
			   XtParseTranslationTable(file_name_load_translations));
	    /* make <return> and a double click in the file list window load the file */
	    XtOverrideTranslations(file_flist,
			   XtParseTranslationTable(file_list_load_translations));

	} else { /* must be F_MERGE */
	    XtUnmanageChild(save_button);
	    XtUnmanageChild(load_button);
	    XtManageChild(merge_button);
	    FirstArg(XtNtitle, "Xfig: Merge Figure");
	    SetValues(file_popup);
	    /* make <return> in the filename window merge the file */
	    XtOverrideTranslations(file_selfile,
			   XtParseTranslationTable(file_name_merge_translations));
	    /* make <return> and a double click in the file list window merge the file */
	    XtOverrideTranslations(file_flist,
			   XtParseTranslationTable(file_list_merge_translations));

	}

	FirstArg(XtNlabel, (figure_modified ? "Modified    " :
					      "Not modified"));
	SetValues(file_status);
	sprintf(buf, "%d", object_count(&objects));
	FirstArg(XtNlabel, buf);
	SetValues(num_objects);
	XtPopup(file_popup, XtGrabNone);

	/* get rid of the dummy label if we haven't already */
	XtUnmanageChild(dummy_label);
	/* and put in the real preview widget */
	XtManageChild(preview_widget);
	/* insure that the most recent colormap is installed */
	set_cmap(XtWindow(file_popup));
	(void) XSetWMProtocols(tool_d, XtWindow(file_popup), &wm_delete_window, 1);
	reset_cursor();

	/* this will install the wheel accelerators on the scrollbars
	   now that the panel is realized */
	Rescan(0, 0, 0, 0);
}

static void
file_xoff_unit_select(Widget w, XtPointer new_unit, XtPointer garbage)
{
	(void)garbage;
    FirstArg(XtNlabel, XtName(w));
    SetValues(file_xoff_unit_panel);
    xoff_unit_setting = (intptr_t) new_unit;
}

static void
file_yoff_unit_select(Widget w, XtPointer new_unit, XtPointer garbage)
{
	(void)garbage;
    FirstArg(XtNlabel, XtName(w));
    SetValues(file_yoff_unit_panel);
    yoff_unit_setting = (intptr_t) new_unit;
}

void create_file_panel(void)
{
	Widget		 file, beside, below, butbelow;
	XFontStruct	*temp_font;
	static Boolean   actions_added=False;
	Widget		 preview_form;
	Position	 xposn, yposn;

	xoff_unit_setting = yoff_unit_setting = (intptr_t) appres.INCHES? 0: 1;

	XtTranslateCoords(tool, (Position) 0, (Position) 0, &xposn, &yposn);

	FirstArg(XtNx, xposn+50);
	NextArg(XtNy, yposn+50);
	NextArg(XtNtitle, "Xfig: File menu");
	NextArg(XtNtitleEncoding, XA_STRING);
	NextArg(XtNcolormap, tool_cm);
	NextArg(XtNinput, True);
	file_popup = XtCreatePopupShell("file_popup",
					transientShellWidgetClass,
					tool, Args, ArgCount);
	XtOverrideTranslations(file_popup,
			   XtParseTranslationTable(file_translations));

	file_panel = XtCreateManagedWidget("file_panel", formWidgetClass,
					   file_popup, NULL, ZERO);

	FirstArg(XtNlabel, "  File Status");
	NextArg(XtNinternational, False);
	NextArg(XtNjustify, XtJustifyLeft);
	NextArg(XtNborderWidth, 0);
	NextArg(XtNresize, False);
	NextArg(XtNtop, XtChainTop);
	NextArg(XtNbottom, XtChainTop);
	NextArg(XtNleft, XtChainLeft);
	NextArg(XtNright, XtChainLeft);
	file_stat_label = XtCreateManagedWidget("file_status_label", labelWidgetClass,
					    file_panel, Args, ArgCount);
	/* start with long label so length is enough - it will be reset by calling proc */
	FirstArg(XtNlabel, "Not modified ");
	NextArg(XtNinternational, False);
	NextArg(XtNfromHoriz, file_stat_label);
	NextArg(XtNjustify, XtJustifyLeft);
	NextArg(XtNborderWidth, 0);
	NextArg(XtNresize, False);
	NextArg(XtNtop, XtChainTop);
	NextArg(XtNbottom, XtChainTop);
	NextArg(XtNleft, XtChainLeft);
	NextArg(XtNright, XtChainLeft);
	file_status = XtCreateManagedWidget("file_status", labelWidgetClass,
					    file_panel, Args, ArgCount);

	FirstArg(XtNlabel, " # of Objects");
	NextArg(XtNinternational, False);
	NextArg(XtNfromVert, file_status);
	NextArg(XtNjustify, XtJustifyLeft);
	NextArg(XtNborderWidth, 0);
	NextArg(XtNtop, XtChainTop);
	NextArg(XtNbottom, XtChainTop);
	NextArg(XtNleft, XtChainLeft);
	NextArg(XtNright, XtChainLeft);
	num_obj_label = XtCreateManagedWidget("num_objects_label", labelWidgetClass,
					  file_panel, Args, ArgCount);

	FirstArg(XtNwidth, 50);
	NextArg(XtNinternational, False);
	NextArg(XtNfromVert, file_status);
	NextArg(XtNfromHoriz, num_obj_label);
	NextArg(XtNjustify, XtJustifyLeft);
	NextArg(XtNborderWidth, 0);
	NextArg(XtNresize, False);
	NextArg(XtNtop, XtChainTop);
	NextArg(XtNbottom, XtChainTop);
	NextArg(XtNleft, XtChainLeft);
	NextArg(XtNright, XtChainLeft);
	num_objects = XtCreateManagedWidget("num_objects", labelWidgetClass,
					    file_panel, Args, ArgCount);

	FirstArg(XtNlabel, "Figure Offset");
	NextArg(XtNinternational, False);
	NextArg(XtNfromVert, num_objects);
	NextArg(XtNborderWidth, 0);
	NextArg(XtNtop, XtChainTop);
	NextArg(XtNbottom, XtChainTop);
	NextArg(XtNleft, XtChainLeft);
	NextArg(XtNright, XtChainLeft);
	figure_off = XtCreateManagedWidget("fig_offset_label", labelWidgetClass,
				     file_panel, Args, ArgCount);
	FirstArg(XtNlabel, "X");
	NextArg(XtNinternational, False);
	NextArg(XtNfromHoriz, figure_off);
	NextArg(XtNfromVert, num_objects);
	NextArg(XtNborderWidth, 0);
	NextArg(XtNtop, XtChainTop);
	NextArg(XtNbottom, XtChainTop);
	NextArg(XtNleft, XtChainLeft);
	NextArg(XtNright, XtChainLeft);
	fig_offset_lbl_x = XtCreateManagedWidget("fig_offset_lbl_x", labelWidgetClass,
				     file_panel, Args, ArgCount);

	FirstArg(XtNwidth, 40);
	NextArg(XtNleftMargin, 4);
	NextArg(XtNeditType, XawtextEdit);
	NextArg(XtNstring, "0");
	NextArg(XtNinsertPosition, 0);
	NextArg(XtNfromHoriz, fig_offset_lbl_x);
	NextArg(XtNfromVert, num_objects);
	NextArg(XtNborderWidth, INTERNAL_BW);
	NextArg(XtNscrollHorizontal, XawtextScrollWhenNeeded);
	NextArg(XtNtop, XtChainTop);
	NextArg(XtNbottom, XtChainTop);
	NextArg(XtNleft, XtChainLeft);
	NextArg(XtNright, XtChainLeft);
	NextArg(XtNinternational, appres.international);
	fig_offset_x = XtCreateManagedWidget("fig_offset_x", asciiTextWidgetClass,
					     file_panel, Args, ArgCount);
	FirstArg(XtNfromHoriz, fig_offset_x);
	NextArg(XtNfromVert, num_objects);
	NextArg(XtNtop, XtChainTop);
	NextArg(XtNbottom, XtChainTop);
	NextArg(XtNleft, XtChainLeft);
	NextArg(XtNright, XtChainLeft);
	NextArg(XtNleftBitmap, menu_arrow);	/* use menu arrow for pull-down */
	NextArg(XtNinternational, False);
	file_xoff_unit_panel = XtCreateManagedWidget(offset_unit_items[appres.INCHES? 0: 1],
				menuButtonWidgetClass, file_panel, Args, ArgCount);
	make_pulldown_menu(offset_unit_items, XtNumber(offset_unit_items),
				   -1, "", file_xoff_unit_panel, file_xoff_unit_select);

	/* put the Y offset below the X */

	FirstArg(XtNlabel, "Y");
	NextArg(XtNinternational, False);
	NextArg(XtNfromHoriz, figure_off);
	NextArg(XtNfromVert, file_xoff_unit_panel);
	NextArg(XtNborderWidth, 0);
	NextArg(XtNtop, XtChainTop);
	NextArg(XtNbottom, XtChainTop);
	NextArg(XtNleft, XtChainLeft);
	NextArg(XtNright, XtChainLeft);
	fig_offset_lbl_y = XtCreateManagedWidget("fig_offset_lbl_y", labelWidgetClass,
				     file_panel, Args, ArgCount);

	FirstArg(XtNwidth, 40);
	NextArg(XtNleftMargin, 4);
	NextArg(XtNeditType, XawtextEdit);
	NextArg(XtNstring, "0");
	NextArg(XtNinsertPosition, 0);
	NextArg(XtNfromHoriz, fig_offset_lbl_y);
	NextArg(XtNfromVert, file_xoff_unit_panel);
	NextArg(XtNborderWidth, INTERNAL_BW);
	NextArg(XtNscrollHorizontal, XawtextScrollWhenNeeded);
	NextArg(XtNtop, XtChainTop);
	NextArg(XtNbottom, XtChainTop);
	NextArg(XtNleft, XtChainLeft);
	NextArg(XtNright, XtChainLeft);
	NextArg(XtNinternational, appres.international);
	fig_offset_y = XtCreateManagedWidget("fig_offset_y", asciiTextWidgetClass,
					     file_panel, Args, ArgCount);
	FirstArg(XtNfromHoriz, fig_offset_y);
	NextArg(XtNfromVert, file_xoff_unit_panel);
	NextArg(XtNtop, XtChainTop);
	NextArg(XtNbottom, XtChainTop);
	NextArg(XtNleft, XtChainLeft);
	NextArg(XtNright, XtChainLeft);
	NextArg(XtNleftBitmap, menu_arrow);	/* use menu arrow for pull-down */
	NextArg(XtNinternational, False);
	file_yoff_unit_panel = XtCreateManagedWidget(offset_unit_items[appres.INCHES? 0: 1],
				menuButtonWidgetClass, file_panel, Args, ArgCount);
	make_pulldown_menu(offset_unit_items, XtNumber(offset_unit_items),
				   -1, "", file_yoff_unit_panel, file_yoff_unit_select);

	FirstArg(XtNlabel, " Current File");
	NextArg(XtNinternational, False);
	NextArg(XtNfromVert, file_yoff_unit_panel);
	NextArg(XtNjustify, XtJustifyLeft);
	NextArg(XtNborderWidth, 0);
	NextArg(XtNtop, XtChainTop);
	NextArg(XtNbottom, XtChainTop);
	NextArg(XtNleft, XtChainLeft);
	NextArg(XtNright, XtChainLeft);
	cfile_lab = XtCreateManagedWidget("cur_file_label", labelWidgetClass,
					  file_panel, Args, ArgCount);

	FirstArg(XtNlabel, cur_filename);
	NextArg(XtNinternational, appres.international);
	NextArg(XtNfromVert, file_yoff_unit_panel);
	NextArg(XtNfromHoriz, cfile_lab);
	NextArg(XtNjustify, XtJustifyLeft);
	NextArg(XtNborderWidth, 0);
	NextArg(XtNwidth, FILE_WID);
	NextArg(XtNtop, XtChainTop);
	NextArg(XtNbottom, XtChainTop);
	NextArg(XtNleft, XtChainLeft);
	NextArg(XtNright, XtChainRight);
	cfile_text = XtCreateManagedWidget("cur_file_name", labelWidgetClass,
					   file_panel, Args, ArgCount);

	FirstArg(XtNlabel, "     Filename");
	NextArg(XtNinternational, False);
	NextArg(XtNfromVert, cfile_lab);
	NextArg(XtNborderWidth, 0);
	NextArg(XtNtop, XtChainTop);
	NextArg(XtNbottom, XtChainTop);
	NextArg(XtNleft, XtChainLeft);
	NextArg(XtNright, XtChainLeft);
	file = XtCreateManagedWidget("file_label", labelWidgetClass,
				     file_panel, Args, ArgCount);
	FirstArg(XtNfont, &temp_font);
	GetValues(file);

	/* make the filename slot narrow so we can show the preview to the right */
	FirstArg(XtNwidth, FILE_WID);
	NextArg(XtNleftMargin, 4);
	NextArg(XtNheight, max_char_height(temp_font) * 2 + 4);
	NextArg(XtNeditType, XawtextEdit);
	NextArg(XtNstring, cur_filename);
	NextArg(XtNinternational, appres.international);
	NextArg(XtNinsertPosition, strlen(cur_filename));
	NextArg(XtNfromHoriz, file);
	NextArg(XtNfromVert, cfile_lab);
	NextArg(XtNscrollHorizontal, XawtextScrollWhenNeeded);
	NextArg(XtNtop, XtChainTop);
	NextArg(XtNbottom, XtChainTop);
	NextArg(XtNleft, XtChainLeft);
	NextArg(XtNright, XtChainRight);
	file_selfile = XtCreateManagedWidget("file_name", asciiTextWidgetClass,
					     file_panel, Args, ArgCount);
	XtOverrideTranslations(file_selfile,
			   XtParseTranslationTable(text_translations));

	if (!actions_added) {
	    XtAppAddActions(tool_app, file_actions, XtNumber(file_actions));
	    actions_added = True;
	    /* add action to load file */
	    XtAppAddActions(tool_app, file_name_actions, XtNumber(file_name_actions));
	}

	/* make the directory list etc */
	change_directory(cur_file_dir);
	strcpy(save_file_dir, cur_file_dir);
	create_dirinfo(True, file_panel, file_selfile, &beside, &butbelow, &file_mask,
		       &file_dir, &file_flist, &file_dlist, FILE_ALT_WID, True);

	/*****************************************************/
	/* now make a preview canvas on the whole right side */
	/*****************************************************/

	/* first a form just to the right of the figure offset menu button */
	FirstArg(XtNfromHoriz, file_yoff_unit_panel);
	NextArg(XtNtop, XtChainTop);
	NextArg(XtNbottom, XtChainTop);
	NextArg(XtNleft, XtChainRight);
	NextArg(XtNright, XtChainRight);
	preview_form = XtCreateManagedWidget("preview_form", formWidgetClass,
				file_panel, Args, ArgCount);

	/* a title */
	FirstArg(XtNlabel, "Preview   ");
	NextArg(XtNinternational, False);
	NextArg(XtNborderWidth, 0);
	preview_label = XtCreateManagedWidget("preview_label", labelWidgetClass,
					    preview_form, Args, ArgCount);

	/* make a label for the figure size */
	FirstArg(XtNlabel, " ");
	NextArg(XtNinternational, False);
	NextArg(XtNfromHoriz, preview_label);
	NextArg(XtNborderWidth, 0);
	preview_size = XtCreateManagedWidget("preview_size", labelWidgetClass,
					    preview_form, Args, ArgCount);

	/* and one for the figure name in the preview */
	FirstArg(XtNlabel, " ");
	NextArg(XtNinternational, appres.international);
	NextArg(XtNfromVert, preview_label);
	NextArg(XtNborderWidth, 0);
	preview_name = XtCreateManagedWidget("preview_name", labelWidgetClass,
					    preview_form, Args, ArgCount);

	/* now a label widget for the preview canvas */

	/* first create a pixmap for its background, into which we'll draw the figure */
	/* actually, make one for landscape and one for portrait */
	preview_land_pixmap = XCreatePixmap(tool_d, canvas_win,
			PREVIEW_CANVAS_W, PREVIEW_CANVAS_H, tool_dpth);
	preview_port_pixmap = XCreatePixmap(tool_d, canvas_win,
			PREVIEW_CANVAS_H, PREVIEW_CANVAS_W, tool_dpth);

	/* make a form for the preview widget so we can center it */

	FirstArg(XtNwidth, PREVIEW_CANVAS_W+8);
	NextArg(XtNheight, PREVIEW_CANVAS_W+8);
	NextArg(XtNfromVert, preview_name);
	NextArg(XtNtop, XtChainTop);
	NextArg(XtNbottom, XtChainTop);
	NextArg(XtNleft, XtChainLeft);
	NextArg(XtNright, XtChainLeft);
	preview_widget_form = XtCreateManagedWidget("preview_widget_form",
				formWidgetClass, preview_form, Args, ArgCount);

	/* make a dummy label to fill the space until we put up the preview */
	FirstArg(XtNlabel, "");
	NextArg(XtNinternational, False);
	NextArg(XtNwidth, PREVIEW_CANVAS_W);
	NextArg(XtNheight, PREVIEW_CANVAS_W);
	dummy_label = XtCreateManagedWidget("dummy_label", labelWidgetClass,
					  preview_widget_form, Args, ArgCount);

	/* start the widget preview in portrait */
	/* don't manage it yet until the panel is popped up */

	FirstArg(XtNlabel, "");
	NextArg(XtNinternational, False);
	NextArg(XtNbackgroundPixmap, preview_port_pixmap);
	NextArg(XtNhorizDistance, (PREVIEW_CANVAS_W-PREVIEW_CANVAS_H)/2+4); /* center */
	NextArg(XtNvertDistance, 4);
	NextArg(XtNwidth, PREVIEW_CANVAS_H);
	NextArg(XtNheight, PREVIEW_CANVAS_W);
	NextArg(XtNtop, XtChainTop);
	NextArg(XtNbottom, XtChainTop);
	NextArg(XtNleft, XtChainLeft);
	NextArg(XtNright, XtChainLeft);
	preview_widget = XtCreateWidget("preview_widget", labelWidgetClass,
					  preview_widget_form, Args, ArgCount);
	/* erase the preview pixmaps */
	clear_preview();

	/* label for the figure comments */

	FirstArg(XtNlabel, "Figure comments:");
	NextArg(XtNinternational, False);
	NextArg(XtNfromVert, preview_widget_form);
	below = XtCreateManagedWidget("comments_label", labelWidgetClass,
					  preview_form, Args, ArgCount);
	/* window for the figure comments */

	FirstArg(XtNwidth, PREVIEW_CANVAS_W+8);
	NextArg(XtNleftMargin, 4);
	NextArg(XtNheight, 50);
	NextArg(XtNfromVert, below);
	NextArg(XtNvertDistance, 1);
	NextArg(XtNborderWidth, INTERNAL_BW);
	NextArg(XtNscrollHorizontal, XawtextScrollWhenNeeded);
	NextArg(XtNscrollVertical, XawtextScrollWhenNeeded);
	NextArg(XtNdisplayCaret, False);
	NextArg(XtNtop, XtChainTop);
	NextArg(XtNbottom, XtChainTop);
	NextArg(XtNleft, XtChainLeft);
	NextArg(XtNright, XtChainLeft);
	NextArg(XtNinternational, appres.international);
	comments_widget = XtCreateManagedWidget("comments", asciiTextWidgetClass,
					     preview_form, Args, ArgCount);

	/* make cancel preview button */
	FirstArg(XtNlabel, "Stop Preview");
	NextArg(XtNinternational, False);
	NextArg(XtNsensitive, False);
	NextArg(XtNfromVert, comments_widget);
	preview_stop = XtCreateManagedWidget("preview_stop", commandWidgetClass,
				       preview_form, Args, ArgCount);
	XtAddEventHandler(preview_stop, ButtonReleaseMask, False,
			  (XtEventHandler)file_preview_stop, (XtPointer) NULL);

	/* end of preview form */

	/* now make buttons along the bottom */

	FirstArg(XtNlabel, "Cancel");
	NextArg(XtNinternational, False);
	NextArg(XtNfromHoriz, beside);
	NextArg(XtNhorizDistance, 25);
	NextArg(XtNfromVert, butbelow);
	NextArg(XtNvertDistance, 15);
	NextArg(XtNheight, 25);
	NextArg(XtNborderWidth, INTERNAL_BW);
	NextArg(XtNtop, XtChainBottom);
	NextArg(XtNbottom, XtChainBottom);
	NextArg(XtNleft, XtChainLeft);
	NextArg(XtNright, XtChainLeft);
	beside = cancel_button = XtCreateManagedWidget("cancel", commandWidgetClass,
				       file_panel, Args, ArgCount);
	XtAddEventHandler(cancel_button, ButtonReleaseMask, False,
			  (XtEventHandler)cancel_request, (XtPointer) NULL);

	/* now create Save, Open and Merge buttons but don't manage them - that's
	   done in popup_saveas_panel, popup_open_panel, and popup_merge_panel */

	FirstArg(XtNlabel, " Save ");
	NextArg(XtNinternational, False);
	NextArg(XtNvertDistance, 15);
	NextArg(XtNfromVert, butbelow);
	NextArg(XtNfromHoriz, beside);
	NextArg(XtNhorizDistance, 25);
	NextArg(XtNheight, 25);
	NextArg(XtNborderWidth, INTERNAL_BW);
	NextArg(XtNtop, XtChainBottom);
	NextArg(XtNbottom, XtChainBottom);
	NextArg(XtNleft, XtChainLeft);
	NextArg(XtNright, XtChainLeft);
	save_button = XtCreateWidget("save", commandWidgetClass,
				     file_panel, Args, ArgCount);
	XtAddEventHandler(save_button, ButtonReleaseMask, False,
			  (XtEventHandler) do_save, (XtPointer) NULL);

	FirstArg(XtNlabel, " Open ");
	NextArg(XtNinternational, False);
	NextArg(XtNborderWidth, INTERNAL_BW);
	NextArg(XtNvertDistance, 15);
	NextArg(XtNfromVert, butbelow);
	NextArg(XtNfromHoriz, beside);
	NextArg(XtNhorizDistance, 25);
	NextArg(XtNheight, 25);
	NextArg(XtNtop, XtChainBottom);
	NextArg(XtNbottom, XtChainBottom);
	NextArg(XtNleft, XtChainLeft);
	NextArg(XtNright, XtChainLeft);
	load_button = XtCreateWidget("load", commandWidgetClass,
				     file_panel, Args, ArgCount);
	XtAddEventHandler(load_button, ButtonReleaseMask, False,
			  (XtEventHandler)load_request, (XtPointer) NULL);

	FirstArg(XtNlabel, "Merge ");
	NextArg(XtNinternational, False);
	NextArg(XtNhorizDistance, 20);
	NextArg(XtNfromVert, butbelow);
	NextArg(XtNfromHoriz, beside);
	NextArg(XtNvertDistance, 15);
	NextArg(XtNborderWidth, INTERNAL_BW);
	NextArg(XtNheight, 25);
	NextArg(XtNtop, XtChainBottom);
	NextArg(XtNbottom, XtChainBottom);
	NextArg(XtNleft, XtChainLeft);
	NextArg(XtNright, XtChainLeft);
	merge_button = XtCreateWidget("merge", commandWidgetClass,
				      file_panel, Args, ArgCount);
	XtAddEventHandler(merge_button, ButtonReleaseMask, False,
			  (XtEventHandler)merge_request, (XtPointer) NULL);

	FirstArg(XtNlabel, "New xfig...");
	NextArg(XtNinternational, False);
	NextArg(XtNborderWidth, INTERNAL_BW);
	NextArg(XtNvertDistance, 15);
	NextArg(XtNfromVert, butbelow);
	NextArg(XtNfromHoriz, merge_button);
	NextArg(XtNhorizDistance, 25);
	NextArg(XtNheight, 25);
	NextArg(XtNtop, XtChainBottom);
	NextArg(XtNbottom, XtChainBottom);
	NextArg(XtNleft, XtChainLeft);
	NextArg(XtNright, XtChainLeft);
	new_xfig_button = XtCreateWidget("new_xfig", commandWidgetClass,
				     file_panel, Args, ArgCount);
	XtAddEventHandler(new_xfig_button, ButtonReleaseMask, False,
			  (XtEventHandler)new_xfig_request, (XtPointer) NULL);

	XtInstallAccelerators(file_panel, cancel_button);
	XtInstallAccelerators(file_panel, save_button);
	XtInstallAccelerators(file_panel, load_button);
	XtInstallAccelerators(file_panel, new_xfig_button);
	XtInstallAccelerators(file_panel, merge_button);

}

/* check if user pressed cancel button */
Boolean
check_cancel(void)
{
    if (preview_in_progress) {
	process_pending();
	return cancel_preview;
    }
    return False;

}

static void
clear_preview(void)
{
    FirstArg(XtNlabel, "                                 ");
    SetValues(preview_name);

    FirstArg(XtNlabel, "                     ");
    SetValues(preview_size);

    /* clear both port and land pixmap */
    XSetForeground(tool_d, gccache[ERASE], getpixel(CANVAS_BG));
    XFillRectangle(tool_d, preview_land_pixmap, gccache[ERASE], 0, 0,
		   PREVIEW_CANVAS_W, PREVIEW_CANVAS_H);
    XFillRectangle(tool_d, preview_port_pixmap, gccache[ERASE], 0, 0,
		   PREVIEW_CANVAS_H, PREVIEW_CANVAS_W);

    FirstArg(XtNbackgroundPixmap, (Pixmap)0);
    SetValues(preview_widget);
    /* put port pixmap in widget */
    FirstArg(XtNbackgroundPixmap, preview_port_pixmap);
    SetValues(preview_widget);
}

static void
file_preview_stop(Widget w, XButtonEvent *ev)
{
	(void)w;
	(void)ev;
    /* set the cancel flag */
    cancel_preview = True;
    /* make the cancel button insensitive */
    XtSetSensitive(preview_stop, False);
    process_pending();
}

void preview_figure(char *filename, Widget parent, Widget canvas, Widget size_widget)
{
    fig_settings    settings;
    int		save_objmask;
    int		save_zoomxoff, save_zoomyoff;
    float	save_zoomscale;
    Boolean	save_shownums;
    F_compound	*figure;
    int		xmin, xmax, ymin, ymax;
    float	width, height;
    int		pixwidth, pixheight;
    int		i, status;
    char	figsize[50];
    Pixel	pb;

    /* alloc a compound object - we must do it this way so free_compound()
       works properly */
    figure = create_compound();

    /* if already previewing file, return */
    if (preview_in_progress == True)
	return;

    /* clear the cancel flag */
    cancel_preview = False;

    /* and any request to load a file */
    file_load_request = False;
    /* and merge */
    file_merge_request = False;
    /* and save */
    file_save_request = False;
    /* and cancel */
    file_cancel_request = False;

    /* say we are in progress */
    preview_in_progress = True;

    /* save active layer array and set all to True */
    save_active_layers();
    save_depths();
    save_counts_and_clear();
    reset_layers();

    /* save user colors in case preview changes them */
    if (!user_colors_saved) {
	save_user_colors();
	save_nuser_colors();
    }

    /* make the cancel button sensitive */
    XtSetSensitive(preview_stop, True);
    /* change label to read "Previewing" */
    FirstArg(XtNlabel, "Previewing");
    SetValues(preview_label);
    /* get current background color of label */
    FirstArg(XtNbackground, &pb);
    GetValues(preview_label);
    /* set it to red while previewing */
    FirstArg(XtNbackground, getpixel(YELLOW));
    SetValues(preview_label);

    /* give filename a chance to appear in the Filename field */
    process_pending();

    /* first, save current zoom settings */
    save_zoomscale	= display_zoomscale;
    save_zoomxoff	= zoomxoff;
    save_zoomyoff	= zoomyoff;

    /* save and turn off showing vertex numbers */
    save_shownums	= appres.shownums;
    appres.shownums	= False;

    /* insure that the most recent colormap is installed */
    set_cmap(XtWindow(parent));

    /* make wait cursor */
    XDefineCursor(tool_d, XtWindow(parent), wait_cursor);
    /* define it for the file list panel too */
    XDefineCursor(tool_d, XtWindow(file_flist), wait_cursor);
    /* but use a "hand" cursor for the Preview Stop button */
    XDefineCursor(tool_d, XtWindow(preview_stop), pick9_cursor);
    process_pending();

    /* if we haven't already saved colors of the images on the canvas */
    if (!image_colors_are_saved) {
	image_colors_are_saved = True;
	/* save current canvas image colors */
	for (i=0; i<avail_image_cols; i++) {
	    save_image_cells[i].color.red   = image_cells[i].color.red;
	    save_image_cells[i].color.green = image_cells[i].color.green;
	    save_image_cells[i].color.blue  = image_cells[i].color.blue;
	}
	save_avail_image_cols = avail_image_cols;
    }

    /* unmanage the preview widget in case we need to change its shape (port<->land) */
    XtUnmanageChild(canvas);
    /* fool the toolkit into drawing the new pixmap */
    FirstArg(XtNbackgroundPixmap, (Pixmap)0);
    SetValues(canvas);

    /* read the figure into the local F_compound "figure" */

    /* we'll ignore the stuff returned in "settings" */
    if ((status = read_figc(filename,figure, DONT_MERGE, REMAP_IMAGES, 0,0,&settings)) != 0) {
	switch (status) {
	   case -1: file_msg("Bad format");
		   break;
	   case -2: file_msg("File is empty");
		   break;
	   default: file_msg("Error reading %s: %s",filename,strerror(errno));
	}
	/* clear pixmap of any figure so user knows something went wrong */
	clear_preview();
    } else {
	/*
	 *  successful read, get the size
	*/

	add_compound_depth(figure);	/* count objects at each depth */

	/* update the preview name */
	FirstArg(XtNlabel, filename);
	SetValues(preview_name);

	/* put in any comments */
	if (figure->comments) {
	    FirstArg(XtNstring, figure->comments);
	} else {
	    FirstArg(XtNstring, "");
	}
	SetValues(comments_widget);

	/* get bounds of figure */
	xmin = figure->nwcorner.x;
	xmax = figure->secorner.x;
	ymin = figure->nwcorner.y;
	ymax = figure->secorner.y;
	width = xmax - xmin;
	height = ymax - ymin;
	/* calculate size in current units */
	if (width < PIX_PER_INCH || height < PIX_PER_INCH) {
	    /* for small figures, show more decimal places */
	    if (appres.INCHES) {
		sprintf(figsize, "%.2f x %.2f in",width/PIX_PER_INCH,height/PIX_PER_INCH);
	    } else {
		sprintf(figsize, "%.1f x %.1f cm",width/PIX_PER_CM,height/PIX_PER_CM);
	    }
	} else {
	    if (appres.INCHES) {
		sprintf(figsize, "%.1f x %.1f in",width/PIX_PER_INCH,height/PIX_PER_INCH);
	    } else {
		sprintf(figsize, "%.0f x %.0f cm",width/PIX_PER_CM,height/PIX_PER_CM);
	    }
	}

	if (size_widget) {
	    FirstArg(XtNlabel, figsize);
	    SetValues(preview_size);
	}

	/* now switch the drawing canvas to our preview window and set width/height */
	if (settings.landscape) {
	    canvas_win = (Window) preview_land_pixmap;
	    pixwidth = PREVIEW_CANVAS_W;
	    pixheight = PREVIEW_CANVAS_H;
	} else {
	    canvas_win = (Window) preview_port_pixmap;
	    pixwidth = PREVIEW_CANVAS_H;
	    pixheight = PREVIEW_CANVAS_W;
	}
	XftDrawChange(canvas_draw, canvas_win);

	/* scale to fit the preview canvas */
	display_zoomscale =
			min2((float)(pixwidth-8)/width, (float)(pixheight-8)/height) *
				ZOOM_FACTOR;
	/* limit magnification to 5.0 */
	if (display_zoomscale > 5.0)
		display_zoomscale = 5.0;
	zoomscale = display_zoomscale/ZOOM_FACTOR;

	/* center the figure in the canvas */
	zoomxoff = -(pixwidth/zoomscale - width) / 2.0 + xmin;
	zoomyoff = -(pixheight/zoomscale - height) / 2.0 + ymin;

	/* insure that the most recent colormap is installed */
	/* we may have switched to a private colormap when previewing this figure */
	set_cmap(XtWindow(parent));

	/* no markers */
	save_objmask = cur_objmask;
	cur_objmask = 0;

	/* clear the pixmap with the canvas background color */
	XSetForeground(tool_d, gccache[ERASE], getpixel(CANVAS_BG));
	XFillRectangle(tool_d, canvas_win, gccache[ERASE], 0, 0, pixwidth, pixheight);

	/* if he pressed "cancel preview" while reading file, skip displaying figure */
	if (! check_cancel()) {
	    /* draw the preview into the pixmap */
	    redisplay_objects(figure);
	}

	/* restore marker flag */
	cur_objmask = save_objmask;

	/* put the pixmap back in and set the width and height and center it */
	FirstArg(XtNbackgroundPixmap, canvas_win);
	NextArg(XtNwidth, pixwidth);
	NextArg(XtNheight, pixheight);
	if (settings.landscape) {
	    NextArg(XtNvertDistance, (PREVIEW_CANVAS_W-PREVIEW_CANVAS_H)/2+4);
	    NextArg(XtNhorizDistance, 4);
	} else {
	    NextArg(XtNvertDistance, 4);
	    NextArg(XtNhorizDistance, (PREVIEW_CANVAS_W-PREVIEW_CANVAS_H)/2+4);
	}
	SetValues(canvas);
	XtManageChild(canvas);
    }

    /* free the figure compound */
    free_compound(&figure);

    /* switch canvas back */
    canvas_win = main_canvas;
    XftDrawChange(canvas_draw, main_canvas);

    /* restore active layer array */
    restore_active_layers();
    /* and depth and counts */
    restore_depths();
    restore_counts();

    /* now restore settings for main canvas/figure */
    display_zoomscale	= save_zoomscale;
    zoomxoff		= save_zoomxoff;
    zoomyoff		= save_zoomyoff;
    zoomscale		= display_zoomscale/ZOOM_FACTOR;

    /* restore shownums */
    appres.shownums	= save_shownums;

    /* reset cursors */
    XUndefineCursor(tool_d, XtWindow(parent));
    XUndefineCursor(tool_d, XtWindow(file_flist));
    XUndefineCursor(tool_d, XtWindow(preview_stop));

    /* reset the cancel button and flag */
    cancel_preview = False;

    /* make the cancel button insensitive */
    XtSetSensitive(preview_stop, False);

    /* change label to read "Preview" */
    FirstArg(XtNlabel, "Preview");
    /* and change background to original color */
    NextArg(XtNbackground, pb);
    SetValues(preview_label);

    process_pending();
    /* check if user had double clicked on filename which means he wanted
       to load the file, not just preview it */

    /* say we are finished */
    preview_in_progress = False;

    if (file_cancel_request) {
	cancel_preview = False;
	file_panel_cancel((Widget) 0, (XButtonEvent *) 0);
    }
    if (file_load_request) {
	cancel_preview = False;
	do_load((Widget) 0, (XButtonEvent *) 0);
	/* if redisplay_region was called don't bother because
	   we're loading a file over it */
	request_redraw = False;
    }
    if (file_merge_request) {
	cancel_preview = False;
	do_merge((Widget) 0, (XButtonEvent *) 0);
    }
    if (file_save_request) {
	cancel_preview = False;
	do_save((Widget) 0, (XButtonEvent *) 0);
    }
    /* if user requested a canvas redraw while preview was being generated
       do that now for full canvas */
    if (request_redraw) {
	redisplay_region(0, 0, CANVAS_WD, CANVAS_HT);
	request_redraw = False;
    }
}

/* save user colors into temp vars */
void save_user_colors(void)
{
	int	i;

	if (appres.DEBUG)
		fprintf(stderr,
			"** Saving user colors. Before: bool user_colors_saved = %d\n",
		user_colors_saved);

	user_colors_saved = True;

	/* first save the current colors because del_color_cell destroys them */
	for (i = 0; i < num_usr_cols; ++i) {
		saved_user_colors[i] = user_color[i];
		/* and save Free entries */
		saved_userFree[i] = colorFree[i];
		/* now free any previously defined user colors */
		del_color_cell(i);	/* remove widget and colormap entry */
	}

	saved_user_num = num_usr_cols;
}

/* save n_user colors into temp vars */
void save_nuser_colors(void)
{
	int		i;

	if (appres.DEBUG)
		fprintf(stderr,
			"** Saving n_user colors. Before: bool nuser_colors_saved = %d\n",
			user_colors_saved);

	nuser_colors_saved = True;

	/* first save the current colors because del_color_cell destroys them */
	for (i = 0; i < n_num_usr_cols; ++i) {
		saved_nuser_colors[i] = n_user_colors[i];
		/* and save Free entries */
		saved_nuserFree[i] = n_colorFree[i];
	}
	saved_nuser_num = n_num_usr_cols;
}

/* restore user colors from temp vars */
void restore_user_colors(void)
{
	int	i, num;

	if (!user_colors_saved)
		return;

	if (appres.DEBUG)
		fprintf(stderr,
			"** Restoring user colors. Before: bool user_colors_saved = %d\n",
			user_colors_saved);

	user_colors_saved = False;

	/* first free any previously defined user colors */
	for (i = 0; i < num_usr_cols; ++i) {
		del_color_cell(i);	/* remove widget and colormap entry */
	}

	num_usr_cols = saved_user_num;

	/* now restore the orig user colors */
	for (i = 0; i < num_usr_cols; ++i) {
		user_color[i] = saved_user_colors[i];
		/* and Free entries */
		colorFree[i] = saved_userFree[i];
	}

	/* now try to allocate those colors */
	if (num_usr_cols > 0) {
		num = num_usr_cols;
		num_usr_cols = 0;
		/* fill the colormap and the color memories */
		for (i = 0; i < num; ++i) {
			if (colorFree[i]) {
				colorUsed[i] = False;
				continue;
			}
			/* and add a widget and colormap entry */
			if (add_color_cell(USE_EXISTING_COLOR, i,
						user_color[i].color.red/256,
						user_color[i].color.green/256,
						user_color[i].color.blue/256)
					== -1) {
				file_msg("Can't allocate more than %d user colors, not enough colormap entries",
						num_usr_cols);
				return;
			}
			colorUsed[i] = True;
		}
	}
}

/* Restore user colors from temp vars into n_user_...  */
void restore_nuser_colors(void)
{
	int	i;

	if (!nuser_colors_saved)
		return;

	if (appres.DEBUG)
		fprintf(stderr,
			"** Restoring user colors into n_user colors\n");

	nuser_colors_saved = False;

	n_num_usr_cols = saved_nuser_num;

	/* now restore the orig user colors */
	for (i = 0; i < n_num_usr_cols; ++i) {
		n_user_colors[i] = saved_nuser_colors[i];
		/* and Free entries */
		n_colorFree[i] = saved_nuserFree[i];
	}
}

